paper.install(window); function gen_tree(pos, maxheight) { var baum = new Branch(); baum.length = 0.15 * maxheight + 0.15 * maxheight * Math.random(); var shapernd = Math.random() * 3; if(shapernd < 1) { baum.crown = new KiteCrown( 0.49 * maxheight + 0.21 * maxheight * Math.random(), 0.4 + Math.random() * 0.2, {bisect: 0.2 + Math.random() * 0.2}, ); } else if(shapernd < 2) { baum.crown = new TriangleCrown( 0.49 * maxheight + 0.21 * maxheight * Math.random(), 0.4 + Math.random() * 0.2, ); } else { baum.crown = new EllipseCrown( 0.49 * maxheight + 0.21 * maxheight * Math.random(), 0.4 + Math.random() * 0.2, {bisect: 0.1 + Math.random() * 0.4}, ); } baum.angle = Math.PI / 2; if(Math.random() < 0.3) { baum.angle += Math.random() * 0.2 - 0.1; } if((baum.crown.shape != "triangle") && (Math.random() < 0.5)) { var b = { length: baum.length * 0.4, angle: Math.PI / 2, crown: baum.crown.clone() }; b.crown.length *= 0.4; b.crown.width = 0.8; baum.forks = [{ pos: 0.2 + Math.random() * 0.4, branch: new Branch(b), }]; baum.draw(pos); var step = (Math.random() < 0.5) ? -0.1 : 0.1; while(baum.collides_crown(baum.forks[0].branch)) { baum.forks[0].branch.angle += step; baum.draw(pos); } var range = 4*Math.PI/9 - Math.abs(Math.PI/2 - baum.forks[0].branch.angle); if(step > 0) { baum.forks[0].branch.angle += range * (0.1 + 0.9 * Math.random()); } else { baum.forks[0].branch.angle -= range * (0.1 + 0.9 * Math.random()); } baum.draw(pos); } baum.draw(pos); return baum; } class Crown { constructor(length, width, shape) { this.width = width; this.length = length; this.shape = shape; } undraw() { if(typeof this._path !== "undefined") { this._path.removeSegments(); delete this._path; } } collides(other) { return this._path.intersects(other._path); } get bounds() { return this._path.bounds; } } class KiteCrown extends Crown { constructor(length, width, {bisect = 0.3}={}) { super(length, width, 'kite'); this.bisect = bisect; } clone() { return new KiteCrown(this.length, this.width, {bisect: this.bisect}); } draw(branch, bvec, cpos) { this.undraw(); var cvec = bvec.multiply(1/bvec.length * this.length); var bswidth = this.length * this.width; var bsvec = new Point(Math.sin(branch.angle) * bswidth / 2, Math.cos(branch.angle) * bswidth / 2); this._path = new Path(); this._path.strokeColor = 'black'; this._path.fillColor = 'white'; this._path.strokeWidth = 2; this._path.closed = true; this._path.add(cpos); this._path.add(cpos.add(cvec.multiply(this.bisect)).add(bsvec)); this._path.add(cpos.add(cvec)); this._path.add(cpos.add(cvec.multiply(this.bisect)).subtract(bsvec)); } } class TriangleCrown extends Crown { constructor(length, width) { super(length, width, 'triangle'); } clone() { return new TriangleCrown(this.length, this.width); } draw(branch, bvec, cpos) { this.undraw(); var cvec = bvec.multiply(1/bvec.length * this.length); var bswidth = this.length * this.width; var bsvec = new Point(Math.sin(branch.angle) * bswidth / 2, Math.cos(branch.angle) * bswidth / 2); this._path = new Path(); this._path.strokeColor = 'black'; this._path.fillColor = 'white'; this._path.strokeWidth = 2; this._path.closed = true; this._path.add(cpos.add(bsvec)); this._path.add(cpos.add(cvec)); this._path.add(cpos.subtract(bsvec)); } } class EllipseCrown extends Crown { constructor(length, width, {bisect = 0.5}={}) { super(length, width, 'ellipse'); this.bisect = bisect; } clone() { return new EllipseCrown(this.length, this.width, {bisect: this.bisect}); } draw(branch, bvec, cpos) { this.undraw(); var cvec = bvec.multiply(1/bvec.length * this.length); var bswidth = this.length * this.width; var bsvec = new Point(Math.sin(branch.angle) * bswidth / 2, Math.cos(branch.angle) * bswidth / 2); this._path = new Path(); this._path.strokeColor = 'black'; this._path.fillColor = 'white'; this._path.strokeWidth = 2; this._path.closed = true; var cmid = cpos.add(cvec.multiply(this.bisect)); this._path.add(new Segment(cpos, bsvec.multiply(0.5), bsvec.multiply(-0.5), )); this._path.add(new Segment(cmid.subtract(bsvec), bvec.multiply(-0.5), bvec.multiply(0.5), )); this._path.add(new Segment(cpos.add(cvec), bsvec.multiply(-0.5), bsvec.multiply(0.5), )); this._path.add(new Segment(cmid.add(bsvec), bvec.multiply(0.5), bvec.multiply(-0.5), )); } } class Branch { constructor(data) { Object.assign(this, data); if(typeof this.forks !== "undefined") { for (let fork of this.forks) { fork.branch = new Branch(fork.branch, this); } } } draw(pos) { if(typeof this._branch !== "undefined") { this._branch.removeSegments(); delete this._branch; } this._branch = new Path(); var bvec = new Point(Math.cos(this.angle) * this.length, -1 * Math.sin(this.angle) * this.length); var end = pos.add(bvec); this._branch.strokeColor = 'black'; this._branch.strokeWidth = 2; this._branch.add(pos); this._branch.add(end); if(typeof this.crown !== "undefined") { this.crown.draw(this, bvec, end); } if(typeof this.forks !== "undefined") { for (let fork of this.forks) { fork.branch.draw(pos.add(bvec.multiply(fork.pos))); } } } collides_crown(other) { return this.crown.collides(other.crown); } get bounds() { var b = this._branch.bounds; b = b.unite(this.crown.bounds); if(typeof this.forks !== "undefined") { for (let fork of this.forks) { b = b.unite(fork.branch.bounds); /* var r = new Path.Rectangle({ point: fork.branch.bounds.point, size: fork.branch.bounds.size, strokeWidth: 1, strokeColor: 'green'}); */ } } return b; } } function draw() { var canvas = document.getElementById('canvas'); paper.setup(canvas); var width = 148 * 5; var height = 105 * 5; var rows = 3; var rowspace = 20; var inspace = 30; var outspace = 10; var maxh = (height - (rows-1)*rowspace - 2*outspace - 2*inspace) / rows; var outline = new Path.Rectangle({ point: new Point(outspace, outspace), size: new Size(width - 2*outspace, height - 2*outspace), strokeWidth: 2, strokeColor: 'black' }); for(var row=0; row