@@ -139,6 +139,7 @@ impl<S: Clone + Send + Sync + Debug> Mesh<S> {
139139 /// This works even when two polygons' edges do not share endpoints
140140 /// but are colinear and overlapping, because the overlapping
141141 /// endpoints lie strictly in the interior of the opposite segment.
142+ #[ cfg( not( feature = "parallel" ) ) ]
142143 fn fix_t_junctions_on_shared_edges ( polygons : & mut [ Polygon < S > ] ) {
143144 let eps = tolerance ( ) ;
144145 let eps2 = eps * eps;
@@ -279,6 +280,159 @@ impl<S: Clone + Send + Sync + Debug> Mesh<S> {
279280 }
280281 }
281282
283+ #[ cfg( feature = "parallel" ) ]
284+ fn fix_t_junctions_on_shared_edges ( polygons : & mut [ Polygon < S > ] ) {
285+ use rayon:: prelude:: * ;
286+
287+ let eps = tolerance ( ) ;
288+ let eps2 = eps * eps;
289+
290+ if polygons. len ( ) < 2 {
291+ return ;
292+ }
293+
294+ // --- Detection pass (parallel) ---
295+ // Immutable view of polygons for detection.
296+ let edge_splits: Vec < HashMap < usize , Vec < ( Real , Vertex ) > > > = {
297+ let polys: & [ Polygon < S > ] = & * polygons;
298+
299+ polys
300+ . par_iter ( )
301+ . enumerate ( )
302+ . map ( |( j, poly_j) | {
303+ let mut splits_map: HashMap < usize , Vec < ( Real , Vertex ) > > = HashMap :: new ( ) ;
304+
305+ let verts_j = & poly_j. vertices ;
306+ let n_j = verts_j. len ( ) ;
307+ if n_j < 2 {
308+ return splits_map;
309+ }
310+
311+ for edge_start in 0 ..n_j {
312+ let a = & verts_j[ edge_start] ;
313+ let b = & verts_j[ ( edge_start + 1 ) % n_j] ;
314+
315+ // Edge vector AB
316+ let ab = b. pos - a. pos ;
317+ let ab_len_sq = ab. norm_squared ( ) ;
318+ if ab_len_sq < eps2 {
319+ // Degenerate edge
320+ continue ;
321+ }
322+
323+ // Test all vertices of all *other* polygons against this edge
324+ for ( i, poly_i) in polys. iter ( ) . enumerate ( ) {
325+ if i == j || poly_i. vertices . len ( ) < 2 {
326+ continue ;
327+ }
328+
329+ for vert in & poly_i. vertices {
330+ // Vectors from endpoints to the candidate vertex
331+ let av = vert. pos - a. pos ;
332+ let bv = vert. pos - b. pos ;
333+
334+ // Skip if vertex is basically at one of the endpoints
335+ if av. norm_squared ( ) < eps2 || bv. norm_squared ( ) < eps2 {
336+ continue ;
337+ }
338+
339+ // Parametric coordinate of the projection of vert onto AB
340+ let t = ab. dot ( & av) / ab_len_sq;
341+
342+ // Only consider points strictly inside the segment (avoid ends)
343+ if t <= eps || t >= 1.0 - eps {
344+ // Too close to edge endpoints
345+ continue ;
346+ }
347+
348+ // Closest point on AB to vert
349+ let projected = a. pos + ab * t;
350+
351+ // Check that the vertex actually lies on the segment (within eps)
352+ if ( vert. pos - projected) . norm_squared ( ) > eps2 {
353+ // Not actually on the edge
354+ continue ;
355+ }
356+
357+ // We now know vert lies on edge (a, b) of polygon j.
358+ // Create a vertex consistent with polygon j's plane.
359+ let new_vertex = Vertex :: new ( vert. pos , poly_j. plane . normal ( ) ) ;
360+
361+ // Register the split
362+ let entry =
363+ splits_map. entry ( edge_start) . or_insert_with ( Vec :: new) ;
364+
365+ // Avoid duplicate splits (same t / same position)
366+ let mut already_present = false ;
367+ for ( existing_t, existing_v) in entry. iter ( ) {
368+ if ( existing_t - t) . abs ( ) < eps {
369+ already_present = true ;
370+ break ;
371+ }
372+
373+ if ( existing_v. pos - new_vertex. pos ) . norm_squared ( ) < eps2
374+ {
375+ already_present = true ;
376+ break ;
377+ }
378+ }
379+
380+ if !already_present {
381+ entry. push ( ( t, new_vertex) ) ;
382+ }
383+ }
384+ }
385+ }
386+
387+ splits_map
388+ } )
389+ . collect ( )
390+ } ;
391+
392+ // --- Application pass (parallel) ---
393+ polygons
394+ . par_iter_mut ( )
395+ . enumerate ( )
396+ . for_each ( |( poly_index, poly) | {
397+ let splits_map = & edge_splits[ poly_index] ;
398+ if splits_map. is_empty ( ) {
399+ return ;
400+ }
401+
402+ let original = poly. vertices . clone ( ) ;
403+ let n = original. len ( ) ;
404+ if n < 2 {
405+ return ;
406+ }
407+
408+ let extra_vertices: usize = splits_map. values ( ) . map ( |v| v. len ( ) ) . sum ( ) ;
409+
410+ let mut new_vertices = Vec :: with_capacity ( n + extra_vertices) ;
411+
412+ for ( edge_start, vertex) in original. iter ( ) . enumerate ( ) {
413+ // Always keep the original starting vertex of the edge
414+ new_vertices. push ( * vertex) ;
415+
416+ if let Some ( splits) = splits_map. get ( & edge_start) {
417+ // Sort new points along the edge
418+ let mut splits_sorted = splits. clone ( ) ;
419+ splits_sorted. sort_by ( |( t_a, _) , ( t_b, _) | {
420+ t_a. partial_cmp ( t_b) . unwrap_or ( Ordering :: Equal )
421+ } ) ;
422+
423+ // Insert them in parametric order between edge_start and edge_start+1
424+ for ( _, v) in splits_sorted {
425+ new_vertices. push ( v) ;
426+ }
427+ }
428+ }
429+
430+ poly. vertices = new_vertices;
431+ // Inserted vertices lie on existing edges, so the polygon AABB
432+ // remains valid and we don't need to reset poly.bounding_box.
433+ } ) ;
434+ }
435+
282436 /// Triangulate each polygon in the Mesh returning a Mesh containing triangles
283437 pub fn triangulate ( & self ) -> Mesh < S > {
284438 // Work on a local copy so we do not mutate the original mesh.
0 commit comments