09.04 BOIDSv0.00 ~現レル~

随分前になるでしょうか、「自分のパソコンは速いですか?」というように聞かれたら、どのように答えるかと書きました。
そこでどんな速いパソコンでも数を表示している数をどんどん増やしていくと時間内に処理が終わらなくなってタイマーを使用していると、ゆっくりになってきます。このためsin()、cos()などの算術演算関数はやめましょうと書きました。今回初めから鰯の恰好だと処理時間がかかりすぎるので、前から言っているように円形に方向を示す矢印のみを表記する「ポッド」で表すことにします。



課題「ブラウン運動」の場合次のような計算式で移動していましたが、
これと同じ動きで今回も移動します。


{ x 1 = r 1 × cos θ 1 y 1 = r 1 × sin θ 1
次の一歩は下記となります。

{ x 2 = x 1 + r 2 × cos θ 2 y 2 = y 1 + r 2 × sin θ 2

この時に長さrと角度θからなるベクトル v
よるものですが、今回は座標がピクセル(ドット)で整数値であるため、まったく正確に角度や長さを決めることはできません。つまり中心が現在座標(100,100)の場合、x、y成分を±1単位でしか移動することができないということです。



このため、今回移動は現在の座標値(x、y)と移動量(vx、vy)で表現します.すると、この時の角度は次の式で表されます。


θ = arcTan Vy Vx

但し 前に書きましたがコンピュータの世界ではアークタンジェントは二種類あります。この場合は一周をカバーする必要がある為atan2()を用います。

    atan()                -(π/2) ~ (π/2)
    atan2()               -π ~ π




★ 座標




ウィンドウズが描画した世界の中で最も思い込みによる水が多いのが「座標系」であります。 しかも複数あり、数学でいう座標とは少し違っています。


①windows原点
ウィンドウズが起動した際(マルチディスプレイではないと仮定します)画面の左上の角をウィンドウズの原点といいます。個の座標系は数学とは異なりX座標は右方向ですが、Y座標系は下に伸びています。(以降すべての座標はこれ基準ですので下がプラスYとなります)。

②ウィンドウ原点
間違えやすいので①は英語、②はカタカナにしましたが②はプログラムを起動して開くウィンドウの原点を指します。したがってウィンドウズ作成する際、プロパティの「Top」はここが基準となっています。但しウィンドウをどこの位置に表示するかは①基準となります。

③ピクチャーボックス原点
今回使用しているビットマップイメージの原点は③が原点となります。

④描画サークルの先頭座標原点
,Netサークル描画すると移動するための原点は中心ではなく④の位置です。


課題「ブラウン運動」の際はランダムウォークなのであまり気にもしませんでしたが、例えばグラフのように計算上の象限と画面が一致していないといけないような場合は、Y座標の符号を反対にする必要があります。また今回のようにポッドが移動して相互に作用するような場合、考えやすいようにサイクル中心が移動原点となるように予め半径だけ移動させる必要があります。また表示の象限が数学上の第一象限しかないのでピクチャーボックスの原点を中心へ移動させるのも手かもしれません。またはすべてプラスになるように移動位置を作成するかです。

いずれにしても、上記の座標系を知って作成しないと失敗ばかり起こるのは目に見えています。






★ 作ってみます
ブラウン運動プログラムを応用して一部を組み込んでみます。

【データ宣言部】
ブラウン運動ではArrayクラスを試験的に用いたが、今回構造体配列が都合がよかったため、初心者らしく通常の構造体配列を用いてみました。
実施は構造体配列をどうやってarrayクラスで作るかを知らない!!


#include <math.h>
#define SCREEN_SIZE_YOKO  1250  /* スクリーンの横幅 */
#define SCREEN_SIZE_TATE  670   /* スクリーンの縦幅 */
#define IWASHI_NUM     100000 /* ボイドの数 配列決定用最大値 */
#define IWASHI_SIZE     7    /* ボイドの大きさ */
#define MAX_SPEED      5    /* ボイドの最大速度 */

struct iwashi_tab {   /* IWASHIの本体 */
  int     x;   /* 座標     */
  int     y;
  int     vx;   /* x方向の速度 */
  int     vy;   /* y方向の速度 */
  double   a;
  int     temp_x;
  int     temp_y;
} iwashi[IWASHI_NUM];

 Graphics^		gf;
 Bitmap^		canvas;
 System::Drawing::Pen ^ PodPen; /* ポッドに使用されるペン */

	MyForm(void)
{

  PodPen = gcnew Pen(Color::Blue, 1);
  PodPen->EndCap = System::Drawing::Drawing2D::LineCap::ArrowAnchor;



【処理部】
今回は速度要因(vx,vy)は一定値のまま進行します。
DrawEllipseとDrawLineの内容が複雑なのは、項目「座標」を参照してください。

/************************************************************************
* 位置の設定と速度の更新                        *
************************************************************************/
void draw_part(void)
{
  double   r;
  int     i;
  double   speed;

  gf->Clear(Color::White);

  for (i = 0; i<Iwashi_Count; i++)
  {
    speed = sqrt(iwashi[i].vx ^ 2 + iwashi[i].vy ^ 2);/* 速度の更新 */
    if (speed >= MAX_SPEED) /* 最大速度で丸める */
    {
      r = MAX_SPEED / speed;

      iwashi[i].vx = (int)((double)iwashi[i].vx * r);
      iwashi[i].vy = (int)((double)iwashi[i].vy * r);
    }

    if (((iwashi[i].x < 0) && (iwashi[i].vx < 0))
          || /* ウィンドウリミットによる丸め */
      (((iwashi[i].x+ LSize) > SCREEN_SIZE_YOKO)&&  (iwashi[i].vx > 0)))
    {
      iwashi[i].vx *= -1;
    }

    if (((iwashi[i].y < 0) && (iwashi[i].vy < 0))
          ||
      (((iwashi[i].y+ LSize) > SCREEN_SIZE_TATE)&&  (iwashi[i].vy > 0)))
    {
      iwashi[i].vy *= -1;
    }

    iwashi[i].a = atan2(iwashi[i].vy, iwashi[i].vx);
      /* 半径線を算出するための角度算出 */

    iwashi[i].x += iwashi[i].vx;/* 次の位置算出 */
    iwashi[i].y += iwashi[i].vy;

    iwashi[i].temp_x = (LSize / 2) * cos(iwashi[i].a);
    iwashi[i].temp_y = (LSize / 2) * sin(iwashi[i].a);
      /* 半径線の頂点座標 一応矢印 */

    gf->DrawEllipse(Pens::Black, iwashi[i].x, iwashi[i].y, LSize, LSize);
    gf->DrawLine (PodPen, iwashi[i].x+ (LSize / 2), iwashi[i].y+ (LSize / 2),
        (int)(iwashi[i].x+ (LSize / 2) + iwashi[i].temp_x), (int)((iwashi[i].y+ (LSize / 2) + iwashi[i].temp_y)));
  }
  pictureBox1->Image = canvas;
}

/************************************************************************
* ボイド位置・速度更新初期値設定                    *
************************************************************************/
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
  try {
    Iwashi_Count = int::Parse(textBox1->Text);
    LSize = int::Parse(textBox2->Text);
  }
  catch (Exception^)
  {
    Iwashi_Count = IWASHI_NUM;
    LSize = IWASHI_SIZE;
  }
  Random^ rr;

  rr = gcnew Random();
  for (int i = 0; i<Iwashi_Count; i++)
  {
    iwashi[i].x = rr->Next(0, SCREEN_SIZE_YOKO);
    iwashi[i].y = rr->Next(0, SCREEN_SIZE_TATE);

    iwashi[i].vx = MAX_SPEED;
    iwashi[i].vy = MAX_SPEED;
  }

  timer1->Interval = 40;
  timer1->Enabled = true;
}

private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e)
{
  draw_part();
}

private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e)
{
  timer1->Enabled = false;
}



これを実行すると、こんなように動くはずですけど!!

【BOIDS MODEL V0 00 ~現レル~】