この記事は グラフィックス全般 Advent Calendar 2024 11日目の記事です。
どうも。個人でElectron&Webなお絵かきアプリ)を作っていたりいなかったりする者です。こりずに続けています。
背景
現在作成しているお絵描きアプリはいわゆるベクター系です。
つまり、絵をラスターデータ(画素データ)で保持するのではなく、点の座標とか線の太さとかいったベクトルデータで保持する形式です。
ベクトルデータで持ち続ける関係で面倒なことの一つが、マウスやペンにより入力された座標をそのまま使うとデータが多くなりすぎたり、描画結果が美しくなくなったりしがちなことです。データが多くなるのはメモリ容量の問題だけでなく、後から編集しやすいというベクトルデータの良さも生かせなくなってしまいます。
自作のアプリでも、できるだけストロークを美しくかつデータ少なく入力できるようにしたいので、今まで適当だったアルゴリズムを改善することにしました。
この記事では、自分なりに考えたアルゴリズムのまとめとしてアウトプットしたいと思います。
線の入力アルゴリズム1-間引き処理
マウスで小さく「あ」と描くと、下の画像ような離散的な座標データが得られます。点が格子状に並んでいるのは、画面の1ピクセルより細かいマウスの位置が取れないからです。
拡大しているので極端に見えますが、ガクガクして見えますね。また、隣接した点がこれほどの数がなくとも、ストロークは十分表現できるはずです。これを改善するのがアルゴリズム1の間引き処理です。
やることは単純で、一つ一つ入力されてくる座標が入力済みの最後の点から一定以上離れた座標になったときストロークの点として採用するだけです。
すると下のような感じで一定間隔で間引きされた状態になります。どんな間隔で間引くのが最適かはアプリによると思います。自作のアプリの場合は、基本は自分の好み(!)で決めて、それを画面のズームの状態に合わせて調節するようにしました。
線の入力アルゴリズム2-角でストロークを分割
自作のアプリではストロークは描画時にベジエ曲線で補間して滑らかに表示するようにしています。ところが、ストロークに角になる部分があると、ベジエ曲線で補間されて下の画像のようにモニョッとした見た目になります。
これは描画側では解決しづらいし、繋がっていないほうが後で編集しやすいので、入力の段階でストロークを角で分割するようにしました。これがアルゴリズム2です。
ところで、角には二種類あります。
一つ目は画像の左のように、角になる点が一つだけある場合です。これは点のところでストロークが曲がる角度を計算すれば簡単に検出できます。ただし、アルゴリズム1を適用した後でないと不要な角がたくさん検出されてしまうので注意が必要です。
二つ目は、画像の右のように、隣接する二つの点で角になっている場合です。この場合というのは、実はアルゴリズム1を適用することで頻繁に発生するようになります。しかも、アルゴリズム1を適用した後だと厳密にこれが角なのかどうか判定するのは、周囲の点も見ないといけないので結構面倒です。そこで今回は、直角に近い角だけを検出することにして(比較的)単純なアルゴリズムで検出することにします。具体的には、二つの点の曲がり角度を足すと90度に近く、かつ二つの点の曲がり角度が同じくらいの場合を検出すれば、ちょうど画像の右のような形を検出できます。
そして今回はおまけで、二つの点で角になっている場合にかぎり、角に隣接する線分の延長線上にできる交点を求めることで、より正確な角の位置を計算する機能も追加で実装します。
実装結果サンプル
見やすいようにかなり拡大して表示しています。
See the Pen Untitled by 柏崎ワロタロ (@warotarock) on CodePen.
おわりに
実行していただけるとお分かりになると思いますが、鋭角が入力しづらいとか、まだまだできることがたくさんあります。ストロークをなめらかにするスムージング処理とか、自作アプリではまだ色々細かいことをやっています。それらは今後の課題(記事を書くという課題&もっと改善する課題)にしたいと思います。
まだまだアドベントカレンダーは続きます。引き続きよろしくお願いいたします!