1use std::collections::{HashMap, HashSet};
33use std::io::Read;
34
35use anyhow::{anyhow, bail, Result};
36
37use super::{check_triangles, Mesh, MAX_TRIANGLES};
38
39pub const MAGIC: &[u8] = b"3D Geometry File Format ";
41const MAX_HEADER_OFFSET: usize = 32 * 1024 * 1024;
43pub const MAX_DEPTH: usize = 8;
45const MAX_SUBD_FACES: usize = 200_000;
47const MAX_SUBD_LEVELS: usize = 4;
49
50const TCODE_SHORT: u32 = 0x8000_0000;
51const TCODE_CRC: u32 = 0x8000;
52const TCODE_ENDOFFILE: u32 = 0x0000_7FFF;
53const TCODE_ENDOFTABLE: u32 = 0xFFFF_FFFF;
54const TCODE_PROPERTIES_TABLE: u32 = 0x1000_0014;
55const TCODE_SETTINGS_TABLE: u32 = 0x1000_0015;
56const TCODE_LAYER_TABLE: u32 = 0x1000_0011;
57const TCODE_OBJECT_TABLE: u32 = 0x1000_0013;
58const TCODE_IDEF_TABLE: u32 = 0x1000_0021;
59const TCODE_OPENNURBS_VERSION: u32 = 0xA000_0026;
60const TCODE_COMPRESSED_PREVIEW: u32 = 0x2000_8025;
61const TCODE_UNITS: u32 = 0x2000_8031;
62const TCODE_LAYER_RECORD: u32 = 0x2000_8050;
63const TCODE_IDEF_RECORD: u32 = 0x2000_8076;
64const TCODE_OBJECT_RECORD: u32 = 0x2000_8070;
65const TCODE_OBJECT_TYPE: u32 = 0x8200_0071;
66const TCODE_OBJECT_ATTRIBUTES: u32 = 0x0200_8072;
67const TCODE_OBJECT_END: u32 = 0x8200_007F;
68const TCODE_CLASS: u32 = 0x0002_7FFA;
69const TCODE_CLASS_UUID: u32 = 0x0002_FFFB;
70const TCODE_CLASS_DATA: u32 = 0x0002_FFFC;
71const TCODE_CLASS_END: u32 = 0x8002_7FFF;
72const TCODE_ANONYMOUS: u32 = 0x4000_8000;
73const TCODE_V1_MESH: u32 = 0x0010_0015;
74const TCODE_V1_MESH_GEOMETRY: u32 = 0x0010_0017;
75
76const fn uuid(d1: u32, d2: u16, d3: u16, d4: [u8; 8]) -> [u8; 16] {
78 let a = d1.to_le_bytes();
79 let b = d2.to_le_bytes();
80 let c = d3.to_le_bytes();
81 [
82 a[0], a[1], a[2], a[3], b[0], b[1], c[0], c[1], d4[0], d4[1], d4[2], d4[3], d4[4], d4[5],
83 d4[6], d4[7],
84 ]
85}
86
87const MESH: [u8; 16] = uuid(
88 0x4ED7_D4E4,
89 0xE947,
90 0x11d3,
91 [0xBF, 0xE5, 0x00, 0x10, 0x83, 0x01, 0x22, 0xF0],
92);
93const BREPS: [[u8; 16]; 4] = [
96 uuid(
97 0x60B5_DBC5,
98 0xE660,
99 0x11d3,
100 [0xBF, 0xE4, 0x00, 0x10, 0x83, 0x01, 0x22, 0xF0],
101 ),
102 uuid(
103 0x0705_FDEF,
104 0x3E2A,
105 0x11d4,
106 [0x80, 0x0E, 0x00, 0x10, 0x83, 0x01, 0x22, 0xF0],
107 ),
108 uuid(
109 0x2D4C_FEDB,
110 0x3E2A,
111 0x11d4,
112 [0x80, 0x0E, 0x00, 0x10, 0x83, 0x01, 0x22, 0xF0],
113 ),
114 uuid(
115 0xF06F_C243,
116 0xA32A,
117 0x4608,
118 [0x9D, 0xD8, 0xA7, 0xD2, 0xC4, 0xCE, 0x2A, 0x36],
119 ),
120];
121const EXTRUSION: [u8; 16] = uuid(
122 0x36F5_3175,
123 0x72B8,
124 0x4d47,
125 [0xBF, 0x1F, 0xB4, 0xE6, 0xFC, 0x24, 0xF4, 0xB9],
126);
127const SUBD: [u8; 16] = uuid(
128 0xF09B_A4D9,
129 0x455B,
130 0x42C3,
131 [0xBA, 0x3B, 0xE6, 0xCC, 0xAC, 0xEF, 0x85, 0x3B],
132);
133const INSTANCE_REF: [u8; 16] = uuid(
134 0xF9CF_B638,
135 0xB9D4,
136 0x4340,
137 [0x87, 0xE3, 0xC5, 0x6E, 0x78, 0x65, 0xD9, 0x6A],
138);
139
140fn millimetres_per_unit(unit: u32) -> f64 {
142 match unit {
143 1 => 1e-3,
144 3 => 10.0,
145 4 => 1000.0,
146 5 => 1e6,
147 6 => 2.54e-5,
148 7 => 0.0254,
149 8 => 25.4,
150 9 => 304.8,
151 10 => 1_609_344.0,
152 12 => 1e-7,
153 13 => 1e-6,
154 14 => 100.0,
155 15 => 1e4,
156 16 => 1e5,
157 19 => 914.4,
158 20 => 1_852_000.0,
159 _ => 1.0,
160 }
161}
162
163fn short_file(at: usize) -> anyhow::Error {
168 anyhow!("The Rhino file is damaged or ends early (byte {at}).")
169}
170
171#[derive(Clone, Copy)]
173struct Cur<'a> {
174 data: &'a [u8],
175 at: usize,
176 end: usize,
177 big: bool,
179 v1: bool,
181}
182
183struct Chunk<'a> {
184 tcode: u32,
185 value: i64,
186 body: Option<Cur<'a>>,
187}
188
189impl<'a> Cur<'a> {
190 fn left(&self) -> usize {
191 self.end.saturating_sub(self.at)
192 }
193
194 fn bytes(&mut self, n: usize) -> Result<&'a [u8]> {
195 let end = self
196 .at
197 .checked_add(n)
198 .filter(|&e| e <= self.end)
199 .ok_or_else(|| short_file(self.at))?;
200 let s = self
201 .data
202 .get(self.at..end)
203 .ok_or_else(|| short_file(self.at))?;
204 self.at = end;
205 Ok(s)
206 }
207
208 fn array<const N: usize>(&mut self) -> Result<[u8; N]> {
209 let at = self.at;
210 <[u8; N]>::try_from(self.bytes(N)?).map_err(|_| short_file(at))
211 }
212
213 fn skip(&mut self, n: usize) -> Result<()> {
214 self.bytes(n).map(|_| ())
215 }
216
217 fn skip_items(&mut self, count: usize, size: usize) -> Result<()> {
219 let n = count.checked_mul(size).ok_or_else(|| short_file(self.at))?;
220 self.skip(n)
221 }
222
223 fn u8(&mut self) -> Result<u8> {
224 self.array::<1>().map(|[b]| b)
225 }
226 fn u16(&mut self) -> Result<u16> {
227 self.array().map(u16::from_le_bytes)
228 }
229 fn u32(&mut self) -> Result<u32> {
230 self.array().map(u32::from_le_bytes)
231 }
232 fn i32(&mut self) -> Result<i32> {
233 self.array().map(i32::from_le_bytes)
234 }
235 fn f64(&mut self) -> Result<f64> {
236 self.array().map(f64::from_le_bytes)
237 }
238 fn f64s<const N: usize>(&mut self) -> Result<[f64; N]> {
239 let mut out = [0.0; N];
240 for v in &mut out {
241 *v = self.f64()?;
242 }
243 Ok(out)
244 }
245 fn uuid(&mut self) -> Result<[u8; 16]> {
246 self.array()
247 }
248 fn count(&mut self, size: usize) -> Result<usize> {
250 let at = self.at;
251 let n = self.u32()? as usize;
252 if n.saturating_mul(size.max(1)) > self.left() {
253 bail!("The Rhino file is damaged: a count at byte {at} exceeds the data.");
254 }
255 Ok(n)
256 }
257 fn version(&mut self) -> Result<(u8, u8)> {
259 let v = self.u8()?;
260 Ok((v >> 4, v & 0x0F))
261 }
262 fn version2(&mut self) -> Result<(i32, i32)> {
264 Ok((self.i32()?, self.i32()?))
265 }
266 fn skip_string(&mut self) -> Result<()> {
268 let n = self.count(2)?;
269 self.skip_items(n, 2)
270 }
271
272 fn chunk(&mut self) -> Result<Chunk<'a>> {
273 let tcode = self.u32()?;
274 let short = tcode & TCODE_SHORT != 0;
275 let value = if self.big {
276 i64::from_le_bytes(self.array()?)
277 } else if short
278 && tcode != TCODE_OPENNURBS_VERSION
279 && tcode != TCODE_OBJECT_TYPE
280 && tcode != TCODE_ENDOFTABLE
281 {
282 i64::from(self.i32()?)
283 } else {
284 i64::from(self.u32()?)
285 };
286 if short {
287 return Ok(Chunk {
288 tcode,
289 value,
290 body: None,
291 });
292 }
293 let len = usize::try_from(value)
294 .ok()
295 .filter(|&n| n <= self.left())
296 .ok_or_else(|| {
297 anyhow!(
298 "The Rhino file is damaged: a chunk at byte {} is too long.",
299 self.at
300 )
301 })?;
302 let crc = if tcode & TCODE_CRC != 0 && !self.v1 {
303 4
304 } else {
305 0
306 };
307 if len < crc {
308 bail!(
309 "The Rhino file is damaged: a chunk at byte {} is too short.",
310 self.at
311 );
312 }
313 let body = Cur {
314 at: self.at,
315 end: self.at + len - crc,
316 ..*self
317 };
318 self.at += len;
319 Ok(Chunk {
320 tcode,
321 value,
322 body: Some(body),
323 })
324 }
325
326 fn next(&mut self) -> Result<Option<Chunk<'a>>> {
328 if self.left() == 0 {
329 return Ok(None);
330 }
331 let c = self.chunk()?;
332 Ok(Some(c))
333 }
334
335 fn body(&mut self) -> Result<Cur<'a>> {
337 let at = self.at;
338 self.chunk()?.body.ok_or_else(|| {
339 anyhow!("The Rhino file is damaged: unexpected short chunk at byte {at}.")
340 })
341 }
342}
343
344fn read_object<'a>(cur: &mut Cur<'a>) -> Result<([u8; 16], Option<Cur<'a>>)> {
346 let at = cur.at;
347 let c = cur.chunk()?;
348 let (TCODE_CLASS, Some(mut class)) = (c.tcode, c.body) else {
349 bail!("The Rhino file is damaged: no object at byte {at}.");
350 };
351 let mut id = [0; 16];
352 let mut data = None;
353 while let Some(c) = class.next()? {
354 match (c.tcode, c.body) {
355 (TCODE_CLASS_UUID, Some(mut b)) => id = b.uuid()?,
356 (TCODE_CLASS_DATA, Some(b)) => data = Some(b),
357 (TCODE_CLASS_END, _) => break,
358 _ => {}
359 }
360 }
361 Ok((id, data))
362}
363
364#[derive(Default)]
370struct Part {
371 positions: Vec<f32>,
372 indices: Vec<u32>,
373}
374
375impl Part {
376 fn triangles(&self) -> usize {
377 self.indices.len() / 3
378 }
379}
380
381enum Geometry {
382 Parts(Vec<Part>),
383 Instance {
384 definition: [u8; 16],
385 xform: [[f64; 4]; 4],
386 },
387}
388
389struct Object {
390 id: Option<[u8; 16]>,
391 layer: i32,
392 mode: u8,
394 visible: bool,
395 geometry: Geometry,
396}
397
398struct Model {
399 version: u32,
400 opennurbs: u64,
401 unit: u32,
402 hidden_layers: Vec<bool>,
403 definitions: HashMap<[u8; 16], Vec<[u8; 16]>>,
404 objects: Vec<Object>,
405 triangles: usize,
407}
408
409fn open(data: &[u8]) -> Result<(u32, Cur<'_>)> {
411 let start = data
412 .get(..data.len().min(MAX_HEADER_OFFSET + MAGIC.len()))
413 .and_then(|head| head.windows(MAGIC.len()).position(|w| w == MAGIC))
414 .ok_or_else(|| anyhow!("This is not a Rhino model (.3dm header missing)."))?;
415 let digits = data
416 .get(start + MAGIC.len()..start + 32)
417 .ok_or_else(|| short_file(start))?;
418 let mut version: u32 = 0;
419 for &d in digits.iter().skip_while(|&&d| d == b' ') {
420 let d = if d == b'X' { b'2' } else { d };
422 if !d.is_ascii_digit() {
423 bail!("This is not a Rhino model (invalid version).");
424 }
425 version = version
426 .saturating_mul(10)
427 .saturating_add(u32::from(d - b'0'));
428 }
429 if version == 0 {
430 bail!("This is not a Rhino model (invalid version).");
431 }
432 let cur = Cur {
433 data,
434 at: start + 32,
435 end: data.len(),
436 big: version >= 50,
437 v1: version == 1,
438 };
439 Ok((version, cur))
440}
441
442pub fn read(data: &[u8]) -> Result<Mesh> {
444 let (version, mut cur) = open(data)?;
445 let mut model = Model {
446 version,
447 opennurbs: 0,
448 unit: 2,
449 hidden_layers: Vec::new(),
450 definitions: HashMap::new(),
451 objects: Vec::new(),
452 triangles: 0,
453 };
454 while let Some(c) = cur.next()? {
455 let Some(body) = c.body else {
456 continue;
457 };
458 match c.tcode {
459 TCODE_PROPERTIES_TABLE => model.properties(body)?,
460 TCODE_SETTINGS_TABLE => model.settings(body)?,
461 TCODE_LAYER_TABLE => model.layers(body)?,
462 TCODE_IDEF_TABLE => model.definitions(body)?,
463 TCODE_OBJECT_TABLE => model.objects(body)?,
464 TCODE_V1_MESH if version == 1 => {
465 if let Ok(part) = v1_mesh(body) {
467 model.add_triangles(part.triangles())?;
468 model.objects.push(Object {
469 id: None,
470 layer: 0,
471 mode: 0,
472 visible: true,
473 geometry: Geometry::Parts(vec![part]),
474 });
475 }
476 }
477 TCODE_ENDOFFILE => break,
478 _ => {}
479 }
480 }
481 model.scene()
482}
483
484impl Model {
485 fn add_triangles(&mut self, n: usize) -> Result<()> {
486 self.triangles = self.triangles.saturating_add(n);
487 check_triangles(self.triangles)
488 }
489
490 fn properties(&mut self, mut table: Cur<'_>) -> Result<()> {
491 while let Some(c) = table.next()? {
492 match c.tcode {
493 TCODE_OPENNURBS_VERSION => self.opennurbs = u64::try_from(c.value).unwrap_or(0),
494 TCODE_ENDOFTABLE => break,
495 _ => {}
496 }
497 }
498 Ok(())
499 }
500
501 fn settings(&mut self, mut table: Cur<'_>) -> Result<()> {
502 while let Some(c) = table.next()? {
503 match (c.tcode, c.body) {
504 (TCODE_UNITS, Some(mut b)) => {
505 let version = b.i32()?;
506 if (100..200).contains(&version) {
507 self.unit = b.u32()?;
508 }
509 }
510 (TCODE_ENDOFTABLE, _) => break,
511 _ => {}
512 }
513 }
514 Ok(())
515 }
516
517 fn layers(&mut self, mut table: Cur<'_>) -> Result<()> {
518 while let Some(c) = table.next()? {
519 match (c.tcode, c.body) {
520 (TCODE_LAYER_RECORD, Some(mut record)) => {
521 let hidden = read_object(&mut record)
523 .ok()
524 .and_then(|(_, data)| data)
525 .and_then(|mut d| {
526 let (major, _) = d.version().ok()?;
527 (major == 1).then(|| d.i32().ok()).flatten()
528 })
529 .is_some_and(|mode| mode == 1);
530 self.hidden_layers.push(hidden);
531 }
532 (TCODE_ENDOFTABLE, _) => break,
533 _ => {}
534 }
535 }
536 Ok(())
537 }
538
539 fn definitions(&mut self, mut table: Cur<'_>) -> Result<()> {
540 while let Some(c) = table.next()? {
541 match (c.tcode, c.body) {
542 (TCODE_IDEF_RECORD, Some(mut record)) => {
543 let Ok((_, Some(data))) = read_object(&mut record) else {
544 continue;
545 };
546 if let Ok((id, objects)) = definition(data, self.version) {
547 self.definitions.insert(id, objects);
548 }
549 }
550 (TCODE_ENDOFTABLE, _) => break,
551 _ => {}
552 }
553 }
554 Ok(())
555 }
556
557 fn objects(&mut self, mut table: Cur<'_>) -> Result<()> {
558 while let Some(c) = table.next()? {
559 match (c.tcode, c.body) {
560 (TCODE_OBJECT_RECORD, Some(record)) => {
561 match self.object(record) {
563 Ok(Some(o)) => self.objects.push(o),
564 Ok(None) => {}
565 Err(e) if self.triangles > MAX_TRIANGLES => return Err(e),
566 Err(_) => {}
567 }
568 }
569 (TCODE_ENDOFTABLE, _) => break,
570 _ => {}
571 }
572 }
573 Ok(())
574 }
575
576 fn object(&mut self, mut record: Cur<'_>) -> Result<Option<Object>> {
577 let mut geometry = None;
578 let mut attributes = None;
579 while record.left() > 0 {
580 let at = record.at;
581 let c = record.chunk()?;
582 match (c.tcode, c.body) {
583 (TCODE_OBJECT_TYPE, _) => {}
584 (TCODE_CLASS, Some(_)) => {
585 record.at = at;
586 let (class, data) = read_object(&mut record)?;
587 if let Some(data) = data {
588 geometry = self.geometry(class, data)?;
589 }
590 }
591 (TCODE_OBJECT_ATTRIBUTES, Some(b)) => {
592 attributes = Some(read_attributes(b, self.version, self.opennurbs)?);
593 }
594 (TCODE_OBJECT_END, _) => break,
595 _ => {}
596 }
597 }
598 let Some(geometry) = geometry else {
599 return Ok(None);
600 };
601 let a = attributes.unwrap_or_default();
602 Ok(Some(Object {
603 id: a.id,
604 layer: a.layer,
605 mode: a.mode,
606 visible: a.visible,
607 geometry,
608 }))
609 }
610
611 fn geometry(&mut self, class: [u8; 16], mut data: Cur<'_>) -> Result<Option<Geometry>> {
612 let parts = if class == MESH {
613 vec![mesh(data)?]
614 } else if BREPS.contains(&class) {
615 brep_meshes(data)?
616 } else if class == EXTRUSION {
617 extrusion_mesh(data)?.into_iter().collect()
618 } else if class == SUBD {
619 subd::mesh(data, self.version)?.into_iter().collect()
620 } else if class == INSTANCE_REF {
621 let (major, _) = data.version()?;
622 if major != 1 {
623 return Ok(None);
624 }
625 let definition = data.uuid()?;
626 let m: [f64; 16] = data.f64s()?;
627 let row = |r: usize| -> [f64; 4] {
628 let mut out = [0.0; 4];
629 for (k, v) in out.iter_mut().enumerate() {
630 *v = m.get(r * 4 + k).copied().unwrap_or(0.0);
631 }
632 out
633 };
634 return Ok(Some(Geometry::Instance {
635 definition,
636 xform: [row(0), row(1), row(2), row(3)],
637 }));
638 } else {
639 return Ok(None);
640 };
641 let n = parts.iter().map(Part::triangles).sum();
642 self.add_triangles(n)?;
643 Ok((!parts.is_empty()).then_some(Geometry::Parts(parts)))
644 }
645
646 fn hidden(&self, o: &Object) -> bool {
647 o.mode == 1
648 || !o.visible
649 || usize::try_from(o.layer)
650 .ok()
651 .and_then(|l| self.hidden_layers.get(l))
652 .copied()
653 .unwrap_or(false)
654 }
655
656 fn scene(&self) -> Result<Mesh> {
658 let by_id: HashMap<[u8; 16], &Object> = self
659 .objects
660 .iter()
661 .filter_map(|o| o.id.map(|id| (id, o)))
662 .collect();
663 let in_definition: HashSet<[u8; 16]> =
664 self.definitions.values().flatten().copied().collect();
665 let top: Vec<&Object> = self
666 .objects
667 .iter()
668 .filter(|o| o.mode != 3 && !o.id.is_some_and(|id| in_definition.contains(&id)))
669 .filter(|o| !self.hidden(o))
670 .collect();
671 let mut total = 0usize;
673 for o in &top {
674 total = total.saturating_add(self.count(o, &by_id, 0));
675 check_triangles(total)?;
676 }
677 if total == 0 {
678 bail!(
679 "The Rhino model stores no meshes to show (polysurfaces saved without render \
680 meshes, e.g. with \"Save Small\", curves or points only)."
681 );
682 }
683 let scale = millimetres_per_unit(self.unit);
684 let mut out = Mesh::default();
685 let identity = scaling(scale);
686 for o in top {
687 self.emit(o, &by_id, &identity, 0, &mut out);
688 }
689 Ok(out)
690 }
691
692 fn count(&self, o: &Object, by_id: &HashMap<[u8; 16], &Object>, depth: usize) -> usize {
693 match &o.geometry {
694 Geometry::Parts(parts) => parts.iter().map(Part::triangles).sum(),
695 Geometry::Instance { definition, .. } => {
696 if depth >= MAX_DEPTH {
697 return 0;
698 }
699 self.members(definition, by_id)
700 .map(|c| self.count(c, by_id, depth + 1))
701 .fold(0usize, usize::saturating_add)
702 }
703 }
704 }
705
706 fn members<'s>(
707 &'s self,
708 definition: &[u8; 16],
709 by_id: &'s HashMap<[u8; 16], &'s Object>,
710 ) -> impl Iterator<Item = &'s Object> + 's {
711 self.definitions
712 .get(definition)
713 .into_iter()
714 .flatten()
715 .filter_map(|id| by_id.get(id).copied())
716 .filter(|o| !self.hidden(o))
717 }
718
719 fn emit(
720 &self,
721 o: &Object,
722 by_id: &HashMap<[u8; 16], &Object>,
723 xform: &[[f64; 4]; 4],
724 depth: usize,
725 out: &mut Mesh,
726 ) {
727 match &o.geometry {
728 Geometry::Parts(parts) => {
729 let mut mesh = Mesh {
730 parts: 1,
731 ..Mesh::default()
732 };
733 for part in parts {
734 let base = mesh.vertex_count() as u32;
735 for p in part.positions.chunks_exact(3) {
736 if let [x, y, z] = *p {
737 mesh.positions.extend(apply(xform, [x, y, z]));
738 }
739 }
740 mesh.indices.extend(part.indices.iter().map(|i| i + base));
741 }
742 out.append(weld(mesh));
743 }
744 Geometry::Instance {
745 definition,
746 xform: local,
747 } => {
748 if depth >= MAX_DEPTH {
749 return;
750 }
751 let child = multiply(xform, local);
752 for c in self.members(definition, by_id) {
753 self.emit(c, by_id, &child, depth + 1, out);
754 }
755 }
756 }
757 }
758}
759
760fn scaling(s: f64) -> [[f64; 4]; 4] {
761 [
762 [s, 0.0, 0.0, 0.0],
763 [0.0, s, 0.0, 0.0],
764 [0.0, 0.0, s, 0.0],
765 [0.0, 0.0, 0.0, 1.0],
766 ]
767}
768
769fn multiply(a: &[[f64; 4]; 4], b: &[[f64; 4]; 4]) -> [[f64; 4]; 4] {
771 let mut out = [[0.0; 4]; 4];
772 for (i, row) in out.iter_mut().enumerate() {
773 for (j, v) in row.iter_mut().enumerate() {
774 *v = (0..4)
775 .map(|k| {
776 a.get(i).and_then(|r| r.get(k)).copied().unwrap_or(0.0)
777 * b.get(k).and_then(|r| r.get(j)).copied().unwrap_or(0.0)
778 })
779 .sum();
780 }
781 }
782 out
783}
784
785fn apply(m: &[[f64; 4]; 4], [x, y, z]: [f32; 3]) -> [f32; 3] {
787 let p = [f64::from(x), f64::from(y), f64::from(z), 1.0];
788 let row = |r: &[f64; 4]| r.iter().zip(p).map(|(a, b)| a * b).sum::<f64>();
789 let [r0, r1, r2, r3] = m;
790 let w = row(r3);
791 let w = if w == 0.0 || !w.is_finite() { 1.0 } else { w };
792 [
793 (row(r0) / w) as f32,
794 (row(r1) / w) as f32,
795 (row(r2) / w) as f32,
796 ]
797}
798
799fn weld(mesh: Mesh) -> Mesh {
802 let mut map: HashMap<[i64; 3], u32> = HashMap::with_capacity(mesh.vertex_count());
803 let mut remap = Vec::with_capacity(mesh.vertex_count());
804 let mut out = Mesh {
805 parts: mesh.parts,
806 ..Mesh::default()
807 };
808 for p in mesh.positions.chunks_exact(3) {
809 let key = [0, 1, 2].map(|k| {
810 p.get(k)
811 .map_or(0, |&c| (f64::from(c) * 1000.0).round() as i64)
812 });
813 let next = out.vertex_count() as u32;
814 let index = *map.entry(key).or_insert_with(|| {
815 out.positions.extend_from_slice(p);
816 next
817 });
818 remap.push(index);
819 }
820 for t in mesh.indices.chunks_exact(3) {
821 let [a, b, c] = [0, 1, 2].map(|k| {
822 t.get(k)
823 .and_then(|&i| remap.get(i as usize))
824 .copied()
825 .unwrap_or(0)
826 });
827 if a != b && b != c && a != c {
828 out.indices.extend([a, b, c]);
829 }
830 }
831 out
832}
833
834struct Attributes {
839 id: Option<[u8; 16]>,
840 layer: i32,
841 mode: u8,
842 visible: bool,
843}
844
845impl Default for Attributes {
846 fn default() -> Self {
847 Attributes {
848 id: None,
849 layer: 0,
850 mode: 0,
851 visible: true,
852 }
853 }
854}
855
856fn read_attributes(mut c: Cur<'_>, version: u32, opennurbs: u64) -> Result<Attributes> {
858 let (_, minor) = c.version()?;
859 let mut a = Attributes {
860 id: Some(c.uuid()?),
861 layer: c.i32()?,
862 ..Attributes::default()
863 };
864 if version >= 5 && opennurbs >= 200_712_190 {
865 loop {
867 match c.u8()? {
868 0 => break,
869 1 | 2 => c.skip_string()?,
870 3 | 4 | 6 | 7 | 10 | 22 => c.skip(4)?,
871 5 => {
872 c.body()?;
873 }
874 8 => c.skip(8)?,
875 9 | 13..=17 | 19 => c.skip(1)?,
876 11 => a.visible = c.u8()? != 0,
877 12 => a.mode = c.u8()? & 0x0F,
878 18 => {
879 let n = c.count(4)?;
880 c.skip_items(n, 4)?;
881 }
882 20 => c.skip(16)?,
883 21 => {
884 let n = c.count(32)?;
885 c.skip_items(n, 32)?;
886 }
887 _ => break,
888 }
889 }
890 return Ok(a);
891 }
892 c.skip(4 + 4 + 20 + 4)?;
894 a.mode = c.u8()? & 0x0F;
895 a.visible = a.mode != 1;
896 c.skip(3)?;
897 c.skip_string()?;
898 c.skip_string()?;
899 if minor >= 1 {
900 let n = c.count(4)?;
901 c.skip_items(n, 4)?;
902 if minor >= 2 {
903 a.visible = c.u8()? != 0;
904 }
905 }
906 Ok(a)
907}
908
909fn definition(mut c: Cur<'_>, version: u32) -> Result<([u8; 16], Vec<[u8; 16]>)> {
911 let ids = |c: &mut Cur<'_>| -> Result<Vec<[u8; 16]>> {
912 let n = c.count(16)?;
913 (0..n).map(|_| c.uuid()).collect()
914 };
915 if version >= 60 {
916 let mut probe = c;
917 let first = probe.chunk()?;
918 if let (TCODE_ANONYMOUS, Some(mut a)) = (first.tcode, first.body) {
919 a.version2()?;
920 let mut attributes = a.body()?; attributes.version2()?;
922 let bits = attributes.u32()?;
923 if bits & 1 == 0 {
924 bail!("block definition without id");
925 }
926 let id = attributes.uuid()?;
927 a.skip(4)?; a.body()?; a.skip_string()?;
930 a.skip_string()?;
931 a.skip_string()?;
932 a.skip(48)?; let objects = if a.u8()? != 0 {
934 ids(&mut a)?
935 } else {
936 Vec::new()
937 };
938 return Ok((id, objects));
939 }
940 }
941 c.version()?;
942 let id = c.uuid()?;
943 Ok((id, ids(&mut c)?))
944}
945
946fn compressed(c: &mut Cur<'_>, size: usize) -> Result<Vec<u8>> {
953 if size == 0 {
954 return Ok(Vec::new());
955 }
956 let crc = c.u32()?;
957 let out = match c.u8()? {
958 0 => c.bytes(size)?.to_vec(),
959 1 => {
960 let chunk = c.chunk()?;
961 let (TCODE_ANONYMOUS, Some(mut z)) = (chunk.tcode, chunk.body) else {
962 bail!("The Rhino file is damaged: compressed data expected.");
963 };
964 let input = z.bytes(z.left())?;
965 if size / 1032 > input.len() {
967 bail!("The Rhino file is damaged: compressed data too short.");
968 }
969 let mut out = Vec::with_capacity(size);
970 flate2::read::ZlibDecoder::new(input)
971 .take(size as u64 + 1)
972 .read_to_end(&mut out)
973 .map_err(|e| anyhow!("The Rhino file is damaged: {e}."))?;
974 out
975 }
976 m => bail!("The Rhino file is damaged: unknown compression {m}."),
977 };
978 if out.len() != size {
979 bail!("The Rhino file is damaged: compressed data has the wrong size.");
980 }
981 let mut check = flate2::Crc::new();
982 check.update(&out);
983 if check.sum() != crc {
984 bail!("The Rhino file is damaged: checksum error in compressed data.");
985 }
986 Ok(out)
987}
988
989fn floats(bytes: &[u8]) -> Vec<f32> {
990 super::quads(bytes).map(f32::from_le_bytes).collect()
991}
992
993fn part(positions: Vec<f32>, faces: impl Iterator<Item = [u32; 4]>) -> Part {
996 let n = (positions.len() / 3) as u32;
997 let finite = |i: u32| {
998 positions
999 .get(i as usize * 3..i as usize * 3 + 3)
1000 .is_some_and(|p| p.iter().all(|v| v.is_finite()))
1001 };
1002 let mut indices = Vec::new();
1003 for [a, b, c, d] in faces {
1004 if a >= n || b >= n || c >= n || d >= n || ![a, b, c, d].into_iter().all(finite) {
1005 continue;
1006 }
1007 indices.extend([a, b, c]);
1008 if c != d {
1009 indices.extend([a, c, d]);
1010 }
1011 }
1012 Part { positions, indices }
1013}
1014
1015fn mesh(mut c: Cur<'_>) -> Result<Part> {
1017 let (major, _) = c.version()?;
1018 if major != 1 && major != 3 {
1019 bail!("unsupported mesh version {major}");
1020 }
1021 let vcount = c.u32()? as usize;
1022 let fcount = c.u32()? as usize;
1023 if vcount > 4 * MAX_TRIANGLES || fcount > MAX_TRIANGLES {
1024 bail!("The Rhino file is damaged: too many mesh vertices or faces.");
1025 }
1026 c.skip(8 * 8 + 2 * 8 + 16 * 4 + 4)?;
1028 if c.u8()? != 0 {
1029 c.body()?; }
1031 for _ in 0..4 {
1032 if c.u8()? != 0 {
1033 c.body()?; }
1035 }
1036 let index_size = c.i32()?;
1037 let index_size = match index_size {
1038 1 | 2 | 4 => index_size as usize,
1039 _ if fcount == 0 => 4,
1040 other => bail!("unsupported face index size {other}"),
1041 };
1042 if fcount.saturating_mul(4 * index_size) > c.left() {
1043 bail!("The Rhino file is damaged: too many mesh faces.");
1044 }
1045 let face_bytes = c.bytes(fcount * 4 * index_size)?;
1046 let index = |i: usize| -> u32 {
1047 let at = i * index_size;
1048 match index_size {
1049 1 => face_bytes.get(at).map_or(u32::MAX, |&b| u32::from(b)),
1050 2 => face_bytes
1051 .get(at..at + 2)
1052 .and_then(|s| <[u8; 2]>::try_from(s).ok())
1053 .map_or(u32::MAX, |b| u32::from(u16::from_le_bytes(b))),
1054 _ => face_bytes
1055 .get(at..at + 4)
1056 .and_then(|s| <[u8; 4]>::try_from(s).ok())
1057 .map_or(u32::MAX, u32::from_le_bytes),
1058 }
1059 };
1060 let positions = if major == 1 {
1061 let n = c.count(12)?;
1062 floats(c.bytes(n * 12)?)
1063 } else if vcount > 0 {
1064 let size = c.u32()? as usize;
1065 if size != 0 && size != vcount.saturating_mul(12) {
1066 bail!("The Rhino file is damaged: vertex buffer has the wrong size.");
1067 }
1068 floats(&compressed(&mut c, size)?)
1069 } else {
1070 Vec::new()
1071 };
1072 let faces = (0..fcount).map(|f| [0, 1, 2, 3].map(|k| index(f * 4 + k)));
1073 Ok(part(positions, faces))
1074}
1075
1076fn brep_meshes(mut c: Cur<'_>) -> Result<Vec<Part>> {
1078 let (major, minor) = c.version()?;
1079 if major != 3 || minor < 1 {
1080 return Ok(Vec::new());
1081 }
1082 for _ in 0..8 {
1084 c.body()?;
1085 }
1086 c.skip(48)?; let chunk = c.chunk()?;
1088 let (TCODE_ANONYMOUS, Some(mut meshes)) = (chunk.tcode, chunk.body) else {
1089 bail!("The Rhino file is damaged: B-rep render meshes expected.");
1090 };
1091 let mut out = Vec::new();
1092 while meshes.left() > 0 {
1093 if meshes.u8()? == 0 {
1094 continue;
1095 }
1096 let (class, data) = read_object(&mut meshes)?;
1097 if let (true, Some(data)) = (class == MESH, data) {
1098 out.push(mesh(data)?);
1099 }
1100 }
1101 Ok(out)
1102}
1103
1104fn extrusion_mesh(mut c: Cur<'_>) -> Result<Option<Part>> {
1106 let mut e = c.body()?;
1107 let (major, minor) = e.version2()?;
1108 if major != 1 || minor < 3 {
1109 return Ok(None);
1110 }
1111 read_object(&mut e)?; e.skip(6 * 8 + 2 * 8 + 3 * 8 + 2 + 6 * 8 + 2 * 8 + 1 + 4 + 2)?;
1114 let mut cache = e.body()?;
1115 cache.version2()?;
1116 while cache.u8()? == 1 {
1117 let mut item = cache.body()?;
1118 item.version2()?;
1119 item.uuid()?;
1120 let (class, data) = read_object(&mut item)?;
1121 if let (true, Some(data)) = (class == MESH, data) {
1122 return Ok(Some(mesh(data)?));
1124 }
1125 }
1126 Ok(None)
1127}
1128
1129fn v1_mesh(mut c: Cur<'_>) -> Result<Part> {
1131 let chunk = c.chunk()?;
1132 let (TCODE_V1_MESH_GEOMETRY, Some(mut g)) = (chunk.tcode, chunk.body) else {
1133 bail!("not a Rhino 1 mesh");
1134 };
1135 let points = g.count(6)?;
1136 let faces = g.count(8)?;
1137 g.skip(8)?; let min: [f64; 3] = g.f64s()?;
1139 let max: [f64; 3] = g.f64s()?;
1140 let mut positions = Vec::with_capacity(points * 3);
1141 for _ in 0..points {
1142 for (lo, hi) in min.iter().zip(max) {
1143 let q = f64::from(g.u16()?);
1144 positions.push((lo + (hi - lo) * q / 65535.0) as f32);
1145 }
1146 }
1147 let wide = points >= 65535;
1148 let mut list = Vec::with_capacity(faces);
1149 for _ in 0..faces {
1150 let mut f = [0u32; 4];
1151 for v in &mut f {
1152 *v = if wide { g.u32()? } else { u32::from(g.u16()?) };
1153 }
1154 list.push(f);
1155 }
1156 Ok(part(positions, list.into_iter()))
1157}
1158
1159pub fn preview_dib(data: &[u8]) -> Option<Vec<u8>> {
1166 let (_, mut cur) = open(data).ok()?;
1167 while let Some(c) = cur.next().ok()? {
1168 if c.tcode == TCODE_PROPERTIES_TABLE {
1169 let mut table = c.body?;
1170 while let Some(c) = table.next().ok()? {
1171 if let (TCODE_COMPRESSED_PREVIEW, Some(mut b)) = (c.tcode, c.body) {
1172 return dib(&mut b).ok();
1173 }
1174 }
1175 return None;
1176 }
1177 }
1178 None
1179}
1180
1181fn dib(b: &mut Cur<'_>) -> Result<Vec<u8>> {
1182 let mut header = Vec::with_capacity(40);
1183 let _size = b.u32()?;
1184 header.extend(40u32.to_le_bytes());
1185 header.extend(b.bytes(8)?); header.extend(b.bytes(4)?); header.extend(b.bytes(24)?); let pixels = b.u32()? as usize;
1189 if pixels > 64 * 1024 * 1024 {
1190 bail!("preview too large");
1191 }
1192 let mut out = header;
1193 out.extend(compressed(b, pixels)?);
1194 Ok(out)
1195}
1196
1197mod subd {
1202 use std::collections::HashMap;
1205
1206 use anyhow::{bail, Result};
1207
1208 use super::{Cur, Part, MAX_SUBD_FACES, MAX_SUBD_LEVELS};
1209
1210 const END: u8 = 255;
1212 const ANONYMOUS: u8 = 254;
1213 const CREASE: u8 = 2;
1214 const CORNER: u8 = 3;
1215
1216 fn addition(c: &mut Cur<'_>, valid: u8) -> Result<u8> {
1218 let sz = c.u8()?;
1219 if sz != 0 && sz != valid && sz != END {
1220 bail!("The Rhino file is damaged: bad SubD data.");
1221 }
1222 Ok(sz)
1223 }
1224
1225 fn finish(c: &mut Cur<'_>) -> Result<()> {
1227 loop {
1228 match c.u8()? {
1229 END => return Ok(()),
1230 ANONYMOUS => {
1231 c.body()?;
1232 }
1233 n => c.skip(usize::from(n))?,
1234 }
1235 }
1236 }
1237
1238 fn base(c: &mut Cur<'_>, v7: bool) -> Result<u32> {
1240 let archive_id = c.u32()?;
1241 c.skip(4 + 2)?; if !v7 {
1243 if c.u8()? != 0 {
1244 c.skip(24)?;
1245 }
1246 if c.u8()? != 0 {
1247 c.skip(24)?;
1248 }
1249 return Ok(archive_id);
1250 }
1251 for (valid, size) in [(24, 24), (4, 4), (5, 5)] {
1252 match addition(c, valid)? {
1253 END => return Ok(archive_id),
1254 0 => {}
1255 _ => c.skip(size)?,
1256 }
1257 }
1258 finish(c)?;
1259 Ok(archive_id)
1260 }
1261
1262 fn list(c: &mut Cur<'_>) -> Result<Vec<(u32, u8)>> {
1264 let n = usize::from(c.u16()?);
1265 if n.saturating_mul(5) > c.left() {
1266 bail!("The Rhino file is damaged: bad SubD list.");
1267 }
1268 (0..n).map(|_| Ok((c.u32()?, c.u8()?))).collect()
1269 }
1270
1271 fn element_end(c: &mut Cur<'_>, v7: bool) -> Result<()> {
1272 if v7 {
1273 finish(c)
1274 } else if c.u8()? != 0 {
1275 bail!("The Rhino file is damaged: bad SubD element.")
1276 } else {
1277 Ok(())
1278 }
1279 }
1280
1281 struct Net {
1282 points: Vec<[f64; 3]>,
1283 corner: Vec<bool>,
1284 creases: Vec<(u32, u32)>,
1285 faces: Vec<Vec<u32>>,
1286 }
1287
1288 fn read(mut c: Cur<'_>, version: u32) -> Result<Option<Net>> {
1289 let v7 = version >= 70;
1290 if c.u8()? != 1 {
1291 return Ok(None);
1292 }
1293 let mut s = c.body()?;
1294 if s.version2()?.0 != 1 {
1295 return Ok(None);
1296 }
1297 let levels = s.u32()?;
1298 s.skip(12 + 48)?;
1299 if levels == 0 {
1300 return Ok(None);
1301 }
1302 let mut l = s.body()?;
1303 l.version2()?;
1304 l.skip(2 + 3 + 48)?;
1305 let [p0, p1, p2, p3] = [l.u32()?, l.u32()?, l.u32()?, l.u32()?];
1306 if !(p0 <= p1 && p1 <= p2 && p2 <= p3) || (p3 - p0) as usize > l.left() / 16 {
1307 bail!("The Rhino file is damaged: bad SubD counts.");
1308 }
1309 let mut vertex = HashMap::new();
1310 let mut net = Net {
1311 points: Vec::new(),
1312 corner: Vec::new(),
1313 creases: Vec::new(),
1314 faces: Vec::new(),
1315 };
1316 for _ in p0..p1 {
1317 let id = base(&mut l, v7)?;
1318 let tag = l.u8()?;
1319 let p: [f64; 3] = l.f64s()?;
1320 l.skip(4)?; if l.u8()? != 0 {
1322 let n = l.count(101)?;
1324 l.skip_items(n, 101)?;
1325 }
1326 list(&mut l)?;
1327 list(&mut l)?;
1328 element_end(&mut l, v7)?;
1329 if !p.iter().all(|v| v.is_finite()) {
1330 bail!("The Rhino file is damaged: SubD point not finite.");
1331 }
1332 vertex.insert(id, net.points.len() as u32);
1333 net.points.push(p);
1334 net.corner.push(tag == CORNER);
1335 }
1336 let mut edges = HashMap::new();
1337 for _ in p1..p2 {
1338 let id = base(&mut l, v7)?;
1339 let tag = l.u8()?;
1340 l.skip(2 + 16 + 8)?; let ends = list(&mut l)?;
1342 list(&mut l)?;
1343 if v7 && version >= 80 {
1344 match l.u8()? {
1345 END => {}
1346 8 => {
1347 l.skip(8)?;
1348 finish(&mut l)?;
1349 }
1350 _ => bail!("The Rhino file is damaged: bad SubD edge."),
1351 }
1352 } else {
1353 element_end(&mut l, v7)?;
1354 }
1355 let end = |k: usize| ends.get(k).and_then(|(v, _)| vertex.get(v)).copied();
1356 if let (Some(a), Some(b)) = (end(0), end(1)) {
1357 edges.insert(id, (a, b));
1358 if tag == CREASE {
1359 net.creases.push((a, b));
1360 }
1361 }
1362 }
1363 for _ in p2..p3 {
1364 base(&mut l, v7)?;
1365 l.skip(4 + 4 + 2)?; let loop_edges = list(&mut l)?;
1367 if v7 {
1368 let mut ended = false;
1370 for (i, valid) in [34u8, 4, 4, 4, 4].into_iter().enumerate() {
1371 match addition(&mut l, valid)? {
1372 END => {
1373 ended = true;
1374 break;
1375 }
1376 0 => {}
1377 _ if i < 4 => l.skip(usize::from(valid))?,
1378 _ => {
1379 let tens = l.u32()? as usize;
1380 for _ in 0..tens {
1381 if l.u8()? != 240 {
1382 bail!("The Rhino file is damaged: bad SubD texture.");
1383 }
1384 l.skip(240)?;
1385 }
1386 let rest = loop_edges.len() % 10;
1387 if rest > 0 {
1388 if usize::from(l.u8()?) != rest * 24 {
1389 bail!("The Rhino file is damaged: bad SubD texture.");
1390 }
1391 l.skip(rest * 24)?;
1392 }
1393 }
1394 }
1395 }
1396 if !ended {
1397 finish(&mut l)?;
1398 }
1399 } else {
1400 element_end(&mut l, v7)?;
1401 }
1402 let corners: Option<Vec<u32>> = loop_edges
1403 .iter()
1404 .map(|(e, flags)| {
1405 edges
1406 .get(e)
1407 .map(|&(a, b)| if flags & 1 == 0 { a } else { b })
1408 })
1409 .collect();
1410 if let Some(f) = corners.filter(|f| f.len() >= 3) {
1411 net.faces.push(f);
1412 }
1413 }
1414 Ok(Some(net))
1415 }
1416
1417 fn key(a: u32, b: u32) -> (u32, u32) {
1418 (a.min(b), a.max(b))
1419 }
1420
1421 fn add(a: [f64; 3], b: [f64; 3], s: f64) -> [f64; 3] {
1422 [a[0] + b[0] * s, a[1] + b[1] * s, a[2] + b[2] * s]
1423 }
1424
1425 fn scale(a: [f64; 3], s: f64) -> [f64; 3] {
1426 [a[0] * s, a[1] * s, a[2] * s]
1427 }
1428
1429 fn point(points: &[[f64; 3]], i: u32) -> [f64; 3] {
1430 points.get(i as usize).copied().unwrap_or([0.0; 3])
1431 }
1432
1433 struct Topology {
1435 edge_faces: HashMap<(u32, u32), Vec<usize>>,
1436 vertex_edges: Vec<Vec<(u32, u32)>>,
1437 vertex_faces: Vec<Vec<usize>>,
1438 }
1439
1440 fn topology(n: usize, faces: &[Vec<u32>]) -> Topology {
1441 let mut t = Topology {
1442 edge_faces: HashMap::new(),
1443 vertex_edges: vec![Vec::new(); n],
1444 vertex_faces: vec![Vec::new(); n],
1445 };
1446 for (fi, f) in faces.iter().enumerate() {
1447 for (i, &a) in f.iter().enumerate() {
1448 let b = f.get((i + 1) % f.len()).copied().unwrap_or(a);
1449 t.edge_faces.entry(key(a, b)).or_default().push(fi);
1450 if let Some(vf) = t.vertex_faces.get_mut(a as usize) {
1451 vf.push(fi);
1452 }
1453 }
1454 }
1455 for &(a, b) in t.edge_faces.keys() {
1456 for v in [a, b] {
1457 if let Some(ve) = t.vertex_edges.get_mut(v as usize) {
1458 ve.push((a, b));
1459 }
1460 }
1461 }
1462 t
1463 }
1464
1465 fn other(e: (u32, u32), v: u32) -> u32 {
1466 if e.0 == v {
1467 e.1
1468 } else {
1469 e.0
1470 }
1471 }
1472
1473 struct Level {
1474 points: Vec<[f64; 3]>,
1475 corner: Vec<bool>,
1476 creases: std::collections::HashSet<(u32, u32)>,
1477 faces: Vec<Vec<u32>>,
1478 }
1479
1480 fn subdivide(l: &Level) -> Level {
1482 let n = l.points.len();
1483 let t = topology(n, &l.faces);
1484 let sharp = |e: &(u32, u32)| {
1485 t.edge_faces.get(e).is_none_or(|f| f.len() != 2) || l.creases.contains(e)
1486 };
1487 let face_points: Vec<[f64; 3]> = l
1488 .faces
1489 .iter()
1490 .map(|f| {
1491 let s = f
1492 .iter()
1493 .fold([0.0; 3], |acc, &v| add(acc, point(&l.points, v), 1.0));
1494 scale(s, 1.0 / f.len() as f64)
1495 })
1496 .collect();
1497 let face_point = |fi: usize| face_points.get(fi).copied().unwrap_or([0.0; 3]);
1498 let mut edge_index = HashMap::with_capacity(t.edge_faces.len());
1499 let mut edge_points = Vec::with_capacity(t.edge_faces.len());
1500 for (e, faces) in &t.edge_faces {
1501 let mid = scale(add(point(&l.points, e.0), point(&l.points, e.1), 1.0), 0.5);
1502 let p = match (sharp(e), faces.as_slice()) {
1503 (false, [f, g]) => {
1504 scale(add(add(mid, face_point(*f), 0.5), face_point(*g), 0.5), 0.5)
1505 }
1506 _ => mid,
1507 };
1508 edge_index.insert(*e, (n + edge_points.len()) as u32);
1509 edge_points.push(p);
1510 }
1511 let mut points = Vec::with_capacity(n + edge_points.len() + face_points.len());
1512 for v in 0..n {
1513 let p = point(&l.points, v as u32);
1514 let edges = t.vertex_edges.get(v).map(Vec::as_slice).unwrap_or(&[]);
1515 let sharp_edges: Vec<&(u32, u32)> = edges.iter().filter(|e| sharp(e)).collect();
1516 let corner = l.corner.get(v).copied().unwrap_or(false);
1517 let q = if corner || sharp_edges.len() > 2 || edges.is_empty() {
1518 p
1519 } else if let [a, b] = sharp_edges.as_slice() {
1520 let s = add(
1521 point(&l.points, other(**a, v as u32)),
1522 point(&l.points, other(**b, v as u32)),
1523 1.0,
1524 );
1525 add(scale(p, 0.75), s, 0.125)
1526 } else {
1527 let k = edges.len() as f64;
1528 let faces = t.vertex_faces.get(v).map(Vec::as_slice).unwrap_or(&[]);
1529 let f = scale(
1530 faces
1531 .iter()
1532 .fold([0.0; 3], |acc, &fi| add(acc, face_point(fi), 1.0)),
1533 1.0 / faces.len().max(1) as f64,
1534 );
1535 let r = scale(
1536 edges.iter().fold([0.0; 3], |acc, e| {
1537 add(
1538 acc,
1539 add(point(&l.points, e.0), point(&l.points, e.1), 1.0),
1540 0.5,
1541 )
1542 }),
1543 1.0 / k,
1544 );
1545 scale(add(add(f, r, 2.0), p, k - 3.0), 1.0 / k)
1546 };
1547 points.push(q);
1548 }
1549 points.extend(edge_points);
1550 let first_face = points.len() as u32;
1551 points.extend(face_points.iter().copied());
1552 let mut faces = Vec::new();
1553 let mut creases = std::collections::HashSet::new();
1554 for (fi, f) in l.faces.iter().enumerate() {
1555 let m = f.len();
1556 for (i, &b) in f.iter().enumerate() {
1557 let a = f.get((i + m - 1) % m).copied().unwrap_or(b);
1558 let c = f.get((i + 1) % m).copied().unwrap_or(b);
1559 let (Some(&next), Some(&prev)) =
1560 (edge_index.get(&key(b, c)), edge_index.get(&key(a, b)))
1561 else {
1562 continue;
1563 };
1564 faces.push(vec![b, next, first_face + fi as u32, prev]);
1565 }
1566 }
1567 for (e, &mid) in &edge_index {
1568 if sharp(e) {
1569 creases.insert(key(e.0, mid));
1570 creases.insert(key(mid, e.1));
1571 }
1572 }
1573 let mut corner = l.corner.clone();
1574 corner.resize(points.len(), false);
1575 Level {
1576 points,
1577 corner,
1578 creases,
1579 faces,
1580 }
1581 }
1582
1583 fn limit(l: &Level) -> Vec<[f64; 3]> {
1585 let t = topology(l.points.len(), &l.faces);
1586 let sharp = |e: &(u32, u32)| {
1587 t.edge_faces.get(e).is_none_or(|f| f.len() != 2) || l.creases.contains(e)
1588 };
1589 (0..l.points.len())
1590 .map(|v| {
1591 let p = point(&l.points, v as u32);
1592 let edges = t.vertex_edges.get(v).map(Vec::as_slice).unwrap_or(&[]);
1593 let sharp_edges: Vec<&(u32, u32)> = edges.iter().filter(|e| sharp(e)).collect();
1594 if l.corner.get(v).copied().unwrap_or(false)
1595 || sharp_edges.len() > 2
1596 || edges.is_empty()
1597 {
1598 return p;
1599 }
1600 if let [a, b] = sharp_edges.as_slice() {
1601 let s = add(
1602 point(&l.points, other(**a, v as u32)),
1603 point(&l.points, other(**b, v as u32)),
1604 1.0,
1605 );
1606 return scale(add(scale(p, 4.0), s, 1.0), 1.0 / 6.0);
1607 }
1608 let n = edges.len() as f64;
1609 let e = edges.iter().fold([0.0; 3], |acc, e| {
1610 add(acc, point(&l.points, other(*e, v as u32)), 1.0)
1611 });
1612 let faces = t.vertex_faces.get(v).map(Vec::as_slice).unwrap_or(&[]);
1613 let f = faces.iter().fold([0.0; 3], |acc, &fi| {
1614 let face = l.faces.get(fi).map(Vec::as_slice).unwrap_or(&[]);
1615 let opposite = face
1616 .iter()
1617 .position(|&x| x as usize == v)
1618 .and_then(|i| face.get((i + 2) % face.len().max(1)))
1619 .copied()
1620 .unwrap_or(v as u32);
1621 add(acc, point(&l.points, opposite), 1.0)
1622 });
1623 scale(
1624 add(add(scale(p, n * n), e, 4.0), f, 1.0),
1625 1.0 / (n * (n + 5.0)),
1626 )
1627 })
1628 .collect()
1629 }
1630
1631 pub(super) fn mesh(data: Cur<'_>, version: u32) -> Result<Option<Part>> {
1633 let Some(net) = read(data, version)? else {
1634 return Ok(None);
1635 };
1636 if net.faces.is_empty() {
1637 return Ok(None);
1638 }
1639 let mut level = Level {
1640 points: net.points,
1641 corner: net.corner,
1642 creases: net.creases.iter().map(|&(a, b)| key(a, b)).collect(),
1643 faces: net.faces,
1644 };
1645 let mut corners: usize = level.faces.iter().map(Vec::len).sum();
1646 let mut steps = 0;
1647 while steps == 0 || (steps < MAX_SUBD_LEVELS && corners.saturating_mul(4) <= MAX_SUBD_FACES)
1648 {
1649 if corners > MAX_SUBD_FACES {
1650 bail!("The SubD has too many faces for the preview.");
1651 }
1652 level = subdivide(&level);
1653 corners = level.faces.len() * 4;
1654 steps += 1;
1655 }
1656 let points = limit(&level);
1657 let mut positions = Vec::with_capacity(points.len() * 3);
1658 for p in &points {
1659 positions.extend(p.map(|v| v as f32));
1660 }
1661 let mut indices = Vec::with_capacity(level.faces.len() * 6);
1662 for f in &level.faces {
1663 if let [a, b, c, d] = f.as_slice() {
1664 indices.extend([*a, *b, *c, *a, *c, *d]);
1665 }
1666 }
1667 Ok(Some(Part { positions, indices }))
1668 }
1669
1670 #[cfg(test)]
1671 mod tests {
1672 use super::*;
1673
1674 fn smooth_cube(steps: usize, creased: bool) -> Vec<[f64; 3]> {
1676 let mut points = Vec::new();
1677 for i in 0..8u32 {
1678 points.push([
1679 f64::from(i & 1) * 10.0,
1680 f64::from((i >> 1) & 1) * 10.0,
1681 f64::from((i >> 2) & 1) * 10.0,
1682 ]);
1683 }
1684 let faces = vec![
1685 vec![0, 2, 3, 1],
1686 vec![4, 5, 7, 6],
1687 vec![0, 1, 5, 4],
1688 vec![2, 6, 7, 3],
1689 vec![0, 4, 6, 2],
1690 vec![1, 3, 7, 5],
1691 ];
1692 let mut creases = std::collections::HashSet::new();
1693 if creased {
1694 for (a, b) in [(4, 5), (5, 7), (7, 6), (6, 4)] {
1696 creases.insert(key(a, b));
1697 }
1698 }
1699 let mut level = Level {
1700 points,
1701 corner: vec![false; 8],
1702 creases,
1703 faces,
1704 };
1705 for _ in 0..steps {
1706 level = subdivide(&level);
1707 }
1708 limit(&level)
1709 }
1710
1711 fn z_range(points: &[[f64; 3]]) -> (f64, f64) {
1712 points.iter().fold((f64::MAX, f64::MIN), |(lo, hi), p| {
1713 (lo.min(p[2]), hi.max(p[2]))
1714 })
1715 }
1716
1717 #[test]
1718 fn the_limit_surface_does_not_depend_on_the_steps() {
1719 let (lo1, hi1) = z_range(&smooth_cube(1, false));
1720 let (lo3, hi3) = z_range(&smooth_cube(3, false));
1721 assert!((hi1 - (5.0 + 340.0 / 81.0)).abs() < 1e-9, "{hi1}");
1723 assert!((lo1 - (5.0 - 340.0 / 81.0)).abs() < 1e-9, "{lo1}");
1724 assert!((hi3 - hi1).abs() < 1e-9 && (lo3 - lo1).abs() < 1e-9);
1725 }
1726
1727 #[test]
1728 fn creases_keep_their_edges() {
1729 let (_, hi) = z_range(&smooth_cube(3, true));
1730 assert!((hi - 10.0).abs() < 1e-9, "{hi}");
1732 }
1733 }
1734}
1735
1736#[cfg(test)]
1737pub(crate) mod tests {
1738 use super::*;
1739
1740 #[test]
1741 fn uuids_are_stored_little_endian() {
1742 assert_eq!(
1743 MESH,
1744 [
1745 0xE4, 0xD4, 0xD7, 0x4E, 0x47, 0xE9, 0xD3, 0x11, 0xBF, 0xE5, 0x00, 0x10, 0x83, 0x01,
1746 0x22, 0xF0
1747 ]
1748 );
1749 }
1750
1751 #[test]
1752 fn units_become_millimetres() {
1753 assert_eq!(millimetres_per_unit(2), 1.0);
1754 assert_eq!(millimetres_per_unit(4), 1000.0);
1755 assert_eq!(millimetres_per_unit(8), 25.4);
1756 assert_eq!(millimetres_per_unit(0), 1.0);
1757 }
1758
1759 #[test]
1760 fn transforms_compose_as_column_vectors() {
1761 let mut shift = scaling(1.0);
1762 shift[0][3] = 10.0;
1763 let twice = multiply(&scaling(2.0), &shift);
1764 assert_eq!(apply(&twice, [1.0, 0.0, 0.0]), [22.0, 0.0, 0.0]);
1766 }
1767
1768 #[test]
1769 fn not_a_rhino_file() {
1770 assert!(read(b"solid cube\nendsolid").is_err());
1771 assert!(read(b"3D Geometry File Format abc").is_err());
1772 }
1773
1774 #[test]
1775 fn a_header_alone_is_an_empty_model() {
1776 let err = read(b"3D Geometry File Format 50").unwrap_err();
1777 assert!(err.to_string().contains("no meshes"), "{err}");
1778 }
1779
1780 fn chunk(tcode: u32, body: &[u8]) -> Vec<u8> {
1783 let mut out = tcode.to_le_bytes().to_vec();
1784 let crc = if tcode & TCODE_CRC != 0 { 4 } else { 0 };
1785 out.extend(((body.len() + crc) as i64).to_le_bytes());
1786 out.extend(body);
1787 out.extend(vec![0; crc]); out
1789 }
1790
1791 fn short(tcode: u32, value: i64) -> Vec<u8> {
1792 let mut out = tcode.to_le_bytes().to_vec();
1793 out.extend(value.to_le_bytes());
1794 out
1795 }
1796
1797 fn object(class: [u8; 16], data: &[u8]) -> Vec<u8> {
1798 let mut body = chunk(TCODE_CLASS_UUID, &class);
1799 body.extend(chunk(TCODE_CLASS_DATA, data));
1800 body.extend(short(TCODE_CLASS_END, 0));
1801 chunk(TCODE_CLASS, &body)
1802 }
1803
1804 fn table(tcode: u32, records: &[Vec<u8>]) -> Vec<u8> {
1805 let mut body = records.concat();
1806 body.extend(short(TCODE_ENDOFTABLE, 0));
1807 chunk(tcode, &body)
1808 }
1809
1810 fn triangle() -> Vec<u8> {
1812 let mut d = vec![0x10];
1813 d.extend(3i32.to_le_bytes());
1814 d.extend(1i32.to_le_bytes());
1815 d.extend(vec![0; 8 * 8 + 2 * 8 + 16 * 4 + 4]);
1816 d.extend([0, 0, 0, 0, 0]);
1817 d.extend(4i32.to_le_bytes());
1818 for i in [0u32, 1, 2, 2] {
1819 d.extend(i.to_le_bytes());
1820 }
1821 d.extend(3i32.to_le_bytes());
1822 for v in [0f32, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0] {
1823 d.extend(v.to_le_bytes());
1824 }
1825 d
1826 }
1827
1828 fn instance(definition: [u8; 16], [x, y, z]: [f64; 3]) -> Vec<u8> {
1829 let mut d = vec![0x10];
1830 d.extend(definition);
1831 let m = [
1832 1.0, 0.0, 0.0, x, 0.0, 1.0, 0.0, y, 0.0, 0.0, 1.0, z, 0.0, 0.0, 0.0, 1.0,
1833 ];
1834 for v in m {
1835 d.extend(f64::to_le_bytes(v));
1836 }
1837 d.extend(vec![0; 48]); d
1839 }
1840
1841 fn record(class: [u8; 16], data: &[u8], id: u8, mode: u8) -> Vec<u8> {
1842 let mut attributes = vec![0x20];
1843 attributes.extend([id; 16]);
1844 attributes.extend(0i32.to_le_bytes());
1845 attributes.extend([12, mode, 0]);
1846 let mut body = short(TCODE_OBJECT_TYPE, 0x20);
1847 body.extend(object(class, data));
1848 body.extend(chunk(TCODE_OBJECT_ATTRIBUTES, &attributes));
1849 body.extend(short(TCODE_OBJECT_END, 0));
1850 chunk(TCODE_OBJECT_RECORD, &body)
1851 }
1852
1853 fn definition(id: u8, members: &[u8]) -> Vec<u8> {
1854 let mut d = vec![0x10];
1855 d.extend([id; 16]);
1856 d.extend((members.len() as i32).to_le_bytes());
1857 for &m in members {
1858 d.extend([m; 16]);
1859 }
1860 chunk(TCODE_IDEF_RECORD, &object([0; 16], &d))
1861 }
1862
1863 fn preview() -> Vec<u8> {
1865 let mut d = Vec::new();
1866 for v in [40i32, 8, 8] {
1867 d.extend(v.to_le_bytes());
1868 }
1869 d.extend(1i16.to_le_bytes());
1870 d.extend(24i16.to_le_bytes());
1871 d.extend([0u8; 24]);
1872 let pixels = [200u8; 8 * 24];
1873 d.extend((pixels.len() as u32).to_le_bytes());
1874 let mut crc = flate2::Crc::new();
1875 crc.update(&pixels);
1876 d.extend(crc.sum().to_le_bytes());
1877 d.push(0);
1878 d.extend(pixels);
1879 chunk(TCODE_COMPRESSED_PREVIEW, &d)
1880 }
1881
1882 pub(crate) fn model() -> Vec<u8> {
1887 let mut file = b"3D Geometry File Format 50".to_vec();
1888 file.extend(table(
1889 TCODE_PROPERTIES_TABLE,
1890 &[short(TCODE_OPENNURBS_VERSION, 201_209_145), preview()],
1891 ));
1892 let mut units = 102i32.to_le_bytes().to_vec();
1893 units.extend(4u32.to_le_bytes());
1894 file.extend(table(TCODE_SETTINGS_TABLE, &[chunk(TCODE_UNITS, &units)]));
1895 file.extend(table(
1896 TCODE_IDEF_TABLE,
1897 &[definition(0xD1, &[0xA1]), definition(0xD2, &[0xA2])],
1898 ));
1899 file.extend(table(
1900 TCODE_OBJECT_TABLE,
1901 &[
1902 record(MESH, &triangle(), 0xA1, 3),
1903 record(
1904 INSTANCE_REF,
1905 &instance([0xD1; 16], [0.0, 0.0, 5.0]),
1906 0xA2,
1907 3,
1908 ),
1909 record(
1910 INSTANCE_REF,
1911 &instance([0xD2; 16], [10.0, 0.0, 0.0]),
1912 0xB1,
1913 0,
1914 ),
1915 record(INSTANCE_REF, &instance([0xD1; 16], [0.0; 3]), 0xB2, 0),
1916 record(MESH, &triangle(), 0xB3, 1),
1917 ],
1918 ));
1919 file.extend(chunk(TCODE_ENDOFFILE, &[0; 8]));
1920 file
1921 }
1922
1923 #[test]
1924 fn blocks_are_placed_hidden_objects_left_out() {
1925 let mesh = read(&model()).unwrap();
1926 mesh.check().unwrap();
1927 assert_eq!(mesh.triangle_count(), 2);
1928 assert_eq!(mesh.parts, 2);
1929 assert_eq!(mesh.bounds(), [0.0, 0.0, 0.0, 11_000.0, 1000.0, 5000.0]);
1930 }
1931
1932 #[test]
1933 fn the_stored_preview_bitmap_is_found() {
1934 let dib = preview_dib(&model()).unwrap();
1935 assert_eq!(dib.len(), 40 + 8 * 24);
1936 assert_eq!(dib.get(..4), Some(&40u32.to_le_bytes()[..]));
1937 assert!(preview_dib(b"3D Geometry File Format 50").is_none());
1938 }
1939
1940 #[test]
1941 fn damaged_models_are_errors_not_panics() {
1942 let data = model();
1943 for cut in 0..data.len() {
1944 let _ = read(data.get(..cut).unwrap_or_default());
1945 }
1946 for at in 32..data.len() {
1947 for v in [0x00, 0xFF, 0x7F] {
1948 let mut copy = data.clone();
1949 if let Some(b) = copy.get_mut(at) {
1950 *b = v;
1951 }
1952 let _ = read(©);
1953 let _ = preview_dib(©);
1954 }
1955 }
1956 }
1957
1958 #[test]
1959 fn welding_joins_coincident_corners() {
1960 let mesh = Mesh {
1961 positions: vec![
1962 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, ],
1965 indices: vec![0, 1, 2, 3, 4, 5],
1966 parts: 1,
1967 ..Mesh::default()
1968 };
1969 let w = weld(mesh);
1970 assert_eq!(w.vertex_count(), 4);
1971 assert_eq!(w.triangle_count(), 2);
1972 }
1973}