1use crate::topology::{
2 BrepSolid, CoedgeRecord, EdgeRecord, FaceRecord, LoopRecord, ShellRecord, VertexRecord,
3};
4use crate::{NurbsCurve, NurbsSurface, Vec3};
5use rustc_hash::FxHashMap as HashMap;
6use slotmap::{new_key_type, SlotMap};
7
8new_key_type! {
9 pub struct VertexId;
10 pub struct EdgeId;
11 pub struct CoedgeId;
12 pub struct LoopId;
13 pub struct FaceId;
14 pub struct ShellId;
15}
16
17#[derive(Clone, Debug)]
18pub struct ArenaVertex {
19 pub wire_id: u64,
20 pub point: Vec3,
21}
22
23#[derive(Clone, Debug)]
24pub struct ArenaEdge {
25 pub wire_id: u64,
26 pub curve: NurbsCurve,
27 pub t0: f64,
28 pub t1: f64,
29 pub start: VertexId,
30 pub end: VertexId,
31 pub degenerate: bool,
32 pub name: Option<String>,
33}
34
35#[derive(Clone, Debug)]
36pub struct ArenaCoedge {
37 pub wire_id: u64,
38 pub edge: EdgeId,
39 pub forward: bool,
40 pub pcurve: NurbsCurve,
41}
42
43#[derive(Clone, Debug)]
44pub struct ArenaLoop {
45 pub wire_id: u64,
46 pub coedges: Vec<CoedgeId>,
47}
48
49#[derive(Clone, Debug)]
50pub struct ArenaFace {
51 pub wire_id: u64,
52 pub surface: NurbsSurface,
53 pub same_sense: bool,
54 pub loops: Vec<LoopId>,
55 pub name: Option<String>,
56}
57
58#[derive(Clone, Debug)]
59pub struct ArenaShell {
60 pub wire_id: u64,
61 pub faces: Vec<FaceId>,
62}
63
64#[derive(Clone, Debug)]
67pub struct TopologyArena {
68 pub wire_solid_id: u64,
69 pub genus: i64,
70 pub vertices: SlotMap<VertexId, ArenaVertex>,
71 pub edges: SlotMap<EdgeId, ArenaEdge>,
72 pub coedges: SlotMap<CoedgeId, ArenaCoedge>,
73 pub loops: SlotMap<LoopId, ArenaLoop>,
74 pub faces: SlotMap<FaceId, ArenaFace>,
75 pub shells: SlotMap<ShellId, ArenaShell>,
76}
77
78impl TopologyArena {
79 pub fn from_brep(solid: &BrepSolid) -> Result<Self, String> {
80 let mut arena = Self {
81 wire_solid_id: solid.id,
82 genus: solid.genus,
83 vertices: SlotMap::with_key(),
84 edges: SlotMap::with_key(),
85 coedges: SlotMap::with_key(),
86 loops: SlotMap::with_key(),
87 faces: SlotMap::with_key(),
88 shells: SlotMap::with_key(),
89 };
90 let mut vertex_ids = HashMap::<u64, VertexId>::default();
91 for vertex in &solid.vertices {
92 if vertex_ids.contains_key(&vertex.id) {
93 return Err(format!("duplicate vertex wire id {}", vertex.id));
94 }
95 let id = arena.vertices.insert(ArenaVertex {
96 wire_id: vertex.id,
97 point: vertex.point,
98 });
99 vertex_ids.insert(vertex.id, id);
100 }
101 let mut edge_ids = HashMap::<u64, EdgeId>::default();
102 for edge in &solid.edges {
103 if edge_ids.contains_key(&edge.id) {
104 return Err(format!("duplicate edge wire id {}", edge.id));
105 }
106 let start = *vertex_ids.get(&edge.start_vertex_id).ok_or_else(|| {
107 format!(
108 "edge {} references missing start vertex {}",
109 edge.id, edge.start_vertex_id
110 )
111 })?;
112 let end = *vertex_ids.get(&edge.end_vertex_id).ok_or_else(|| {
113 format!(
114 "edge {} references missing end vertex {}",
115 edge.id, edge.end_vertex_id
116 )
117 })?;
118 let id = arena.edges.insert(ArenaEdge {
119 wire_id: edge.id,
120 curve: edge.curve.clone(),
121 t0: edge.t0,
122 t1: edge.t1,
123 start,
124 end,
125 degenerate: edge.degenerate,
126 name: edge.name.clone(),
127 });
128 edge_ids.insert(edge.id, id);
129 }
130
131 let mut seen_shells = HashMap::<u64, ShellId>::default();
132 let mut seen_faces = HashMap::<u64, FaceId>::default();
133 let mut seen_loops = HashMap::<u64, LoopId>::default();
134 let mut seen_coedges = HashMap::<u64, CoedgeId>::default();
135 for shell in &solid.shells {
136 if seen_shells.contains_key(&shell.id) {
137 return Err(format!("duplicate shell wire id {}", shell.id));
138 }
139 let mut face_keys = Vec::with_capacity(shell.faces.len());
140 for face in &shell.faces {
141 if seen_faces.contains_key(&face.id) {
142 return Err(format!("duplicate face wire id {}", face.id));
143 }
144 let mut loop_keys = Vec::with_capacity(face.loops.len());
145 for loop_record in &face.loops {
146 if seen_loops.contains_key(&loop_record.id) {
147 return Err(format!("duplicate loop wire id {}", loop_record.id));
148 }
149 let mut coedge_keys = Vec::with_capacity(loop_record.coedges.len());
150 for coedge in &loop_record.coedges {
151 if seen_coedges.contains_key(&coedge.id) {
152 return Err(format!("duplicate coedge wire id {}", coedge.id));
153 }
154 let edge = *edge_ids.get(&coedge.edge_id).ok_or_else(|| {
155 format!(
156 "coedge {} references missing edge {}",
157 coedge.id, coedge.edge_id
158 )
159 })?;
160 let key = arena.coedges.insert(ArenaCoedge {
161 wire_id: coedge.id,
162 edge,
163 forward: coedge.forward,
164 pcurve: coedge.pcurve.clone(),
165 });
166 seen_coedges.insert(coedge.id, key);
167 coedge_keys.push(key);
168 }
169 let key = arena.loops.insert(ArenaLoop {
170 wire_id: loop_record.id,
171 coedges: coedge_keys,
172 });
173 seen_loops.insert(loop_record.id, key);
174 loop_keys.push(key);
175 }
176 let key = arena.faces.insert(ArenaFace {
177 wire_id: face.id,
178 surface: face.surface.clone(),
179 same_sense: face.same_sense,
180 loops: loop_keys,
181 name: face.name.clone(),
182 });
183 seen_faces.insert(face.id, key);
184 face_keys.push(key);
185 }
186 let key = arena.shells.insert(ArenaShell {
187 wire_id: shell.id,
188 faces: face_keys,
189 });
190 seen_shells.insert(shell.id, key);
191 }
192 Ok(arena)
193 }
194
195 pub fn to_brep(&self) -> Result<BrepSolid, String> {
196 let vertices = self
197 .vertices
198 .values()
199 .map(|vertex| VertexRecord {
200 id: vertex.wire_id,
201 point: vertex.point,
202 })
203 .collect();
204 let edges = self
205 .edges
206 .values()
207 .map(|edge| {
208 Ok(EdgeRecord {
209 id: edge.wire_id,
210 curve: edge.curve.clone(),
211 t0: edge.t0,
212 t1: edge.t1,
213 start_vertex_id: self
214 .vertices
215 .get(edge.start)
216 .ok_or_else(|| "arena edge has missing start vertex".to_string())?
217 .wire_id,
218 end_vertex_id: self
219 .vertices
220 .get(edge.end)
221 .ok_or_else(|| "arena edge has missing end vertex".to_string())?
222 .wire_id,
223 degenerate: edge.degenerate,
224 name: edge.name.clone(),
225 })
226 })
227 .collect::<Result<Vec<_>, String>>()?;
228 let shells = self
229 .shells
230 .values()
231 .map(|shell| {
232 let faces = shell
233 .faces
234 .iter()
235 .map(|face_id| {
236 let face = self
237 .faces
238 .get(*face_id)
239 .ok_or_else(|| "arena shell has missing face".to_string())?;
240 let loops = face
241 .loops
242 .iter()
243 .map(|loop_id| {
244 let loop_record = self
245 .loops
246 .get(*loop_id)
247 .ok_or_else(|| "arena face has missing loop".to_string())?;
248 let coedges = loop_record
249 .coedges
250 .iter()
251 .map(|coedge_id| {
252 let coedge =
253 self.coedges.get(*coedge_id).ok_or_else(|| {
254 "arena loop has missing coedge".to_string()
255 })?;
256 Ok(CoedgeRecord {
257 id: coedge.wire_id,
258 edge_id: self
259 .edges
260 .get(coedge.edge)
261 .ok_or_else(|| {
262 "arena coedge has missing edge".to_string()
263 })?
264 .wire_id,
265 forward: coedge.forward,
266 pcurve: coedge.pcurve.clone(),
267 })
268 })
269 .collect::<Result<Vec<_>, String>>()?;
270 Ok(LoopRecord {
271 id: loop_record.wire_id,
272 coedges,
273 })
274 })
275 .collect::<Result<Vec<_>, String>>()?;
276 Ok(FaceRecord {
277 id: face.wire_id,
278 surface: face.surface.clone(),
279 same_sense: face.same_sense,
280 loops,
281 name: face.name.clone(),
282 })
283 })
284 .collect::<Result<Vec<_>, String>>()?;
285 Ok(ShellRecord {
286 id: shell.wire_id,
287 faces,
288 })
289 })
290 .collect::<Result<Vec<_>, String>>()?;
291 Ok(BrepSolid {
292 id: self.wire_solid_id,
293 vertices,
294 edges,
295 shells,
296 genus: self.genus,
297 })
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304 use crate::{make_cylinder_brep, Vec3};
305
306 #[test]
307 fn typed_arena_round_trips_wire_topology() {
308 let solid =
309 make_cylinder_brep(Vec3::new(2.0, 3.0, 4.0), Vec3::new(0.0, 0.0, 1.0), 2.0, 7.0)
310 .unwrap();
311 let arena = TopologyArena::from_brep(&solid).unwrap();
312 let round_trip = arena.to_brep().unwrap();
313 assert_eq!(
314 serde_json::to_value(&round_trip).unwrap(),
315 serde_json::to_value(&solid).unwrap()
316 );
317 }
318
319 #[test]
320 fn typed_ids_cannot_alias_entity_kinds() {
321 let solid = crate::make_box_brep(Vec3::default(), 1.0, 1.0, 1.0).unwrap();
322 let arena = TopologyArena::from_brep(&solid).unwrap();
323 let vertex = arena.vertices.keys().next().unwrap();
324 let edge = arena.edges.keys().next().unwrap();
325 assert_ne!(format!("{vertex:?}"), format!("{edge:?}"));
326 }
327}