Skip to main content

geop_ops_model/spacing/
references.rs

1//! What instances are laid out along, round or at, from whichever element
2//! gives it — of the part itself, or of a part placed in it, where it is
3//! placed: [`TurnAxis`], a line or what a round element turns around;
4//! [`PatternDirection`], a line or a plane's normal; [`PointsRef`], a point
5//! or a sketch's points of its own.
6
7use std::collections::BTreeSet;
8
9use geop_core_editor::one_of;
10use geop_core_geometry::shape::Axis;
11use geop_core_math::{
12    geop_error::{GeopError, GeopResult, WithContext},
13    primitives::Motion,
14    vector::Vector3,
15    with_context,
16};
17use geop_core_program::{element::ElementReference, placed::InInstance, reader::Reader};
18use geop_core_sketch::CurveKind;
19
20use crate::{
21    Line, NamedModel, Plane, Point, Round,
22    datums::Datums,
23    parameters::located,
24    sketch::{PlacedSketch, SketchRef},
25};
26
27/// The motion placing what is in the instance `placed` is of where the
28/// part `reader` reads has it: none for the part's own.
29fn motion_of<T>(reader: &Reader, placed: &InInstance<T>) -> GeopResult<Motion> {
30    Ok(located(reader, &placed.instance)?.motion())
31}
32
33one_of! {
34    /// An axis to turn about: a line — a straight edge, a datum axis or a
35    /// frame's, a sketch's line — or what a round element turns around: a
36    /// circular edge, a cylindrical or conical face, a sketch's circle or
37    /// arc.
38    pub enum TurnAxis -> Axis {
39        Edge(ElementReference<NamedModel, Line>),
40        Round(ElementReference<NamedModel, Round>),
41        Datum(ElementReference<Datums, Line>),
42        Sketch(ElementReference<PlacedSketch, Line>),
43        SketchRound(ElementReference<PlacedSketch, Round>),
44    }
45}
46
47impl TurnAxis {
48    /// The axis, where the part `reader` reads has it: of the part itself,
49    /// or of a part placed in it, moved where that is.
50    pub fn placed(&self, reader: &Reader) -> GeopResult<Axis> {
51        let axis = self.resolve_placed(reader)?;
52        Ok(axis.element.transform(&motion_of(reader, &axis)?))
53    }
54}
55
56one_of! {
57    /// A direction to go along: a line — a straight edge, a datum axis or
58    /// a frame's, a sketch's line — or a plane, along its normal: a flat
59    /// face, a datum plane, a sketch's plane.
60    pub enum PatternDirection {
61        Edge(ElementReference<NamedModel, Line>),
62        Face(ElementReference<NamedModel, Plane>),
63        Datum(ElementReference<Datums, Line>),
64        DatumPlane(ElementReference<Datums, Plane>),
65        Sketch(ElementReference<PlacedSketch, Line>),
66        SketchPlane(ElementReference<PlacedSketch, Plane>),
67    }
68}
69
70impl PatternDirection {
71    /// The unit vector it goes along, where the part `reader` reads has
72    /// it: a line's direction, a plane's normal — of the part itself, or of
73    /// a part placed in it, turned as that is.
74    pub fn direction(&self, reader: &Reader) -> GeopResult<Vector3> {
75        let line = |placed: InInstance<Axis>| -> GeopResult<Vector3> {
76            Ok(motion_of(reader, &placed)?.rotate(&placed.element.direction))
77        };
78        let plane = |placed: InInstance<_>| -> GeopResult<Vector3> {
79            let motion = motion_of(reader, &placed)?;
80            let frame: geop_core_math::primitives::CoordinateSystem = placed.element;
81            Ok(motion.rotate(frame.w()))
82        };
83        match self {
84            PatternDirection::Edge(r) => line(reader.retrieve_placed(r)?),
85            PatternDirection::Datum(r) => line(reader.retrieve_placed(r)?),
86            PatternDirection::Sketch(r) => line(reader.retrieve_placed(r)?),
87            PatternDirection::Face(r) => plane(reader.retrieve_placed(r)?),
88            PatternDirection::DatumPlane(r) => plane(reader.retrieve_placed(r)?),
89            PatternDirection::SketchPlane(r) => plane(reader.retrieve_placed(r)?),
90        }
91    }
92}
93
94one_of! {
95    /// Where something goes: a vertex, a datum point or a frame's origin,
96    /// a sketch's point — or a sketch, standing for its points that no
97    /// line, arc or spline runs through.
98    pub enum PointsRef {
99        Vertex(ElementReference<NamedModel, Point>),
100        Datum(ElementReference<Datums, Point>),
101        SketchPoint(ElementReference<PlacedSketch, Point>),
102        Sketch(SketchRef),
103    }
104}
105
106/// The points `refs` stand for, where the part `reader` reads has them —
107/// of the part itself, or of a part placed in it, moved where that is —
108/// each by a name of its own: a sketch's point `K,p3` for the point `p3`
109/// of the sketch `K`, else the point's name. A sketch stands for its points
110/// no line, arc or spline runs through: points placed on their own, and
111/// circles' centres. One picked twice is refused.
112pub fn resolve_points(reader: &Reader, refs: &[PointsRef]) -> GeopResult<Vec<(String, Vector3)>> {
113    let mut found = Vec::new();
114    for point in refs {
115        let ctx = with_context!("the point {point:?}");
116        match point {
117            PointsRef::Sketch(sketch) => {
118                let placed = reader.retrieve_placed(sketch).with_context(ctx)?;
119                let motion = motion_of(reader, &placed).with_context(ctx)?;
120                let (name, sketch) = (&placed.element.name, &placed.element.sketch);
121                let on_curves: BTreeSet<_> = sketch
122                    .curves
123                    .values()
124                    .filter(|c| !matches!(c.kind, CurveKind::Circle { .. }))
125                    .flat_map(|c| c.points())
126                    .collect();
127                let geometry = sketch.enclose().with_context(ctx)?;
128                let before = found.len();
129                for (&id, p) in &sketch.points {
130                    if !p.fixed && !on_curves.contains(&id) {
131                        let at = placed.element.plane.uv_to_xyz(&geometry.points[&id]);
132                        found.push((format!("{name},{id}"), motion.apply(&at)));
133                    }
134                }
135                if found.len() == before {
136                    return Err(GeopError::new(format!(
137                        "sketch {name:?} has no points of its own: place points in it, or pick them one by one"
138                    )));
139                }
140            }
141            PointsRef::SketchPoint(at) => {
142                let whole = SketchRef::whole(at.target().clone());
143                let sketch = &reader
144                    .retrieve_placed(&whole)
145                    .with_context(ctx)?
146                    .element
147                    .name;
148                let p = reader.retrieve_placed(at).with_context(ctx)?;
149                let motion = motion_of(reader, &p).with_context(ctx)?;
150                found.push((format!("{sketch},{}", at.name()), motion.apply(&p.element)));
151            }
152            PointsRef::Vertex(at) => {
153                let p = reader.retrieve_placed(at).with_context(ctx)?;
154                let motion = motion_of(reader, &p).with_context(ctx)?;
155                found.push((at.name().to_string(), motion.apply(&p.element)));
156            }
157            PointsRef::Datum(at) => {
158                let p = reader.retrieve_placed(at).with_context(ctx)?;
159                let motion = motion_of(reader, &p).with_context(ctx)?;
160                found.push((at.name().to_string(), motion.apply(&p.element)));
161            }
162        }
163    }
164    let mut seen = BTreeSet::new();
165    for (scope, _) in &found {
166        if !seen.insert(scope) {
167            return Err(GeopError::new(format!("the point {scope} is picked twice")));
168        }
169    }
170    Ok(found)
171}