Files
solidcraft-upstream/crates/doc/src/feature_refs.rs
T
JIJINBEI 86ea018d05 One walk over a feature's references (2/3, #876) (#988)
Part 2 of #876: one walk over the references a feature holds (the sketches it's built from, the features it names, the bodies it works on) replaces three hand-written matches, with a golden record covering every variant.
2026-10-14 11:14:25 -04:00

272 lines
10 KiB
Rust

//! 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.
use std::collections::BTreeMap;
use crate::{FeatureKind, PatternKind, PlaneRef};
/// 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).
pub(crate) enum Ref<S, N> {
Sketch(S),
Feature(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::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::SheetConvert { body, .. }
| FeatureKind::SurfaceExtend { 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> {
let mut v = Vec::new();
self.walk_refs(&mut |r| {
if let Ref::Sketch(id) = r {
v.push(*id);
}
});
v
}
/// Point references to sketches (by id) and features (by name) at their copies, or at a
/// feature's new name (`timeline.rename`).
pub fn remap_refs(&mut self, sketches: &BTreeMap<u64, u64>, names: &BTreeMap<String, String>) {
self.walk_refs_mut(&mut |r| match r {
Ref::Sketch(id) => {
if let Some(n) = sketches.get(id) {
*id = *n;
}
}
Ref::Feature(name) => {
if let Some(n) = names.get(name) {
*name = n.clone();
}
}
Ref::Body(_) => {}
});
}
/// Point references to bodies (by name) at their copies.
pub fn remap_bodies(&mut self, bodies: &BTreeMap<String, String>) {
self.walk_refs_mut(&mut |r| {
if let Ref::Body(name) = r
&& let Some(n) = bodies.get(name)
{
*name = n.clone();
}
});
}
}