テオ・ヤンセンリンク機構を作ってみましょう HOME > テオ・ヤンセンリンク機構 > テオ・ヤンセンリンク機構を作ってみましょう

08.05 テオ・ヤンセンリンク機構を作ってみましょう

今回の「課題」は「別の言語で書かれた描画プログラムをVisualStudio2017に変換する」というものです。下記サイトにプログラムが掲載されていますが、このプログラムはHTML5 のCanvasを用いていますので、このままでは殆どがエラーとなってしまいます。 どのような仕組みを用いたらかけるか、またどのようにアレンジするかを考えながら作成します。

「roombaの日記」のテオ・ヤンセン機構
http://roomba.hatenablog.com/entry/2015/02/25/133951


【データ宣言部】
実際のプログラムを作成する場合の注意点として、下記のようにこのプログラムの作者や参考サイト名などの情報をコメントして記載する必要があります(通常「看板」)。


#pragma once
#include <math.h>
/************************************************************************
*  Project : Theo Jansen StrandBeestのJansen LINKシミュレーション  *
*  create : 2017/09/16                        *
*  参考  : roombaの日記                       *
*     http://roomba.hatenablog.com/entry/2015/02/25/133951      *
*************************************************************************/

namespace ProtoType {

  using namespace System;
  using namespace System::ComponentModel;
  using namespace System::Collections;
  using namespace System::Windows::Forms;
  using namespace System::Data;
  using namespace System::Drawing;

