[Go to “teaching” in Morimoto Lab]

Drawing & Pattern 1

もっと関数について学びます。

再帰関数で木を描画します。

また、再帰関数は少し特殊な関数の使い方ですが、今後の講義やプログラミングでは、関数で実装した場合の効率性を考えて、使えるところは積極的に関数で実装してみるようにしましょう!


1. もっと関数を知ろう

オリジナルの関数を定義するときに、関数の頭についているvoidやintなどは関数の戻り値の型と言います。(Processingのsetup()にはvoidが付いていますね!それです)

この型は、関数が返す値によって決めます。
返す値のことを返り値戻り値と言い、関数内でreturnを記述することで返します。

ちなみに、voidはよく関数の頭についていますが、変数の定義に使ったことはありませんでした。
voidは「この関数は何も返しませんよ!」という意味になります。
この場合、return文は省略可です。または、return ;などとして、何も返さない指定をします。
※return ; は関数内のその後のコードを無視してくれるのが結構便利です。

関数には値を入力することができます。これを引数と言います。つまり関数は何かの入力に対して値を返すもので、まさに数学の関数(f(x)=…)ですね。

キーワード(上記のまとめ)
戻り値の型:関数の定義のとき、頭についているvoid, int, float,…などのこと。
戻り値(返り値とも):関数の処理によって返す値のこと。
return文:戻り値を返すときにreturn a;などと、returnを使って値を返す。return文以降の関数の処理は行われない。
void:関数の型で使われた場合、「この関数は何も返しませんよ」という意味。この場合、関数にreturn文は不要。
引数:関数への入力として使われる値。

以下のプログラムを実行して、説明を読みながら、中身を少しアレンジして挙動を確認してみてください。

int a;
void setup()
{
size(300,300);
a=0;
//↓定義した関数を呼び出しています。setupから呼べばhelloは一回だけprintlnされます。
myFuncVoid();
}
void draw()
{
//↓もしここで呼ぶと怖いくらいたくさんhello!と言われます
//myFuncVoid();
//↓returnがある関数でも代入しなければその値は特に使われません
myFuncInt();
//↓returnのある関数は直接代入に使えます。boolなら直接if文に入れてもOKなど。
float aaa = myFuncFloat(5.0);
}
//↓voidがついた関数は何も返しません
void myFuncVoid()
{
println(“Hello!”);
}
//↓intなどのデータ型が付いた関数は、そのデータ型の変数を返します
int myFuncInt()
{
int b = 0;
return b;//関数の最後にreturnで返したい変数を返します。
}
//↓括弧の中に引数を定義できます。渡したい値があるときに使います。複数定義可能。
float myFuncFloat(float b)
{
return a + b + 1.5f;//このaはグローバル変数のaです
}

Note: 関数の 引数 は以下のように複数に増やすことができます.

float myFuncFloat(int a, float b, color c, PImage img)

Note: 関数を呼び出すときに与える引数は、定義されたデータ型と一致していればOKです。
呼び出し側は、数値でもいいですし、変数で指定する場合も同じ名前を使う必要などはありません。

Note: メソッドというのも関数とかなり似た言葉で、関数の一種です。
クラスの中で定義したメンバ関数のことをメソッドと、厳密には言うみたいです。
メンバ関数というのはクラスに属する関数のこと、
クラスはまたのちの講義で説明します(あまり今は気にしなくてよいです)。


Ex A. オリジナルの関数を追加し、引数で与えた位置から、引数で与えた角度の線を描画してください。線の描画角度はsinやcosを使いましょう。

ヒント0:Processingに限らず、2次元のPC画面の原点は左上になり、y座標は上下逆転していることを思い出しておきます。
ヒント1:まず関数を使わずに極座標系で円の式で終点を計算し、始点を原点とした線を描画してみましょう。そのあと関数化するとよいです。角度を少しずつ増やしてアニメーションさせてもイメージしやすいかもしれません。
ヒント2:極座標系の式を使うとよいです。
ヒント3:sin, cosの使い方はリンク先を見てください
ヒント4:上記のsin, cosにはラジアンで入力しないといけません。processingに用意されているPIを使って計算してもいいですし、radians()という関数を使うと角度をラジアンに変換してくれます。
ヒント5:原点中心の線が描画できたら、始点の位置をx,yとした場合、終点にx,yを加算すると、始点を中心に意図した角度の線が描けます。(課題ではここまでできていなくても、x,y,angleの引数で線が描画できていれば可とします。つらい人は次の再帰関数のサンプルを見てみてください。)原点周りでできた線をそのままスライドして移動させるようなことを、「平行移動」と言います。アフィン変換の一部です。

コーヒーブレイク☕
少し先を行く人たち用への話をします。課題が終わってない人は流してください。

CGや画像処理では、平行移動、回転、拡大縮小など(アフィン変換)をよく扱います。
Processingでは以下のような関数でそれらを扱うことができます。
https://processing.org/reference/pushMatrix_.html
本講義では上記URLのような内容は扱いませんが、CGの基礎的なことなので、知っておくとよいです。(アフィン変換については次回の最後に少し詳しく書いています)

