fix: booleans with whole-turn round faces read from STEP are exact instead of failing (#1892)

Thanks @NOWN-DEV.
This commit is contained in:
NOWN-DEV authored and GitHub committed 2026-10-17 04:08:34 -04:00
1 parent 962070e955
commit c38992fc09
6 files changed
+233 -4

No files matched your search

+6
View File
@@ -53,6 +53,12 @@ Changes:
farther from the leader's point than the curve's tolerance (the projection settled on another
part of a surface, such as the far side of a small hole) is dropped for the search from
nothing, as before. `crates/kernel/src/issue_1038_tests.rs` holds it to that search.
- `decorators/revolved_curve.rs`: a point on a revolved surface's seam (angle 0, the same place
as 2π) takes the end of the period nearer the hinted angle. The angle was decided by the sign of
a cross product that is rounding noise there, so a boundary followed point by point could come
back to the seam at the other end of the period and fold its parameter polygon over itself (no
intersection loop then lay inside the face). `crates/kernel/src/seam_faces_tests.rs` holds it
to a cone read from STEP.
## truck-stepio 0.3.0 (Apache-2.0, © RICOS Co. Ltd., https://github.com/ricosjp/truck)
+28 -4
View File
@@ -82,6 +82,20 @@ impl Revolution {
}
}
/// SolidCraft: the angle of a point on the seam, where 0 and 2π are the same place, taken at the end
/// of the period nearer the hint's angle. [`Revolution::proj_angle`] decides it by the sign of a cross
/// product that is rounding noise there, so a boundary followed point by point could come back to
/// the seam at the other end of the period from where it left, and its parameter polygon folded
/// over itself (a whole turn of a cylinder had no area, and no intersection loop lay inside it).
fn seam_side(angle: f64, hint: Option<f64>) -> f64 {
const SEAM: f64 = 1.0e-7;
match hint {
Some(h) if angle < SEAM && h > PI => angle + 2.0 * PI,
Some(h) if angle > 2.0 * PI - SEAM && h < PI => angle - 2.0 * PI,
_ => angle,
}
}
impl<C: ParametricCurve3D> ParametricSurface for RevolutedCurve<C> {
type Point = Point3;
type Vector = Vector3;
@@ -330,14 +344,19 @@ impl<C: ParametricCurve3D + BoundedCurve> SearchParameter<D2> for RevolutedCurve
revolution: self.revolution,
};
let p = self.revolution.proj_point(point);
let hint0 = match hint.into() {
let hint = hint.into();
let hint_angle = match hint {
SPHint2D::Parameter(_, y) => Some(y),
_ => None,
};
let hint0 = match hint {
SPHint2D::Parameter(x, _) => SPHint1D::Parameter(x),
SPHint2D::Range((x0, _), (x1, _)) => SPHint1D::Range(x0, x1),
SPHint2D::None => SPHint1D::None,
};
let t = proj_curve.search_parameter(p, hint0, trials)?;
let p = self.curve.subs(t);
let ang = self.revolution.proj_angle(p, point);
let ang = seam_side(self.revolution.proj_angle(p, point), hint_angle);
match self.subs(t, ang).near(&point) {
true => Some((t, ang)),
false => None,
@@ -377,14 +396,19 @@ impl<C: ParametricCurve3D + BoundedCurve> SearchNearestParameter<D2> for Revolut
revolution: self.revolution,
};
let p = self.revolution.proj_point(point);
let hint0 = match hint.into() {
let hint = hint.into();
let hint_angle = match hint {
SPHint2D::Parameter(_, y) => Some(y),
_ => None,
};
let hint0 = match hint {
SPHint2D::Parameter(x, _) => SPHint1D::Parameter(x),
SPHint2D::Range((x0, _), (x1, _)) => SPHint1D::Range(x0, x1),
SPHint2D::None => SPHint1D::None,
};
let t = proj_curve.search_nearest_parameter(p, hint0, trials)?;
let p = self.curve.subs(t);
Some((t, self.revolution.proj_angle(p, point)))
Some((t, seam_side(self.revolution.proj_angle(p, point), hint_angle)))
}
}
}