2010/12/14

JavaScriptでミステリーサークルのようなものを自動生成

フラクタル図形等を作成する際によく用いられる形式文法L-Systemを使って、ミステリーサークルのようなものを自動で生成するものをつくりました。これにより、なんかの儀式にミステリーサークル的な模様が必要になった時に簡単に模様をつくることができます。でも、自分で念を込めて模様を考えたほうが、儀式はうまくいくと思います。

例えばこんな感じの模様ができます。

 

デモ
Key :






なにをしているのか
以下のようなL-Systemを構成し、JavaScriptでCanvasに描画しています。詳しくはソースコードを御覧ください。
Grammer
  Variables : 0
  Constants : [1-9][a-f]
  Start : 0
  Rule : 0 -> md5hash
  Iterate count : 3
ここでmd5hashというのはテキストエリアに入力された文字列のMD5Hash値です。
Semantics
  0 : なにもしない
  d=[1-9] : 左に d x 10 度回転
  a : 50前に直線を描いて進む
  b : 回れ右する
  c : 半径50の円を描く
  d : 原点に戻る
  e : 32前にジャンプする
  f : 半径32の円を描く


余談
テキストエリアに入力された値が同じであれば、同じ模様が出力されます。なにか面白い模様ができたらぜひKeyをコメントしてください!
MD5Hashの計算にはサイボウズ・ラボの光成茂雄さんのライブラリを使用させていただきました。ありがとうございます。

参考
Canvasに色々描いてみた - Webと文字

2010/11/26

Node.js Canvas共有に絵を保存するGallery機能追加しました

Canvas共有サーバーが機能しているかどうか、よくチェックしているのですが、たまに
「すごい!」
「wonderful!」
「ちょっとカクカクするけどすごい!感動!」
といったメッセージが書きこまれていることがありました。
こういうのを見ると、嬉しくなります!
それに加え、折角書いてもらった絵が保存されずに消されるのは勿体無いことだと感じたので、絵を保存する機能を付け加えました。

YkitamotoさんにcanvasのtoDataURLというメソッドを教えていただいたので、それを利用することにしました。クライアントがClearボタンを押すと、canvasのデータがbase64形式でエンコードされてサーバーに届きます。サーバーはそれをデコードして保存します。
消そうと思ったら保存されてしまうのです。消しゴムは今のところ意図的に付けていません。

デモ
こちらです。どんどん書いてください!
Galleryはこちら。WebSocketに対応していないブラウザでもGalleryは見れます。


参考文献
toDataURL() - WHATWG

2010/11/17

WebWorkersを使ってJavaScript並列素数計算

Web Workersはページ内でバックグラウンドで並列にスクリプトを実行するための機能です。メッセージパッシングでスレッドのような操作ができます。これを用いることにより、例えばJavaScriptで非常に時間のかかる処理をする際、ブラウザが固まってしまう状況を避けることができます。
今回はWeb Workersを使って並列に素数を数えてみようと思います。

以下のデモでは 1000000000001から 1000000001000までの素数を、子Workerを4つ生成し、それらに仕事を等分して分け与えて計算させています。( ) 内は子WorkerのIDです。
申し訳ありませんが、今のところFirefoxのみでしか動きません。ChromeやSafariではWorker内でWorkerオブジェクトにアクセス出来ないのです。

デモ
http://etsukata.com/js/ww.html


Souce code
ww.js
worker.js

Web Workersを触ってみて、これは「ミニErlangだ」と感じました。Erlang程自由にWorker間でメッセージを自由にやりとり出来ないものの、非同期メッセージパッシングというパラダイムは共通です。
Web Workersは単に「ブラウザが固まらないようにバックグラウンドで処理するだけのもの」ではありません。将来的にはJavaScriptでのマルチコアプログラミングや分散コンピューティングに使われるのでないかと予想しています。だいぶ先(5年以上)のことと思いますが。もしかすると、現在ErlangがやっていることがWeb Workersを用いてJavaScriptで記述されるようになる日が来るかもしれません。未来的超高水準言語JavaScriptがErlangすら内包するかも、と嘯いてみます。

