geop_core_topology/edit/
assemble_solid.rs1use std::collections::HashSet;
2
3use crate::{
4 CoedgeGeometry, CoedgeId, EdgeId, FaceId, Model, Shell, Solid, SolidId, VertexId,
5 boundary::BoundaryType,
6};
7use geop_core_math::{
8 geop_error::{GeopError, GeopResult},
9 scalars::Scalar,
10};
11
12impl<S: Scalar> Model<S> {
13 pub fn assemble_solid(
26 &mut self,
27 consumed: &[SolidId],
28 keep: &[FaceId],
29 ) -> GeopResult<Option<SolidId>> {
30 let mut consumed_faces: HashSet<FaceId> = HashSet::new();
31 for &solid in consumed {
32 consumed_faces.extend(self.solid_faces(solid)?);
33 }
34 if let Some(face) = keep.iter().find(|f| !consumed_faces.contains(f)) {
35 return Err(GeopError::new(format!(
36 "Model::assemble_solid: face {face} is to be kept, but belongs to none of the consumed solids {consumed:?}"
37 )));
38 }
39 for &solid in consumed {
40 for shell in self.get_solid(solid)?.shells.clone() {
41 self.shells.remove(&shell);
42 }
43 self.solids.remove(&solid);
44 }
45
46 let kept: HashSet<FaceId> = keep.iter().copied().collect();
47 self.faces
48 .retain(|id, _| !consumed_faces.contains(id) || kept.contains(id));
49
50 if keep.is_empty() {
51 self.prune_unreachable();
52 return Ok(None);
53 }
54
55 let shell_id = self.insert_shell(Shell {
56 faces: keep.to_vec(),
57 solid: SolidId(0),
58 });
59 let solid_id = self.insert_solid(Solid {
60 shells: vec![shell_id],
61 });
62 self.get_shell_mut(shell_id)?.solid = solid_id;
63 for &face_id in keep {
64 self.get_face_mut(face_id)?.shell = shell_id;
65 }
66
67 self.prune_unreachable();
68 Ok(Some(solid_id))
69 }
70
71 pub fn merge_solids(&mut self, into: SolidId, from: SolidId) -> GeopResult<()> {
76 if into == from {
77 return Err(GeopError::new(format!(
78 "Model::merge_solids: refusing to merge solid {into} into itself"
79 )));
80 }
81 self.get_solid(into)?;
82 let shells = self.get_solid(from)?.shells.clone();
83 for &shell in &shells {
84 self.get_shell_mut(shell)?.solid = into;
85 }
86 self.solids.remove(&from);
87 self.get_solid_mut(into)?.shells.extend(shells);
88 Ok(())
89 }
90
91 fn prune_unreachable(&mut self) {
94 let live_faces: Vec<FaceId> = self.faces.keys().copied().collect();
95 let live_coedges: HashSet<CoedgeId> = live_faces
96 .iter()
97 .flat_map(|&f| self.iterate_face_coedges(f).collect::<Vec<_>>())
98 .collect();
99 self.coedges.retain(|id, _| live_coedges.contains(id));
100
101 let live_edges: HashSet<EdgeId> = self
102 .coedges
103 .values()
104 .filter_map(|c| c.edge().ok())
105 .collect();
106 self.edges.retain(|id, _| live_edges.contains(id));
107
108 let mut live_vertices: HashSet<VertexId> = self
109 .edges
110 .values()
111 .flat_map(|e| [e.start_vertex, e.end_vertex])
112 .collect();
113 for coedge in self.coedges.values() {
114 if let CoedgeGeometry::Vertex(v) = coedge.geometry {
115 live_vertices.insert(v);
116 }
117 }
118 for face in self.faces.values() {
119 for boundary in face.boundaries() {
120 if let BoundaryType::Vertex(v) = boundary {
121 live_vertices.insert(v);
122 }
123 }
124 }
125 self.vertices.retain(|id, _| live_vertices.contains(id));
126 }
127}