オブジェクト指向的なフラクタルの木の描画

<!DOCTYPE html>
<html lang="ja">
<meta charset="UTF-8">
<title></title>

<style>
  canvas { width: 100vw; height: 100vh; }
</style>
<canvas></canvas>

<script>
class CG {
  constructor (canvas) {
    this.canvas = canvas;
    this.ctx = canvas.getContext ('2d');
    canvas.width = canvas.clientWidth;
    canvas.height = canvas.clientHeight;
  }

  line (x0 = 0, y0 = 0, x1 = 0, y1 = 0, color = "black") {
    let { ctx } = this;
    ctx.beginPath ();
    ctx.strokeStyle = color;
    ctx.moveTo (x0, y0);
    ctx.lineTo (x1, y1);
    ctx.stroke ();
  }

  lines (points, color = "black") {
    let { ctx } = this;
    ctx.beginPath ();
    ctx.strokeStyle = color;
    for (let [{x: x0, y: y0}, {x: x1, y: y1}] of points) {
      ctx.moveTo (x0, y0);
      ctx.lineTo (x1, y1);
    }
    ctx.stroke ();
  }

}

class P2 {
  constructor (x = 0, y = 0) { this.x = x; this.y = y }
  add ({x = 0, y = 0}) { this.x += x; this.y += y; return this; }
  scalarMultiple (n = 0) { this.x *= n; this.y *= n; return this;}
  rotation (angle = 0) {
    let deg = -angle * Math.PI / 180, sin = Math.sin (deg), cos = Math.cos (deg), { x, y } = this;
    this.x = x * cos + y * sin; this.y = x * -sin + y * cos;
    return this; 
  }
  get copy () { return new Vector (this.x, this.y); }
  get length () { return (this.x**2+this.y**2)**.5; }
}

class Vector extends P2 {
  constructor (x, y) { super (x, y) }
}


class Node {
  constructor (parent, attr, ...childs) {
    this.parent = parent;
    this.attr = attr;
    this.childs = childs;
  }
  append (...childs) { this.childs.push (...childs) }
  hasChild () { return !!this.childs.length;}
}


class Tree extends Node {
  constructor (parent, attr, gene, branch = 0) {
    super (parent, attr);
    this.gene = gene;

    if (branch) this.growup (branch);
  }

  growup (cnt = 1) {
    const loop = (e, cnt)=> {//再起呼び出し
      if (cnt--) {
        if (! e.hasChild ())
          e.append (...e.gene (e));

        e.childs.forEach (a=> loop (a, cnt));
      }
    };

    loop (this, cnt);
    return this;
  }


  getTreeData (position = new P2) {
    const loop = (e, position, cnt = 0)=> {
      let vector = e.attr;
      let p0 = position.copy;
      let p1 = p0.copy.add (vector);
      rst.push ([p0, p1, cnt]);
      e.childs.forEach (a=> loop (a, p1, cnt + 1));
    }
    let rst = [ ];
    loop (this, position);
    return rst;
  }

  makeChild (...attrs) {
    let {attr, gene} = this, rst = [ ];
    for (let [len, rot] of attrs)
      rst.push (new Tree (this, attr.copy.scalarMultiple (len).rotation (rot), gene));
    return rst;
  }
}

//__________________________________________________


const
  //canvas
  cg = new CG (document.querySelector ('#D20101003C')),
  //遺伝子となる関数
  geneA = e=> e.makeChild ([.70, 30], [.70, -30]),
  geneB = e=> e.makeChild ([.85, 45], [.8, 15], [.75, -15], [.70, -45]),
  geneC = e=> e.makeChild ([.71, 90], [.71, -90]),
  geneD = e=> e.makeChild ([.80, Math.random() * 30], [.80, Math.random() *-30], [.99, Math.random()*10-5]),
  //木を作る  
  treeA = new Tree (null, new Vector (0, -90), geneA, 9),
  treeB = new Tree (null, new Vector (40, -90), geneB, 6);
  treeC = new Tree (null, new Vector (0, -100), geneC, 10);
  treeD = new Tree (null, new Vector (0, -80), geneD, 8);
  
//木から座標を得て描画する
cg.lines (treeA.getTreeData (new P2(200,280)), "blue");
cg.lines (treeB.getTreeData (new P2(200,550)), "red");
cg.lines (treeC.getTreeData (new P2(200,800)), "black");
cg.lines (treeD.getTreeData (new P2(750,750)), "rgba(0,100,0,1)");

</script>