* Find pairs of intersecting triangles using spatial hashing for the broad * phase and the Separating Axis Theorem for the narrow phase. * Skips triangle pairs that share any vertex (topological neighbors). * * @param {THREE.BufferGeometry} geometry * @param {{ get:() => number }} token - abor
(geometry, token)
| 69 | * phase and the Separating Axis Theorem for the narrow phase. |
| 70 | * Skips triangle pairs that share any vertex (topological neighbors). |
| 71 | * |
| 72 | * @param {THREE.BufferGeometry} geometry |
| 73 | * @param {{ get:() => number }} token - abort when token.get() !== startValue |
| 74 | * @returns {Promise<number>} count of intersecting triangle pairs |
| 75 | */ |
| 76 | async function findIntersectingTriangles(geometry, token) { |
| 77 | const startToken = token.get(); |
| 78 | const pos = geometry.attributes.position.array; |
| 79 | const triCount = pos.length / 9; |
| 80 | |
| 81 | // Assign a numeric ID to each unique vertex position (quantized). |
| 82 | // Two triangles that share any vertex are topological neighbors and must be |
| 83 | // skipped — they touch at that vertex and SAT would flag them otherwise. |
| 84 | const QUANT = 1e4; |
| 85 | const posToId = new QuantizedPointMap(QUANT, Math.min(triCount * 3, 1 << 22)); |
| 86 | let nextVId = 0; |
| 87 | const triVerts = new Uint32Array(triCount * 3); |
| 88 | for (let t = 0; t < triCount; t++) { |
| 89 | const b = t * 9; |
| 90 | for (let v = 0; v < 3; v++) { |
| 91 | const off = b + v * 3; |
| 92 | const id = posToId.getOrSet(pos[off], pos[off+1], pos[off+2], nextVId); |
| 93 | if (posToId.inserted) nextVId++; |
| 94 | triVerts[t * 3 + v] = id; |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | function sharesVertex(a, b) { |
| 99 | const aBase = a * 3, bBase = b * 3; |
| 100 | for (let i = 0; i < 3; i++) { |
| 101 | const vid = triVerts[aBase + i]; |
| 102 | if (vid === triVerts[bBase] || vid === triVerts[bBase + 1] || vid === triVerts[bBase + 2]) return true; |
| 103 | } |
| 104 | return false; |
| 105 | } |
| 106 | |
| 107 | // Build per-triangle AABB |
| 108 | const minX = new Float32Array(triCount); |
| 109 | const minY = new Float32Array(triCount); |
| 110 | const minZ = new Float32Array(triCount); |
| 111 | const maxX = new Float32Array(triCount); |
| 112 | const maxY = new Float32Array(triCount); |
| 113 | const maxZ = new Float32Array(triCount); |
| 114 | |
| 115 | for (let t = 0; t < triCount; t++) { |
| 116 | const b = t * 9; |
| 117 | const ax = pos[b], ay = pos[b+1], az = pos[b+2]; |
| 118 | const bx = pos[b+3], by = pos[b+4], bz = pos[b+5]; |
| 119 | const cx = pos[b+6], cy = pos[b+7], cz = pos[b+8]; |
| 120 | minX[t] = Math.min(ax, bx, cx); maxX[t] = Math.max(ax, bx, cx); |
| 121 | minY[t] = Math.min(ay, by, cy); maxY[t] = Math.max(ay, by, cy); |
| 122 | minZ[t] = Math.min(az, bz, cz); maxZ[t] = Math.max(az, bz, cz); |
| 123 | } |
| 124 | |
| 125 | // Determine grid cell size from median AABB extent |
| 126 | const extents = new Float32Array(triCount); |
| 127 | for (let t = 0; t < triCount; t++) { |
| 128 | extents[t] = Math.max(maxX[t] - minX[t], maxY[t] - minY[t], maxZ[t] - minZ[t]); |
no test coverage detected