use super::BrepSolid;
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum BodyKind {
#[default]
Solid,
Sheet,
Cellular,
Wire,
}
impl BodyKind {
pub fn is_solid(&self) -> bool {
matches!(self, BodyKind::Solid)
}
pub fn codec_code(self) -> f64 {
match self {
BodyKind::Solid => 0.0,
BodyKind::Sheet => 1.0,
BodyKind::Cellular => 2.0,
BodyKind::Wire => 3.0,
}
}
pub fn from_codec_code(code: f64) -> Option<Self> {
match code {
code if code == 0.0 => Some(BodyKind::Solid),
code if code == 1.0 => Some(BodyKind::Sheet),
code if code == 2.0 => Some(BodyKind::Cellular),
code if code == 3.0 => Some(BodyKind::Wire),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TopologyKind {
Empty,
Wire,
ManifoldSolid,
Sheet,
NonManifoldSolid,
Mixed,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct KindReport {
pub kind: TopologyKind,
pub lamina_edges: usize,
pub manifold_edges: usize,
pub spine_edges: usize,
pub wire_edges: usize,
pub shells: usize,
pub open_shells: usize,
}
pub fn topology_kind(solid: &BrepSolid) -> KindReport {
let degenerate: HashSet<u64> = solid
.edges
.iter()
.filter(|edge| edge.degenerate)
.map(|edge| edge.id)
.collect();
let mut uses: HashMap<u64, usize> = HashMap::default();
let mut faces = 0usize;
let mut open_shells = 0usize;
for shell in &solid.shells {
faces += shell.faces.len();
let mut shell_uses: HashMap<u64, usize> = HashMap::default();
for coedge in shell
.faces
.iter()
.flat_map(|face| &face.loops)
.flat_map(|record| &record.coedges)
{
if degenerate.contains(&coedge.edge_id) {
continue;
}
*uses.entry(coedge.edge_id).or_insert(0) += 1;
*shell_uses.entry(coedge.edge_id).or_insert(0) += 1;
}
if shell_uses.values().any(|&count| count == 1) {
open_shells += 1;
}
}
let mut report = KindReport {
kind: TopologyKind::Empty,
lamina_edges: 0,
manifold_edges: 0,
spine_edges: 0,
wire_edges: 0,
shells: solid.shells.len(),
open_shells,
};
for edge in solid.edges.iter().filter(|edge| !edge.degenerate) {
match uses.get(&edge.id).copied().unwrap_or(0) {
0 => report.wire_edges += 1,
1 => report.lamina_edges += 1,
2 => report.manifold_edges += 1,
_ => report.spine_edges += 1,
}
}
let edges = report.wire_edges + report.lamina_edges + report.manifold_edges + report.spine_edges;
report.kind = if faces == 0 && edges == 0 {
TopologyKind::Empty
} else if faces == 0 {
TopologyKind::Wire
} else if report.wire_edges > 0 {
TopologyKind::Mixed
} else if report.spine_edges > 0 {
TopologyKind::NonManifoldSolid
} else if report.lamina_edges == 0 && report.open_shells == 0 {
TopologyKind::ManifoldSolid
} else if report.shells == 1 {
TopologyKind::Sheet
} else {
TopologyKind::Mixed
};
report
}