IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

ブラウザでお絵描きプログラミング! Processing.js 登場!

はじめに

今日、 jQuery の作者として有名な John Resig さんが Processing.js という JavaScript のライブラリを公開しました。
John Resig - Processing.js
このライブラリを使うと、比較的簡単に以下のようなグラフィックスやアニメーションを書くことができるようになります。

というわけで、公開されたばかりのこのライブラリを簡単な使い方から詳しい使い方までとことん掘り下げてみたいと思います。

Processing.js 概要

まず、 Processing.js とは何かという話をします。
Processing.js とは、ブラウザで Processing というプログラミング言語を実行する JavaScript のライブラリです。
では、 Processing とはどのようなプログラミング言語なのでしょうか。

Processing とは

Processing とは、ビジュアルデザインのためのプログラミング言語で、 Java のような文法をしています。
ビジュアルデザインのためのプログラミング言語というと難しそうですが、実際はとても簡単です。
たとえば、以下の 2 行を書けば、丸が書けてしまいます。

size(200, 200);
ellipse(100, 100, 100, 100);


もうすこし、たくさん描いてみましょう

size(200, 200);
background(0);
noStroke();

for (int i = 0; i < 100; i ++) {
  fill(random(255), random(255), 255, 100);
  ellipse(random(200), random(200), 60, 60);
}


簡単ですね^^
というわけで、この Processing をブラウザで使えるようになるのです。わくわくしますね!

Processing.js を使うための準備

Processing.js を使うための準備をしましょう。 Processing.js を使うには、以下の手順が必要です。

  1. Processing.js をダウンロードする。
  2. HTML ファイルを作って、Processing.js を読み込む。
  3. canvas 要素を挿入する。
  4. Processing という関数を呼び出す。

ちょっとめんどくさそうですが、やってみると簡単です。順番に細かく説明します。

Processing.js をダウンロードする。

以下の JavaScript ファイルをダウンロードします。
http://ejohn.org/apps/processing.js/processing.js

HTML ファイルを作って、 Processing.js を読み込む

以下のように、HTML ファイルを作って script 要素の src 属性にさっきダウンロードしてきた processing.js のファイルを指定します。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
 <head>
  <title>Processing Sample</title>
  <script type="text/javascript" src="processing.js"></script>
 </head>
 <body>
  <h1>Processing Sample</h1>
  <div>
  </div>
 </body>
<html>

サンプル

canvas 要素を挿入する

さっきの HTML ファイルに canvas 要素を挿入します。 width 属性と height 属性で canvas 要素の大きさを指定します。

<body>
  <h1>Processing Sample</h1>
  <div>
   <canvas width="400" height="400"></canvas>
  </div>
 </body>

サンプル
ちなみに、 canvas 要素とは、 JavaScript から絵を描ける要素です。また、 IE には canvas 要素はありませんが、 excanvas.js を読み込むことで canvas 要素を使えるようになります。

Processing という関数を呼び出す

Processing.js によって Processing 関数が作られているので、 JavaScript から Processing 関数を呼び出します。
引数には、

を指定します。

<title>Processing Sample</title>
  <script type="text/javascript" src="processing.js"></script>
  <script type="text/javascript">
// ロードされた時に実行
window.onload = function() {
  // canvas 要素
  var canvas = document.getElementsByTagName('canvas')[0];
  // Processing のソースコード
  var code = "size(400, 400); noStroke(); fill(255, 0, 0, 255 * 0.5); ellipse(200, 150, 200, 200); fill(0, 255, 0, 255 * 0.5); ellipse(250, 250, 200, 200); fill(0, 0, 155, 255 * 0.5); ellipse(150, 250, 200, 200);";
  // Processing 関数を呼び出す
  Processing(canvas, code);
};
  </script>
 </head>

サンプル
これで、丸が描かれます。

準備も簡単ですね。

もっと準備(必要な人は)

基本的な準備は完了ですが、もう 2 個テクニックを追加しておきます。
これは、別にやらなくてもいいです。めんどうな人やメリットを感じない人はこの項は飛ばしてください^^;

script 要素に Processing のソースコードを書く

