私は描画アプリケーションの構築を検討しており、通常の線と破線を実行できましたが、蛇行とも呼ばれる「波状」の線が必要でした。AS3 でこれを達成する方法について何か考えはありますか?
更新:回答されたコードは質問に答えますが、よりフリーハンドの蛇になりたいと思っていたので、マウスを動かすと以下のようなものを描くことができます(ただし、波を通る線は必要ありません)。
私は描画アプリケーションの構築を検討しており、通常の線と破線を実行できましたが、蛇行とも呼ばれる「波状」の線が必要でした。AS3 でこれを達成する方法について何か考えはありますか?
更新:回答されたコードは質問に答えますが、よりフリーハンドの蛇になりたいと思っていたので、マウスを動かすと以下のようなものを描くことができます(ただし、波を通る線は必要ありません)。
ベイザー曲線を使用することもできますが、最も簡単な方法はおそらく線分を描くことです。これが実際の例です:
package
{
import flash.display.*;
import flash.events.Event;
import flash.geom.*;
public class Main extends Sprite {
public function Main():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
this.graphics.lineStyle(2, 0x0, 0.5);
// step = 1 creates a high-quality wave
drawSerpentLine(this.graphics, new Point(50, 50), new Point(150, 250), 4, 20, 1);
// step = 8 and the wave is not as smooth
drawSerpentLine(this.graphics, new Point(300, 30), new Point(233, 150), 6, 10, 8);
}
// takes a graphics reference, and draws a serpent line between the two
// specified points using the g's current lineStyle
// frequency: determines how many waves
// amplitude: how "much" wave
// step: the quality (lower means smoother lines at the cost of speed)
private function drawSerpentLine(g : Graphics, from : Point, to : Point, frequency : int = 5, amplitude : int = 20, step : int = 2):void {
// the angle between the two points
var ang : Number = Math.atan2(to.y - from.y, to.x - from.x);
// the distance between the points
var dis : Number = Point.distance(from, to);
// a point which we use to store the current position to draw
var currPoint : Point = new Point(from.x, from.y);
for (var i:int = 0; i <= dis; i += step) {
// how far away (perpendicularly) from the straight lines the current points should be
var waveOffsetLength : Number = Math.sin((i / dis) * Math.PI * frequency) * amplitude;
// calculate the current point.
currPoint.x = from.x + Math.cos(ang) * i + Math.cos(ang - Math.PI/2)*waveOffsetLength;
currPoint.y = from.y + Math.sin(ang) * i + Math.sin(ang - Math.PI/2)*waveOffsetLength;
if (i > 0) {
this.graphics.lineTo(currPoint.x, currPoint.y);
} else {
this.graphics.moveTo(currPoint.x, currPoint.y);
}
}
// close the last line so we end up at the end point
this.graphics.lineTo(to.x, to.y);
}
}
}
アップデート:
これは、フリーハンドで描画する修正版です。アプローチは似ています。マウスを動かすたびに、2 点間に蛇を描きます。ただし、蛇の「フェーズ」を変数に格納して、別の drawSerpentLine() 呼び出しが最後の呼び出しのフェーズで継続されるようにする必要があります。また、最後のマウス座標と現在のマウス座標の間に単純に蛇を描くことはできません。滑らかな外観を作成できないからです。したがって、座標を平均化します。
package
{
import flash.display.*;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.*;
public class Main extends Sprite {
private var m_mouseIsDown : Boolean;
private var m_lastPoint : Point = new Point();
private var m_currPoint : Point = new Point();
private var m_phase : Number = 0;
private var m_firstDrawAfterMouseDown : Boolean;
public function Main():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
this.graphics.lineStyle(2, 0x0, 0.5);
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseEvent);
this.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseEvent);
this.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseEvent);
}
private function drawSerpentLine(g : Graphics, from : Point, to : Point, frequency : Number = 0.15, amplitude : int = 6, step : int = 1):void {
// the angle between the two points
var ang : Number = Math.atan2(to.y - from.y, to.x - from.x);
// the distance between the points
var dis : Number = Point.distance(from, to);
for (var i:int = 0; i <= dis; i += step) {
m_phase += frequency;
// how far away (perpendicularly) from the straight lines the current points should be
var waveOffsetLength : Number = Math.sin(m_phase) * amplitude;
// calculate the current point.
var x : Number = from.x + Math.cos(ang) * i + Math.cos(ang - Math.PI/2)*waveOffsetLength;
var y : Number = from.y + Math.sin(ang) * i + Math.sin(ang - Math.PI/2)*waveOffsetLength;
if (m_firstDrawAfterMouseDown) {
this.graphics.moveTo(x, y);
m_firstDrawAfterMouseDown = false;
} else {
this.graphics.lineTo(x, y);
}
}
}
private function onMouseEvent(event : MouseEvent):void {
switch(event.type) {
case MouseEvent.MOUSE_DOWN:
m_mouseIsDown = m_firstDrawAfterMouseDown = true;
m_currPoint.x = m_lastPoint.x = this.mouseX;
m_currPoint.y = m_lastPoint.y = this.mouseY;
break;
case MouseEvent.MOUSE_MOVE:
if (m_mouseIsDown) {
// to create a smoother look we average the mouse coords
// where only 10% of the new mouse position is used, 90% is the old position
// the lower the first percentage the smoother the look, but the more
// the serpent will lag behind the actual mouse position (until you release the mouse)
m_currPoint.x = this.mouseX * 0.1 + m_lastPoint.x * 0.9;
m_currPoint.y = this.mouseY * 0.1 + m_lastPoint.y * 0.9;
drawSerpentLine(this.graphics, m_lastPoint, m_currPoint);
m_lastPoint.x = m_currPoint.x;
m_lastPoint.y = m_currPoint.y;
}
break;
case MouseEvent.MOUSE_UP:
m_mouseIsDown = false;
// when the user releases, we complete the serpent
// by drawing the full distance to the current position
// (no percentages like in mouse move)
m_currPoint.x = this.mouseX;
m_currPoint.y = this.mouseY;
drawSerpentLine(this.graphics, m_lastPoint, m_currPoint);
break;
}
}
}
}