1. ホーム
  2. エッシャーに挑戦!
  3. トカゲの絵を再現してみる

エッシャーの絵でトカゲが描かれたものがあります。今回はこれを再現してみたいと思います。書籍「エッシャー・マジック―だまし絵の世界を数理で読み解く」の3.2節(p.44~p.46)を参考にして描いてみました。

トカゲの絵

今回描いてみたトカゲの絵は以下のような図形になりました。それっぽくなったのではないでしょうか。

トカゲの絵を再現

描き方

こんな絵をそんな簡単に描けるの? と思うかもしれませんが、壁紙アートの記事「基本図形を変形する」のところで解説した考え方を用いれば、意外に簡単に描くことができます。結論としては、P3群の基本図形となる正六角形を変形するだけで、描くことができます。

P3群の基本図形の変形

P3群の基本図形を変形するために、再度隣り合う基本図形同士の辺のラベルと向きをみてみます。

P3群の基本図形に対する辺のラベルと向き

辺\(a\)と辺\(b\)、辺\(c\)と辺\(d\)、および辺\(e\)と辺\(f\)がそれぞれ異なる向きに重なっています。つまり、辺\(a\)を自由に変形したものを用意し、それを辺\(b\)の部分に反対向きに置きます。また辺\(c\)と辺\(d\)、辺\(e\)と辺\(f\)についても同じように変形します。そうすると、隣り合う基本図形同士を重ねることなくP3群の対称性に従って敷き詰めることができます。

辺\(a\)と辺\(b\)の変形

では、実際に辺の形を変形していきます。まず辺\(a\)と辺\(b\)の辺です。

辺\(a\)と辺\(b\)の変形

辺\(a\)で変形したものを辺\(b\)の部分に反対向きに置いていることが分かると思います。これがトカゲの左腕と左足の部分になります。

辺\(c\)と辺\(d\)の変形

次に、辺\(c\)と辺\(d\)の辺です。

辺\(c\)と辺\(d\)の変形

これがトカゲの尻尾と右足の部分になります。

辺\(e\)と辺\(f\)の変形

最後に、辺\(e\)と辺\(f\)の辺です。

辺\(e\)と辺\(f\)の変形

これがトカゲの頭と右腕の部分になります。

トカゲの形の完成

以上の各辺の変形を組み合わせると、基本図形がトカゲの形になります。

P3群の基本図形がトカゲの形に

トカゲの絵を完成させる

トカゲの形に変形した基本図形をP3群の対称性を考慮して並べていくと、最初に示したような図形を得ることができます。

プログラムコード

最後に、プログラムコードを載せておきます。基本は別記事「渦巻き図形(P3)」に記載しているプログラムコードのうち、makeRecurHexagon関数を下記のmakeLizard関数に置き換えたものになります。なお、トカゲの眼や背中の線、指にあたる部分に線を無理矢理入れてみました。絵の中身に関しては別の方法で入れた方がいいでしょうね。

