[Go to “teaching” in Morimoto Lab]

Drawing & Pattern 0

描画と模様について演習していきます。

まず、少しプログラムに慣れるように、線の描画の演習をします。

次に、基礎知識として、
・スコープ(変数の寿命)、
・プログラムの構成、
・関数(functionとも言う)
について学びます。

次回以降では、木の描画、模様の描画、数式を使った描画などをします。


線による描画はCGの基本要素です。
これに絡めた研究を以下にいくつか紹介します
少し古いものは、最新の研究では計算方法などは置き換わっているものが多いかと思いますが、こういうトピックスがあるんだなと思っていただければ。

ちなみに、CG研究は論文と一緒にデモ&説明動画が添えられていることが多いです。
ここでは動画があれば動画をまず見ることをお勧めします
(皆さんも最終課題でこのような動画を作ることになるかもしれません)

1. Sketch系インターフェース
描画はCG分野ではスケッチ入力としてよく登場します。

2. 植物や植物柄のモデリング
植物や植物柄のモデリング(形作る)を以下に簡単に行うか、という研究もたくさんあります。

3. Line Neatening & Beautification
描画した線画をきれいに美化する研究もあり、Adobeソフトなどで使われたりします。(そもそもAdobeがこのような研究をしていたり)

4. 検索と合成
少し変わり種?描画した内容を表すような画像合成手法の元祖。


2. 線で模様を作る

それでは線の描画です。
まず練習で、しましまを描きましょう。
・・・。
次に、for文でしましまを描きましょう。
色や線の間隔/範囲/位置、などを変えてみてどうなるか見て理解してください。

以下のサンプルプログラムの実行結果が以下の画像になります。
実行してみて、幅を変えたり、色や位置を変えたりしてみてください。

Alt text

[sample code A]

size(500,500); //window size
background(255); //background color
//draw a line
strokeWeight(5); //line weight(sen haba)
stroke(0); //line color
line(0,0,100,100); //line drawing
//start x, y, end x, y
//draw lines by for
stroke(50,200,50); //line color RGB (tree-like color)
for(int i=0 ; i < 200; i++){
line( i*10,100,
i*10,200);
}
//draw lines using different colors
strokeWeight(1);
for(int i=0 ; i < 255; i++){
stroke(50,i,50);//color
line( i,200,
i,300);
}

次に課題です。
以下のような画像を作るため、ステップを分けています。

Alt text

Ex A:まず、縦じま、または、横じまを作ってみる。

Ex B:次に、形を1/4だけ作る

Alt text
↑ヒント

Ex C:形を全て作る

Ex D:色をグラデーションさせる


3. スコープ

ここまでの話題と少しかわるのですが、今後、アニメーションやインタラクションに対応するため、
GameByBallを思い出してみてください。

少し基礎的な知識を整理します。

何気なく使ってきたintやfloatなどのデータのことを、変数と言います。
その変数は、定義する位置によって、使える範囲が違います。
この変数の使える範囲のことをスコープといいます。

変数(variable) : intやfloatで作ったデータのこと。配列やクラスで作るものも。

ブロック : 中括弧 {} でまとめられた部分。forやif文にもあるし、setup()やdraw()のあとにもありますね。(その他、自分で作りたいところにブロックは作ってもエラーにはなりません。)

スコープ : 変数の有効範囲のこと。ブロックの中で作られた変数はそのブロックの中だけで有効です。

グローバル変数 : ブロックの外で作られた変数のこと。有効範囲はそのプログラム上のすべてになります。

ローカル変数 : ブロックの中で作られた変数のこと。有効範囲はそのブロック内になります。

Alt text

上の図を見ながら、以下を確認してください。

4. プログラムの構造化

Processingのプログラムの構成は大体3つになります。
以下の例を見てみましょう。

//構成する前

int a;
a=0;
a++;
println(a);//a is 1

//構成した後

//★1. usually define global data (variables).
int a;
//★2. only once at first time
void setup()
{
size(300,300); //size() must be here.
a=0;
}
//★3. repeated after setup
void draw()
{
a++;
println(a);//a is 1,2,3,4,5,…
}

前後でプログラムの挙動は変わってしまうので、「こうした方がいい」というものではありません。
(場合によっては前者のsetupやdrawのないプログラムを選択すべきかもしれないです。それも含めて)
ここで言いたいのは、

Point:
どこに何を書くべきか、判断しましょう。
計算コストやエラー防止、プログラムの可読性(わかりやすさ)などで判断しますが、大体作法は以下です。
グローバル変数:あまり増やさない。この場所(通常setupより上のファイルの最初のところを指す)に処理を書かない。
setup:最初に一回だけ行うべき処理を書く。グローバル変数の初期化など。

高みを目指す人へ:読みやすいコードを書くためのおすすめの本

