16
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

javascriptでリアス式海岸を描く

Posted at

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

これを作成します。
スクリーンショット 2016-03-21 22.20.01.png

コッホ曲線を描く

とりあえずコッホ曲線を描きましょう。
コッホ曲線の説明はウィキペディアに乗っています。

スクリーンショット 2016-03-21 22.14.57.png

処理の手順は

  1. 初期値となる2点(下図P01,P1)を設定する。
  2. 2点を三分割にする点(Q01,Q11)を設定する。
  3. 2の2点を頂点とする直角三角形を作成する
  4. 1〜3を繰り返す。
    繰り返すことでどんどん細かい凸が作られます。
koch.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}
$$

 //座標系
 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)はこれまでのコッホ曲線を引数として、一つ細かいコッホ曲線を作成します。

コッホ曲線の凸を乱数で決める 

上記はすべて外向きに凸がありますが、内向きにも凸を作ります。

  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を計算します。

ソースコード

<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の世界地図作ったりするときには便利かもしれませんね。

16
14
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
16
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?