関連して、ローカル座標グローバル座標という考え方を紹介します。
個々のモデルの作成(ローカル)と、全体のシーンでの配置(グローバル)は別にした方が扱いやすいという思想に基づいています。

余裕のある人へ. Ex. Aの引数を全て配列にして、2つ以上の線(他の形状でもOK)をそれぞれ指定した位置に表示してみましょう。

これは配列を思い出したり、関数への引数が配列の場合の渡し方に決まりがある(下記)ので、少し難しいです。

Hint: 配列の要素数は.lengthで取得できます。関数内でここを定数などにしてはいけません。エラーが起こります。参照

Hint2: 配列の関数への渡し方は決まりがあるので、最低限の例を以下に書きます。

//定義の例:記述場所などは考えてみて下しさい
int[] xx = new int[3]; //xの位置
//値の代入はxx[0]=...などでもできますが、forを使うと便利です。
//自分で書いてみましょう。
//関数の定義は以下のようになります
void drawText(int xx[]){
//中身は書いて下さい
}
//関数の使用は以下のようになります
drawText(xx);

2. 再起関数で木の描画

再帰関数で描画する対象として、最もポピュラーな「木」を描きましょう。
用意する関数は一本の枝と見立てた「一本の線」を描く関数です。
この関数は描画の「始点・角度・深さ」を引数とする関数であれば、最低限の木が描けます。

Alt text

void setup(){
size(500,300); //window size
background(255); //background color
strokeWeight(5); //line weight(sen haba)
stroke(50,200,50); //line color RGB
branch(width/2, height, -90, 10);
}
void branch(float sx, float sy, float angle, int depth){
if(depth>0){
float rad = radians(angle);
float radius = depth * 5;
//end point
float ex =sx + cos(rad)* radius;
float ey =sy + sin(rad)* radius;
line(sx, sy, ex, ey);
//branch(ex,ey,angle+20, depth-1);
//branch(ex,ey,angle-20, depth-1);
}
}

上記のプログラムでは枝を描く関数の中で、枝を描く関数を呼び出しています
これによって再帰的な処理になります。
depthはどんどん小さくなり、0になったらif文でもうbranch()を呼ばないようにすれば終了です。
!終了処理がうまく働かないコードを作ってしまった場合、ソフトウェアを強制終了させる事態になりますので、気を付けてください!

Alt text


更に枝の太さ、色、長さ、を考慮するために工夫すると、もっとリッチな木が描けます。
枝も線でなく、文字やお花などに変更したり、付け加えたりすれば表現力∞ですね!
以下の課題を通して、オリジナルの木を表現してみてください。

Ex B: 枝の太さや長さを徐々に変更するなど、サンプルとは異なるように工夫してみましょう

Ex C: HSB色空間を使って、色を変える

Ex D: オリジナルのアイデアで面白いものを作ってください。

実・鳥などを付けたり、アニメーションさせてみるなど?
風に揺れるさま、爆発するさま…?
再帰描画に関連することなら木に限らなくてもよいです。

余裕のある人へ: 曲線を使ってみる、または、分岐数を変えるなど…

参考URL←何となく絵を眺めてみるだけでも…

ヒント:

  1. 引数を増やすなどし、最大階層数を関数内で使えるようにすれば、depth/最大階層数、が0~1のグラデーションの値になり、色のパラメータなどとして使えるようになります。

  2. HSB色空間は、H:色相、S:彩度、B:明度、で表され、グラデーションなどを設定する際にRGBより直感的です。公式ページの説明を見て、設定してみてください。
    (HSB色空間についても調べるとよいです)

  3. オリジナルの木はランダムを使ったりしてもよいですし、枝以外のものを使っても面白いかもしれません。参考web

  4. random()関数は手っ取り早く動かしたり、不揃い感を出すのに便利です。

Alt text
HSB色空間

余裕がある人へ: ほかに再帰関数で描画できそうなものに挑戦してください

例えば?

  1. 雪の結晶

  2. シダ植物(Processingじゃないですけど参照

  3. きれいな模様(曼荼羅?魔法陣?)

  4. プラレール!(参考URL

  5. 再帰で模様を作る論文

  6. 再帰なのか微妙ですが、ついでにスピログラフ

  7. シェルピンスキーのギャスケットも係数を変えると面白いそうです

  8. その他:思いつけば研究になるかも!

おまけ:L-System

L-Systemも再帰性があり、自己相似図形やフラクタル図形のような形状を幅広く記述できます。
https://ja.wikipedia.org/wiki/L-system

英語ですが、こちらのテキストが分かりやすく、
Processingのコードもあります。
http://www.erase.net/projects/l-systems/

Processing official web site > Example > Fractals and L-Systems

Penrose Snowflake
Penrose Tile
Pentigree
にもサンプルがあります。

色々なパターンを作るためのL-Systemの構成?は以下のページが参考になります。
http://hiiragicompany.net/ls.html

一番見やすそうなのは、Coding ChallengeL-systemでした!
上記の【コード】です。

自信のある人へ: L-systemの実装に挑戦してみてください。

後日学ぶ「クラス」を知ってないと少し難しいですが…。