User:Shoebby/Webby Sprouts
Download here: https://addons.mozilla.org/en-US/firefox/addon/webby-sprouts/
blogpost: https://www.lexie.land/dist/blogpost-penplot.html
(Where am I in) The Process
My dive into fractals brought me into contact with a lot of different mediums for drawing them, here is the process shown by way of images in (rough) chronological order.
Pen Plotting
import math
import numpy as np
file = open("fractal.hgl", "w")
def rotate(vec, angle):
rads = math.radians(angle)
a = math.cos(rads)
b = math.sin(rads)
R = np.array([[a,-b], [b,a]])
return np.dot(R, vec)
def tree(x1, y1, x2, y2, angleLeft, angleRight, ratio, depth):
if depth == 0: return
file.write(f'PU {round(x1)},{round(y1)};\n')
file.write(f'PD {round(x2)},{round(y2)};\n')
base = np.array([x2 - x1, y2 - y1])
new_base = base * ratio
right = rotate(new_base, angleRight)
left = rotate(new_base, -angleLeft)
end = np.array([x2, y2])
right_end = np.add(end, right)
left_end = np.add(end, left)
tree(x2, y2, right_end[0], right_end[1], angleLeft, angleRight, ratio, depth - 1)
tree(x2, y2, left_end[0], left_end[1], angleLeft, angleRight, ratio, depth - 1)
print("What's the stem's starting x position? (whole numbers)")
start_x = int(input())
print("What's the stem's starting y position? (whole numbers)")
start_y = int(input())
print("What's the stem's end x position? (whole numbers)")
end_x = int(input())
print("What's the stem's end y position? (whole numbers)")
end_y = int(input())
print("Are the branch angles symmetrical? (y/n)")
symmetrical = input().lower().strip() == "y"
if symmetrical:
print("What angle are the branches? (in degrees, whole numbers)")
angle = int(input())
angleLeft = angle
angleRight = angle
else:
print("What angle is the left branch? (in degrees, whole numbers)")
angleLeft = int(input())
print("What angle is the right branch? (in degrees, whole numbers)")
angleRight = int(input())
print("What ratio should the branches be? (decimal numbers (i.e. .75)) >1 means the branches grow with every recursion, <1 means they shrink.")
ratio = float(input())
print("How many recursions should this fractal be? (whole numbers) 5-10 is usually a good starting point to get an idea of the overall shape.)")
recursions = int(input())
tree(start_x, start_y, end_x, end_y, angleLeft, angleRight, ratio, recursions)
The Canvas Element
function tree(x1, y1, x2, y2, angleLeft, angleRight, ratio, depth) {
console.log("entered tree depth: " + depth)
console.log("(" + x1 + ", " + y1 + ") to (" + x2 + ", " + y2 + ")");
if (depth <= 0) {
return;
}
ctx.strokeStyle = "rgb(" + (parseInt(depth)*10) + " " + (parseInt(depth)*3) + " " + (parseInt(depth)*14) + ")";
if (depth == 1) {
ctx.fillStyle = "white";
ctx.strokeStyle = "white";
ctx.beginPath();
ctx.arc(x2, y2, 5, 0, Math.PI * 2, false);
ctx.fill();
}
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
var base = new Vector2(x2 - x1, y2 - y1);
var new_base = base.clone().multiplyScalar(ratio);
var angleLeft_rad = MathUtils.degToRad(angleLeft);
var angleRight_rad = MathUtils.degToRad(angleRight);
var left = new_base.clone().rotateAround(new Vector2(0, 0), -angleLeft_rad);
var right = new_base.clone().rotateAround(new Vector2(0, 0), angleRight_rad);
var end = new Vector2(x2, y2);
var left_end = end.clone().add(left);
var right_end = end.clone().add(right);
tree(x2, y2, left_end.x, left_end.y, angleLeft, angleRight, ratio, depth - 1);
tree(x2, y2, right_end.x, right_end.y, angleLeft, angleRight, ratio, depth - 1);
}
Now taking fractals fully digital I used the canvas element to generate them, generate your own here!
Fractals From Divs
function initTrees(treeAmount) {
var startAngle = 360 / treeAmount;
depthstep = 255 / depth;
for (i = 0; i < treeAmount; i++) {
tree(startLength, startAngle*i, depth, null, null, ratio);
}
}
function tree(startLength, startRot, depth, parent, angle, ratio) {
if (depth == 0) {
return;
}
const newBranch = document.createElement("div");
newBranch.classList.add("branch");
newBranch.setAttribute("id", "treeBranch");
if (parent != null) {
parent.appendChild(newBranch);
newBranch.style.setProperty('--w', parent.offsetWidth * ratio + "px");
newBranch.style.setProperty('--a', angle + "deg");
} else if (parent == null) {
newBranch.classList.add("root");
document.body.appendChild(newBranch);
newBranch.style.setProperty('--w', startLength + "px");
newBranch.style.setProperty('--a', startRot + "deg");
}
newBranch.innerHTML = "" + newBranch.offsetWidth + ""
newBranch.style.setProperty('--colorR', 255);
newBranch.style.setProperty('--colorG', 255 - (depth * depthstep));
newBranch.style.setProperty('--colorB', 0);
newBranch.style.setProperty('--colorA', 1);
parent = newBranch;
tree(startLength, startRot, depth - 1, newBranch, angleLeft, ratio);
tree(startLength, startRot, depth - 1, newBranch, -angleRight, ratio);
}
It works, and I could even apply past things like uneven left/right angles already, play with it yourself here
Fractals From The DOM
function webTree(startLength, parentElement, parentBranch, angle, ratio, color) {
const newBranch = document.createElement("div");
newBranch.classList.add("branchDiv");
parentElement.classList.add("branchElement");
if (parentBranch != null) {
parentBranch.appendChild(newBranch);
newBranch.style.setProperty('--w', parentBranch.offsetWidth * ratio + "px");
newBranch.style.setProperty('--a', angle + "deg");
} else if (parentBranch == null) {
newBranch.classList.add("rootDiv");
document.body.appendChild(newBranch);
newBranch.style.setProperty('--w', startLength + "px");
newBranch.style.setProperty('--a', "90deg");
}
newBranch.style.setProperty('--colorR', 255);
newBranch.style.setProperty('--colorG', 255 - newBranch.offsetWidth);
newBranch.style.setProperty('--colorB', 0);
newBranch.style.setProperty('--colorA', 1);
if (newBranch.hasChildNodes() == false) {
const newBulb = document.createElement("div");
newBulb.classList.add("branchDiv");
newBulb.style.height = "50px";
newBulb.style.width = "50px";
newBulb.style.backgroundColor = "white";
}
if (parentElement.tagName == "A") {
newBranch.classList.add('linkFlower');
newBranch.innerHTML = "<a href=" + parentElement + " style='width:100%;height:100%;'></a>";
}
var siblings = parentElement.children;
for (let i = 0; i < siblings.length; i++) {
if (siblings[i].classList.contains("branchDiv") || siblings[i].tagName == "BR") {
continue;
}
var newAngle = getRndInteger(-55, 55);
var newRatio = getRndInteger(50, 100) / 100;
webTree(startLength, siblings[i], newBranch, newAngle, newRatio, color);
}
}