nodejs.orgにありがたいお言葉を見つけましたので以下に転載します。
The fundamentals of scalable systems are fast networking and non-blocking design—the rest is message passing. In future versions, Node will be able to fork new processes (using the Web Workers API ) which fits well into the current design.


仕様が定まっておらず、実装が錯綜しているので今のところはなんとも言えない感触です。

追記
Chromeがsubworkerをつくることが出来ない問題についてhidekiyさんにより情報頂きました。いつもお世話になります。Chromiumにissueが出ています。
http://code.google.com/p/chromium/issues/detail?id=50432

参考文献
Web Workers specification - WHATWG
http://www.whatwg.org/specs/web-workers/current-work/
The Basics of Web Workers - HTML5Rocks
Using web workers - MDC
https://developer.mozilla.org/En/Using_web_workers

2010/11/12

Erlang + Websocket でCanvas共有

拙作のerlang_websocket_serverを用いて、Canvas共有をErlangに移植しました。
非同期メッセージパッシングというパラダイムのおかげで簡単に書けます。

こちらです!どんどん落書きしてください!
http://etsukata.com/erl/cvws.html
ChromeおよびSafariで動作確認しています。複数のウインドウを立ち上げて描いてみてください。

クライアントサイドのコードはNode.js版と全く一緒です。
サーバーサイドのコードを以下に掲載します。

githubでお読みになると、横スクロールがなくて見やすいです。
https://github.com/Etsukata/erlang_websocket_server/blob/master/example/canvas_sharing_handler.erl

-module(canvas_sharing_handler).
-import(websocket_server, [unicast/2, broadcast/2, sendall/1]).
-compile(export_all).

go() -> websocket_server:start("etsukata.com", 9000, ?MODULE, canvas_sharing_handler, [[],[]]).

canvas_sharing_handler(IDList, PointList) ->
  receive
    {open, ConnectionID} ->
      broadcast("@NC:" ++ ConnectionID, ConnectionID),
      unicast("@ID:" ++ ConnectionID, ConnectionID),
      StrIDList = lists:foldr(fun(X, Xs) -> X ++ "," ++ Xs end, "", IDList),
      Str = case StrIDList of
              []   -> [];
              _Any -> lists:sublist(StrIDList, length(StrIDList) -1)
            end,
      unicast("@PT:" ++ Str, ConnectionID),
      lists:foreach(fun(X) -> unicast(X, ConnectionID) end, lists:reverse(PointList)),
      canvas_sharing_handler([ConnectionID|IDList], PointList);
    {message, Data, ConnectionID} ->
      case lists:member($@, Data) of
        true ->
          case string:substr(Data, 1, 3) of
            "@CU" ->
              broadcast(Data, ConnectionID),
              canvas_sharing_handler(IDList, []);
            _Any ->
              canvas_sharing_handler(IDList, PointList)
          end;
        false ->
          broadcast(Data, ConnectionID),
          canvas_sharing_handler(IDList, [Data|PointList])
      end;
    {closed, ConnectionID} ->
      broadcast("@CL:" ++ ConnectionID, ConnectionID),
      canvas_sharing_handler(lists:delete(ConnectionID,IDList), PointList);
    Any ->
      io:format("Any:~w~n", [Any]),    
      canvas_sharing_handler(IDList, PointList)
  end.
Node.js版のソースコードとほとんど長さは変わっていません。
Node.jsのスピード感のある開発も楽しいです。しかしErlangのメッセージパッシングで考えたほうがほうが単純に物事を捉えることができ、これもまた楽しいのです。僕はNode.jsとErlang、どっちも好きです!

2010/11/11

ErlangでWebSocket Server実装を書きました

こちらで公開しています
https://github.com/Etsukata/erlang_websocket_server
この記事はGithubに公開している情報を日本語で若干加筆して提供いたします。


