« 2006年08月 | メイン | 2006年10月 »

2006年09月17日

遅延制御の作用・副作用

せっかくなので setTimeout や setInterval についてもうひとつ。
以下のコードを実行すると、どうなるでしょうか。
function hoge( str ){
	trace( str + " called by " + arguments.caller );
}
function callhoge(){
	setTimeout( hoge, 1000, "hello");
}
this.callhoge();

arguments.caller は自身を呼んだ関数を参照するので、callhoge を参照してるんだなと思ったら大間違いで、callhoge は1秒後に hoge を呼ぶように setTimeout で遅延処理を登録しただけなんですよね。
実際に呼んでいるのは、どこの誰とも分からないシステム中枢。

もうひとつ、騙されやすいのが以下のコード。
function hoge( str ){
	trace( this + ": " + str );
}
setTimeout( this.hoge, 1000, "hello");

1秒後にメソッドを呼んでいるんだなと思いがちで、実際その通りなんだけどスコープが違います。
この場合、メソッド hoge 内での this の参照先は存在せず undefined になりますが、以下のような呼び出し方をすれば問題ありません。
setTimeout( this, "hoge", 1000, "hello");

これはおそらく1秒後に内部的に
this["hoge"].apply( this, "hello");

と呼んでいるのでしょう。


たとえば、呼び出される前にメソッド hoge の内容を書き換えるとどうなるかというと
setTimeout( this.hoge, 1000, "hello");
this.hoge = function(){
	trace("hoge");
}
--> 書き換え前の hoge() が実行される

setTimeout( this, "hoge", 1000, "hello");
this.hoge = function(){
	trace("hoge");
}
--> 書き換え後の hoge() が実行される

前者はメソッドの参照を直接渡しているので実行に至るまで全く影響を受けませんが、後者はあくまで実行時にメソッドを参照するので影響受けまくリング。
むしろメソッドを未定義なままで遅延登録しても、実行時までに定義すればいいというちょっと便利な仕様。

これらの挙動を何かに応用できないかと考えてみたけれど、明らかにバグの温床になるようなところなので何もしない方がいいでしょう。合掌。

2006年09月13日

遅延委譲のお話

仕事でActionScriptごりごり書いてる時にちょっと必要に迫られて試してみたら、当たり前のようで当たり前じゃない盲点みたいな仕様はっけーん。

Function.apply, Function.call と setTimeout, setInterval が同時に使えない!

ユーティリティなメソッドで特定のムービーを遅延制御したかったんですが、あえなく撃沈。
Function.apply もれっきとしたメソッドだし、返り値を見ても setTimeout による登録が失敗したということではなさそうなのだけれど、内部的にスコープ処理がずれるのか一切実行されません。
たとえば以下のコード

function hoge( str1, str2 ){
	trace( this + ": " + str1 + "," + str2 );
}
setTimeout( hoge.apply, 1000, this, ["Hello", "world"] );

setTimeout によって1秒後に

hoge.apply( this, ["Hello", "world"] );

が実行されることを期待してしまうのですが、上記の通り動きません。
このままでは埒が明かないので、上記のコードに加えてプロキシ委譲メソッドを用意します。

function proxyapply( func, target, args ){
	func.apply( target, args );
}
setTimeout( proxyapply, 1000, hoge, this, ["Hello", "world"] );

こうすると、1秒後に

proxyapply( hoge, this, ["Hello", "world"] );

が実行され、結果
hoge.apply( this, ["Hello", "world"] );

が実行されます。

こうして遅延制御がうまくいきました、めでたしめでたしー
つか、これバグ? 仕様? バグという名の仕様?

2006年09月12日

CHRONO VIEWER

ウェブカム映像の時空間を歪めましょなFlash作品 CHRONO VIEWER を公開しました。
ウェブカム必須です。
といっても元ネタがあります。

Flashじゃ到底重くて話にならないと思ってたけど、昨夜糸口を見つけてスクリプトごりごり。
連続した時間のビットマップを保持しつつ、さらにそれをマスクで重ねるとかだと高負荷でファンが唸り出しますが、それを BitmapData.scrollと数回の BitmapData.copyPixels、さらに DisplacementMapFilter で一発処理することで劇的に軽くなりました。
それでも結構重いんですけどね・・・

ウェブカムの前で変顔してみたり、グーチョキパーしてみたりしながら時空間の歪みをグリグリ味わってみてください。
以下、技術的なこと。

アクセス後に表示される映像の配列は一枚のBitmapDataです。
これの右端1列をcopyPixelsで保持しておいて、全体を映像の大きさ分だけ右に一度scrollさせます。
さらに保持しておいたBitmapDataを今度は左端に一段だけ下げてcopyPixelsで貼り付け、最後に左上の1マスにウェブカムの最新映像をdrawすれば1単位時間進んだ映像群の一枚絵が得られるというわけです。

そして次にクリック&ドラッグによる歪み生成ですが、上記の手順で得られた1枚絵を有効活用します。
右に進めば進むほど、下に進めば進むほど過去の映像を参照しているということは、特定の領域だけ過去の映像に差し替えるには映像の縦横サイズ分だけずらせばいいということになります。
ここで DisplacementMapFilter の出番です。

映像をクリックし続けると、そのマウス座標を中心とする正円を次々に生成するようにしておき、生成順に微妙に色を変化させていきます。
そしてそれらの円をまとめてdrawし、置き換えマップを手に入れます。
この置き換えマップによって移動する量を、映像の縦横サイズぴったりになるように各種パラメータを調整するのですがここでは割愛します。

ちなみに、ここで画質高だと置き換えマップ生成時に円の境界にアンチエイリアスがかかって色が変わってしまうので、画質低にしておきます。
タイトルのテキストとかどうしようと思っていたのですが、ちょうど fladdict さんとこで有益なエントリが上がっていたので参考にさせてもらいました。

2006年09月04日

げしゅたると

特に何か大きな意味があるわけではないのですが、ビットマップデータの習作Flashを公開しました。

げしゅたると

「ゲシュタルト」はwikipediaによると「全体性を持ったまとまりのある構造」という意味なのですが、こういう演出に適用できるかどうかは分かりません。
むしろ「ゲシュタルト崩壊」からでっちあげた作品です。
DRAW COMPLEXITYも「複雑系」という言葉から適当にでっちあげてたし。
あまり深く考えてません。

どちらかというと立ち上げっぱなしにしておいて、スクリーンセーバー代わりにどうぞーみたいな感じかなあ。
むしろスクリーンセーバーとしてリリースした方がよかった?
つか、ひらがなのタイトルってものすごく萌えゲーの匂いがする・・・なぜだ。

一見すごく重い処理してそうに見えますが、思ったより全然軽いと思います。