// 正六角形からトカゲの形に変形する関数(基本図形)
PShape makeLizard(){

  PVector[] v = new PVector[6]; // 正六角形の頂点
  for (int i=0; i<6; i++){
    v[i] = PVector.fromAngle(-i * PI / 3 + PI).mult(scalar / 3.0);
    v[i].add( base[1].copy().mult(scalar / 3.0) );
  }

  // トカゲの形を作っていく
  PShape lizard = createShape(GROUP);
  PShape lizard_parts = createShape();
  lizard_parts.beginShape(); 
  PVector[] auxiliary_point = new PVector[8];
  // 辺aを描く
  lizard_parts.vertex(v[0].x, v[0].y);
  auxiliary_point = getAuxiliaryPoints1(v[0], v[1], v[1].copy().sub(v[3].copy()));
  for(int i=0; i<5; i++){
    lizard_parts.vertex(auxiliary_point[i].x, auxiliary_point[i].y);
  }
  // 辺bを描く
  lizard_parts.vertex(v[1].x, v[1].y);
  auxiliary_point = getAuxiliaryPoints1(v[2], v[1], v[5].copy().sub(v[1].copy())); 
  for(int i=0; i<5; i++){
    lizard_parts.vertex(auxiliary_point[4-i].x, auxiliary_point[4-i].y);
  }
  // 辺cを描く
  lizard_parts.vertex(v[2].x, v[2].y);
  auxiliary_point = getAuxiliaryPoints2(v[2], v[3], v[3].copy().sub(v[5].copy()));
  for(int i=0; i<8; i++){
    lizard_parts.vertex(auxiliary_point[i].x, auxiliary_point[i].y);
  }
  // 辺dを描く
  lizard_parts.vertex(v[3].x, v[3].y);
  auxiliary_point = getAuxiliaryPoints2(v[4], v[3], v[1].copy().sub(v[3].copy())); 
  for(int i=0; i<8; i++){
    lizard_parts.vertex(auxiliary_point[7-i].x, auxiliary_point[7-i].y);
  }
  // 辺eを描く  
  lizard_parts.vertex(v[4].x, v[4].y);
  auxiliary_point = getAuxiliaryPoints3(v[4], v[5], v[5].copy().sub(v[1].copy()));
  for(int i=0; i<8; i++){
    lizard_parts.vertex(auxiliary_point[i].x, auxiliary_point[i].y);
  }
  // 辺fを描く
  lizard_parts.vertex(v[5].x, v[5].y);
  auxiliary_point = getAuxiliaryPoints3(v[0], v[5], v[2].copy().sub(v[0].copy())); 
  for(int i=0; i<8; i++){
    lizard_parts.vertex(auxiliary_point[7-i].x, auxiliary_point[7-i].y);
  }
  
  lizard_parts.endShape(CLOSE);
  lizard.addChild(lizard_parts);

  fill(0,0,0);
  
  // 背中の線1
  lizard_parts = createShape();
  lizard_parts.beginShape();
  auxiliary_point[0] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(11.0/15.0));
  auxiliary_point[1] = v[4].copy().add(v[5].copy().sub(v[4].copy()).mult(3.5/7.0)).add(v[2].copy().sub(v[4].copy()).mult(3/12.0));
  auxiliary_point[2] = v[1].copy().add(v[2].copy().sub(v[1].copy()).mult(3.5/7.0)).sub(v[2].copy().sub(v[4].copy()).mult(1/12.0));
  auxiliary_point[3] = v[2].copy().add(v[3].copy().sub(v[2].copy()).mult(2.5/7.0)).add(v[2].copy().sub(v[0].copy()).mult(2.5/12.0));
  auxiliary_point[4] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(11.2/15.0));
  lizard_parts.vertex(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard_parts.bezierVertex(auxiliary_point[1].x, auxiliary_point[1].y, auxiliary_point[2].x, auxiliary_point[2].y, auxiliary_point[3].x, auxiliary_point[3].y);
  lizard_parts.bezierVertex(auxiliary_point[2].x, auxiliary_point[2].y, auxiliary_point[1].x, auxiliary_point[1].y, auxiliary_point[4].x, auxiliary_point[4].y);
  lizard_parts.endShape(CLOSE);
  lizard.addChild(lizard_parts);

  // 背中の線2
  lizard_parts = createShape();
  lizard_parts.beginShape();
  auxiliary_point[0] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(9.0/15.0));
  auxiliary_point[1] = v[4].copy().add(v[5].copy().sub(v[4].copy()).mult(3.5/7.0)).add(v[2].copy().sub(v[4].copy()).mult(3/12.0));
  auxiliary_point[2] = v[1].copy().add(v[2].copy().sub(v[1].copy()).mult(3.5/7.0));//.sub(v[2].copy().sub(v[4].copy()).mult(1/12.0));
  auxiliary_point[3] = v[2].copy().add(v[3].copy().sub(v[2].copy()).mult(2.5/7.0)).add(v[2].copy().sub(v[0].copy()).mult(2.5/12.0));
  auxiliary_point[4] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(8.8/15.0));
  lizard_parts.vertex(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard_parts.bezierVertex(auxiliary_point[1].x, auxiliary_point[1].y, auxiliary_point[2].x, auxiliary_point[2].y, auxiliary_point[3].x, auxiliary_point[3].y);
  lizard_parts.bezierVertex(auxiliary_point[2].x, auxiliary_point[2].y, auxiliary_point[1].x, auxiliary_point[1].y, auxiliary_point[4].x, auxiliary_point[4].y);
  lizard_parts.endShape(CLOSE);
  lizard.addChild(lizard_parts);

  // 頭部の線1
  lizard_parts = createShape();
  lizard_parts.beginShape();
  auxiliary_point[0] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(11.0/15.0)).sub(v[2].copy().sub(v[0].copy()).mult(1.5/12.0));
  auxiliary_point[1] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(11.0/15.0)).sub(v[2].copy().sub(v[0].copy()).mult(0.5/12.0));
  auxiliary_point[2] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(14.0/15.0));//.sub(v[2].copy().sub(v[0].copy()).mult(2.0/12.0));
  auxiliary_point[3] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(11.0/15.0)).add(v[2].copy().sub(v[0].copy()).mult(1.0/12.0));
  auxiliary_point[4] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(14.1/15.0));//.sub(v[2].copy().sub(v[0].copy()).mult(2.0/12.0));
  auxiliary_point[5] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(11.1/15.0)).sub(v[2].copy().sub(v[0].copy()).mult(0.5/12.0));
  lizard_parts.vertex(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard_parts.bezierVertex(auxiliary_point[1].x, auxiliary_point[1].y, auxiliary_point[2].x, auxiliary_point[2].y, auxiliary_point[3].x, auxiliary_point[3].y);
  lizard_parts.bezierVertex(auxiliary_point[4].x, auxiliary_point[4].y, auxiliary_point[5].x, auxiliary_point[5].y, auxiliary_point[0].x, auxiliary_point[0].y);
  lizard_parts.endShape(CLOSE);
  lizard.addChild(lizard_parts);

  // 頭部の線2
  lizard_parts = createShape();
  lizard_parts.beginShape();
  auxiliary_point[0] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(9.0/15.0)).sub(v[2].copy().sub(v[0].copy()).mult(1.5/12.0));
  auxiliary_point[1] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(9.0/15.0)).sub(v[2].copy().sub(v[0].copy()).mult(0.5/12.0));
  auxiliary_point[2] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(7.0/15.0));//.sub(v[2].copy().sub(v[0].copy()).mult(2.0/12.0));
  auxiliary_point[3] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(9.0/15.0)).add(v[2].copy().sub(v[0].copy()).mult(1.0/12.0));
  auxiliary_point[4] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(7.1/15.0));//.sub(v[2].copy().sub(v[0].copy()).mult(2.0/12.0));
  auxiliary_point[5] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(9.1/15.0)).sub(v[2].copy().sub(v[0].copy()).mult(0.5/12.0));
  lizard_parts.vertex(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard_parts.bezierVertex(auxiliary_point[1].x, auxiliary_point[1].y, auxiliary_point[2].x, auxiliary_point[2].y, auxiliary_point[3].x, auxiliary_point[3].y);
  lizard_parts.bezierVertex(auxiliary_point[4].x, auxiliary_point[4].y, auxiliary_point[5].x, auxiliary_point[5].y, auxiliary_point[0].x, auxiliary_point[0].y);
  lizard_parts.endShape(CLOSE);
  lizard.addChild(lizard_parts);

  // 眼の線1
  lizard_parts = createShape();
  lizard_parts.beginShape();
  auxiliary_point[0] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(12.0/15.0)).sub(v[2].copy().sub(v[0].copy()).mult(0.5/12.0));
  auxiliary_point[1] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(14.0/15.0));//.sub(v[2].copy().sub(v[0].copy()).mult(2.0/12.0));
  auxiliary_point[2] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(12.0/15.0)).add(v[2].copy().sub(v[0].copy()).mult(0.5/12.0));
  auxiliary_point[3] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(14.1/15.0));//.sub(v[2].copy().sub(v[0].copy()).mult(2.0/12.0));
  lizard_parts.vertex(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard_parts.quadraticVertex(auxiliary_point[1].x, auxiliary_point[1].y, auxiliary_point[2].x, auxiliary_point[2].y);
  lizard_parts.quadraticVertex(auxiliary_point[1].x, auxiliary_point[1].y, auxiliary_point[0].x, auxiliary_point[0].y);
  lizard_parts.endShape(CLOSE);
  lizard.addChild(lizard_parts);

  // 眼の線2
  lizard_parts = createShape();
  lizard_parts.beginShape();
  auxiliary_point[0] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(8.0/15.0)).sub(v[2].copy().sub(v[0].copy()).mult(0.5/12.0));
  auxiliary_point[1] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(7.0/15.0));//.sub(v[2].copy().sub(v[0].copy()).mult(2.0/12.0));
  auxiliary_point[2] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(8.0/15.0)).add(v[2].copy().sub(v[0].copy()).mult(0.5/12.0));
  auxiliary_point[3] = v[0].copy().add(v[5].copy().sub(v[0].copy()).mult(7.1/15.0));//.sub(v[2].copy().sub(v[0].copy()).mult(2.0/12.0));
  lizard_parts.vertex(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard_parts.quadraticVertex(auxiliary_point[1].x, auxiliary_point[1].y, auxiliary_point[2].x, auxiliary_point[2].y);
  lizard_parts.quadraticVertex(auxiliary_point[1].x, auxiliary_point[1].y, auxiliary_point[0].x, auxiliary_point[0].y);
  lizard_parts.endShape(CLOSE);
  lizard.addChild(lizard_parts);

  // 指の線1-1  
  lizard_parts = getLizardFinger();
  lizard_parts.rotate(radians(110.0));
  auxiliary_point[0] = v[0].copy().add(v[1].copy().sub(v[0].copy()).mult(1.0/7.0)).add(v[4].copy().sub(v[0].copy()).mult(1.0/12.0));  
  lizard_parts.translate(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard.addChild(lizard_parts);

  // 指の線1-2  
  lizard_parts = getLizardFinger();
  lizard_parts.rotate(radians(110.0));
  auxiliary_point[0] = v[0].copy().add(v[1].copy().sub(v[0].copy()).mult(0.7/7.0)).add(v[4].copy().sub(v[0].copy()).mult(0.7/12.0));  
  lizard_parts.translate(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard.addChild(lizard_parts);

  // 指の線2-1  
  lizard_parts = getLizardFinger();
  lizard_parts.rotate(radians(110.0));
  auxiliary_point[0] = v[4].copy().add(v[5].copy().sub(v[4].copy()).mult(0.0/7.0)).add(v[5].copy().sub(v[1].copy()).mult(3.2/12.0));  
  lizard_parts.translate(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard.addChild(lizard_parts);
  
  // 指の線2-2  
  lizard_parts = getLizardFinger();
  lizard_parts.rotate(radians(110.0));
  auxiliary_point[0] = v[4].copy().add(v[5].copy().sub(v[4].copy()).mult(0.5/7.0)).add(v[5].copy().sub(v[1].copy()).mult(3.5/12.0));  
  lizard_parts.translate(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard.addChild(lizard_parts);

  // 指の線3-1  
  lizard_parts = getLizardFinger();
  lizard_parts.rotate(radians(270.0));
  auxiliary_point[0] = v[1].copy().add(v[2].copy().sub(v[1].copy()).mult(1.5/7.0)).add(v[1].copy().sub(v[5].copy()).mult(3.3/12.0));  
  lizard_parts.translate(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard.addChild(lizard_parts);
  
  // 指の線3-2 
  lizard_parts = getLizardFinger();
  lizard_parts.rotate(radians(270.0));
  auxiliary_point[0] = v[1].copy().add(v[2].copy().sub(v[1].copy()).mult(1.9/7.0)).add(v[1].copy().sub(v[5].copy()).mult(3.3/12.0));  
  lizard_parts.translate(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard.addChild(lizard_parts);  

  // 指の線4-1  
  lizard_parts = getLizardFinger();
  lizard_parts.rotate(radians(220.0));
  auxiliary_point[0] = v[3].copy().add(v[4].copy().sub(v[3].copy()).mult(0.5/7.0)).add(v[4].copy().sub(v[0].copy()).mult(1.0/12.0));  
  lizard_parts.translate(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard.addChild(lizard_parts);  

  // 指の線4-2  
  lizard_parts = getLizardFinger();
  lizard_parts.rotate(radians(220.0));
  auxiliary_point[0] = v[3].copy().add(v[4].copy().sub(v[3].copy()).mult(0.5/7.0)).add(v[4].copy().sub(v[0].copy()).mult(0.5/12.0));  
  lizard_parts.translate(auxiliary_point[0].x, auxiliary_point[0].y);
  lizard.addChild(lizard_parts);  
  
  return lizard;
}

// 辺aと辺bを変形するために必要な補助点を算出する関数
PVector[] getAuxiliaryPoints1(PVector start, PVector end, PVector dir){
  PVector[] auxiliary_point = new PVector[8];
  auxiliary_point[0] = getAuxiliaryPoint(start, end, 2.0/7.0, 1.0/12.0*sqrt(3.0));
  auxiliary_point[1] = getAuxiliaryPoint(start, end, 4.0/7.0, 0.0);
  auxiliary_point[2] = getAuxiliaryPoint(start, end, 4.0/7.0, -3.0/12.0*sqrt(3.0));
  auxiliary_point[3] = getAuxiliaryPoint(start, end, 6.0/7.0, -3.6/12.0*sqrt(3.0));
  auxiliary_point[4] = getAuxiliaryPoint(start, end, 5.6/7.0, -1.8/12.0*sqrt(3.0));
  return auxiliary_point;
}

// 辺cと辺dを変形するために必要な補助点を算出する関数
PVector[] getAuxiliaryPoints2(PVector start, PVector end, PVector dir){
  PVector[] auxiliary_point = new PVector[8];
  auxiliary_point[0] = getAuxiliaryPoint(start, end, 1.5/7.0, 3.0/12.0*sqrt(3.0));
  auxiliary_point[1] = getAuxiliaryPoint(start, end, 4.0/7.0, 3.0/12.0*sqrt(3.0));
  auxiliary_point[2] = getAuxiliaryPoint(start, end, 3.0/7.0, 2.0/12.0*sqrt(3.0));
  auxiliary_point[3] = getAuxiliaryPoint(start, end, 2.0/7.0, 0.0);
  auxiliary_point[4] = getAuxiliaryPoint(start, end, 3.0/7.0, -1.0/12.0*sqrt(3.0));
  auxiliary_point[5] = getAuxiliaryPoint(start, end, 5.0/7.0, -1.0/12.0*sqrt(3.0));
  auxiliary_point[6] = getAuxiliaryPoint(start, end, 6.0/7.0, -2.0/12.0*sqrt(3.0));
  auxiliary_point[7] = getAuxiliaryPoint(start, end, 6.8/7.0, -2.0/12.0*sqrt(3.0));
  return auxiliary_point;
}

// 辺eと辺fを変形するために必要な補助点を算出する関数
PVector[] getAuxiliaryPoints3(PVector start, PVector end, PVector dir){
  PVector[] auxiliary_point = new PVector[8];
  auxiliary_point[0] = getAuxiliaryPoint(start, end, 0.5/7.0, 2.0/12.0*sqrt(3.0));
  auxiliary_point[1] = getAuxiliaryPoint(start, end, -0.5/7.0, 2.0/12.0*sqrt(3.0));
  auxiliary_point[2] = getAuxiliaryPoint(start, end, -0.5/7.0, 3.0/12.0*sqrt(3.0));
  auxiliary_point[3] = getAuxiliaryPoint(start, end, 1.5/7.0, 4.0/12.0*sqrt(3.0));
  auxiliary_point[4] = getAuxiliaryPoint(start, end, 2.5/7.0, 3.5/12.0*sqrt(3.0));
  auxiliary_point[5] = getAuxiliaryPoint(start, end, 2.0/7.0, 0.0);
  auxiliary_point[6] = getAuxiliaryPoint(start, end, 3.0/7.0, -3.0/12.0*sqrt(3.0));
  auxiliary_point[7] = getAuxiliaryPoint(start, end, 4.0/7.0, -3.0/12.0*sqrt(3.0));
  return auxiliary_point;
}

// 辺を変形するために必要な補助点を算出する関数
PVector getAuxiliaryPoint(
  PVector start,
  PVector end, 
  float parallel_size,
  float vertical_size
){
  PVector dir_parallel = end.copy().sub(start.copy());
  PVector dir_vertical = new PVector(-dir_parallel.y, dir_parallel.x);
  PVector auxiliary_point = start.copy().add(dir_parallel.copy().mult(parallel_size)).add(dir_vertical.copy().mult(vertical_size));
  return auxiliary_point;
}

// 指の線を描く関数
PShape getLizardFinger(){
  PShape lizard_finger = createShape();
  lizard_finger.beginShape(QUAD);
  lizard_finger.vertex(0.0, 0.0);
  lizard_finger.vertex(0.0, 1.0);
  lizard_finger.vertex(10.0, 1.0);
  lizard_finger.vertex(10.0, 0.0);
  lizard_finger.endShape(CLOSE);
  
  return lizard_finger;
}

これを思いつくって、結構すごいですよね。エッシャーはどういう発想しているんでしょうか。なかなかこういう発想はできませんね。

コメントを残す