ProcessingのcurveVertex()をAS3で(2)
ということで前回の続き。
Catmull-Romスプライン曲線を描くためには、4つ以上制御点がいるので、curveVertex()の回数が4回目以上になったら、ラインを引くようにすればいい。
前回のsplineTo()もちょっと係数の辺りを修正した。
public function curveVertex( x:Number, y:Number ):void
{
var p:Point = new Point(x, y);
__splineVertex[__splineVertexCount] = p;
__splineVertexCount++;
if (__splineVertexCount >= 4) {
var idx0:int = __splineVertexCount - 4;
var idx1:int = __splineVertexCount - 3;
var idx2:int = __splineVertexCount - 2;
var p2:Point = __splineVertex[idx1];
this.graphics.moveTo(p2.x, p2.y);
splineTo(__splineVertex[idx0], p2, __splineVertex[idx2], p);
}
}
public function splineTo( p0:Point, p1:Point, p2:Point, p3:Point ):void
{
var t:Number = 0;
var k:Number = 1.0 / splineSegments;
for (var i:uint = 1; i <= splineSegments; i++) {
t = i * k;
lineTo(
catmullRom(p0.x, p1.x, p2.x, p3.x, t),
catmullRom(p0.y, p1.y, p2.y, p3.y, t)
);
}
}
curveVertex()を呼ぶたびに、__splineVertex[]にPointとしてプッシュ。
で、4回目以降ならsplineTo()で曲線を描画。
実際にProcessingと同じ見た目になるかどうかのテスト。
リファレンスの例をやってみた。
package
{
import flash.display.Sprite;
import flash.display.Graphics;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.geom.Point;
import flash.events.Event;
[SWF(backgroundColor = 0xffffff)]
public class Main extends Sprite
{
private var g:Graphics;
private var __splineVertex:Array;
private var __splineVertexCount:uint;
public var splineSegments:uint = 20;
public function Main()
{
if (stage) {
initialize();
} else {
addEventListener(Event.ADDED_TO_STAGE, initialize);
}
}
private function initialize(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, initialize);
// main process
g = this.graphics;
begin();
curveVertex(84, 91);
curveVertex(84, 91);
curveVertex(68, 19);
curveVertex(21, 17);
curveVertex(32, 100);
curveVertex(32, 100);
end();
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
}
private function begin( thickness:Number=1, color:uint=0x000000 ):void
{
__splineVertex = [];
__splineVertexCount = 0;
g.lineStyle(thickness, color);
}
private function end():void
{
__splineVertexCount = 0;
}
private function curveVertex( x:Number, y:Number ):void
{
var p:Point = new Point(x, y);
__splineVertex[__splineVertexCount] = p;
__splineVertexCount++;
if (__splineVertexCount >= 4) {
var idx0:int = __splineVertexCount - 4;
var idx1:int = __splineVertexCount - 3;
var idx2:int = __splineVertexCount - 2;
var p2:Point = __splineVertex[idx1];
g.moveTo(p2.x, p2.y);
splineTo(__splineVertex[idx0], p2, __splineVertex[idx2], p);
}
}
private function splineTo( p0:Point, p1:Point, p2:Point, p3:Point ):void
{
var t:Number = 0;
var k:Number = 1.0 / splineSegments;
for (var i:uint = 1; i <= splineSegments; i++) {
t = i * k;
g.lineTo(
catmullRom(p0.x, p1.x, p2.x, p3.x, t),
catmullRom(p0.y, p1.y, p2.y, p3.y, t)
);
}
}
private function catmullRom( p0:Number, p1:Number, p2:Number, p3:Number, t:Number ):Number
{
var v0:Number = (p2 - p0) * 0.5;
var v1:Number = (p3 - p1) * 0.5;
return (2*p1 - 2*p2 + v0 + v1)*t*t*t + (-3*p1 + 3*p2 - 2*v0 - v1)*t*t + v0*t + p1;
}
}
}
うん、リファレンスの絵と全く同じ。
思ったよりも長くなったなー。
とはいえ、これでいい感じに曲線が描けるようになりました。
Graphicsクラスを拡張して汎用的に使えるようにしようっと。