2008年4月19日土曜日

TigerでGAE

TigerでGAEのHelloWorldを動かす。

まず Google App Engine SDK をダウンロードして インストールを試みるが

python2.5がないといわれる。 /opt以下もチェックしてるようなので MacPortsでインストールしても大丈夫そうだ。

$ sudo port install python25

すると、ビルドできない。 調べるとXcodeをupgradeする必要があるらしい。

ADC にいって 3.0はLeopard用なので2.5をDownload。

Xcodeを2.5にupgradeしたら python25のビルドは成功。

そして、GAE SDKもインストールできた。

Hello, World! - Google App Engine - Google Code にしたがって helloworld.pyとapp.yamlをhelloworldフォルダにつくり

$ dev_appserver.py ../helloworld/

するが

Error: Python 2.3 is not supported. Please use version 2.5 or greater.

といわれる。

dev_appserver.pyは

$ which dev_appserver.py
/usr/local/bin/dev_appserver.py

にあって

$ head -1 /usr/local/bin/dev_appserver.py 
#!/usr/bin/env python

envでpythonを探してる。 MacPortsのpythonはどこへ?

/opt/local/binの下をみると /opt/local/bin/python2.5 と python2.5というファイル名でインストールされていた。

/opt 以下を汚したくないので

$ sudo ln -s /opt/local/bin/python2.5 /usr/local/bin/python

と /usr/local/binの下に pythonというファイル名でシンボリックリンクを作成。

dev_appserver.pyを起動すると

ImportError: No module named _md5

といわれる。 調べるとpy25_hashlibが必要らしい。

$ sudo port install py25-hashlib

してdev_appserver.pyを起動すると

AttributeError: 'module' object has no attribute 'HTTPSHandler'

といわれる。

こんどはpy25-socket-sslが必要みたい。

$ sudo port install py25-socket-ssl

して、起動成功。

http://localhost:8080/にアクセスして Hello, world! が表示された。

つまり、GAE SDKをTigerで動かすには

  • Xcodeを2.5にupgrade
  • MacPortsのpython25, py25-hashlib, py25-socket-ssl
  • MacPortsのpython2.5をpythonというファイル名でパスを通す

が必要。

2008年4月8日火曜日

pv3dのFace3D.renderを理解したい

pv3dではどうやってレンダリングしてるんだろう と調べていたら Face3Dにいきついた。

Face3D.renderに

var a1  :Number  = this._a;
var b1  :Number  = this._b;
var c1  :Number  = this._c;
var d1  :Number  = this._d;
var tx1 :Number = this._tx;
var ty1 :Number = this._ty;

var a2 :Number = x1 - x0;
var b2 :Number = y1 - y0;
var c2 :Number = x2 - x0;
var d2 :Number = y2 - y0;

var matrix :Matrix = _bitmapMatrix;
matrix.a = a1*a2 + b1*c2;
matrix.b = a1*b2 + b1*d2;
matrix.c = c1*a2 + d1*c2;
matrix.d = c1*b2 + d1*d2;
matrix.tx = tx1*a2 + ty1*c2 + x0;
matrix.ty = tx1*b2 + ty1*d2 + y0;

container.beginBitmapFill( texture, matrix, true, material.smooth );

という処理部分がある。

_a,_b,_c,_d,_tx,_ty は Face3D.transformUVであらかじめ計算されている。

x0,x1,x2,y0,y1,y2 は3つの頂点(vertices)のx,y座標。

で、ここは結局なにをしているかというと

     / a1  b1  0 \
M0 = | c1  d1  0 |
     \ tx1 ty1 1 /

     / a2  b2  0 \
M1 = | c2  d2  0 |
     \ x0  y0  1 /

とした場合

M0 * M1

を計算した結果を、beginBitmapFillの引数に渡しているだけだ。

この M0 * M1 は 3つの頂点からなる三角形にビットマップを塗るための 変形を意味しているのだろう。 3つの頂点がobjectのx,y,z座標によって変化することで 三角形は変形する。 その変形に合わせて塗るビットマップを変形するために指定するのが この行列ということだろう。

しかし このM0,M1は 何を意味しているのだろうか。

M0はFace3D.transformUVを見ると uvから行列をつくって それを逆行列に変換している。 つまり M0 * M1 は 実際には なにかしらの行列の逆行列を M1にかけていることになる。

どういうときに逆行列をかけるのか調べると 計算しやすいように 基本的な図形(例えば長方形なら正方形)に戻す というときに使われることがあるようだ。

