diff --git a/crates/engine/src/cmd/plan.rs b/crates/engine/src/cmd/plan.rs index efbef43..0d015e8 100644 --- a/crates/engine/src/cmd/plan.rs +++ b/crates/engine/src/cmd/plan.rs @@ -1592,9 +1592,8 @@ mod acceptance { } } } - // The layout is mirror symmetric and the smoother visits mirror twins alike; what is left - // is the projection onto the body's tessellation, which the kernel does not make mirror - // symmetric (a chordal tolerance of 0.0165 mm, on 1.5 mm elements). + // The layout is mirror symmetric; the smoother (Gauss-Seidel in node order) and the + // projection onto the body's tessellation are not, so the corners differ a little. let (lo, hi) = worst.iter().fold((f64::INFINITY, 0.0f64), |(l, h), w| (l.min(*w), h.max(*w))); assert!(hi - lo < 0.03, "per corner min scaled Jacobian {worst:?}: {r}"); assert!(lo > 0.65, "per corner min scaled Jacobian {worst:?}"); diff --git a/crates/mesh/src/project/smooth.rs b/crates/mesh/src/project/smooth.rs index 76a18db..4749180 100644 --- a/crates/mesh/src/project/smooth.rs +++ b/crates/mesh/src/project/smooth.rs @@ -343,27 +343,6 @@ pub(crate) fn smooth(m: Mesh, g: &dyn Geometry, targets: &[Vec], method: Sm report } -/// The order nodes are visited in, a sweep being Gauss–Seidel (each node sees the moves made -/// before it): by distance from the nodes' centroid, then by height along each axis's absolute -/// offset from it, so a mesh and its mirror image (or a quarter turn of it) about the centroid -/// visit corresponding nodes alike and smooth alike. Keys are rounded so mirror twins tie; twins -/// lie apart, so their order among themselves does not matter. -fn sweep_order(x: &[P]) -> Vec { - let n = x.len().max(1) as f64; - let c = x.iter().fold([0.0; 3], |a, p| add(a, scale(*p, 1.0 / n))); - let span = x.iter().map(|p| dist(*p, c)).fold(0.0, f64::max).max(f64::MIN_POSITIVE); - let q = |v: f64| (v / span * 1e7).round() as i64; - let mut order: Vec = (0..x.len() as u32).collect(); - order.sort_by_key(|&v| { - let p = at(x, v); - let d = sub(p, c); - let mut offs = [q(d[0].abs()), q(d[1].abs()), q(d[2].abs())]; - offs.sort_unstable(); - (q(norm(d)), offs, v) - }); - order -} - /// One smoothing run; sweep moves keep every element at or above the lower of its scaled /// Jacobian before and `sweep_floor`. fn pass(m: &mut Mesh, g: &dyn Geometry, targets: &[Vec], method: Smoothing, iterations: usize, tol: f64, sweep_floor: f64) -> SmoothReport { @@ -420,21 +399,11 @@ fn pass(m: &mut Mesh, g: &dyn Geometry, targets: &[Vec], method: Smoothing, if method == Smoothing::None { return report; } - let order = sweep_order(m.nodes); - let rank: Vec = { - let mut r = vec![0; n]; - for (i, &v) in order.iter().enumerate() { - if let Some(x) = r.get_mut(v as usize) { - *x = i; - } - } - r - }; let ptol = 1e-3 * tol; for it in 0..iterations { report.iterations = it + 1; let mut moved = 0.0f64; - for &v in &order { + for v in 0..n as u32 { let c = cons(v); let p = at(m.nodes, v); let mean = |nbrs: &mut dyn Iterator| -> Option

{ @@ -498,7 +467,7 @@ fn pass(m: &mut Mesh, g: &dyn Geometry, targets: &[Vec], method: Smoothing, for _ in 0..OPTIMIZE_ROUNDS { let mut poor: Vec = m.hexes.iter().filter(|h| hex_scaled_jacobian(&h.map(|q| at(m.nodes, q))) < LIFT_BELOW).flat_map(|h| h.iter().copied()).collect(); - poor.sort_unstable_by_key(|&v| rank.get(v as usize).copied().unwrap_or(usize::MAX)); + poor.sort_unstable(); poor.dedup(); if poor.is_empty() { break;