Point:
コードが長くなりすぎて、Processingのプログラムファイル(.pde)一つでは管理が大変になってきたら、ファイルを追加することができます。 (作り方の参考
この時の注意として、ファイルを追加した場合、それは最初のファイルの続きのような扱いになりますので、異なる別のプログラムにはなりません


余裕のある人へ: 最初に線で描画したプログラムをsetup()やdraw()を使って構造化してみましょう。


5. オリジナルの関数を作ろう

setup()やdraw()、println()なども関数と言いますが、processingの方で元から用意されているものです。
以下のように、オリジナルの関数を作ることもできます。
setup, draw以外は勝手に名前を付けて作りました。
作った関数はsetupやdrawから呼び出して使います。
setup, drawなどと違って実行するタイミングは、それが記述されている箇所のタイミングになります。

関数(function) : 特定の機能や処理をひと固まりにしたもの。
引数(ひきすう, parameter): println(a);などと書いた場合のaのこと。関数に渡す変数、または、値。

5.1. 関数を使ううま味

同じプログラムでも関数を使うと色々と便利である。
以下を実行してみましょう。

Alt text

[sample code B]

void setup() {
size(700, 700); //window size
background(255); //background color
noFill();
//顔の描画
strokeWeight(9); //line weight
stroke(50, 200, 50); //line color RGB
rect( 0,0,300, 250);
rect( 50,100,30, 30);//left eye
rect( 220,100,30, 30);//right eye
rect( 80, 180, 150, 20);//mouth
//drawFace();
//drawFace2(300);
}
void drawFace()
{
strokeWeight(9); //line weight
stroke(50, 200, 50); //line color RGB
rect( 0,0,300, 250);
rect( 50,100,30, 30);//left eye
rect( 220,100,30, 30);//right eye
rect( 80, 180, 150, 20);//mouth
}
void drawFace2(float x)
{
strokeWeight(9); //line weight
stroke(50, 200, 50); //line color RGB
rect( x+0,0,300, 250);
rect( x+50,100,30, 30);//left eye
rect( x+220,100,30, 30);//right eye
rect( x+80, 180, 150, 20);//mouth
}

rect()はProcessingで用意してある四角形を描く関数です。
最初の引数が四角形の左上のx座標、次がy座標、3番目は横幅、4番目が縦幅です。
rectの詳しい使い方はこちら


rect()のあとにコメントアウトで隠れているdrawFace()には、setup関数内にある顔の描画と同じプログラムが書いてあります


では、
setup内の //顔の描画 のあとの6行を削除orコメントアウトし、
drawFace()をアンコメントアウトしてください
最初と同じ内容が描画されたと思います。

さて、何がうれしいでしょうか?

…もともと6行だったところが1行で書けました!(setup関数内だけ見れば)
見やすくなりましたね!
関数名もdrawFaceなどにすると、更に分かりやすくなります。
が、これだけではまだあまりうれしくありません。


このままだと関数の恩恵があまりないので、少し工夫します。
関数には指定した変数を持たせることができます。
このような関数に渡せる数値のことを引数(ひきすう)と言います。
上記のプログラムにもう一つある関数、drawFace2の(float x)が引数です
使い方は、例えばsetupで記述する場合、括弧にfloatの値を書くと、xにその値を入れてdrawFace2内のプログラムを実行します。

この関数、中身はほとんどdrawFaceと同じですが、すべてのrectの最初の値に、drawFace2の引数xが足してあります
drawFace2を案コメントアウトすると以下のように、顔を簡単にずらした位置に書けます。
引数の値を少し変えるだけで、簡単にずらせます。

Alt text Alt text

関数のうま味:
このように関数を使うと、一行で命令を呼び出せます。
見やすいコードを描くのにも便利ですし、
似たようなコードなら引数を工夫すれば一つの関数で色々な処理をカバーすることもできます。


Ex. E: 上記のプログラムを変更し、setup()内でfor文を使ってdrawFace2をたくさん呼び出し、位置を変更して顔をたくさん表示してください。

ウィンドウサイズが足りなければsize()の値を変えて大きくするなど。
顔の描画自体を変えてもいいです。

Alt text
↑顔をたくさん

Alt text
↑例えば、3つの丸を関数で描画するように変更

Alt text
↑その関数をforでたくさん呼び出す


余裕のある人へ:引数と乱数とforを使いこなそう。

Alt text

  1. drawFace2に引数を追加して、顔等の色を引数で変えれるように変更してください。

  2. drawFace2に引数を追加して、yの位置もずらせるようにしてください。

  3. for文を使ってdrawFace2をたくさん呼び出し、その際にrandomで色と位置を変更して顔等をたくさん表示してください。

上記2のHint: ランダム関数の使い方
random(50) で、0-50の間のfloat型の乱数を返します。
random(-10, 50) などとすると、-10~50の間のfloat型の乱数を返します。
[詳しくはこちらのリンクを参照]


☕Coffee Break「名付けの重要性」
結構大事な話で、オリジナルの関数は

などの名づけの決まりがあります。
でも、同じ名前の関数でも、引数が異なればOKです。
例えば以下のように関数を並べて定義しても、エラーにはなりません。

void drawFace(){
strokeWeight(5); //line weight(sen haba)
stroke(50,200,50); //line color RGB
for(int i=0 ; i < 200; i++){
line( i*10,100,
i*10,200);
}
}
void drawFace(int offsetY){
strokeWeight(5); //line weight(sen haba)
stroke(50,200,50); //line color RGB
for(int i=0 ; i < 200; i++){
line( i*10,100+offsetY,
i*10,200+offsetY);
}
}

「まぁでもとりあえず同じ名前の関数を作るのはやめとこ」 と思いました?
関数や変数の名前は重要で、これでプログラムの読みやすさが変わってきます。
プログラムの読みやすさは気分の問題とかでなく、プログラムの技術としてとても大事です。
類似する機能を持った関数は、同じ名前を付けた方が分かりやすいかもしれません。
ちなみに、同じ名前で異なる引数の関数を定義することを関数のオーバーロードといいます。


Point: 関数や変数の名前の付け方
これも大事な話です。上記の関数drawFace()が
myFunc()
など、「関数だったらなんでもいい」みたいな名前になっていたら、
setupだけ見たときに何が書いているかわからず、
プログラムの可読性が低くなります
わかりにくい、読みにくいプログラムということですね。
同じように変数名も
int a;
とするより、
int linenum; //line number = 線の数
などと、意味を持たせた方がわかりやすくなります。