1use std::collections::HashSet;
6
7use geop_core_geometry::deform::Deformation;
8use geop_core_math::{
9 geop_error::{GeopError, GeopResult},
10 scalars::Scalar,
11 vector::Vector3,
12};
13use geop_core_topology::{
14 Body, Edge, EdgeId, FaceId, ShellId, SolidId, Vertex, VertexId,
15 build::{BodySpec, BuiltBody, SpecSources},
16};
17
18use super::NamedModel;
19use crate::ids::RefId;
20
21impl NamedModel {
22 pub fn insert_vertex(
26 &mut self,
27 point: Vector3,
28 name: impl Into<String>,
29 ) -> GeopResult<VertexId> {
30 let vertex = self.topology.insert_vertex(Vertex { point });
31 self.names.insert(vertex, name)?;
32 Ok(vertex)
33 }
34
35 pub fn insert_edge(&mut self, edge: Edge, name: impl Into<String>) -> GeopResult<EdgeId> {
39 let edge = self.topology.insert_edge(edge);
40 self.names.insert(edge, name)?;
41 Ok(edge)
42 }
43
44 pub fn merge_vertex(
47 &mut self,
48 vertex_into_id: VertexId,
49 vertex_deleted_id: VertexId,
50 ) -> GeopResult<()> {
51 self.topology
52 .merge_vertex(vertex_into_id, vertex_deleted_id)?;
53 self.names.remove(vertex_deleted_id);
54 Ok(())
55 }
56
57 pub fn merge_edge(
60 &mut self,
61 edge_into_id: EdgeId,
62 edge_deleted_id: EdgeId,
63 reversed: bool,
64 ) -> GeopResult<()> {
65 self.topology
66 .merge_edge(edge_into_id, edge_deleted_id, reversed)?;
67 self.names.remove(edge_deleted_id);
68 Ok(())
69 }
70
71 pub fn reverse_face(&mut self, face_id: FaceId) -> GeopResult<()> {
74 self.topology.reverse_face(face_id)
75 }
76
77 pub fn splice_edge_into_face(
84 &mut self,
85 edge_id: EdgeId,
86 face_id: FaceId,
87 max_nodes: usize,
88 min_subdivision_size: Scalar,
89 new_face_name: impl Into<String>,
90 ) -> GeopResult<Option<FaceId>> {
91 let new_face = self.topology.splice_edge_into_face(
92 edge_id,
93 face_id,
94 max_nodes,
95 min_subdivision_size,
96 )?;
97 if let Some(face) = new_face {
98 self.names.insert(face, new_face_name)?;
99 }
100 Ok(new_face)
101 }
102
103 pub fn split_edge_at_vertex(
107 &mut self,
108 edge_id: EdgeId,
109 edge_t: Scalar,
110 vertex_id: VertexId,
111 max_nodes: usize,
112 min_subdivision_size: Scalar,
113 new_edge_name: impl Into<String>,
114 ) -> GeopResult<EdgeId> {
115 let new_edge = self.topology.split_edge_at_vertex(
116 edge_id,
117 edge_t,
118 vertex_id,
119 max_nodes,
120 min_subdivision_size,
121 )?;
122 self.names.insert(new_edge, new_edge_name)?;
123 Ok(new_edge)
124 }
125
126 pub fn build_body(&mut self, spec: BodySpec, names: BodyNames) -> GeopResult<BuiltBody> {
130 let counts = [
131 (names.vertices.len(), spec.vertices.len(), "vertices"),
132 (names.edges.len(), spec.edges.len(), "edges"),
133 (names.faces.len(), spec.faces.len(), "faces"),
134 ];
135 if let Some((n, m, what)) = counts.iter().find(|(n, m, _)| n != m) {
136 return Err(GeopError::new(format!(
137 "NamedModel::build_body: {n} names for {m} {what}"
138 )));
139 }
140 if names.solid.is_some() != spec.solid {
141 return Err(GeopError::new(format!(
142 "NamedModel::build_body: a solid name {:?} for a body that {} a solid",
143 names.solid,
144 if spec.solid { "is" } else { "is not" }
145 )));
146 }
147 let all = names
148 .vertices
149 .iter()
150 .chain(&names.edges)
151 .chain(&names.faces)
152 .chain(&names.solid);
153 let mut seen = HashSet::new();
154 for name in all {
155 if !seen.insert(name) || self.names.is_taken(name) {
156 return Err(GeopError::new(format!(
157 "NamedModel::build_body: the name {name:?} is taken"
158 )));
159 }
160 }
161 let built = self.topology.build_body(spec)?;
162 for (&id, name) in built.vertices.iter().zip(names.vertices) {
163 self.names.insert(id, name)?;
164 }
165 for (&id, name) in built.edges.iter().zip(names.edges) {
166 self.names.insert(id, name)?;
167 }
168 for (&id, name) in built.faces.iter().zip(names.faces) {
169 self.names.insert(id, name)?;
170 }
171 if let (Some(id), Some(name)) = (built.solid, names.solid) {
172 self.names.insert(id, name)?;
173 }
174 Ok(built)
175 }
176
177 pub fn copy_faces(
183 &mut self,
184 faces: &[FaceId],
185 solid: Option<String>,
186 rename: impl Fn(&str) -> String,
187 ) -> GeopResult<BuiltBody> {
188 let (spec, sources) = self.topology().body_spec(faces, solid.is_some())?;
189 let names = BodyNames {
190 solid,
191 ..self.source_names(&sources)?.renamed(rename)
192 };
193 self.build_body(spec, names)
194 }
195
196 pub fn copy_body(
200 &mut self,
201 body: Body,
202 rename: impl Fn(&str) -> String,
203 ) -> GeopResult<BuiltBody> {
204 let (spec, names) = self.body_record(body)?;
205 self.build_body(spec, names.renamed(rename))
206 }
207
208 pub fn body_record(&self, body: Body) -> GeopResult<(BodySpec, BodyNames)> {
213 let mut faces = self.topology().body_faces(body)?;
215 faces.sort();
216 let solid = match body {
217 Body::Solid(solid) => Some(self.name(solid)?.to_string()),
218 Body::Sheet(_) => None,
219 };
220 let (mut spec, sources) = self.topology().body_spec(&faces, solid.is_some())?;
221 let mut shells: Vec<(ShellId, Vec<usize>)> = Vec::new();
224 for (index, &face) in sources.faces.iter().enumerate() {
225 let shell = self.topology().get_face(face)?.shell;
226 match shells.iter_mut().find(|(s, _)| *s == shell) {
227 Some((_, members)) => members.push(index),
228 None => shells.push((shell, vec![index])),
229 }
230 }
231 spec.shells = shells.into_iter().map(|(_, members)| members).collect();
232 let names = BodyNames {
233 solid,
234 ..self.source_names(&sources)?
235 };
236 Ok((spec, names))
237 }
238
239 pub fn source_names(&self, sources: &SpecSources) -> GeopResult<BodyNames> {
243 fn names<Id: Into<super::RefId> + Copy>(
244 model: &NamedModel,
245 ids: &[Id],
246 ) -> GeopResult<Vec<String>> {
247 (ids.iter())
248 .map(|&id| Ok(model.name(id)?.to_string()))
249 .collect()
250 }
251 Ok(BodyNames {
252 vertices: names(self, &sources.vertices)?,
253 edges: names(self, &sources.edges)?,
254 faces: names(self, &sources.faces)?,
255 solid: None,
256 })
257 }
258
259 pub fn merge_faces_on_one_surface(
272 &mut self,
273 body: Body,
274 edge_may_go: impl Fn(EdgeId) -> bool,
275 vertex_may_go: impl Fn(VertexId) -> bool,
276 ) -> GeopResult<Body> {
277 let (spec, names) = self.body_record(body)?;
278 let mut faces = self.topology().body_faces(body)?;
280 faces.sort();
281 let (_, sources) = self.topology().body_spec(&faces, false)?;
282 let (merged, map) = spec.merged(
283 |e| edge_may_go(sources.edges[e]),
284 |v| vertex_may_go(sources.vertices[v]),
285 )?;
286 if merged.faces.len() == spec.faces.len() && merged.edges.len() == spec.edges.len() {
287 return Ok(body);
288 }
289 let face_into: Vec<Option<usize>> = map.faces.iter().map(|&f| Some(f)).collect();
290 let faces = self.merged_names(&sources.faces, &face_into, merged.faces.len())?;
291 let edges = self.merged_names(&sources.edges, &map.edges, merged.edges.len())?;
292 let vertices =
293 self.merged_names(&sources.vertices, &map.vertices, merged.vertices.len())?;
294 let new_names = BodyNames {
295 vertices: vertices.names,
296 edges: edges.names.clone(),
297 faces: faces.names.clone(),
298 solid: names.solid.clone(),
299 };
300 self.delete_bodies(&[body])?;
301 let built = self.build_body(merged, new_names)?;
302 for merged in [faces, edges] {
303 for (alias, k) in &merged.aliases {
304 self.names.alias(alias.clone(), merged.names[*k].clone())?;
305 }
306 }
307 built.body()
308 }
309
310 fn merged_names<Id: Into<RefId> + Copy + Ord>(
315 &self,
316 ids: &[Id],
317 into: &[Option<usize>],
318 count: usize,
319 ) -> GeopResult<MergedNames> {
320 let mut by: Vec<Vec<Id>> = vec![Vec::new(); count];
321 for (&id, &into) in ids.iter().zip(into) {
322 if let Some(into) = into {
323 by[into].push(id);
324 }
325 }
326 let mut out = MergedNames::default();
327 for (k, mut ids) in by.into_iter().enumerate() {
328 ids.sort();
329 let Some((&oldest, rest)) = ids.split_first() else {
330 return Err(GeopError::new(
331 "NamedModel::merge_faces_on_one_surface: an entity made of nothing",
332 ));
333 };
334 out.names.push(self.name(oldest)?.to_string());
335 for &id in rest {
336 out.aliases.push((self.name(id)?.to_string(), k));
337 }
338 }
339 Ok(out)
340 }
341
342 pub fn transform_body(&mut self, body: Body, map: &dyn Deformation) -> GeopResult<()> {
345 self.topology.transform_body(body, map)
346 }
347
348 pub fn assemble_solid(
352 &mut self,
353 consumed: &[Body],
354 keep: &[FaceId],
355 solid_name: impl Into<String>,
356 ) -> GeopResult<Option<SolidId>> {
357 let solid = self.topology.assemble_solid(consumed, keep)?;
358 self.forget_dead_names();
359 if let Some(solid) = solid {
360 self.names.insert(solid, solid_name)?;
361 }
362 Ok(solid)
363 }
364
365 pub fn assemble_sheet(
368 &mut self,
369 consumed: &[Body],
370 keep: &[FaceId],
371 ) -> GeopResult<Option<ShellId>> {
372 let sheet = self.topology.assemble_sheet(consumed, keep)?;
373 self.forget_dead_names();
374 Ok(sheet)
375 }
376
377 pub fn delete_bodies(&mut self, bodies: &[Body]) -> GeopResult<()> {
380 self.assemble_sheet(bodies, &[])?;
381 Ok(())
382 }
383
384 pub fn replace_with_solid(
388 &mut self,
389 bodies: &[Body],
390 spec: BodySpec,
391 names: BodyNames,
392 ) -> GeopResult<SolidId> {
393 self.delete_bodies(bodies)?;
394 self.build_body(spec, names)?
395 .solid
396 .ok_or_else(|| GeopError::new("building the result made no solid"))
397 }
398
399 pub fn merge_solids(&mut self, into: SolidId, from: SolidId) -> GeopResult<()> {
402 self.topology.merge_solids(into, from)?;
403 self.names.remove(from);
404 Ok(())
405 }
406}
407
408#[derive(Default)]
412struct MergedNames {
413 names: Vec<String>,
414 aliases: Vec<(String, usize)>,
415}
416
417#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
421pub struct BodyNames {
422 pub vertices: Vec<String>,
423 pub edges: Vec<String>,
424 pub faces: Vec<String>,
425 pub solid: Option<String>,
426}
427
428impl BodyNames {
429 pub fn renamed(&self, rename: impl Fn(&str) -> String) -> BodyNames {
431 let all = |names: &[String]| names.iter().map(|n| rename(n)).collect();
432 BodyNames {
433 vertices: all(&self.vertices),
434 edges: all(&self.edges),
435 faces: all(&self.faces),
436 solid: self.solid.as_deref().map(&rename),
437 }
438 }
439}