この記事は Web グラフィックス Advent Calendar 2022 の17目の記事です。(二日遅れ・・・)
自作ドローツールで線をはみ出さない塗りがしたい
筆者が個人で作っているElectronベースのドロー系ツールで線をはみ出さない塗りがしたいということで、前回まで色々とやってきました。
今回は、ついに最終回といいますか、現状の最終案をご紹介したいと思います。課題もありますが、いちおう自作ツールにも組み込んでみた方法です。
処理の概要
一口にはみ出さないといっても色々ですが、今回の方法では、ブラシの中心から見て線分によって遮蔽される部分をはみ出すと考えます。
ブラシは複数の線分で遮蔽される場合がありますので、どこが遮蔽されるのかを「遮蔽マップ」に書き込みます。遮蔽マップは、ブラシ中心を中心として、角度ごとに一番近い線分との距離を記録したものです。遮蔽されない角度はブラシ半径と等しい距離を書き込んでおきます。
なお角度の決め方もいろいろありますが、ここでは右方向をゼロとして反時計回りとしました。
遮蔽マップを作ったら、次にマスク画像を作成します。
色を塗るところを自前でコーディングすれば遮蔽マップだけでいいのですが、Canvas2Dの機能を活用したいのでマスクを使うことにしました。
ブラシのサイズの範囲について、遮蔽マップを参照しながらマスクを塗りつぶしていきます。たとえばブラシ中心からの相対位置が (x, y) だった場合、atan2(y, x) で角度を求めれば、遮蔽マップからその角度の一番近い線分までの距離が取得できます。(x, y)の位置がその距離よりも近ければ遮蔽されていないのでマスクを 1 にし、遠ければ 0 にします。
マスク画像ができたら、あとは普通にCanvas2Dでブラシを描画したあとglobalCompositeOperation = 'destination-in' でマスク画像を描画すれば、マスクが1の部分だけを残すことができます。これではみ出した部分が消えるので、はみ出さない塗りが実現できます。
実装したもの
See the Pen Canvas2D マスク処理によるはみ出さないブラシ塗りの実装実験 by 柏崎ワロタロ (@warotarock) on CodePen.
考察
この実験では1回の描画で1回だけ処理をすればよいのでそれほど重くないのですが、ツールではけっこう膨大な量のブラシの点があるので、現実的な処理時間で実現できるのか問題でした。
それは実際にツールに組み込んでみないと分からないところがあったのでツールで実装してみました。
結果、どうだったかというと・・・
すごく重い。
・゚(゜´Д`゜)゚・。
線分が1000くらいあって、遮蔽マップで計算する角度が300あって、ブラシの点が1000くらいあるということが今作っているツールでは普通にあるので、ベタに実装すると1000×300×1000の繰り返し回数になってしまいます。そのため、ちょっとした絵でもものすごく重たくなります。
ま、まあでも、高速化というか必要なところだけを計算するような作りにはしていけると思いますので、しばらくこれでツールを作ってくことにしました。
まとめ
- 遮蔽マップとマスクによる線をはみ出さないブラシ塗りを実装してみた
- マスクを利用することでCanvas2Dの機能を活用できる
- そのままツールに組み込むと激重いので工夫が必要
- 動いたときは嬉しかった
ひとまず、今年のアドベントカレンダーでテーマだったブラシ塗りはこれで完結です。あとは残りの日のどこかで作成中のツールの紹介をできたらと思っております。