"プログラミング"でビジュアルアートができるようになるソフトウェアです。例えばこんな、
こんなこんなこともできます。
Processingでは比較的簡単にプログラムを書くことができるのでプログラミングの入門に向いています。Processingを通してプログラミングの基礎を学んでいきます。
Processingはサイトからダウンロードしてきてインストールするだけで、プログラミング環境が整います。
以下サイトからダウンロードしてください。寄付の必要はありません。「No Donation」にチェックを入れてください。
https://processing.org/download/
エディタの見方です。エディタ部分にソースコードを記述し、再生ボタンでプログラムを実行します。コンソールには色々な情報が出力されます。
まずは初期設定です。Macならメニューバーの"Processing"のところをクリックして環境設定を選択してください。Windowsならファイル>設定と進みます。
以下を設定してください。
設定したら、一度再起動して下さい。
以下のプログラムをコード入力画面にコピー&ペーストしてください。
//プログラムを実行したとき、始めに一回だけ実行されるブロック
void setup()
{
size(700,700); //ウィンドウのサイズを設定するメソッド。700x700に設定
background(0,255,0); //背景色を設定する関数。緑に設定
fill(255,0,0); //図形の塗りつぶし色を設定するメソッド。赤に設定
}
//プログラムを実行したとき、ループして実行されるブロック
void draw()
{
int x = 350; //円の中心のx座標を表すint型の変数、350を代入
int y = 350; //円の中心のy座標を表すint型の変数、350を代入
int d = 200; //円の直径を表すint型の変数、200を代入
ellipse(x,y,d,d); //円(楕円)を描くメソッド
}
実行してみましょう。再生マークのボタンを押します。以下のような画面が出てくれば正解です。
さて、完成したプログラムの保存します。WindowsならCtrl+s,MacならCmd+sを押します。これは上書き保存のショートカットです。まだ一度も保存していないので保存先を指定するウィンドウが開きます。これはただ円を描くだけのプログラムなので、「SimpleCircle」と名前をつけましょう。保存先はどこにしたかしっかり確認しておきましょう。
今はまだソースコードを読んでも何が起きているかさっぱりだと思います。まずはプログラミングとProcessingの基礎事項を学ぶ必要があります。
Processingは"プログラミング"でビジュアルアートができると言いました。Processingについて知る前に、まずは一般的なプログラミングの基本について知ります。(分かりやすくするためにちょくちょく嘘をついています。Javaが分かる人が読んだらごめんなさいって感じです。)
プログラムは基本的に行単位で上から順に実行されます。よって命令を記述する順番が大事になってきます。ただし、プログラミング言語には特殊な構文が用意されているので、この限りではありません。
プログラミングではソースコードのここではどんな処理をしているのか、注釈を残すことをします。これをコメントといいます。コメントはパソコンにソースコードの一部として解釈されなくなります。ソースコード中に出てくる //(スラッシュ2個)以下はコメントです。
リテラルとはプログラミング用語でソースコードに直接記述された"値"のことです。値とは、数値や文字列などさまざまです。プログラミングは基本的に数値を計算したり、データを加工したりするものです。
10 //これは整数リテラル
5.2 //これは浮動小数点リテラル(単純には小数を含む数のこと)
"こんにちは、ぼくドラえもんです" //これは文字列リテラル
文字列リテラルはダブルクォーテーションで囲うことに注意して下さい。同じ数字でも、
2017 //これは整数リテラル
2017.0 //これは浮動小数点リテラル
"2017" //これは文字列リテラル
と、書き方によって異なります。
変数とは値を格納するもののことです。どういうことかというと、
int a = 10; //int型の変数aを宣言して10を代入する
float pi = 3.141592 //float型の変数piを宣言して3.1415921を代入する
String str = "22世紀の猫型ロボット" //String型の変数strを用意して文字列を代入
このように書くとaの中には10という値が保持され、piには3.141592という値が、strの中には"22世紀の猫型ロボット"という値が保持されます。
変数を使うためにはその変数がどんな種類の値を保持しているのか明記する必要があります。それらを変数の型と言います。変数の名前の手前についているintやfloatやStringが変数の型を表しています。intは整数型を表し、整数値を保持できます。floatは浮動小数点型を表し、小数点を含む数を保持できます。String型は文字列型を表し、文字列を保持できます。
変数を使うためにはその変数をこれからプログラム上で使うということを明記しなければいけません。それを変数の宣言といいます。変数の宣言をするときは、その変数にどんな値を保持させるのか、変数の型を指定してやる必要があります。
int num; //int型の変数numを宣言
float f; //float型の変数fを宣言
String s; //String型の変数sを宣言
変数の宣言ができました。次に宣言した変数に値を入れていきます。この変数に値を入れることを代入といいます。代入するときは、
//変数の宣言は済んでいるものとする。
num = 2112; //numに2112を代入する。aには2112が保持される。
f = 129.3; //fに129.3を代入する。fには129.3が保持される。
s = "頭テカテカ さえてピカピカ"; //sに文字列を代入。sには文字列が保持される。
と"="を使って書きます。宣言のときに型を指定したので代入のときは型は必要ありません。変数の宣言と代入は同時に行うことができます。この項の一番最初に挙げた例がそれです。
変数への値の代入はリテラルによるものだけでなく、変数を使うこともできます。どういうことかというと、
int a = 10; //aに10を代入
int b; //bには何も入っていない
b = a; //bにaを代入。bには10という値が保持される
ここで混乱しがちなこととして、数学では"="という記号を「同値」という意味で使うのに対してプログラミングでは「代入」という意味で使うといううことがあります。イメージとしては、
ソースコードにb=a
と書いてあると、何かbとaが同じものであるという意味に捉えがちです。しかしプログラミング言語でb=a
と書いた場合、aの保持している値をbにコピーするという意味になります。
足し算や引き算などの書き方について学びます。
4 + 5 //9
3 - 5 //-2
21 * 3 //63
18 / 3 //6
19 % 3 // 1
2.1 * 3 // 6.3
5.0 * 3 // 15.0
5.5 / 2 // 2.25
かけ算とわり算は*(アスタリスク)と/(バックスラッシュ)です。%(パーセント)は剰余演算子といいます。前の数を後ろの数で割ったときの余りを表します。
通常の四則演算と同じでかけ算とわり算が足し算、引き算よりも優先されます。括弧で囲むことで、括弧内を優先的に計算させることもできます。
8 + 3 * 4 //20
(8 + 3) * 4 //44
計算結果は変数に代入することができます。
int a = 10;
int b = 10 * 3 / 5;
int c = a * 4; //cには10*4の結果である40が代入される。
例えばこれは角度からラジアンを計算しています。
float pi = 3.141592;
float degree = 30; //角度
float rad = degree * pi / 180.0; //ラジアン
文字列の足し算もできます。
String boku = "僕、";
String dora = "ドラ";
String emon = "えもん";
String str = boku + dora + emon + "です。"; //僕、ドラえもんです。
ただし引き算はできません。
今は何に使うか分からないと思いますが、後で使うので覚えて下さい。
a >= 10 //aは10以上である
a > 10 //aは10より大きい(10は含まない)
b <= c //bはc以下である
b < c //bはcより小さい(cは含まない)
x == 5 //xは5と等しい
同値はイコールが2つになります。条件式はつなげることができます。
x > 10 && x < 20 //xは10より大きいかつ20より小さい
x <= y || 10 > z //xはy以下またはzが10より小さい
x == 5 && y == 10 && z == 7 //xは5かつyは10かつzは7
関数(メソッド)とはある機能を持った命令のことです。例えばprintln関数はコンソール(エディタの見方の部分を見直してみて下さい)に文字を表示する機能を持っています。このようなコードを実行してみます。
float f = 129.3;
println("こんにちは、ぼくドラえもんです。");
println("身長:" + f + "cm " + "体重:" + f + "kg");
するとこのようにコンソール上に文字列が表示されます。
「プログラムを実行してみよう」の項まで戻ってソースコードを見てみてください。ellipse関数がみつかるはずです。ellipse関数も円(楕円)を描くという機能を持った関数です。「文字を表示する」機能や「円を描く」機能を持つ2つの関数ですが、どちらも機能しかないので、どんな文字列を表示するのか、どんな大きさの円を描くのかなどを関数に教えてやる必要があります。この関数に教えてやる値のことを、関数の引数(ひきすう) と言います。引数に与えられる値は、変数かリテラルです。ellipse関数を例に見てみます。
int x = 70; //中心のx座標を表す
int y = 50; //中心のy座標を表す
int d = 30; //円の直径を表す
ellipse(x, y, d, d);
ellipse関数は4つの引数を必要とします。1番目は中心のx座標、2番目の中心のy座標、3番目は楕円の横の直径の長さ、4番目は楕円の縦の直径の長さを指定します。円を描く場合は3番目と4番目に同じ値を入れればよいです。
関数の中には計算した結果を返すものがあります。例えばsin関数は引数にラジアンの値を取り、結果として正弦の数値を返します。このように使います。
float pi = 3.141592;
float degree = 30; //角度
float rad = degree * pi / 180.0; //ラジアン
float result = sin(rad); //sin関数を実行し、その計算結果をresultに代入する。
//つまり、resultには30度のsinの値が入る
この返ってくる値のことを、関数の引数に対して、戻り値と言います。戻り値はあるとは限りません。例えばellipse関数は円を表示したらそれで終わりなので、戻り値はありません。
ある条件のときのみ実行して欲しい命令がある、場合によって実行する命令を変えたい、そんなときは条件分岐構文を使います。これをif-else文と言います。このように書きます。
if(条件式)
{
//条件式が真であるときのみこのブロック内のコードが実行させる
}
else
{
//どの条件式も満たさないとき
}
条件式は演算子の項で勉強しました。具体例を見てみます。
int a = 7;
if(a <= 10)
{
println("aは10以下です");
}
else
{
println("aは10以下ではありません");
}
aの値が10以下であるときのみコンソールに「aは10以下です」と表示されます。ですから、aに代入する値を15などにすると、「aは10以下ではありません」と表示されます。条件式を満たさないとき、特に何もしなくてよいと言うときは、elseは必ずしも書く必要はありません。
//random()は0から引数に与えた数未満のランダムな数を返す関数
float f = random(10); //fは0以上10未満となる
if(f < 5.0) //fが5未満のとき
{
println("fは5未満だよ");
}
//↑fが5以上のときは特に何もしない
println("fの値=" + f);
同じような処理をまとめて繰り返し処理をする構文があります。1つはwhile文と言います。このように書きます。
while(条件式)
{
//条件式を満たしている間だけ繰り返し実行する
}
繰り返し処理のとき、変数が活きてきます。具体例を見てみます。
int i = 1; //int型の変数iを用意して1を代入
while(i <= 10) //iが10以下の間
{
println("これは" + i + "回目");
i = i + 1; //iの数を1増やす
}
実行してみるとこのようになります。
while文の中身が繰り返し実行されていることが分かります。変数の値を毎回1ずつ増やしているので、iが10を超えたところで繰り返し処理は終わります。結果的に10回実行されることになります。
繰り返し処理にはもう一つ種類があります。それをfor文といいます。このように書きます。
for (初期化式; 条件式; 再初期化式)
{
//繰り返し実行される
}
言葉で書いてもよくわからないと思うので、while文で挙げた例のコードをfor文に書き直してみます。
for(int i = 1; i <= 10; i = i + 1)
{
println("これは" + i + "回目");
}
繰り返し処理ではこの繰り返しが一体何回目なのか管理する変数(この場合iのこと)をよく使います。その宣言と回数を増やす処理を一行で書けてしまう点がwhile文より優れています。繰り返し処理には2つの書き方があることを覚えて下さい。
何気なく書いていましたが、セミコロンが気になった方も多いと思います。プログラミングでは変数の宣言や値の代入と関数を書いたとき、その最後に;(セミコロン)を書く必要があります。
int num = 100;
println("100倍した数は" + num * 100) //←セミコロンのつけ忘れ
println("0.1倍した数は" + num * 0.1);
セミコロン1つ付け忘れるだけでエラーが出て実行できなくなります。何かエラーが出たときはセミコロンのつけ忘れを疑ってみて下さい。
ソースコードのこの部分ではこんなことをやっているとメモ書きをしたいというときはコメントアウト機能を使います。//(バックスラッシュ2本)でそれ以降の同一行内はコメントとして認識されます。
String str = "ここは実行されるけど"; //バックスラッシュ以下は
//コメントとなり
//認識されなくなる
println(str);
コメントアウトして書かないと、コメントして書いたつもりの文章もプログラムの一部として認識されてしまいます。
さて、プログラミングの基礎を勉強しました。ここからはProcessingの機能について学んでいきます。ここで改めて、一番始めのコードを読み直してみます。
//プログラムを実行したとき、始めに一回だけ実行されるブロック
void setup()
{
size(700,700); //ウィンドウのサイズを設定する関数。700x700に設定
background(0,255,0); //背景色を設定する関数。緑に設定
fill(255,0,0); //図形の塗りつぶし色を設定する関数。赤に設定
}
//プログラムを実行したとき、ループして実行されるブロック
void draw()
{
int x = 350; //円の中心のx座標を表すint型の変数、350を代入
int y = 350; //円の中心のy座標を表すint型の変数、350を代入
int r = 200; //円の直径を表すint型の変数、200を代入
ellipse(x,y,r,r); //円(楕円)を描く関数
}
プログラミングの基礎を学んだことによって大分読めるようになったのではないでしょうか。でもまだvoid setupやらvoid drawやら分からないところがありますね。
Processingにはsetupブロックとdrawブロックがあります。setupブロックにはプログラムの一番最初に一度だけ実行したい処理を書きます。対してdrawブロックには繰り返し実行したい処理を書きます。ですから、Processingでプログラミングをするときは、以下のコードがテンプレートとなります。
//一度だけ実行される
void setup()
{
}
//繰り返し実行される
void draw()
{
}
Processingでは画面上での位置を座標で決めます。ただし数学の座標とは向きが違うので注意して下さい。以下のコードを実行してみます。
void setup()
{
size(700,700); //画面サイズを700x700にする
background(220,220,220); //背景色を灰色にする
fill(255,0,0); //図形の塗りつぶし色を赤にする
stroke(0,0,255); //線の色を青にする
strokeWeight(3); //線の太さを3にする
}
void draw()
{
ellipse(500, 400, 200, 200); //(500, 400)の位置に直径200の円を描く
}
左上を原点としてx座標は右に、y座標は下に伸びています。例の場合、右下の座標が画面サイズと同じ(700,700)となります。
画面の縦横の長さはwidth、heightと書くと参照できます。ですから、画面の中央に円を表示させたい場合、以下のようなコードとなります。
ellipse(width/2, height/2, 200, 200); //画面の中央に直径200の円を描く
Processingには図形の色や線の色、背景の色など、色の指定が必要な関数が多くあります。色を指定するときはRGBを使います。RGBは赤、青、緑の三色を基本色として、その順番で、それぞれの色の濃さを0から255の間で決めます。例えばこのように使います。
//fill関数は図形の塗りつぶし色を決める関数
//(R,G,B)=(0,255,0)なので緑のみ
fill(0,255,0); //図形は緑色で塗りつぶされることになる。
//stroke関数は図形の線の色を決める関数
//(R,G,B)=(200,0,100)なので赤と青が混ざった色
stroke(200,0,100); //図形は赤に近い紫色で塗りつぶされることになる
色については直感でわかりづらいので色見本を見ながら決めると良いです。
http://www.colordic.org/
図形は描いた順番に上書きされるということに注意して下さい。次のようなプログラムを実行してみます。rect関数は四角形を描く関数です。
void setup()
{
size(700,700); //画面サイズを700x700にする
background(220,220,220); //背景色を灰色にする
stroke(0,0,0); //線の色を黒にする
}
void draw()
{
fill(255,0,0); //図形の塗りつぶし色を赤にする
ellipse(width/2, height/2, 200, 200); //画面の中央に直径200の円を描く
fill(0,191, 255); //図形の塗りつぶし色を水色にする
rect(width/2, height/2, 150, 150); //左上の座標を画面の中央とする150x150の四角形を描く
}
すると以下のように円の上に四角形が上書きされた状態になります。
これはellipse関数を実行してからrect関数を実行したためです。これを逆にしてみると…
void setup()
{
size(700,700); //画面サイズを700x700にする
background(220,220,220); //背景色を灰色にする
stroke(0,0,0); //線の色を黒にする
}
void draw()
{
fill(0,191, 255); //図形の塗りつぶし色を水色にする
rect(width/2, height/2, 150, 150); //左上の座標を画面の中央とする150x150の四角形を描く
fill(255,0,0); //図形の塗りつぶし色を赤にする
ellipse(width/2, height/2, 200, 200); //画面の中央に直径200の円を描く
}
このように位置関係も逆になります。
さて、長いことプログラミングの勉強をしてきました。やっとProcessingの実践演習に入れます。問題形式にしたので考えながら進めてみてください。
青色の四角形を中央に描いてみます。
void setup()
{
size(700,700); //ウィンドウのサイズを700x700にする
fill(0,0,255); //塗りつぶし色を青にする
}
void draw()
{
rect(width/2, height/2, 100, 100); //100x100の四角形を描く
}
widthとheightという変数はウィンドウの横と縦の長さが保存されているため、width/2,height/2は画面の中央の座標を表すことになります。
このプログラムには問題点があります。中央の座標を指定したにもかかわらず中央より若干右下になってしまっています。これはrect関数が四角形の左上の座標を指定するためです。これを修正して正しく中央に表示されるようにしてください。
Processingでは透明度を指定することができます。色を指定する関数(fill関数やbackground関数など)では第3引数まででRGBを指定していました。これに加えて第4引数に透明度として0-255の値を指定できます。値が大きいほど透明度は低くなります。
さて、以下のようなコードを考えます。
void setup()
{
size(700,700); //画面サイズを700x700にする
background(255,255,255); //背景色を白色にする
stroke(0,0,0); //線の色を黒にする
}
void draw()
{
fill(255,0,0); //塗りつぶし色を赤に設定
ellipse(350, 300, 200, 200);
fill(100,255,0); //塗りつぶし色を黄緑色に設定
rect(180,150, 300, 300); //100x50の四角形を描画
}
コード上では円と四角形の2つの図形を表示しているのですが、実行してみると分かる通り、円しか表示されません。これは、四角形が描画された上にそれよりも大きい円が描画され、覆い被さっているためです。
上記のコードを改修して、透明度を指定することにより円の下の四角形が見えるようにしてください。(円の座標を変えるとかはなしです!)
図形を動かすことを考えてみたいと思います。以下のコードを見て下さい。
int x = 0; //円の現在のx座標を保存する変数
void setup()
{
size(700,700); //画面サイズを700x700にする
background(255,255,255); //背景色を白色にする
}
void draw()
{
stroke(0,0,0); //線の色を黒に指定
fill(0,0,255); //塗りつぶし色を青に設定
ellipse(x, height/2, 200, 200);
x = x + 1; //x座標を1増やす
if(x > width) //xの値が画面サイズより大きくなったら
{
x = 0; //x座標を0にする(円は右端に戻る)
}
}
drawブロックは繰り返し実行されるのでxの値は徐々に大きくなっていき、円は右へと移動します。でも実行してみると分かる通り、思っていたのと違い、変な軌跡が残ってしまいます。これはProcessingが図形を上書きしていく仕様のために、前に描画したxを増やす前の図形が残ってしまうためです。これを回避するためには、background関数で画面全体を背景色で塗りつぶす方法が有効です。
background(255,255,255); //背景色と同じ色を指定
上記の画面をクリアするbackground関数を正しい位置に配置して、軌跡が残らないようにしてください。
以下のコードをエディタに貼り付けて、一度保存して下さい。
PImage img;
void setup()
{
size(700, 700);
img = loadImage("cat.png");
}
void draw()
{
background(200);
image(img, mouseX, mouseY, 150, 150);
}
次に、いらすとやからイラストを拝借します。
コードを保存したフォルダと同じフォルダに、右クリックで「cat.png」として保存して下さい。そうした状態で実行すると、猫がマウスに追従するプログラムが動きます。
まずはsetupブロック内についてです。ここで新しく出てきたのはPImage
という型やloadImageという関数です。float
やint
は数値を保存する型でしたが、同様にPImage
は画像データを保存する型です。また、loadImage関数は指定したパスから画像を読み込む関数です。PImage
型の変数img
にcat.pngの画像データを保存したことになります。
次にdrawブロック内についてです。ここで新しく出てきたのはimage
関数です。この関数は指定された座標に画像を表示する関数です。image(PImageの変数, x座標, y座標, 横幅, 縦幅);
といった使い方をします。mouseX
及びmouseY
はマウスの現在の座標が入っています。
準備中…
ちょっとわかりづらいですが、赤色の円を動かして、さらにそれをぐるぐる円を描くように回転させてみます。さて、そのプログラムはこちらです。
float degree = 0; //角度を保存する変数
float r = 150; //回転の半径を保存する変数
//一番初めに一度実行される
void setup()
{
size(700,700); //ウィンドウのサイズを700x700にする
background(0,0,0); //背景色を黒にする
}
//毎フレーム実行される
void draw()
{
//画面をクリアする
fill(0,0,0); //塗りつぶし色を黒にする
rect(0,0, width, height); //黒色の四角形を描く
degree = degree + 5; //角度を5度増やす。degree += 5;と書いても同じ。
if(degree >= 360) //もし角度が360度以上だったら
{
degree = 0; //角度を0にする(degreeに0を代入する)
}
//sin(),cos()はラジアンを引数にとり、三角関数の値を返す関数
float rad = radians(degree); //角度をラジアンに変換し、変数radに代入する。
float x = width/2 + r * cos(rad); //円を描くx座標を計算し、xに代入する。
float y = height/2 + r * sin(rad); //円を描くy座標を計算し、yに代入する。
fill(255,0,0); //塗りつぶし色を赤にする
ellipse(x,y,100,100); //直径100の円を描く
}
一気に長くなりましたね。if文はカッコの中身の条件式を満たすときのみ、ブロックの中身を実行するというものでした。条件式は例にあげたような>=(以上)の他に、<=(以下)、>(より大きい)、<(より小さい)、==(同じ、イコールは二つ)などがあることは勉強しましたね。
上のプログラムを改修し、楕円を描くように動くようにしてください。楕円の大きさは自由で構いません。
ヒント:今は半径rを用意して計算していますが、代わりに長半径と短半径を指定する新たな変数a,bを用意します。楕円の式は以下の通りです。
x = a × cos(θ)
y = b × sin(θ)
さて、繰り返し処理を利用したプログラムを作ってみます。以下のプログラムはランダムな色の円を横一列に並べるプログラムです。
int N = 10; //並べる円の数
void setup()
{
size(700, 350); //700x350にする
background(0, 0, 0); //背景色を黒にする
colorMode(HSB, 360, 255, 255); //カラーモードをHSB色空間にする。
noStroke(); //線を描かない
fill(color(random(360), 200, 200)); //ランダムな色にする
}
void draw()
{
//繰り返し処理
for(int i = 1; i <= N; i = i + 1) //iの値は1からNまで1ずつ増える
{
float spanx = width / N; //円同士の間隔を指定する。Nの数に合わせてうまく全ての円が収まるようにしている
float d = (width / 2) / N; //描く円の直径
ellipse(i * spanx - d , height/2, d, d); //iの値が増えるほどx座標が増える
}
}
実行すると以下ようになります。
for文ではループの回数を重ねる毎に自動的に変数iを増やすことが出来るのは前に確認しました。
Nは描く円の数を表しています。変えてみるとどうなるかぜひ試してみてください。
HSBとはRGBとはまた別の色の指定方法です。気になる方は調べてみてください。
先のプログラムを改造して以下のように縦にならべるようにして下さい。
ヒント:for文の中に、y方向の間隔を保存する変数spanyを作成し、現在のwidthを用いたコードを参考にしてみて下さい。
マウスがクリックされたときに何かアクションをするといったプログラムを考えます。Processingではマウスのクリックやマウスの位置を検出する機能があります。以下のコードを見て下さい。
//javaを勉強したら分かります。
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
Ripple rip;
List<Ripple> ripList = new ArrayList<Ripple>();
void setup()
{
size(700,700);
colorMode(HSB, 360,255,255);
background(0, 0, 0);
}
void draw()
{
fade();
Iterator<Ripple> iter = ripList.iterator();
while(iter.hasNext())
{
Ripple rip = iter.next();
rip.update();
rip.display();
if(rip.getDiameter() > width)
{
iter.remove();
}
}
}
//クリックされたときに実行される
void mousePressed()
{
ripList.add(new Ripple(width/2, height/2)); //波紋を追加する
}
void fade()
{
noStroke();
fill(0,0,0,50);
rect(0,0,width, height);
}
class Ripple
{
private float x, y;
private float d;
private color c;
public Ripple(float x, float y)
{
this.x = x;
this.y = y;
this.d = 1;
this.c = color(random(360), 200, 200);
}
public float getDiameter()
{
return d;
}
public void setDiameter(float d)
{
this.d = d;
}
public void update()
{
d += 5.0;
}
public void display()
{
noFill();
stroke(c);
strokeWeight(5);
ellipse(x, y, d, d);
}
}
マウスのクリックを検出するとランダムな色の波紋を生成するプログラムです。実行してウィンドウ上で適当に何度かクリックしてみて下さい。すると以下のようになると思います。
どこでマウスがクリックされたときの動作が記述されているかというと、上記のソースコードの以下の部分になります。
//クリックされたときに実行される
void mousePressed()
{
ripList.add(new Ripple(width/2, height/2)); //円を追加する
}
Processingにはsetupブロックとdrawブロックに加えて、mousePressedブロックが用意されています。このブロックには、名前の通り、マウスがクリックされた時の動作が記述できます。今回の場合、プログラムの詳細は省きますが、簡単には波紋を追加する命令を書いています。そのため、クリックされる度に波紋が増える処理が実現できました。
上記のプログラムでは、画面上のどこをクリックしても画面の中心から波紋が生成されていました。これは波紋を生成する座標を(width/2, height/2)としたためです。これを改変して、画面をクリックしたところを中心に波紋を生成するようにしてください。現在のマウスの位置は、width,heightと同じようにmouseX,mouseYで取得できます。以下のようになります。
次のプログラムを実行してみてください。
import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
List<Circle> cirList = new LinkedList<Circle>();
void setup()
{
size(1280,720);
colorMode(HSB, 360, 100, 100);
background(0);
noStroke();
}
void draw()
{
if(Math.random() < 0.75)
{
cirList.add(new Circle(random(width), random(height), 0, color(180, 50, 100)));
}
updateCircles();
drawCircles();
fade();
}
void updateCircles()
{
float vRadius = 5.0; //半径変化量
float maxSize = 100; //最大半径
Iterator<Circle> iter = cirList.iterator();
while(iter.hasNext())
{
Circle cir = iter.next();
cir.setRadius(cir.getRadius() + vRadius);
if(cir.getRadius() > maxSize )
{
iter.remove();
}
}
}
void drawCircles()
{
for(Circle cir : cirList)
{
cir.draw();
}
}
void fade()
{
fill(0, 0, 0, 15);
rect(0, 0, width, height);
}
class Circle
{
private float x, y, r;
private color c;
public Circle(float x, float y, float r, color c)
{
this.x = x;
this.y = y;
this.c = c;
this.r = r;
}
public void setRadius(float r)
{
this.r = r;
}
public float getRadius()
{
return r;
}
public void draw()
{
fill(c);
ellipse(x,y, r,r);
}
}
すると以下のようになります。
random関数を利用することでランダムな位置に円を表示させています。random関数の使い方は以下の通りです。
random(50) //0<=x<50のランダムな浮動小数点数
random(20, 100) //20<=x<100のランダムな浮動小数点数
int(random(30)) //0<=n<30のランダムな整数
Math.random() //0<=x<1のランダムな浮動小数点数
ランダムな位置に表示させることは出来ましたが、色についてもランダムにしてみましょう。以下のようになるようにプログラムを改変してください。(ヒント:draw関数内の一部のパラメータをランダムにします。)
次のプログラムを実行してみてください。
void setup()
{
size(700,700);
translate(width/2,height/2);
circlesR(0, 0, 350, 4);
}
void draw()
{
}
void circlesR(float x, float y, float r, float n) //nは繰り返し回数
{
ellipse(x, y, r*2, r*2);
if(n <= 1) return;
float newR = r/2;
circlesR(x+newR, y, newR, n-1); //右側の円
circlesR(x-newR, y, newR, n-1); //左側の円
}
実行すると次のようになります。
circlesR関数に着目すると、関数内でcirclesR自身をまた呼び出していることが分かります。このような関数を一般に再帰関数と呼びます。再帰関数は「深さ優先探索」と呼ばれるアルゴリズムを実装したり、数学の漸化式を解いたり出来ます。circlesR関数の動きを追っていくと、円を描いてその円の内側にさらに2つの円を描いて…を繰り返していることが分かると思います。setup関数内で呼び出しているcirclesR関数の第4引数(今は4になっている)を書き換えるとどんな風になるかも試してみてください。
上記のプログラムでは円の内側の左右2つに円を配置していました。これを改造して円の上下左右に配置するようにしてみてください。n=6で実行すると以下のようになります。
再帰関数を応用してフラクタルを作ってみます。次のプログラムを実行してみてください。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
void setup()
{
size(700, 700);
FractalGenerator gen = new FractalGenerator();
// 座標を指定しています。追加した順番で線が結ばれます。
gen.add(new PVector(50, 450));
gen.add(new PVector(200, 450));
gen.add(new PVector(220, 150));
gen.add(new PVector(240, 450));
gen.add(new PVector(650, 450));
gen.show(1);
}
void draw()
{
}
class FractalGenerator
{
List<PVector> vecs = new ArrayList<PVector>();
PVector p0, pp, v0;
public void add(PVector p)
{
if(p0 == null)
{
p0 = p;
pp = p;
return;
}
vecs.add(PVector.sub(p, pp));
v0 = PVector.sub(p, p0);
pp = p;
}
public void show(int n)
{
pushMatrix();
translate(p0.x, p0.y);
rotate(v0.heading());
rec(1, n);
popMatrix();
}
private void rec(float rate, int n)
{
pushMatrix();
float pRad = v0.heading();
for(PVector v : vecs)
{
float rad = v.heading();
float len = v.mag();
rotate(rad - pRad);
if (n == 1)
{
line(0, 0, rate * len, 0);
}
else
{
rec(rate * len / v0.mag(), n - 1);
}
translate(rate * len, 0);
pRad = rad;
}
popMatrix();
}
}
何の変哲もない次のような図形が表示されます。
次に、上記のコードを以下のように変えて再度実行してみてください。
gen.show(1)
→
gen.show(2)
すると次の画像のようになります。
これをよく観察すると、初めの図形の各辺を図形自身に置き換えたものだと分かります。さらに次のようにコードを変えます。
gen.show(2)
→
gen.show(5)
すると次の画像のように複雑になります。
数字は各辺を自分自身で置き換える回数を指定してました。このように自己相似形が繰り返し発見できる図形のことを一般にフラクタルと呼びます。
次の画像のような曲線を描くにはどのように座標を指定すればいいか考えてください。n=5です。ちなみにこの曲線はコッホ曲線と呼ばれています。
google先生に聞きましょう。Processingにかぎらずプログラミングで困ったらググればたいていどこかの誰かが答えてくれてます。例えばProcessingで四角形を描きたいのに関数の名前を忘れてしまいました。そういうときは「Processing 四角形」などとググります。
この資料では触れませんでしたがProcessingは「Java」というプログラミング言語で記述されています。Processingをもっとできるようになりたい!という方はJava言語の勉強から初めてみてください。
Processingでよく使う数値や命令(プログラミング用語で関数と言います)をまとめておきます。あとで分からなくなったりしたときに見て下さい。