もしそうならば M0は M1変換を行うための前準備にあたるのかもしれない。 そこで先にM1について考える。

三角形で基本的な図形というと P0(0, 0), P1(0, 1), P2(1, 0) の3点からなる直角二等辺三角形を 考えてみた。

     / a2  b2  0 \
M1 = | c2  d2  0 |
     \ x0  y0  1 /

を詳しくかくと

     / x1 - x0  y1 - y0  0 \
M1 = | x2 - x0  y2 - y0  0 |
     \   x0       y0     1 /

ということになる。 頂点P 3点にこのM1をかけるとどうなるか。

              / x1 - x0  y1 - y0  0 \
< 0  0  1 > * | x2 - x0  y2 - y0  0 |
              \   x0       y0     1 /
= < x0  y0  1 >

              / x1 - x0  y1 - y0  0 \
< 1  0  1 > * | x2 - x0  y2 - y0  0 |
              \   x0       y0     1 /
= < x1-x0+x0  y1-y0+y0  1 >
= < x1  y1  1 >

              / x1 - x0  y1 - y0  0 \
< 1  0  1 > * | x2 - x0  y2 - y0  0 |
              \   x0       y0     1 /
= < x2-x0+x0  y2-y0+y0  1 >
= < x2  y2  1 >

となる。 これはつまり M1は P0(0, 0), P1(0, 1), P2(1, 0) の3点からなる直角二等辺三角形を Q0(x0, y0), Q1(x1, y1) Q2(x2, y2) の任意の3点からなる三角形に変形する行列 ということになる。

では次に M0は任意の三角形を P0(0, 0), P1(0, 1), P2(1, 0) に戻す変形を行っているのだろうか。

M1の逆行列は Face3D.transformUVをみると

         / u1 - u0  v1 - v0  0 \
M0inv =  | u2 - u0  v2 - v0  0 |
         \   u0       v0     1 /

となっている。 (M1と同じ形だ)

u1,u2,u3 は 3つのuv (三角形の3つの頂点のu,v値) の uにbitmap.widthを vにbitmap.heightを それぞれかけたものだ。

var w  :Number = material.bitmap.width;
var h  :Number = material.bitmap.height;

var u0 :Number = uv[0].u * w;
var v0 :Number = uv[0].v * h;
var u1 :Number = uv[1].u * w;
var v1 :Number = uv[1].v * h;
var u2 :Number = uv[2].u * w;
var v2 :Number = uv[2].v * h;

u,vは0から1の値で それにwidth,heightをかけることで bitmapのどの位置とマップをとるかの bitmap上の座標を得ることができる。

で M1にM0invの逆行列をかけているということは 頂点P 3点からなる三角形に M0inv変換を行うと bitmap (厳密にはbitmapを三角形にきりだした部分か?) になる ということを期待していることになる。

ここで例えば 10x10のビットマップがあって それを対角線上に2つに分割し B0(0,0), B1(10,0), B2(0,10) からなる三角形を得た場合

それぞれのuvは B0uv(0,0), B1uv(1,0), B2uv(0,1) になるであろうから これらにwidth,heightをかけて M0invは M1と同じ形をしているので 頂点P 3点からなる直角二等辺三角形を R0(0,0),R1(10,0),R2(0,10) の3点からなる三角形へ変形する行列をあらわしていて この三角形は 切り出したビットマップに一致する。

なるほど。

つまり 逆にいうと uv値というのは 頂点P 3点からなる直角二等辺三角形から もとのビットマップから切り出した三角形へ 復元できるような値を表している ということか。

2008年4月4日金曜日

as2のTweenでY軸回転

まず thisのx, y, zがきまれば

function render() {
  var scale:Number = FOCAL_LENGTH
    / (Programs.FOCAL_LENGTH + this.z);
  this._xscale = this._yscale = scale * 100;
  this._x = VANISHING_POINT_X + (this.x * scale);
  this._y = VANISHING_POINT_Y + (this.y * scale);
  this.swapDepths(-this.z);
}

で疑似3Dに配置できる。

で Y軸回転した場合 yは変化しないので xとzさえもとめればよい。

xz座標の30度の位置に動かしたければ

var angleY:Number = 30 * Math.PI / 180;
this.x = RADIUS * Math.cos(angleY);
this.z = RADIUS * Math.sin(angleY);

でもとまる。

現在位置から30度移動したい場合は

