p5(s=>{
const PHI = (1 + Math.sqrt(5)) / 2;
const PHISQ = PHI*PHI;
const OBTSUE_H = Math.sqrt(1 - PHISQ/4);
const ACUTE_H = Math.sqrt(1 - 1 / (4 * PHISQ));
class Triangle {
constructor(x, y, rotation, size, type, direction, level){
this.x = x;
this.y = y;
this.rotation = rotation;
this.size = size;
this.d = direction;
this.type = type;
this.level = level;
this.divided = false;
}
divide(){
this.divided = true;
let res = [];
if (this.type === 1) {
let newSz = this.size / PHISQ;
let r = - 3 * Math.PI/5;
let newRotation = this.d === 1 ? this.rotation + r : this.rotation - r;
let ang = this.d === 1 ? this.rotation + Math.PI/10 : this.rotation - Math.PI/10;
let nx = this.x + newSz * Math.cos(ang);
let ny = this.y + newSz * Math.sin(ang);
res.push(new Triangle(nx, ny, newRotation, newSz, 0, this.d, this.level + 1));
newSz = this.size / PHI;
r = 3 * Math.PI / 5;
newRotation = this.d === 1 ? this.rotation - r : this.rotation + r;
ang = this.d === 1 ? this.rotation + Math.PI/10 : this.rotation - Math.PI/10;
nx = this.x + this.size * Math.cos(ang);
ny = this.y + this.size * Math.sin(ang);
res.push(new Triangle(nx, ny, newRotation, newSz, 1, this.d, this.level + 1));
r = 4 * Math.PI / 5;
newRotation = this.d === 1 ? this.rotation - r : this.rotation + r;
ang = this.d === 1 ? this.rotation + Math.PI/10 : this.rotation - Math.PI / 10;
nx = this.x + this.size * Math.cos(ang);
ny = this.y + this.size * Math.sin(ang);
res.push(new Triangle(nx, ny, newRotation, newSz, 1, (this.d + 1) % 2, this.level + 1));
} else {
//obtuse, divide to two
let newSz = this.size;
let r = 3 * Math.PI / 5;
let newRotation = this.d === 1 ? this.rotation + r : this.rotation - r;
let ang = this.d === 1 ? this.rotation - 3 * Math.PI / 10 : this.rotation + 3 * Math.PI / 10;
let nx = this.x + this.size * Math.cos(ang);
let ny = this.y + this.size * Math.sin(ang);
res.push(new Triangle(nx, ny, newRotation, newSz, 1, (this.d + 1) % 2, this.level + 1));
newSz = this.size / PHI;
r = 4 * Math.PI / 5;
newRotation = this.d === 1 ? this.rotation + r : this.rotation - r;
ang = this.d === 1 ? this.rotation + Math.PI / 10 : this.rotation - Math.PI / 10;
nx = this.x + newSz * Math.cos(ang);
ny = this.y + newSz * Math.sin(ang);
res.push(new Triangle(nx, ny, newRotation, newSz, 0, this.d, this.level + 1));
}
return res;
}
render(){
let ds = this.size;
s.push();
s.translate(this.x, this.y);
s.scale(ds);
s.rotate(this.rotation);
s.strokeWeight(1/ds); // make sure strokeweight looks the same in different scale;
if (this.type === 0){
s.triangle(OBTSUE_H, - PHI/2, 0, 0, OBTSUE_H, PHI/2);
} else {
s.triangle(ACUTE_H, - 1 / (PHI * 2), 0, 0, ACUTE_H, 1 / (PHI * 2));
}
s.pop();
}
}
s.setup = function(){
s.createCanvas(400,400);
s.noFill();
s.stroke(0);
let tris = [];
let size = s.width/2;
for (let i = 0; i < 5; i ++) {
let ang = i * 2 * Math.PI / 5;
tris.push(new Triangle(s.width/2, s.height/2, ang, size, 1, 0, 0));
tris.push(new Triangle(s.width/2, s.height/2, ang + Math.PI/5, size, 1, 1, 0));
}
for (let i = 0; i < tris.length; ++i) {
let t = tris[i]
if (t.level < 5 && Math.random() * (t.level + 1) > 0.2) tris.push(... t.divide());
}
tris = tris.filter(t => !t.divided);
tris.forEach(t => t.render());
}
})