<!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>