SFC – デザインとプログラミング 2017
反復と乱数
プログラムを構造化していく際に、3つの重要な構成要素があります。それは、「順次 (Sequence)」、「反復 (Iteration)」、「分岐 (Selection)」 です。今回は、この3つの構成要素の中の反復 (Iteration) に注目して、Processingで形を描きながら反復について考えます。また、一意的でないランダムな値である「乱数」と反復を組合せて、どのような表現か可能となるか探求していきます。
スライド資料
反復 (Iteration)
- コンピューターは、「くりかえし」の天才!
- 最近のマシンであれば、1秒間に2億回(2GHz)以上のクロックで、正確に計算をくりかえすことができる
- 反復は、プログラムの基本的で重要な構造のひとつ
構造化プログラミング、3つの構成要素
- 順次 (Sequence)、反復 (Iteration)、分岐 (Selection)
30年前のPCでのプログラムを再現
- Commodore 64
- HELLOをひたすらくりかえす
10 PRINT “HELLO” 20 GOTO 10
- 実行結果 – プログラムを止めるまで、延々と“HELLO”を出力
- ちょっとアレンジ
10 PRINT “/\”; 20 GOTO 10
- 波の模様が延々と続く
- さらに、乱数(後述)を利用して…
10 PRINT CHR$(205.5+RND(1)); 20 GOTO 10
- 迷路のような複雑な模様が浮かびあがる
- Mac OS Xのターミナルでも、以下のコードで同じ現象を再現可能
yes 'c=(╱ ╲);printf ${c[RANDOM%2]}'|bash
参考: 10 PRINT CHR$(205.5+RND(1)); : GOTO 10
- では、Processingをつかって、くりかえしの実験をしてみましょう!
変数
- クイズ: 正方形の中に、ちょうど半分の幅と高さの正方形を描く
- 答
size(400,400); rect(100,100,200,200); rect(100,100,100,100);
- クイズ2:さらに半分の正方形を中に
- 答
size(400,400); rect(100,100,200,200); rect(100,100,100,100); rect(100,100,50,50);
手で計算するのではなく、もっとかしこくできないか
- 描いた四角形の常に半分のサイズで次の四角形を描く
- 現在のサイズの値を記録できると便利なのに…
- 「値を記録する」→ 変数を利用する
変数
- 変数とは?
- 一時的に値(文字、文字列、数字など)を記憶しておく場所
- データを入れておく「箱」のようなもの
- データ型 – 値の種類
- Processingでよく用いられるデータ型
- int:整数 (-1, 0, 1, 2, 3….)
- float:少数 (-0.01, 3.14, 21.314)
- boolean:ブール値、真か偽か、(true, false)
- char:1文字分のデータ(a, b, c, d…)
- color:色の情報を保存
- 宣言:使用する変数の名前の箱を準備する
int hoo;
- 代入:変数の箱に値を入れる
hoo = 0;
- 演算:変数の値を計算する
hoo = hoo + 1;
- 先程の例は、変数を利用すると下記のようになる
size(400,400); float rectSize = 200; rect(100,100,rectSize,rectSize); rectSize = rectSize / 2; rect(100,100,rectSize,rectSize); rectSize = rectSize / 2; rect(100,100,rectSize,rectSize); rectSize = rectSize / 2; rect(100,100,rectSize,rectSize); rectSize = rectSize / 2; rect(100,100,rectSize,rectSize); rectSize = rectSize / 2; ...
- どんどん小さな正方形を描いていくことができる
- 変数を利用して、いろいろ実験してみましょう
- 変数の使用例
size(800, 600); background(0); noStroke(); fill(0, 127, 255, 100); float x, y, diameter; x=width/2; y=height/2; diameter=height; ellipse(x, y, diameter, diameter); diameter = diameter / 1.4; ellipse(x, y, diameter, diameter); diameter = diameter / 1.4; ellipse(x, y, diameter, diameter); diameter = diameter / 1.4; ellipse(x, y, diameter, diameter); diameter = diameter / 1.4; ellipse(x, y, diameter, diameter);
– 実行結果
繰り返し – for文
- さっきのプログラムに注目
diameter = diameter / 1.4; ellipse(x, y, diameter, diameter); diameter = diameter / 1.4; ellipse(x, y, diameter, diameter); diameter = diameter / 1.4; ellipse(x, y, diameter, diameter); diameter = diameter / 1.4; ellipse(x, y, diameter, diameter); ...
- 繰り返しの部分を何度も書かずに一気に指定したい
diameter = diameter / 1.4; ellipse(x, y, diameter, diameter);
例えば、これを10回くりかえすという指定はできないだろうか? → for文を使う!
- for文の書きかた
for (初期化式; 継続条件式; 再初期化式) { 文; }
- 初期化式:初期化の際の条件式
- 継続条件式:繰り返しを継続する条件式
- 再初期化式:繰り返されるたびに実行される式
Processingでfor文を使ってみる
- forを用いた、繰り返しの例 1
int i; for (i=0; i < 100; i=i+1){ print("+"); }
- forを用いた、繰り返しの例 2
int i; for (i=0; i < 100; i=i+1){ print(i + ", "); }
- 同心円をくりかえしを用いて描画
//初期条件の設定 size(800, 600); background(0); noStroke(); smooth(); fill(0, 127, 255, 40); float x, y, diameter; x = width / 2; y = height / 2; diameter = height; int i; //同心円を描いていく for (i=0; i < 32; i++) { ellipse(x, y, diameter, diameter); diameter = diameter / 1.2; }
くりかえしによる入れ子構造
- for文を使ってくりかえし図形を描く
- 工夫することで図形が入れ子状に連なった図形を描くことが可能
入れ子状の矩形
size(800, 600); background(0); stroke(0); fill(0, 127, 255, 100); float x, y, w, h; x = 0; y = 0; w = width; h = height; for (int i = 0; i < 32; i++) { w = w / 2; h = h / 2; rect(x, y, w, h); rect(x + w, y + h, w, h); }
入れ子状の円
size(800, 600); background(0); stroke(0); fill(0, 127, 255, 100); float x, y, diameter; x = width/4; y = height/2; diameter = width/2; for (int i = 0; i < 32; i++) { ellipse(x, y, diameter, diameter); ellipse(x + diameter, y, diameter, diameter); diameter = diameter / 2; x = x / 2; }
回転のくりかえし
size(800, 800); background(0); noStroke(); fill(0, 127, 255, 30); float diameter; diameter = width/1.8; translate(width/2, height/2); rotate(PI/4); for (int i = 0; i < 256; i++) { ellipse(diameter / -2, 0, diameter, diameter); ellipse(diameter / 2, 0, diameter, diameter); diameter = diameter / 1.05; rotate(PI / 24.0); }
色について – RGBモデルとHSBモデル
- 色を指定するには?
- R(赤) G(緑) B(青)の三原色で指定する
- 加法混色 (光の三原色であることに注意) ←→ 色料の三原色
- RGB以外の方法での色の指定
- HSB (HSV)による指定
- 色相 (Hue):色の種類
- 彩度 (Saturation):色の鮮かさ
- 明度 (Brightness):色の明かるさ
- HSB (HSV)による指定
- プログラミングしながら色を指定する場合は、HSBの方が直感的に望みの色を指定し易いことも多い
- HSB (HSV) 色空間の視覚イメージ
- 色相:外環の角度
- 彩度:中心点からの距離
- 明度:高さ
- HSB (HSV) 色空間への切替方法
- colorMode関数を使用する、
- それぞれのパラメータの範囲も同時に指定する
- 例:
- 色相:360階調(°)
- 彩度:100階調(%)
- 明度:100階調(%)
colorMode(HSB, 360, 100, 100);
繰り返しによる色の表現
- HSB (HSV) 色空間と繰り返しを使用して、色の変化をつかった様々な表現に挑戦
例:虹色のグラデーション
//変数の宣言 int i; //初期設定 size(800,600); colorMode(HSB, 360, 100, 100); noStroke(); //色相を変化させながら四角を描いていく for(i=0; i < width ; i++){ fill(i * 360.0/800.0,100,100); rect(i,0,1,height); }
乱数
- プログラムは、設定された手続きを何度でも繰り返す
- そのままでは、予想外の挙動はしない
- 完全に全てをコントロールするのではなく、偶然性、意外性をとり入れたい
- 実行する度に毎回異なる値を出力する仕組み
- 乱数
- 何ら法則性、規則性のない、でたらめな値を出力
- ランダム
Processingで乱数を生成 – 乱数の範囲を指定する
- 例:0から100の乱数を生成
random(100);
- 例:100から1000の乱数を生成
random(100, 1000);
ランダムに色を塗る
//ランダムに色を塗る //変数の宣言 int i; //初期設定 size(800, 600); colorMode(HSB, 360, 100, 100); noStroke(); //ランダムに色を塗る for (i=0; i < width ; i++) { fill(random(360), random(100), random(100)); rect(i*1, 0, 1, height); }
似たような色相で
//似たような色相で色を塗る //変数の宣言 int i; //初期設定 size(300,300); colorMode(HSB, 360, 100, 100); noStroke(); //似たような色相で色を塗る for(i=0; i < 300; i++){ fill(random(200,220),random(100),random(100)); rect(i*1,0,1,300); }
似たような明度と彩度で
//変数の宣言 int i; //初期設定 size(800, 600); colorMode(HSB, 360, 100, 100); noStroke(); //似たような明度と彩度で色を塗る for (i=0; i < width ; i++) { fill(random(360), 60, 90); rect(i*1, 0, 1, width); }
例6:たくさん円を描く
- さらに図形を描く場所もランダムに!
//変数の宣言 int i; //初期設定 size(800, 600); background(0); colorMode(HSB, 360, 100, 100, 100); noStroke(); //たくさんの円を描く for (i=0; i < 100; i++) { float diameter; fill(random(200, 240), random(50, 100), random(50, 100), 50); diameter = random(50, 200); ellipse(random(width), random(height), diameter, diameter); }
たくさん四角形を描く
//たくさんの四角形を描く //変数の宣言 int i; //初期設定 size(800, 600); background(0); colorMode(HSB, 360, 100, 100, 100); rectMode(CENTER); noStroke(); //たくさんの四角形を描く for (i=0; i < 100; i++) { fill(random(0, 40), random(50, 100), random(50, 100), 50); rect(random(800), random(600), random(5, 200), random(5, 200)); }