1use crate::topology::{
29 BodyKind, BrepSolid, CellRecord, 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 if !solid.cells.is_empty() && solid.kind != BodyKind::Cellular {
92 return Err("solid codec: cells require declared Cellular kind".into());
93 }
94 let mut names = SolidNames::default();
95 let mut data = Vec::with_capacity(4096);
96 if solid.kind.is_solid() {
97 data.extend_from_slice(&[SOLID_CODEC_VERSION, id_to_f64(solid.id)?, solid.genus as f64]);
98 } else {
99 data.extend_from_slice(&[
100 SOLID_CODEC_VERSION_KIND,
101 id_to_f64(solid.id)?,
102 solid.genus as f64,
103 solid.kind.codec_code(),
104 ]);
105 }
106 data.extend_from_slice(&[
107 solid.vertices.len() as f64,
108 solid.edges.len() as f64,
109 solid.shells.len() as f64,
110 ]);
111 for vertex in &solid.vertices {
112 data.extend_from_slice(&[
113 id_to_f64(vertex.id)?,
114 vertex.point.x,
115 vertex.point.y,
116 vertex.point.z,
117 ]);
118 }
119 for edge in &solid.edges {
120 data.extend_from_slice(&[
121 id_to_f64(edge.id)?,
122 edge.t0,
123 edge.t1,
124 id_to_f64(edge.start_vertex_id)?,
125 id_to_f64(edge.end_vertex_id)?,
126 if edge.degenerate { 1.0 } else { 0.0 },
127 ]);
128 push_curve(&mut data, &edge.curve);
129 if let Some(name) = &edge.name {
130 names.edges.insert(edge.id, name.clone());
131 }
132 }
133 for shell in &solid.shells {
134 data.push(id_to_f64(shell.id)?);
135 data.push(shell.faces.len() as f64);
136 for face in &shell.faces {
137 data.push(id_to_f64(face.id)?);
138 data.push(if face.same_sense { 1.0 } else { 0.0 });
139 push_surface(&mut data, &face.surface);
140 data.push(face.loops.len() as f64);
141 for loop_record in &face.loops {
142 data.push(id_to_f64(loop_record.id)?);
143 data.push(loop_record.coedges.len() as f64);
144 for coedge in &loop_record.coedges {
145 data.extend_from_slice(&[
146 id_to_f64(coedge.id)?,
147 id_to_f64(coedge.edge_id)?,
148 if coedge.forward { 1.0 } else { 0.0 },
149 ]);
150 push_curve(&mut data, &coedge.pcurve);
151 }
152 }
153 if let Some(name) = &face.name {
154 names.faces.insert(face.id, name.clone());
155 }
156 }
157 }
158 if !solid.cells.is_empty() {
161 data.push(solid.cells.len() as f64);
162 for cell in &solid.cells {
163 data.extend_from_slice(&[id_to_f64(cell.id)?, if cell.void { 1.0 } else { 0.0 },
164 cell.faces.len() as f64]);
165 for &(face, forward) in &cell.faces {
166 data.extend_from_slice(&[id_to_f64(face)?, if forward { 1.0 } else { 0.0 }]);
167 }
168 }
169 }
170 Ok((data, names))
171}
172
173struct Reader<'a> {
174 data: &'a [f64],
175 cursor: usize,
176}
177
178impl<'a> Reader<'a> {
179 fn next(&mut self) -> Result<f64, String> {
180 let value = *self
181 .data
182 .get(self.cursor)
183 .ok_or("solid codec: truncated buffer")?;
184 self.cursor += 1;
185 Ok(value)
186 }
187
188 fn next_usize(&mut self) -> Result<usize, String> {
189 let value = self.next()?;
190 if value < 0.0 || value.fract() != 0.0 {
191 return Err(format!("solid codec: expected count, got {value}"));
192 }
193 Ok(value as usize)
194 }
195
196 fn next_id(&mut self) -> Result<u64, String> {
197 let value = self.next()?;
198 if value < 0.0 || value.fract() != 0.0 || value > MAX_EXACT_ID as f64 {
199 return Err(format!("solid codec: expected id, got {value}"));
200 }
201 Ok(value as u64)
202 }
203
204 fn next_slice(&mut self, count: usize) -> Result<&'a [f64], String> {
205 let end = self
208 .cursor
209 .checked_add(count)
210 .ok_or("solid codec: truncated buffer")?;
211 let slice = self
212 .data
213 .get(self.cursor..end)
214 .ok_or("solid codec: truncated buffer")?;
215 self.cursor = end;
216 Ok(slice)
217 }
218
219 fn curve(&mut self) -> Result<NurbsCurve, String> {
220 let degree = self.next_usize()?;
221 let knot_count = self.next_usize()?;
222 let knots = self.next_slice(knot_count)?.to_vec();
223 let control_count = self.next_usize()?;
224 let raw = self.next_slice(
225 control_count
226 .checked_mul(4)
227 .ok_or("solid codec: truncated buffer")?,
228 )?;
229 let control_points = raw
230 .chunks_exact(4)
231 .map(|chunk| Vec4 {
232 x: chunk[0],
233 y: chunk[1],
234 z: chunk[2],
235 w: chunk[3],
236 })
237 .collect();
238 NurbsCurve::new(degree, knots, control_points)
239 }
240
241 fn surface(&mut self) -> Result<NurbsSurface, String> {
242 let degree_u = self.next_usize()?;
243 let degree_v = self.next_usize()?;
244 let knots_u_count = self.next_usize()?;
245 let knots_u = self.next_slice(knots_u_count)?.to_vec();
246 let knots_v_count = self.next_usize()?;
247 let knots_v = self.next_slice(knots_v_count)?.to_vec();
248 let rows = self.next_usize()?;
249 let columns = self.next_usize()?;
250 if rows == 0 || columns == 0 {
253 return Err("solid codec: surface with empty control net".into());
254 }
255 let raw = self.next_slice(
256 rows.checked_mul(columns)
257 .and_then(|cells| cells.checked_mul(4))
258 .ok_or("solid codec: truncated buffer")?,
259 )?;
260 let control_points = raw
261 .chunks_exact(columns * 4)
262 .map(|row| {
263 row.chunks_exact(4)
264 .map(|chunk| Vec4 {
265 x: chunk[0],
266 y: chunk[1],
267 z: chunk[2],
268 w: chunk[3],
269 })
270 .collect()
271 })
272 .collect();
273 NurbsSurface::new(degree_u, degree_v, knots_u, knots_v, control_points)
274 }
275}
276
277pub fn decode_solid(data: &[f64], names: &SolidNames) -> Result<BrepSolid, String> {
278 let mut reader = Reader { data, cursor: 0 };
279 let version = reader.next()?;
280 if version != SOLID_CODEC_VERSION && version != SOLID_CODEC_VERSION_KIND {
281 return Err(format!("solid codec: unsupported version {version}"));
282 }
283 let id = reader.next_id()?;
284 let genus = reader.next()? as i64;
285 let kind = if version == SOLID_CODEC_VERSION_KIND {
286 let code = reader.next()?;
287 BodyKind::from_codec_code(code)
288 .ok_or_else(|| format!("solid codec: unknown body kind {code}"))?
289 } else {
290 BodyKind::Solid
291 };
292 let vertex_count = reader.next_usize()?;
293 let edge_count = reader.next_usize()?;
294 let shell_count = reader.next_usize()?;
295 let mut vertices = Vec::with_capacity(vertex_count);
296 for _ in 0..vertex_count {
297 vertices.push(VertexRecord {
298 id: reader.next_id()?,
299 point: Vec3::new(reader.next()?, reader.next()?, reader.next()?),
300 });
301 }
302 let mut edges = Vec::with_capacity(edge_count);
303 for _ in 0..edge_count {
304 let id = reader.next_id()?;
305 let t0 = reader.next()?;
306 let t1 = reader.next()?;
307 let start_vertex_id = reader.next_id()?;
308 let end_vertex_id = reader.next_id()?;
309 let degenerate = reader.next()? != 0.0;
310 let curve = reader.curve()?;
311 edges.push(EdgeRecord {
312 id,
313 curve,
314 t0,
315 t1,
316 start_vertex_id,
317 end_vertex_id,
318 degenerate,
319 name: names.edges.get(&id).cloned(),
320 });
321 }
322 let mut shells = Vec::with_capacity(shell_count);
323 for _ in 0..shell_count {
324 let shell_id = reader.next_id()?;
325 let face_count = reader.next_usize()?;
326 let mut faces = Vec::with_capacity(face_count);
327 for _ in 0..face_count {
328 let face_id = reader.next_id()?;
329 let same_sense = reader.next()? != 0.0;
330 let surface = reader.surface()?;
331 let loop_count = reader.next_usize()?;
332 let mut loops = Vec::with_capacity(loop_count);
333 for _ in 0..loop_count {
334 let loop_id = reader.next_id()?;
335 let coedge_count = reader.next_usize()?;
336 let mut coedges = Vec::with_capacity(coedge_count);
337 for _ in 0..coedge_count {
338 let coedge_id = reader.next_id()?;
339 let edge_id = reader.next_id()?;
340 let forward = reader.next()? != 0.0;
341 let pcurve = reader.curve()?;
342 coedges.push(CoedgeRecord {
343 id: coedge_id,
344 edge_id,
345 forward,
346 pcurve,
347 });
348 }
349 loops.push(LoopRecord {
350 id: loop_id,
351 coedges,
352 });
353 }
354 faces.push(FaceRecord {
355 id: face_id,
356 surface,
357 same_sense,
358 loops,
359 name: names.faces.get(&face_id).cloned(),
360 });
361 }
362 shells.push(ShellRecord {
363 id: shell_id,
364 faces,
365 });
366 }
367 let mut cells = Vec::new();
368 if kind == BodyKind::Cellular && reader.cursor < data.len() {
369 let count = reader.next_usize()?;
370 if count == 0 || count > (data.len()-reader.cursor)/3 {
371 return Err("solid codec: invalid cell count".into());
372 }
373 for _ in 0..count {
374 let id = reader.next_id()?;
375 let void = match reader.next()? {
376 0.0 => false, 1.0 => true,
377 _ => return Err("solid codec: invalid cell void flag".into()),
378 };
379 let face_count = reader.next_usize()?;
380 if face_count > (data.len()-reader.cursor)/2 {
381 return Err("solid codec: truncated cell faces".into());
382 }
383 let mut faces = Vec::with_capacity(face_count);
384 for _ in 0..face_count {
385 let face = reader.next_id()?;
386 let forward = match reader.next()? {
387 0.0 => false, 1.0 => true,
388 _ => return Err("solid codec: invalid cell face sense".into()),
389 };
390 faces.push((face, forward));
391 }
392 cells.push(CellRecord { id, faces, void });
393 }
394 }
395 if reader.cursor != data.len() {
396 return Err("solid codec: trailing data".into());
397 }
398 Ok(BrepSolid {
399 mass_properties_cache: Default::default(),
400 id,
401 vertices,
402 edges,
403 shells,
404 genus,
405 kind,
406 cells,
407 })
408}
409