1use crate::topology::{
29 BodyKind, BrepSolid, CoedgeRecord, EdgeRecord, FaceRecord, LoopRecord, ShellRecord,
30 VertexRecord,
31};
32use crate::{NurbsCurve, NurbsSurface, Vec3, Vec4};
33use std::collections::BTreeMap;
34
35pub const SOLID_CODEC_VERSION: f64 = 1.0;
39pub const SOLID_CODEC_VERSION_KIND: f64 = 1.1;
41
42const MAX_EXACT_ID: u64 = 1 << 53;
46
47fn id_to_f64(id: u64) -> Result<f64, String> {
48 if id > MAX_EXACT_ID {
49 return Err(format!(
50 "solid codec: id {id} exceeds 2^53 and cannot round-trip through f64"
51 ));
52 }
53 Ok(id as f64)
54}
55
56#[derive(serde::Serialize, serde::Deserialize, Default, Debug)]
57pub struct SolidNames {
58 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
59 pub faces: BTreeMap<u64, String>,
60 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
61 pub edges: BTreeMap<u64, String>,
62}
63
64fn push_curve(data: &mut Vec<f64>, curve: &NurbsCurve) {
65 data.push(curve.degree as f64);
66 data.push(curve.knots.len() as f64);
67 data.extend_from_slice(&curve.knots);
68 data.push(curve.control_points.len() as f64);
69 for point in &curve.control_points {
70 data.extend_from_slice(&[point.x, point.y, point.z, point.w]);
71 }
72}
73
74fn push_surface(data: &mut Vec<f64>, surface: &NurbsSurface) {
75 data.push(surface.degree_u as f64);
76 data.push(surface.degree_v as f64);
77 data.push(surface.knots_u.len() as f64);
78 data.extend_from_slice(&surface.knots_u);
79 data.push(surface.knots_v.len() as f64);
80 data.extend_from_slice(&surface.knots_v);
81 data.push(surface.control_points.len() as f64);
82 data.push(surface.control_points[0].len() as f64);
83 for row in &surface.control_points {
84 for point in row {
85 data.extend_from_slice(&[point.x, point.y, point.z, point.w]);
86 }
87 }
88}
89
90pub fn encode_solid(solid: &BrepSolid) -> Result<(Vec<f64>, SolidNames), String> {
91 let mut names = SolidNames::default();
92 let mut data = Vec::with_capacity(4096);
93 if solid.kind.is_solid() {
94 data.extend_from_slice(&[SOLID_CODEC_VERSION, id_to_f64(solid.id)?, solid.genus as f64]);
95 } else {
96 data.extend_from_slice(&[
97 SOLID_CODEC_VERSION_KIND,
98 id_to_f64(solid.id)?,
99 solid.genus as f64,
100 solid.kind.codec_code(),
101 ]);
102 }
103 data.extend_from_slice(&[
104 solid.vertices.len() as f64,
105 solid.edges.len() as f64,
106 solid.shells.len() as f64,
107 ]);
108 for vertex in &solid.vertices {
109 data.extend_from_slice(&[
110 id_to_f64(vertex.id)?,
111 vertex.point.x,
112 vertex.point.y,
113 vertex.point.z,
114 ]);
115 }
116 for edge in &solid.edges {
117 data.extend_from_slice(&[
118 id_to_f64(edge.id)?,
119 edge.t0,
120 edge.t1,
121 id_to_f64(edge.start_vertex_id)?,
122 id_to_f64(edge.end_vertex_id)?,
123 if edge.degenerate { 1.0 } else { 0.0 },
124 ]);
125 push_curve(&mut data, &edge.curve);
126 if let Some(name) = &edge.name {
127 names.edges.insert(edge.id, name.clone());
128 }
129 }
130 for shell in &solid.shells {
131 data.push(id_to_f64(shell.id)?);
132 data.push(shell.faces.len() as f64);
133 for face in &shell.faces {
134 data.push(id_to_f64(face.id)?);
135 data.push(if face.same_sense { 1.0 } else { 0.0 });
136 push_surface(&mut data, &face.surface);
137 data.push(face.loops.len() as f64);
138 for loop_record in &face.loops {
139 data.push(id_to_f64(loop_record.id)?);
140 data.push(loop_record.coedges.len() as f64);
141 for coedge in &loop_record.coedges {
142 data.extend_from_slice(&[
143 id_to_f64(coedge.id)?,
144 id_to_f64(coedge.edge_id)?,
145 if coedge.forward { 1.0 } else { 0.0 },
146 ]);
147 push_curve(&mut data, &coedge.pcurve);
148 }
149 }
150 if let Some(name) = &face.name {
151 names.faces.insert(face.id, name.clone());
152 }
153 }
154 }
155 Ok((data, names))
156}
157
158struct Reader<'a> {
159 data: &'a [f64],
160 cursor: usize,
161}
162
163impl<'a> Reader<'a> {
164 fn next(&mut self) -> Result<f64, String> {
165 let value = *self
166 .data
167 .get(self.cursor)
168 .ok_or("solid codec: truncated buffer")?;
169 self.cursor += 1;
170 Ok(value)
171 }
172
173 fn next_usize(&mut self) -> Result<usize, String> {
174 let value = self.next()?;
175 if value < 0.0 || value.fract() != 0.0 {
176 return Err(format!("solid codec: expected count, got {value}"));
177 }
178 Ok(value as usize)
179 }
180
181 fn next_id(&mut self) -> Result<u64, String> {
182 let value = self.next()?;
183 if value < 0.0 || value.fract() != 0.0 || value > MAX_EXACT_ID as f64 {
184 return Err(format!("solid codec: expected id, got {value}"));
185 }
186 Ok(value as u64)
187 }
188
189 fn next_slice(&mut self, count: usize) -> Result<&'a [f64], String> {
190 let end = self
193 .cursor
194 .checked_add(count)
195 .ok_or("solid codec: truncated buffer")?;
196 let slice = self
197 .data
198 .get(self.cursor..end)
199 .ok_or("solid codec: truncated buffer")?;
200 self.cursor = end;
201 Ok(slice)
202 }
203
204 fn curve(&mut self) -> Result<NurbsCurve, String> {
205 let degree = self.next_usize()?;
206 let knot_count = self.next_usize()?;
207 let knots = self.next_slice(knot_count)?.to_vec();
208 let control_count = self.next_usize()?;
209 let raw = self.next_slice(
210 control_count
211 .checked_mul(4)
212 .ok_or("solid codec: truncated buffer")?,
213 )?;
214 let control_points = raw
215 .chunks_exact(4)
216 .map(|chunk| Vec4 {
217 x: chunk[0],
218 y: chunk[1],
219 z: chunk[2],
220 w: chunk[3],
221 })
222 .collect();
223 NurbsCurve::new(degree, knots, control_points)
224 }
225
226 fn surface(&mut self) -> Result<NurbsSurface, String> {
227 let degree_u = self.next_usize()?;
228 let degree_v = self.next_usize()?;
229 let knots_u_count = self.next_usize()?;
230 let knots_u = self.next_slice(knots_u_count)?.to_vec();
231 let knots_v_count = self.next_usize()?;
232 let knots_v = self.next_slice(knots_v_count)?.to_vec();
233 let rows = self.next_usize()?;
234 let columns = self.next_usize()?;
235 if rows == 0 || columns == 0 {
238 return Err("solid codec: surface with empty control net".into());
239 }
240 let raw = self.next_slice(
241 rows.checked_mul(columns)
242 .and_then(|cells| cells.checked_mul(4))
243 .ok_or("solid codec: truncated buffer")?,
244 )?;
245 let control_points = raw
246 .chunks_exact(columns * 4)
247 .map(|row| {
248 row.chunks_exact(4)
249 .map(|chunk| Vec4 {
250 x: chunk[0],
251 y: chunk[1],
252 z: chunk[2],
253 w: chunk[3],
254 })
255 .collect()
256 })
257 .collect();
258 NurbsSurface::new(degree_u, degree_v, knots_u, knots_v, control_points)
259 }
260}
261
262pub fn decode_solid(data: &[f64], names: &SolidNames) -> Result<BrepSolid, String> {
263 let mut reader = Reader { data, cursor: 0 };
264 let version = reader.next()?;
265 if version != SOLID_CODEC_VERSION && version != SOLID_CODEC_VERSION_KIND {
266 return Err(format!("solid codec: unsupported version {version}"));
267 }
268 let id = reader.next_id()?;
269 let genus = reader.next()? as i64;
270 let kind = if version == SOLID_CODEC_VERSION_KIND {
271 let code = reader.next()?;
272 BodyKind::from_codec_code(code)
273 .ok_or_else(|| format!("solid codec: unknown body kind {code}"))?
274 } else {
275 BodyKind::Solid
276 };
277 let vertex_count = reader.next_usize()?;
278 let edge_count = reader.next_usize()?;
279 let shell_count = reader.next_usize()?;
280 let mut vertices = Vec::with_capacity(vertex_count);
281 for _ in 0..vertex_count {
282 vertices.push(VertexRecord {
283 id: reader.next_id()?,
284 point: Vec3::new(reader.next()?, reader.next()?, reader.next()?),
285 });
286 }
287 let mut edges = Vec::with_capacity(edge_count);
288 for _ in 0..edge_count {
289 let id = reader.next_id()?;
290 let t0 = reader.next()?;
291 let t1 = reader.next()?;
292 let start_vertex_id = reader.next_id()?;
293 let end_vertex_id = reader.next_id()?;
294 let degenerate = reader.next()? != 0.0;
295 let curve = reader.curve()?;
296 edges.push(EdgeRecord {
297 id,
298 curve,
299 t0,
300 t1,
301 start_vertex_id,
302 end_vertex_id,
303 degenerate,
304 name: names.edges.get(&id).cloned(),
305 });
306 }
307 let mut shells = Vec::with_capacity(shell_count);
308 for _ in 0..shell_count {
309 let shell_id = reader.next_id()?;
310 let face_count = reader.next_usize()?;
311 let mut faces = Vec::with_capacity(face_count);
312 for _ in 0..face_count {
313 let face_id = reader.next_id()?;
314 let same_sense = reader.next()? != 0.0;
315 let surface = reader.surface()?;
316 let loop_count = reader.next_usize()?;
317 let mut loops = Vec::with_capacity(loop_count);
318 for _ in 0..loop_count {
319 let loop_id = reader.next_id()?;
320 let coedge_count = reader.next_usize()?;
321 let mut coedges = Vec::with_capacity(coedge_count);
322 for _ in 0..coedge_count {
323 let coedge_id = reader.next_id()?;
324 let edge_id = reader.next_id()?;
325 let forward = reader.next()? != 0.0;
326 let pcurve = reader.curve()?;
327 coedges.push(CoedgeRecord {
328 id: coedge_id,
329 edge_id,
330 forward,
331 pcurve,
332 });
333 }
334 loops.push(LoopRecord {
335 id: loop_id,
336 coedges,
337 });
338 }
339 faces.push(FaceRecord {
340 id: face_id,
341 surface,
342 same_sense,
343 loops,
344 name: names.faces.get(&face_id).cloned(),
345 });
346 }
347 shells.push(ShellRecord {
348 id: shell_id,
349 faces,
350 });
351 }
352 if reader.cursor != data.len() {
353 return Err("solid codec: trailing data".into());
354 }
355 Ok(BrepSolid {
356 mass_properties_cache: Default::default(),
357 id,
358 vertices,
359 edges,
360 shells,
361 genus,
362 kind,
363 })
364}
365