Processing のソースコードを文字列で渡すのはめんどうくさいですね><
というわけで、そんなときは script 要素を使いましょう。
script 要素は、 type 属性に実行できないタイプが指定されると何もしません。逆にそれを利用して script 要素に Processing のソースコードを書くのです。

<script type="text/javascript" src="processing.js"></script>
  <script type="text/javascript">
window.onload = function() {
  var canvas = document.getElementsByTagName('canvas')[0];

  // Processing のソースコードが書かれた script 要素
  var codeElm = document.getElementById('processing-code');
  // 要素の内容を取得する
  var code = codeElm.textContent || codeElm.innerText;

  Processing(canvas, code);
};
  </script>
  <script id="processing-code" type="application/processing">
// ここに Processing のソースコードが書ける
size(400, 400);
noStroke();
fill(255, 0, 0, 255 * 0.5);
ellipse(200, 150, 200, 200);
fill(0, 255, 0, 255 * 0.5);
ellipse(250, 250, 200, 200);
fill(0, 0, 155, 255 * 0.5);
ellipse(150, 250, 200, 200);
  </script>

サンプル
こうしておけば、 Processing のソースコードが長くなっても可読性を損なわずにすみますね^^

IE にも対応させる

前の項でも、チラっと言いましたが IEcanvas 要素に対応していないので excanvas.js という JavaScript のファイルを読み込んであげる必要があります。
excanvas.js は以下のページからダウンロードできます。
http://sourceforge.net/project/showfiles.php?group_id=163391
ダウンロードした excanvas_0002.zip ファイルを解凍すると、 excanvas.js が入っているので、それを script タグで読み込みます。

<head>

  <script type="text/javascript" src="excanvas.js"></script>

 </head>

サンプル
excanvas.js については、詳しく解説しませんがとりあえず読み込んでおけば間違いありません。 IE 以外の環境では何もしない無害な JS なので、安心です。
ほら、 IE でも動いてますね!

準備完了!何か書いてみよう!

というわけで、ちょっとアニメーションを書いてみました。コメントを参考にしてください。

Particle particles = new Particle[20];

// setup は window.onload のようなもの
void setup() {
    size(400, 400);
    for (int i = 0; i < particles.length; i ++)
        particles[i] = new Particle();
}

// draw はタイムラインが更新されるために呼ばれる
void draw() {
    for (int i = 0; i < particles.length; i ++)
        particles[i].update();
}

// 線を管理するクラスを作る
class Particle {
    // フィールドの定義
    float x, y, xvel, yvel, r, g, b, a;

    // コンストラクタで、フィールドの初期化
    Particle () {
        x = random(width);
        y = random(height);
        r = random(255);
        g = random(255);
        b = random(255);
        a = 0;
        xvel = random(1) * 10 - 5;
        yvel = random(1) * 10 - 5;
    }

    // draw から呼び出す
    // フィールドを更新して描画する
    void update() {
        if (a > 2 * 3.14) return;
        float px = x, py = y;
        x += xvel, y += yvel;
        yvel += 0.1;
        if (x < 0 || width < x) xvel *= -1;
        if (y < 0 || height < y) yvel *= -1;
        stroke(r, g, b, sin(a += 0.01) * 255);
        line(px, py, x, y);
    }
}

サンプル
もちろん、クロスブラウザで動きます。(ただ、 IE だと重いです><)

もっと Processing.js のサンプルが見たいかたは、以下に行くといいです。
http://ejohn.org/apps/processing.js/examples/basic/
Processing の文法は、以下にあります。
Language Reference (API) \ Processing 3+
皆さんも、自分で面白いものを作ってみてください!
ここでは、書かなかったですが本物の Processing と同様にマウスとの連携やキーボードとの連携もできますよ!

JavaScript との値の共有

Processing.js では、 JavaScript のグローバル変数やグローバル関数を Processing のグローバル変数、グローバル関数として使うことができます。
たとえば、以下のように JavaScript で定義したグローバル変数 message を Processing から呼び出すことができます。

  <script type="text/javascript">
window.onload = function() {
  var canvas = document.getElementsByTagName('canvas')[0];
  var codeElm = document.getElementById('processing-code');
  var code = codeElm.textContent || codeElm.innerText || codeElm.text;
  Processing(canvas, code);
};

