Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate overlap between two ellipses

I have 2 ellipses and I need to detect any overlap between them.

Here is an example of detecting overlap between two circles, and I am looking for something similar for ellipses:

var circle1 = {radius: 20, x: 5, y: 5};
var circle2 = {radius: 12, x: 10, y: 5};

var dx = circle1.x - circle2.x;
var dy = circle1.y - circle2.y;
var distance = Math.sqrt(dx * dx + dy * dy);

if (distance < circle1.radius + circle2.radius) {
    // collision !
}

For ellipses, I have the same variable because my radius on the vertical axis is 2 times smaller as the radius on the horizontal axis:

var oval1 = {radius: 20, x: 5, y: 5};
var oval2 = {radius: 12, x: 10, y: 5};

// what comes here?

if ( /* condition ? */ ) {
    // collision !
}

Image: collision between two oval

var result = document.getElementById("result");
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");


// First eclipse
var eclipse1 = { radius: 20,
                 x: 100,
                 y: 40 };


// Second eclipse
var eclipse2 = { radius: 20,
                 x: 120,
                 y: 65 };



function have_collision( element1, element2 )
{
  var dx = element1.x - element2.x;
  var dy = element1.y - element2.y;
  var distance = Math.sqrt(dx * dx + dy * dy);

  if (distance <= element1.radius + element2.radius) {
    return true;
  }
  else {
    return false;
   }
}

function draw( element ) {
// http://scienceprimer.com/draw-oval-html5-canvas
context.beginPath();
for (var i = 0 * Math.PI; i < 2 * Math.PI; i += 0.01 ) {
  xPos = element.x - (element.radius/2 * Math.sin(i)) * Math.sin(0 * Math.PI) + (element.radius * Math.cos(i)) * Math.cos(0 * Math.PI);
  yPos = element.y + (element.radius * Math.cos(i)) * Math.sin(0 * Math.PI) + (element.radius/2 * Math.sin(i)) * Math.cos(0 * Math.PI);

  if (i == 0) {
    context.moveTo(xPos, yPos);
  } else {
    context.lineTo(xPos, yPos);
  }
}
context.fillStyle = "#C4C4C4";
context.fill();
context.lineWidth = 2;
context.strokeStyle = "#FF0000";
context.stroke();
context.closePath();
}

function getMousePos(canvas, evt) {
  var rect = canvas.getBoundingClientRect();
  return {
    x: evt.clientX - rect.left,
    y: evt.clientY - rect.top
  };
}

canvas.addEventListener('mousemove', function(e) {
  var mousePos = getMousePos(canvas, e);
  eclipse2.x = mousePos.x;
  eclipse2.y = mousePos.y;
  result.innerHTML = 'Collision ? ' + have_collision( eclipse1, eclipse2 );
  context.clearRect(0, 0, canvas.width, canvas.height);
  draw( eclipse1 );
  draw( eclipse2 );
}, false);


draw( eclipse1 );
draw( eclipse2 );
result.innerHTML = 'Collision ? ' + have_collision( eclipse1, eclipse2 );
#canvas {
  border: solid 1px rgba(0,0,0,0.5);
}
<canvas id="canvas"></canvas>
<p id="result"></p>
<code>distance = Math.sqrt(dx * dx + dy * dy);</code>
like image 327
Rod Avatar asked Nov 21 '15 22:11

Rod


1 Answers

As your ellipses are very specific, in that they are just circles shrunk along the Y axis, you can just imagine what would happen if you would stretch the plane along the Y-axis with a factor 2. You would agree that those overlapping ellipses would become overlapping circles, and those that did not overlap will also not overlap once stretched. You could imagine it as if the ellipses were drawn on a elastic material, and you just stretch the material in vertical direction: this will of course not change any overlapping condition.

So, you could write this:

var stretchedDistance = Math.sqrt(dx * dx + 2 * dy * 2 * dy);

... and continue with the condition as it stands, because it is based on the radius in the X-direction, which, after stretching, also is the radius in the Y-direction. Of course, I named the variable differently, so you should test with that variable. So to complete the code, we get:

var stretchedDistance = Math.sqrt(dx * dx + 4 * dy * dy);
if (stretchedDistance < circle1.radius + circle2.radius) {
    // collision !
}

Note that the stretching is taken into account by multiplying dy with 2. In the distance formula it is equivalent and shorter to write 4 * dy * dy.

Here is the nice interactive fiddle you created, with my update to it.

like image 121
trincot Avatar answered Oct 21 '22 02:10

trincot