* Separating Axis Theorem test for two triangles. * Tests 13 axes: 2 face normals + 9 edge cross products. * Also handles coplanar case via 2D edge-normal axes.
(pos, tA, tB)
| 197 | } |
| 198 | |
| 199 | /** |
| 200 | * Separating Axis Theorem test for two triangles. |
| 201 | * Tests 13 axes: 2 face normals + 9 edge cross products. |
| 202 | * Also handles coplanar case via 2D edge-normal axes. |
| 203 | */ |
| 204 | function trianglesIntersectSAT(pos, tA, tB) { |
| 205 | const bA = tA * 9, bB = tB * 9; |
| 206 | |
| 207 | // Triangle A vertices |
| 208 | const a0x = pos[bA], a0y = pos[bA+1], a0z = pos[bA+2]; |
| 209 | const a1x = pos[bA+3], a1y = pos[bA+4], a1z = pos[bA+5]; |
| 210 | const a2x = pos[bA+6], a2y = pos[bA+7], a2z = pos[bA+8]; |
| 211 | |
| 212 | // Triangle B vertices |
| 213 | const b0x = pos[bB], b0y = pos[bB+1], b0z = pos[bB+2]; |
| 214 | const b1x = pos[bB+3], b1y = pos[bB+4], b1z = pos[bB+5]; |
| 215 | const b2x = pos[bB+6], b2y = pos[bB+7], b2z = pos[bB+8]; |
| 216 | |
| 217 | // Edge vectors for A |
| 218 | const eA0x = a1x-a0x, eA0y = a1y-a0y, eA0z = a1z-a0z; |
| 219 | const eA1x = a2x-a1x, eA1y = a2y-a1y, eA1z = a2z-a1z; |
| 220 | const eA2x = a0x-a2x, eA2y = a0y-a2y, eA2z = a0z-a2z; |
| 221 | |
| 222 | // Edge vectors for B |
| 223 | const eB0x = b1x-b0x, eB0y = b1y-b0y, eB0z = b1z-b0z; |
| 224 | const eB1x = b2x-b1x, eB1y = b2y-b1y, eB1z = b2z-b1z; |
| 225 | const eB2x = b0x-b2x, eB2y = b0y-b2y, eB2z = b0z-b2z; |
| 226 | |
| 227 | // Face normals |
| 228 | const nAx = eA0y*eA2z - eA0z*eA2y; // eA0 x (-eA2) = eA0 x (a0-a2) |
| 229 | const nAy = eA0z*eA2x - eA0x*eA2z; |
| 230 | const nAz = eA0x*eA2y - eA0y*eA2x; |
| 231 | |
| 232 | const nBx = eB0y*eB2z - eB0z*eB2y; |
| 233 | const nBy = eB0z*eB2x - eB0x*eB2z; |
| 234 | const nBz = eB0x*eB2y - eB0y*eB2x; |
| 235 | |
| 236 | // Helper: project 6 vertices onto axis, return true if separated |
| 237 | function separated(ax, ay, az) { |
| 238 | const lenSq = ax*ax + ay*ay + az*az; |
| 239 | if (lenSq < 1e-20) return false; // degenerate axis, skip |
| 240 | |
| 241 | const pA0 = a0x*ax + a0y*ay + a0z*az; |
| 242 | const pA1 = a1x*ax + a1y*ay + a1z*az; |
| 243 | const pA2 = a2x*ax + a2y*ay + a2z*az; |
| 244 | const pB0 = b0x*ax + b0y*ay + b0z*az; |
| 245 | const pB1 = b1x*ax + b1y*ay + b1z*az; |
| 246 | const pB2 = b2x*ax + b2y*ay + b2z*az; |
| 247 | |
| 248 | const minA = Math.min(pA0, pA1, pA2), maxA = Math.max(pA0, pA1, pA2); |
| 249 | const minB = Math.min(pB0, pB1, pB2), maxB = Math.max(pB0, pB1, pB2); |
| 250 | |
| 251 | // Use a relative epsilon for the overlap test |
| 252 | const eps = 1e-8 * Math.max(Math.abs(maxA), Math.abs(maxB), Math.abs(minA), Math.abs(minB), 1); |
| 253 | return maxA < minB - eps || maxB < minA - eps; |
| 254 | } |
| 255 | |
| 256 | // Test face normal of A |
no test coverage detected