以前、JavaでStrokeを変更してお絵かきするとプルプルする現象の対策で検討したことですが、もうちょっとちゃんとした実装をすべきだと思ったので、再度検討します。
そもそも、プルプルとはどういうことかから、簡単に書いておきます。
Graphics2D クラスの setStroke メソッドを用いると、描画の線の太さを指定することができます。
しかし、ペンの形状が正方形のままで太さを変更し、drawLine メソッドで線を描くと、
線の始点と終点を結ぶように、勝手に斜めの長方形が描かれてしまいます。
単に長方形を描くだけの場合は問題が無いのですが、マウスでお絵かきする場合には
斜め長方形と真っ直ぐの長方形が高速で交互に入れ替わり、プルプルしてしまう問題が発生します。
元となるソースコードは、以下のようなものです。
int mouseX, mouseY, mouseDragX, mouseDragY; BufferedImage bufImage; private void MousePressed(java.awt.event.MouseEvent evt) { mouseX = evt.getX(); mouseY = evt.getY(); } private void MouseDragged(java.awt.event.MouseEvent evt) { mouseDragX = evt.getX(); mouseDragY = evt.getY(); Graphics2D g = bufImage.createGraphics(); g.setStroke(new BasicStroke(50)); g.setColor(Color.black); g.drawLine(mouseX, mouseY, mouseDragX, mouseDragY); // bufImage をラベル等に貼り付ける処理(省略) mouseX = mouseDragX; mouseY = mouseDragY; }
マウスがプレスされた瞬間の座標を記憶し、ドラッグ操作が行われるたび、drawLine で線を描くというものです。
線の太さを 50 にして実際に実行してみ結果は、以下のとおり。
静止画では少しわかりづらいですが、プルプルしながらこのような変な線が描かれてしまいます。
そこで、まずは以下のように線ではなく点で描画するコードにしてみます。
int mouseDragX, mouseDragY; BufferedImage bufImage; private void MouseDragged(java.awt.event.MouseEvent evt) { mouseDragX = evt.getX(); mouseDragY = evt.getY(); Graphics2D g = bufImage.createGraphics(); g.setStroke(new BasicStroke(50)); g.setColor(Color.black); g.drawLine(mouseDragX, mouseDragY, mouseDragX, mouseDragY); // bufImage をラベル等に貼り付ける処理(省略) }
このように、ドラッグ操作のたびに取得した座標に点を打てば、傾きの無い正方形が描画されます。
しかし実行すると、マウスを高速で動かせば動かすほど、飛び飛びの点が描画されてしまいます。
前回はここで、マウスの速度に応じて点か線かを分岐するプログラムを作成しましたが、
よくよく考えたら既存の線描画に相当するものを自作すれば済む話でした。
具体的には、次のようなコードを書きます。
int mouseX, mouseY, mouseDragX, mouseDragY; BufferedImage bufImage; private void MousePressed(java.awt.event.MouseEvent evt) { mouseX = evt.getX(); mouseY = evt.getY(); } private void MouseDragged(java.awt.event.MouseEvent evt) { mouseDragX = evt.getX(); mouseDragY = evt.getY(); Graphics2D g = bufImage.createGraphics(); g.setStroke(new BasicStroke(50)); g.setColor(Color.black); int plotX, plotY; double dif = Math.hypot(mouseDragX-mouseX, mouseDragY-mouseY); double difX = (mouseDragX-mouseX) / dif; double difY = (mouseDragY-mouseY) / dif; for (int i = 0; i < (int) dif; i++) { plotX = mouseX + (int)(difX * i); plotY = mouseY + (int)(difY * i); g.drawLine(plotX, plotY, plotX, plotY); } g.drawLine(mouseDragX, mouseDragY, mouseDragX, mouseDragY); // bufImage をラベル等に貼り付ける処理(省略) mouseX = mouseDragX; mouseY = mouseDragY; }
まず 17 行目で、ドラッグの始点と終点の座標間の距離を計算します。
描画の最小単位はピクセルなので、この距離の回数だけ点の描画を行えば線が描画できることがわかります。
(例えば距離が 13.5 ピクセルであれば、14 回点の描画を行えば十分。)
18,19行目では、繰り返し点の描画を行う際の、X、Y座標の変化量を計算しています。
あとは打つべき座標を計算しながら、始点と終点の間を埋めるように点を描画していくだけです。
最後に、念のため終点だけ座標指定して打っています。
実行結果はこのとおり。
割りと理想的な正方形形状のペン描画ができるようになりました。