1use crate::simplify_math::{
26 averaged_vertex_normals, conjugate_yup_to_zup, invert_affine_row_major,
27 transform_point_row_major, yup_to_zup, zup_to_yup,
28};
29use ifc_lite_geometry::simplify::{simplify_mesh, SimplifyOptions};
30use ifc_lite_geometry::Mesh;
31
32#[derive(Debug, Clone)]
35pub struct SimplifyRecordInput<'a> {
36 pub positions: &'a [f32],
38 pub normals: &'a [f32],
40 pub indices: &'a [u32],
42 pub origin: [f64; 3],
44 pub local_to_world: Option<[f64; 16]>,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum SimplifySkip {
53 NoGeometry,
55 MissingPlacement,
59 SingularPlacement,
61 EmptyResult,
64 InvalidUnitScale,
68}
69
70impl SimplifySkip {
71 pub fn as_str(&self) -> &'static str {
72 match self {
73 SimplifySkip::NoGeometry => "no-geometry",
74 SimplifySkip::MissingPlacement => "missing-placement",
75 SimplifySkip::SingularPlacement => "singular-placement",
76 SimplifySkip::EmptyResult => "empty-result",
77 SimplifySkip::InvalidUnitScale => "invalid-unit-scale",
78 }
79 }
80}
81
82#[derive(Debug, Clone)]
85pub struct SimplifiedElement {
86 pub render_positions: Vec<f32>,
88 pub render_normals: Vec<f32>,
89 pub render_indices: Vec<u32>,
91 pub render_origin: [f64; 3],
93 pub local_positions: Vec<f64>,
96 pub local_indices: Vec<u32>,
99 pub tris_before: u32,
100 pub tris_after: u32,
101 pub cavity_components_dropped: u32,
102}
103
104pub fn simplify_element(
110 records: &[SimplifyRecordInput<'_>],
111 level: u8,
112 rtc_offset: [f64; 3],
113 unit_scale: f64,
114 y_up: bool,
115) -> Result<SimplifiedElement, SimplifySkip> {
116 let l2w_raw = records
119 .iter()
120 .find_map(|r| r.local_to_world)
121 .ok_or(SimplifySkip::MissingPlacement)?;
122 let l2w = if y_up {
123 conjugate_yup_to_zup(&l2w_raw)
124 } else {
125 l2w_raw
126 };
127 let inv_l2w = invert_affine_row_major(&l2w).ok_or(SimplifySkip::SingularPlacement)?;
128 if !(unit_scale.is_finite() && unit_scale > 0.0) {
129 return Err(SimplifySkip::InvalidUnitScale);
130 }
131
132 let mut world: Vec<[f64; 3]> = Vec::new();
136 let mut normals: Vec<f32> = Vec::new();
137 let mut indices: Vec<u32> = Vec::new();
138 let mut have_normals = true;
139 for rec in records {
140 let base = world.len() as u32;
141 let n_verts = rec.positions.len() / 3;
142 let origin = if y_up {
143 yup_to_zup(rec.origin)
144 } else {
145 rec.origin
146 };
147 for chunk in rec.positions.chunks_exact(3) {
148 let p = [chunk[0] as f64, chunk[1] as f64, chunk[2] as f64];
149 let p = if y_up { yup_to_zup(p) } else { p };
150 world.push([p[0] + origin[0], p[1] + origin[1], p[2] + origin[2]]);
151 }
152 if rec.normals.len() == rec.positions.len() {
153 for chunk in rec.normals.chunks_exact(3) {
154 let n = [chunk[0] as f64, chunk[1] as f64, chunk[2] as f64];
155 let n = if y_up { yup_to_zup(n) } else { n };
156 normals.extend_from_slice(&[n[0] as f32, n[1] as f32, n[2] as f32]);
157 }
158 } else {
159 have_normals = false;
160 }
161 for tri in rec.indices.chunks_exact(3) {
162 if (tri[0] as usize) >= n_verts
163 || (tri[1] as usize) >= n_verts
164 || (tri[2] as usize) >= n_verts
165 {
166 continue;
167 }
168 if y_up {
171 indices.extend_from_slice(&[tri[0] + base, tri[2] + base, tri[1] + base]);
172 } else {
173 indices.extend_from_slice(&[tri[0] + base, tri[1] + base, tri[2] + base]);
174 }
175 }
176 }
177 if world.is_empty() || indices.is_empty() {
178 return Err(SimplifySkip::NoGeometry);
179 }
180
181 let mut min = [f64::INFINITY; 3];
185 let mut max = [f64::NEG_INFINITY; 3];
186 for w in &world {
187 for k in 0..3 {
188 min[k] = min[k].min(w[k]);
189 max[k] = max[k].max(w[k]);
190 }
191 }
192 let centre = [
193 0.5 * (min[0] + max[0]),
194 0.5 * (min[1] + max[1]),
195 0.5 * (min[2] + max[2]),
196 ];
197 let mut mesh = Mesh::new();
198 mesh.positions = world
199 .iter()
200 .flat_map(|w| {
201 [
202 (w[0] - centre[0]) as f32,
203 (w[1] - centre[1]) as f32,
204 (w[2] - centre[2]) as f32,
205 ]
206 })
207 .collect();
208 mesh.normals = if have_normals && normals.len() == mesh.positions.len() {
209 normals
210 } else {
211 Vec::new()
212 };
213 mesh.indices = indices;
214 mesh.origin = centre;
215 mesh.local_to_world = Some(l2w);
216
217 let (mut out, stats) = simplify_mesh(&mesh, &SimplifyOptions::for_level(level));
219 if out.indices.is_empty() || out.positions.is_empty() {
220 return Err(SimplifySkip::EmptyResult);
221 }
222 if out.normals.len() != out.positions.len() {
223 out.normals = averaged_vertex_normals(&out.positions, &out.indices);
224 }
225
226 let n_out = out.positions.len() / 3;
228 let mut local_positions: Vec<f64> = Vec::with_capacity(n_out * 3);
229 for chunk in out.positions.chunks_exact(3) {
230 let tw = [
231 chunk[0] as f64 + out.origin[0] + rtc_offset[0],
232 chunk[1] as f64 + out.origin[1] + rtc_offset[1],
233 chunk[2] as f64 + out.origin[2] + rtc_offset[2],
234 ];
235 let local = transform_point_row_major(&inv_l2w, tw);
236 local_positions.extend_from_slice(&[
237 local[0] / unit_scale,
238 local[1] / unit_scale,
239 local[2] / unit_scale,
240 ]);
241 }
242 let local_indices = out.indices.clone();
243
244 let (render_positions, render_normals, render_indices, render_origin) = if y_up {
246 let positions = out
247 .positions
248 .chunks_exact(3)
249 .flat_map(|c| {
250 let p = zup_to_yup([c[0] as f64, c[1] as f64, c[2] as f64]);
251 [p[0] as f32, p[1] as f32, p[2] as f32]
252 })
253 .collect();
254 let normals = out
255 .normals
256 .chunks_exact(3)
257 .flat_map(|c| {
258 let n = zup_to_yup([c[0] as f64, c[1] as f64, c[2] as f64]);
259 [n[0] as f32, n[1] as f32, n[2] as f32]
260 })
261 .collect();
262 let mut indices = out.indices.clone();
263 for tri in indices.chunks_exact_mut(3) {
264 tri.swap(1, 2);
265 }
266 (positions, normals, indices, zup_to_yup(out.origin))
267 } else {
268 (out.positions, out.normals, out.indices, out.origin)
269 };
270
271 Ok(SimplifiedElement {
272 render_positions,
273 render_normals,
274 render_indices,
275 render_origin,
276 local_positions,
277 local_indices,
278 tris_before: stats.tris_before,
279 tris_after: stats.tris_after,
280 cavity_components_dropped: stats.cavity_components_dropped,
281 })
282}
283