ProcessingのcurveVertex()をAS3で(1)

posted by cheesepie on 2009.05.08, under actionscript3
30th

Processingで描いたものをAS3に移植していて、curveVertex()で躓いた。
curveVertex()ってのは、スプライン曲線の点を追加するメソッドで、4つ点を決めてbeginShape()とendShape()で囲むだけでスプライン曲線を描いてくれるスグレもの。

これをActionScript3でやろうとしたところ、メチャ大変だった。
自分が曲線のアルゴリズムを全く知らなかったせいもあるけど。

ちょっと調べてみたんですけど、スプライン曲線にもいろいろあるんですね。
っていうかスプライン曲線とベジエ曲線の違いをようやく理解した。
スプライン曲線は、曲線の断片をつなげて連続した曲線を作り、与えられた制御点はすべて通過する、というか通過点を指定する曲線。
ベジエ曲線は、制御点を利用して曲線を描くが、制御点を曲線が通らない、というか端点の接戦ベクトルを指定する曲線。

つまりスプライン曲線とベジエ曲線は別物。
そして、3次とか2次とかって、3次元とかのことじゃなくて、多項式が2次か3次かということだったのね。。。
とまあこんなレベルですorz

で、スプライン曲線でよく使われるのはCatmull-Romスプライン曲線と呼ばれるものらしい。
(調べてる途中で「B-スプライン曲線」というものを知ったのだけど、これとベジエ曲線って何が違うの???)

Catmull-Rom曲線については、こちらのページが詳しいです。

これをAS3でやると、こんな感じになる。

public 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;
}

※ tは「0~1」の値を取ります。

で、短い直線を描きまくって曲線っぽくしていく。
分割数は多すぎると重くなるので、20くらいがいいみたい。

とりあえず、一番少ない4点でスプライン曲線を描いてみた。

package
{
  import flash.display.Sprite;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.geom.Point;

  [SWF(backgroundColor = "0x000000", frameRate = "30")]
  public class Main extends Sprite
  {
    private var points:Array;
    private var num:uint = 4;
    private var seg: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);
      stage.scaleMode = StageScaleMode.NO_SCALE;
      stage.align = StageAlign.TOP_LEFT;

      // main process
      points = [];
      for(var i:uint = 0; i < num; i++) {
        var p:Point = new Point();
        p.x = Math.round(Math.random() * stage.stageWidth);
        p.y = Math.round(Math.random() * stage.stageHeight);
        points.push(p);
      }
      draw();
    }

    private function draw():void
    {
      graphics.clear();
      for(var i:uint = 0, len:uint = points.length; i < len - 3; i++) {
        var p0:Point = points[i];
        var p1:Point = points[i + 1];
        var p2:Point = points[i + 2];
        var p3:Point = points[i + 3];
        splineTo(p0, p1, p2, p3);
      }
    }

    private function splineTo(p0:Point, p1:Point, p2:Point, p3:Point):void
    {
      graphics.lineStyle(1, 0xffffff);
      graphics.moveTo(p1.x, p1.y);
      for(var i:uint = 0; i < seg; i++) {
        var t:Number = (i + 1) / seg;
        graphics.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;
    }
  }
}

眠いので今日はここまでにして、curveVertex()は明日実装しよ。。。
(やっぱり複雑な図形を描こうと思ったら、数学の知識が必要なんですねorz)

Comments are closed.

TrackBack URL :

pagetop