var angleY:Number = 30 * Math.PI / 180;
var cosY:Number = Math.cos(angleY);
var sinY:Number = Math.sin(angleY);
var x1:Number = this.x * cosY - this.z * sinY;
var z1:Number = this.z * cosY + this.x * sinY;
this.x = x1;
this.z = z1;

となる。

これに Tweenを組み合わせる。

Tweenは なにかしらのプロパティを指定時間の間変化させてくれるので その変化させるプロパティを決めなければならない。 この場合は angleY。 で プロパティが変化するたびに onMotionChangedが呼ばれるので そのなかでrenderすればよい。

var angleY:Number = 30 * Math.PI / 180;
var tween:Tween = new Tween(this, 'angleY', Regular.easeInOut,
                            0, angleY, 0.5, true);
tween.onMotionChanged = Delegate.create(this,
                                        this.onMotionChanged);

onMotionChangedのなかは

function onMotionChanged(tween:Tween, pos:Number) {
  this.x = RADIUS * Math.cos(this.angleY);
  this.z = RADIUS * Math.sin(this.angleY);
  this.render();
}

となる。 これで0度から30度まで0.5秒かけて回転してくれる。

しかし たくさんのオブジェクトを移動したい場合 移動量が同じでも それぞれのオブジェクトにとって 初期角度と移動後の角度が異なるため この方法でTweenする場合 それぞれのオブジェクトにTweenを仕掛ける必要がある。

var angleY:Number = 30 * Math.PI / 180;
var cosY:Number = Math.cos(angleY);
var sinY:Number = Math.sin(angleY);
var x1:Number = this.x * cosY - this.z * sinY;
var z1:Number = this.z * cosY + this.x * sinY;
this.x = x1;
this.z = z1;

は 移動量を指定するタイプなので こちらがつかえれば Tweenはひとつで済む。

そこで this.objectsに 移動したいobjectが入っているとして

var angleY:Number = 30 * Math.PI / 180;
var tween:Tween = new Tween(this, 'dummy', Regular.easeInOut,
                            0, angleY, 0.5, true);
tween.onMotionChanged = Delegate.create(this,
                                        this.onMotionChanged);

としておいて onMotionChangedでは TweenのprevPosをつかって

function onMotionChanged(tween:Tween, pos:Number) {
  var delta:Number = pos - tween.prevPos;
  var cosY:Number = Math.cos(delta);
  var sinY:Number = Math.sin(delta);
  for (var i:Number=0; i<this.objects.length; i++) {
    var object = this.objects[i];
    var x1:Number = object.x * cosY - object.z * sinY;
    var z1:Number = object.z * cosY + object.x * sinY;
    object.x = x1;
    object.z = z1;
    object.render();
  }
}

とdelta(移動量)を求めて すべてのobjectsを同じ移動量だけ回転させることができる。

また、cosYとsinYはループにはいる前に決定できるので 効率がよい。

2008年3月27日木曜日

flex3のEffectをMXMLでSequence

as3でもflex3のeffectが使える。

import mx.effects.Move;

とすれば as3で

var move:Move = new Move();
move.play([this]);

とflexのEffectを利用できる。

しかし よく使うEffectはMXMLで

<mx:Move yBy="-1" duration="50"/>

と書いた方が楽だしきれいに思う。 ただし Effectの記述は純粋にViewの記述とは言いがたいので Effect用途毎に 別ファイルとして記述したい。

そこで

<mx:Move xmlns:mx="http://www.adobe.com/2006/mxml"
         suspendBackgroundProcessing="true"
         yBy="-1" duration="50"/>

という内容でHogeEffect.mxmlとして保存すれば as3では

var hoge:HogeEffect = new HogeEffect();
hoge.play([this]);

newできる。

しかし Sequenceだとうまくいかなかった。

<mx:Sequence xmlns:mx="http://www.adobe.com/2006/mxml"
             suspendBackgroundProcessing="true">
  <mx:Move yBy="-1" duration="50"/>
  <mx:Move yBy="+2" duration="100"/>
  <mx:Move yBy="-1" duration="50"/>
</mx:Sequence>

とMXMLを記述しても (正しいはずだが) 子Effectが再生されない。 as3上でchilrenをみても空になっていた。

試行錯誤した結果

<mx:Sequence xmlns:mx="http://www.adobe.com/2006/mxml"
             suspendBackgroundProcessing="true">
  <mx:children>
    <mx:Move yBy="-1" duration="50"/>
    <mx:Move yBy="+2" duration="100"/>
    <mx:Move yBy="-1" duration="50"/>
  </mx:children>