// この関数を Processing から呼び出す
function message(msg) {
    document.getElementById('message').innerHTML = msg;
}
  </script>
  <script id="processing-code" type="application/processing">
void setup() {
    size(400, 400);
    noStroke();
    frameRate(10);
}

void draw() {
    fill(0, 0, 0, 100);
    float r = random(400 / 2), x = random(400), y = random(400);
    ellipse(x, y, r, r);

    // ここで JavaScript の関数を呼ぶ
    message("r = " + r + ", x = " + x + ", y = " + y);
}
  </script>

サンプル
こんな感じになります。 canvas の下に文字列が挿入されていますね。

この仕組みを使うと Processing のアニメーションと、 HTML の連携を簡単に行うことができます。

Processing.js の仕組み

ここからは、 Processing.js の内部の話をします。この項を知らなくても Processing.js は使えるので、全然飛ばしていいです^^;

Processing.js の内部を知る意味

Processing の関数名は rect とか ellipse とか直感的なものが多いです。なので、 Processing 関数名から Processing.js を調べれば、「canvas ではこう書くんだー」ってのがすぐ分かって面白いです。
あと、たまに特定のブラウザで動かないときもあるので、それを調べるときには内部の仕組みを知っておいた方がいいですね^^
Processing.js は全体で 1700 行くらいしかありません、なので全然読めます。

Processing.js の実行の流れ

Processing.js では、以下の流れで処理が進みます。

  1. Processing 関数が呼び出される
  2. 内部で Processing 言語のグローバルの名前空間となるオブジェクト p が生成される。
  3. 名前空間 p に関数やクラス(実際は JavaScript のプロトタイプ)が追加される。
  4. Processing のソースコードが内部の parse 関数で JavaScriptソースコードに変換される。
  5. 名前空間 p が with され、そのスコープ内で JavaScriptソースコードeval される。
  6. すると、あら不思議。絵が描画される。

では、実際にソースを見てみましょう。

9 - 14 行目: Processing

以下が Processing 関数の全貌です。この関数(実行コンテキスト)では、 this はグローバルオブジェクトになっているので、 Processing はグローバル関数になります。

this.Processing = function Processing( aElement, aCode )
{
  var p = buildProcessing( aElement );
  p.init( aCode );
  return p;
};

で、 buildProcessing が Processing のグローバル名前空間 p を生成する関数で p.init が Processing → JavaScriptソースコード変換と実行を行う関数です。
ここで注目すべきは、 p を返してるところですね。Processing のグローバル名前空間 p を返しているので JavaScript で Processing の名前空間をすべて使えるわけです。
Hack し放題です!やっほう!です。

32 - 263 行目: parse

この行では、 parse という Processing → JavaScriptソースコード変換を行う関数が定義されています。

window.parse = function parse( aCode, p )
{
  // (略)
  // このコードの中で順番に、文字列の replace メソッドによって  JavaScript に変換されて行く
  // 縦に長いコードなので、コメントを追っていけば内容は分かる
  return aCode;
}

第二引数は使ってないです。たぶん、消し忘れですね。
もし JavaScript のエラーが出る場合は、この最後の最後の行に console.log や alert などを差し込むと良さそうですね。

265 - 1693 行目: buildProcessing

Processing のグローバル名前空間 p を作る buildProcessing 関数が定義されています。

function buildProcessing( curElement ){
  var p = {};

  p.PI = Math.PI;
  p.TWO_PI = 2 * p.PI;

  // (略)

  p.RGB = 1; 
  p.HSB = 2; 

  // (略)

  p.ellipse = function(x, y, width, height) {

    // (略)

  };

  // (略)

  p.init = function(code) {

    // (略)

  };

  // (略)

  return p;
}

こんな感じで、ただひたすら p に関数やプロパティを追加しまくります。で、その追加された関数や変数が Processing のグローバル関数やグローバル変数になるわけですね。
この p にはものすごい数の関数や変数が定義されます。とくに重要なものを見て行きましょう。

1565 - 1690 行目: p.init

