342 lines
9.5 KiB
JavaScript
342 lines
9.5 KiB
JavaScript
(function () {
|
|
var canvas = document.getElementById("vines");
|
|
var context = canvas.getContext("2d");
|
|
var canvasb = document.getElementById("leaves");
|
|
var contextb = canvasb.getContext("2d");
|
|
var leaves = [];
|
|
|
|
// Resize canvas
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
canvasb.width = window.innerWidth;
|
|
canvasb.height = window.innerHeight;
|
|
|
|
// Clear canvas
|
|
context.fillStyle = "#89f1d7";
|
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// Calculate distance from point to line
|
|
function distancePointToLine(point, line) {
|
|
var L = Math.sqrt(
|
|
Math.pow(line[1].x - line[0].x, 2) + Math.pow(line[1].y - line[0].y, 2)
|
|
);
|
|
var r =
|
|
((point.x - line[0].x) * (line[1].x - line[0].x) +
|
|
(point.y - line[0].y) * (line[1].y - line[0].y)) /
|
|
Math.pow(L, 2);
|
|
var s =
|
|
((line[0].y - point.y) * (line[1].x - line[0].x) -
|
|
(line[0].x - point.x) * (line[1].y - line[0].y)) /
|
|
Math.pow(L, 2);
|
|
if (r >= 0 && r <= 1) {
|
|
return Math.abs(s) * L;
|
|
} else {
|
|
return Math.min(
|
|
Math.sqrt(
|
|
Math.pow(point.x - line[0].x, 2) + Math.pow(point.y - line[0].y, 2)
|
|
),
|
|
Math.sqrt(
|
|
Math.pow(point.x - line[1].x, 2) + Math.pow(point.y - line[1].y, 2)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
// Draw leaf
|
|
function drawLeaf(leaf) {
|
|
leaf.size = Math.max(10, leaf.size);
|
|
contextb.save();
|
|
contextb.translate(leaf.x, leaf.y);
|
|
contextb.rotate(leaf.angle);
|
|
contextb.translate(-leaf.x, -leaf.y);
|
|
contextb.fillStyle = "#ff0078";
|
|
contextb.beginPath();
|
|
contextb.moveTo(leaf.x, leaf.y);
|
|
contextb.bezierCurveTo(
|
|
leaf.x + leaf.size,
|
|
leaf.y,
|
|
leaf.x + leaf.size * 2,
|
|
leaf.y + leaf.size,
|
|
leaf.x,
|
|
leaf.y + leaf.size * 4
|
|
);
|
|
contextb.bezierCurveTo(
|
|
leaf.x - leaf.size * 2,
|
|
leaf.y + leaf.size,
|
|
leaf.x - leaf.size,
|
|
leaf.y,
|
|
leaf.x,
|
|
leaf.y
|
|
);
|
|
contextb.closePath();
|
|
contextb.fill();
|
|
contextb.restore();
|
|
}
|
|
|
|
// Draw vine
|
|
function drawVinesWithLattice(
|
|
lattice,
|
|
seeds,
|
|
interations,
|
|
sort,
|
|
prune,
|
|
minLength,
|
|
maxLength
|
|
) {
|
|
var t = 1;
|
|
var maxLineWidth = 10;
|
|
|
|
// Create initial branches
|
|
var branches = [];
|
|
for (var i in seeds) {
|
|
branches.push({
|
|
points: [
|
|
{ x: seeds[i].x, y: seeds[i].y },
|
|
{ x: seeds[i].x, y: seeds[i].y },
|
|
{ x: seeds[i].x, y: seeds[i].y },
|
|
{ x: seeds[i].x, y: seeds[i].y },
|
|
],
|
|
angle: 0,
|
|
distanceToLattice: 1000,
|
|
lineWidth: 6,
|
|
active: true,
|
|
leaf: false,
|
|
});
|
|
}
|
|
|
|
// Animation function
|
|
function tick() {
|
|
// Draw branches
|
|
contextb.clearRect(0, 0, canvasb.width, canvasb.height);
|
|
for (var i in branches) {
|
|
var ax =
|
|
(-branches[i].points[0].x +
|
|
3 * branches[i].points[1].x -
|
|
3 * branches[i].points[2].x +
|
|
branches[i].points[3].x) /
|
|
6;
|
|
var ay =
|
|
(-branches[i].points[0].y +
|
|
3 * branches[i].points[1].y -
|
|
3 * branches[i].points[2].y +
|
|
branches[i].points[3].y) /
|
|
6;
|
|
var bx =
|
|
(branches[i].points[0].x -
|
|
2 * branches[i].points[1].x +
|
|
branches[i].points[2].x) /
|
|
2;
|
|
var by =
|
|
(branches[i].points[0].y -
|
|
2 * branches[i].points[1].y +
|
|
branches[i].points[2].y) /
|
|
2;
|
|
var cx = (-branches[i].points[0].x + branches[i].points[2].x) / 2;
|
|
var cy = (-branches[i].points[0].y + branches[i].points[2].y) / 2;
|
|
var dx =
|
|
(branches[i].points[0].x +
|
|
4 * branches[i].points[1].x +
|
|
branches[i].points[2].x) /
|
|
6;
|
|
var dy =
|
|
(branches[i].points[0].y +
|
|
4 * branches[i].points[1].y +
|
|
branches[i].points[2].y) /
|
|
6;
|
|
context.lineWidth = branches[i].lineWidth;
|
|
context.beginPath();
|
|
context.moveTo(
|
|
ax * Math.pow(t, 3) + bx * Math.pow(t, 2) + cx * t + dx,
|
|
ay * Math.pow(t, 3) + by * Math.pow(t, 2) + cy * t + dy
|
|
);
|
|
context.lineTo(
|
|
ax * Math.pow(t + 0.1, 3) +
|
|
bx * Math.pow(t + 0.1, 2) +
|
|
cx * (t + 0.1) +
|
|
dx,
|
|
ay * Math.pow(t + 0.1, 3) +
|
|
by * Math.pow(t + 0.1, 2) +
|
|
cy * (t + 0.1) +
|
|
dy
|
|
);
|
|
context.stroke();
|
|
context.closePath();
|
|
|
|
// Draw leaf
|
|
if (branches[i].leaf) {
|
|
if (branches[i].active) {
|
|
var x1 = ax * Math.pow(t, 3) + bx * Math.pow(t, 2) + cx * t + dx;
|
|
var y1 = ay * Math.pow(t, 3) + by * Math.pow(t, 2) + cy * t + dy;
|
|
var x2 =
|
|
ax * Math.pow(t + 0.1, 3) +
|
|
bx * Math.pow(t + 0.1, 2) +
|
|
cx * (t + 0.1) +
|
|
dx;
|
|
var y2 =
|
|
ay * Math.pow(t + 0.1, 3) +
|
|
by * Math.pow(t + 0.1, 2) +
|
|
cy * (t + 0.1) +
|
|
dy;
|
|
branches[i].leaf.x = x2;
|
|
branches[i].leaf.y = y2;
|
|
branches[i].leaf.angle = Math.atan2(y2 - y1, x2 - x1) - Math.PI / 2;
|
|
branches[i].leaf.size = branches[i].lineWidth * 2;
|
|
} else {
|
|
branches[i].leaf.size = (branches[i].lineWidth + t) * 2;
|
|
}
|
|
drawLeaf(branches[i].leaf);
|
|
}
|
|
}
|
|
|
|
// Draw leaves
|
|
for (var i in leaves) {
|
|
drawLeaf(leaves[i]);
|
|
}
|
|
|
|
// When finished drawing splines, create a new set of branches
|
|
t += 0.02;
|
|
if (t >= 1) {
|
|
var new_branches = [];
|
|
for (var j = branches.length - 1; j >= 0; j--) {
|
|
if (branches[j].active) {
|
|
// Split branch into two
|
|
for (var k = 0; k < 2; k++) {
|
|
// Generate random deviation from previous angle
|
|
var angle = branches[j].angle - (Math.random() * 180 - 90);
|
|
|
|
// Determine closest lattice point
|
|
var distance = 100000;
|
|
for (var l in lattice) {
|
|
var result = distancePointToLine(
|
|
branches[j].points[3],
|
|
lattice[l]
|
|
);
|
|
if (result < distance) distance = result;
|
|
}
|
|
|
|
// Generate random length
|
|
var length = Math.random() * (maxLength - minLength) + minLength;
|
|
|
|
// Calculate new point
|
|
var x2 =
|
|
branches[j].points[3].x +
|
|
Math.sin((Math.PI * angle) / 180) * length;
|
|
var y2 =
|
|
branches[j].points[3].y -
|
|
Math.cos((Math.PI * angle) / 180) * length;
|
|
|
|
// Add to new branch array
|
|
new_branches.push({
|
|
points: [
|
|
branches[j].points[1],
|
|
branches[j].points[2],
|
|
branches[j].points[3],
|
|
{ x: x2, y: y2 },
|
|
],
|
|
angle: angle,
|
|
distanceToLattice: distance,
|
|
lineWidth: 6,
|
|
active: true,
|
|
leaf: {
|
|
x: x2,
|
|
y: y2,
|
|
angle: 0,
|
|
},
|
|
parent: branches[j],
|
|
});
|
|
|
|
// "Deactivate" branch
|
|
branches[j].active = false;
|
|
}
|
|
|
|
// Grow branch
|
|
} else {
|
|
branches[j].lineWidth++;
|
|
if (branches[j].lineWidth > maxLineWidth) {
|
|
leaves.push(branches[j].leaf);
|
|
branches.splice(j, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort branches by distance to lattice
|
|
new_branches.sort(function (a, b) {
|
|
return a.distanceToLattice - b.distanceToLattice;
|
|
});
|
|
|
|
// If over 10 branches, prune the branches furthest from the lattice
|
|
if (prune) {
|
|
if (sort) {
|
|
while (new_branches.length > 20) new_branches.pop();
|
|
} else {
|
|
while (new_branches.length > 20) {
|
|
new_branches.splice(
|
|
Math.floor(Math.random() * new_branches.length),
|
|
1
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove leaves from parent
|
|
for (var i = 0; i < new_branches.length; i++) {
|
|
new_branches[i].parent.leaf = false;
|
|
}
|
|
|
|
// Replace old branch array with new
|
|
branches = branches.concat(new_branches);
|
|
interations--;
|
|
t = 0;
|
|
}
|
|
|
|
// Keep on animating
|
|
if (interations > 0) {
|
|
requestAnimationFrame(tick);
|
|
}
|
|
}
|
|
tick();
|
|
}
|
|
|
|
// Setup lattice
|
|
var space = canvas.height / 3;
|
|
var lattice = [];
|
|
for (var y = -space * 4; y < space * 4; y += space) {
|
|
lattice.push([
|
|
{ x: 0, y: y + canvas.height },
|
|
{ x: canvas.width, y: y },
|
|
]);
|
|
}
|
|
|
|
// Draw lattice
|
|
context.strokeStyle = "rgba(0, 0, 0, 0.1)";
|
|
context.lineWidth = canvas.height * 0.05;
|
|
context.lineCap = "square";
|
|
for (var i in lattice) {
|
|
context.beginPath();
|
|
context.moveTo(lattice[i][0].x, lattice[i][0].y);
|
|
context.lineTo(lattice[i][1].x, lattice[i][1].y);
|
|
context.stroke();
|
|
}
|
|
context.strokeStyle = "#ff0078";
|
|
|
|
// Create vines
|
|
var minLength = canvas.width * 0.05;
|
|
var maxLength = canvas.width * 0.1;
|
|
var iterations = 20;
|
|
var seeds = [
|
|
{ x: -5, y: space },
|
|
{ x: space, y: -5 },
|
|
{ x: -5, y: space * 3 },
|
|
{ x: canvas.width + 5, y: space },
|
|
{ x: space * 3, y: canvas.height + 5 },
|
|
];
|
|
drawVinesWithLattice(
|
|
lattice,
|
|
seeds,
|
|
iterations,
|
|
true,
|
|
true,
|
|
minLength,
|
|
maxLength
|
|
);
|
|
})();
|