@@ -276,6 +276,156 @@ describe 'Spatial Query Utilities', ->
276276
277277 return
278278
279+ it ' should handle edges nearly parallel to slicing plane' , ->
280+
281+ # Create triangles with edges nearly parallel to Z=0.5 plane.
282+ # This tests the adaptive epsilon for near-parallel edge detection.
283+ vertexArray = new Float32Array ([
284+ # Triangle 1: Clear intersection, one edge nearly parallel to plane.
285+ - 1.0 , - 1.0 , 0.2 , # Well below plane.
286+ 1.0 , - 1.0 , 0.4999998 , # Very slightly below Z=0.5 (nearly on plane).
287+ 1.0 , 1.0 , 0.7 , # Well above plane.
288+
289+ # Triangle 2: Similar configuration, different position.
290+ - 1.0 , 1.0 , 0.3 , # Well below plane.
291+ - 0.5 , 0.5 , 0.5000002 , # Very slightly above Z=0.5 (nearly on plane).
292+ 1.0 , 1.0 , 0.8 , # Well above plane.
293+ ])
294+
295+ normalArray = new Float32Array ([
296+ 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1 ,
297+ 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1 ,
298+ ])
299+
300+ geometry = new BufferGeometry ()
301+ geometry .setAttribute (' position' , new BufferAttribute (vertexArray, 3 ))
302+ geometry .setAttribute (' normal' , new BufferAttribute (normalArray, 3 ))
303+ geometry .setIndex ([0 , 1 , 2 , 3 , 4 , 5 ])
304+
305+ mesh = new Mesh (geometry, new MeshBasicMaterial ())
306+
307+ # Slice at Z=0.5 where edges are nearly parallel.
308+ layers = Polytree .sliceIntoLayers (mesh, 0.1 , 0.5 , 0.5 )
309+
310+ expect (layers .length ).toBe (1 )
311+
312+ # Should detect intersection segments despite near-parallel edges.
313+ # Both triangles should produce segments.
314+ expect (layers[0 ].length ).toBe (2 )
315+
316+ return
317+
318+ it ' should handle very small edge distances with adaptive epsilon' , ->
319+
320+ # Create geometry where edge endpoints have very small distances to plane.
321+ # This simulates floating-point precision issues in real-world meshes.
322+ vertexArray = new Float32Array ([
323+ # Triangle crossing Z=1.0 with tiny distances.
324+ - 1.0 , - 1.0 , 0.8 , # Below plane.
325+ 1.0 , - 1.0 , 0.9999999999 , # 1e-10 below plane.
326+ 1.0 , 1.0 , 1.2 , # Above plane.
327+
328+ # Triangle 2: One vertex extremely close to plane.
329+ - 1.0 , 1.0 , 0.9 , # Well below Z=1.0.
330+ - 0.5 , 0.5 , 1.0000000005 , # 5e-10 above plane.
331+ 1.0 , 1.0 , 1.2 , # Well above plane.
332+ ])
333+
334+ normalArray = new Float32Array ([
335+ 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1 ,
336+ 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1 ,
337+ ])
338+
339+ geometry = new BufferGeometry ()
340+ geometry .setAttribute (' position' , new BufferAttribute (vertexArray, 3 ))
341+ geometry .setAttribute (' normal' , new BufferAttribute (normalArray, 3 ))
342+ geometry .setIndex ([0 , 1 , 2 , 3 , 4 , 5 ])
343+
344+ mesh = new Mesh (geometry, new MeshBasicMaterial ())
345+
346+ layers = Polytree .sliceIntoLayers (mesh, 0.5 , 1.0 , 1.0 )
347+
348+ expect (layers .length ).toBe (1 )
349+
350+ # Should correctly identify intersections with adaptive epsilon.
351+ expect (layers[0 ].length ).toBe (2 ) # Two triangles should intersect.
352+
353+ return
354+
355+ it ' should not duplicate segments for edges on plane with epsilon tolerance' , ->
356+
357+ # Create geometry with edges exactly on the slicing plane.
358+ # Tests that duplicate detection works with epsilon-based comparisons.
359+ vertexArray = new Float32Array ([
360+ # Two adjacent triangles sharing an edge on Z=2.0.
361+ - 1.0 , 0.0 , 2.0 , # Shared vertex 1 on plane.
362+ 1.0 , 0.0 , 2.0 , # Shared vertex 2 on plane.
363+ 0.0 , - 1.0 , 1.5 , # Triangle 1 below.
364+
365+ - 1.0 , 0.0 , 2.0 , # Shared vertex 1 (same as above).
366+ 1.0 , 0.0 , 2.0 , # Shared vertex 2 (same as above).
367+ 0.0 , 1.0 , 2.5 , # Triangle 2 above.
368+ ])
369+
370+ normalArray = new Float32Array ([
371+ 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1 ,
372+ 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1 ,
373+ ])
374+
375+ geometry = new BufferGeometry ()
376+ geometry .setAttribute (' position' , new BufferAttribute (vertexArray, 3 ))
377+ geometry .setAttribute (' normal' , new BufferAttribute (normalArray, 3 ))
378+ geometry .setIndex ([0 , 1 , 2 , 3 , 4 , 5 ])
379+
380+ mesh = new Mesh (geometry, new MeshBasicMaterial ())
381+
382+ layers = Polytree .sliceIntoLayers (mesh, 1.0 , 2.0 , 2.0 )
383+
384+ expect (layers .length ).toBe (1 )
385+
386+ # Should have 2 segments (one from each triangle).
387+ # Duplicate detection should prevent extra segments.
388+ expect (layers[0 ].length ).toBe (2 )
389+
390+ return
391+
392+ it ' should handle long edges with scaled epsilon' , ->
393+
394+ # Create geometry with very long edges to test epsilon scaling.
395+ # Long edges accumulate more floating-point error.
396+ vertexArray = new Float32Array ([
397+ # Triangle with very long edges crossing Z=10.0.
398+ - 100.0 , - 100.0 , 9.5 , # Below plane.
399+ 100.0 , 100.0 , 9.9999 , # Very close below, far from origin.
400+ 100.0 , - 100.0 , 10.5 , # Above plane, far from origin.
401+
402+ # Smaller triangle for comparison.
403+ - 1.0 , 0.0 , 9.5 ,
404+ 1.0 , 0.0 , 10.5 ,
405+ 0.0 , 1.0 , 12.0 ,
406+ ])
407+
408+ normalArray = new Float32Array ([
409+ 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1 ,
410+ 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1 ,
411+ ])
412+
413+ geometry = new BufferGeometry ()
414+ geometry .setAttribute (' position' , new BufferAttribute (vertexArray, 3 ))
415+ geometry .setAttribute (' normal' , new BufferAttribute (normalArray, 3 ))
416+ geometry .setIndex ([0 , 1 , 2 , 3 , 4 , 5 ])
417+
418+ mesh = new Mesh (geometry, new MeshBasicMaterial ())
419+
420+ layers = Polytree .sliceIntoLayers (mesh, 1.0 , 10.0 , 10.0 )
421+
422+ expect (layers .length ).toBe (1 )
423+
424+ # Adaptive epsilon should handle both long and short edges correctly.
425+ expect (layers[0 ].length ).toBe (2 )
426+
427+ return
428+
279429 describe ' shapecast' , ->
280430
281431 it ' should find triangles matching custom query' , ->
0 commit comments