Wednesday, 22 April 2020

Intersections

It is common when writing JavaScript games to need to calculate if a line will intersect with a given rectangle, circle or another line. In the chapter on Ray Casting in my book on learning JavaScript programming the code required to test for an intersection between a "ray" and a game world wall or sprite proved straightforward as the problem was deliberately simplified by the overall structure. In other projects some more general functions might prove helpful.

Interactive demo web page here

Demo code available for download here

The demo page features three intersection functions. All three functions are based upon Internet research and I am grateful for all of the very useful suggestions I found for each.

1. Line intersection with a rectangle

function lineIntersectsRect(lStart, lEnd, rect) { let minX = lStart.x; let maxX = lEnd.x; if(lEnd.x < lStart.x) { maxX = lStart.x; minX = lEnd.x; } maxX = Math.min(maxX, rect.x + rect.width); minX = Math.max(minX, rect.x); if(minX > maxX) {return false;} let minY = lStart.y; let maxY = lEnd.y; let dx = lEnd.x - lStart.x; if(dx) { // there is a horizontal slope let a = (lEnd.y - lStart.y)/dx; let b = lStart.y - a * lStart.x; minY = a * minX + b; maxY = a * maxX + b; } if(minY > maxY) { [minY, maxY] = [maxY, minY]; // ES6 Destructuring Assignment swaps values } if(Math.max(minY, rect.y) > Math.min(maxY, rect.y + rect.height)){ return false; } return true; }























2. Line intersection with a circle

function lineIntersectsCircle(lStart, lEnd, center, radius) { let dist; const dx1 = lEnd.x - lStart.x; const dy1 = lEnd.y - lStart.y; const dx2 = center.x - lStart.x; const dy2 = center.y - lStart.y; // get the unit distance along the line of the closest point to circle center const u = (dx2 * dx1 + dy2 * dy1) / (dy1 * dy1 + dx1 * dx1); // if the point is on the line segment get the distance squared to circle center if(u >= 0 && u <= 1){ dist = Math.pow((lStart.x + dx1 * u - center.x), 2) + Math.pow((lStart.y + dy1 * u - center.y), 2); } else { // use the u distance to determine which end is closest and get dist squared to circle dist = u < 0 ? Math.pow((lStart.x - center.x), 2) + Math.pow((lStart.y - center.y), 2) : Math.pow((lEnd.x - center.x), 2) + Math.pow((lEnd.y - center.y), 2); } return dist < Math.pow(radius, 2); }

















3. line intersection with a line

function lineIntersectsLine(p0, p1, p2, p3) { let dx1 = p1.x - p0.x; let dy1 = p1.y - p0.y; let dx2 = p3.x - p2.x; let dy2 = p3.y - p2.y; let d = dx1 * dy2 - dx2 * dy1; if(!d) { return false;} let dIsPositive = d > 0; let xd1 = p0.x - p2.x; let yd1 = p0.y - p2.y; let s_numer = dx1 * yd1 - dy1 * xd1; if((s_numer < 0) === dIsPositive) { return false; // watch logic = both true or both false returns false } let t_numer = dx2 * yd1 - dy2 * xd1; if((t_numer < 0) === dIsPositive) { return false; } if((s_numer > d) === dIsPositive || (t_numer > d) === dIsPositive) { return false; } return true; }