ここでは、実際に変換された JavaScript のコードを eval したり、アニメーションのタイマーを走らせたり、という初期化処理をまとめてする p.init という関数が定義されています。これは、 Processing 関数の中で呼ばれます。

  p.init = function init(code){

    // (略)

    if ( code )
    {    
      // ここのコードで p が eval され、 parse 関数で変換されたコードが eval される。
      (function(Processing){with (p){ 
        eval(parse(code, p)); 
      }})(p);
    }    

    // Processing のコード中に setup 関数を宣言した場合はその関数が実行される
    if ( p.setup )
    {    
      inSetup = true;
      p.setup();
    }    
    
    // (略)
    
    // Processing のコード中に draw 関数を宣言した場合は p.loop が実行される
    // p.loop は内部で setInterval を呼び出し、そこから、定期的に draw 関数が呼び出される。
    if ( p.draw )
    {    
      if ( !doLoop )
      {    
        p.redraw();
      }    
      else 
      {    
        p.loop();
      }    
    }    

    // イベントの初期化のコード
  };

アニメーションが始まらない場合や、setup 時の状態をデバッグしたい場合はこの関数からデバッグすればいいです。

1039 - 1058 行目: p.loop

アニメーションの開始を行う p.loop も見ておきましょう。

  p.loop = function loop()
  {
    if ( loopStarted )
      return;
    
    var looping = setInterval(function()
    {    
      try  
      {    
        // これを curFrameRate で指定された間隔で呼び出してるだけ。
        // curFrameRate は p.frameRate 関数で設定することができる。
        p.redraw();
      }    
      catch(e)
      {    
        clearInterval( looping );
        throw e;
      }    
    }, 1000 / curFrameRate );
    
    loopStarted = true;
  }

こんな感じで、シンプルに redraw を読んでるだけですね。

1025 - 1037 行目: p.redraw

p.redraw も見ておきましょう。

  p.redraw = function redraw()
  {
    if ( hasBackground )
    {
      p.background();
    }
   
    inDraw = true;
    p.pushMatrix();
    p.draw();
    p.popMatrix();
    inDraw = false;
  }

おお。 background は毎回書いてくれるんですね。。。。と思ったけど、どこにも hasBackground フラグを建てる方法が無かったです><(毎回、自分で background を呼ばなきゃダメみたいですね。)
p.pushMatrix と p.popMatrix では、現在の塗りの色とかの描画状態を保存して、戻してます。

仕組みはこんな感じですかね。

これらの関数だけ押さえておけば、だいたいあとはデバッグできます。
やっぱ、 jQuery の作者さんだけあって、ソースもきれいで読みやすかったです!

まとめ

そろそろまとめますよ。と。

まとめのまとめ

JavaScripter のみなさん、 Processing を初めてみませんか
Processinger のみなさん、 JavaScript を初めてみませんか

おしまい

【訂正】 kmyacc と bison の違い

http://www.kt.rim.or.jp/~kbk/zakkicho/08/zakkicho0805a.html#D20080508-7

を見て、ちゃんと kmyacc を調べてみました。

bison の error トークンで行き詰まる - IT戦記

とか書いてましたが、完全に僕の勘違いでした。
申し訳ありませんでした。
教えていただきありがとうございました。

駄文 - 久しぶりにエンジニアと飲んだ

脱ひきこもり記念

ConcurrentThread の牧さんと
プロトタイプチェーンとスコープチェーンって似てるよね!
その橋渡しをするのが、 with 文だよね!
でも、代入時に奥に追加されるか、手前に追加されるかが違うよね!
スコープチェーンをプロトタイプチェーンで実装したら、 Python と同じ変数スコープになるよね!
という話をするなどした。
あと、タブレットを買おうと思った。
あと、パックラットパーサの概要を教えて貰った。
あと、 yu さんがまたなんか変なものを作りそうな感じがした。
あと、 marquee タグと政治の話を聞いた。
あと、川崎さんが疲れてて心配になった
あと、河村さんの生き方に感動した
あと、アッガイかわいい。
あと、富士山登りたい
あと、 Mozilla 界隈の人になりたい
とかいろいろめっちゃ楽しかった。

あ、あと
第一言語 JavaScript の人が三人もいた。 piro たん、牧さん、 amachang。これはすごい。