geop_ops/operation/
aspects.rs1use geop_core_geometry::{
8 nurb_curve::NurbCurve3D,
9 shape::{Arc, Axis},
10};
11use geop_core_math::{
12 geop_error::{GeopResult, WithContext},
13 primitives::{CoordinateSystem, DatumKind},
14 scalars::Scalar,
15 vector::Vector3,
16 with_context,
17};
18use geop_core_sketch::CurveKind;
19use serde::Serialize;
20
21use super::EntityRef;
22use crate::Part;
23
24#[derive(Clone, Debug, Default)]
29pub struct Aspects<S: Scalar> {
30 pub point: Option<Vector3<S>>,
31 pub line: Option<Axis<S>>,
33 pub plane: Option<CoordinateSystem<S>>,
36 pub arc: Option<Arc<S>>,
37 pub round: Option<Axis<S>>,
40 pub curve: Option<NurbCurve3D<S>>,
42 pub frame: Option<CoordinateSystem<S>>,
44 pub solid: bool,
46 pub sketch: bool,
48}
49
50impl<S: Scalar> Aspects<S> {
51 pub fn of(entity: &EntityRef, part: &Part<S>) -> GeopResult<Self> {
53 let ctx = with_context!("resolving {entity}");
54 let mut g = Aspects::default();
55 match entity {
56 EntityRef::Vertex { name } => {
57 let id = part.vertex_id(name).with_context(ctx)?;
58 g.point = Some(part.topology().get_vertex(id).with_context(ctx)?.point);
59 }
60 EntityRef::Edge { name } => {
61 let id = part.edge_id(name).with_context(ctx)?;
62 let curve = part
63 .topology()
64 .get_edge(id)
65 .with_context(ctx)?
66 .curve
67 .clone();
68 g.line = curve.as_line().with_context(ctx)?;
69 g.arc = curve.as_arc().with_context(ctx)?;
70 g.round = g.arc.as_ref().map(|arc| arc.circle.axis());
71 g.curve = Some(curve);
72 }
73 EntityRef::Face { name } => {
74 let id = part.face_id(name).with_context(ctx)?;
75 let surface = &part.topology().get_face(id).with_context(ctx)?.surface;
76 g.plane = entity.resolve_plane(part).ok();
77 g.round = surface.axis_of_revolution().with_context(ctx)?;
78 }
79 EntityRef::Datum { .. } => {
80 let datum = entity.resolve_datum(part)?;
81 let frame = datum.frame;
82 match datum.kind {
83 DatumKind::Point => g.point = Some(*frame.origin()),
84 DatumKind::Axis => g.line = Some(Axis::try_new(*frame.origin(), *frame.w())?),
85 DatumKind::Plane => g.plane = Some(frame.clone()),
86 DatumKind::Frame => g.point = Some(*frame.origin()),
89 }
90 g.frame = Some(frame);
91 }
92 EntityRef::Solid { name } => {
93 part.solid_id(name).with_context(ctx)?;
94 g.solid = true;
95 }
96 EntityRef::Sketch { name } => {
97 part.sketch_id(name).with_context(ctx)?;
98 g.sketch = true;
99 }
100 EntityRef::SketchPoint { sketch, point } => {
101 let placed = part.sketch(part.sketch_id(sketch).with_context(ctx)?)?;
102 placed.sketch.point(*point).with_context(ctx)?;
103 let geometry = placed.sketch.enclose::<S>().with_context(ctx)?;
104 g.point = Some(placed.plane.uv_to_xyz(&geometry.points[point]));
105 }
106 EntityRef::SketchCurve { sketch, curve } => {
107 let placed = part.sketch(part.sketch_id(sketch).with_context(ctx)?)?;
108 let kind = &placed.sketch.curve(*curve).with_context(ctx)?.kind;
109 let geometry = placed.sketch.enclose::<S>().with_context(ctx)?;
110 let at = |p| placed.plane.uv_to_xyz(&geometry.points[&p]);
111 match *kind {
112 CurveKind::Line { start, end } => {
113 let (a, b) = (at(start), at(end));
114 g.line = Some(Axis::try_new(a, b.sub(&a)).with_context(ctx)?);
115 }
116 CurveKind::Circle { center, .. } => {
117 g.round = Some(Axis::try_new(at(center), *placed.plane.w())?);
118 }
119 CurveKind::Arc { .. } | CurveKind::Spline { .. } => {}
121 }
122 }
123 }
124 Ok(g)
125 }
126
127 pub fn roles(&self) -> Vec<Role> {
129 Role::ALL
130 .into_iter()
131 .filter(|role| role.fits(self))
132 .collect()
133 }
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
139#[serde(rename_all = "snake_case")]
140pub enum Role {
141 Point,
143 Line,
145 Plane,
147 Edge,
149 Circle,
151 Round,
154 Solid,
156 Sketch,
158}
159
160impl Role {
161 pub const ALL: [Role; 8] = [
162 Role::Point,
163 Role::Line,
164 Role::Plane,
165 Role::Edge,
166 Role::Circle,
167 Role::Round,
168 Role::Solid,
169 Role::Sketch,
170 ];
171
172 pub fn fits<S: Scalar>(self, aspects: &Aspects<S>) -> bool {
173 match self {
174 Role::Point => aspects.point.is_some(),
175 Role::Line => aspects.line.is_some(),
176 Role::Plane => aspects.plane.is_some(),
177 Role::Edge => aspects.curve.is_some(),
178 Role::Circle => aspects.arc.is_some(),
179 Role::Round => aspects.round.is_some(),
180 Role::Solid => aspects.solid,
181 Role::Sketch => aspects.sketch,
182 }
183 }
184
185 pub fn describe(self) -> &'static str {
187 match self {
188 Role::Point => "a point",
189 Role::Line => "a line",
190 Role::Plane => "a plane",
191 Role::Edge => "an edge",
192 Role::Circle => "a circular edge",
193 Role::Round => "a circular edge or a round face",
194 Role::Solid => "a solid",
195 Role::Sketch => "a sketch",
196 }
197 }
198
199 pub fn name(self) -> &'static str {
201 match self {
202 Role::Point => "point",
203 Role::Line => "line",
204 Role::Plane => "plane",
205 Role::Edge => "edge",
206 Role::Circle => "circle",
207 Role::Round => "round",
208 Role::Solid => "solid",
209 Role::Sketch => "sketch",
210 }
211 }
212}
213
214pub fn describe_roles(roles: &[Role], joiner: &str) -> String {
216 roles
217 .iter()
218 .map(|r| r.describe())
219 .collect::<Vec<_>>()
220 .join(joiner)
221}