intersect.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. --- Various geometric intersections
  2. -- @module intersect
  3. local modules = (...):gsub('%.[^%.]+$', '') .. "."
  4. local constants = require(modules .. "constants")
  5. local vec3 = require(modules .. "vec3")
  6. local utils = require(modules .. "utils")
  7. local DBL_EPSILON = constants.DBL_EPSILON
  8. local sqrt = math.sqrt
  9. local abs = math.abs
  10. local min = math.min
  11. local max = math.max
  12. local intersect = {}
  13. -- https://blogs.msdn.microsoft.com/rezanour/2011/08/07/barycentric-coordinates-and-point-in-triangle-tests/
  14. -- point is a vec3
  15. -- triangle[1] is a vec3
  16. -- triangle[2] is a vec3
  17. -- triangle[3] is a vec3
  18. function intersect.point_triangle(point, triangle)
  19. local u = triangle[2] - triangle[1]
  20. local v = triangle[3] - triangle[1]
  21. local w = point - triangle[1]
  22. local vw = v:cross(w)
  23. local vu = v:cross(u)
  24. if vw:dot(vu) < 0 then
  25. return false
  26. end
  27. local uw = u:cross(w)
  28. local uv = u:cross(v)
  29. if uw:dot(uv) < 0 then
  30. return false
  31. end
  32. local d = uv:len()
  33. local r = vw:len() / d
  34. local t = uw:len() / d
  35. return r + t <= 1
  36. end
  37. -- point is a vec3
  38. -- aabb.min is a vec3
  39. -- aabb.max is a vec3
  40. function intersect.point_aabb(point, aabb)
  41. return
  42. aabb.min.x <= point.x and
  43. aabb.max.x >= point.x and
  44. aabb.min.y <= point.y and
  45. aabb.max.y >= point.y and
  46. aabb.min.z <= point.z and
  47. aabb.max.z >= point.z
  48. end
  49. -- point is a vec3
  50. -- frustum.left is a plane { a, b, c, d }
  51. -- frustum.right is a plane { a, b, c, d }
  52. -- frustum.bottom is a plane { a, b, c, d }
  53. -- frustum.top is a plane { a, b, c, d }
  54. -- frustum.near is a plane { a, b, c, d }
  55. -- frustum.far is a plane { a, b, c, d }
  56. function intersect.point_frustum(point, frustum)
  57. local x, y, z = point:unpack()
  58. local planes = {
  59. frustum.left,
  60. frustum.right,
  61. frustum.bottom,
  62. frustum.top,
  63. frustum.near,
  64. frustum.far or false
  65. }
  66. -- Skip the last test for infinite projections, it'll never fail.
  67. if not planes[6] then
  68. table.remove(planes)
  69. end
  70. local dot
  71. for i = 1, #planes do
  72. dot = planes[i].a * x + planes[i].b * y + planes[i].c * z + planes[i].d
  73. if dot <= 0 then
  74. return false
  75. end
  76. end
  77. return true
  78. end
  79. -- http://www.lighthouse3d.com/tutorials/maths/ray-triangle-intersection/
  80. -- ray.position is a vec3
  81. -- ray.direction is a vec3
  82. -- triangle[1] is a vec3
  83. -- triangle[2] is a vec3
  84. -- triangle[3] is a vec3
  85. function intersect.ray_triangle(ray, triangle)
  86. local e1 = triangle[2] - triangle[1]
  87. local e2 = triangle[3] - triangle[1]
  88. local h = ray.direction:cross(e2)
  89. local a = h:dot(e1)
  90. -- if a is too close to 0, ray does not intersect triangle
  91. if abs(a) <= DBL_EPSILON then
  92. return false
  93. end
  94. local f = 1 / a
  95. local s = ray.position - triangle[1]
  96. local u = s:dot(h) * f
  97. -- ray does not intersect triangle
  98. if u < 0 or u > 1 then
  99. return false
  100. end
  101. local q = s:cross(e1)
  102. local v = ray.direction:dot(q) * f
  103. -- ray does not intersect triangle
  104. if v < 0 or u + v > 1 then
  105. return false
  106. end
  107. -- at this stage we can compute t to find out where
  108. -- the intersection point is on the line
  109. local t = q:dot(e2) * f
  110. -- return position of intersection and distance from ray origin
  111. if t >= DBL_EPSILON then
  112. return ray.position + ray.direction * t, t
  113. end
  114. -- ray does not intersect triangle
  115. return false
  116. end
  117. -- https://gamedev.stackexchange.com/questions/96459/fast-ray-sphere-collision-code
  118. -- ray.position is a vec3
  119. -- ray.direction is a vec3
  120. -- sphere.position is a vec3
  121. -- sphere.radius is a number
  122. function intersect.ray_sphere(ray, sphere)
  123. local offset = ray.position - sphere.position
  124. local b = offset:dot(ray.direction)
  125. local c = offset:dot(offset) - sphere.radius * sphere.radius
  126. -- ray's position outside sphere (c > 0)
  127. -- ray's direction pointing away from sphere (b > 0)
  128. if c > 0 and b > 0 then
  129. return false
  130. end
  131. local discr = b * b - c
  132. -- negative discriminant
  133. if discr < 0 then
  134. return false
  135. end
  136. -- Clamp t to 0
  137. local t = -b - sqrt(discr)
  138. t = t < 0 and 0 or t
  139. -- Return collision point and distance from ray origin
  140. return ray.position + ray.direction * t, t
  141. end
  142. -- http://gamedev.stackexchange.com/a/18459
  143. -- ray.position is a vec3
  144. -- ray.direction is a vec3
  145. -- aabb.min is a vec3
  146. -- aabb.max is a vec3
  147. function intersect.ray_aabb(ray, aabb)
  148. local dir = ray.direction:normalize()
  149. local dirfrac = vec3(
  150. 1 / dir.x,
  151. 1 / dir.y,
  152. 1 / dir.z
  153. )
  154. local t1 = (aabb.min.x - ray.position.x) * dirfrac.x
  155. local t2 = (aabb.max.x - ray.position.x) * dirfrac.x
  156. local t3 = (aabb.min.y - ray.position.y) * dirfrac.y
  157. local t4 = (aabb.max.y - ray.position.y) * dirfrac.y
  158. local t5 = (aabb.min.z - ray.position.z) * dirfrac.z
  159. local t6 = (aabb.max.z - ray.position.z) * dirfrac.z
  160. local tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6))
  161. local tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6))
  162. -- ray is intersecting AABB, but whole AABB is behind us
  163. if tmax < 0 then
  164. return false
  165. end
  166. -- ray does not intersect AABB
  167. if tmin > tmax then
  168. return false
  169. end
  170. -- Return collision point and distance from ray origin
  171. return ray.position + ray.direction * tmin, tmin
  172. end
  173. -- http://stackoverflow.com/a/23976134/1190664
  174. -- ray.position is a vec3
  175. -- ray.direction is a vec3
  176. -- plane.position is a vec3
  177. -- plane.normal is a vec3
  178. function intersect.ray_plane(ray, plane)
  179. local denom = plane.normal:dot(ray.direction)
  180. -- ray does not intersect plane
  181. if abs(denom) < DBL_EPSILON then
  182. return false
  183. end
  184. -- distance of direction
  185. local d = plane.position - ray.position
  186. local t = d:dot(plane.normal) / denom
  187. if t < DBL_EPSILON then
  188. return false
  189. end
  190. -- Return collision point and distance from ray origin
  191. return ray.position + ray.direction * t, t
  192. end
  193. -- https://web.archive.org/web/20120414063459/http://local.wasp.uwa.edu.au/~pbourke//geometry/lineline3d/
  194. -- a[1] is a vec3
  195. -- a[2] is a vec3
  196. -- b[1] is a vec3
  197. -- b[2] is a vec3
  198. -- e is a number
  199. function intersect.line_line(a, b, e)
  200. -- new points
  201. local p13 = a[1] - b[1]
  202. local p43 = b[2] - b[1]
  203. local p21 = a[2] - a[1]
  204. -- if lengths are negative or too close to 0, lines do not intersect
  205. if p43:len2() < DBL_EPSILON or p21:len2() < DBL_EPSILON then
  206. return false
  207. end
  208. -- dot products
  209. local d1343 = p13:dot(p43)
  210. local d4321 = p43:dot(p21)
  211. local d1321 = p13:dot(p21)
  212. local d4343 = p43:dot(p43)
  213. local d2121 = p21:dot(p21)
  214. local denom = d2121 * d4343 - d4321 * d4321
  215. -- if denom is too close to 0, lines do not intersect
  216. if abs(denom) < DBL_EPSILON then
  217. return false
  218. end
  219. local numer = d1343 * d4321 - d1321 * d4343
  220. local mua = numer / denom
  221. local mub = (d1343 + d4321 * mua) / d4343
  222. -- return positions of intersection on each line
  223. local out1 = a[1] + p21 * mua
  224. local out2 = b[1] + p43 * mub
  225. local dist = out1:dist(out2)
  226. -- if distance of the shortest segment between lines is less than threshold
  227. if e and dist > e then
  228. return false
  229. end
  230. return { out1, out2 }, dist
  231. end
  232. -- a[1] is a vec3
  233. -- a[2] is a vec3
  234. -- b[1] is a vec3
  235. -- b[2] is a vec3
  236. -- e is a number
  237. function intersect.segment_segment(a, b, e)
  238. local c, d = intersect.line_line(a, b, e)
  239. if c and ((
  240. a[1].x <= c[1].x and
  241. a[1].y <= c[1].y and
  242. a[1].z <= c[1].z and
  243. c[1].x <= a[2].x and
  244. c[1].y <= a[2].y and
  245. c[1].z <= a[2].z
  246. ) or (
  247. a[1].x >= c[1].x and
  248. a[1].y >= c[1].y and
  249. a[1].z >= c[1].z and
  250. c[1].x >= a[2].x and
  251. c[1].y >= a[2].y and
  252. c[1].z >= a[2].z
  253. )) and ((
  254. b[1].x <= c[2].x and
  255. b[1].y <= c[2].y and
  256. b[1].z <= c[2].z and
  257. c[2].x <= b[2].x and
  258. c[2].y <= b[2].y and
  259. c[2].z <= b[2].z
  260. ) or (
  261. b[1].x >= c[2].x and
  262. b[1].y >= c[2].y and
  263. b[1].z >= c[2].z and
  264. c[2].x >= b[2].x and
  265. c[2].y >= b[2].y and
  266. c[2].z >= b[2].z
  267. )) then
  268. return c, d
  269. end
  270. -- segments do not intersect
  271. return false
  272. end
  273. -- a.min is a vec3
  274. -- a.max is a vec3
  275. -- b.min is a vec3
  276. -- b.max is a vec3
  277. function intersect.aabb_aabb(a, b)
  278. return
  279. a.min.x <= b.max.x and
  280. a.max.x >= b.min.x and
  281. a.min.y <= b.max.y and
  282. a.max.y >= b.min.y and
  283. a.min.z <= b.max.z and
  284. a.max.z >= b.min.z
  285. end
  286. -- aabb.position is a vec3
  287. -- aabb.extent is a vec3 (half-size)
  288. -- obb.position is a vec3
  289. -- obb.extent is a vec3 (half-size)
  290. -- obb.rotation is a mat4
  291. function intersect.aabb_obb(aabb, obb)
  292. local a = aabb.extent
  293. local b = obb.extent
  294. local T = obb.position - aabb.position
  295. local rot = obb.rotation:transpose()
  296. local B = {}
  297. local t
  298. for i = 1, 3 do
  299. B[i] = {}
  300. for j = 1, 3 do
  301. assert((i - 1) * 4 + j < 16 and (i - 1) * 4 + j > 0)
  302. B[i][j] = abs(rot[(i - 1) * 4 + j]) + 1e-6
  303. end
  304. end
  305. t = abs(T.x)
  306. if not (t <= (b.x + a.x * B[1][1] + b.y * B[1][2] + b.z * B[1][3])) then return false end
  307. t = abs(T.x * B[1][1] + T.y * B[2][1] + T.z * B[3][1])
  308. if not (t <= (b.x + a.x * B[1][1] + a.y * B[2][1] + a.z * B[3][1])) then return false end
  309. t = abs(T.y)
  310. if not (t <= (a.y + b.x * B[2][1] + b.y * B[2][2] + b.z * B[2][3])) then return false end
  311. t = abs(T.z)
  312. if not (t <= (a.z + b.x * B[3][1] + b.y * B[3][2] + b.z * B[3][3])) then return false end
  313. t = abs(T.x * B[1][2] + T.y * B[2][2] + T.z * B[3][2])
  314. if not (t <= (b.y + a.x * B[1][2] + a.y * B[2][2] + a.z * B[3][2])) then return false end
  315. t = abs(T.x * B[1][3] + T.y * B[2][3] + T.z * B[3][3])
  316. if not (t <= (b.z + a.x * B[1][3] + a.y * B[2][3] + a.z * B[3][3])) then return false end
  317. t = abs(T.z * B[2][1] - T.y * B[3][1])
  318. if not (t <= (a.y * B[3][1] + a.z * B[2][1] + b.y * B[1][3] + b.z * B[1][2])) then return false end
  319. t = abs(T.z * B[2][2] - T.y * B[3][2])
  320. if not (t <= (a.y * B[3][2] + a.z * B[2][2] + b.x * B[1][3] + b.z * B[1][1])) then return false end
  321. t = abs(T.z * B[2][3] - T.y * B[3][3])
  322. if not (t <= (a.y * B[3][3] + a.z * B[2][3] + b.x * B[1][2] + b.y * B[1][1])) then return false end
  323. t = abs(T.x * B[3][1] - T.z * B[1][1])
  324. if not (t <= (a.x * B[3][1] + a.z * B[1][1] + b.y * B[2][3] + b.z * B[2][2])) then return false end
  325. t = abs(T.x * B[3][2] - T.z * B[1][2])
  326. if not (t <= (a.x * B[3][2] + a.z * B[1][2] + b.x * B[2][3] + b.z * B[2][1])) then return false end
  327. t = abs(T.x * B[3][3] - T.z * B[1][3])
  328. if not (t <= (a.x * B[3][3] + a.z * B[1][3] + b.x * B[2][2] + b.y * B[2][1])) then return false end
  329. t = abs(T.y * B[1][1] - T.x * B[2][1])
  330. if not (t <= (a.x * B[2][1] + a.y * B[1][1] + b.y * B[3][3] + b.z * B[3][2])) then return false end
  331. t = abs(T.y * B[1][2] - T.x * B[2][2])
  332. if not (t <= (a.x * B[2][2] + a.y * B[1][2] + b.x * B[3][3] + b.z * B[3][1])) then return false end
  333. t = abs(T.y * B[1][3] - T.x * B[2][3])
  334. if not (t <= (a.x * B[2][3] + a.y * B[1][3] + b.x * B[3][2] + b.y * B[3][1])) then return false end
  335. -- https://gamedev.stackexchange.com/questions/24078/which-side-was-hit
  336. -- Minkowski Sum
  337. local wy = (aabb.extent * 2 + obb.extent * 2) * (aabb.position.y - obb.position.y)
  338. local hx = (aabb.extent * 2 + obb.extent * 2) * (aabb.position.x - obb.position.x)
  339. if wy.x > hx.x and wy.y > hx.y and wy.z > hx.z then
  340. if wy.x > -hx.x and wy.y > -hx.y and wy.z > -hx.z then
  341. return vec3(obb.rotation * { 0, -1, 0, 1 })
  342. else
  343. return vec3(obb.rotation * { -1, 0, 0, 1 })
  344. end
  345. else
  346. if wy.x > -hx.x and wy.y > -hx.y and wy.z > -hx.z then
  347. return vec3(obb.rotation * { 1, 0, 0, 1 })
  348. else
  349. return vec3(obb.rotation * { 0, 1, 0, 1 })
  350. end
  351. end
  352. end
  353. -- http://stackoverflow.com/a/4579069/1190664
  354. -- aabb.min is a vec3
  355. -- aabb.max is a vec3
  356. -- sphere.position is a vec3
  357. -- sphere.radius is a number
  358. local axes = { "x", "y", "z" }
  359. function intersect.aabb_sphere(aabb, sphere)
  360. local dist2 = sphere.radius ^ 2
  361. for _, axis in ipairs(axes) do
  362. local pos = sphere.position[axis]
  363. local amin = aabb.min[axis]
  364. local amax = aabb.max[axis]
  365. if pos < amin then
  366. dist2 = dist2 - (pos - amin) ^ 2
  367. elseif pos > amax then
  368. dist2 = dist2 - (pos - amax) ^ 2
  369. end
  370. end
  371. return dist2 > 0
  372. end
  373. -- aabb.min is a vec3
  374. -- aabb.max is a vec3
  375. -- frustum.left is a plane { a, b, c, d }
  376. -- frustum.right is a plane { a, b, c, d }
  377. -- frustum.bottom is a plane { a, b, c, d }
  378. -- frustum.top is a plane { a, b, c, d }
  379. -- frustum.near is a plane { a, b, c, d }
  380. -- frustum.far is a plane { a, b, c, d }
  381. function intersect.aabb_frustum(aabb, frustum)
  382. -- Indexed for the 'index trick' later
  383. local box = {
  384. aabb.min,
  385. aabb.max
  386. }
  387. -- We have 6 planes defining the frustum, 5 if infinite.
  388. local planes = {
  389. frustum.left,
  390. frustum.right,
  391. frustum.bottom,
  392. frustum.top,
  393. frustum.near,
  394. frustum.far or false
  395. }
  396. -- Skip the last test for infinite projections, it'll never fail.
  397. if not planes[6] then
  398. table.remove(planes)
  399. end
  400. for i = 1, #planes do
  401. -- This is the current plane
  402. local p = planes[i]
  403. -- p-vertex selection (with the index trick)
  404. -- According to the plane normal we can know the
  405. -- indices of the positive vertex
  406. local px = p.a > 0.0 and 2 or 1
  407. local py = p.b > 0.0 and 2 or 1
  408. local pz = p.c > 0.0 and 2 or 1
  409. -- project p-vertex on plane normal
  410. -- (How far is p-vertex from the origin)
  411. local dot = (p.a * box[px].x) + (p.b * box[py].y) + (p.c * box[pz].z)
  412. -- Doesn't intersect if it is behind the plane
  413. if dot < -p.d then
  414. return false
  415. end
  416. end
  417. return true
  418. end
  419. -- outer.min is a vec3
  420. -- outer.max is a vec3
  421. -- inner.min is a vec3
  422. -- inner.max is a vec3
  423. function intersect.encapsulate_aabb(outer, inner)
  424. return
  425. outer.min.x <= inner.min.x and
  426. outer.max.x >= inner.max.x and
  427. outer.min.y <= inner.min.y and
  428. outer.max.y >= inner.max.y and
  429. outer.min.z <= inner.min.z and
  430. outer.max.z >= inner.max.z
  431. end
  432. -- a.position is a vec3
  433. -- a.radius is a number
  434. -- b.position is a vec3
  435. -- b.radius is a number
  436. function intersect.circle_circle(a, b)
  437. return a.position:dist(b.position) <= a.radius + b.radius
  438. end
  439. -- a.position is a vec3
  440. -- a.radius is a number
  441. -- b.position is a vec3
  442. -- b.radius is a number
  443. function intersect.sphere_sphere(a, b)
  444. return intersect.circle_circle(a, b)
  445. end
  446. -- http://realtimecollisiondetection.net/blog/?p=103
  447. -- sphere.position is a vec3
  448. -- sphere.radius is a number
  449. -- triangle[1] is a vec3
  450. -- triangle[2] is a vec3
  451. -- triangle[3] is a vec3
  452. function intersect.sphere_triangle(sphere, triangle)
  453. -- Sphere is centered at origin
  454. local A = triangle[1] - sphere.position
  455. local B = triangle[2] - sphere.position
  456. local C = triangle[3] - sphere.position
  457. -- Compute normal of triangle plane
  458. local V = (B - A):cross(C - A)
  459. -- Test if sphere lies outside triangle plane
  460. local rr = sphere.radius * sphere.radius
  461. local d = A:dot(V)
  462. local e = V:dot(V)
  463. local s1 = d * d > rr * e
  464. -- Test if sphere lies outside triangle vertices
  465. local aa = A:dot(A)
  466. local ab = A:dot(B)
  467. local ac = A:dot(C)
  468. local bb = B:dot(B)
  469. local bc = B:dot(C)
  470. local cc = C:dot(C)
  471. local s2 = (aa > rr) and (ab > aa) and (ac > aa)
  472. local s3 = (bb > rr) and (ab > bb) and (bc > bb)
  473. local s4 = (cc > rr) and (ac > cc) and (bc > cc)
  474. -- Test is sphere lies outside triangle edges
  475. local AB = B - A
  476. local BC = C - B
  477. local CA = A - C
  478. local d1 = ab - aa
  479. local d2 = bc - bb
  480. local d3 = ac - cc
  481. local e1 = AB:dot(AB)
  482. local e2 = BC:dot(BC)
  483. local e3 = CA:dot(CA)
  484. local Q1 = A * e1 - d1 * AB
  485. local Q2 = B * e2 - d2 * BC
  486. local Q3 = C * e3 - d3 * CA
  487. local QC = C * e1 - Q1
  488. local QA = A * e2 - Q2
  489. local QB = B * e3 - Q3
  490. local s5 = (Q1:dot(Q1) > rr * e1 * e1) and (Q1:dot(QC) > 0)
  491. local s6 = (Q2:dot(Q2) > rr * e2 * e2) and (Q2:dot(QA) > 0)
  492. local s7 = (Q3:dot(Q3) > rr * e3 * e3) and (Q3:dot(QB) > 0)
  493. -- Return whether or not any of the tests passed
  494. return s1 or s2 or s3 or s4 or s5 or s6 or s7
  495. end
  496. -- sphere.position is a vec3
  497. -- sphere.radius is a number
  498. -- frustum.left is a plane { a, b, c, d }
  499. -- frustum.right is a plane { a, b, c, d }
  500. -- frustum.bottom is a plane { a, b, c, d }
  501. -- frustum.top is a plane { a, b, c, d }
  502. -- frustum.near is a plane { a, b, c, d }
  503. -- frustum.far is a plane { a, b, c, d }
  504. function intersect.sphere_frustum(sphere, frustum)
  505. local x, y, z = sphere.position:unpack()
  506. local planes = {
  507. frustum.left,
  508. frustum.right,
  509. frustum.bottom,
  510. frustum.top,
  511. frustum.near
  512. }
  513. if frustum.far then
  514. table.insert(planes, frustum.far, 5)
  515. end
  516. local dot
  517. for i = 1, #planes do
  518. dot = planes[i].a * x + planes[i].b * y + planes[i].c * z + planes[i].d
  519. if dot <= -sphere.radius then
  520. return false
  521. end
  522. end
  523. -- dot + radius is the distance of the object from the near plane.
  524. -- make sure that the near plane is the last test!
  525. return dot + sphere.radius
  526. end
  527. function intersect.capsule_capsule(c1, c2)
  528. local dist2, p1, p2 = intersect.closest_point_segment_segment(c1.a, c1.b, c2.a, c2.b)
  529. local radius = c1.radius + c2.radius
  530. if dist2 <= radius * radius then
  531. return p1, p2
  532. end
  533. return false
  534. end
  535. function intersect.closest_point_segment_segment(p1, p2, p3, p4)
  536. local s -- Distance of intersection along segment 1
  537. local t -- Distance of intersection along segment 2
  538. local c1 -- Collision point on segment 1
  539. local c2 -- Collision point on segment 2
  540. local d1 = p2 - p1 -- Direction of segment 1
  541. local d2 = p4 - p3 -- Direction of segment 2
  542. local r = p1 - p3
  543. local a = d1:dot(d1)
  544. local e = d2:dot(d2)
  545. local f = d2:dot(r)
  546. -- Check if both segments degenerate into points
  547. if a <= DBL_EPSILON and e <= DBL_EPSILON then
  548. s = 0
  549. t = 0
  550. c1 = p1
  551. c2 = p3
  552. return (c1 - c2):dot(c1 - c2), s, t, c1, c2
  553. end
  554. -- Check if segment 1 degenerates into a point
  555. if a <= DBL_EPSILON then
  556. s = 0
  557. t = utils.clamp(f / e, 0, 1)
  558. else
  559. local c = d1:dot(r)
  560. -- Check is segment 2 degenerates into a point
  561. if e <= DBL_EPSILON then
  562. t = 0
  563. s = utils.clamp(-c / a, 0, 1)
  564. else
  565. local b = d1:dot(d2)
  566. local denom = a * e - b * b
  567. if abs(denom) > 0 then
  568. s = utils.clamp((b * f - c * e) / denom, 0, 1)
  569. else
  570. s = 0
  571. end
  572. t = (b * s + f) / e
  573. if t < 0 then
  574. t = 0
  575. s = utils.clamp(-c / a, 0, 1)
  576. elseif t > 1 then
  577. t = 1
  578. s = utils.clamp((b - c) / a, 0, 1)
  579. end
  580. end
  581. end
  582. c1 = p1 + d1 * s
  583. c2 = p3 + d2 * t
  584. return (c1 - c2):dot(c1 - c2), c1, c2, s, t
  585. end
  586. return intersect