kairo-gokko (3) データの整形(重複した線の処理)



適当に描いた直線の部分の重複をなくして整えます。

[before]

f:id:sonota88:20200212212835p:plain

[after]

f:id:sonota88:20200215121201p:plain

上図の [after] のように、隣接するセル間をつなぐ線をひとつの単位として、

  • (1) 長い線をこの単位になるようにバラす
  • (2) バラした短い線同士を比較して、同じ始点と終点を持っていたら重複とみなす

という方針でやってみました。

線の重複を認めているのは、細かいことを気にせず適当に作図したいからで、 その適当さをここでなんとかします。

(ちなみに、上図 [before] の角の部分のように線が繋がっていない場合も、両方の線の端が同じセルに入っていれば繋がっているとみなします。)

具体的な描画処理を Drawer クラスにカプセル化

  • 今は Plumo を使って描画していますが、後で他のものに置き換える予定なので
  • メソッド名、引数は DXRuby に揃える
    • 独自のものにすると覚えることが増えるので
  • あまり余計なことはせず基本的な描画命令にしておく
    • ただし、セル座標からピクセルへの変換は煩雑なので Drawer 内でやってしまう

レシーバが Window じゃない以外は見た目 DXRuby っぽい感じに:

  drawer.draw_line(
    wf.x1 + 0.5, wf.y1 + 0.5,
    wf.x2 + 0.5, wf.y2 + 0.5,
    C_WHITE
  )

グリッドを描画

  • 主に位置確認のため

LiboDraw::Line#tate? を追加

  • 後で使うので
  • 縦横は horizontal/vertical の方がかっこいいんでしょうけど、 tate/yoko の方が短いし分かりやすい(=間違えにくい)し、いいよね……という

Unit モジュールを追加

  • 部品ぽいものはいったんここに置くことに
  • とりあえず PointWireFragment を追加
- module Unit
  - class Point
  - class WireFragment

Point は頻用するので、毎回 Unit::Point.new(x, y) と書かなくてもいいように

def Point(x, y)
  Unit::Point.new(x, y)
end

としてみました。 今までこういう大文字で始まるメソッドを(しかもトップレベルに)作ったことがなかったので若干落ち着かない感じはしますね。

単に「面倒だから」というだけだと、 こういうショートカットを用意する 理由としては弱い気がしますが、 Kernel.Integer とかありますし、Integer とか Rational みたいなプリミティブな型の仲間という位置づけなら問題ないかなと *1

いや、そもそも完全に趣味のプログラムだし「動けばよし」で何も問題ないのですが……。

LiboDraw::Line を WireFragment に分割

def to_wire_fragments(lines)
  wfs = Set.new

  lines.each { |line|
    x1 = line.x1.floor
    y1 = line.y1.floor
    x2 = line.x2.floor
    y2 = line.y2.floor

    if line.tate?
      # tate
      x = x1
      y_min, y_max = [y1, y2].minmax

      (y_min...y_max).each { |y|
        wfs << Unit::WireFragment.new(
          Point(x, y    ),
          Point(x, y + 1)
        )
      }
    else # yoko
      x_min, x_max = [x1, x2].minmax
      y = y1

      (x_min...x_max).each { |x|
        wfs << Unit::WireFragment.new(
          Point(x    , y),
          Point(x + 1, y)
        )
      }
    end
  }

  wfs
end

これらを使って、LiboDraw::Line から WireFragment に変換して描画。

wfs = to_wire_fragments(doc.pages[0].lines)

wfs.each { |wf|
  drawer.draw_line(
    wf.x1 + 0.5, wf.y1 + 0.5,
    wf.x2 + 0.5, wf.y2 + 0.5,
    C_WHITE
  )
}

というわけで、上で貼った画像と同じものですがこのようになりました。 (赤い点は区切りが見えるように別途描き加えたものです)

f:id:sonota88:20200215121201p:plain



*1: この書き方だと Kernel.Point にはなってませんが