</mx:Sequence>

と きちんとmx:chilrenで囲むと 動いた。

2008年3月26日水曜日

as3の安易なカスタムイベント

as3でイベントを起動するには、通常

this.dispatchEvent(new Event(Event.COMPLETE));

のようにイベントタイプをクラス定数で渡すという お行儀の良い方法をとる。

でもas2のようにおきらくにカスタムイベントを使いたい。 で、

this.addEventListener('hoge', this.onHoge);

として

this.dispatchEvent(new Event('hoge'));

と任意の文字列でイベントタイプを渡すこともできるが イベントタイプの識別は文字列であって どのイベントクラスの定数かまでは考慮されないので

this.addEventListener('complete', this.onComplete);

とカスタムイベントをセットしているつもりでも イベントタイプがthisの組み込みのものと一致すると 予期しないタイミングで組み込みイベントEvent.COMPLETEが発生してしまい 危険。

とにかく 安全にカスタムイベントをセットするには 組み込みイベントタイプと一致しない文字列を指定しなければならない。

そこで

public class E extends Event {
  private static const PREFIX:String = 'easy_event_prefix_';

  public function E(type:String,
                      bubbles:Boolean=false,
                      cancelable:Boolean=false) {
    super(E.t(type), bubbles, cancelable);
  }

  public override function clone():Event {
    return new E(type, bubbles, cancelable);
  }

  public static function t(type:String):String {
    return E.PREFIX + type;
  }
}

のようにEventクラスを継承したEクラスなどを1つつくっておき

イベントの登録には

this.addEventListener(E.t('complete'), this.onComplete);

イベントの起動には

this.dispatchEvent(new E('complete'));

としておけば easy_event_prefix_という接頭辞がついたイベントタイプで 受け渡しが行われるので 組み込みイベントに反応することはない。

2008年3月22日土曜日

javascript.elと[:punct:]

javascript-modeで 行の先頭が$なんかのとき インデント1つ分ずれる。

function hoge() {
  fuga();
    $('hoge').empty();
}

これは、modify-syntax-entryかなと思ったら 効かず。 javascript-mode-hookで (setq c-echo-syntactic-information-p t) するもエコーされない。

javascript.elを読むと この人はindent-line-functionを cc-modeのをつかわず 自分で実装していた。

ずれる原因は

(let ((p (parse-partial-sexp (point-min) (point)))
      (brace-p (looking-at "[{}]"))
      (continued-expr-p (looking-at "[[:punct:]]")))

(looking-at "[[:punct:]]") の部分で 表示可能な記号文字が先頭のとき 余分にインデントされるようになっていた。

(let ((p (parse-partial-sexp (point-min) (point)))
      (brace-p (looking-at "[{}]"))
      (continued-expr-p (and (not (looking-at "['$_]"))
                             (looking-at "[[:punct:]]"))))

と['$_]のときはcontinued-expr-pと解釈されないようにして 修正。

2008年3月19日水曜日

emacsでphpのインデントがずれる

ずれる。

<?php
function hoge () {

  }
?>

となる。

php-mode.el cc-*.el を読んだり debugでトレースしたりして わかったのは <?php の部分が原因のようであること。

<?php<? にすれば ずれない。

前へさかのぼってサーチしていって phpがワードで その前の?がデリミタになって バッファの先頭までいってサーチが終了し サーチが終了したひとつ前にポイントがどこだったか でインデントが決まり <?php の3文字目がそれにあたるので それまでの2文字分インデントされてしまう ということらしい。

つまり、 関数宣言部分が 本来はfunctionの頭から始まるわけだが php function となっていっていて phpとfunctionの間が改行されている と解釈されているよう。

cc-mode.el はC系の言語のことしか考えてくれてないみたいで php-mode-hook で外から変数いじっても <?php を無視するようにできそうになかった。

しかたがないので php-mode-hookで (setq indent-line-function 'my-php-indent-line) と indent-line-function を上書きして my-php-indent-lineでは

(defun my-php-indent-line ()
  (save-restriction
   (let ((here (point)))
     (if (not (search-backward "<?php" nil t))
         (funcall 'php-cautious-indent-line)
       (goto-char here)
       (narrow-to-region (match-end 0) (point-max))
       (funcall 'php-cautious-indent-line)
       ))))

と php-cautious-indent-lineする前に narrow-to-regionで <?php を 隠したら うまくインデントされるようになった。

php-cautious-indent-lineを2回書いているのが汚い。