### Expanding circs

Adaptation of mathrabbit's Expanding Rects, but it's just circs.
(Expanding Rects)

Thanks chatgpt for this horrendous mess

```let NUM_CIRCLES = 500; // min=0, max=1000, step=10
let CENTER_RAD = 1; // min=0.5, max=5, step=0.1
let SHRINK = 1; // min=-10, max=10, step=1
let HIT_SIDES_CHANCE = 0.2; // min=0, max=1, step=0.1
let OUTER_PADDING = 10; // min=0, max=50, step=1
let SEED_PADDING = 30; // min=0, max=50, step=1
let MIN_RAD = 2; // min=0, max=20, step=0.5
let MAX_RAD = 10; // min=0, max=20, step=0.5

// The overlap can be a fun parameter to experiment with.
let OVERLAP_ALLOWANCE = 2; // min=0, max=10, step=0.5

// Booleans
let DO_EXPAND = 1; // min=0, max=1, step=1
let DRAW_CENTERS = 0; // min=0, max=1, step=1
let DRAW_FRAME = 1; // min=0, max=1, step=1
let DRAW_X = 0; // min=0, max=1, step=1

// Hatching options
let HATCH_CHANCE = 0.8; // min=0, max=1, step=0.1
let HATCH_SPACING = 1.5; // min=1, max=10, step=0.5
let SPARSE_HATCH_CHANCE = 0.15; // min=0, max=1, step=0.1
let R_SPACING = 15; // min=5, max=45, step=5
let R_SPACING_VARIANCE = 5; // min=0, max=20, step=1
let R_SPACING_MIN = 5; // min=1, max=20, step=1

let HATCH_TYPE_CHANCE = 0.5;

Canvas.setpenopacity(1);
const turtle = new Turtle();
turtle.pendown();

let rand = (min, max) => Math.random() * (max - min) + min;
let randInt = (min, max) => Math.floor(rand(min, max));
let degreesToRadians = degrees => degrees * (Math.PI / 180);

let intersect = (circle1, circle2) => {
let dx = circle1.x - circle2.x;
let dy = circle1.y - circle2.y;
let distance = Math.sqrt(dx * dx + dy * dy);
return distance < circle1.r + circle2.r - OVERLAP_ALLOWANCE;
};

let Circle = (x, y, r) => {
let circle = {
x: x,
y: y,
r: r,
};

circle.draw = () => {
if (DRAW_FRAME) {
turtle.jump(circle.x, circle.y - circle.r);
turtle.circle(circle.r);
}

if (DRAW_CENTERS) {
turtle.jump(circle.x, circle.y - 0.5);  // Adjust the 0.5 to the desired dot radius
turtle.circle();
}

if (Math.random() < HATCH_CHANCE) {
let isRadialHatch = Math.random() < HATCH_TYPE_CHANCE;

let spacing = HATCH_SPACING;
if (Math.random() < SPARSE_HATCH_CHANCE) {
spacing *= 2;
}

// Adjust the spacing based on variance and minimum spacing for radial hatching
let randomSpacing = R_SPACING + (Math.random() - 0.5) * 2 * R_SPACING_VARIANCE;
randomSpacing = Math.max(randomSpacing, R_SPACING_MIN);

for (let angle = 0; angle < 360; angle += randomSpacing) {
turtle.goto(circle.x, circle.y);
}
} else {
// 45° hatching using the original 'spacing' value
let distance = Math.SQRT2 * circle.r;  // This ensures hatching covers the entire circle
let x1 = circle.x - distance / 2;
let y1 = circle.y + distance / 2;
let x2 = circle.x + distance / 2;
let y2 = circle.y - distance / 2;

for (let i = -distance/2; i <= distance/2; i += spacing) {
turtle.jump(x1 + i, y1);
turtle.goto(x2 + i, y2);

}
}
}
};

circle.expand = (amt) => {
circle.r += amt;
};

circle.isValid = () => circle.r > 0;

return circle;
};

let circles = [];

for (let ii = 0; ii < NUM_CIRCLES; ii++) {
let limit = 100 - SEED_PADDING;
let x = randInt(-limit, limit);
let y = randInt(-limit, limit);
let circle = Circle(x, y, r);
let intersects = false;
for (let jj = 0; jj < circles.length; jj++) {
if (intersect(circles[jj], circle)) {
intersects = true;
break;
}
}
if (!intersects) {
circles.push(circle);
}
}
console.log('' + circles.length + ' circles');

for (let c of circles) {
c.expand(-1);
}
circles = circles.filter(c => c.isValid());

function walk(ii) {
circles[ii].draw();
return ii < circles.length-1;
}
```