  /// 
  /// MyForm の概要
  /// 
  public ref class MyForm : public System::Windows::Forms::Form
  {
  public:

  Graphics^  gf;
  Bitmap^   canvas;
  array^   Point_O;
  array^   Point_A;
  array^   Point_B;
  array^   Point_C;
  array^   Point_D;
  array^   Point_E;
  array^   Point_F;
  array^   Point_G;
  double   Line_a = 38;
  double   Line_b = 41.5;
  double   Line_c = 39.3;
  double   Line_d = 40.1;
  double   Line_e = 55.8;
  double   Line_f = 39.4;
  double   Line_g = 36.7;
  double   Line_h = 65.7;
  double   Line_i = 49.0;
  double   Line_j = 50.0;
  double   Line_k = 61.9;
  double   Line_l = 7.8;
  double   Line_m = 15.0;
  double   theta_main = 0.0;
  double   PI = 3.141592653;
  int     Honsu = 1;
  float    LSize = 4.0;

  MyForm(void)
  {
    double   SizeRate;

    InitializeComponent();

    //pictureBoxを対象とするビットマップImageオブジェクトを作成する
    canvas = gcnew Bitmap(pictureBox1->Width, pictureBox1->Height);
    //ImageオブジェクトのGraphicsオブジェクトを作成する
    gf = Graphics::FromImage(canvas);

    SizeRate = 2.4;
    Line_a = 38   * SizeRate;
    Line_b = 41.5  * SizeRate;
    Line_c = 39.3  * SizeRate;
    Line_d = 40.1  * SizeRate;
    Line_e = 55.8  * SizeRate;
    Line_f = 39.4  * SizeRate;
    Line_g = 36.7  * SizeRate;
    Line_h = 65.7  * SizeRate;
    Line_i = 49.0  * SizeRate;
    Line_j = 50.0  * SizeRate;
    Line_k = 61.9  * SizeRate;
    Line_l = 7.8  * SizeRate;
    Line_m = 15.0  * SizeRate;

    Point_O = gcnew array(2);
    Point_A = gcnew array(2);
    Point_B = gcnew array(2);
    Point_C = gcnew array(2);
    Point_D = gcnew array(2);
    Point_E = gcnew array(2);
    Point_F = gcnew array(2);
    Point_G = gcnew array(2);
    Point_O[0] = 0.0;
    Point_O[1] = 0.0;

    Point_A[0] = 0.0;
    Point_A[1] = 0.0;

    Point_B[0] = -Line_a;
    Point_B[1] = -Line_l;

    Point_C[0] = 0.0;
    Point_C[1] = 0.0;

    Point_D[0] = 0.0;
    Point_D[1] = 0.0;

    Point_E[0] = 0.0;
    Point_E[1] = 0.0;

    Point_F[0] = 0.0;
    Point_F[1] = 0.0;

    Point_G[0] = 0.0;
    Point_G[1] = 0.0;
  }



【データ処理部・描画部】
ここで注意すべき点は
① 小看板をつける
    各々が何を目的とするパーツなのかを明確にするため、小さめの看板を設置します。
    各ステートメントに関する説明は”/*”~”*/” または”//”によるコメントを付けて説明します。

② 各動きパーツを分離するためのチェックボックス
    画面左に□マークのチェックボックスをつけてリンクパーツを分割表示できるようにしています。

③ math.h
    オリジナルではMath.Sinのように表記していますが、以前説明したように#include <math.h>にて算術演算用
    関数を既にインクルードしているためにsin(),cos(),tan()はそのままの表記でいけます。

④ 配列
    これも前に説明したようにarrayクラスを使用した配列方式を使用しているため、インスタンス化が必要と
    なります。

⑤ 原動節は回転角度
    タイマー内は原動節が回転するため、回転角ラジアンで設定されています。


/************************************************************************
* リンク機構間に線を描画する                      *
************************************************************************/
void draw_link(Pen^ pen, double x1,double y1,double x2,double y2)
{
  double rate = 1.5;

  gf->DrawLine(pen, (int)(rate *x1+ (pictureBox1->Width / 2)), (int)((-1)*rate * y1+ (pictureBox1->Height / 4)), (int)(rate * x2 + (pictureBox1->Width / 2)), (int)((-1)*rate*y2 + (pictureBox1->Height / 4)));
}


/************************************************************************
* ヤンセンリンクの描画                         *
************************************************************************/
void draw_jansen(double theta_A, int sign, float size)
{
  double theta_ab, theta_bc, theta_de, theta_df;
  double xc, yc;
  double AB, DE;
  Pen^  BlackPen;
  Pen^  BluePen;

  BlackPen = gcnew Pen(Color::Black, size);
  BluePen = gcnew Pen(Color::Blue , size);


// link j,b
  AB = sqrt((Line_a + Point_A[0]) * (Line_a + Point_A[0]) + (Line_l + Point_A[1])*(Line_l + Point_A[1]));
  xc = (AB * AB + Line_b * Line_b - Line_j * Line_j) / (2.0 * AB);
  yc = sqrt(Line_b * Line_b - xc * xc);
  theta_ab = atan2(Point_A[1] - Point_B[1], Point_A[0] - Point_B[0]);
  Point_C[0] = -Line_a + xc * cos(theta_ab) + yc*cos(theta_ab + PI / 2);
  Point_C[1] = -Line_l + xc * sin(theta_ab) + yc*sin(theta_ab + PI / 2);
  if (checkBox1->Checked == false) draw_link(BlackPen, sign * Point_A[0], Point_A[1], sign * Point_C[0], Point_C[1]);
  if (checkBox2->Checked == false) draw_link(BluePen , sign * Point_B[0], Point_B[1], sign * Point_C[0], Point_C[1]);

// link e,d
  xc = (Line_b * Line_b + Line_d * Line_d - Line_e * Line_e) / (2.0 * Line_b);
  yc = sqrt(Line_d * Line_d - xc * xc);
  theta_bc = atan2(Point_C[1] - Point_B[1], Point_C[0] - Point_B[0]);
  Point_E[0] = Point_B[0] + xc * cos(theta_bc) + yc * cos(theta_bc + PI / 2);
  Point_E[1] = Point_B[1] + xc * sin(theta_bc) + yc * sin(theta_bc + PI / 2);
  if (checkBox3->Checked == false) draw_link(BluePen, sign * Point_E[0], Point_E[1], sign * Point_C[0], Point_C[1]);
  if (checkBox4->Checked == false) draw_link(BluePen, sign * Point_B[0], Point_B[1], sign * Point_E[0], Point_E[1]);

//link k,c
  xc = (AB * AB + Line_c * Line_c - Line_k * Line_k) / (2.0 * AB);
  yc = sqrt(Line_c * Line_c - xc * xc);
  Point_D[0] = Point_B[0] + xc * cos(theta_ab) + yc * cos(theta_ab - PI / 2);
  Point_D[1] = Point_B[1] + xc * sin(theta_ab) + yc * sin(theta_ab - PI / 2);
  if (checkBox5->Checked == false) draw_link(BlackPen, sign * Point_A[0], Point_A[1], sign * Point_D[0], Point_D[1]);
  if (checkBox6->Checked == false) draw_link(BlackPen, sign * Point_B[0], Point_B[1], sign * Point_D[0], Point_D[1]);

//link f,g
  DE = sqrt((Point_D[0] - Point_E[0]) * (Point_D[0] - Point_E[0]) + (Point_D[1] - Point_E[1]) * (Point_D[1] - Point_E[1]));
  xc = (DE*DE + Line_g * Line_g - Line_f * Line_f) / (2.0 * DE);
  yc = sqrt(Line_g * Line_g - xc * xc);
  theta_de = atan2(Point_E[1] - Point_D[1], Point_E[0] - Point_D[0]);
  Point_F[0] = Point_D[0] + xc * cos(theta_de) + yc * cos(theta_de + PI / 2);
  Point_F[1] = Point_D[1] + xc * sin(theta_de) + yc * sin(theta_de + PI / 2);
  if (checkBox7->Checked == false) draw_link(BlackPen, sign * Point_F[0], Point_F[1], sign * Point_D[0], Point_D[1]);
  if (checkBox8->Checked == false) draw_link(BlackPen, sign * Point_E[0], Point_E[1], sign * Point_F[0], Point_F[1]);

//link h,i
  xc = (Line_g * Line_g + Line_i * Line_i - Line_h * Line_h) / (2.0 * Line_g);
  yc = sqrt(Line_i * Line_i - xc * xc);
  theta_df = atan2(Point_F[1] - Point_D[1], Point_F[0] - Point_D[0]);
  Point_G[0] = Point_D[0] + xc * cos(theta_df) + yc * cos(theta_df + PI / 2);
  Point_G[1] = Point_D[1] + xc * sin(theta_df) + yc * sin(theta_df + PI / 2);
  if (checkBox9->Checked == false) draw_link(BlackPen, sign * Point_F[0], Point_F[1], sign * Point_G[0], Point_G[1]);
  if (checkBox10->Checked == false) draw_link(BlackPen, sign * Point_D[0], Point_D[1], sign * Point_G[0], Point_G[1]);

}

void draw_pair(double theta_A)
{
  double master_theta;
  Pen^ RedPen;
  float size;

  size = LSize;
  gf->Clear(Color::White);
  master_theta = theta_A;
  RedPen = gcnew Pen(Color::Red, size);

  master_theta += PI / 4;

  Point_A[0] = Line_m * cos(master_theta);
  Point_A[1] = Line_m * sin(master_theta);
  draw_link(RedPen, Point_O[0], Point_O[1], Point_A[0], Point_A[1]);
  draw_jansen(master_theta, 1 , size);
  pictureBox1->Image = canvas;
}

private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
  try{
    Honsu = int::Parse(textBox1->Text);
    LSize = float::Parse(textBox2->Text);
  }
  catch (Exception^)
  {
   Honsu = 1;
   LSize = 4.0;
  }
  timer1->Enabled = true;
}

private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e)
{
  double omega = 0.04;

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



これを動かすと、このようになります。

【テオ・ヤンセンリンク機構 解説編】




★オリジナル編
オリジナルでは、原動節から位相を変えたもう一つの足が動き次のような動きになれば正解です。
 
【テオ・ヤンセンリンク機構 オリジナル編】


★応用編
ではもう少し変更して、あの実際に見たストランドビーストを再現してみましょう。
テオ・ヤンセン展では手押し車のように人が押すと動くビーストもあり、この足は前後一対で6組、合計12脚ありました。また土曜にすべての足は当然ですがカムにより位相がずれていました。各々の足がどういう位相ずれで動いているかの資料がない為複数の足を所定の位相ずれにより動作させる仕組みを作りました。

これはブラウン運動課題時の配列を応用すれば容易にできるはずです。
 
これが完成版です。


【テオ・ヤンセンリンク機構 応用編】


【三重県美術館開館35周年記念 テオ・ヤンセン展 人工生命体、上陸】



HOME > テオ・ヤンセンリンク機構 > 爺になってからの行列  > テオ・ヤンセンリンク機構を作ってみましょう