Using a circle-packing algorithm to make a pen-plot with a name in the middle.
Log in to post a comment.
// Forked from "Packed in for a Good Wander" by ProjectGrantwood // https://turtletoy.net/turtle/04187a129d // Forked from "Beadpackpathwander" by ProjectGrantwood // https://turtletoy.net/turtle/5f76422d98 Canvas.setpenopacity(1); const LETTER_HEIGHT = 45; const LETTER_WIDTH = 30; const LETTER_UNIT_SIZE = 4; const LETTER_DIAGONAL = Math.sqrt(LETTER_HEIGHT * LETTER_HEIGHT + LETTER_WIDTH * LETTER_WIDTH) - Math.sqrt(LETTER_UNIT_SIZE * 2); const LETTER_DIAGONAL_ANGLE = Math.atan2(LETTER_HEIGHT, LETTER_WIDTH); const LETTER_CIRCLE_UNIT = 180 / LETTER_UNIT_SIZE - 1; const MARGIN = LETTER_UNIT_SIZE * 3.5; let shrinkageAcceleration = 50.0; let shrinkageAcceleration_FineAdjust = 0.0; let AngleIncrementA = 0.0 //min=-180, max=180, step=1 let AngleIncrementA_FineAdjust = 0 //min=0, max=1, step=0.01 let AngleIncrementB = -95 // min=-180, max=180, step=1 let AngleIncrementB_FineAdjust = 0.0 //min=0, max=1, step=0.01 let minimumRadius = 0.5 // min=0.05, max=4, step=0.01 let initialRadius = LETTER_UNIT_SIZE; let maxFailures = 0 //min=0, max=180, step=1 shrinkageAcceleration = (101 - (shrinkageAcceleration + shrinkageAcceleration_FineAdjust)) / 1000; initialRadius = minimumRadius > initialRadius ? minimumRadius : initialRadius; const angleIncrement1 = (AngleIncrementA + AngleIncrementA_FineAdjust) * Math.PI / 180; const angleIncrement2 = (AngleIncrementB + AngleIncrementB_FineAdjust) * Math.PI / 180; const t = new Turtle(); let circleArray = [ //Letter N new Array(Math.floor(LETTER_HEIGHT / LETTER_UNIT_SIZE)).fill().map((e, i) => createCircle(-LETTER_WIDTH * 3 + MARGIN, LETTER_HEIGHT / 2 + i * -LETTER_UNIT_SIZE, LETTER_UNIT_SIZE / 2, -Math.PI / 2, '00')), new Array(Math.floor(LETTER_DIAGONAL / LETTER_UNIT_SIZE)).fill().map((e, i) => createCircle(-LETTER_WIDTH * 3 + MARGIN + i * LETTER_UNIT_SIZE * Math.cos(LETTER_DIAGONAL_ANGLE), -LETTER_HEIGHT / 2 + LETTER_UNIT_SIZE - i * -LETTER_UNIT_SIZE, LETTER_UNIT_SIZE / 2, -Math.PI / 2, '00')), new Array(Math.floor(LETTER_HEIGHT / LETTER_UNIT_SIZE)).fill().map((e, i) => createCircle(-LETTER_WIDTH * 2 + MARGIN - LETTER_UNIT_SIZE, LETTER_HEIGHT / 2 + i * -LETTER_UNIT_SIZE, LETTER_UNIT_SIZE / 2, -Math.PI / 2, '00')), //Letter O new Array(Math.round(LETTER_CIRCLE_UNIT + 1)).fill().map((e, i) => createCircle(-LETTER_WIDTH + MARGIN - LETTER_UNIT_SIZE / 2 + (LETTER_WIDTH / 2) * Math.cos(Math.PI * 2/ LETTER_CIRCLE_UNIT * i), LETTER_UNIT_SIZE / 2 + (LETTER_HEIGHT / 2) * Math.sin(Math.PI * 2 / LETTER_CIRCLE_UNIT * i), LETTER_UNIT_SIZE / 2, Math.round(i / 90) * 90 * Math.PI / 180 + Math.PI / 4, '00')), //Letter E new Array(Math.floor(LETTER_HEIGHT / LETTER_UNIT_SIZE)).fill().map((e, i) => createCircle(MARGIN + LETTER_UNIT_SIZE / 2, LETTER_HEIGHT / 2 + i * -LETTER_UNIT_SIZE, LETTER_UNIT_SIZE / 2, -Math.PI / 2, '00')), new Array(Math.floor(LETTER_WIDTH / LETTER_UNIT_SIZE)).fill().map((e, i) => createCircle(MARGIN + LETTER_UNIT_SIZE / 2 + i * LETTER_UNIT_SIZE, LETTER_HEIGHT / 2, LETTER_UNIT_SIZE / 2, 0, '00')), new Array(Math.floor(LETTER_WIDTH / LETTER_UNIT_SIZE - 1)).fill().map((e, i) => createCircle(MARGIN + LETTER_UNIT_SIZE + i * LETTER_UNIT_SIZE, LETTER_UNIT_SIZE / 2, LETTER_UNIT_SIZE / 2, 0, '00')), new Array(Math.floor(LETTER_WIDTH / LETTER_UNIT_SIZE)).fill().map((e, i) => createCircle(MARGIN + LETTER_UNIT_SIZE / 2 + i * LETTER_UNIT_SIZE, -LETTER_HEIGHT / 2 + LETTER_UNIT_SIZE / 2 , LETTER_UNIT_SIZE / 2, 0, '00')), //Letter L new Array(Math.floor(LETTER_HEIGHT / LETTER_UNIT_SIZE)).fill().map((e, i) => createCircle(LETTER_WIDTH + MARGIN * 2, LETTER_HEIGHT / 2 + i * -LETTER_UNIT_SIZE, LETTER_UNIT_SIZE / 2, -Math.PI / 2, '00')), new Array(Math.floor(LETTER_WIDTH / LETTER_UNIT_SIZE - 1)).fill().map((e, i) => createCircle(LETTER_WIDTH + MARGIN * 2 + i * LETTER_UNIT_SIZE, LETTER_HEIGHT / 2, LETTER_UNIT_SIZE / 2, 0, '00')) ].flat(1); let ratio = 0.8; let ratio2 = 0.999; const initialRatio = ratio; let currentCircle = circleArray[0]; let failures = 0; t.radians(); t.pd(); let time = 0; function walk(){ if (time === 0){ for (let z = 0; z < circleArray.length; z++){ draw(circleArray[z], t, '10'); } time += 1; } else { for (let j = 0; j < 200; j++){ currentCircle = getAndCheck(currentCircle, circleArray, time); } time += 1; } return time < 20000; } function createCircle(x, y, rad, heading, connectRadii = false){ return [x, y, rad, heading, connectRadii]; } function checkCircleOverlap(c1, c2){ const xd = (c2[0] - c1[0]) ** 2; const yd = (c2[1] - c1[1]) ** 2; const dsq = xd + yd; return dsq >= (c1[2] + c2[2]) ** 2; } function draw(c, turtle, i, flags = c[4]){ turtle.jmp(c[0], c[1] - c[2]); if (flags[0] === '1'){ turtle.jmp(c[0], c[1]) turtle.circle(c[2]) } if (flags[1] === '1') { turtle.jmp(c[0], c[1]); turtle.seth(c[3] - Math.PI) let travelDistance = c[2]; travelDistance = c[2] === minimumRadius ? c[2] + c[2] + minimumRadius : c[2] + c[2] / ratio + minimumRadius turtle.forward(travelDistance) } } function getNextCircle(c){ let radiusOld = c[2]; let radiusNew = radiusOld * ratio radiusNew = radiusNew < minimumRadius ? minimumRadius : radiusNew let headingNew = c[3] += angleIncrement1; c[3] = headingNew; let d = ((radiusOld + radiusNew) + minimumRadius); let xOld = c[0]; let yOld = c[1]; let xNew = xOld + d * Math.cos(headingNew); let yNew = yOld + d * Math.sin(headingNew); return createCircle(xNew, yNew, radiusNew, headingNew, '01'); } function checkBounds(c){ let add = true; add &= c[0] < 100 - MARGIN - c[2]; add &= c[0] > -100 + MARGIN + c[2]; add &= c[1] < 100 - MARGIN - c[2]; add &= c[1] > -100 + MARGIN + c[2]; return add; } function getAndCheck(c1, circleArray, i){ let c2 = getNextCircle(currentCircle); let add = 1; add *= checkBounds(c2); let newC; if (add) { for (let c of circleArray){ add *= checkCircleOverlap(c, c2); if (!add){ newC = c; break; } } } if (!add){ c1[3] += angleIncrement2 failures++; if (failures >= maxFailures){ failures = 0; c1[3] += angleIncrement2 ratio *= initialRatio + shrinkageAcceleration; radius = initialRadius; let index = circleArray.indexOf(c1) - 1; index = index < 0 || Math.random() < 0.04 ? Math.floor(Math.random() * circleArray.length) : index; return circleArray[index]; // return circleArray[Math.floor(Math.random() * circleArray.length)] } return currentCircle; } else { //ratio /= initialRatio + shrinkageAcceleration; draw(c2, t, i); circleArray.push(c2); return c2; } } function map(val, hi, lo, newhi, newlo){ return ((val - lo) * (newhi - newlo)) / (hi - lo) + newlo; }