動機
以前Node.jsのWebSocket Server実装であるnode-websocket-serverを用いてCanvas共有アプリをつくりました。そのとき手軽でスピード感のある開発に快感を覚えたので、ネットワークとの親和性のが非常に高く、今注目を集めているErlangではできないものかと思い、作りました。
Dave Bryson氏による実装であるerlang_websocketを主に参考にさせて頂きました。氏の実装では一対一の相互通信を行うものであったので、それを拡張し、特定のクライアントにデータを送信したり、マルチキャストしたりできるようにしました。
なにより、node-websocket-serverのように手軽にWebSocketを用いたアプリをつくれるようにするのが目的です。

Process Design Pattern
Erlangでシステムを構築するときはどのようなProcess Design Patternを採用するかが鍵になります。当ライブラリでは以下のような構造になっています。



Socket Receiverがクライアントからデータを受け取り、順々に右のプロセスに渡していきます。ReceiverはWebSocket Frameから生のデータを取り出しHandlerに渡します。Handlerはそれを加工しSenderに渡します。SenderはWebSocket Frameをつくり、各クライアントとつながっているSocket Senderに対しユニキャストまたはマルチキャストでデータを配信します。Socket Senderは受け取ったものをクライアントに送信します。
と、いった感じです。
ユーザーはHandlerの振る舞いについてのみ記述すればいいようになっています。

サンプル
エコーサーバーを書く場合は以下で十分です。

-module(echo_handler).
-compile(export_all).
-import(websocket_server, [unicast/2]).
go() ->
   websocket_server:start("localhost", 9000, ?MODULE, default_echo_handler, []).
default_echo_handler() ->
  receive
    {message, Data, ConnectionID} ->
      unicast(Data, ConnectionID),
      default_echo_handler();
    _Any -> default_echo_handler()
  end.

ドキュメント
こちらにあります。
http://etsukata.com/erl/docs/


今後
これからはどのような機能を盛りこんでいくかを決めるために、いくつかこのライブラリを用いたWebSocketアプリをつくっていくつもりです。経験を積んで、何が必要で重要なのか、何が要らない物なのかを見定めたいと思っています。

参考URL
Comet is dead long live websockets
http://armstrongonsoftware.blogspot.com/2009/12/comet-is-dead-long-live-websockets.html
davebryson / erlang_websocket
https://github.com/davebryson/erlang_websocket
MiCHiLU / erlang_websocket
https://github.com/MiCHiLU/erlang_websocket

2010/11/07

[Node.js] How to handle ECONNRESET, Connection reset by peer and automatic crashing.

Error Message
node.js:63
    throw e;
    ^
Error: ECONNRESET, Connection reset by peer
    at Stream._readImpl (net:304:14)
    at IOWatcher.callback (net:454:24)
    at node.js:768:9

The error 'ECONNRESET, Connection reset by peer' like the above is thrown when a connection was forcibly closed by peer. For example, a sudden lost of connection due to unstable network environment or unexpected crash of a browser, etc. 
If such exception bubbles all the way back to event loop, the node process print a stack trace and exit as the default action. Try/catch blocks can't catch the excption which occurs in event loop.
To prevent these errors from crashing down your node server, please use

process.on('uncaughtException', function (err) {
  // handle the error
});

Reference
[nodejs.org] Event: 'uncautException'
[nodejs@googlegroups.com] How to prevent node from dying on error?
[technet.microsoft.com] Connection reset by peer

Node.js + Websocket Canvas共有システムが時折ダウンする問題について

2010/10/27にCanvas共有Websocketサーバーを作動させて以来、サーバーがのべ三度にわたりダウンするという問題が発生していました。原因の究明を進めていたところ、一部ダウンする状況の再現及び対処法を突き止めましたのでご報告申し上げます。なお、ダウン中にアクセスされ、Canvas共有を体験なさることが出来なかった読者の方々には深くお詫び申し上げます。これからもダウンの折には早急な原因の解明と対処に努めますので、どうか今後ともよろしくお願いいたします。

