子回路へ分割の実装編です。
修正前はこうなっていたのを、
# class ChildCircuit def self.create(lines, rects) all_plus_poles = ... all_minus_poles = ... all_switches = ... wf_set = to_wire_fragments(lines) all_edges = to_edges(wf_set) ChildCircuit.new( all_edges, all_plus_poles, all_minus_poles, all_switches ) end
次のように変更しました。
def self.create(lines, rects) all_plus_poles = ... all_minus_poles = ... all_switches = ... wf_set = to_wire_fragments(lines) all_edges = to_edges(wf_set) edge_groups = to_edge_groups(all_edges) edge_groups.map { |edges| plus_poles = select_child_circuit_units(edges, all_plus_poles) minus_poles = select_child_circuit_units(edges, all_minus_poles) switches = select_child_circuit_units(edges, all_switches) ChildCircuit.new( edges, plus_poles, minus_poles, switches ) } end
to_edge_groups()
でエッジを子回路ごとにグループ化し、- グループごとに
select_child_circuit_units()
でグループ内のエッジ上にあるプラス極、マイナス極、スイッチだけを選び、- edges と一緒に
ChildCircuit.new
に渡してインスタンスを生成
to_edge_groups()
は何をしているかというのが下記です。
ここが分割処理のメインの部分。
def self.to_edge_groups(all_edges) ecs = all_edges.map { |edge| EdgeCluster.new([edge]) } loop do merge_occured = false ecs.combination(2).each { |ec_a, ec_b| if ec_a.connected_to?(ec_b) ec_a.merge(ec_b) merge_occured = true end } break unless merge_occured ecs = ecs.reject { |ec| ec.edges.empty? } end ecs.map { |ec| ec.edges } end
EdgeCluster はエッジの配列と便利メソッドを持つ作業用のクラスです。
- EdgeCluster 1つにつき 1本のエッジを持たせた状態から開始し、
EdgeCluster 同士のすべての組み合わせについてエッジのいずれかがつながっているかを調べ、
- つながっていたら片方の EdgeCluster が持っているエッジの配列をもう片方にマージ(移動)する。
マージが発生しなくなるまで繰り返す
小さなクラスタ同士をちょっとずつくっつけていって、それ以上くっつかなくなったら終わり、みたいなイメージですね。オレオレなのでもっと良い方法があるかもしれません。
最初は組み合わせの列挙をベタに二重ループで書いていましたが、
Array#combination
で書けると気づいて書き直しました。便利。
Array#combination (Ruby 2.7.0 リファレンスマニュアル)
https://docs.ruby-lang.org/ja/latest/method/Array/i/combination.html
2つのエッジがつながっているかどうかを判断するにはノードが重なっているかを見てあげれば OK。
# class EdgeCluster def connected_to?(other) all_node_set.intersect?(other.all_node_set) end
以上で子回路ごとに分割する処理ができました。
あとは、
ChildCircuit.create
が単体の ChildCircuit ではなく
ChildCircuit の配列を返すようになったので、それに合わせて
preprocess.rb
や main.rb
を修正すればできあがりです。
大丈夫ですね、動いてますね!
以下の iframe で実際に試せます (※マウス操作のできないスマホ・タブレットは非対応)。
こちらも同じものです。
https://sonota88.github.io/kairo-gokko/pages/18/index.html
ちなみに、「つながっている」「つながっていない」と適当に言ってきましたが、グラフ理論の用語では「連結」「非連結」と言うそうです。
今回の分割処理は、「非連結な1つのグラフから複数の連結なグラフへの変換」と言える……んでしょうか?(この表現で合ってます?)。 間違ってたらごめんなさい。
(2021-05-26 追記: ここで子回路と呼んでいるものはグラフ理論の用語では「連結成分」に相当するようです)