One walk over the geometry a feature holds (3/3, #876) (#1028)

Moves the per-feature geometry handling (transforms, face points, profile selections, point expressions) into one geometry walk in feature_geometry.rs, with a golden record of what each feature kind moves. sheet.perforate holds no world geometry and joins the no-op arm.
This commit is contained in:
JIJINBEI authored and GitHub committed 2026-10-14 13:29:31 -04:00
1 parent f79dfe41b6
commit 1a456d2d1f
9 files changed
+709 -258

No files matched your search

+11 -155
View File
@@ -11,7 +11,7 @@
use serde::{Deserialize, Serialize};
use solidcraft_geom::{Plane, Vec3};
use crate::{AxisRef, DocError, Document, FeatureKind, PatternKind, PlaneRef, Result};
use crate::{DocError, Document, FeatureKind, PlaneRef, Result};
/// Column-major 4×4 affine transform (`m[col][row]`).
pub type Mat = [[f64; 4]; 4];
@@ -204,160 +204,16 @@ pub fn map_point_expr(m: &Mat, p: &mut crate::PointExpr) {
impl FeatureKind {
/// Map the feature's world geometry (points, directions, explicit planes) by `m`.
pub fn transform_geometry(&mut self, m: &Mat) {
let pt = |p: &mut Vec3| *p = apply_point(m, *p);
let dir = |v: &mut Vec3| *v = apply_vector(m, *v);
match self {
FeatureKind::Sketch { plane, .. } | FeatureKind::ConstructionPlane { plane } | FeatureKind::Mirror { plane, .. } => {
map_plane_ref(m, plane)
}
FeatureKind::ConstructionAxis { def } => def.refs_mut().into_iter().for_each(|r| map_geo_ref(m, r)),
FeatureKind::ConstructionPoint { def } => def.refs_mut().into_iter().for_each(|r| map_geo_ref(m, r)),
FeatureKind::Split { plane, tool, .. } => {
map_plane_ref(m, plane);
if let Some(t) = tool {
pt(&mut t.point);
}
}
FeatureKind::SplitFace { faces, plane, tool, .. } => {
faces.iter_mut().for_each(pt);
map_plane_ref(m, plane);
if let Some(t) = tool {
pt(&mut t.point);
}
}
FeatureKind::Revolve { axis: AxisRef::Line { origin, dir: d }, .. }
| FeatureKind::SurfaceRevolve { axis: AxisRef::Line { origin, dir: d }, .. } => {
pt(origin);
dir(d);
}
FeatureKind::Fillet { edges, style, .. } => {
edges.iter_mut().for_each(pt);
if let crate::FilletStyle::Variable { start, .. } | crate::FilletStyle::Spline { start, .. } = style {
pt(start);
}
}
FeatureKind::Chamfer { edges, .. } => edges.iter_mut().for_each(pt),
FeatureKind::Shell { faces, .. }
| FeatureKind::OffsetFace { faces, .. }
| FeatureKind::DeleteFace { faces, .. }
| FeatureKind::SurfaceOffset { faces, .. } => faces.iter_mut().for_each(pt),
FeatureKind::Draft { faces, neutral, pull, .. } => {
faces.iter_mut().for_each(pt);
map_plane_ref(m, neutral);
dir(pull);
}
FeatureKind::Box { corner, .. } => map_point_expr(m, corner),
FeatureKind::Cylinder { base, axis, .. } => {
map_point_expr(m, base);
dir(axis);
}
FeatureKind::Sphere { center, .. } | FeatureKind::Torus { center, .. } => map_point_expr(m, center),
FeatureKind::Pattern { pattern: PatternKind::Rectangular { dir1, dir2, .. }, .. } => {
dir(dir1);
if let Some(d) = dir2 {
dir(d);
}
}
FeatureKind::Pattern { pattern: PatternKind::Circular { origin, axis, .. }, .. } => {
pt(origin);
dir(axis);
}
// Follows its sketch.
FeatureKind::Pattern { pattern: PatternKind::Path { .. }, .. } => {}
FeatureKind::Hole { position, direction, .. } => {
pt(position);
dir(direction);
}
FeatureKind::Thread { face, .. } => pt(face),
FeatureKind::SheetFlange { edges, .. } => edges.iter_mut().for_each(|e| pt(&mut e.point)),
FeatureKind::SheetHem { edges, .. } => edges.iter_mut().for_each(pt),
FeatureKind::SheetConvert { face, bends, .. } => {
pt(face);
bends.iter_mut().for_each(pt);
}
FeatureKind::SheetFold { a, b, fixed, .. } => {
pt(a);
pt(b);
if let Some(f) = fixed {
pt(f);
}
}
FeatureKind::Boss { position, direction, .. } => {
pt(position);
dir(direction);
}
FeatureKind::Lip { face, .. } => pt(face),
FeatureKind::BoundaryFill { cells, .. } => cells.iter_mut().for_each(pt),
FeatureKind::Rest { position, direction, along, .. } => {
pt(position);
dir(direction);
if let Some(a) = along {
dir(a);
}
}
FeatureKind::SnapFit { position, direction, hook, .. } => {
pt(position);
dir(direction);
dir(hook);
}
FeatureKind::SheetBase { .. }
| FeatureKind::SheetContour { .. }
| FeatureKind::SheetUnfold { .. }
| FeatureKind::SheetPerforate { .. } => {}
FeatureKind::Coil { base, axis, .. } => {
map_point_expr(m, base);
dir(axis);
}
FeatureKind::ReplaceFace { faces, target, .. } => {
faces.iter_mut().for_each(pt);
map_plane_ref(m, target);
}
FeatureKind::Align { from, to, from_normal, to_normal, .. } => {
pt(from);
pt(to);
if let Some(n) = from_normal {
dir(n);
}
if let Some(n) = to_normal {
dir(n);
}
}
FeatureKind::Scale { origin, .. } => pt(origin),
FeatureKind::Move { rotate_axis, .. } => {
if let Some(a) = rotate_axis {
dir(a);
}
}
FeatureKind::Revolve { .. }
| FeatureKind::Extrude { .. }
| FeatureKind::SurfaceExtrude { .. }
| FeatureKind::SurfaceRevolve { .. }
| FeatureKind::SurfaceSweep { .. }
| FeatureKind::SurfaceLoft { .. }
| FeatureKind::Combine { .. }
| FeatureKind::Loft { .. }
| FeatureKind::Sweep { .. }
| FeatureKind::BoundingSolid { .. }
| FeatureKind::Pipe { .. }
| FeatureKind::Emboss { .. }
| FeatureKind::Rib { .. }
| FeatureKind::Remove { .. }
| FeatureKind::Stitch { .. }
| FeatureKind::Thicken { .. }
| FeatureKind::Import { .. }
| FeatureKind::MeshImport { .. }
| FeatureKind::MeshConvert { .. }
| FeatureKind::BodyTransfer { .. } => {}
FeatureKind::Patch { edges, .. } | FeatureKind::SurfaceExtend { edges, .. } => edges.iter_mut().for_each(pt),
FeatureKind::Unstitch { faces, .. } | FeatureKind::ReverseNormal { faces, .. } => faces.iter_mut().for_each(pt),
FeatureKind::SurfaceTrim { plane, tool, keep, .. } => {
map_plane_ref(m, plane);
if let Some(t) = tool {
pt(&mut t.point);
}
pt(keep);
}
}
use crate::feature_geometry::Geo;
self.walk_geometry_mut(&mut |g| match g {
Geo::Pick(p) | Geo::Pos(p) => *p = apply_point(m, *p),
Geo::Dir(v) => *v = apply_vector(m, *v),
Geo::Plane(p) => map_plane_ref(m, p),
Geo::Construct(r) => map_geo_ref(m, r),
Geo::Placement(e) => map_point_expr(m, e),
// An Emboss cylinder is picked in its component's frame (`EmbossFace`).
Geo::Pinned(_) | Geo::Profiles(..) => {}
});
}
}