適当に描いた直線の部分の重複をなくして整えます。
[before]
[after]
上図の [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 モジュールを追加
- 部品ぽいものはいったんここに置くことに
- とりあえず
Point
とWireFragment
を追加
- 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 に分割
- 隣接するセル間を結ぶ線に対応するのが
WireFragment
- 分割と一緒に重複の除去もここでやっています。
Set
におまかせ。- そのために
WireFragment
に#hash
と#eql?
を追加
- そのために
Enumerable#minmax
を初めて使いました。べんり。
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 ) }
というわけで、上で貼った画像と同じものですがこのようになりました。 (赤い点は区切りが見えるように別途描き加えたものです)
*1: この書き方だと Kernel.Point にはなってませんが