数理生物学演習bio-math10.biology.kyushu-u.ac.jp/~haenoh/CompBio/13... · 2015. 7. 13. ·...

32
数理生物学演習 コンピュータでアニメーションを つくる。動物行動を題材として。 1

Transcript of 数理生物学演習bio-math10.biology.kyushu-u.ac.jp/~haenoh/CompBio/13... · 2015. 7. 13. ·...

  • 数理生物学演習

    コンピュータでアニメーションをつくる。動物行動を題材として。

    1

  • 目標 •  Processingを知る •  簡単なアニメーションを作れるようになる

    課題 •  Processingを用いて、おもしろいアニメーションを作り、工夫した点について教えてください。プログラムを添付してください。

    •  提出先は、[email protected](久保) •  本文中に、所属・名前・学籍番号を書いてください。 •  締め切りは、7月22日(水)です。

    2

  • Processingとはなに

    •  Processingは、可視化表現に使うために作られたプログラミング言語です。

    •  アニメーションを直感的に書き表していくことができます。

    •  Processingのウェブサイト https://processing.org

    アプリケーションがない場合はダウンロードしてください。 (大学のMacにはあらかじめ導入されているはずです。) •  Javaベースの言語なので、Cの文法と似てます。

    3

  • •  Finder → アプリケーション → Processing

    Processingを起動しましょう

    4

  • 画面をつくる

    void setup() {size(400, 400);

    }

    エディタに書きます。

    ここを押します。 再生ボタンみたいなところ。

    画面ができます。

    Reference

    size(x, y);画面の(x, y)座標の最大値を指定する。

    5

  • 画面には座標がある

    void setup() {size(400, 400);ellipse(0, 0, 30, 30);

    }

    エディタに書き加えます。

    Reference

    ellipse(x, y, a, b);円を描かせるFunctionです。 (x, y)は円の中心の座標、a, bはそれぞれ、円の横幅(a), 円の縦幅(b)を表します。

    a

    b

    画面左上が(x, y) = (0, 0)と初期設定されているので、左上の角を中心とした円が描かれます。

    画面右下は(x, y) = (400, 400) 6

  • ランダム・ウォーク •  ランダム・ウォークとはなに  ランダム・ウォークは「気体分子の動き」から「ギャンブラーがカジノで一日に使うお金の動き」にいたるまで、現実で起こる現象をモデル化するときに使われています。 ・ランダム・ウォークの考え方  コイントスを二回して、表裏の組を考えます。4組の出方の確率は等しい。  対応した方向に動き、またコイントスを二回して、……を繰り返すとランダム・ウォークになります。

    Flip 1 Flip 2 Result 表 表 前 表 裏 右 裏 表 左 裏 裏 後

    7

  • ランダム・ウォークのプログラム •  ランダム・ウォークのプログラムを作ります。  自身の座標を(x, y)とすると、ランダム・ウォークのプログラムは以下のように表されます。

    int x;int y;void setup() {

    size(400, 400);background(255);x = width/2;y = height/2;ellipse(x, y, 5, 5);

    }void draw() {

    int choice = int(random(4));if (choice == 0) {

    x++;} else if (choice == 1) {

    x--;} else if (choice == 2) {

    y++;} else {

    y--;}

    ellipse(x, y, 5, 5);}

    8

  • ランダム・ウォークの実行例 •  ランダム・ウォークのプログラムを実行すると、下図のように、小さい円がウィンドウの真ん中(初期位置)から、ランダムに動き始めます。黒い部分は動いた軌跡です。

    9

  • ランダム・ウォークのプログラムの解説1 int x; ← x座標を宣言int y; ← y座標を宣言void setup() {

    size(400, 400);background(255);x = width/2;y = height/2;ellipse(x, y, 5, 5);

    }void draw() {

    int choice = int(random(4));if (choice == 0) {x++;

    } else if (choice == 1) {x--;

    } else if (choice == 2) {y++;

    } else {y--;

    }

    ellipse(x, y, 5, 5);}

    Reference

    setup()

    setup()はプログラムが始まったときに一度だけ実行されるものです。初期状態を決めるために使われます。スクリーンサイズ (size(x, y))や、背景色 (background())を指定します。

    draw()

    draw()は、その中に含まれるプログラムを繰り返し実行します。アニメーションは静止画の繰り返しで作られるので、ここが重要なポイントとなります。

    setup()で初期設定し、draw()で繰り返して、アニメーションを表現します。

    10

  • ランダム・ウォークのプログラムの解説2 int x; int y; void setup() {

    size(400, 400);background(255);x = width/2;y = height/2;ellipse(x, y, 5, 5);

    }void draw() {

    int choice = int(random(4));if (choice == 0) {x++;

    } else if (choice == 1) {x--;

    } else if (choice == 2) {y++;

    } else {y--;

    }

    ellipse(x, y, 5, 5);}

    Reference

    background()

    background()はウィンドウの背景色を指定します。

    setup()ではウィンドウサイズ、背景色、円の初期位置を決め、円を1回描いている。

    background(51);

    background(255, 204, 0);

    widthsize()で決めた横幅の値を返す。

    heightsize()で決めた縦幅の値を返す。

    11

  • ランダム・ウォークのプログラムの解説3 int x; int y; void setup() {

    size(400, 400);background(255);x = width/2;y = height/2;ellipse(x, y, 5, 5);

    }void draw() {

    int choice = int(random(4));if (choice == 0) {x++; ←右へ移動

    } else if (choice == 1) {x--; ←左へ移動

    } else if (choice == 2) {y++; ←上へ移動

    } else {y--; ←下へ移動

    }

    ellipse(x, y, 5, 5);}

    Reference

    random()

    random(a)は0以上a未満の値をfloatで返す。つまり、a=1だとすれば、random(1) = 0.31415などの値を取ります。

    random(4)で0以上4未満の値を得るが、それをint(random(4))とすることで、小数点以下を切り捨てて、とり得る値を0,1,2,3のいずれかに限定する。 このとき、それぞれの値が出る確率は等しいので、それぞれの値に対して方向を指定すれば、ランダムウォークをさせることができる。

    12

  • ランダム・ウォークのプログラムの解説4 int x; int y; void setup() {

    size(400, 400);background(255);x = width/2;y = height/2;ellipse(x, y, 5, 5);

    }void draw() {

    int choice = int(random(4));if (choice == 0) {x++;

    } else if (choice == 1) {x--;

    } else if (choice == 2) {y++;

    } else {y--;

    }background(255);ellipse(x, y, 5, 5);

    }

    最後にellipse()で円を描く。 プログラムの流れとしては、 i.  setup()で初期設定 ii.  draw()内、random()を使って0,1,2,3

    の値を同確率で出させる。 iii.  0,1,2,3のそれぞれの値に方向を指定す

    る。(x, y)の座標を更新。 iv.  新しい(x, y)座標を使って円を描く。 v.  ii.~iv.を繰り返す。Got it!!(^ω^)b

    ここに、background(255)を加えると、軌跡が消えます。試してみてください。 background()は塗りつぶしのイメージ。挿入する位置に注意。

    13

  • 円の動き(座標の変化)は以下の式で表されます。 この4変数を繰り返し更新して移動を表現します。

    ベクトルで動きを表す •  ベクトルで個体の動きを表します。 •  個体に位置(x,  y)と速さv、向きθの変数を持たせます。

    (0, 0)

    (x, y)

    (xnext, ynext )

    θv

    xnext = x + vcosθynext = y+ vsinθ

    14

  • 周期境界で動かす •  端がつながっている空間で動かします。

    double x;double y;double v;double d;void setup() {

    size(400, 400);background(255);x = width/2;y = height/2;v = 4;d = HALF_PI;ellipse(x, y, 10, 10);

    }void draw() {

    x = x + v*cos(d);y = y + v*sin(d);

    if (x < 0) { x = x + width;}if (y < 0) { y = y + height;}if (x > width) { x = x – width;}if (y > height) { y = y – height;}

    background(255);ellipse(x, y, 10, 10);

    }

    15

  • 非周期境界で動かす •  端がつながっていない空間で動かします。反射する。

    double x;double y;double v;double d;void setup() {

    size(400, 400);background(255);x = width/2;y = height/2;v = 4;d = PI/3;ellipse(x, y, 10, 10);

    }void draw() {

    x = x + v*cos(d);y = y + v*sin(d);

    if (x < 0) { x = - x; d = PI - d; } if (y < 0) { y = - y; d = TWO_PI - d; } if (x > width) { x = 2*width - x; d = PI - d; } if (y > height) { y = 2*height - y; d = TWO_PI - d; }

    background(255);ellipse(x, y, 10, 10);

    }

    ※境界指定に抜けがあるかもしれません。四隅とか。お許し下さい。 16

  • Reference

    cos(a), sin(a)

    角度aのコサイン、サインの値を返します。角度aはラジアンで与えられます (From 0 to PI*2)。

    PI

    円周率πの値を返します。ほかにも、 TWO_PI = 2π HALF_PI = π/2 QUARTER_PI = π/4 が使えます。

    練習のためのアイデア ・向きの変数dを少しずつ変化させるようにしたらどうなるでしょうか。 例えば、

    d = d + random(1) – 0.5;を書き加えると。

    17

  • 追うもの、追われるもの •  捕食者(追うもの)と被食者(追われるもの)の関係を表してみましょう。•  まずは、基本のルールを決めます。

    捕食者P

    被食者R

    1.  捕食者Pは被食者Rをいつも追いかける。

    2.  被食者Rは捕食者Pが一定距離まで近づいてきたら逃げる。

    3.  捕食者Pは非周期境界、被食者Rは周期境界で移動できるように試しにしてみます。

    18

  • 追う、追われるのプログラム1

    double px;double py;double pv;double pd;double rx;double ry;double rv;double rd;double rr;double rEscape;void setup() { size(600, 600); background(255);

    px = random(width); py = random(height); pv = 5; pd = random(TWO_PI); fill(100); ellipse(px, py, 10, 10); rx = random(width); ry = random(height); rv = 4; rd = random(TWO_PI); rr = 100; noStroke(); fill(50,20); ellipse(rx, ry, rr*2, rr*2); stroke(0); fill(255); ellipse(rx, ry, 10, 10);} //setup()閉じる

    •  解説は後にあります。

    つづく 19

  • 追う、追われるのプログラム2

    void draw() { double distance = sqrt((rx-px)*(rx-px)+(ry-py)*(ry-py)); px = px + pv*(rx-px)/distance; py = py + pv*(ry-py)/distance; pd += random(1) - 0.5; if (px < 0) { px = -px; pd = PI - pd; } if (py < 0) { py = -py; pd = TWO_PI - pd; }

    if (px > width) { px = 2*width - px; pd = PI - pd; } if (py > height) { py = 2*height - py; pd = TWO_PI - pd; } background(255); fill(100); ellipse(px, py, 10, 10);

    •  つづきです。

    つづく 20

  • 追う、追われるのプログラム3

    if (distance 0 && (ry-py)>0) { rEscape = atan((ry-py)/(rx-px)); } if ((rx-px)0) { rEscape = atan((ry-py)/(rx-px)) + PI; } if ((rx-px)

  • 追う、追われるのプログラム4

    rx = rx + rv*cos(rd); ry = ry + rv*sin(rd); if (rx < 0) { rx = rx + width; } if (ry < 0) { ry = ry + height; } if (rx > width) { rx = rx - width; } if (ry > height) { ry = ry - height; }

    noStroke(); fill(50,20); ellipse(rx, ry, rr*2, rr*2); stroke(0); fill(255); ellipse(rx, ry, 10, 10);} //draw()閉じる

    •  つづきです。

    22

  • 追い、追われプログラムの解説1 double px; ←捕食者Pのx座標double py; ←捕食者Pのy座標double pv; ←捕食者Pの速さdouble pd; ←捕食者Pの向きdouble rx; ←捕食者Rのx座標double ry; ←捕食者Rのy座標double rv; ←捕食者Rの速さdouble rd; ←捕食者Rの向きdouble rr; ←捕食者Pの逃走開始距離double rEscape;void setup() { size(600, 600); background(255); px = random(width); py = random(height); pv = 5; pd = random(TWO_PI); fill(100); ellipse(px, py, 10, 10);

    Reference

    fill(a)

    fill(a)は図形の内側を塗りつぶす関数です。グレースケールの場合は、a = 0で黒に、a = 255で白になります。 また、fill(a, b, c)とすることで、他の色も指定できます。

    捕食者Pと被食者Rの変数をそれぞれ用意します。 fill()を使って、PとRの色を区別しようとしています。

    つづく 23

  • 追い、追われプログラムの解説2 rx = random(width); ry = random(height); rv = 4; rd = random(TWO_PI); rr = 100; noStroke(); fill(50,20); ellipse(rx, ry, rr*2, rr*2); stroke(0); fill(255); ellipse(rx, ry, 10, 10);} //setup()閉じる

    Reference

    noStroke()

    noStroke()は、図形の枠線を消す関数です。逆に、

    二つの円を描いていますが、被食者R自身と被食者Rの逃走開始距離を可視化したものです。逃走開始距離は視界範囲と考えてもいいでしょう。noStroke(), stroke(), fill()を用いて見やすくしています。

    つづく

    stroke(a)

    stroke(a)は、図形の枠線の色を指定する関数です。

    24

  • 追い、追われプログラムの解説3 void draw() { double distance = sqrt((rx-px)*(rx-px)+(ry-py)*(ry-py)); px = px + pv*(rx-px)/distance; py = py + pv*(ry-py)/distance;

    Reference

    sqrt(a)

    sqrt(a)は、aの平方根を返す関数です。

    ここで新たに宣言した変数distanceは、捕食者Pと被食者Rの距離を取らせるものです。 捕食者Pは捕食者Rの位置に向かって動くので、その方向ベクトルの単位ベクトルは、 x成分:(rx-px)/distancey成分:(ry-py)/distance となり、それぞれに速さpvをかけることで、Pの変位を得ることができます。左図。

    つづく

    P  (px,  py)

    R  (rx,  ry)

    方向ベクトル(rx-‐px,  ry-‐py)

    25

  • 追い、追われプログラムの解説4 if (px < 0) { px = -px; pd = PI - pd; } if (py < 0) { py = -py; pd = TWO_PI - pd; } if (px > width) { px = 2*width - px; pd = PI - pd; } if (py > height) { py = 2*height - py; pd = TWO_PI - pd; } background(255); fill(100); ellipse(px, py, 10, 10);

    捕食者Pは非周期境界(端で反射する)の空間で動くので、それを指定しています。 fill(100)で黒に近い灰色を指定し、捕食者Pを表す円を描きます。

    つづく 26

  • 追い、追われプログラムの解説5 if (distance 0 && (ry-py)>0) { rEscape = atan((ry-py)/(rx-px)); } if ((rx-px)0) { rEscape = atan((ry-py)/(rx-px)) + PI; } if ((rx-px)

  • 追い、追われプログラムの解説6 if ((rx-px)==0 && (ry-py)==0) { rEscape = 0; } if ((rx-px)==0 && (ry-py)>0) { rEscape = HALF_PI; } if ((rx-px)0 && (ry-py)==0) { rEscape = 0; } if ((rx-px)==0 && (ry-py)  0

    28

  • 追い、追われプログラムの解説7 rd = rEscape; rd = rd % TWO_PI; } else { rd += random(1) - 0.5; } rx = rx + rv*cos(rd); ry = ry + rv*sin(rd); if (rx < 0) { rx = rx + width; } if (ry < 0) { ry = ry + height; } if (rx > width) { rx = rx - width; } if (ry > height) { ry = ry - height; }

    場合分けで得られた、逃走に適した向きrEscapeを次のときの向き(rd)とします。ただ、rdの定義域は 0 to PI*2 としたいので、% (modulo)で剰余をいちおう取っておきます。 捕食者Pが近くにいないときは、動きに変化をつけたいので、random()項を加えておきます (else{}内)。

    つづく 29

  • 追い、追われプログラムの解説8 noStroke(); fill(50,20); ellipse(rx, ry, rr*2, rr*2); stroke(0); fill(255); ellipse(rx, ry, 10, 10);} //draw()閉じる

    最後に描画します。setup()内と同じ。

    練習のためのアイデア ・行動の規則を増やしてみましょう。 たとえば、捕食者が加速できるとか。 被食者が瞬間移動できるとか。 なんでもいいです。 ・見た目を工夫してみましょう。 すごく近づいたときに色が変わるようにしてみる。 ・捕食者も周期境界で移動できるとしたらどうなるでしょうか。 ちょっと面倒ですが、興味のあるひとは考えてみてください。

    30

  • 課題 •  Processingを用いて、おもしろいアニメーションを作り、工夫した点について教えてください。プログラムを添付してください。

    •  質問や感想があれば書いてください。 •  提出先は、[email protected](久保) •  本文中に、所属・名前・学籍番号を書いてください。 •  締め切りは、7月22日(水)です。

    •  関数のより詳しい説明は、Processingのウェブサイトに。 https://processing.org/reference/

    31

  • 配列を用いた群れの行動 •  配列を用いて群れの行動を表してみましょう。•  基本のルールは1つ:群れの中の個体が向きを合わせようとすること。

    32