サーバーダウン時のエラー出力の内容
node.js:63
    throw e;
    ^
Error: ECONNRESET, Connection reset by peer
    at Stream._readImpl (net:304:14)
    at IOWatcher.callback (net:454:24)
    at node.js:768:9

原因
Node.jsでは発生した例外がイベントループまで到達した場合、デフォルトの動作として、スタックトレースを出力し、終了することになっています。上記の例外が発生した場合、サーバーはダウンします。なお、スタックトレースをご覧いただければわかりますとおり、例外がイベントループ内において発生していますので try/catchで捕まえることはできません。
問題は、どのような状況において上記の例外が発生するか、ということです。
上記の例外は時折にしか発生せず、原因がなにかわからなかったのですが、ようやく突き止めました。Websocketクライアントが正常に接続を終了しない場合に発生していたのです。試しにサーバーへのコネクションを張ったまま、LANケーブルを引っこ抜いてみると、まんまと上記の例外が出ました。おそらくは不安定なネットワーク環境のもとCanvas共有にアクセスし、途中でネットワークが切断された際やブラウザがクラッシュした際等に発生していたのでしょう。

対策
例外発生時の動作をデフォルトのものから変更します。
参照:Event: 'uncaughtException'

ちなみに「Connection reset by peer node.js」で検索したところ、同じ問題で困っていた仲間が大量にいました。


Node.jsで何か作る際には、あらかじめ例外発生時の動作を適切なものに設定なさることをおすすめします。デフォルトだとプロセスは終了してしまいます。
ただ、なんでもprocess.on('uncaughtException')を使って何事もなかったかのようにサーバーを動かし続けることには注意が必要な場合があります。以下を御覧ください。
node.js - Dealing with uncaught exceptions
http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb

2010/10/27

Node.js + Websocket でCanvas共有

友人のhidekiyさんにNode.jsというこれから流行りそうなおもちゃを紹介してもらったので遊ぼうと思います。
Node.jsはV8 JavaScriptエンジン上でイベント化された入出力を扱うフレームワークです。
Node.jsを用いて簡単な落書き板のようなものをつくりました。Websocketサーバーに接続して、みんなでリアルタイムにCanvasを共有し、書き込むことができます。接続時に、サーバーに落書きデータがある場合はそれをダウンロードしてクライアントのCanvasに書き加えます。
クライアントサイドもサーバーサイドもJavaScriptで書けるって、やっぱり気持ちいいです。

対応ブラウザはSafariとChromeです。
Port:8888 をWebsocketの通信に使用しています。ウイルス対策ソフトのブロッキングやプロキシ環境下においては通信できない可能性がありますのでご注意ください。

ぜひ、落書きしてみてください!
http://etsukata.com/node/cvws.html
↑これです

Sorce codeはサーバーサイド、クライアントサイド共にこちらでご覧いただけます。
http://etsukata.com/node/
サーバーサイド    : cvwsserver.js
クライアントサイド : cvwsclient.js





参考URL:
Node.js
http://nodejs.org/
websocket-server
http://github.com/miksago/node-websocket-server


2010/10/19

Ajaxでフラクタルを描画 with Google App Engine

Google App Engineを用いてマンデルブロ集合を描画してみました。
目的はpure JavaScriptで時間のかかる処理をGAEにまかせてしまえば高速に描画できるのではないかと思ったからです。GoogleMapみたいにフラクタル世界をスイスイしたかったのです。

処理内容
1.サーバーサイドで各点の発散を調べ、ループ回数を返します。
2.クライアントサイドでCanvasに色付けします。

問題点
自分の環境だと残念ながらscaleがある程度大きくない限り、ほぼ似たような処理時間になってしまいました。Canvasへのピクセル操作に時間がかかってしまうのです。

