STEP export: shelled revolved bodies export instead of panicking (#1496)

A scale across a revolution's axis keeps the face a surface of revolution and is written as one; any other refusal from the writer is an export error, not a panic.
This commit is contained in:
H. Conrad authored and GitHub committed 2026-10-15 22:19:10 -04:00
1 parent d188eea1f4
commit 63accec16e
5 files changed
+161 -13

No files matched your search

+6
View File
@@ -53,6 +53,12 @@ Changes:
mirrored revolutions (ISO 10303-42 parameterises them by angle then profile, so their normal
is the opposite of truck's; a mirroring transform reverses it again), and the modeling-surface
wrapper passes the revolution's sense through instead of always `true`.
- `out/geometry.rs`: a surface of revolution under a transform that commutes with turning about
its axis (a scale across the axis that differs from the scale along it, as a shell or offset
makes of a revolved face) is written as the transformed profile revolved about the
transformed axis. Only uniform scales were taken. Other transforms are still refused: the
writer stops with a formatting error, which SolidCraft's `step.rs` now reports as an export
error (`to_string` panicked on it).
- `lib.rs`: compiler warnings no longer fail the build. Example and test targets are dropped
(their sources are not vendored).
+32 -12
View File
@@ -748,22 +748,42 @@ impl<C> StepSurface for RevolutedCurve<C> {
impl<C> DisplayByStep for Processor<RevolutedCurve<C>, Matrix4>
where C: StepLength + Transformed<Matrix4> + DisplayByStep
{
// SolidCraft: a transform that commutes with turning about the axis keeps the surface one of
// revolution: the transformed profile revolved about the transformed axis. That holds for
// uniform scales (the only case taken before) and also for a scale across the axis that
// differs from the scale along it, which is how a shell or an offset moves a revolved
// cylinder or cone face. Any other transform is still refused.
fn fmt(&self, idx: usize, f: &mut Formatter<'_>) -> Result {
let surface = self.entity();
let transform = self.transform();
let (k, a, _) = match transform.iwasawa_decomposition() {
Some(x) => x,
None => {
f.write_str("Transform is not regular")?;
return ERR;
}
};
if !a[0][0].near(&a[1][1]) || !a[1][1].near(&a[2][2]) {
f.write_str("Transform contains non-uniform scale.")?;
let transform = *self.transform();
if transform.determinant().so_small() {
f.write_str("Transform is not regular")?;
return ERR;
}
let curve = surface.entity_curve().transformed(*transform);
let axis = k.transform_vector(surface.axis());
let a = surface.axis().normalize();
let la = transform.transform_vector(a);
if la.so_small() {
f.write_str("Transform is not regular")?;
return ERR;
}
let axis = la.normalize();
// Two directions across the axis, and their images.
let seed = if a.x.abs() < 0.9 { Vector3::unit_x() } else { Vector3::unit_y() };
let u = (seed - a * a.dot(seed)).normalize();
let v = a.cross(u);
let (lu, lv) = (transform.transform_vector(u), transform.transform_vector(v));
let s = lu.magnitude();
// Across the axis: a similarity onto the plane across the image axis.
let commutes = !s.so_small()
&& (lu.dot(axis) / s).so_small()
&& (lv.dot(axis) / s).so_small()
&& (lu.dot(lv) / (s * s)).so_small()
&& (lv.magnitude() / s).near(&1.0);
if !commutes {
f.write_str("Transform does not keep the surface one of revolution.")?;
return ERR;
}
let curve = surface.entity_curve().transformed(transform);
let origin = transform.transform_point(surface.origin());
let surface = RevolutedCurve::by_revolution(curve, origin, axis);
DisplayByStep::fmt(&surface, idx, f)