1#![allow(clippy::must_use_candidate)]
12
13use crate::generated::model as m;
14use crate::scene::geometry::{Face, Solid};
15use crate::scene::{Ctx, Scene};
16
17impl Scene<'_> {
19 pub fn all_mesh_groups(&self) -> impl Iterator<Item = MeshGroup<'_>> + '_ {
23 let cx = self.ctx();
24 let solids = (0..cx.model.tessellated_solid_arena.items.len()).map(move |i| MeshGroup {
25 cx,
26 which: GroupImpl::Solid(m::TessellatedSolidId(i)),
27 });
28 let shells = (0..cx.model.tessellated_shell_arena.items.len()).map(move |i| MeshGroup {
29 cx,
30 which: GroupImpl::Shell(m::TessellatedShellId(i)),
31 });
32 solids.chain(shells)
33 }
34
35 pub fn all_meshes(&self) -> impl Iterator<Item = Mesh<'_>> + '_ {
38 let cx = self.ctx();
39 let faces = (0..cx.model.complex_triangulated_face_arena.items.len()).map(move |i| Mesh {
40 cx,
41 which: MeshImpl::ComplexFace(m::ComplexTriangulatedFaceId(i)),
42 });
43 let sets =
44 (0..cx.model.complex_triangulated_surface_set_arena.items.len()).map(move |i| Mesh {
45 cx,
46 which: MeshImpl::ComplexSurfaceSet(m::ComplexTriangulatedSurfaceSetId(i)),
47 });
48 let plain = (0..cx.model.tessellated_face_arena.items.len()).map(move |i| Mesh {
49 cx,
50 which: MeshImpl::PlainFace(m::TessellatedFaceId(i)),
51 });
52 faces.chain(sets).chain(plain)
53 }
54}
55
56#[derive(Clone, Copy)]
57enum MeshImpl {
58 ComplexFace(m::ComplexTriangulatedFaceId),
59 ComplexSurfaceSet(m::ComplexTriangulatedSurfaceSetId),
60 PlainFace(m::TessellatedFaceId),
61}
62
63#[derive(Clone, Copy)]
66pub struct Mesh<'m> {
67 cx: Ctx<'m>,
68 which: MeshImpl,
69}
70
71#[derive(Clone, Debug, PartialEq)]
74pub enum MeshNormals {
75 None,
76 Uniform([f64; 3]),
77 PerVertex(Vec<[f64; 3]>),
78}
79
80type MeshParts<'m> = (
83 &'m str,
84 &'m m::CoordinatesListRef,
85 &'m [Vec<f64>],
86 &'m [i64],
87 &'m [Vec<i64>],
88 &'m [Vec<i64>],
89);
90
91fn row3(row: &[f64]) -> [f64; 3] {
92 [
93 row.first().copied().unwrap_or(0.0),
94 row.get(1).copied().unwrap_or(0.0),
95 row.get(2).copied().unwrap_or(0.0),
96 ]
97}
98
99impl<'m> Mesh<'m> {
100 fn parts(&self) -> MeshParts<'m> {
101 match self.which {
102 MeshImpl::ComplexFace(i) => {
103 let f = self.cx.model.complex_triangulated_face_arena.get(i.0);
104 (
105 &f.name,
106 &f.coordinates,
107 &f.normals,
108 &f.pnindex,
109 &f.triangle_strips,
110 &f.triangle_fans,
111 )
112 }
113 MeshImpl::ComplexSurfaceSet(i) => {
114 let s = self
115 .cx
116 .model
117 .complex_triangulated_surface_set_arena
118 .get(i.0);
119 (
120 &s.name,
121 &s.coordinates,
122 &s.normals,
123 &s.pnindex,
124 &s.triangle_strips,
125 &s.triangle_fans,
126 )
127 }
128 MeshImpl::PlainFace(i) => {
129 let f = self.cx.model.tessellated_face_arena.get(i.0);
130 (&f.name, &f.coordinates, &f.normals, &[], &[], &[])
131 }
132 }
133 }
134
135 pub fn name(&self) -> &'m str {
136 self.parts().0
137 }
138
139 pub fn key(&self) -> m::EntityKey {
141 match self.which {
142 MeshImpl::ComplexFace(i) => m::EntityKey::ComplexTriangulatedFace(i),
143 MeshImpl::ComplexSurfaceSet(i) => m::EntityKey::ComplexTriangulatedSurfaceSet(i),
144 MeshImpl::PlainFace(i) => m::EntityKey::TessellatedFace(i),
145 }
146 }
147
148 pub fn points(&self) -> Vec<[f64; 3]> {
151 let (_, coords, _, pnindex, ..) = self.parts();
152 let m::CoordinatesListRef::CoordinatesList(id) = coords;
153 let all = &self
154 .cx
155 .model
156 .coordinates_list_arena
157 .get(id.0)
158 .position_coords;
159 if pnindex.is_empty() {
160 all.iter().map(|row| row3(row)).collect()
161 } else {
162 pnindex
163 .iter()
164 .filter_map(|&i| {
165 let idx = usize::try_from(i).ok()?.checked_sub(1)?;
166 all.get(idx).map(|row| row3(row))
167 })
168 .collect()
169 }
170 }
171
172 pub fn triangles(&self) -> Vec<[usize; 3]> {
179 let (_, _, _, _, strips, fans) = self.parts();
180 let n = self.points().len();
181 let cx = self.cx;
182 let mut out = Vec::new();
183 let mut push = |a: i64, b: i64, c: i64| {
184 let tri: Option<Vec<usize>> = [a, b, c]
185 .iter()
186 .map(|&v| {
187 usize::try_from(v)
188 .ok()
189 .and_then(|v| v.checked_sub(1))
190 .filter(|&v| v < n)
191 })
192 .collect();
193 if let Some(t) = tri {
194 out.push([t[0], t[1], t[2]]);
195 } else {
196 cx.warn("MESH.triangles: vertex index out of range".to_owned());
197 }
198 };
199 for strip in strips {
200 for i in 2..strip.len() {
201 let (a, b, c) = (strip[i - 2], strip[i - 1], strip[i]);
202 if c == a || c == b {
203 continue; }
205 if i % 2 == 0 {
207 push(a, c, b);
208 } else {
209 push(a, b, c);
210 }
211 }
212 }
213 for fan in fans {
214 for i in 2..fan.len() {
215 let (centre, prev, cur) = (fan[0], fan[i - 1], fan[i]);
216 if cur == fan[i - 2] || prev == fan[i - 2] {
217 continue; }
219 push(centre, cur, prev);
220 }
221 }
222 out
223 }
224
225 pub fn normals(&self) -> MeshNormals {
228 let (_, _, normals, ..) = self.parts();
229 match normals.len() {
230 0 => MeshNormals::None,
231 1 => MeshNormals::Uniform(row3(&normals[0])),
232 k if k == self.points().len() => {
233 MeshNormals::PerVertex(normals.iter().map(|row| row3(row)).collect())
234 }
235 _ => {
236 self.cx
237 .warn("MESH.normals: count is neither 1 nor per-vertex".to_owned());
238 MeshNormals::None
239 }
240 }
241 }
242
243 pub fn face(&self) -> Option<Face<'m>> {
246 let link = match self.which {
247 MeshImpl::ComplexFace(i) => self
248 .cx
249 .model
250 .complex_triangulated_face_arena
251 .get(i.0)
252 .geometric_link
253 .as_ref(),
254 MeshImpl::PlainFace(i) => self
255 .cx
256 .model
257 .tessellated_face_arena
258 .get(i.0)
259 .geometric_link
260 .as_ref(),
261 MeshImpl::ComplexSurfaceSet(_) => None,
262 }?;
263 match link {
264 m::FaceOrSurfaceRef::AdvancedFace(id) => Some(Face::from_advanced_face(self.cx, *id)),
265 _ => None,
266 }
267 }
268}
269
270#[derive(Clone, Copy)]
275enum GroupImpl {
276 Solid(m::TessellatedSolidId),
277 Shell(m::TessellatedShellId),
278}
279
280#[derive(Clone, Copy)]
284pub struct MeshGroup<'m> {
285 cx: Ctx<'m>,
286 which: GroupImpl,
287}
288
289impl<'m> MeshGroup<'m> {
290 fn items(&self) -> &'m [m::TessellatedStructuredItemRef] {
291 match self.which {
292 GroupImpl::Solid(i) => &self.cx.model.tessellated_solid_arena.get(i.0).items,
293 GroupImpl::Shell(i) => &self.cx.model.tessellated_shell_arena.get(i.0).items,
294 }
295 }
296
297 pub fn name(&self) -> &'m str {
298 match self.which {
299 GroupImpl::Solid(i) => &self.cx.model.tessellated_solid_arena.get(i.0).name,
300 GroupImpl::Shell(i) => &self.cx.model.tessellated_shell_arena.get(i.0).name,
301 }
302 }
303
304 pub fn key(&self) -> m::EntityKey {
306 match self.which {
307 GroupImpl::Solid(i) => m::EntityKey::TessellatedSolid(i),
308 GroupImpl::Shell(i) => m::EntityKey::TessellatedShell(i),
309 }
310 }
311
312 pub fn meshes(&self) -> Vec<Mesh<'m>> {
314 let cx = self.cx;
315 self.items()
316 .iter()
317 .filter_map(|it| {
318 let which = match it {
319 m::TessellatedStructuredItemRef::ComplexTriangulatedFace(id) => {
320 MeshImpl::ComplexFace(*id)
321 }
322 m::TessellatedStructuredItemRef::TessellatedFace(id) => {
323 MeshImpl::PlainFace(*id)
324 }
325 _ => return None,
326 };
327 Some(Mesh { cx, which })
328 })
329 .collect()
330 }
331
332 pub fn solid(&self) -> Option<Solid<'m>> {
337 let cx = self.cx;
338 match self.which {
339 GroupImpl::Solid(i) => {
340 let link = cx
341 .model
342 .tessellated_solid_arena
343 .get(i.0)
344 .geometric_link
345 .as_ref()?;
346 match link {
347 m::ManifoldSolidBrepRef::ManifoldSolidBrep(id) => Some(Solid::from_id(cx, *id)),
348 m::ManifoldSolidBrepRef::BrepWithVoids(id) => {
349 Some(Solid::from_void_id(cx, *id))
350 }
351 m::ManifoldSolidBrepRef::Complex(_) => None,
352 }
353 }
354 GroupImpl::Shell(i) => {
355 let link = cx
356 .model
357 .tessellated_shell_arena
358 .get(i.0)
359 .topological_link
360 .as_ref()?;
361 let m::ConnectedFaceSetRef::ClosedShell(shell_id) = link else {
362 return None;
363 };
364 let rg = cx.ref_graph();
365 let outer_is = |outer: &m::ClosedShellRef| matches!(outer, m::ClosedShellRef::ClosedShell(s) if s == shell_id);
366 for r in rg.referrers(m::EntityKey::ClosedShell(*shell_id)) {
367 match r {
368 m::EntityKey::ManifoldSolidBrep(sid) => {
369 let brep = cx.model.manifold_solid_brep_arena.get(sid.0);
370 if outer_is(&brep.outer) {
371 return Some(Solid::from_id(cx, *sid));
372 }
373 }
374 m::EntityKey::BrepWithVoids(sid) => {
375 let brep = cx.model.brep_with_voids_arena.get(sid.0);
376 if outer_is(&brep.outer) {
377 return Some(Solid::from_void_id(cx, *sid));
378 }
379 }
380 _ => {}
381 }
382 }
383 None
384 }
385 }
386 }
387}
388
389impl<'m> Mesh<'m> {
390 pub fn group(&self) -> Option<MeshGroup<'m>> {
393 let cx = self.cx;
394 let rg = cx.ref_graph();
395 for r in rg.referrers(self.key()) {
396 let which = match r {
397 m::EntityKey::TessellatedSolid(id) => GroupImpl::Solid(*id),
398 m::EntityKey::TessellatedShell(id) => GroupImpl::Shell(*id),
399 _ => continue,
400 };
401 return Some(MeshGroup { cx, which });
402 }
403 None
404 }
405}