Files
OneCAD/crates/doc/src/walk.rs
T

451 lines
15 KiB
Rust

//! The walks over a feature: its expressions (`params`), its references to the rest of the design
//! (`feature_refs`) and its geometry (`feature_geometry`). `#[derive(Walk)]`
//! (`solidcraft-derive`) generates them from `#[walk(...)]` roles on the fields of `FeatureKind`
//! and the types inside it, so what a field is, is written once, where it is declared. The
//! traits here carry the walks through the types the fields have: options, lists, arrays, planes
//! and construction definitions.
pub(crate) use solidcraft_geom::Vec3;
pub(crate) use crate::ProfileSel;
use crate::construct::{AxisDef, GeoRef, PointDef};
pub(crate) use crate::expr::Kind;
pub(crate) use crate::feature_geometry::Geo;
pub(crate) use crate::feature_refs::Ref;
pub(crate) use crate::params::ExprRole;
use crate::{BodyName, Dir, Expr, FacePick, FeatureName, PlaneRef, PointExpr, Position, SketchId};
/// An expression: its label, the expression, its kind and its role.
pub(crate) type ExprItem<'a> = (&'static str, &'a Expr, Kind, ExprRole);
pub(crate) type ExprItemMut<'a> = (&'static str, &'a mut Expr, Kind, ExprRole);
pub(crate) type RefItem<'a> = Ref<&'a SketchId, &'a FeatureName, &'a BodyName>;
pub(crate) type RefItemMut<'a> = Ref<&'a mut SketchId, &'a mut FeatureName, &'a mut BodyName>;
pub(crate) type GeoItem<'a> = Geo<&'a Vec3, &'a PlaneRef, &'a GeoRef, &'a PointExpr, &'a ProfileSel>;
pub(crate) type GeoItemMut<'a> = Geo<&'a mut Vec3, &'a mut PlaneRef, &'a mut GeoRef, &'a mut PointExpr, &'a mut ProfileSel>;
/// A value's expressions, references and geometry, each walked in order. A type with none of
/// them implements it empty.
pub(crate) trait Walk {
fn walk_exprs<'a>(&'a self, _: &mut dyn FnMut(ExprItem<'a>)) {}
fn walk_exprs_mut<'a>(&'a mut self, _: &mut dyn FnMut(ExprItemMut<'a>)) {}
fn walk_refs<'a>(&'a self, _: &mut dyn FnMut(RefItem<'a>)) {}
fn walk_refs_mut<'a>(&'a mut self, _: &mut dyn FnMut(RefItemMut<'a>)) {}
fn walk_geometry<'a>(&'a self, _: &mut dyn FnMut(GeoItem<'a>)) {}
fn walk_geometry_mut<'a>(&'a mut self, _: &mut dyn FnMut(GeoItemMut<'a>)) {}
}
impl Walk for String {}
impl Walk for std::sync::Arc<str> {}
impl Walk for bool {}
impl Walk for u64 {}
impl Walk for i8 {}
impl Walk for f64 {}
impl Walk for ProfileSel {}
impl Walk for solidcraft_sketch::Sketch {}
impl Walk for solidcraft_sketch::PatternAlignment {}
impl Walk for solidcraft_kernel::StepNode {}
impl Walk for crate::document::Operation {}
impl Walk for solidcraft_kernel::Continuity {}
impl Walk for crate::document::Direction {}
impl Walk for crate::document::FilletFit {}
impl Walk for crate::document::MeshData {}
impl Walk for crate::document::RevolveConvention {}
impl Walk for crate::document::CoilSection {}
impl Walk for crate::document::ThinSide {}
impl Walk for crate::document::ExtendMode {}
impl Walk for crate::document::RuledMode {}
impl Walk for crate::document::RuledCorners {}
impl Walk for crate::document::ExtrudeConvention {}
/// A reference walks itself: a sketch, a feature or a body.
macro_rules! reference {
($t:ty, $role:ident) => {
impl Walk for $t {
fn walk_refs<'a>(&'a self, f: &mut dyn FnMut(RefItem<'a>)) {
f(Ref::$role(self))
}
fn walk_refs_mut<'a>(&'a mut self, f: &mut dyn FnMut(RefItemMut<'a>)) {
f(Ref::$role(self))
}
}
};
}
reference!(SketchId, Sketch);
reference!(FeatureName, Feature);
reference!(BodyName, Body);
/// A point walks itself with its role: a face pick, a position or a direction. As a plain
/// `Vec3` it also serves a field whose attribute gives it another role (`pinned`, `pick_if`).
macro_rules! point {
($t:ty, $role:ident) => {
impl Walk for $t {
fn walk_geometry<'a>(&'a self, f: &mut dyn FnMut(GeoItem<'a>)) {
f(Geo::$role(&self.0))
}
fn walk_geometry_mut<'a>(&'a mut self, f: &mut dyn FnMut(GeoItemMut<'a>)) {
f(Geo::$role(&mut self.0))
}
}
impl Each<Vec3> for $t {
fn each<'a>(&'a self, f: &mut dyn FnMut(&'a Vec3)) {
f(&self.0)
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(&'a mut Vec3)) {
f(&mut self.0)
}
}
};
}
point!(FacePick, Pick);
point!(Position, Pos);
point!(Dir, Dir);
/// Containers walk what they hold, in order.
macro_rules! walk_each {
($([$($g:tt)*] $t:ty),*) => {$(
impl<$($g)*> Walk for $t {
fn walk_exprs<'a>(&'a self, f: &mut dyn FnMut(ExprItem<'a>)) {
for t in self {
t.walk_exprs(f)
}
}
fn walk_exprs_mut<'a>(&'a mut self, f: &mut dyn FnMut(ExprItemMut<'a>)) {
for t in self {
t.walk_exprs_mut(f)
}
}
fn walk_refs<'a>(&'a self, f: &mut dyn FnMut(RefItem<'a>)) {
for t in self {
t.walk_refs(f)
}
}
fn walk_refs_mut<'a>(&'a mut self, f: &mut dyn FnMut(RefItemMut<'a>)) {
for t in self {
t.walk_refs_mut(f)
}
}
fn walk_geometry<'a>(&'a self, f: &mut dyn FnMut(GeoItem<'a>)) {
for t in self {
t.walk_geometry(f)
}
}
fn walk_geometry_mut<'a>(&'a mut self, f: &mut dyn FnMut(GeoItemMut<'a>)) {
for t in self {
t.walk_geometry_mut(f)
}
}
}
)*};
}
walk_each!([T: Walk] Option<T>, [T: Walk] Vec<T>, [T: Walk, const N: usize] [T; N]);
/// The values of a field with a role (`T`, an optional one or a list), in order.
pub(crate) trait Each<T> {
fn each<'a>(&'a self, f: &mut dyn FnMut(&'a T));
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(&'a mut T));
}
impl<T> Each<T> for T {
fn each<'a>(&'a self, f: &mut dyn FnMut(&'a T)) {
f(self)
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(&'a mut T)) {
f(self)
}
}
impl<T> Each<T> for Option<T> {
fn each<'a>(&'a self, f: &mut dyn FnMut(&'a T)) {
if let Some(t) = self {
f(t)
}
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(&'a mut T)) {
if let Some(t) = self {
f(t)
}
}
}
impl<T> Each<T> for Vec<T> {
fn each<'a>(&'a self, f: &mut dyn FnMut(&'a T)) {
for t in self {
f(t)
}
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(&'a mut T)) {
for t in self {
f(t)
}
}
}
impl<T, const N: usize> Each<T> for [T; N] {
fn each<'a>(&'a self, f: &mut dyn FnMut(&'a T)) {
for t in self {
f(t)
}
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(&'a mut T)) {
for t in self {
f(t)
}
}
}
impl<T, const N: usize> Each<T> for Option<[T; N]> {
fn each<'a>(&'a self, f: &mut dyn FnMut(&'a T)) {
if let Some(a) = self {
for t in a {
f(t)
}
}
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(&'a mut T)) {
if let Some(a) = self {
for t in a {
f(t)
}
}
}
}
impl<T> Each<T> for Option<Vec<T>> {
fn each<'a>(&'a self, f: &mut dyn FnMut(&'a T)) {
for t in self.iter().flatten() {
f(t)
}
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(&'a mut T)) {
for t in self.iter_mut().flatten() {
f(t)
}
}
}
/// The expressions of an expression field, each with its index and their count (for its label).
pub(crate) trait ExprField {
fn each<'a>(&'a self, f: &mut dyn FnMut(usize, usize, &'a Expr));
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(usize, usize, &'a mut Expr));
}
impl ExprField for Expr {
fn each<'a>(&'a self, f: &mut dyn FnMut(usize, usize, &'a Expr)) {
f(0, 1, self)
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(usize, usize, &'a mut Expr)) {
f(0, 1, self)
}
}
/// A position along an edge with its radius (a variable fillet's mid radii).
impl ExprField for (f64, Expr) {
fn each<'a>(&'a self, f: &mut dyn FnMut(usize, usize, &'a Expr)) {
f(0, 1, &self.1)
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(usize, usize, &'a mut Expr)) {
f(0, 1, &mut self.1)
}
}
impl<T: ExprField> ExprField for Option<T> {
fn each<'a>(&'a self, f: &mut dyn FnMut(usize, usize, &'a Expr)) {
if let Some(t) = self {
t.each(f)
}
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(usize, usize, &'a mut Expr)) {
if let Some(t) = self {
t.each_mut(f)
}
}
}
/// Lists and arrays number their elements.
macro_rules! expr_list {
($([$($g:tt)*] $t:ty),*) => {$(
impl<$($g)*> ExprField for $t {
fn each<'a>(&'a self, f: &mut dyn FnMut(usize, usize, &'a Expr)) {
let n = self.len();
for (i, t) in self.iter().enumerate() {
t.each(&mut |_, _, e| f(i, n, e));
}
}
fn each_mut<'a>(&'a mut self, f: &mut dyn FnMut(usize, usize, &'a mut Expr)) {
let n = self.len();
for (i, t) in self.iter_mut().enumerate() {
t.each_mut(&mut |_, _, e| f(i, n, e));
}
}
}
)*};
}
expr_list!([T: ExprField] Vec<T>, [T: ExprField, const N: usize] [T; N]);
/// An expression's label: one for every element, one per element, or worked out from the
/// element's index and the count.
pub(crate) trait Label {
fn at(&self, i: usize, n: usize) -> &'static str;
}
impl Label for &'static str {
fn at(&self, _: usize, _: usize) -> &'static str {
self
}
}
impl<const N: usize> Label for [&'static str; N] {
fn at(&self, i: usize, _: usize) -> &'static str {
self.get(i).copied().unwrap_or("")
}
}
impl<F: Fn(usize, usize) -> &'static str> Label for F {
fn at(&self, i: usize, n: usize) -> &'static str {
self(i, n)
}
}
/// The offsets and angles of a plane, outermost first, and an explicit origin's coordinates
/// (nesting is capped like every walk over a plane), the construction plane it is built on, and
/// the plane itself.
impl Walk for PlaneRef {
fn walk_exprs<'a>(&'a self, f: &mut dyn FnMut(ExprItem<'a>)) {
let mut p = self;
for _ in 0..64 {
match p {
PlaneRef::Offset { base, distance } => {
f(("Offset", distance, Kind::Length, ExprRole::Input));
p = base;
}
PlaneRef::AtAngle { base, angle, .. } => {
f(("Angle", angle, Kind::Angle, ExprRole::Input));
p = base;
}
PlaneRef::AlongPath { t, .. } => return f(("Distance (0 to 1)", t, Kind::Unitless, ExprRole::Input)),
PlaneRef::Placed { origin, .. } => {
for (label, e) in ["X", "Y", "Z"].into_iter().zip(origin) {
f((label, e, Kind::Length, ExprRole::Placement));
}
return;
}
_ => return,
}
}
}
fn walk_exprs_mut<'a>(&'a mut self, f: &mut dyn FnMut(ExprItemMut<'a>)) {
let mut p = self;
for _ in 0..64 {
match p {
PlaneRef::Offset { base, distance } => {
f(("Offset", distance, Kind::Length, ExprRole::Input));
p = base;
}
PlaneRef::AtAngle { base, angle, .. } => {
f(("Angle", angle, Kind::Angle, ExprRole::Input));
p = base;
}
PlaneRef::AlongPath { t, .. } => return f(("Distance (0 to 1)", t, Kind::Unitless, ExprRole::Input)),
PlaneRef::Placed { origin, .. } => {
for (label, e) in ["X", "Y", "Z"].into_iter().zip(origin) {
f((label, e, Kind::Length, ExprRole::Placement));
}
return;
}
_ => return,
}
}
}
fn walk_refs<'a>(&'a self, f: &mut dyn FnMut(RefItem<'a>)) {
let mut p = self;
for _ in 0..64 {
match p {
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => p = base,
PlaneRef::Construction { name } => return f(Ref::Feature(name)),
_ => return,
}
}
}
fn walk_refs_mut<'a>(&'a mut self, f: &mut dyn FnMut(RefItemMut<'a>)) {
let mut p = self;
for _ in 0..64 {
match p {
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => p = base,
PlaneRef::Construction { name } => return f(Ref::Feature(name)),
_ => return,
}
}
}
fn walk_geometry<'a>(&'a self, f: &mut dyn FnMut(GeoItem<'a>)) {
f(Geo::Plane(self))
}
fn walk_geometry_mut<'a>(&'a mut self, f: &mut dyn FnMut(GeoItemMut<'a>)) {
f(Geo::Plane(self))
}
}
/// Construction axes and points: the expressions of the planes they are built from (and a point
/// along a path's distance), and their references to the model. They name no sketches, features
/// or bodies: copies remap their geometry references on their own (`Feature::remap_clone`).
fn plane_exprs<'a>(refs: Vec<&'a GeoRef>, f: &mut dyn FnMut(ExprItem<'a>)) {
for r in refs {
if let GeoRef::Plane { plane } = r {
plane.walk_exprs(f);
}
}
}
fn plane_exprs_mut<'a>(refs: Vec<&'a mut GeoRef>, f: &mut dyn FnMut(ExprItemMut<'a>)) {
for r in refs {
if let GeoRef::Plane { plane } = r {
plane.walk_exprs_mut(f);
}
}
}
macro_rules! construct_geometry {
() => {
fn walk_geometry<'a>(&'a self, f: &mut dyn FnMut(GeoItem<'a>)) {
for r in self.refs() {
f(Geo::Construct(r));
}
}
fn walk_geometry_mut<'a>(&'a mut self, f: &mut dyn FnMut(GeoItemMut<'a>)) {
for r in self.refs_mut() {
f(Geo::Construct(r));
}
}
};
}
impl Walk for AxisDef {
fn walk_exprs<'a>(&'a self, f: &mut dyn FnMut(ExprItem<'a>)) {
plane_exprs(self.refs(), f)
}
fn walk_exprs_mut<'a>(&'a mut self, f: &mut dyn FnMut(ExprItemMut<'a>)) {
plane_exprs_mut(self.refs_mut(), f)
}
construct_geometry!();
}
impl Walk for PointDef {
fn walk_exprs<'a>(&'a self, f: &mut dyn FnMut(ExprItem<'a>)) {
match self {
PointDef::AlongPath { t, .. } => f(("Distance (0 to 1)", t, Kind::Unitless, ExprRole::Input)),
other => plane_exprs(other.refs(), f),
}
}
fn walk_exprs_mut<'a>(&'a mut self, f: &mut dyn FnMut(ExprItemMut<'a>)) {
match self {
PointDef::AlongPath { t, .. } => f(("Distance (0 to 1)", t, Kind::Unitless, ExprRole::Input)),
other => plane_exprs_mut(other.refs_mut(), f),
}
}
construct_geometry!();
}