Skip to main content

brep_kernel/brep/
topology_arena.rs

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/// Type-safe mutable topology used inside the kernel.  The JSON/WASM record
65/// model remains the stable wire format; conversion happens at that boundary.
66#[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// BREP private tests: 762085004b866dbc