Skip to content

Commit 25c70b1

Browse files
committed
parallel fix_t_junctions_on_shared_edges
1 parent adb3096 commit 25c70b1

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

src/mesh/mod.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)