ProcessingのcurveVertex()をAS3で(2)

posted by cheesepie on 2009.05.12, under actionscript3
12th

ということで前回の続き。

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クラスを拡張して汎用的に使えるようにしようっと。

ProcessingのcurveVertex()をAS3で(1)

posted by cheesepie on 2009.05.08, under actionscript3
08th

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)

MacVimでAS3のシンタックスを設定する

posted by cheesepie on 2009.04.12, under actionscript3, vim
12th

さて、MacVimに乗り換えたわけですが、どうもActionScript3のシンタックスが効いてない。
Vimでは効いてるのに・・!

というわけで、MacVimで「set filetype」するときは、省略せずにちゃんと書きましょうということみたい。

autocmd BufNewFile,BufRead *.as set filetype=actionscript

Syntaxファイルはこちらからもらってきてます。

ついでなんでちょっと便利Tipsを。
よくVImでファイル編集してて、文字コードや改行コード変えたいなとか文字コード変えて開き直したいとかあると思うんですけど、そんなときはこんな感じにショートカットキーを割り当てとくと便利です。

" file encoding
nmap ,U :set fileencoding=utf-8<CR>
nmap ,E :set fileencoding=euc-jp<<R>
nmap ,S :set fileencoding=cp932<CR>

"reload file with given file encoding
nmap =U :e ++enc=utf8<CR>
nmap =S :e ++enc=sjis<CR>
nmap =E :e ++enc=euc-jp<CR>
nmap =J :e ++enc=iso-2022-jp<CR>

" file format
nmap ,W :set ff=dos<CR>
nmap ,L :set ff=unix<CR>

こんな感じにしとくと、コマンドモードで「=U」でUTF-8で開き直したりできて便利です。
ってVim使いの人ならこのくらい当たり前か。。。

pagetop