2011/07/25

Recursion (processing教學)

各位好久不見,著實過了好一陣子沒有更新processing的介紹了.
沒辦法,我只要一找到機會可以打懶可是從來都不客氣的,
所以最近都很心安理得的告訴自己因為要弄迪化街的東西,
沒有做processing介紹也是情非得已啦.

不過好景不常,昨天上課就被道德勸說是不是該繼續processing的研究了?
套用我爸的名句"伸頭是一刀,縮頭也是一刀",不如就乖乖繼續吧. :)

------------------------------------------------------------------------------------------------------------------

除了後來才補講的noise之外,
我們的課本進度已經來到如何以void來做2D的GIF動畫了.

http://tesignstudio.blogspot.com/2011/07/blog-post_917.html

而那一篇能含蓋的內容我覺得一該從p173-200應該看一看都會懂,
所以我就不多說了,不過還是自己做過一遍比較穩當囉.
唯一額外需要講的我覺得是p201-205的小單元, Recursion

Recursion顧名思義就是遞迴,循環.
因此我們可以簡單推測如果在使用這方法時,
沒有告訴電腦什麼時候停(一個終點)的話,那它就會一直算到電腦燒起來為止.
以下就先來一盤小菜讓大家簡單了解,p201的22-07.


 void setup(){
  size(200,200);
  background(255);
  smooth();

  noLoop();
}


int a = 10; //a對應的是x
int b = 18; //b對應的是num


void draw(){
  frameRate(10);
  drawLines(a,b);
  //saveFrame("p201-##.jpg");
}


void drawLines(int x, int num){
  strokeWeight(x/30);
  line(x,20,x,180);

  if(num > 0){
    drawLines(x+10,num-1);
  }
}




最重要的部分就是 
  if(num > 0){
    drawLines(x+10,num-1);
  }
我們可以看到在drawLines這集合裡又跑了drawLines,

這意味著drawLines會先把自己內裡的drawLines都跑完,
之後才會再丟給上面的void draw去畫出來.(很饒舌我知道.)

所以到底它是怎麼跑的呢? 
首先我們知道b = num = 18,
因此一開始跑就會符合if(num > 0)
因為每畫一次就會drawLines(x+10,num-1);
到減第18次時num都還會大於1,
同時還要加最原本的那條,所以線會依序畫19條,
至於x方面因為drawLines(x+10,num-1);因而為呈現為
                line(  x  ,  20  ,  x  ,  180  );
第00代            10            10

第01代            20            20
第02代            30            30
第03代            40            40
                          .               .
                          .               .
                          .               .
第17代          180           180
第18代          190           190

所以我們知道recursion其實是在還未丟入void draw的集合內進行其內部的自行運算.
再來進階一點的是p203的22-10,以下


void setup(){
  size(200,200);
  background(255);
  noStroke();
  smooth();
  noLoop();
}


int xx = 100;
int r = 85;
int n = 4;


void draw(){
  drawCircles(xx,r,n); 
  //saveFrame("p203-##.jpg"); 
}


void drawCircles(int x, int radius, int num){
  float tt = 200 * num/4;
  fill(tt);
  ellipse(x,100,radius*2,radius*2);
 
  if(num > 1){
    num -= 1;
    drawCircles(x - radius/2, radius/2, num);
    drawCircles(x + radius/2, radius/2, num);
  }
}


在這部分最重要的是
  if(num > 1){
    num -= 1;
    drawCircles(x - radius/2, radius/2, num);
    drawCircles(x + radius/2, radius/2, num);
  }
除了num的處理跟上面一個案例一樣之外,

我們可以看到在畫完第一個中心為(100,100)的大圓之後,
隨著recursion每次會畫兩個圓,其直徑為之前圓的一半,
同時其兩個圓的圓心會一往左一往右順x軸偏移,
移動大小為之前圓直徑的1/4.
所以最後我們可以看到因為總共算了4次,
除了原本大圓之外還分裂了3次,所以可以看到
1 + 2 + 4 + 8 = 15個大小不一的圓.


所以最後的終極魔王就是p204的22-11,
它不但用if的recursion,同時還在if中加了for,以下.


void setup(){
  size(200,200);
  background(255);
  noStroke();
  smooth();
}


int x = 100;
int y = 100;
int r = 50;
int n = 5;


void draw(){
  frameRate(10);
  drawCircle(x,y,r,n);
 
  //if(frameCount < 200){
  //  saveFrame("p204-###.jpg");
  //} 
}


void drawCircle(float x, float y, int radius, int num){
  float value = 126 * num / 6;
  fill(value,153);
  ellipse(x,y,radius*2,radius*2);
 
  if(num > 1){
    num = num - 1;  // num的多少決定會算幾次
    int branches = int (random(2,6)); //這更可怕,branch的多少決定它之後衍生的數量
    for(int i = 0; i < branches; i += 1){
      float a = random(0,TWO_PI);
      float newx = x + cos(a) * 6 * num;  //每次新的圓心x點會偏移
      float newy = y + sin(a) * 6 * num;  //每次新的圓心y點會偏移
      drawCircle(newx,newy,radius/2,num);
    }
  }
}

如果還看不懂,嘗試把int n改小,而int branches也改成不要random,
同時在setup裡加上noLoop,好好看一張圖是怎麼運算的,可以幫助你理解.
所以只要稍做調整,我們也可以很輕易的做出類似潑油漆的畫作.



void setup(){
  size(200,200);
  background(255);
  noStroke();
  smooth();
}


int x = 100;
int y = 100;
int r = 50;
int n = 8;
int c = 0;


void draw(){
  frameRate(100);
 
  c += 1;
  if(c>2){
    background(255);
    c = 0;
  }

  drawCircle(x,y,r,n);
 
  //if(frameCount < 200){
  //  saveFrame("p204-2-###.jpg");
  //} 
}


void drawCircle(float x, float y, int radius, int num){
  float value = 126 * num / 6;
  fill(value,random(255),random(255),50);
  ellipse(x,y,radius*2,radius*2);
 
  if(num > 1){
    num = num - 1;
    int branches = int (random(2,6));
    for(int i = 0; i < branches; i += 1){
      float a = random(0,TWO_PI);
      float newx = x + cos(a) * 6 * num;
      float newy = y + sin(a) * 6 * num;
      drawCircle(newx,newy,radius/2,num);
    }
  }
}


Try it. :)

沒有留言:

張貼留言