Circle collisions ⚪

Collisions of circles on a grid

Log in to post a comment.

Canvas.setpenopacity(0.85);

const turtle = new Turtle();
const grid = 11; // min=5, max=17, step=2
const radius = 11; // min=2, max=23, step=2
const size = 7/9 * 200 / grid;

function getX(x) { return x - (grid/2|0) };
function getY(y) { return y - (grid/2|0) };

function walk() {
    const circles = [];
    for(let idx=0;idx<grid*grid;idx++) {
        const px = getX(idx % grid);
        const py = getY(idx / grid | 0);
        const r = 3 + Math.random() * radius;
        circles.push([px * size, py * size, r]);
    }
    
    circles.forEach(circle => drawCircle(circle, turtle));

    let collisions = [];
    for(let idx=0;idx<circles.length-1;idx++) {
        let circle1 = circles[idx];
        for(let idy=idx + 1;idy<circles.length;idy++) {
            let circle2 = circles[idy];
            let intersection = circleToCircle(circle1, circle2);
            if (intersection) {
                collisions.push({circle1, circle2, intersection});
            }
        }
    }
    collisions.forEach((col, idx) => {
        const circle1 = col.circle1;
        const circle2 = col.circle2;
        const [p1, p2] = col.intersection;
        
        drawCircle([...p1, 0.75], turtle);
        drawCircle([...p2, 0.75], turtle);
    });
}

// centered circle
function drawCircle(c, t) {
    t.jump(add(c, scl([0, -c[2] ],1)));
    t.circle(c[2]);
}

function drawArc(c, a1, a2, t, DENSITY = 0.25) {
    while (a2 < a1) a2 += Math.PI*2;
    const [x,y,r] = c;
    for (let a = a1; a < a2; a += 1/r/DENSITY) {
        if (a == a1) t.jump(x + Math.cos(a) * r, y + Math.sin(a) * r);
        t.goto(x + Math.cos(a) * r, y + Math.sin(a) * r);
    }
    t.goto(x + Math.cos(a2) * r, y + Math.sin(a2) * r);
}

// https://www.xarg.org/2016/07/calculate-the-intersection-points-of-two-circles/
function circleToCircle(p0, p1) {
    const r0 = p0[2];
    const r1 = p1[2];
    const dd = sub(p1, p0);
    const d = len(dd);
    if (d <= r0 + r1 && d >= Math.abs(r1 - r0)) {
      const e = scl(dd, 1 / d);
      const x = ((r0**2) - (r1**2) + (d**2)) / (2*d);
      const y = Math.sqrt((r0**2) - (x**2));
      return [
          [
            p0[0] + x * e[0] - y * e[1],
            p0[1] + x * e[1] + y * e[0],
          ],
          [
            p0[0] + x * e[0] + y * e[1],
            p0[1] + x * e[1] - y * e[0],
          ]
      ];
    } else {
        return null;
    }
}

function pointInCircle(p,c) {
    return len(sub(p,c)) + 0.001 < c[2];
}

// vec2 functions
function vec2(a)    { return [a,a]; }
function scl(a,b)   { return [a[0]*b, a[1]*b]; }
function add(a,b)   { return [a[0]+b[0], a[1]+b[1]]; }
function sub(a,b)   { return [a[0]-b[0], a[1]-b[1]]; }
function dot(a,b)   { return a[0]*b[0] + a[1]*b[1]; }
function len(a)     { return Math.sqrt(a[0]**2 + a[1]**2); }
function nrm(a)     { return scl(a, 1/len(a)); }
function lrp(a,b,f) { return [a[0]*f+b[0]*(1-f), a[1]*f+b[1]*(1-f)]; }
function angle(a) { return Math.atan2(a[1], a[0]); }
function eql(a,b)    { return a[0]==b[0] && a[1]==b[1]; }