#![allow(clippy::must_use_candidate)]
use crate::generated::model as m;
use crate::scene::geometry::{Face, Solid};
use crate::scene::{Ctx, Scene};
impl Scene<'_> {
pub fn all_mesh_groups(&self) -> impl Iterator<Item = MeshGroup<'_>> + '_ {
let cx = self.ctx();
let solids = (0..cx.model.tessellated_solid_arena.items.len()).map(move |i| MeshGroup {
cx,
which: GroupImpl::Solid(m::TessellatedSolidId(i)),
});
let shells = (0..cx.model.tessellated_shell_arena.items.len()).map(move |i| MeshGroup {
cx,
which: GroupImpl::Shell(m::TessellatedShellId(i)),
});
solids.chain(shells)
}
pub fn all_meshes(&self) -> impl Iterator<Item = Mesh<'_>> + '_ {
let cx = self.ctx();
let faces = (0..cx.model.complex_triangulated_face_arena.items.len()).map(move |i| Mesh {
cx,
which: MeshImpl::ComplexFace(m::ComplexTriangulatedFaceId(i)),
});
let sets =
(0..cx.model.complex_triangulated_surface_set_arena.items.len()).map(move |i| Mesh {
cx,
which: MeshImpl::ComplexSurfaceSet(m::ComplexTriangulatedSurfaceSetId(i)),
});
let plain = (0..cx.model.tessellated_face_arena.items.len()).map(move |i| Mesh {
cx,
which: MeshImpl::PlainFace(m::TessellatedFaceId(i)),
});
faces.chain(sets).chain(plain)
}
}
#[derive(Clone, Copy)]
enum MeshImpl {
ComplexFace(m::ComplexTriangulatedFaceId),
ComplexSurfaceSet(m::ComplexTriangulatedSurfaceSetId),
PlainFace(m::TessellatedFaceId),
}
#[derive(Clone, Copy)]
pub struct Mesh<'m> {
cx: Ctx<'m>,
which: MeshImpl,
}
#[derive(Clone, Debug, PartialEq)]
pub enum MeshNormals {
None,
Uniform([f64; 3]),
PerVertex(Vec<[f64; 3]>),
}
type MeshParts<'m> = (
&'m str,
&'m m::CoordinatesListRef,
&'m [Vec<f64>],
&'m [i64],
&'m [Vec<i64>],
&'m [Vec<i64>],
);
fn row3(row: &[f64]) -> [f64; 3] {
[
row.first().copied().unwrap_or(0.0),
row.get(1).copied().unwrap_or(0.0),
row.get(2).copied().unwrap_or(0.0),
]
}
impl<'m> Mesh<'m> {
fn parts(&self) -> MeshParts<'m> {
match self.which {
MeshImpl::ComplexFace(i) => {
let f = self.cx.model.complex_triangulated_face_arena.get(i.0);
(
&f.name,
&f.coordinates,
&f.normals,
&f.pnindex,
&f.triangle_strips,
&f.triangle_fans,
)
}
MeshImpl::ComplexSurfaceSet(i) => {
let s = self
.cx
.model
.complex_triangulated_surface_set_arena
.get(i.0);
(
&s.name,
&s.coordinates,
&s.normals,
&s.pnindex,
&s.triangle_strips,
&s.triangle_fans,
)
}
MeshImpl::PlainFace(i) => {
let f = self.cx.model.tessellated_face_arena.get(i.0);
(&f.name, &f.coordinates, &f.normals, &[], &[], &[])
}
}
}
pub fn name(&self) -> &'m str {
self.parts().0
}
pub fn key(&self) -> m::EntityKey {
match self.which {
MeshImpl::ComplexFace(i) => m::EntityKey::ComplexTriangulatedFace(i),
MeshImpl::ComplexSurfaceSet(i) => m::EntityKey::ComplexTriangulatedSurfaceSet(i),
MeshImpl::PlainFace(i) => m::EntityKey::TessellatedFace(i),
}
}
pub fn points(&self) -> Vec<[f64; 3]> {
let (_, coords, _, pnindex, ..) = self.parts();
let m::CoordinatesListRef::CoordinatesList(id) = coords;
let all = &self
.cx
.model
.coordinates_list_arena
.get(id.0)
.position_coords;
if pnindex.is_empty() {
all.iter().map(|row| row3(row)).collect()
} else {
pnindex
.iter()
.filter_map(|&i| {
let idx = usize::try_from(i).ok()?.checked_sub(1)?;
all.get(idx).map(|row| row3(row))
})
.collect()
}
}
pub fn triangles(&self) -> Vec<[usize; 3]> {
let (_, _, _, _, strips, fans) = self.parts();
let n = self.points().len();
let cx = self.cx;
let mut out = Vec::new();
let mut push = |a: i64, b: i64, c: i64| {
let tri: Option<Vec<usize>> = [a, b, c]
.iter()
.map(|&v| {
usize::try_from(v)
.ok()
.and_then(|v| v.checked_sub(1))
.filter(|&v| v < n)
})
.collect();
if let Some(t) = tri {
out.push([t[0], t[1], t[2]]);
} else {
cx.warn("MESH.triangles: vertex index out of range".to_owned());
}
};
for strip in strips {
for i in 2..strip.len() {
let (a, b, c) = (strip[i - 2], strip[i - 1], strip[i]);
if c == a || c == b {
continue; }
if i % 2 == 0 {
push(a, c, b);
} else {
push(a, b, c);
}
}
}
for fan in fans {
for i in 2..fan.len() {
let (centre, prev, cur) = (fan[0], fan[i - 1], fan[i]);
if cur == fan[i - 2] || prev == fan[i - 2] {
continue; }
push(centre, cur, prev);
}
}
out
}
pub fn normals(&self) -> MeshNormals {
let (_, _, normals, ..) = self.parts();
match normals.len() {
0 => MeshNormals::None,
1 => MeshNormals::Uniform(row3(&normals[0])),
k if k == self.points().len() => {
MeshNormals::PerVertex(normals.iter().map(|row| row3(row)).collect())
}
_ => {
self.cx
.warn("MESH.normals: count is neither 1 nor per-vertex".to_owned());
MeshNormals::None
}
}
}
pub fn face(&self) -> Option<Face<'m>> {
let link = match self.which {
MeshImpl::ComplexFace(i) => self
.cx
.model
.complex_triangulated_face_arena
.get(i.0)
.geometric_link
.as_ref(),
MeshImpl::PlainFace(i) => self
.cx
.model
.tessellated_face_arena
.get(i.0)
.geometric_link
.as_ref(),
MeshImpl::ComplexSurfaceSet(_) => None,
}?;
match link {
m::FaceOrSurfaceRef::AdvancedFace(id) => Some(Face::from_advanced_face(self.cx, *id)),
_ => None,
}
}
}
#[derive(Clone, Copy)]
enum GroupImpl {
Solid(m::TessellatedSolidId),
Shell(m::TessellatedShellId),
}
#[derive(Clone, Copy)]
pub struct MeshGroup<'m> {
cx: Ctx<'m>,
which: GroupImpl,
}
impl<'m> MeshGroup<'m> {
fn items(&self) -> &'m [m::TessellatedStructuredItemRef] {
match self.which {
GroupImpl::Solid(i) => &self.cx.model.tessellated_solid_arena.get(i.0).items,
GroupImpl::Shell(i) => &self.cx.model.tessellated_shell_arena.get(i.0).items,
}
}
pub fn name(&self) -> &'m str {
match self.which {
GroupImpl::Solid(i) => &self.cx.model.tessellated_solid_arena.get(i.0).name,
GroupImpl::Shell(i) => &self.cx.model.tessellated_shell_arena.get(i.0).name,
}
}
pub fn key(&self) -> m::EntityKey {
match self.which {
GroupImpl::Solid(i) => m::EntityKey::TessellatedSolid(i),
GroupImpl::Shell(i) => m::EntityKey::TessellatedShell(i),
}
}
pub fn meshes(&self) -> Vec<Mesh<'m>> {
let cx = self.cx;
self.items()
.iter()
.filter_map(|it| {
let which = match it {
m::TessellatedStructuredItemRef::ComplexTriangulatedFace(id) => {
MeshImpl::ComplexFace(*id)
}
m::TessellatedStructuredItemRef::TessellatedFace(id) => {
MeshImpl::PlainFace(*id)
}
_ => return None,
};
Some(Mesh { cx, which })
})
.collect()
}
pub fn solid(&self) -> Option<Solid<'m>> {
let cx = self.cx;
match self.which {
GroupImpl::Solid(i) => {
let link = cx
.model
.tessellated_solid_arena
.get(i.0)
.geometric_link
.as_ref()?;
match link {
m::ManifoldSolidBrepRef::ManifoldSolidBrep(id) => Some(Solid::from_id(cx, *id)),
m::ManifoldSolidBrepRef::BrepWithVoids(id) => {
Some(Solid::from_void_id(cx, *id))
}
m::ManifoldSolidBrepRef::Complex(_) => None,
}
}
GroupImpl::Shell(i) => {
let link = cx
.model
.tessellated_shell_arena
.get(i.0)
.topological_link
.as_ref()?;
let m::ConnectedFaceSetRef::ClosedShell(shell_id) = link else {
return None;
};
let rg = cx.ref_graph();
let outer_is = |outer: &m::ClosedShellRef| matches!(outer, m::ClosedShellRef::ClosedShell(s) if s == shell_id);
for r in rg.referrers(m::EntityKey::ClosedShell(*shell_id)) {
match r {
m::EntityKey::ManifoldSolidBrep(sid) => {
let brep = cx.model.manifold_solid_brep_arena.get(sid.0);
if outer_is(&brep.outer) {
return Some(Solid::from_id(cx, *sid));
}
}
m::EntityKey::BrepWithVoids(sid) => {
let brep = cx.model.brep_with_voids_arena.get(sid.0);
if outer_is(&brep.outer) {
return Some(Solid::from_void_id(cx, *sid));
}
}
_ => {}
}
}
None
}
}
}
}
impl<'m> Mesh<'m> {
pub fn group(&self) -> Option<MeshGroup<'m>> {
let cx = self.cx;
let rg = cx.ref_graph();
for r in rg.referrers(self.key()) {
let which = match r {
m::EntityKey::TessellatedSolid(id) => GroupImpl::Solid(*id),
m::EntityKey::TessellatedShell(id) => GroupImpl::Shell(*id),
_ => continue,
};
return Some(MeshGroup { cx, which });
}
None
}
}