0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

p5.jsで双曲タイリングを描画する

Posted at

はじめに

 上半平面の双曲タイリングの描画レシピ(簡単なもの)。

fsfef34eg4r.jpg

 こんな感じのものを作ってみる。

 まず角度を決める。そのうちひとつは簡単のため$\pi/2$とする。それは双曲平面だと直線というのが実軸に垂直なものと実軸に直交する半円であるのだけど、それが1点で交わるときのなす角度。だからこの図だと中央やや下方の半円と真ん中の垂直線だけが交わっている部分に相当する。その他、2つの角度を指定する。それらを$m,n$として、

$$\frac{1}{m}+\frac{1}{n}+\frac{1}{2}<1$$

を満たすようにしないといけない。この描画では$4,5$を選んでいる。そこで、

$$\theta = \frac{\pi}{4},~~~\phi = \frac{\pi}{5}$$

とおく。これらに対し次の量:

$$F(\theta,~\phi)=\frac{\cos \phi + \sqrt{\cos^2\phi - \sin^2\theta}}{\sin^2\theta}$$

を計算し、

$$s=F(\theta,~\phi),~~~a=-s\cos\theta$$

とおく。これらより、反射計算を作る。次の3つ。

  • $x<0$のとき、$x=0$で反射($x=-x$という操作)

  • $x^2+y^2<1$のとき、円$x^2+y^2=1$で反射(反転)

$ m = 1/(x^2+y^2)$として$x = mx, ~y=my.$

  • $(x-a)^2 + y^2 > s^2$のとき、円$(x-a)^2 + y^2 = s^2$で反射(反転)

$n = s^2 / ((x-a)^2 + y^2)$として$x = a + n(x - a), ~y = ny.$

これで描画できる。実際のプログラムはこんな感じ。

作品ページ

let f = 0;
let a = -1.7;

function setup(){
  createCanvas(800, 400);
}

function draw(){
  let r = 400;
  while(r-- && f < 800 * 400){
    let u = f % 800;
    let v = floor(f / 800);
    let c = 0;
    let p = 36;
    let x = u / 400 - 1;
    let y = 1 - v / 400;

    while(p--){
      if(x < 0){
        x = -x; c++;
      }else if(x * x + y * y < 1){
        let m = 1 / (x * x + y * y);
        x *= m; y *= m; c++;
      }else if((x - a) * (x - a) + y * y > 5.78){
        let n = 5.78 / ((x - a) * (x - a) + y * y);
        x = a + n * (x - a); y *= n; c++;
      }else{
        break;
      }
    }
    stroke(0, (c % 4) * 64, 255);
    circle(u, v, 1);
    f++;
  }
}

$s$は$s^2$という形でしか使わないので既に計算してある。

$$\theta = \frac{\pi}{4},~~\phi = \frac{\pi}{5}, ~~F(\theta,~\phi)=2.404185\cdots$$

のようになり、

$$s^2 = F(\theta,~\phi)^2 = 5.7801072\cdots,~~~a = -s\cos\theta = -1.700157\cdots$$

のようになるので近似値も妥当である。隣り合う領域は反射回数のパリティが異なるので、2や4で割った余りで反射回数を加工して色付けに使えばタイリングになる。反射回数をそのまま使ってカラフルに仕上げてもいい。

  colorMode(HSB, 100);
  stroke((c % 8) * 12 , 50 + (c % 5) * 10, 100);

を使った例:

fwf3t3gf4g4.jpg

ただ、普通は$\mathrm{GLSL}$で書いた方がいいと思う。処理も軽くなるし、動きを付けるのもたやすい。

let myShader;

// 原点が真ん中の一番下になるようにする。

const vShader =
`#version 300 es
in vec3 aPosition;
uniform float uAspect; // width/height
out vec2 vUv;
void main(){
  vUv = aPosition.xy + 0.5;
  vUv.x -= 0.5;
  vUv.x *= uAspect;
  gl_Position = vec4(aPosition*2.0, 1.0);
}
`

const fShader =
`#version 300 es
precision highp float;
in vec2 vUv;
out vec4 color;
const float pi = 3.14159;
const int ITERATION = 64;

// 双曲タイリング
float calc_ref(in vec2 p){
  float theta = pi / 4.0;
  float phi = pi / 5.0;
  float s = (cos(phi) + sqrt(pow(cos(phi), 2.0) - pow(sin(theta), 2.0))) / pow(sin(theta), 2.0);
  float a = -s * cos(theta);
  float c = 0.0;

  for(int rep = 0; rep < ITERATION; rep++){
    if(p.x < 0.0){
      p.x = -p.x;
      c += 1.0;
    }else if(p.x * p.x + p.y * p.y < 1.0){
      float m = 1.0 / (p.x * p.x + p.y * p.y);
      p.x *= m;
      p.y *= m;
      c += 1.0;
    }else if((p.x - a) * (p.x - a) + p.y * p.y > s * s){
      float n = s * s / ((p.x - a) * (p.x - a) + p.y * p.y);
      p.x = a + n * (p.x - a);
      p.y *= n;
      c += 1.0;
    }else{
      break;
    }
  }
  return c;
}
void main(){
  float count = calc_ref(vUv);
  vec3 col = vec3(0.0, mod(count, 4.0) * 0.25, 1.0);
  color = vec4(col, 1.0);
}
`;

function setup(){
  createCanvas(800, 400, WEBGL);
  pixelDensity(1);

  myShader = createShader(vShader, fShader);
  noStroke();
}

function draw(){
  shader(myShader);
  myShader.setUniform("uAspect", width/height);
  plane(0);
}

GLSLで描いたもの:

feg4g4t4g4.jpg

作品ページ:shader hyperbolic

 応用すると絵柄でやることもできる。

hyperbolic_foxImage_7788.png

また、上半平面をポアンカレ円盤に移す写像を使えば、円でタイリングすることもできる。

hyperbolic_foxImage_12687.png

作品ページ:foxHyperbolic

おわりに

 ここまでお読みいただいてありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?