Packed in for a Good Wander

Re-imagining of the sketch from which this is forked. Tweaked adjustment parameters, changed rendering to lines instead of circles, added fine-tuned adjustment slider to AngleIncrementA.

Most recent revision:
- added breakpoints for responsive pen opacity based on the minimum radius slider, and a corresponding function.

- Added a slider to adjust the number of iterations before the sketch stops running.

Log in to post a comment.

// Forked from "Beadpackpathwander" by ProjectGrantwood
// https://turtletoy.net/turtle/5f76422d98



let shrinkageAcceleration = 0; //min=0, max=50, step=1
let shrinkageAcceleration_FineAdjust = 0.0//min=0, max=1, step=0.001
let AngleIncrementA = 20 // min=-180, max=180, step=1
let AngleIncrementA_FineAdjust = 0 //min=0, max=1, step=0.01
let AngleIncrementB = -26 // min=-180, max=180, step=1
let AngleIncrementB_FineAdjust =0 //min=0, max=1, step=0.01
let minimumRadius = 0.2 // min=0.05, max=4, step=0.01
let initialRadius = 4 // min=1, max=34, step=1
let maxFailures = 0 //min=0, max=200, step=1
let MaxIterations = 20000 //min=1000, max=80000, step=1000


// the breakpoints are based on the value of the minimumRadius
let pen_opacity_breakpoints = [0.05, 0.08, 0.12, 0.15, 0.2, 0.25]
let pen_opacities = [0.375, 0.425, 0.5, 0.625, 0.75, 0.875]

let opacity = findCurrentPenOpacityBreakpoint(
        minimumRadius, 
        pen_opacity_breakpoints, 
        pen_opacities
    );

Canvas.setpenopacity(opacity);

// 

shrinkageAcceleration = (100 - (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();
const circleArray = [createCircle(0, 0, initialRadius, -Math.PI * 2)];

let ratio = 0.9;
let ratio2 = 0.999;
const initialRatio = ratio;

let currentCircle = circleArray[0]; 
let failures = 0;

t.pd();
t.radians();

function walk(i){
    if (i === 0){
       // draw(circleArray[0], t, '10');
    } else {
        for (let j = 0; j < 12; j++){
       currentCircle = getAndCheck(currentCircle, circleArray);
        }
    }
        
    return i < MaxIterations;
}

function createCircle(x, y, rad, heading){
    return [x, y, rad, heading];
}

function checkCircles(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, flags = '01'){
    turtle.jmp(c[0], c[1] - c[2]);
    if (flags[0] === '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 //* [-1, 1][Math.floor(Math.random() * 2)];
    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);
}

function checkBounds(c){
    let add = 1;
    add *= c[0] < 100 - c[2];
    add *= c[0] > -100 + c[2];
    add *= c[1] < 100 - c[2];
    add *= c[1] > -100 + c[2];
    return add;
}

function getAndCheck(c1, circleArray){
    let c2 = getNextCircle(currentCircle);
    let add = 1;
    add *= checkBounds(c2);
    let newC;
    for (let c of circleArray){
        if (!add){
            break;
        }
        add *= checkCircles(c, c2);
        if (!add){
            newC = c2;
            break;
        }
    }
    if (!add){
        c1[3] += angleIncrement2
        failures++;
        if (failures > maxFailures){
            failures = 0;
             c1[3] += angleIncrement2 //* [-1, 1][Math.floor(Math.random() * 2)];
             ratio *= initialRatio + shrinkageAcceleration;
             radius = initialRadius;
            let index = circleArray.indexOf(c1) - 1;
            index = index < 0 ? circleArray.length - 1 : index;
          
             //return circleArray[index];
             return circleArray[Math.floor(Math.random() * circleArray.length)]
            
        }
        return currentCircle;
    } else {
        ratio /= initialRatio + shrinkageAcceleration;
        draw(c2, t);
        circleArray.push(c2);
        return c2;
    }
        }
        
function findCurrentPenOpacityBreakpoint(min_radius, pen_opacity_breakpoints, pen_opacities) {
    let breakpoint = 0;
    for (let i = 0; i < pen_opacity_breakpoints.length; i++){
        if (min_radius > pen_opacity_breakpoints[i]){
            breakpoint++;
        }
        if (min_radius <= pen_opacity_breakpoints[breakpoint]){
            break;
        }
        }
        return pen_opacities[breakpoint];
    }