Skip to main content

geop_core_part/
resolve.rs

1//! Looking an entity up by its name instead of its id — what every
2//! operation that refers to existing entities by name is built on.
3
4use geop_core_math::{
5    geop_error::{GeopError, GeopResult},
6    scalars::Scalar,
7};
8use geop_core_topology::{CoedgeId, EdgeId, FaceId, Sense, SolidId, VertexId};
9
10use crate::ids::{DatumId, RefId, SketchId};
11use crate::part::Part;
12
13impl<S: Scalar> Part<S> {
14    /// `name`'s id, checked to be the particular kind `extract` accepts.
15    fn named<T>(
16        &self,
17        name: &str,
18        kind: &str,
19        extract: impl Fn(RefId) -> Option<T>,
20    ) -> GeopResult<T> {
21        let id = self
22            .names
23            .id_of(name)
24            .ok_or_else(|| GeopError::new(format!("no entity is named {name:?}")))?;
25        extract(id).ok_or_else(|| GeopError::new(format!("{name:?} names {id}, not a {kind}")))
26    }
27
28    pub fn vertex_id(&self, name: &str) -> GeopResult<VertexId> {
29        self.named(name, "vertex", |r| match r {
30            RefId::Vertex(id) => Some(id),
31            _ => None,
32        })
33    }
34
35    pub fn edge_id(&self, name: &str) -> GeopResult<EdgeId> {
36        self.named(name, "edge", |r| match r {
37            RefId::Edge(id) => Some(id),
38            _ => None,
39        })
40    }
41
42    pub fn face_id(&self, name: &str) -> GeopResult<FaceId> {
43        self.named(name, "face", |r| match r {
44            RefId::Face(id) => Some(id),
45            _ => None,
46        })
47    }
48
49    pub fn solid_id(&self, name: &str) -> GeopResult<SolidId> {
50        self.named(name, "solid", |r| match r {
51            RefId::Solid(id) => Some(id),
52            _ => None,
53        })
54    }
55
56    pub fn sketch_id(&self, name: &str) -> GeopResult<SketchId> {
57        self.named(name, "sketch", |r| match r {
58            RefId::Sketch(id) => Some(id),
59            _ => None,
60        })
61    }
62
63    pub fn datum_id(&self, name: &str) -> GeopResult<DatumId> {
64        self.named(name, "datum", |r| match r {
65            RefId::Datum(id) => Some(id),
66            _ => None,
67        })
68    }
69
70    /// The unique coedge of `edge_name` lying on `face_name`. An edge shared
71    /// by two distinct faces has exactly one coedge per face, so this pair
72    /// identifies it unambiguously — except for a connector edge
73    /// [`geop_core_topology::Model::mve`] or
74    /// [`geop_core_topology::Model::mekr`] mints, both of whose coedges sit
75    /// on the very same face; resolve one of those with
76    /// [`Part::coedge_id_with_sense`] instead.
77    pub fn coedge_id(&self, edge_name: &str, face_name: &str) -> GeopResult<CoedgeId> {
78        let edge = self.edge_id(edge_name)?;
79        let face = self.face_id(face_name)?;
80        let mut on_face = self
81            .topology
82            .coedges_of_edge(edge)
83            .into_iter()
84            .filter(|&c| {
85                self.topology
86                    .get_coedge(c)
87                    .map(|co| co.face == face)
88                    .unwrap_or(false)
89            });
90        let found = on_face.next().ok_or_else(|| {
91            GeopError::new(format!(
92                "edge {edge_name:?} has no coedge on face {face_name:?}"
93            ))
94        })?;
95        if on_face.next().is_some() {
96            return Err(GeopError::new(format!(
97                "edge {edge_name:?} has more than one coedge on face {face_name:?} — use coedge_id_with_sense to disambiguate"
98            )));
99        }
100        Ok(found)
101    }
102
103    /// The coedge of `edge_name` on `face_name` with sense `sense` — needed
104    /// only for a connector edge whose two coedges both sit on one face (see
105    /// [`Part::coedge_id`]'s own doc comment), where the plain `(edge, face)`
106    /// pair is ambiguous.
107    pub fn coedge_id_with_sense(
108        &self,
109        edge_name: &str,
110        face_name: &str,
111        sense: Sense,
112    ) -> GeopResult<CoedgeId> {
113        let edge = self.edge_id(edge_name)?;
114        let face = self.face_id(face_name)?;
115        self.topology
116            .coedges_of_edge(edge)
117            .into_iter()
118            .find(|&c| {
119                self.topology
120                    .get_coedge(c)
121                    .map(|co| co.face == face && co.sense == sense)
122                    .unwrap_or(false)
123            })
124            .ok_or_else(|| {
125                GeopError::new(format!(
126                    "edge {edge_name:?} has no {sense:?} coedge on face {face_name:?}"
127                ))
128            })
129    }
130}