多くのオープンソース・プロジェクトが、既存のアプリケーションに代わるアプリケーションを構築する必要に迫られているなか、新しい革新的な取り組みを示しているプロジェクトも数多くあります。そのようなプログラムの 1 つが、Processing です。Processing は 2001年にマサチューセッツ工科大学 (MIT) の Media Lab で開始されたプロジェクトで、Ben Fry と Casey Reas を中心に作成されましたが、カーネギー・メロン大学、カリフォルニア大学ロサンゼルス校、マイアミ大学なども、その作成に寄与しています。
Processing が当初目標としていたのは、コンピューター・サイエンスの基礎を (グラフィックスを使って) 教えるために使用できる、グラフィック・スケッチブックとグラフィック環境を開発することでした。しかしその後、Processing は専門的な研究成果をグラフィックスで視覚化するために使用できる環境へと進化しました。Processing を中心としたコミュニティーが形成され、この言語と環境をアニメーション、可視化、画像、ネットワーク・プログラミング、そしてその他多くのアプリケーションに対応させるためのライブラリーを作成しています。Processing の単純なインターフェース、強力な言語、さらにデータおよびアプリケーションのエクスポート用に用意された豊富なメカニズムを見れば、Processing がデータを視覚化するための優れた環境であることは一目瞭然です。
GNU/Linux®、Mac OS X および Windows® 上で動作する Processing は、画像をさまざまなフォーマットでエクスポートできるようになっています。動的アプリケーションの場合、Processing アプリケーションを Web 環境で使用できる Java™ アプレットとしてエクスポートすることさえ可能です。
この連載第 1 回の記事では、Processing IDE について説明した後、Processing 言語の主要な側面を概説します。続いて重要なグラフィック・プリミティブを取り上げ、最後にこれらのプリミティブを利用したアプリケーションの例をいくつか紹介し、その内容を探ります。
まず始めにやるべきことは、Processing 環境をインストールすることです。Processing.org にアクセスして「Download Processing (Processing のダウンロード)」をクリックし、お使いのオペレーティング・システムを選択してください。この記事に記載する例で使用しているのは、Processing の V1.2.1 です。gzip で圧縮された tarball をダウンロードしたら、tar xvfz processing-1.2.1.tgz
(この記事での例) を実行して展開します。
インストールの際には、Java 技術が使用可能になっていることも確認してください。Ubuntu の場合、「sudo apt-get install openjdk-6-jdk
」と入力すれば確認することができます。
インストールが完了したら、tarball によって作成された「processing-1.2.1」ディレクトリーに進み、「./processing
」と入力してテストを行います。
すると、図 1 に示す Processing 開発環境 (PDE、Processing IDE) が表示されるはずです。このウィンドウの大部分はテキスト・エディターで占められています。下図に示されている 2 行を入力して「Run (実行)」(三角形が描かれた左上のボタン) をクリックすると、入力したこの単純なプログラム (Processing 用語では「スケッチ」) の結果として、ウィンドウが表示されます。「Stop (停止)」(正方形が描かれた左上のボタン) をクリックすると、プログラムが終了し、ウィンドウが消えます。
図 1. PDE と結果ウィンドウ
次は Processing 言語の詳細を探り、その主要な機能について説明してから、興味深いアプリケーションの開発へと話を進めていきます。
Processing は Java プログラミング言語で作成されています。言語ツリーの中で Processing が最も似ている言語も Java であるため、C
または Java 言語の知識があれば、Processing を習得するのは簡単です。プログラムの作成方法を単純化する Processing には、Java 言語の機能のうち、比較的高度な機能になると組み込まれていないものもありますが、Java 言語の多くの機能はユーザーが理解しなくても済むように Processing に統合されています。
Processing を記述する言語として Java が選ばれた理由は、Processing アプリケーションは Java コードに変換されてから実行されるためです。Java パラダイムを選択することで、コードの変換を単純化し、ビジュアル・プログラムをごく簡単に開発、実行できるようにしています。Processing 言語と Java 言語の比較については、「参考文献」を参照してください。
図 1 を見ておわかりのように、Processing での開発作業には PDE とディスプレイ・ウィンドウを使用します。例として、図 2 に 2 次元グラフィックスの座標系を示します。size
キーワードは、ディスプレイ・ウィンドウのサイズをピクセル単位で定義します。このディスプレイ・ウィンドウのサイズを定義することが、一般に Processing アプリケーションの最初のステップとなります。
図 2. 2 次元ディスプレイ・ウィンドウの座標
図 2 に示されているように、size
キーワードが指定するのはディスプレイ・ウィンドウの X 座標と Y 座標の上限値です。line
キーワードは、ディスプレイ・ウィンドウ内にピクセル座標で示した 2 点間を線で結びます (x1
, y1
から x2
, y2
に描画するという形)。注意する点として、画面の外 (size
で定義された範囲外) に描画しても不正ではありませんが、無視されるだけです。
この記事では詳しく説明しませんが、size
はオプションで 3 つ目の引数を mode
として受け入れます。mode は使用するレンダリング・エンジンを定義するキーワードで、PDF (Adobe® PDF 文書への直接描画)、OPENGL
(使用可能な Open-GL グラフィック・アダプターを利用)、P3D
(高速 3 次元描画) などをサポートします。デフォルトは、高画質の 2 次元画像処理に最適な JAVA2D
です。
次のセクションでは、サンプル・アプリケーションについて詳しく探る前に、基本的なグラフィックス・プリミティブをいくつか取り上げて説明します。
Processing には、多種多様な幾何学的形状と、これらの形状の制御機能が組み込まれています。このセクションでは、基本的なグラフィックス・プリミティブを紹介します。
background
関数は、ディスプレイ・ウィンドウの色を設定するために使用します。この関数では (濃淡値、つまり RGB (Red-Green-Blue) 色を定義するために) さまざまなパラメーターを使用することができます。リスト 1 のコード・セグメントが生成する出力は、図 3 の Cell A に示されています。
リスト 1. background 関数を使用する
size(100, 100); background( 0, 128, 0 ); |
set
関数を使用して、個々のピクセルを描画することもできます。この関数は、ディスプレイ・ウィンドウ内の x,y 座標と、色を指定する 3 番目の引数を取ります。Processing には、特定の操作に使用する色を定義するための color
という名前の型もあります。以下の例では、色のインスタンスを作成し、そのインスタンスを使用してディスプレイ・ウィンドウ内の特定のピクセルを設定しています (リスト 2 および図 3 の Cell B を参照)。
リスト 2. ピクセルおよび色を設定する
size(100, 100); for (int x = 0 ; x < 100 ; x++) { for (int y = 0 ; y < 100 ; y++) { color c = color( x*2, y*2, 128 ); set(x, y, c); } } |
表示内の特定のピクセルの色を読み取るには、get
操作を使用します。set
はシンプルですが、表示を操作する最速の手段ではありません。アクセスを高速化するには、代わりにpixels
配列を (loadPixels
および updatePixels
関数と併せて) 使用してください。
Processing で形状を描画するのも簡単です。それには single 関数を使用します。形状を描画するときに使用する色を設定するには、stroke
関数を使用してください。この関数は濃淡パラメーターを 1 つだけ取ることも、RGB として 3 つのパラメーターを取ることもできます。さらに、fill
コマンドを使用して形状を塗りつぶす色を定義することも可能です。
リスト 3 に、線、四角形、円 (楕円を使用)、楕円を描画する方法を示します。line
関数が取る 4 つの引数は、線で結ぶ上下左右の点を表します。四角形を描画する rect
関数の場合には、最初の 2 つの点が位置を定義し、続く 2 つの点のそれぞれが幅と高さを定義します。ellipse
関数も同じく、位置と幅/高さを定義する 4 つの引数を取ります。幅と高さが同じであれば、形状は円となります。楕円を調整するには、ellipseMode
関数を使用することもできます。この関数では、x,y 位置が角 (CORNER
) を表すのか、または楕円の中心 (CENTER
) を表すのかを指定します。図 3 の Cell C を参照してください。
リスト 3. 線と形状
size(100, 100); stroke(0, 128, 0); line(10, 10, 90, 90); fill(20, 50, 150); rect(30, 30, 60, 40); fill(190, 0, 30); ellipse(30, 70, 20, 20); fill(0, 150, 90); ellipse(70, 30, 30, 20); |
Processing では、quad
を使用して四辺形 (4 辺を持つ多角形) も簡単に描画することができます。四辺形は、四辺形の 4 点を表す 8 つの引数を取ります。リスト 4 の例では、10 個の四辺形をランダムに作成しています (点は時計回り、または反時計回りの順になっていなければなりません)。このコードはまた、各四辺形の濃淡色もランダムに作成しています。
リスト 4. 四辺形の描画
size(100, 100); for (int i = 0 ; i < 10 ; i++) { int x1 = (int)random(50); int y1 = (int)random(50); int x2 = (int)random(50) + 50; int y2 = (int)random(50); int x3 = (int)random(50) + 50; int y3 = (int)random(50) + 50; int x4 = (int)random(50); int y4 = (int)random(50) + 50; fill( color((int)random(255) ) ); quad( x1, y1, x2, y2, x3, y3, x4, y4 ); } |
図 3. リスト 1 からリスト 4 までのグラフィックス出力
他にも多数の形状があるだけでなく、線の幅や画像の滑らかさを制御する機能もさまざまに揃っています。図 4 は、リスト 4 のquad
関数の例に smooth
関数の呼び出しを追加したバージョンです。この関数で境界部のアンチエイリアス処理を行うことにより、速度は犠牲になりますが、画質が改善されます。
図 4. smooth 関数を使用する
これまで、Processing 言語について一連の単純なスクリプトで説明してきました。これらのコードは、アプリケーションの単純な要素となる非構造化コードですが、Processing アプリケーションには構造があります。継続的に実行してディスプレイ・ウィンドウを時間と共に変化させるグラフィック・アプリケーションを開発する場合 (例えばアニメーションの場合) には、構造が重要となります。このコンテキストにおいて大きな役割を果たす関数は、setup
と draw
の 2 つです。
初期化のために使用される setup
関数は、Processing ランタイムによって一度だけ実行されます。一般に setup
関数の中には、size
関数 (ウィンドウの境界を定義) の他、操作中に使用する変数の初期化が組み込まれています。一方、draw
関数は Processing ランタイムが継続的に実行します。draw
関数が終了するたびに新しいフレームがディスプレイ・ウィンドウに描画され、それから draw 関数が再び呼び出されます。デフォルトの描画速度は 1 秒あたり 60 フレームですが、この速度は frameRate
関数を呼び出して変更することができます。
noLoop
関数と draw
関数を使ってフレームを描画するタイミングを制御することもできます。noLoop
関数によって描画は停止します。これを再開するには、loop
関数を使用します。draw
関数が呼び出されるタイミングは、redraw
関数の呼び出しによって制御することができます。
Processing アプリケーションの開発方法がわかったところで、今度はテキストの使用方法を具体的に説明する単純な例を見てみましょう。
Processing は、ディスプレイ・ウィンドウ内のテキスト、そしてデバッグ用コンソールという形でのテキストもサポートします。ディスプレイ・ウィンドウ内でテキストを使用する場合は、フォントが必要です。したがって、フォントを作成することがテキストを使用する際の最初のステップとなります (それには、PDE の「Tools (ツール)」オプションを使用します)。作成するフォントを選択すると、そのフォントのファイル (VLW) がプロジェクトの ./data サブディレクトリーに作成されます。このファイルを loadFont
関数を使ってロードした後、textFont
を使用してデフォルトとして定義します。図 5 の setup
関数の中には、この両方のステップが示されています。この関数では、フレーム速度を遅くして 1 秒あたり 1 フレームに設定していることにも注意してください (これが、必然的に更新が発生する頻度だからです)。
この図の中で、draw
関数には記事でまだ取り上げていない関数がいくつか含まれています。最初に示されているのは時間の関数で、これらの関数はクロックの時、分、秒を返します。この他、年、月、日を返す関数もあることに注意してください。時間データを保管した後は、数値を文字列に変換する nf
関数を使用して文字列を作成します。時計にバリエーションを持たせるには、background
関数と fill
関数を使用して背景と時計の文字の色を操作してください。背景色の範囲は 255 (白) から 137 (明るいグレー) です。テキストに色を付ける fill
関数には、100 (明るいグレー) から 218 (ほぼ黒に近い色) を指定することができます。色を設定したら、text
関数により、ディスプレイ・ウィンドウの定義された座標に時刻を表す文字列を出力します。また、println
関数を使用してコンソールにも時刻を表す文字列を出力します (図 5 の左下を参照)。
図 5. Processing アプリケーションでテキストを使用する
ここからは、Processing を使って作成したシミュレーションをいくつか見ていきます。最初に紹介するシミュレーションは、森林火災モデルを実装する 2 次元セル・オートマトンの実装です。このモデル (Chopard と Droz による「Cellular Automata Modeling of Physical Systems」から引用) は、格子状の木々の成長と落雷による火災の広がりを説明する単純なシステムです。シミュレーションは、以下のように定義された単純なルール・セットからなります。
- 何もない場所 (茶色) では、木は成長確率 pGrowth で成長する
- 隣接する木が 1 本でも燃えていれば、その木はやがて燃えている木 (赤色) となる
- 燃えている木 (赤色) は、やがて何もない場所 (茶色) になる
- 隣接する木が 1 本も燃えていない木は、やがて燃焼確率 pBurn の燃えている木となる。このような木は、例えば落雷の結果として発生する
これらのルールがエンコードされた update
関数 (リスト 5 を参照) が 2 次元空間を繰り返し処理し、定義されたルールに従って状態がどのように変化していくかを決定します。この 2 次元空間は、実際には 3 次元空間であることに注意してください。なぜなら、空間のコピーを 2 つ (現在の繰り返し処理用と、前回の繰り返し処理用にそれぞれ 1 つ) 保持しているためです。このようにして、変更による空間の破損を回避します。繰り返し処理の後、空間はディスプレイ空間 (表示される内容が含まれる空間)、そして計算空間 (ルールが適用される空間) となります。これは、空間が生成されるごとにディスプレイ空間と計算空間は交互に置き換わるという仕組みです。
全体的に見て、このアプリケーションは Processing のグラフィックス・キーワードをほんの少ししか使用していません。空間にはわずかな数の色だけが定義されていて、stroke
によって色が変更され、point
によってピクセルが描画されます。この Processing モデルを使用して、draw
関数は update
関数を呼び出してルールを適用し、update
関数から処理が返された時点で更新後の空間をディスプレイ・ウィンドウに出力します。
リスト 5. セル・オートマトン森林火災モデル
int[][][] pix = new int[2][400][400]; int toDraw = 0; int tree = 0; int burningTree = 1; int emptySite = 2; int x_limit = 400; int y_limit = 400; color brown = color(80, 50, 10); // brown color red = color(255, 0, 0); // red; color green = color(0, 255, 0); // green float pGrowth = 0.01; float pBurn = 0.00006; boolean prob( float p ) { if (random(0, 1) < p) return true; else return false; } void setup() { size(x_limit, y_limit); frameRate(60); /* Initialize to all empty sites */ for (int x = 0 ; x < x_limit ; x++) { for (int y = 0 ; y < y_limit ; y++) { pix[toDraw][x][y] = emptySite; } } } void draw() { update(); for (int x = 0 ; x < x_limit ; x++) { for (int y = 0 ; y < y_limit ; y++) { if (pix[toDraw][x][y] == tree) { stroke( green ); } else if (pix[toDraw][x][y] == burningTree) { stroke( red ); } else stroke( brown ); point( x, y ); } } toDraw = (toDraw == 0) ? 1 : 0; } void update() { int x, y, dx, dy, cell, chg, burningTreeCount; int toCompute = (toDraw == 0) ? 1 : 0; for (x = 1 ; x < x_limit-1 ; x++) { for (y = 1 ; y < y_limit-1 ; y++) { cell = pix[toDraw][x][y]; // Survey area for burning trees burningTreeCount = 0; for (dx = -1 ; dx < 2 ; dx++) { for (dy = -1 ; dy < 2 ; dy++) { if ((dx == 0) && (dy == 0)) continue; else if (pix[toDraw][x+dx][y+dy] == burningTree) burningTreeCount++; } } // Determine next state if (cell == burningTree) chg = emptySite; else if ((cell == emptySite) && (prob(pGrowth))) chg = tree; else if ((cell == tree) && (prob(pBurn))) chg = burningTree; else if ((cell == tree) && (burningTreeCount > 0)) chg = burningTree; else chg = cell; pix[toCompute][x][y] = chg; } } } |
図 6 に、ルール・セットの効果がわかるように、セル・オートマトン森林火災モデルの繰り返しを抜粋して示します。Time 0 には何もない空間があるだけで、ここに木が成長していきます。Time 40 になると、火災によって木が燃えているのがわかるようになり、火災は最終的に空間全体に広がっていきます。Time 100 のあたりになると、木が成長しているのが明らかになってきますが、Time 120 の時点でさらに火災が発生し、これまでと同じプロセスが繰り返されます。
リスト 6. セル・オートマトン森林火災モデルによる出力
感染症 (SIR: Susceptible/Infected/Recovered) モデルは、病院内での病気の広がりをシミュレートするモデルです。森林火災モデルと同じように、SIR は単純なルール一式によって実装されていますが、複雑で興味深い振る舞いが加わっています。このモデルでは、患者のベッドがグリッドとなります。Time 0 の時点では、すべての患者は新しい病気にかかりやすい状態となっています。つまり、患者たちはその病気にかかったことがないため、感染する可能性があるということです。四方にいる 4 人の患者のうちの 1 人が病気にかかると、その中心にいる患者はτ (tau) の確率で感染します。感染した患者の病状は K 日間続き、その間、他の患者に病気を伝染させる可能性があります。K 日後に患者は回復し、病気に対する抵抗力を持つことになります。
前に説明したサンプル・アプリケーションと同様に、setup
関数により、病気に感染した中心の患者を除くすべての患者が未感染の状態となるように病院を初期化します。この実装では、0
が未感染、1-K
が感染、-1
が回復を表します。draw
関数が形状をディスプレイ・ウィンドウに出力し、update
関数が SIR ルールを実装します。前のサンプル・アプリケーションと同じく、現在表示されている形状と現在処理中の形状を保持するために、3 次元の配列を使用します。リスト 6 に、このコードを記載します。
リスト 6. Processing での SIR モデル
int[][][] beds = new int[2][200][200]; int toDraw = 0; int x_limit = 200; int y_limit = 200; color brown = color(80, 50, 10); // brown color red = color(255, 0, 0); // red; color green = color(0, 255, 0); // green int susceptible = 0; int recovered = -1; float tau = 0.2; int k = 4; boolean prob( float p ) { if (random(0, 1) < p) return true; else return false; } void setup() { size(x_limit, y_limit); frameRate(50); for (int x = 0 ; x < x_limit ; x++) { for (int y = 0 ; y < y_limit ; y++) { beds[toDraw][x][y] = susceptible; } } beds[toDraw][100][100] = 1; } void draw() { update(); for (int x = 0 ; x < x_limit ; x++) { for (int y = 0 ; y < y_limit ; y++) { if (beds[toDraw][x][y] == recovered) stroke( brown ); else if (beds[toDraw][x][y] == susceptible) stroke( green ); else if (beds[toDraw][x][y] < k) stroke( red ); point( x, y ); } } toDraw = (toDraw == 0) ? 1 : 0; } boolean sick( int patient ) { if ((patient > 0) && (patient < k)) return true; return false; } void update() { int x, y, cell; int toCompute = (toDraw == 0) ? 1 : 0; for (x = 1 ; x < x_limit-1 ; x++) { for (y = 1 ; y < y_limit-1 ; y++) { cell = beds[toDraw][x][y]; if (cell == k) cell = recovered; else if (sick(cell)) cell++; else if (cell == susceptible) { if (sick(beds[toDraw][x][y-1]) || sick(beds[toDraw][x][y+1]) || sick(beds[toDraw][x-1][y]) || sick(beds[toDraw][x+1][y])) { if (prob(tau)) cell = 1; } } beds[toCompute][x][y] = cell; } } } |
Processing での SIR モデルによる出力を図 7 に示します。この図では、緑色のピクセルが未感染の患者を表し、赤色が感染した患者、茶色が回復した患者を表します。病状が続くのが 4 日間で、周りの患者が病気にかかる確率が 20 パーセントだとすると、病気は病院中に不規則に広がって多くの患者が感染しますが、ところどころに未感染の患者が集まる部分が残ることになります。
図 7. Processing での SIR モデルによる出力
この記事が皆さんの Processing に対する興味を駆り立て、この卓越したオープンソースの言語と環境を学ぶきっかけとなったようであれば幸いです。次回の記事では、より高度な Processing の機能を取り上げ、別のアプリケーションをさらに紹介します。具体的には、オブジェクト指向プログラミング、画像処理、粒子群について、さらにアプリケーションを Java アプレットとしてエクスポートする方法を説明する予定です。
学ぶために
- Processing.org では最新バージョンの Processing 言語と環境をダウンロードして、完全な言語マニュアルにアクセスできる他、Processing がどこで使用されているかも調べることができます。Processing 言語が Java などの他の言語とどのように異なるかについても学んでください。
- Processing は、MIT Media Lab の John Maeda が率いる Design By Numbers プロジェクトから誕生しました。このプロジェクトは、ビジュアル・デザイナーとアーティスト向けのプログラミング入門として作成されたものです。言語に明らかな違いはいくつかあるものの、Design By Numbers の環境が Processing の環境に大きな影響を与えていることは明らかです。
- セル・オートマトン森林火災モデルと SIR モデルは、単純なルール一式による興味深く複雑な振る舞いを明らかにします。
- Processing の最も優れたマニュアルの 1 つとして挙げられるのは、『Processing: A Programming Handbook for Visual Designers and Artists』(Casey Reas、Ben Fry 共著、MIT Press、2007年) です。さらに、『Getting Started with Processing』(Casey Reas、Ben Fry 共著、O'Reilly Media、2010年) もリリースされています。
0 件のコメント:
コメントを投稿