generated at
自動勉強会 vol.0 ペイントツール作製技術 前半

課題(やるかどうかは自由)
ペイントツール作ったことないけど話に参加したい人用の入門セットとして、課題を2つ作ってあります。やってから参加すると話の内容が入ってきやすいかもです
ペイントツール課題の実装例回答 by hashrock
シンプルな線を描く
this.old = { x: ev.offsetX, y: ev.offsetY } やるやる…… D
Vue2系
GUI初心者なのでナニモワカラナイyamanoku
マウス座標のとり方を毎回調べ直しているmiyanokomiya
毛筆風ブラシ
単純にlineWidthを増減させてたのか、なるほどmiyamonzdaiiz
マウス離してもdrawが続くのが描きづらい
これをなんとかしようとするといい感じに細くする部分の実装が工夫が要りそう
マウス離したらN秒間かけてだんだん細くなってゆく仕組みにするとどうなのかな mactkg
動かせば描かれて、動かなかったらそのままみたいな感じにすると、うまくはらい(?)が作れるかな
妄想が広がる
マウスイベントの時間を測り始めると実装が辛くなってくる...miyanokomiya
https://perfect-freehand-example.vercel.app/ だと常にペン先は細くなってる。
毛筆というよりインクペンぽくなったmiyamonz
解けなかったdaiiz
ペイントツール実装技術(1時間経過済)
線を引く・ブラシの実装
曲線補間 ( Spline / B-Spline / Catmull-Rom / ... )
seanchas_tは3次B-Spline使いがち (打った点を制御点に)
2点から何点でもOK

