1#![deny(clippy::disallowed_methods)]
10
11use cadmpeg_ir::be;
12use cadmpeg_ir::math::Point3;
13use std::collections::{BTreeMap, BTreeSet};
14
15#[derive(Debug, Clone)]
17pub struct Node {
18 pub kind: u8,
20 pub xmt: u32,
22 pub pos: usize,
24 shift: usize,
25 bytes: Vec<u8>,
26}
27
28#[derive(Debug, Clone, Copy)]
30pub struct FaceFields {
31 pub attributes: u32,
33 pub tolerance: f64,
35 pub next_face: u32,
37 pub previous_face: u32,
39 pub loop_xmt: u32,
41 pub shell: u32,
43 pub surface: u32,
45 pub sense: u8,
47}
48
49#[derive(Debug, Clone, Copy)]
51pub struct EdgeFields {
52 pub attributes: u32,
54 pub tolerance: f64,
56 pub fin: u32,
58 pub curve: u32,
60}
61
62#[derive(Debug, Clone, Copy)]
64pub struct ShellFields {
65 pub attributes: u32,
67 pub body: u32,
69 pub next_shell: u32,
71 pub first_face: u32,
73 pub sentinel_0: u32,
75 pub sentinel_1: u32,
77 pub region: u32,
79 pub last_face: u32,
81}
82
83#[derive(Debug, Clone, Copy)]
85pub struct LoopFields {
86 pub attributes: u32,
88 pub fin: u32,
90 pub face: u32,
92 pub next_loop: u32,
94}
95
96#[derive(Debug, Clone, Copy)]
98pub struct FinFields {
99 pub attributes: u32,
101 pub loop_xmt: u32,
103 pub forward: u32,
105 pub backward: u32,
107 pub vertex: u32,
109 pub edge: u32,
111 pub other: u32,
113 pub curve_xmt: u32,
115 pub sense: u8,
117}
118
119#[derive(Debug, Clone, Copy)]
121pub struct VertexFields {
122 pub attributes: u32,
124 pub point: u32,
126 pub tolerance: f64,
128}
129
130impl Node {
131 pub fn attribute_field_offset(&self) -> Option<usize> {
133 match self.kind {
134 13..=16 | 18 => Some(self.pos + 8 + self.shift),
135 17 => Some(self.pos + 4 + self.shift),
136 _ => None,
137 }
138 }
139
140 pub fn end(&self) -> usize {
142 self.pos + self.bytes.len()
143 }
144
145 pub fn compact_tail_offset(&self) -> Option<usize> {
147 let mut at = 8 + self.shift;
148 read_sequence_at(&self.bytes, &mut at, 5)?;
149 matches!(self.bytes.get(at), Some(b'+' | b'-')).then_some(at + 1)
150 }
151
152 pub fn compact_tail_references(&self, count: usize) -> Option<Vec<u32>> {
154 let mut at = self.compact_tail_offset()?;
155 read_sequence_at(&self.bytes, &mut at, count)
156 }
157
158 pub fn byte_at(&self, offset: usize) -> Option<u8> {
160 self.bytes.get(offset + self.shift).copied()
161 }
162
163 pub fn f64_at(&self, offset: usize) -> Option<f64> {
165 be::f64_at(&self.bytes, offset + self.shift)
166 }
167
168 pub fn u32_at(&self, offset: usize) -> Option<u32> {
170 be::u32_at(&self.bytes, offset + self.shift)
171 }
172
173 pub fn face_fields(&self) -> Option<FaceFields> {
175 (self.kind == 14).then_some(())?;
176 let mut at = 8 + self.shift;
177 let attributes = read_and_advance(&self.bytes, &mut at)?;
178 let tolerance = be::f64_at(&self.bytes, at)?;
179 at += 8;
180 let refs = read_sequence_at(&self.bytes, &mut at, 5)?;
181 let sense = *self.bytes.get(at)?;
182 matches!(sense, b'+' | b'-').then_some(())?;
183 Some(FaceFields {
184 attributes,
185 tolerance,
186 next_face: refs[0],
187 previous_face: refs[1],
188 loop_xmt: refs[2],
189 shell: refs[3],
190 surface: refs[4],
191 sense,
192 })
193 }
194
195 pub fn edge_fields(&self) -> Option<EdgeFields> {
197 (self.kind == 16).then_some(())?;
198 let mut at = 8 + self.shift;
199 let attributes = read_and_advance(&self.bytes, &mut at)?;
200 let tolerance = be::f64_at(&self.bytes, at)?;
201 at += 8;
202 let refs = read_sequence_at(&self.bytes, &mut at, 7)?;
203 Some(EdgeFields {
204 attributes,
205 tolerance,
206 fin: refs[0],
207 curve: refs[3],
208 })
209 }
210
211 pub fn shell_fields(&self) -> Option<ShellFields> {
213 (self.kind == 13).then_some(())?;
214 let mut at = 8 + self.shift;
215 let refs = read_sequence_at(&self.bytes, &mut at, 8)?;
216 Some(ShellFields {
217 attributes: refs[0],
218 body: refs[1],
219 next_shell: refs[2],
220 first_face: refs[3],
221 sentinel_0: refs[4],
222 sentinel_1: refs[5],
223 region: refs[6],
224 last_face: refs[7],
225 })
226 }
227
228 pub fn loop_fields(&self) -> Option<LoopFields> {
230 (self.kind == 15).then_some(())?;
231 let mut at = 8 + self.shift;
232 let refs = read_sequence_at(&self.bytes, &mut at, 4)?;
233 Some(LoopFields {
234 attributes: refs[0],
235 fin: refs[1],
236 face: refs[2],
237 next_loop: refs[3],
238 })
239 }
240
241 pub fn fin_fields(&self) -> Option<FinFields> {
243 (self.kind == 17).then_some(())?;
244 let mut at = 4 + self.shift;
245 let refs = read_sequence_at(&self.bytes, &mut at, 9)?;
246 let sense = *self.bytes.get(at)?;
247 matches!(sense, b'+' | b'-').then_some(())?;
248 Some(FinFields {
249 attributes: refs[0],
250 loop_xmt: refs[1],
251 forward: refs[2],
252 backward: refs[3],
253 vertex: refs[4],
254 other: refs[5],
255 edge: refs[6],
256 curve_xmt: refs[7],
257 sense,
258 })
259 }
260
261 pub fn vertex_fields(&self) -> Option<VertexFields> {
263 (self.kind == 18).then_some(())?;
264 let mut at = 8 + self.shift;
265 let refs = read_sequence_at(&self.bytes, &mut at, 5)?;
266 let tolerance = be::f64_at(&self.bytes, at)?;
267 Some(VertexFields {
268 attributes: refs[0],
269 point: refs[4],
270 tolerance,
271 })
272 }
273
274 pub fn point_position(&self) -> Option<Point3> {
276 (self.kind == 29).then_some(())?;
277 let mut at = 8 + self.shift;
278 read_sequence_at(&self.bytes, &mut at, 4)?;
279 let xyz = be::vec3_at(&self.bytes, at)?;
280 xyz.iter()
281 .all(|value| value.is_finite() && (*value * 1000.0).is_finite())
282 .then(|| Point3::new(xyz[0] * 1000.0, xyz[1] * 1000.0, xyz[2] * 1000.0))
283 }
284
285 pub fn surface_geometry(&self) -> Option<cadmpeg_ir::geometry::SurfaceGeometry> {
287 matches!(self.kind, 50..=54).then_some(())?;
288 crate::geometry::decode_surface_record(&self.bytes, self.kind, self.shift)
289 }
290
291 pub fn curve_geometry(&self) -> Option<cadmpeg_ir::geometry::CurveGeometry> {
293 matches!(self.kind, 30..=32).then_some(())?;
294 crate::geometry::decode_curve_record(&self.bytes, self.kind, self.shift)
295 }
296}
297
298#[derive(Debug, Default)]
300pub struct Graph {
301 nodes: BTreeMap<(u8, u32), Node>,
302 by_pos: BTreeMap<usize, (u8, u32)>,
303}
304
305#[derive(Debug, Clone, Copy)]
307pub struct TrimmedCurve {
308 pub xmt: u32,
310 pub basis: u32,
312 pub points: [[f64; 3]; 2],
314 pub parameters: [f64; 2],
316 pub pos: usize,
318}
319
320#[derive(Debug, Clone, Copy)]
322pub struct SurfaceCurve {
323 pub xmt: u32,
325 pub surface: u32,
327 pub pcurve: u32,
329 pub original: u32,
331 pub tolerance: f64,
333 pub pos: usize,
335}
336
337#[derive(Debug, Clone, Copy)]
339pub struct OffsetSurface {
340 pub xmt: u32,
342 pub discriminator: char,
344 pub true_offset: bool,
346 pub support: u32,
348 pub distance: f64,
350 pub pos: usize,
352}
353
354#[derive(Debug, Clone, Copy)]
356pub struct BlendSurface {
357 pub xmt: u32,
359 pub supports: [u32; 2],
361 pub spine: u32,
363 pub offsets: [f64; 2],
365 pub thumb_weights: [f64; 2],
367 pub pos: usize,
369}
370
371#[derive(Debug, Clone, Copy)]
373pub struct CompositeCurve {
374 pub xmt: u32,
376 pub header_references: [u32; 5],
378 pub sense: bool,
380 pub references: [u32; 6],
382 pub delta_twin: bool,
384 pub pos: usize,
386}
387
388pub fn composite_curves(stream: &[u8]) -> Vec<CompositeCurve> {
390 Graph::parse(stream)
391 .of_kind(38)
392 .filter_map(|node| {
393 let mut at = 8 + node.shift;
394 let header = read_sequence_at(&node.bytes, &mut at, 5)?;
395 let sense = match node.bytes.get(at) {
396 Some(b'+') => true,
397 Some(b'-') => false,
398 _ => return None,
399 };
400 at += 1;
401 let references: [u32; 6] =
402 read_sequence_at(&node.bytes, &mut at, 6)?.try_into().ok()?;
403 let chart_with_optional_terms =
404 references[2] > 1 && references[3..=4].iter().all(|reference| *reference >= 1);
405 let null_witness = references[2..=4].iter().all(|reference| *reference == 1);
406 (references.iter().all(|reference| *reference != 0)
407 && (chart_with_optional_terms || null_witness)
408 && (references[0] > 1 || references[1] > 1))
409 .then_some(CompositeCurve {
410 xmt: node.xmt,
411 header_references: header.try_into().ok()?,
412 sense,
413 references,
414 delta_twin: false,
415 pos: node.pos,
416 })
417 })
418 .collect()
419}
420
421pub fn intersection_data_curves(stream: &[u8]) -> Vec<CompositeCurve> {
423 let mut out = Vec::new();
424 let mut seen = BTreeSet::new();
425 for pos in stream
426 .iter()
427 .enumerate()
428 .filter_map(|(pos, byte)| (*byte == 0x5a).then_some(pos))
429 {
430 let Some((xmt, xmt_extra)) = read_xmt(stream, pos + 1) else {
431 continue;
432 };
433 if xmt <= 1 || !seen.insert(xmt) {
434 continue;
435 }
436 let mut at = pos + 1 + 2 + xmt_extra + 4;
437 let mut header_refs = [0u32; 5];
438 let mut valid = true;
439 for reference in &mut header_refs {
440 let Some((value, extra)) = read_xmt(stream, at) else {
441 valid = false;
442 break;
443 };
444 *reference = value;
445 at += 2 + extra;
446 }
447 if !valid || header_refs[0] != 1 {
448 continue;
449 }
450 if header_refs[4] != 1
451 && !stream[..pos]
452 .windows(b"intersection_data".len())
453 .rev()
454 .take(64)
455 .any(|window| window == b"intersection_data")
456 {
457 continue;
458 }
459 let sense = match stream.get(at) {
460 Some(b'+') => true,
461 Some(b'-') => false,
462 _ => continue,
463 };
464 at += 1;
465 let mut references = [0u32; 6];
466 for reference in &mut references {
467 let Some((value, extra)) = read_xmt(stream, at) else {
468 valid = false;
469 break;
470 };
471 *reference = value;
472 at += 2 + extra;
473 }
474 let complete_witness = references[2..=4].iter().all(|reference| *reference > 1);
475 let null_witness = references[2..=4].iter().all(|reference| *reference == 1);
476 if valid
477 && references.iter().all(|reference| *reference != 0)
478 && (complete_witness || null_witness)
479 && (references[0] > 1 || references[1] > 1)
480 {
481 out.push(CompositeCurve {
482 xmt,
483 header_references: header_refs,
484 sense,
485 references,
486 delta_twin: true,
487 pos,
488 });
489 }
490 }
491 out
492}
493
494pub fn blend_surfaces(stream: &[u8]) -> Vec<BlendSurface> {
496 Graph::parse(stream)
497 .of_kind(56)
498 .filter_map(|node| {
499 let mut at = node.compact_tail_offset()?;
500 (*node.bytes.get(at)? == b'R').then_some(())?;
501 at += 1;
502 let refs = read_sequence_at(&node.bytes, &mut at, 3)?;
503 let values = [
504 be::f64_at(&node.bytes, at)?,
505 be::f64_at(&node.bytes, at + 8)?,
506 be::f64_at(&node.bytes, at + 16)?,
507 be::f64_at(&node.bytes, at + 24)?,
508 ];
509 if !values.iter().all(|value| value.is_finite())
510 || node.bytes.get(at + 32..at + 40)? != [0, 1, 0, 1, 0, 1, 0, 1]
511 || refs[0] <= 1
512 || refs[1] <= 1
513 || values[0] == 0.0
514 || values[1] == 0.0
515 || !(values[0] * 1000.0).is_finite()
516 || !(values[1] * 1000.0).is_finite()
517 || (values[0].abs() - values[1].abs()).abs() > 1.0e-9
518 {
519 return None;
520 }
521 Some(BlendSurface {
522 xmt: node.xmt,
523 supports: [refs[0], refs[1]],
524 spine: refs[2],
525 offsets: [values[0] * 1000.0, values[1] * 1000.0],
526 thumb_weights: [values[2], values[3]],
527 pos: node.pos,
528 })
529 })
530 .collect()
531}
532
533pub fn offset_surfaces(stream: &[u8]) -> Vec<OffsetSurface> {
535 Graph::parse(stream)
536 .of_kind(60)
537 .filter_map(|node| {
538 let mut at = node.compact_tail_offset()?;
539 let discriminator = match node.bytes.get(at)? {
540 b'V' => 'V',
541 b'I' => 'I',
542 b'U' => 'U',
543 _ => return None,
544 };
545 at += 1;
546 let true_offset = match node.bytes.get(at)? {
547 0 => false,
548 1 => true,
549 _ => return None,
550 };
551 at += 1;
552 let support = read_and_advance(&node.bytes, &mut at)?;
553 let distance = be::f64_at(&node.bytes, at)?;
554 let distance = distance * 1000.0;
555 (support > 1 && distance.is_finite()).then_some(OffsetSurface {
556 xmt: node.xmt,
557 discriminator,
558 true_offset,
559 support,
560 distance,
561 pos: node.pos,
562 })
563 })
564 .collect()
565}
566
567pub fn surface_curves(stream: &[u8]) -> Vec<SurfaceCurve> {
569 Graph::parse(stream)
570 .of_kind(137)
571 .filter_map(|node| {
572 let mut at = node.compact_tail_offset()?;
573 let refs = read_sequence_at(&node.bytes, &mut at, 3)?;
574 let tolerance = be::f64_at(&node.bytes, at)?;
575 (refs[0] > 1 && refs[1] > 1 && tolerance.is_finite()).then_some(SurfaceCurve {
576 xmt: node.xmt,
577 surface: refs[0],
578 pcurve: refs[1],
579 original: refs[2],
580 tolerance,
581 pos: node.pos,
582 })
583 })
584 .collect()
585}
586
587pub fn trimmed_curves(stream: &[u8]) -> Vec<TrimmedCurve> {
592 Graph::parse(stream)
593 .of_kind(133)
594 .filter_map(|node| {
595 let mut at = node.compact_tail_offset()?;
596 let basis = read_and_advance(&node.bytes, &mut at)?;
597 let mut point_0 = be::vec3_at(&node.bytes, at)?;
598 let mut point_1 = be::vec3_at(&node.bytes, at + 24)?;
599 if point_0
600 .iter()
601 .chain(point_1.iter())
602 .any(|coordinate| !coordinate.is_finite() || !(*coordinate * 1000.0).is_finite())
603 {
604 return None;
605 }
606 for coordinate in point_0.iter_mut().chain(point_1.iter_mut()) {
607 *coordinate *= 1000.0;
608 }
609 let p0 = node.bytes.get(at + 48..at + 56)?;
610 let p1 = node.bytes.get(at + 56..at + 64)?;
611 let p0 = f64::from_be_bytes(p0.try_into().ok()?);
612 let p1 = f64::from_be_bytes(p1.try_into().ok()?);
613 (basis > 1 && p0.is_finite() && p1.is_finite()).then_some(TrimmedCurve {
614 xmt: node.xmt,
615 basis,
616 points: [point_0, point_1],
617 parameters: [p0, p1],
618 pos: node.pos,
619 })
620 })
621 .collect()
622}
623
624impl Graph {
625 pub fn parse(stream: &[u8]) -> Self {
627 let mut graph = Self::default();
628 for pos in 0..stream.len().saturating_sub(3) {
629 if stream[pos] != 0 {
630 continue;
631 }
632 let kind = stream[pos + 1];
633 let Some(len) = fixed_len(kind) else {
634 continue;
635 };
636 let mut candidates = Vec::new();
637 if let Some((xmt, shift)) = read_xmt(stream, pos + 2) {
638 candidates.push((xmt, shift));
639 }
640 if stream.get(pos + 2) == Some(&0xff) {
641 if let Some((xmt, shift)) = read_xmt(stream, pos + 3) {
642 candidates.push((xmt, shift + 1));
643 }
644 }
645 let nodes = candidates
646 .into_iter()
647 .filter_map(|(xmt, shift)| {
648 if xmt <= 1 {
650 return None;
651 }
652 let payload_shift = payload_shift(stream, pos, kind, shift)?;
653 let bytes = stream.get(pos..pos + len + shift + payload_shift)?;
654 let node = Node {
655 kind,
656 xmt,
657 pos,
658 shift,
659 bytes: bytes.to_vec(),
660 };
661 if !node.has_valid_family_framing() {
662 return None;
663 }
664 Some(node)
665 })
666 .collect::<Vec<_>>();
667 let Some(mut node) = nodes.first() else {
668 continue;
669 };
670 if let Some(escaped) = nodes.get(1) {
671 let standard_quality = node.family_quality();
672 let escaped_quality = escaped.family_quality();
673 if escaped_quality > standard_quality
674 || (escaped_quality == standard_quality && escaped.shift == 1)
675 {
676 node = escaped;
677 }
678 }
679 let node = node.clone();
680 let key = (kind, node.xmt);
681 let replace = graph
682 .nodes
683 .get(&key)
684 .is_none_or(|current| node.family_quality() > current.family_quality());
685 if replace {
686 if let Some(current) = graph.nodes.insert(key, node) {
687 graph.by_pos.remove(¤t.pos);
688 }
689 graph.by_pos.insert(pos, key);
690 }
691 }
692 graph
693 }
694
695 pub fn get(&self, kind: u8, xmt: u32) -> Option<&Node> {
697 self.nodes.get(&(kind, xmt))
698 }
699
700 pub fn at_pos(&self, pos: usize) -> Option<&Node> {
702 let &(kind, xmt) = self.by_pos.get(&pos)?;
703 self.get(kind, xmt)
704 }
705
706 pub fn of_kind(&self, kind: u8) -> impl Iterator<Item = &Node> {
708 self.by_pos.values().filter_map(move |key| {
709 let node = self.nodes.get(key)?;
710 (node.kind == kind).then_some(node)
711 })
712 }
713
714 pub fn referenced_curve_xmts(&self) -> BTreeSet<u32> {
717 let mut references = BTreeSet::new();
718 references.extend(
719 self.of_kind(16)
720 .filter_map(Node::edge_fields)
721 .map(|fields| fields.curve)
722 .filter(|reference| *reference > 1),
723 );
724 references.extend(
725 self.of_kind(17)
726 .filter_map(Node::fin_fields)
727 .map(|fields| fields.curve_xmt)
728 .filter(|reference| *reference > 1),
729 );
730 for node in self.of_kind(56) {
731 let Some(mut at) = node.compact_tail_offset() else {
732 continue;
733 };
734 if node.bytes.get(at) != Some(&b'R') {
735 continue;
736 }
737 at += 1;
738 if let Some(spine) = read_sequence_at(&node.bytes, &mut at, 3)
739 .and_then(|items| items.get(2).copied())
740 .filter(|reference| *reference > 1)
741 {
742 references.insert(spine);
743 }
744 }
745 for node in self.of_kind(133) {
746 if let Some(reference) = node
747 .compact_tail_references(1)
748 .and_then(|items| items.first().copied())
749 .filter(|reference| *reference > 1)
750 {
751 references.insert(reference);
752 }
753 }
754 for node in self.of_kind(137) {
755 if let Some(reference) = node
756 .compact_tail_references(3)
757 .and_then(|items| items.get(2).copied())
758 .filter(|reference| *reference > 1)
759 {
760 references.insert(reference);
761 }
762 }
763 references
764 }
765
766 pub fn unique_curve_edge_endpoints(&self, curve_xmt: u32) -> Option<[Point3; 2]> {
768 let edges = self
769 .of_kind(16)
770 .filter_map(Node::edge_fields)
771 .filter(|edge| edge.curve == curve_xmt)
772 .collect::<Vec<_>>();
773 let [edge] = edges.as_slice() else {
774 return None;
775 };
776 let first_fin = self.get(17, edge.fin)?.fin_fields()?;
777 let second_fin = self.get(17, first_fin.forward)?.fin_fields()?;
778 let position = |vertex_xmt| {
779 let point_xmt = self.get(18, vertex_xmt)?.vertex_fields()?.point;
780 self.get(29, point_xmt)?.point_position()
781 };
782 Some([position(first_fin.vertex)?, position(second_fin.vertex)?])
783 }
784
785 pub fn referenced_carrier_xmts(&self) -> BTreeSet<u32> {
787 let mut references = self.referenced_curve_xmts();
788 references.extend(
789 self.of_kind(14)
790 .filter_map(Node::face_fields)
791 .map(|fields| fields.surface)
792 .filter(|reference| *reference > 1),
793 );
794 references.extend(
795 self.of_kind(18)
796 .filter_map(Node::vertex_fields)
797 .map(|fields| fields.point)
798 .filter(|reference| *reference > 1),
799 );
800 references
801 }
802
803 pub fn body_shape_shells(&self) -> Vec<&Node> {
805 self.of_kind(13)
806 .filter(|shell| self.is_body_shape_shell(shell))
807 .collect()
808 }
809
810 pub fn has_complete_body_topology(&self) -> bool {
814 let shells = self.body_shape_shells();
815 if shells.is_empty() {
816 return false;
817 }
818 let mut reachable_fins = BTreeSet::new();
819 for shell in shells {
820 let Some(face_xmts) = self.shell_face_xmts(shell) else {
821 return false;
822 };
823 for face_xmt in face_xmts {
824 let Some(rings) = self.face_loop_rings(face_xmt) else {
825 return false;
826 };
827 if rings.is_empty() {
828 return false;
829 }
830 reachable_fins.extend(rings.into_iter().flat_map(|(_, ring)| ring));
831 }
832 }
833 reachable_fins.iter().all(|xmt| {
834 self.get(17, *xmt)
835 .and_then(Node::fin_fields)
836 .is_some_and(|fields| fields.other == 1 || reachable_fins.contains(&fields.other))
837 })
838 }
839
840 pub fn body_shape_face_count(&self) -> usize {
842 self.body_shape_shells()
843 .into_iter()
844 .filter_map(|shell| self.shell_face_xmts(shell).map(|faces| faces.len()))
845 .sum()
846 }
847
848 pub fn face_loop_rings(&self, face_xmt: u32) -> Option<Vec<(u32, Vec<u32>)>> {
855 let face = self.get(14, face_xmt)?.face_fields()?;
856 let mut loop_xmt = face.loop_xmt;
857 let mut seen_loops = BTreeSet::new();
858 let mut rings = Vec::new();
859 while loop_xmt != 1 {
860 if !seen_loops.insert(loop_xmt) {
861 return None;
862 }
863 let fields = self.get(15, loop_xmt)?.loop_fields()?;
864 if fields.face != face_xmt {
865 return None;
866 }
867 rings.push((loop_xmt, self.fin_ring(loop_xmt, fields.fin)?));
868 loop_xmt = fields.next_loop;
869 }
870 Some(rings)
871 }
872
873 fn fin_ring(&self, loop_xmt: u32, first: u32) -> Option<Vec<u32>> {
874 (first != 1).then_some(())?;
875 let mut current = first;
876 let mut previous = None;
877 let mut seen = BTreeSet::new();
878 let mut ring = Vec::new();
879 loop {
880 if !seen.insert(current) {
881 return (current == first).then_some(ring);
882 }
883 ring.push(current);
884 let fields = self.get(17, current)?.fin_fields()?;
885 let vertex_resolves = self.get(18, fields.vertex).is_some()
886 || (fields.vertex == 1 && fields.forward == current && fields.backward == current);
887 if fields.loop_xmt != loop_xmt
888 || self.get(16, fields.edge).is_none()
889 || !vertex_resolves
890 {
891 return None;
892 }
893 if fields.other != 1 {
894 let other = self.get(17, fields.other)?.fin_fields()?;
895 if other.other != current || other.edge != fields.edge {
896 return None;
897 }
898 }
899 if let Some(previous) = previous {
900 if fields.backward != previous {
901 return None;
902 }
903 }
904 let next = self.get(17, fields.forward)?.fin_fields()?;
905 if next.backward != current {
906 return None;
907 }
908 previous = Some(current);
909 current = fields.forward;
910 }
911 }
912
913 fn is_body_shape_shell(&self, shell: &Node) -> bool {
914 let Some(fields) = shell.shell_fields() else {
915 return false;
916 };
917 if fields.attributes != 1
918 || fields.next_shell != 1
919 || fields.sentinel_0 != 1
920 || fields.sentinel_1 != 1
921 || fields.body <= 1
922 || fields.region <= 1
923 {
924 return false;
925 }
926
927 self.shell_face_xmts(shell).is_some()
928 }
929
930 pub(crate) fn shell_face_xmts(&self, shell: &Node) -> Option<Vec<u32>> {
931 let fields = shell.shell_fields()?;
932 if fields.last_face != 1 {
933 (fields.last_face == fields.first_face).then_some(())?;
934 self.get(14, fields.first_face)
935 .and_then(Node::face_fields)
936 .filter(|face| face.shell == shell.xmt)?;
937 let faces: Vec<_> = self
938 .of_kind(14)
939 .filter(|face| {
940 face.face_fields()
941 .is_some_and(|fields| fields.shell == shell.xmt)
942 })
943 .map(|face| face.xmt)
944 .collect();
945 return (!faces.is_empty()).then_some(faces);
946 }
947
948 let mut face_xmt = fields.first_face;
949 let mut visited = BTreeSet::new();
950 while face_xmt != 1 {
951 if !visited.insert(face_xmt) {
952 return None;
953 }
954 let face = self.get(14, face_xmt).and_then(Node::face_fields)?;
955 if face.shell != shell.xmt {
956 return None;
957 }
958 face_xmt = face.next_face;
959 }
960 (!visited.is_empty()).then(|| visited.into_iter().collect())
961 }
962}
963
964impl Node {
965 fn family_quality(&self) -> usize {
966 match self.kind {
967 13 => self.shell_fields().map_or(0, |fields| {
968 usize::from(fields.attributes == 1)
969 + usize::from(fields.body > 1)
970 + usize::from(fields.first_face > 1)
971 + usize::from(fields.sentinel_0 == 1)
972 + usize::from(fields.sentinel_1 == 1)
973 + usize::from(fields.region > 1)
974 + usize::from(fields.last_face > 0)
975 }),
976 _ => 0,
977 }
978 }
979
980 fn has_valid_family_framing(&self) -> bool {
981 match self.kind {
982 13 => self.shell_fields().is_some(),
983 14 => self.face_fields().is_some(),
984 15 => self.loop_fields().is_some(),
985 16 => self
986 .edge_fields()
987 .is_some_and(|fields| fields.tolerance.is_finite()),
988 17 => self.fin_fields().is_some(),
989 18 => self
990 .vertex_fields()
991 .is_some_and(|fields| fields.tolerance.is_finite()),
992 29 => self.point_position().is_some(),
993 _ => true,
994 }
995 }
996}
997
998fn payload_shift(stream: &[u8], pos: usize, kind: u8, header_shift: usize) -> Option<usize> {
999 if kind == 14 {
1000 let mut at = pos + 8 + header_shift;
1001 let start = at;
1002 read_and_advance(stream, &mut at)?;
1003 at += 8;
1004 read_sequence_at(stream, &mut at, 5)?;
1005 at += 1;
1006 read_sequence_at(stream, &mut at, 5)?;
1007 return Some(at - start - 31);
1008 }
1009 if kind == 16 {
1010 let mut at = pos + 8 + header_shift;
1011 let start = at;
1012 read_and_advance(stream, &mut at)?;
1013 at += 8;
1014 read_sequence_at(stream, &mut at, 7)?;
1015 return Some(at - start - 24);
1016 }
1017 let (offset, before, trailing_bytes, after) = match kind {
1018 13 => (8, 8, 0, 0),
1019 15 => (8, 4, 0, 0),
1020 17 => (4, 9, 1, 0),
1021 18 => (8, 5, 8, 1),
1022 29 => (8, 4, 24, 0),
1023 _ => (0, 0, 0, 0),
1024 };
1025 if before != 0 {
1026 let mut at = pos + offset + header_shift;
1027 let start = at;
1028 read_sequence_at(stream, &mut at, before)?;
1029 at += trailing_bytes;
1030 read_sequence_at(stream, &mut at, after)?;
1031 let compact = before * 2 + trailing_bytes + after * 2;
1032 return Some(at - start - compact);
1033 }
1034 let compact_kind = matches!(
1035 kind,
1036 30..=32 | 38 | 50..=54 | 56 | 60 | 124 | 133 | 134 | 137
1037 );
1038 if !compact_kind {
1039 return Some(0);
1040 }
1041 let mut at = pos + 8 + header_shift;
1042 let start = at;
1043 read_sequence_at(stream, &mut at, 5)?;
1044 matches!(stream.get(at), Some(b'+' | b'-')).then_some(())?;
1045 at += 1;
1046 let common_extra = at - start - 11;
1047 let tail_start = at;
1048 match kind {
1049 38 => {
1050 read_sequence_at(stream, &mut at, 6)?;
1051 }
1052 56 => {
1053 at += 1;
1054 read_sequence_at(stream, &mut at, 3)?;
1055 }
1056 60 => {
1057 at += 2;
1058 read_and_advance(stream, &mut at)?;
1059 }
1060 124 | 134 => {
1061 read_sequence_at(stream, &mut at, 2)?;
1062 }
1063 133 => {
1064 read_and_advance(stream, &mut at)?;
1065 }
1066 137 => {
1067 read_sequence_at(stream, &mut at, 3)?;
1068 }
1069 _ => {}
1070 }
1071 let compact_tail_len = match kind {
1072 38 => 12,
1073 56 => 7,
1074 60 => 4,
1075 124 | 134 => 4,
1076 133 => 2,
1077 137 => 6,
1078 _ => 0,
1079 };
1080 Some(common_extra + at - tail_start - compact_tail_len)
1081}
1082
1083fn read_and_advance(stream: &[u8], at: &mut usize) -> Option<u32> {
1084 let (value, extra) = read_xmt(stream, *at)?;
1085 *at += 2 + extra;
1086 Some(value)
1087}
1088
1089fn read_sequence_at(stream: &[u8], at: &mut usize, count: usize) -> Option<Vec<u32>> {
1090 (0..count).map(|_| read_and_advance(stream, at)).collect()
1091}
1092
1093fn read_xmt(stream: &[u8], at: usize) -> Option<(u32, usize)> {
1096 let first = i16::from_be_bytes([*stream.get(at)?, *stream.get(at + 1)?]);
1097 if first >= 0 {
1098 return Some((first as u32, 0));
1099 }
1100 let remainder = first.unsigned_abs();
1101 let quotient = u16::from_be_bytes([*stream.get(at + 2)?, *stream.get(at + 3)?]);
1102 let value = u32::from(quotient) * 32_767 + u32::from(remainder);
1103 Some((value, 2))
1104}
1105
1106fn fixed_len(kind: u8) -> Option<usize> {
1107 Some(match kind {
1108 12 | 13 => 24,
1109 14 => 39,
1110 15 => 16,
1111 16 => 32,
1112 17 => 23,
1113 18 => 28,
1114 19 => 16,
1115 29 => 40,
1116 30 => 67,
1117 31 => 99,
1118 32 => 107,
1119 38 => 31,
1120 50 => 91,
1121 51 => 99,
1122 52 => 115,
1123 53 => 99,
1124 54 => 107,
1125 56 => 66,
1126 60 => 31,
1127 124 | 134 => 23,
1128 133 => 85,
1129 137 => 33,
1130 _ => return None,
1131 })
1132}