JSONのパースに時間がかかるのかと思い、ランレングス圧縮したシリアライズ方式でもやってみましたが、同様の時間がかかてしましました。Canvasのピクセル操作がネックになっているのでどうしようもないんでしょうか...?救いは無いんですか?
ランレングスパック前 http://mandelbrot-fractal.appspot.com/mandelbrot?centerx=0&centery=0&scale=1&pack=0
ランレングスパック後 http://mandelbrot-fractal.appspot.com/mandelbrot?centerx=0&centery=0&scale=1&pack=1

折角AjaxにしたんならGoogle Mapみたいにドラッグや拡大などできるようにするべきですが、ラグが大きいので諦めてしまいました。

ちなみに"Access-Control-Allow-Origin: *"をHTTP Responseに加えてあるので、Same Origin Policyを満たさなくてもどなたでもご自由にお使いいただけます。サーバーサイドはJavaで書きました。

Fractal with Google App Engine
http://mandelbrot-fractal.appspot.com/

追記(2010/11/10)
コメントしてくださったYkitamotoさんの指摘を元に、ピクセル操作を改善したところ、かなり速くなりました!デモまでつくって教えてくださったYkitamotoさん、ありがとうございます!下記の実行時間比較も書き直しました。



実行時間(ms)比較(パラメーター:Center x = -1.1, Center y = 0, Scale = 10)


GAE PureJS
Google Chrome7 681 1787
Safari5 753 923
FireFox3.6 732 231

IE8とOpera10.63には未対応です。


Ajax with GAE Demo



Center x :
Center y :
Scale :


Pure JavaScript Demo



Center x :
Center y :
Scale :

参考にさせていただいたページ:
[xhr] XMLHttpRequest Level 2時代のクロスドメインリクエストProxy
http://blog.hidekiy.com/2010/10/xhr-xhr-lv2proxy.html
Ajax - Goodbye, JSONP. Hello, Access-Control-Allow-Origin
http://blog.livedoor.jp/dankogai/archives/51502865.html

2010/10/15

改ざん検知機能付きストリーム暗号運用モードMULTI-S01のJavaScriptによる実装

MULTI-S01は日立製作所システム開発研究所により開発された改ざん検知機能付きストリーム暗号運用モードです。 MULTI-S01は暗号技術評価プロジェクトCRYPTREC作成の「電子政府推奨暗号リスト」ISO/IEC 18033の標準暗号として採用されています。

MULTI-S01をJavaScriptで実装しました。 このライブラリでMULTI-S01の改ざん検知機能付きストリーム暗号運用モードを利用できます。
しかし、仕様書等にテストデータが記載されていないので、正確さは保証できません。


日立製作所 MULTI-S01のページ
http://www.sdl.hitachi.co.jp/crypto/s01/

Demonstration

このデモではストリーム暗号にMUGIを使用しています。 生成した暗号文が改ざんされていると、それを検知してエラーメッセージを出力します。 暗号文を生成し、すこし内容を変えてみてDecryptボタンを押してみてください。
Key in hex(128-bit):
InitialVector in hex(128-bit):
Redundancy in hex(64-bit):
Plain Text:
Encrypted Text:
Decrypted Text:
Source: multi.js



擬似乱数生成器MUGIのJavaScriptによる実装

MUGIは日立製作所システム開発研究所により開発された疑似乱数生成器です。 MUGIは暗号技術評価プロジェクトCRYPTREC作成の「電子政府推奨暗号リスト」ISO/IEC 18033の標準暗号として採用されています。

MUGIをJavaScriptで実装しました。 このライブラリでMUGIの疑似乱数生成機能を利用できます。
しかし、仕様書に記載されているテストデータでしかテストしていないため、正確さは保証できません。

日立製作所システム開発研究所MUGIのページ
http://www.sdl.hitachi.co.jp/crypto/mugi/


Demonstration

Key in hex(128-bit):
InitialVector in hex(128-bit):
Size of output:
GeneratedRandom in hex:


Source: mugi.js

2012/11/6 追記
chk さんのご指摘により、擬似乱数を16進数で表記する処理のバグ修正を施しました。
chk さん、誠にありがとうございます!