Reflection fractal
Click to create lines. The canvas below tries to draw a fractal formed by shooting rays and keeping track of the side they exit from.
... it is not a very good fractal, however, so this gets the WIP tag for now 😅
let c2
function setup() {
createCanvas(400, 400);
c2 = new p5((x) => {
let pt = 0
let pti = random() * 400 * 400 +random() * 400 + random() + random() / 400 + random() / 400 / 400
x.setup = () => {
x.createCanvas(400, 400)
x.background(0)
x.strokeWeight(1.5)
x.frameRate(180)
}
x.draw = () => {
for (let i = 0; i < 80; i ++) {
pt += pti//(Math.SQRT2 * 24 + 0.001 + 0.0003 + 0.000005) * 20
p = [pt % width, (pt / width) % height]
//let p = [x.random(0, 400), x.random(0, 400)]
let {points, rayD, finished} = iterateRay(p, [200-p[0], 200-p[1]], 80)
// let {points, rayD, finished} = iterateRay(p, [1, 0], 80)
let wh = points.length / 80 * 200
if (abs(rayD[0]) > abs(rayD[1])) {
if (rayD[0] > 0) {
x.stroke(255, wh, wh)
} else {
x.stroke(wh, 255, wh)
}
} else {
if (rayD[1] > 0) {
x.stroke(wh, wh, 255)
} else {
x.stroke(255, 255, wh)
}
}
x.point(...p)
}
}
})
}
let lines = []
function draw() {
background(220);
for (let [p1, p2] of lines) {
line(...p1, ...p2)
}
let p = [mouseX, mouseY]
let {points, rayO, rayD, finished} = iterateRay(p, [200-p[0], 200-p[1]], 80)
stroke(255, 0, 0)
for (let i = 1; i < 80 && i < points.length; i ++) {
stroke(255, 0, 0, 255 - 255 / 80 * i)
strokeWeight(1 + 3 * i / 80)
line(...points[i - 1], ...points[i])
}
if (finished) {
line(...rayO, rayD[0] * 600 + rayO[0], rayD[1] * 600 + rayO[1])
}
stroke(0)
strokeWeight(1)
if (newLine.length == 0) {
point(mouseX, mouseY)
} else if (newLine.length == 1) {
line(...newLine[0], mouseX, mouseY)
}
}
let newLine = []
function mousePressed() {
newLine.push([mouseX, mouseY])
if (newLine.length == 2) {
c2.background(0)
lines.push(newLine)
newLine = []
}
}
function iterateRay(rayO, rayD, maxI) {
let points = []
{
let l = mag(...rayD)
rayD = [rayD[0] / l, rayD[1] / l]
}
for (let i = 0; i < maxI; i ++) {
let best = null
let bestD = null
for (let [p1, p2] of lines) {
let res = line_intersect(...rayO, rayD[0] + rayO[0], rayD[1] + rayO[1], ...p1, ...p2)
if (res && res.ub > 0 && res.ub < 1 && res.ua > 1e-8 && (!best || best.ua > res.ua)) {
best = res
bestD = [p1[1] - p2[1], p2[0] - p1[0]]
}
}
points.push(rayO)
if (!best) {
return {points, rayO, rayD, finished: true}
break
}
rayO = [best.x, best.y]
let l = mag(...bestD)
let normalized = [bestD[0] / l, bestD[1] / l]
let dt = rayD[0] * normalized[0] + rayD[1] * normalized[1]
rayD = [rayD[0] - 2 * dt * normalized[0], rayD[1] - 2 * dt * normalized[1]]
}
return {points, rayO, rayD, finished: false}
}
// https://stackoverflow.com/a/38977789
function line_intersect(x1, y1, x2, y2, x3, y3, x4, y4) {
let denom = (y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1);
if (denom == 0) {
return null;
}
let ua = ((x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3))/denom;
let ub = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3))/denom;
return {
x: x1 + ua * (x2 - x1),
y: y1 + ua * (y2 - y1),
ua,
ub
};
}(Originally seen at https://editor.p5js.org/bojidar-bg/sketches/RPDC8yBdq)
Browse more articles?
← Ellipse-tracing system Experiments tagged p5 (72/85) Sine ribbon →
← Musical Critter Experiments tagged interactive (23/26) Space critter →
← Match sets Experiments tagged wip (4/5) Hexagon explorer →
← Ellipse-tracing system Experiments on this site (72/85) Sine ribbon →