geop_core_part/
describe.rs1use std::collections::BTreeMap;
4
5use geop_core_math::{
6 geop_error::{GeopError, GeopResult},
7 scalars::Scalar,
8};
9use geop_core_topology::{CoedgeGeometry, Sense, boundary::BoundaryType};
10use serde::{Deserialize, Serialize};
11
12use crate::{ids::RefId, part::Part};
13
14#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
18pub struct FaceDescription {
19 pub outer: Vec<String>,
20 pub holes: Vec<Vec<String>>,
21}
22
23#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
24pub struct EdgeDescription {
25 pub start: String,
26 pub end: String,
27}
28
29#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
35pub struct PartDescription {
36 pub solids: BTreeMap<String, Vec<Vec<String>>>,
38 pub faces: BTreeMap<String, FaceDescription>,
39 pub edges: BTreeMap<String, EdgeDescription>,
40 pub vertices: BTreeMap<String, [f64; 3]>,
41 pub sketches: Vec<String>,
42 pub datums: Vec<String>,
43}
44
45impl PartDescription {
46 pub fn of<S: Scalar>(part: &Part<S>) -> GeopResult<Self> {
47 let model = part.topology();
48 let name = |id: RefId| -> GeopResult<String> {
49 part.name_of(id)
50 .map(str::to_string)
51 .ok_or_else(|| GeopError::new(format!("PartDescription: {id} has no name")))
52 };
53 let describe_loop = |boundary: BoundaryType| -> GeopResult<Vec<String>> {
54 let mut entries = match boundary {
55 BoundaryType::Vertex(v) => vec![format!("@{}", name(v.into())?)],
56 BoundaryType::Loop(anchor) => model
57 .iterate_loop_coedges(anchor)
58 .map(|c| {
59 let coedge = model.get_coedge(c)?;
60 Ok(match coedge.geometry {
61 CoedgeGeometry::Edge(e) => match coedge.sense {
62 Sense::Forward => format!("+{}", name(e.into())?),
63 Sense::Reversed => format!("-{}", name(e.into())?),
64 },
65 CoedgeGeometry::Vertex(v) => format!("@{}", name(v.into())?),
66 })
67 })
68 .collect::<GeopResult<Vec<_>>>()?,
69 };
70 if let Some(first) = (0..entries.len()).min_by_key(|&i| &entries[i]) {
71 entries.rotate_left(first);
72 }
73 Ok(entries)
74 };
75
76 let mut solids = BTreeMap::new();
77 for (&id, solid) in &model.solids {
78 let shells = solid
79 .shells
80 .iter()
81 .map(|&shell| {
82 let mut faces = model
83 .get_shell(shell)?
84 .faces
85 .iter()
86 .map(|&f| name(f.into()))
87 .collect::<GeopResult<Vec<_>>>()?;
88 faces.sort();
89 Ok(faces)
90 })
91 .collect::<GeopResult<Vec<_>>>()?;
92 solids.insert(name(id.into())?, shells);
93 }
94 let mut faces = BTreeMap::new();
95 for (&id, face) in &model.faces {
96 let mut holes = face
97 .holes
98 .iter()
99 .map(|&h| describe_loop(h))
100 .collect::<GeopResult<Vec<_>>>()?;
101 holes.sort();
102 faces.insert(
103 name(id.into())?,
104 FaceDescription {
105 outer: describe_loop(face.outer)?,
106 holes,
107 },
108 );
109 }
110 let mut edges = BTreeMap::new();
111 for (&id, edge) in &model.edges {
112 edges.insert(
113 name(id.into())?,
114 EdgeDescription {
115 start: name(edge.start_vertex.into())?,
116 end: name(edge.end_vertex.into())?,
117 },
118 );
119 }
120 let mut vertices = BTreeMap::new();
121 for (&id, vertex) in &model.vertices {
122 let p = vertex.point;
123 vertices.insert(
124 name(id.into())?,
125 [p[0].to_f64(), p[1].to_f64(), p[2].to_f64()],
126 );
127 }
128 let mut sketches = part
129 .sketches()
130 .map(|(id, _)| name(id.into()))
131 .collect::<GeopResult<Vec<_>>>()?;
132 sketches.sort();
133 let mut datums = part
134 .datums()
135 .map(|(id, _)| name(id.into()))
136 .collect::<GeopResult<Vec<_>>>()?;
137 datums.sort();
138 Ok(Self {
139 solids,
140 faces,
141 edges,
142 vertices,
143 sketches,
144 datums,
145 })
146 }
147}