LINE/CIRCLE
To check if a circle is hitting a line, we use code from previous examples — a practice that we'll use through the rest of the book. The resulting math behind this gets a little hairy, but we'll simplify the harder parts.
First, let's test if either of the ends of the line are inside the circle. This is likely to happen if the line is much smaller than the circle. To do this, we can use Point/Circle from the beginning of the book. If either end is inside, return true
immediately and skip the rest.
var inside1 = ;var inside2 = ;if inside1 || inside2 return true;
Next, we need to get closest point on the line. To start, let's get the length of the line using the Pythagorean Theorem:
var distX = x1 - x2;var distY = y1 - y2;var len = ;
Then, we get a value we're calling dot
. If you've done vector math before, this is the same as doing the dot product of two vectors. If this isn't familiar, no worry! Consider this step a lot of math you can be glad not to have to solve by hand:
var dot = cx-x1*x2-x1 + cy-y1*y2-y1 / ;
Finally, we can use this equation to find the closest point on the line:
var closestX = x1 + r * x2-x1;var closestY = y1 + r * y2-y1;
However, this returns a point anywhere on the line as it extends to infinity in both directions. In other words, it could give us a point off the end of the line! So let's check if that closest point is actually on the line using the Line/Point algorithm we just made. This is the first of many times we'll nest previous functions when working on more complex collisions.
If the point is on the line, we can keep going. If not, we can immediately return false
, since that means the closest point is off one of the ends:
var onSegment = ;if !onSegment return false;
Finally, we get the distance from the circle to the closest point on the line, once again using the Pythagorean Theorem:
distX = closestX - cx;distY = closestY - cy;var distance = ;
If that distance is less than the radius, we have a collision (same as Point/Circle).
if distance <= r return true;return false;
Here's a full example putting everything together. Notice that we have three functions at the bottom: the one we just built and two previous functions.
var cx = 0; // circle position (set by mouse)var cy = 0;var r = 30; // circle radius var x1 = 100; // coordinates of linevar y1 = 300;var x2 = 500;var y2 = 100; { var canvas = ; ; // make it a little easier to see} { ; // update circle to mouse position cx = mouseX; cy = mouseY; // check for collision // if hit, change line's stroke color var hit = ; if hit ; else ; ; // draw the circle ; ; ;} // LINE/CIRCLE { // is either end INSIDE the circle? // if so, return true immediately var inside1 = ; var inside2 = ; if inside1 || inside2 return true; // get length of the line var distX = x1 - x2; var distY = y1 - y2; var len = ; // get dot product of the line and circle var dot = cx-x1*x2-x1 + cy-y1*y2-y1 / ; // find the closest point on the line var closestX = x1 + dot * x2-x1; var closestY = y1 + dot * y2-y1; // is this point actually on the line segment? // if so keep going, but if not, return false var onSegment = ; if !onSegment return false; // optionally, draw a circle at the closest // point on the line ; ; ; // get distance to closest point distX = closestX - cx; distY = closestY - cy; var distance = ; if distance <= r return true; return false;} // POINT/CIRCLE { // get distance between the point and circle's center // using the Pythagorean Theorem var distX = px - cx; var distY = py - cy; var distance = ; // if the distance is less than the circle's // radius the point is inside! if distance <= r return true; return false;} // LINE/POINT { // get distance from the point to the two ends of the line var d1 = ; var d2 = ; // get the length of the line var lineLen = ; // since floats are so minutely accurate, add // a little buffer zone that will give collision var buffer = 01; // higher # = less accurate // if the two distances are equal to the line's // length, the point is on the line! // note we use the buffer here to give a range, // rather than one # if d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer return true; return false;}
Math using lines can benefit from some of the built-in functionality of the PVector
class. If you haven't used PVectors before, it may be worth some time to get familiar with them. The Processing website has a good tutorial. Daniel Shiffman's excellent "Nature of Code" book deals with vectors quite a bit and is a very friendly introduction. We'll cover PVectors a little bit when we start working with polygons, if you want a very short introduction.
This example was based on code by Philip Nicoletti (thanks!). This CodeGuru post inclues a lot more discussion of how this algorithm works and the math behind it, if you're so inclined.