1use geop_core_math::{
5 geop_error::{GeopError, GeopResult},
6 scalars::Scalar,
7};
8use geop_core_topology::{CoedgeId, EdgeId, FaceId, Sense, SolidId, VertexId};
9
10use super::Part;
11use super::ids::{DatumId, RefId, SketchId};
12
13impl<S: Scalar> Part<S> {
14 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 solid_names(&self) -> Vec<String> {
65 let mut solids: Vec<SolidId> = self.topology().solids.keys().copied().collect();
66 solids.sort_by_key(|s| s.0);
67 solids
68 .into_iter()
69 .filter_map(|s| self.name_of(s).map(str::to_string))
70 .collect()
71 }
72
73 pub fn sketch_names(&self) -> Vec<String> {
75 self.sketches()
76 .filter_map(|(id, _)| self.name_of(id).map(str::to_string))
77 .collect()
78 }
79
80 pub fn datum_id(&self, name: &str) -> GeopResult<DatumId> {
81 self.named(name, "datum", |r| match r {
82 RefId::Datum(id) => Some(id),
83 _ => None,
84 })
85 }
86
87 pub fn coedge_id(&self, edge_name: &str, face_name: &str) -> GeopResult<CoedgeId> {
95 let edge = self.edge_id(edge_name)?;
96 let face = self.face_id(face_name)?;
97 let mut on_face = self
98 .topology
99 .coedges_of_edge(edge)
100 .into_iter()
101 .filter(|&c| {
102 self.topology
103 .get_coedge(c)
104 .map(|co| co.face == face)
105 .unwrap_or(false)
106 });
107 let found = on_face.next().ok_or_else(|| {
108 GeopError::new(format!(
109 "edge {edge_name:?} has no coedge on face {face_name:?}"
110 ))
111 })?;
112 if on_face.next().is_some() {
113 return Err(GeopError::new(format!(
114 "edge {edge_name:?} has more than one coedge on face {face_name:?} — use coedge_id_with_sense to disambiguate"
115 )));
116 }
117 Ok(found)
118 }
119
120 pub fn coedge_id_with_sense(
125 &self,
126 edge_name: &str,
127 face_name: &str,
128 sense: Sense,
129 ) -> GeopResult<CoedgeId> {
130 let edge = self.edge_id(edge_name)?;
131 let face = self.face_id(face_name)?;
132 self.topology
133 .coedges_of_edge(edge)
134 .into_iter()
135 .find(|&c| {
136 self.topology
137 .get_coedge(c)
138 .map(|co| co.face == face && co.sense == sense)
139 .unwrap_or(false)
140 })
141 .ok_or_else(|| {
142 GeopError::new(format!(
143 "edge {edge_name:?} has no {sense:?} coedge on face {face_name:?}"
144 ))
145 })
146 }
147}