Skip to main content

ogeom_topo/
content.rs

1//! A face's content written out, for keeping what was computed from a face
2//! across models in which it recurs.
3
4use ogeom_geom::{Curve, Curve3d as _, PlanarCurve};
5use ogeom_math::{KnotVector, Transform};
6
7use crate::{EdgeRepr, Model, Shape, TShapeId};
8
9/// Everything a face is made of, written out as bytes: its orientation,
10/// placement, tolerance and surface, then each wire in order with each of
11/// its edges' orientation, placement, tolerance, flags, every
12/// representation (a curve, a pcurve, a seam's two pcurves, with their
13/// placements and ranges, and whether a pcurve lies on the face's own
14/// surface or another face's) and vertices. An edge met again in the face
15/// is named by where it was first met, so a seam walked twice reads as one
16/// edge. Geometry is written out, never named by its handle, so the same
17/// face built again in another model reads the same.
18///
19/// Floats are written as their bits, or in their debug form, which reads
20/// back to the same bits, and every piece of varying length carries its
21/// length: two faces read the same only where every one of these is the
22/// same. `None` where any part of the face does not resolve, or an edge
23/// carries a mesh's indices, which name a triangulation of this model by
24/// its handle.
25#[must_use]
26pub fn face_content(model: &Model, face: &Shape) -> Option<Vec<u8>> {
27    let mut out = Vec::new();
28    let data = model.node(face)?.data().as_face()?;
29    let geometry = model.geometry();
30    let datums = model.datums();
31    text(&mut out, format_args!("F{:?}", face.orientation()));
32    placement(&mut out, face.transform(datums).ok()?);
33    text(
34        &mut out,
35        format_args!(
36            "{:?}{}{:?}",
37            data.tolerance,
38            data.natural_restriction,
39            geometry.surface(data.surface)?
40        ),
41    );
42    let mut met: Vec<TShapeId> = Vec::new();
43    let wires = model.ordered_children_of(face).ok()?;
44    count(&mut out, wires.len());
45    for wire in wires {
46        text(&mut out, format_args!("W{:?}", wire.orientation()));
47        placement(&mut out, wire.transform(datums).ok()?);
48        let edges = model.ordered_children_of(&wire).ok()?;
49        count(&mut out, edges.len());
50        for edge in edges {
51            let at = met
52                .iter()
53                .position(|&e| e == edge.node())
54                .unwrap_or_else(|| {
55                    met.push(edge.node());
56                    met.len() - 1
57                });
58            let e = model.node(&edge)?.data().as_edge()?;
59            text(&mut out, format_args!("E{at}{:?}", edge.orientation()));
60            placement(&mut out, edge.transform(datums).ok()?);
61            text(
62                &mut out,
63                format_args!(
64                    "{:?}{}{}{}",
65                    e.tolerance,
66                    e.degenerate,
67                    e.same_parameter(),
68                    e.representations.len()
69                ),
70            );
71            for repr in &e.representations {
72                match repr {
73                    EdgeRepr::Curve3d {
74                        curve,
75                        location,
76                        range,
77                    } => {
78                        out.push(b'C');
79                        space_curve(&mut out, geometry.curve(*curve)?);
80                        placement(&mut out, location.composed(datums).ok()?);
81                        bits(&mut out, &[range.0, range.1]);
82                    }
83                    EdgeRepr::PCurve {
84                        curve,
85                        surface,
86                        location,
87                        range,
88                    } => {
89                        out.push(b'P');
90                        planar_curve(&mut out, geometry.pcurve(*curve)?);
91                        out.push(u8::from(*surface == data.surface));
92                        placement(&mut out, location.composed(datums).ok()?);
93                        bits(&mut out, &[range.0, range.1]);
94                    }
95                    EdgeRepr::Seam {
96                        forward,
97                        reversed,
98                        surface,
99                        location,
100                        range,
101                    } => {
102                        out.push(b'S');
103                        planar_curve(&mut out, geometry.pcurve(*forward)?);
104                        planar_curve(&mut out, geometry.pcurve(*reversed)?);
105                        out.push(u8::from(*surface == data.surface));
106                        placement(&mut out, location.composed(datums).ok()?);
107                        bits(&mut out, &[range.0, range.1]);
108                    }
109                    EdgeRepr::PolygonOnTriangulation { .. } => return None,
110                    other => {
111                        out.push(b'R');
112                        text(&mut out, format_args!("{other:?}"));
113                    }
114                }
115            }
116            let vertices = model.ordered_children_of(&edge).ok()?;
117            count(&mut out, vertices.len());
118            for vertex in vertices {
119                let v = model.node(&vertex)?.data().as_vertex()?;
120                text(&mut out, format_args!("V{:?}", vertex.orientation()));
121                placement(&mut out, vertex.transform(datums).ok()?);
122                bits(&mut out, &[v.point.x, v.point.y, v.point.z]);
123                text(&mut out, format_args!("{:?}", v.tolerance));
124            }
125        }
126    }
127    Some(out)
128}
129
130/// A count or a length.
131fn count(out: &mut Vec<u8>, n: usize) {
132    out.extend_from_slice(&(n as u64).to_le_bytes());
133}
134
135/// Text, after its length.
136fn text(out: &mut Vec<u8>, args: std::fmt::Arguments<'_>) {
137    let written = args.to_string();
138    count(out, written.len());
139    out.extend_from_slice(written.as_bytes());
140}
141
142/// Floats as their bits.
143fn bits(out: &mut Vec<u8>, values: &[f64]) {
144    for x in values {
145        out.extend_from_slice(&x.to_bits().to_le_bytes());
146    }
147}
148
149/// A placement: the identity as a letter, any other written out.
150fn placement(out: &mut Vec<u8>, transform: Transform) {
151    if transform == Transform::IDENTITY {
152        out.push(b'I');
153    } else {
154        out.push(b'T');
155        text(out, format_args!("{transform:?}"));
156    }
157}
158
159fn knots(out: &mut Vec<u8>, knots: &KnotVector) {
160    count(out, knots.degree());
161    count(out, knots.knots().len());
162    bits(out, knots.knots());
163}
164
165/// A curve in space; a spline by its bits, its control points being the
166/// bulk of what a face holds.
167fn space_curve(out: &mut Vec<u8>, curve: &Curve) {
168    match curve {
169        Curve::BSpline(spline) => {
170            out.push(b'B');
171            out.push(u8::from(spline.is_rational()));
172            out.push(u8::from(spline.is_periodic()));
173            knots(out, spline.knots());
174            count(out, spline.control_points().len());
175            for c in spline.control_points() {
176                bits(out, &[c.scaled.x, c.scaled.y, c.scaled.z, c.weight]);
177            }
178        }
179        other => {
180            out.push(b'D');
181            text(out, format_args!("{other:?}"));
182        }
183    }
184}
185
186/// A curve in a chart, a spline by its bits.
187fn planar_curve(out: &mut Vec<u8>, curve: &PlanarCurve) {
188    match curve {
189        PlanarCurve::BSpline(spline) => {
190            out.push(b'B');
191            out.push(u8::from(spline.is_rational()));
192            knots(out, spline.knots());
193            count(out, spline.control_points().len());
194            for c in spline.control_points() {
195                bits(out, &[c.scaled.x, c.scaled.y, c.weight]);
196            }
197        }
198        other => {
199            out.push(b'D');
200            text(out, format_args!("{other:?}"));
201        }
202    }
203}