1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
8pub struct BinaryStlTriangle {
9 pub normal: [f32; 3],
10 pub v0: [f32; 3],
11 pub v1: [f32; 3],
12 pub v2: [f32; 3],
13 pub attribute: u16,
14}
15
16#[derive(Debug, Clone)]
17pub struct BinaryStlMesh {
18 pub header: [u8; 80],
19 pub triangles: Vec<BinaryStlTriangle>,
20}
21
22pub fn new_binary_stl_mesh() -> BinaryStlMesh {
23 let mut header = [0u8; 80];
24 let msg = b"OxiHuman Binary STL";
25 header[..msg.len()].copy_from_slice(msg);
26 BinaryStlMesh {
27 header,
28 triangles: Vec::new(),
29 }
30}
31
32pub fn add_binary_stl_triangle(mesh: &mut BinaryStlMesh, tri: BinaryStlTriangle) {
33 mesh.triangles.push(tri);
34}
35
36fn normalize3(v: [f32; 3]) -> [f32; 3] {
37 let l = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
38 if l < 1e-10 {
39 [0.0, 0.0, 1.0]
40 } else {
41 [v[0] / l, v[1] / l, v[2] / l]
42 }
43}
44
45fn write_f32(buf: &mut Vec<u8>, v: f32) {
46 buf.extend_from_slice(&v.to_le_bytes());
47}
48fn write_u16(buf: &mut Vec<u8>, v: u16) {
49 buf.extend_from_slice(&v.to_le_bytes());
50}
51fn write_u32(buf: &mut Vec<u8>, v: u32) {
52 buf.extend_from_slice(&v.to_le_bytes());
53}
54fn write_vec3(buf: &mut Vec<u8>, v: [f32; 3]) {
55 write_f32(buf, v[0]);
56 write_f32(buf, v[1]);
57 write_f32(buf, v[2]);
58}
59
60pub fn encode_binary_stl(mesh: &BinaryStlMesh) -> Vec<u8> {
61 let mut buf = Vec::with_capacity(84 + mesh.triangles.len() * 50);
62 buf.extend_from_slice(&mesh.header);
63 write_u32(&mut buf, mesh.triangles.len() as u32);
64 for tri in &mesh.triangles {
65 write_vec3(&mut buf, tri.normal);
66 write_vec3(&mut buf, tri.v0);
67 write_vec3(&mut buf, tri.v1);
68 write_vec3(&mut buf, tri.v2);
69 write_u16(&mut buf, tri.attribute);
70 }
71 buf
72}
73
74pub fn mesh_to_binary_stl(positions: &[[f32; 3]], indices: &[u32]) -> BinaryStlMesh {
75 let mut mesh = new_binary_stl_mesh();
76 for tri in indices.chunks(3) {
77 if tri.len() < 3 {
78 continue;
79 }
80 let (a, b, c) = (tri[0] as usize, tri[1] as usize, tri[2] as usize);
81 if a >= positions.len() || b >= positions.len() || c >= positions.len() {
82 continue;
83 }
84 let v0 = positions[a];
85 let v1 = positions[b];
86 let v2 = positions[c];
87 let ab = [v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]];
88 let ac = [v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]];
89 let n = normalize3([
90 ab[1] * ac[2] - ab[2] * ac[1],
91 ab[2] * ac[0] - ab[0] * ac[2],
92 ab[0] * ac[1] - ab[1] * ac[0],
93 ]);
94 add_binary_stl_triangle(
95 &mut mesh,
96 BinaryStlTriangle {
97 normal: n,
98 v0,
99 v1,
100 v2,
101 attribute: 0,
102 },
103 );
104 }
105 mesh
106}
107
108pub fn binary_stl_triangle_count(mesh: &BinaryStlMesh) -> usize {
109 mesh.triangles.len()
110}
111pub fn binary_stl_size_bytes(mesh: &BinaryStlMesh) -> usize {
112 84 + mesh.triangles.len() * 50
113}
114pub fn validate_binary_stl(mesh: &BinaryStlMesh) -> bool {
115 !mesh.triangles.is_empty()
116}
117
118pub fn parse_binary_stl_header(data: &[u8]) -> Option<u32> {
119 if data.len() < 84 {
120 return None;
121 }
122 let count = u32::from_le_bytes([data[80], data[81], data[82], data[83]]);
123 Some(count)
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_new_binary_stl_mesh() {
132 let m = new_binary_stl_mesh();
133 assert_eq!(m.triangles.len(), 0);
134 assert_eq!(m.header.len(), 80);
135 }
136
137 #[test]
138 fn test_mesh_to_binary_stl() {
139 let pos = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.5, 1.0, 0.0]];
140 let idx = vec![0u32, 1, 2];
141 let m = mesh_to_binary_stl(&pos, &idx);
142 assert_eq!(binary_stl_triangle_count(&m), 1);
143 }
144
145 #[test]
146 fn test_encode_binary_stl_size() {
147 let m = new_binary_stl_mesh();
148 let bytes = encode_binary_stl(&m);
149 assert_eq!(bytes.len(), 84);
150 }
151
152 #[test]
153 fn test_encode_binary_stl_with_tri() {
154 let pos = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.5, 1.0, 0.0]];
155 let idx = vec![0u32, 1, 2];
156 let m = mesh_to_binary_stl(&pos, &idx);
157 let bytes = encode_binary_stl(&m);
158 assert_eq!(bytes.len(), 84 + 50);
159 }
160
161 #[test]
162 fn test_binary_stl_size_bytes() {
163 let m = new_binary_stl_mesh();
164 assert_eq!(binary_stl_size_bytes(&m), 84);
165 }
166
167 #[test]
168 fn test_validate_binary_stl_empty_fails() {
169 let m = new_binary_stl_mesh();
170 assert!(!validate_binary_stl(&m));
171 }
172
173 #[test]
174 fn test_parse_binary_stl_header() {
175 let pos = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.5, 1.0, 0.0]];
176 let idx = vec![0u32, 1, 2];
177 let m = mesh_to_binary_stl(&pos, &idx);
178 let bytes = encode_binary_stl(&m);
179 let count = parse_binary_stl_header(&bytes).expect("should succeed");
180 assert_eq!(count, 1);
181 }
182
183 #[test]
184 fn test_header_contains_marker() {
185 let m = new_binary_stl_mesh();
186 let s = std::str::from_utf8(&m.header[..19]).expect("should succeed");
187 assert!(s.contains("OxiHuman"));
188 }
189}