|
|
|
|
|
|
|
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 Pattern {
|
|
|
|
constructor(type) {
|
|
|
|
this.type = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
apply(branch) {
|
|
|
|
var crownpath = branch.crown._path;
|
|
|
|
for (let p of this.generate(crownpath.bounds)) {
|
|
|
|
var pp = p.intersect(crownpath, {trace: this._intersect_trace});
|
|
|
|
pp.strokeWidth = 1.5;
|
|
|
|
pp.strokeColor = 'black';
|
|
|
|
}
|
|
|
|
|
|
|
|
if(typeof branch.forks !== "undefined") {
|
|
|
|
for(let fork of branch.forks) {
|
|
|
|
this.apply(fork.branch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class MiniLinePattern extends Pattern {
|
|
|
|
constructor({tasize=7, grid=17, hat=true}={})
|
|
|
|
{
|
|
|
|
super('miniline')
|
|
|
|
this.tasize = tasize;
|
|
|
|
this.grid = grid;
|
|
|
|
this.hat = hat;
|
|
|
|
this._intersect_trace = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
generate(bounds) {
|
|
|
|
var ret = [];
|
|
|
|
for(var y=0; y<bounds.height / this.grid; y++) {
|
|
|
|
for(var x=0; x<bounds.width / this.grid; x++) {
|
|
|
|
var xx = bounds.x + x*this.grid + this.grid/3 * (y%2);
|
|
|
|
var yy = bounds.y + y*this.grid;
|
|
|
|
var p = new Path();
|
|
|
|
p.add(new Point(xx, yy+this.tasize));
|
|
|
|
if(this.hat) {
|
|
|
|
p.add(new Point(xx + this.tasize/2, yy));
|
|
|
|
}
|
|
|
|
p.add(new Point(xx + this.tasize, yy+this.tasize));
|
|
|
|
ret.push(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RandomCirclePattern extends Pattern {
|
|
|
|
constructor({radius=3, grid=15}={})
|
|
|
|
{
|
|
|
|
super('randomcircle')
|
|
|
|
this.radius = radius;
|
|
|
|
this.grid = grid;
|
|
|
|
this._intersect_trace = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
generate(bounds) {
|
|
|
|
var ret = [];
|
|
|
|
for(var y=0; y<bounds.height / this.grid; y++) {
|
|
|
|
for(var x=0; x<bounds.width / this.grid; x++) {
|
|
|
|
var xx = bounds.x + x*this.grid + Math.random() * this.grid/2;
|
|
|
|
var yy = bounds.y + y*this.grid + Math.random() * this.grid/2;
|
|
|
|
var p = new Path.Circle({
|
|
|
|
center: new Point(xx, yy),
|
|
|
|
radius: this.radius
|
|
|
|
});
|
|
|
|
ret.push(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class LinePattern extends Pattern {
|
|
|
|
constructor({grid=5, angle=Math.random()*2*Math.PI}={})
|
|
|
|
{
|
|
|
|
super('line')
|
|
|
|
this.grid = grid;
|
|
|
|
this.angle = angle;
|
|
|
|
this._intersect_trace = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
generate(bounds) {
|
|
|
|
var ret = [];
|
|
|
|
var r = Math.random() * 360;
|
|
|
|
for(var x=0; x<bounds.width * 3; x += this.grid) {
|
|
|
|
var p = new Path();
|
|
|
|
p.add(new Point(bounds.x - bounds.width + x,
|
|
|
|
bounds.y - bounds.height));
|
|
|
|
p.add(new Point(bounds.x - bounds.width + x,
|
|
|
|
bounds.y + bounds.height * 2));
|
|
|
|
p.rotate(this.angle * 360 / (2*Math.PI),
|
|
|
|
new Point(bounds.x + bounds.width * 0.5,
|
|
|
|
bounds.y + bounds.height * 0.5));
|
|
|
|
ret.push(p);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 outline = true;
|
|
|
|
|
|
|
|
var maxh = (height - (rows-1)*rowspace - 2*outspace - 2*inspace) / rows;
|
|
|
|
|
|
|
|
if(outline) {
|
|
|
|
var outlinepath = 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');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var ptrn = null;
|
|
|
|
var ptrnrand = Math.random() * 6;
|
|
|
|
if(ptrnrand < 1) {
|
|
|
|
ptrn = new MiniLinePattern();
|
|
|
|
} else if(ptrnrand < 2) {
|
|
|
|
ptrn = new MiniLinePattern({hat: false});
|
|
|
|
} else if(ptrnrand < 3) {
|
|
|
|
ptrn = new RandomCirclePattern();
|
|
|
|
} else if(ptrnrand < 4) {
|
|
|
|
ptrn = new RandomCirclePattern({radius: 1});
|
|
|
|
} else if(ptrnrand < 5) {
|
|
|
|
ptrn = new LinePattern({grid: 5 + Math.random() * 3});
|
|
|
|
}
|
|
|
|
if(ptrn != null) {
|
|
|
|
ptrn.apply(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
prev = t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|