JavaScript is not available.

           

Copyright © 2016-2018 MediaKobo Co., Ltd. All rights reserved.
JavaScript コンソール       ホーム       目次

オブジェクト指向の基礎

 JavaScript のオブジェクト指向機能を利用して、複素数を扱うクラス?を作成し、それを利用してフラクタル図形を描きます。

複素数

 複素数は、実数 a, b と虚数単位 を用いて a+b で表します。

 2つの複素数 z0, z1 を以下の様に定義すると (a, b, c, d は実数)、


   z0 = a + b
   z1 = c + d

以下の様に、加算、減算、積算、絶対値 が定義できます。


   z0 + z1 = (a + c) + (b + d)
   z0 - z1 = (a - c) + (b - d)
   z0 * z1 = (a*c - b*d) + (a*d + b*c)
   |z0| = sqrt(a*a + b*b)

複素数のクラス作成

 JavaScript には、明確なクラス定義は存在しませんでした。これは、その頃のお話です。
クラスの本質を変数と関数(メソッド)集まりと解釈すると、クラス定義の様なことが出来ます。
また、new 演算子で利用する、クラスのオブジェクトを作成するコンストラクタを定義します。

 具体的には、以下の様にクラスを作成します。

 ここでは、複素数クラス(Complex)の定義として、以下のコンストラクタ、メソッドを作成します。

 クラス変数として、以下を用意します。

 以下に、複素数クラスと実行例のプログラムを示します。
console.log の引数に複素数オブジェクトを指定すると複素数クラスの toString メソッドが自動的に呼ばれます。

function Complex(r, i) {
  this.r = r;
  this.i = i;
}
Complex.prototype.copy = function() {
  return new Complex(this.r, this.i);
}
Complex.prototype.add = function(z) {
  this.r += z.r;
  this.i += z.i;
  return this;
}
Complex.prototype.sub = function(z) {
  this.r -= z.r;
  this.i -= z.i;
  return this;
}
Complex.prototype.mul = function(z) {
  var rw = this.r * z.r - this.i * z.i;
  var iw = this.r * z.i + this.i * z.r;
  this.r = rw;
  this.i = iw;
  return this;
}
Complex.prototype.len2 = function() {
  return this.r * this.r + this.i * this.i;
}
Complex.prototype.toString = function() {
  if(this.i >= 0) {
    return this.r + "+" + this.i + "i";
  } else {
    return this.r + ""  + this.i + "i";
  }
}

var a = new Complex(1, 1);
var b = new Complex(-1, -1);
var c = a.copy();
console.log(a.add(b));
console.log(a.sub(b));
console.log(a.mul(b));
console.log(a.len2());
console.log(c);

フラクタル図形

 自己相似的な特徴を持つフラクタル図形の例として、マンデルブロ集合やジュリア集合があります。
これらの集合は、複素数 z0, c を初期値として与えた時、以下の漸化式で n を無限大にして発散しない初期値の集合です。

 zn+1 = zn2 + c

 ここで、z0 = 0 + 0 の時の c の集合をマンデルブロ集合、c を固定した時の z0 の集合をジュリア集合と言います。

 これらの集合のフラクタル図形は、初期値の複素数の実数部分をx座標、虚数部分をy座標とし、無限大に発散するまで漸化式を繰り返し、その回数に応じた色を付けた図のことを一般的に言います (なので、実際には、色がついているのは、集合でない部分です)。

 以下に、フラクタル図形を表示するサンプルプログラムを示していますが、図形として描画するために、初期値 z0, c を範囲を与えて設定します。
z0 の範囲は、zs ~ ze、c の範囲は、cs ~ ce で与えます。

 マンデルブロ集合の描画は、例えば、以下の初期値の範囲で実行します。


   zs = new Complex( 0.0,  0.0);
   ze = new Complex( 0.0,  0.0);
   cs = new Complex(-2.5, -1.1);
   ce = new Complex( 1.0,  1.1);

 ジュリア集合の描画は、例えば、以下の初期値の範囲で実行します。


   zs = new Complex(-1.7, -1.1);
   ze = new Complex( 1.7,  1.1);
   cs = new Complex(-0.4, -0.6);
   ce = new Complex(-0.4, -0.6);

Mandelbrot Julia

var canvas = document.getElementById('jsccanvas');
var context = canvas.getContext('2d');

function Complex(r, i) {
  this.r = r;
  this.i = i;
}
Complex.prototype.copy = function() {
  return new Complex(this.r, this.i);
}
Complex.prototype.add = function(z) {
  this.r += z.r;
  this.i += z.i;
  return this;
}
Complex.prototype.sub = function(z) {
  this.r -= z.r;
  this.i -= z.i;
  return this;
}
Complex.prototype.mul = function(z) {
  var rw = this.r * z.r - this.i * z.i;
  var iw = this.r * z.i + this.i * z.r;
  this.r = rw;
  this.i = iw;
  return this;
}
Complex.prototype.len2 = function() {
  return this.r * this.r + this.i * this.i;
}
Complex.prototype.toString = function() {
  if(this.i >= 0) {
    return this.r + "+" + this.i + "i";
  } else {
    return this.r + ""  + this.i + "i";
  }
}

function point(x, y, color) {
  var b = Math.floor( (color % imax) * 256 / imax);
  var g = Math.floor(((color / imax) % imax) * 256 / imax);
  var r = Math.floor(((color / imax  / imax) % imax) * 256 / imax);
  context.strokeStyle="rgb(" + r + "," + g + "," + b + ")";
  context.beginPath();
  context.moveTo(x, y);
  context.lineTo(x+1, y+1);
  context.stroke();
}

function converge(z, c) {
  var color = 0;
  var i;
  var zw = z.copy();

  for(i = 0; i < max; ++ i) {
    if(zw.len2() > 4.0) break;
    zw.mul(zw);
    zw.add(c);
    color ++;
  }
  return color;
}

var zs = new Complex( 0.0,  0.0);
var ze = new Complex( 0.0,  0.0);
var cs = new Complex(-2.5, -1.1);
var ce = new Complex( 1.0,  1.1);

//var zs = new Complex(-1.7, -1.1);
//var ze = new Complex( 1.7,  1.1);
//var cs = new Complex(-0.4, -0.6);
//var ce = new Complex(-0.4, -0.6);

var width = 640;
var height = 400;
var x = 0;
var y = 0;
var color;
var imax = 8;
var max = imax * imax * imax;

var zd = new Complex((ze.r-zs.r)/width, (ze.i-zs.i)/height);
var cd = new Complex((ce.r-cs.r)/width, (ce.i-cs.i)/height);
var z  = zs.copy();
var c  = cs.copy();

function loop() {
  z.r = zs.r;
  c.r = cs.r;
  for(x = 0; x < width; ++ x) {
    color = converge(z, c);
    point(x, y, color);
    z.r += zd.r;
    c.r += cd.r;
  }
  z.i += zd.i;
  c.i += cd.i;
}

jscsetintervalid = setInterval(function() {
  if(y >= height) {
    clearInterval(jscsetintervalid);
    jscsetintervalid = null;
  }
  ++ y;
  loop();
}, 10);

目次へ