Circle Packing

Non-overlapping circles are packed inside a rectangle that is rendered into an off-screen graphics buffer.

Positioning the Circles

Similar to the Generative Agency Project, the coordinates of each circle are set by a method that runs while the circle has no coordinates yet or if the circle is positioned outside the shape. The isWithinShape() class method returns if circle is in the shape. p5's get() method is used to determine the colour for the current pixel. The value returned by this method is compared with the colour of the rectangle shape, which is set to black.

setCoordinates() {
    while (this.coordinates == null || !this.isWithinShape(this.coordinates)) {
        this.coordinates = createVector(random(width), random(height)); 
    } 
}
    
isWithinShape(_coordinates) {
    let px = graphics.get(floor(_coordinates.x), floor(_coordinates.y));
    return (
        px[0] == graphicsColor.levels[0] &&
        px[1] == graphicsColor.levels[1] &&
        px[2] == graphicsColor.levels[2] &&
        px[3] == graphicsColor.levels[3]
    );
}

Checking Overlaps

A circle is created each time the draw() function runs and is only added to the circles array if it is not overlapping any other circle. To check overlaps, the distance between the coordinates of the two circles is calculated and checked if it's lesser than the radii of the circles.

function draw() {
    let randR = floor(random(minR, maxR));
    let newC = new Circle(randR);
    addCircle(newC);
}

function addCircle(_newC) {
    let newC = _newC;
    for (let i = 0; i < circles.length; i++) {
        let otherC = circles[i];
        let distance = newC.coordinates.dist(otherC.coordinates);
        if (distance < newC.radius + otherC.radius) return null;
    }
    circles.push(newC);
}

Drawing the Circles

The radius of each circle grows by 0.05 each time and stops when it hits an edge of the canvas or when it collides with another circle.