Skip to main content

geop_ops_model/
describe.rs

1//! [`PartDescription`]: a part's topology in terms of names only.
2
3use std::collections::BTreeMap;
4
5use geop_core_math::geop_error::GeopResult;
6use geop_core_topology::{CoedgeGeometry, Sense, boundary::BoundaryType};
7use serde::{Deserialize, Serialize};
8
9use super::{NamedModel, ids::RefId};
10
11/// A face's boundary loops, each as the coedges it runs through: `+E` / `-E`
12/// for edge `E` traversed forwards / backwards, `@V` for a degenerate
13/// coedge sitting at vertex `V` (a pole).
14#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
15pub struct FaceDescription {
16    pub outer: Vec<String>,
17    pub holes: Vec<Vec<String>>,
18}
19
20#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
21pub struct EdgeDescription {
22    pub start: String,
23    pub end: String,
24}
25
26/// Everything about a part's topology that its names can express, and the
27/// positions of its vertices — with no internal id anywhere, so two parts
28/// built by the same program describe identically however their ids came
29/// out, and a description diffs well as text. Loops start at their smallest
30/// entry and holes are sorted, since neither has a first element of its own.
31#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
32pub struct PartDescription {
33    /// Every solid's shells, each as its sorted face names.
34    pub solids: BTreeMap<String, Vec<Vec<String>>>,
35    pub faces: BTreeMap<String, FaceDescription>,
36    pub edges: BTreeMap<String, EdgeDescription>,
37    pub vertices: BTreeMap<String, [f64; 3]>,
38}
39
40impl PartDescription {
41    pub fn of(part: &NamedModel) -> GeopResult<Self> {
42        let model = part.topology();
43        let name = |id: RefId| part.name(id).map(str::to_string);
44        let describe_loop = |boundary: BoundaryType| -> GeopResult<Vec<String>> {
45            let mut entries = match boundary {
46                BoundaryType::Vertex(v) => vec![format!("@{}", name(v.into())?)],
47                BoundaryType::Loop(anchor) => model
48                    .iterate_loop_coedges(anchor)
49                    .map(|c| {
50                        let coedge = model.get_coedge(c)?;
51                        Ok(match coedge.geometry {
52                            CoedgeGeometry::Edge(e) => match coedge.sense {
53                                Sense::Forward => format!("+{}", name(e.into())?),
54                                Sense::Reversed => format!("-{}", name(e.into())?),
55                            },
56                            CoedgeGeometry::Vertex(v) => format!("@{}", name(v.into())?),
57                        })
58                    })
59                    .collect::<GeopResult<Vec<_>>>()?,
60            };
61            if let Some(first) = (0..entries.len()).min_by_key(|&i| &entries[i]) {
62                entries.rotate_left(first);
63            }
64            Ok(entries)
65        };
66
67        let mut solids = BTreeMap::new();
68        for (&id, solid) in &model.solids {
69            let shells = solid
70                .shells
71                .iter()
72                .map(|&shell| {
73                    let mut faces = model
74                        .get_shell(shell)?
75                        .faces
76                        .iter()
77                        .map(|&f| name(f.into()))
78                        .collect::<GeopResult<Vec<_>>>()?;
79                    faces.sort();
80                    Ok(faces)
81                })
82                .collect::<GeopResult<Vec<_>>>()?;
83            solids.insert(name(id.into())?, shells);
84        }
85        let mut faces = BTreeMap::new();
86        for (&id, face) in &model.faces {
87            let mut holes = face
88                .holes
89                .iter()
90                .map(|&h| describe_loop(h))
91                .collect::<GeopResult<Vec<_>>>()?;
92            holes.sort();
93            faces.insert(
94                name(id.into())?,
95                FaceDescription {
96                    outer: describe_loop(face.outer)?,
97                    holes,
98                },
99            );
100        }
101        let mut edges = BTreeMap::new();
102        for (&id, edge) in &model.edges {
103            edges.insert(
104                name(id.into())?,
105                EdgeDescription {
106                    start: name(edge.start_vertex.into())?,
107                    end: name(edge.end_vertex.into())?,
108                },
109            );
110        }
111        let mut vertices = BTreeMap::new();
112        for (&id, vertex) in &model.vertices {
113            let p = vertex.point;
114            vertices.insert(name(id.into())?, p.to_f64_array());
115        }
116        Ok(Self {
117            solids,
118            faces,
119            edges,
120            vertices,
121        })
122    }
123}