手ブレ補正が効く
Bezierに変換できる
便利!odiak
打った点をそのまま制御点にするのか〜
描いたパスの要所要所が尊重されるか、尊重されないか、というので違いがありますよね(何がなんだったか忘れた) mactkg
あ、制御点だ。制御点を通るかどうか
fabricjs-psbrushは三次スプライン曲線だったようです(と、昔の記事に書いてあった) mactkg
与えられた点列に近い、できるだけ少ない点で構成されたベジェ曲線を返す関数
Kakeruを作るときに、フォークして作りましたが、アルゴリズムはよく理解してないですodiak
実装をほぼ変えずに、TypeScript化しただけ
通過点を通りつつ、いい感じの曲線になってくれる?
これもベジエに変換可能
3Dもいける!miyanokomiya
ベジエに変換して描く方法
InstagramのStoryのペンが描き心地良いのだけどなぜか描き心地がいいのだろうnagayama
インク溜まり(端点)
ポインターの滞留時間で見て重みつけたりするのだろうか
気になる
速度でペン幅が変化している
そこに線形じゃない変換をかけるといい感じになるのかなぁ
Google図形描画、SVG出力できるんだよね。あれ内部で何使ってるのか知らないけどベクターでやって
PNGやJPEG出力のときにラスターに変換してる
ラスターへの変換ってBlob経由してCanvasに転写してbase64にする感じでやったことはありますが、Googleさんはもっとスマートに??
パス単純化
PointerEventそのまま使ってベクタ化するとポイントが多すぎるので間引く
間引くと角が丸くなっちゃう問題
人物の髪とか
市販ソフトのベクターツールでも角がぬるい感じになってしまう
間引いてスムージング(quadratic curve にする)ことで線が滑らかになる効果もある
同時お絵描きツールを作ってみたときにポイントを間引ききつつ滑らかにするのにquadratic curve使いましたnagayama
角が取れなくてすごい(角度の変化量が多いところは残る)
Wikipediaの絵がわかりやすかった mactkgseanchas_todiak
こういうgifすきmiyanokomiyamactkg
割とヒューリスティックmiyamonz
計算量も抑えつつできそう
パスフィッティング (書いた線をベジエなどに単純化)
An algorithm for automatically fitting digitized curves (Paper.jsのやつ http://paperjs.org/examples/path-simplification/ )seanchas_t
インターセクション
懺悔します、ラスターでやってCanvasのcompositeに丸投げしました
ベジェ曲線同士の交差を検知する時は無数の直線に分割して直線同士の交差を探すようにしたり…
あ、まさにそれと同じことをしました!!odiak
ベジェ曲線をN分割してポリラインにして、で交差判定をしました
SVGのブーリアン、Paper.jsのコアにあるぞって書かれてますね
課題1の勢いよく書いても線がカクカクしないバージョン
getCoalescedEventsで中間点を補えるのでなめらかな曲線になる
下図の赤い点が補助点として配列で得られる
PointerEventsのPolyfillっぽいけど、ブラウザ間のバグ対応とかも丁寧にされているっぽい
pressure の値も補完?してくれるとうれしい…arcatdmz
わかる...daiiz
これブラウザでサポートしてるのすごい! mactkgseanchas_tnagayamamiyamonzhanak1amiyanokomiya
iOSだと、補完したデータがApple Pencilで取れたりするんですが、それと一緒だ(アプリの話)
そういうのもあって、最近iPadのネイティブアプリ作りたくなってますodiak
お絵かきソフト以外で需要あるのかこれ……(便利) hanak1a
ジェスチャーとか?odiak
あ〜hanak1a
Safari...
カスタムブラシ
テクスチャを使ったブラシ
水彩 (色混ぜ/水彩境界…)
色混ぜ→1点1点に分割して、その点で平均色を取ってブラシの色と混ぜ合わせる、はやったことあるseanchas_t
この映像制作に使いました: https://baku89.com/work/cts
シェーダーなんですね面白いー
プログラマブルにブラシを作ることができる
アニメ機能もついてる。UIがめちゃめちゃよくできていてすごい mactkg
普通のUI部分がとてもきれい
消しゴム実装されている!
やばいすごいmiyanokomiyamiyamonzdaiiz
ばくさんだ!!
ただ直線を引く
canvasに頼らない話
アンチエイリアスがかかるので塗りつぶしの実装で不都合
ブレゼンハムのアルゴリズムや、独自の実装について
ビットマップの点を全部舐めて、線分から一定距離にある点を全部塗ると、太い線が描けるodiak
処理速度的にも大丈夫そう!hata6502
点と点の距離が短いので!odiak
これは使えなかったって感じだったのかな? imageSmoothingEnabled mactkg
目的が違う。画像を貼り付けるときにピクセル補完をするかというもの
アウトラインに線引いてから中身を埋めるとかできる?miyanokomiya
多角形で輪郭を描いて、ライン単位で塗りつぶす、でやってます
それもありかも
いや、でも下に絵が描いてあると塗りつぶし大変そう
1ドットずつ fillRect してる()
無茶実装感hata6502
https://mattdesl.svbtle.com/drawing-lines-is-hard OpenGLは太い線を描くときにポリゴンにしないといけないので大変 seanchas_t
ポリゴン(3D)じゃん
太さの変わる曲線も同じ
N-gonを三角形面に分割する処理だ……D
今度は逆方向の難しさ mactkg
この辺わりと3Dの知見がD
パフォーマンス
React+SVGのパフォーマンスが悪かったので、Canvasで再実装した人ですodiak
canvasも、ペンで描いている間はできるだけ再描画しないとかの工夫をした
VDOM+SVGだけでも会が開けそうmiyanokomiya
requestAnimationFrameとVueやReactを合わせて使うのって関数内でVuex.Storeのgetterからデータ貰ってきて毎フレーム描画とかしちゃう感じなんでしょうか
描画バックエンドをCanvasにしておいて、UIをVDOMでやりました
バウンディングボックスの四角は<div>のborderです

これの曲線がCanvasで描画してて、制御点とかのUIがVueですね
Kakeruもそれです!odiak
Griffith Sketchも同じmactkg
Figmaもそんな感じだったようなnagayama
Figmaはなんか異常に頑張ってますよね.... mactkg
右クリックして出てくるContext Menuとかは自作してそうな気がしている
SVGのRectではだめなんです?
foreignObjectでUI作ろうとしたけど素のhtmlと比べて挙動が微妙だったmiyanokomiya
foreignObjectのheightを越えられず描画が途切れる という経験
見えなくしたhtml側で描画しておいて高さを測ってsvg側に適用しましょ?(力技)
やっぱこれしかないのか…miyamonz
imgタグで表示しているときにはforeignObject内部のリソースが読み込まれないことを利用した画像+動画
⇡ 新しいタブで開くと動画になる
外部リソースの読み込み制限回避のためにBase64にするのあるあるD
その他のネタ: /daiiz/foreignObject
foreignObjectの面白さは一通り体験できた daiiz
ここでreact-pixi-fiberとかの出番か…!? https://github.com/michalochman/react-pixi-fiber
レイヤーたくさん作ったらメモリが足りなくて大変だった時があった mactkg
レイヤータイリングして描いてない部分は確保しないようにしたいけど大変そうseanchas_t
Fabric.js はオブジェクトごとにbounding boxを計算して持っていて、描画キャッシュはbounding boxサイズで作られるので、レイヤー(fabric.Group)をいっぱい作っても、ラスタみたいに全画面サイズ×レイヤー数のメモリを奪われることはないんですよね arcatdmz
それでもストロークごとに fabric.Path みたいなオブジェクトを生成しているとメモリが足りなくなったりします arcatdmz
上で mactkg が書いてる問題は、たしか retina ディスプレイとかでキャッシュの解像度を調整する部分があって、そのあたりのせいだったような… arcatdmz
canvasが多くなりすぎるのもダメなのか…?
とくにmobile safari なんかのCanvasのメモリ制限がしょっぱい
デカすぎんだろ…(テニヌ)
width=0,height=0設定しないと死なないのかなり罠だ…hanak1a
canvas自体をv-ifで消してあとで再出現させたりすると、getContext('2D')からやり直しになってまう問題

Mobile Safariは200MBちょっとしかメモリがなかった
みんなとにかく色々なことと戦っていてすごいnagayama
組み込みシステムだとメモリ厳しくて undo 回数に制限をかけたことがあるhata6502
cで書く(wasm)
やっぱり速いですか?miyanokomiya
デスクトップアプリと遜色ない速度になっている
js>wasmでデータ渡すところがネックになる場合があるって聞いたことはありますね
Rust でも、wasm が絡むとデータ転送かなり難しい……hata6502
js>wasmは座標データだけ渡して、wasm>jsは更新矩形だけ渡す
1フレームで更新がかかる範囲は局所的なので転送コストがボトルネックになることはない
状態(今のツールとか)ってどこに持ってるんですか?
wasm側はコア+内部的なビットマップとしてのキャンバス
VuexやReduxのstoreってコト…!?
いかに独立した処理として切り出すかが難しそうodiak
8bit paint webはA4 600dpiで持っている。えんぴつチャットはA3 600dpi
A4 600dpi !?D
どいうこと??
4961 x 7016

ペイントツールというテーマが総合格闘技すぎる...!miyanokomiya D