* Find duplicate triangles by exact vertex comparison. * Two triangles are duplicates if they share the same three vertex positions * (bit-identical floats), regardless of winding order. * Also collects the face indices of duplicates for highlighting. * * @param {THREE.BufferGeometry} geometry
(geometry, token)
| 328 | * Also collects the face indices of duplicates for highlighting. |
| 329 | * |
| 330 | * @param {THREE.BufferGeometry} geometry |
| 331 | * @param {{ get:() => number }} token |
| 332 | * @returns {Promise<{ count:number, faces:Set<number> }|number>} |
| 333 | * -1 if aborted |
| 334 | */ |
| 335 | async function findOverlappingTriangles(geometry, token) { |
| 336 | const startToken = token.get(); |
| 337 | const pos = geometry.attributes.position.array; |
| 338 | const triCount = pos.length / 9; |
| 339 | |
| 340 | // Use a DataView to read float32 bits as uint32 for exact hashing |
| 341 | const dv = new DataView(pos.buffer, pos.byteOffset, pos.byteLength); |
| 342 | |
| 343 | // Build a sortable key per vertex from raw float bits (exact, no quantization) |
| 344 | function vertKey(offset) { |
| 345 | // Read the 3 float32 values as uint32 bit patterns |
| 346 | const bx = dv.getUint32(offset, true); |
| 347 | const by = dv.getUint32(offset + 4, true); |
| 348 | const bz = dv.getUint32(offset + 8, true); |
| 349 | return `${bx}_${by}_${bz}`; |
| 350 | } |
| 351 | |
| 352 | const triHashMap = new Map(); |
| 353 | const overlapFaces = new Set(); |
| 354 | |
| 355 | for (let t = 0; t < triCount; t++) { |
| 356 | const byteBase = t * 9 * 4; // 9 floats × 4 bytes |
| 357 | const verts = [ |
| 358 | vertKey(byteBase), |
| 359 | vertKey(byteBase + 12), |
| 360 | vertKey(byteBase + 24), |
| 361 | ]; |
| 362 | verts.sort(); |
| 363 | const key = verts[0] + '|' + verts[1] + '|' + verts[2]; |
| 364 | const existing = triHashMap.get(key); |
| 365 | if (existing !== undefined) { |
| 366 | overlapFaces.add(existing); |
| 367 | overlapFaces.add(t); |
| 368 | } else { |
| 369 | triHashMap.set(key, t); |
| 370 | } |
| 371 | |
| 372 | if (t % 50000 === 0 && t > 0) { |
| 373 | await yieldFrame(); |
| 374 | if (token.get() !== startToken) return -1; |
| 375 | } |
| 376 | } |
| 377 |
no test coverage detected