Feature walks: generate them from field roles with #[derive(Walk)] (#1251)

The three walks over a feature (expressions, references, geometry) are generated from FeatureKind by #[derive(Walk)] in a new proc-macro crate, solidcraft-derive, from #[walk(...)] attributes written next to each field. The hand-written walks in params.rs, feature_refs.rs and feature_geometry.rs are gone; Ruled Surface and Extend have their attributes, and the three golden walk records are unchanged.

Co-authored-by: jijinbei <jijinbei@users.noreply.github.com>
This commit is contained in:
JIJINBEIandjijinbei authored and GitHub committed 2026-10-15 00:13:53 -04:00
1 parent a026a69182
commit 464a6dfad4
13 files changed
+998 -937

No files matched your search

+4 -225
View File
@@ -1,12 +1,12 @@
//! References from a feature to the rest of the design: the sketches it is built from (by id),
//! the features it names (patterned and mirrored features, the construction plane a plane is
//! built on) and the bodies it works on (by name). One walk, written once for `&` and `&mut`,
//! behind `input_sketches`, `remap_refs` and `remap_bodies`. Every variant is listed, so a new
//! one has to say what it refers to.
//! built on) and the bodies it works on (by name). The walk (`crate::walk`) is behind
//! `input_sketches`, `remap_refs` and `remap_bodies`.
use std::collections::BTreeMap;
use crate::{FeatureKind, PatternKind, PlaneRef};
use crate::FeatureKind;
use crate::walk::Walk;
/// A reference a feature holds: `S` is `&u64` or `&mut u64` (a sketch id), `N` is `&String` or
/// `&mut String` (a feature or body name).
@@ -16,228 +16,7 @@ pub(crate) enum Ref<S, N> {
Body(N),
}
/// The construction plane a plane is built on (through offsets and angles), for `&` and `&mut`
/// (`$m` is `mut` or nothing). Nesting is capped like every walk over a plane.
macro_rules! plane_walk {
($name:ident $(, $m:tt)?) => {
fn $name<'a>(mut p: &'a $($m)? PlaneRef, f: &mut impl FnMut(Ref<&'a $($m)? u64, &'a $($m)? String>)) {
for _ in 0..64 {
match p {
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } => p = base,
PlaneRef::Construction { name } => return f(Ref::Feature(name)),
_ => return,
}
}
}
};
}
plane_walk!(plane_refs);
plane_walk!(plane_refs_mut, mut);
/// Every reference of a feature, written once for `&` and `&mut`: `$plane` is the matching plane
/// walk, `$m` is `mut` or nothing.
macro_rules! feature_walk {
($name:ident, $plane:ident $(, $m:tt)?) => {
pub(crate) fn $name<'a>(&'a $($m)? self, f: &mut impl FnMut(Ref<&'a $($m)? u64, &'a $($m)? String>)) {
use Ref::{Body as B, Feature as F, Sketch as S};
match self {
FeatureKind::Sketch { plane, .. } | FeatureKind::ConstructionPlane { plane } => $plane(plane, f),
FeatureKind::Extrude { sketch, targets, .. } | FeatureKind::Revolve { sketch, targets, .. } => {
f(S(sketch));
for b in targets {
f(B(b));
}
}
FeatureKind::Emboss { sketch, targets, face, .. } => {
f(S(sketch));
if let Some(face) = face {
f(B(& $($m)? face.body));
}
for b in targets {
f(B(b));
}
}
FeatureKind::SurfaceExtrude { sketch, .. }
| FeatureKind::SurfaceRevolve { sketch, .. }
| FeatureKind::Rib { sketch, .. }
| FeatureKind::SheetBase { sketch, .. }
| FeatureKind::SheetContour { sketch, .. } => f(S(sketch)),
FeatureKind::SurfaceSweep { sketch, path_sketch, .. } => {
f(S(sketch));
f(S(path_sketch));
}
FeatureKind::SurfaceLoft { sections } => {
for s in sections {
f(S(& $($m)? s.sketch));
}
}
FeatureKind::Sweep { sketch, path_sketch, targets, .. } => {
f(S(sketch));
f(S(path_sketch));
for b in targets {
f(B(b));
}
}
FeatureKind::Pipe { path_sketch, targets, .. } => {
f(S(path_sketch));
for b in targets {
f(B(b));
}
}
FeatureKind::Loft { sections, targets, .. } => {
for s in sections {
f(S(& $($m)? s.sketch));
}
for b in targets {
f(B(b));
}
}
FeatureKind::Coil { targets, .. } => {
for b in targets {
f(B(b));
}
}
FeatureKind::Hole { points, .. } => {
if let Some(p) = points {
f(S(& $($m)? p.sketch));
}
}
FeatureKind::Pattern { features, pattern, bodies } => {
for n in features {
f(F(n));
}
if let PatternKind::Path { path_sketch, .. } = pattern {
f(S(path_sketch));
}
for b in bodies {
f(B(b));
}
}
FeatureKind::Mirror { features, plane, bodies, .. } => {
for n in features {
f(F(n));
}
$plane(plane, f);
for b in bodies {
f(B(b));
}
}
FeatureKind::Split { body, plane, tool } | FeatureKind::SurfaceTrim { body, plane, tool, .. } => {
f(B(body));
$plane(plane, f);
if let Some(t) = tool {
f(B(& $($m)? t.body));
}
}
FeatureKind::SplitFace { body, plane, tool, sketch, .. } => {
$plane(plane, f);
if let Some(s) = sketch {
f(S(s));
}
if let Some(b) = body {
f(B(b));
}
if let Some(t) = tool {
f(B(& $($m)? t.body));
}
}
FeatureKind::Draft { neutral: plane, body, .. } | FeatureKind::ReplaceFace { target: plane, body, .. } => {
$plane(plane, f);
if let Some(b) = body {
f(B(b));
}
}
FeatureKind::SheetFold { sketch, body, .. } | FeatureKind::Patch { sketch, body, .. } => {
if let Some(s) = sketch {
f(S(s));
}
if let Some(b) = body {
f(B(b));
}
}
FeatureKind::Fillet { body, .. }
| FeatureKind::Chamfer { body, .. }
| FeatureKind::Shell { body, .. }
| FeatureKind::OffsetFace { body, .. }
| FeatureKind::DeleteFace { body, .. }
| FeatureKind::SheetFlange { body, .. }
| FeatureKind::SheetHem { body, .. }
| FeatureKind::SheetPerforate { body, .. }
| FeatureKind::SheetUnfold { body, .. }
| FeatureKind::Boss { body, .. }
| FeatureKind::Lip { body, .. }
| FeatureKind::SnapFit { body, .. }
| FeatureKind::Rest { body, .. }
| FeatureKind::SurfaceOffset { body, .. } => {
if let Some(b) = body {
f(B(b));
}
}
FeatureKind::SurfaceUntrim { face, .. } => f(B(& $($m)? face.body)),
FeatureKind::SurfaceExtend { body, up_to, .. } => {
f(B(body));
match up_to {
Some(crate::ExtendTo::Plane(plane)) => $plane(plane, f),
Some(crate::ExtendTo::Face(t)) => f(B(& $($m)? t.body)),
Some(crate::ExtendTo::Bounds) | None => {}
}
}
FeatureKind::SheetConvert { body, .. }
| FeatureKind::SurfaceRuled { body, .. }
| FeatureKind::Unstitch { body, .. }
| FeatureKind::ReverseNormal { body, .. }
| FeatureKind::MeshConvert { body, .. } => f(B(body)),
FeatureKind::Combine { target, tools, .. } => {
f(B(target));
for b in tools {
f(B(b));
}
}
FeatureKind::Thicken { bodies, targets, .. } => {
for b in bodies {
f(B(b));
}
for b in targets {
f(B(b));
}
}
FeatureKind::BodyTransfer { bodies, .. }
| FeatureKind::Scale { bodies, .. }
| FeatureKind::BoundingSolid { bodies, .. }
| FeatureKind::Align { bodies, .. }
| FeatureKind::Remove { bodies }
| FeatureKind::Stitch { bodies, .. }
| FeatureKind::Move { bodies, .. } => {
for b in bodies {
f(B(b));
}
}
FeatureKind::BoundaryFill { tools, .. } => {
for b in tools {
f(B(b));
}
}
// Construction axes and points refer to geometry, which copies remap on their own
// (`Feature::remap_clone`).
FeatureKind::ConstructionAxis { .. }
| FeatureKind::ConstructionPoint { .. }
| FeatureKind::Box { .. }
| FeatureKind::Cylinder { .. }
| FeatureKind::Sphere { .. }
| FeatureKind::Torus { .. }
| FeatureKind::Thread { .. }
| FeatureKind::Import { .. }
| FeatureKind::MeshImport { .. } => {}
}
}
};
}
impl FeatureKind {
feature_walk!(walk_refs, plane_refs);
feature_walk!(walk_refs_mut, plane_refs_mut, mut);
/// Sketches the feature is built from (profiles, paths, points): deleting one of them
/// deletes the feature.
pub fn input_sketches(&self) -> Vec<u64> {