javascriptでリアス式海岸を描く



リアス式海岸をコッホ曲線で作るというのが、数字の国のミステリー（マーカス・デュ・ソートイ著）に載っていたので、javascriptで実装しました。

これを作成します。
<img width="800" alt="スクリーンショット 2016-03-21 22.20.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/51404/93aa1581-1908-bcec-3691-15b3ec057409.png">


# コッホ曲線を描く
とりあえずコッホ曲線を描きましょう。
コッホ曲線の説明は[ウィキペディア](https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%83%E3%83%9B%E6%9B%B2%E7%B7%9A)に乗っています。

<img width="800" alt="スクリーンショット 2016-03-21 22.14.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/51404/2e26db7f-bb34-8dc2-156e-759377d4c8b2.png">


処理の手順は
1. 初期値となる2点(下図P01,P1)を設定する。
2. 2点を三分割にする点(Q01,Q11)を設定する。
3. ２の２点を頂点とする直角三角形を作成する
4. １〜３を繰り返す。
繰り返すことでどんどん細かい凸が作られます。

<img width="500" alt="koch.png" src="https://qiita-image-store.s3.amazonaws.com/0/51404/94572ce0-f0da-3242-f2b8-c5d5b37e72fb.png">

イメージ（左：処理ロジック、右：コッホ曲線の作成過程）




点Q2は以下で計算します。
$$
\begin{align}
 \begin{bmatrix}
q\_2x \\\ 
q\_2y 
\end{bmatrix} &= 
\begin{bmatrix}
cos(60^\circ) & -sin(60^\circ) \\\ 
sin(60^\circ) & cos(60^\circ) 
\end{bmatrix}\begin{bmatrix}
q\_1x - q\_0x \\\ 
q\_1y - q\_0y 
\end{bmatrix} + 
\begin{bmatrix}
q\_0x \\\ 
q\_0y 
\end{bmatrix}
\end{align}
$$



```js
 //座標系
 function Cood(x,y){
    this.x = x;
    this.y = y;
  }

 function koch(p1, p2){
    var q0 = new Cood( (  p2.x + 2*p1.x)/3, (  p2.y + 2*p1.y)/3 );
    var q1 = new Cood( (2*p2.x +   p1.x)/3, (2*p2.y +   p1.y)/3 );

    var q2 = new Cood(  (             (q1.x+q0.x) - Math.sqrt(3)*(q1.y-q0.y))/2, 
                        (Math.sqrt(3)*(q1.x-q0.x) +              (q1.y+q0.y))/2);

    return [q0, q2, q1, p2];
  }

  function koch_list(k){
    var ret_arr = [k[0]];

    for(var i=0, len=k.length; i<len-1; i++){
     Array.prototype.push.apply(ret_arr, koch(k[i], k[i+1]));

    }
    return ret_arr;
  }
```

koch(p1, p2)はコッホ曲線の凸部分を作成します。
4点返しているのはプログラムの都合です。もっと上手くやれると思います。
koch_list(k)はこれまでのコッホ曲線を引数として、一つ細かいコッホ曲線を作成します。



# コッホ曲線の凸を乱数で決める　
上記はすべて外向きに凸がありますが、内向きにも凸を作ります。

```js
  function koch(p1, p2){
    var rand = Math.floor( Math.random() * 2 ) ;

    var q0 = new Cood( (  p2.x + 2*p1.x)/3, (  p2.y + 2*p1.y)/3 );
    var q1 = new Cood( (2*p2.x +   p1.x)/3, (2*p2.y +   p1.y)/3 );

    var q2 = new Cood(  (             (q1.x+q0.x) - Math.sqrt(3)*(q1.y-q0.y))/2, 
                        (Math.sqrt(3)*(q1.x-q0.x) +              (q1.y+q0.y))/2);

   if(rand==1){
        q2 =  new Cood( (              (q1.x+q0.x) + Math.sqrt(3)*(q1.y-q0.y))/2, 
                        (-Math.sqrt(3)*(q1.x-q0.x) +              (q1.y+q0.y))/2);
    }

    return [q0, q2, q1, p2];  
  }
```
2値を取る乱数で、1の時は回転行列を-60°にしてQ2を計算します。

# ソースコード
```html
<canvas id="koch" width="1200" height="600"></canvas>
<script type="text/javascript">
  var canvas = document.getElementById("koch");
  var ctx = canvas.getContext("2d");

  var rand = Math.floor( Math.random() * 2 ) ;
 
  var p;
  var c_w = canvas.width;
  var c_h = canvas.height; 
  
  //座標系
  function Cood(x,y){
    this.x = x;
    this.y = y;
  }

  var ini1 = new Cood(0  , c_h*0.75);
  var ini2 = new Cood(c_w, c_h*0.75);
  var koch_path = [ini1, ini2]; 

  function koch(p1, p2){
    var rand = Math.floor( Math.random() * 2 ) ;

    var q0 = new Cood( (  p2.x + 2*p1.x)/3, (  p2.y + 2*p1.y)/3 );
    var q1 = new Cood( (2*p2.x +   p1.x)/3, (2*p2.y +   p1.y)/3 );

    var q2 = new Cood(  (             (q1.x+q0.x) - Math.sqrt(3)*(q1.y-q0.y))/2, 
                        (Math.sqrt(3)*(q1.x-q0.x) +              (q1.y+q0.y))/2);

   if(rand==1){
        q2 =  new Cood( (              (q1.x+q0.x) + Math.sqrt(3)*(q1.y-q0.y))/2, 
                        (-Math.sqrt(3)*(q1.x-q0.x) +              (q1.y+q0.y))/2);
    }

    return [q0, q2, q1, p2];  
  }

  function koch_list(k){
    var ret_arr = [k[0]];

    for(var i=0, len=k.length; i<len-1; i++){
     Array.prototype.push.apply(ret_arr, koch(k[i], k[i+1]));

    }
    return ret_arr;
  }


  function init(){
    p = new Animation(); 
  }

  init();

  var tm;
  tm = setInterval(main,10);

  function main(){
    //画面のクリア
    ctx.clearRect(0,0,c_w,c_h);

    p.view();
  }

  var cnt = 0;
  function Animation(){
    var obj = this;
     
   obj.view = function(){
      ctx.beginPath()

      ctx.moveTo(koch_path[0].x, koch_path[0].y);
      
      if(cnt%20==0){  
        koch_path = koch_list(koch_path);
      } 
      for(var i = 1,  k_len = koch_path.length ; i < k_len; i++){
        ctx.lineTo(koch_path[i].x, koch_path[i].y);
      }

      ctx.stroke();

      if(cnt<200){
        cnt ++;
      }
    }
  }

</script>
```

RPGの世界地図作ったりするときには便利かもしれませんね。