大分意味不明なタイトルですが、Javaでお絵かきソフトを作る際に少し躓いたところの対策メモです。
まず、MousePressed メソッドと MouseDragged メソッドに、以下のような絵かき処理のコードを書きました。
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; }
マウスがプレスされた瞬間に現在座標を取得し、ドラッグのたびに直線を結び、お絵かきする処理となっています。
ここでは setStroke メソッドを用いることで、線の太さを 50 としています。
一見特に問題無いように思えますが、これを実行してお絵かきしてみると、次のようになります。
ペン形状が正方形の線をマウスで引くと、角度が少し変化する度に正方形の向きが変化するため、プルプルした変な絵になってしまいます。
なんだか気持ちが悪いので、完全なものではないですが修正していきます。
まず、2点間を直線で結ばず、点の描画を連続で行うと、次のようになります。
これだけでも大分滑らかな曲線になりますが、これではマウスのカーソルを動かすスピードが早くなると、飛び飛びの点になってしまいます。
そもそも、点が飛び飛びになるのを防ぐために2点を結んでいたので、この方法ではいけません。
しかしながら、この滑らかさはなかなか捨てがたいので、2点間の距離が大きく空いたときだけ直線で結び、
そうでない場合は点を描けばいいのではと思い、drawLine を以下のように実装しました。
if (Math.hypot(mouseDragX-mouseX, mouseDragY-mouseY) > 10) g.drawLine(mouseX, mouseY, mouseDragX, mouseDragY); else g.drawLine(mouseDragX, mouseDragY, mouseDragX, mouseDragY);
Math.hypot で2点間の距離を計算し、ある数字以上(ここでは適当に 10 としていますが、これはストロークによって変化させなければいけません)の場合は直線を、そうでない場合は点を描くというようにしています。
実行結果は次のようになります。
完璧とは言い難いですが、大分マシにはなりました。
(ここでは hypot を使っていますが、ここには平方根を求める演算が含まれるため、距離の2乗で比較をしたほうが計算コストが軽くなります。)
また、正方形のペン形状に拘らないのであれば、以下のようにペン形状を円形にすることで、綺麗に曲線を描くことができます。
g.setStroke(new BasicStroke(50, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER));