segseg
Version:
2d segment to segment intersection detection
121 lines (94 loc) • 3 kB
JavaScript
import segpoint from './segpoint.js'
const DONT_INTERSECT = 0
const DO_INTERSECT = 1
const COLINEAR = 2
/* Ported from Mukesh Prasad's public domain code:
* http://www.realtimerendering.com/resources/GraphicsGems/gemsii/xlines.c
*
* This function computes whether two line segments,
* respectively joining the input points (x1,y1) -- (x2,y2)
* and the input points (x3,y3) -- (x4,y4) intersect.
* If the lines intersect, the return value is an array
* containing coordinates of the point of intersection.
*
* Params
* p1, p2 Coordinates of endpoints of one segment.
* p3, p4 Coordinates of endpoints of other segment.
*
* The value returned by the function is an enumeration of DONT_INTERSECT | DO_INTERSECT | COLINEAR
*/
function _segseg (out, p1, p2, p3, p4) {
let x1 = p1[0]
let y1 = p1[1]
let x2 = p2[0]
let y2 = p2[1]
let x3 = p3[0]
let y3 = p3[1]
let x4 = p4[0]
let y4 = p4[1]
let a1, a2, b1, b2, c1, c2 // Coefficients of line eqns.
let r1, r2, r3, r4 // 'Sign' values
let denom, offset // Intermediate values
let x, y // Intermediate return values
// Compute a1, b1, c1, where line joining points 1 and 2
// is "a1 x + b1 y + c1 = 0".
a1 = y2 - y1
b1 = x1 - x2
c1 = x2 * y1 - x1 * y2
// Compute r3 and r4.
r3 = a1 * x3 + b1 * y3 + c1
r4 = a1 * x4 + b1 * y4 + c1
// Check signs of r3 and r4. If both point 3 and point 4 lie on
// same side of line 1, the line segments do not intersect.
if ( r3 !== 0 && r4 !== 0 && ((r3 >= 0 && r4 >= 0) || (r3 < 0 && r4 < 0)))
return DONT_INTERSECT
// Compute a2, b2, c2
a2 = y4 - y3
b2 = x3 - x4
c2 = x4 * y3 - x3 * y4
// Compute r1 and r2
r1 = a2 * x1 + b2 * y1 + c2
r2 = a2 * x2 + b2 * y2 + c2
// Check signs of r1 and r2. If both point 1 and point 2 lie
// on same side of second line segment, the line segments do
// not intersect.
if (r1 !== 0 && r2 !== 0 && ((r1 >= 0 && r2 >= 0) || (r1 < 0 && r2 < 0)))
return DONT_INTERSECT
// Line segments intersect: compute intersection point.
denom = a1 * b2 - a2 * b1
if (denom === 0)
return COLINEAR
offset = denom < 0 ? - denom / 2 : denom / 2
x = b1 * c2 - b2 * c1
y = a2 * c1 - a1 * c2
out[0] = ( x < 0 ? x : x ) / denom
out[1] = ( y < 0 ? y : y ) / denom
return DO_INTERSECT
}
export default function segseg (out, p1, p2, p3, p4) {
const result = _segseg(out, p1, p2, p3, p4)
if (result === DO_INTERSECT)
return result
// handle colinear cases and when a line segment endpoint lies on the other segment
if (segpoint(p1, p3, p4)) {
out[0] = p1[0]
out[1] = p1[1]
return true
}
if (segpoint(p2, p3, p4)) {
out[0] = p2[0]
out[1] = p2[1]
return true
}
if (segpoint(p3, p1, p2)) {
out[0] = p3[0]
out[1] = p3[1]
return true
}
if (segpoint(p4, p1, p2)) {
out[0] = p4[0]
out[1] = p4[1]
return true
}
return false
}