You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
8.4 KiB
286 lines
8.4 KiB
|
|
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; // + 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; |
|
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<rows; row++) { |
|
var y = outspace + inspace + maxh * (row + 1) + rowspace * row; |
|
var x = outspace + inspace + 20; // TODO approx width |
|
var prev; |
|
while(x < width - outspace - inspace - 60) { // TODO approx width |
|
var yy = y + Math.random() * 40 - 20; |
|
x += Math.random() * 10; // TODO approx width |
|
var t = gen_tree(new Point(x, yy), maxh); |
|
if(typeof prev !== 'undefined') { |
|
while(prev.bounds.intersects(t.bounds)) { |
|
x += 15; |
|
t.draw(new Point(x, y)); |
|
console.log('intersects'); |
|
} |
|
} |
|
prev = t; |
|
} |
|
} |
|
}
|
|
|