Raiden
Raiden

Reputation: 411

Check how much of a circle is colliding with another circle

Started working on my Agar.io clone again. Basic collision detection between two circles is easy (distance < radii = collision). However, Agar's collision detection is a bit more complex. It seems like it detects how much of circle A encompasses circle B, but because it's not open-source, I can't figure out how they did it. I saw this Agar clone on GitHub, but it would seem they took the easier route and just did regular collision detection.

This is how I'm doing it right now.

collision: function(circle) {
    var xx = circle.x - this.x;
    var yy = circle.y - this.y;
    var distance = Math.sqrt(xx * xx + yy * yy);
    return distance < this.radius + circle.radius;
},

How would I check how much of a circle is inside of another circle?

Upvotes: 3

Views: 251

Answers (1)

markE
markE

Reputation: 105035

You can calculate the area of intersection between 2 circles by adding the areas of the 2 intersecting circle sections:

enter image description here

  1. Calculate the 2 points where the circles intersect.

enter image description here

  1. Calculate the central angle formed by the intersecting wedge. The central angle is formed by connecting one intersecting point to the circle centerpoint to the other intersecting point.

enter image description here

  1. Calculate the area of a circle section. You can calculate the area of a circle section by first calculating the area of the wedge and then subtracting the area of the wedge's non-arc triangle.

enter image description hereminusenter image description hereequalsenter image description here

  1. Repeat for both circles and add both section areas to get the total area of the intersection of 2 circles.

enter image description here

Example code and a Demo

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
  var BB=canvas.getBoundingClientRect();
  offsetX=BB.left;
  offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }


// define 2 circles
var c1={x:100,y:100,r:100};
var c2={x:200,y:200,r:150};

// get points where 2 circles' perimeters intersect 
var pts=intersection(c1.x,c1.y,c1.r,c2.x,c2.y,c2.r);

// get the angle formed by one intersection point
// and the circle's centerpoint
// and the other intersection point
var centralAngle1=centralAngle({x:pts.x1,y:pts.y1},{x:c1.x,y:c1.y},{x:pts.x2,y:pts.y2});
var centralAngle2=centralAngle({x:pts.x1,y:pts.y1},{x:c2.x,y:c2.y},{x:pts.x2,y:pts.y2});

// calculate the area of the two circle sectors
var sector1area=parseInt(sectorArea(c1.r,centralAngle1));
var sector2area=parseInt(sectorArea(c2.r,centralAngle2));

draw();


function draw(){
  // stroke circles
  ctx.beginPath();
  ctx.arc(c1.x,c1.y,c1.r,0,Math.PI*2);
  ctx.closePath();
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(c2.x,c2.y,c2.r,0,Math.PI*2);
  ctx.closePath();
  ctx.stroke();
  // intersecting chord
  ctx.beginPath();
  ctx.moveTo(pts.x1,pts.y1);
  ctx.lineTo(pts.x2,pts.y2);
  ctx.stroke();
  // intersection points 
  ctx.fillStyle='red';
  ctx.beginPath();
  ctx.arc(pts.x1,pts.y1,5,0,Math.PI*2);
  ctx.closePath();
  ctx.fill();
  ctx.beginPath();
  ctx.arc(pts.x2,pts.y2,5,0,Math.PI*2);
  ctx.closePath();
  ctx.fill();
  ctx.fillStyle='black';
  // sector labels
  ctx.fillText('sector2',100,100);
  ctx.fillText('sector1',135,135);
  // report sector areas
  ctx.fillText(sector2area,100,112);
  ctx.fillText(sector1area+'px',135,147);
  ctx.fillText('px',100,124);
  //
  ctx.fillText('circle#1',c1.x-35,c1.y-35);
  ctx.fillText('circle#2',c2.x,c2.y);
}

// calc area of circle section
function sectorArea(radius,radianSweep){
  var PI=Math.PI;
  var PI2=PI*2;
  // area of wedge with specified sweep angle
  var wedgeArea=(PI*radius*radius)*radianSweep/PI2;
  // area of triangle formed by 2 radii & radianSweep 
  // == side angle side method
  var triangleArea=0.50*radius*radius*Math.sin(radianSweep);
  // subtract triangle area from wedge area
  // leaving the sector area
  return(wedgeArea-triangleArea);
}

// return angle between 3 points
function centralAngle(p0,p1,p2) {
  var dx1=p1.x-p0.x;
  var dy1=p1.y-p0.y;
  var dx2=p1.x-p2.x;
  var dy2=p1.y-p2.y;
  var dx3=p2.x-p0.x;
  var dy3=p2.y-p0.y;
  var a=dx1*dx1+dy1*dy1;
  var b=dx2*dx2+dy2*dy2;
  var c=dx3*dx3+dy3*dy3;
  return(Math.acos((a+b-c)/Math.sqrt(4*a*b)));
}


function centralAngleX(x1,y1,x2,y2){
  var dx=x2-x1;
  var dy=y2-y1;
  return(Math.abs(Math.atan2(dy,dx)));
}

function intersection(x0, y0, r0, x1, y1, r1) {
  var a, dx, dy, d, h, rx, ry;
  var x2, y2;
  /* dx and dy are the vertical and horizontal distances between
     * the circle centers.
     */
  dx = x1 - x0;
  dy = y1 - y0;
  /* Determine the straight-line distance between the centers. */
  d = Math.sqrt((dy*dy) + (dx*dx));
  /* Check for solvability. */
  if (d > (r0 + r1)) {
    /* no solution. circles do not intersect. */
    return false;
  }
  if (d < Math.abs(r0 - r1)) {
    /* no solution. one circle is contained in the other */
    return false;
  }
  /* 'point 2' is the point where the line through the circle
     * intersection points crosses the line between the circle
     * centers.  
     */
  /* Determine the distance from point 0 to point 2. */
  a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
  /* Determine the coordinates of point 2. */
  x2 = x0 + (dx * a/d);
  y2 = y0 + (dy * a/d);
  /* Determine the distance from point 2 to either of the
     * intersection points.
     */
  h = Math.sqrt((r0*r0) - (a*a));
  /* Now determine the offsets of the intersection points from
     * point 2.
     */
  rx = -dy * (h/d);
  ry = dx * (h/d);
  /* Determine the absolute intersection points. */
  var xi = x2 + rx;
  var xi_prime = x2 - rx;
  var yi = y2 + ry;
  var yi_prime = y2 - ry;
  return({x1:xi, y1:yi, x2:xi_prime, y2:yi_prime});
}
<canvas id="canvas" width=350 height=350></canvas>

Upvotes: 4

Related Questions