#![allow(clippy::print_stdout, clippy::expect_used, missing_docs)]
use std::collections::HashMap;
use brepkit_io::step::reader::read_step;
use brepkit_operations::tessellate::tessellate_solid_with_tolerance;
use brepkit_topology::Topology;
use brepkit_topology::edge::EdgeId;
use brepkit_topology::explorer::solid_faces;
fn main() {
let path = std::env::args()
.nth(1)
.expect("usage: step_solid_report <file.step>");
let text = std::fs::read_to_string(&path).expect("read");
let mut topo = Topology::new();
let solids = read_step(&text, &mut topo).expect("read_step");
println!("{path}: {} solid(s)", solids.len());
for (n, &sid) in solids.iter().enumerate() {
let faces = solid_faces(&topo, sid).expect("faces");
let mut mix: HashMap<&str, usize> = HashMap::new();
let mut uses: HashMap<EdgeId, usize> = HashMap::new();
for &fid in &faces {
let face = topo.face(fid).expect("face");
*mix.entry(face.surface().type_tag()).or_default() += 1;
for wid in std::iter::once(face.outer_wire()).chain(face.inner_wires().iter().copied())
{
for oe in topo.wire(wid).expect("wire").edges() {
*uses.entry(oe.edge()).or_default() += 1;
}
}
}
let free = uses.values().filter(|&&c| c == 1).count();
let over = uses.values().filter(|&&c| c > 2).count();
let mesh = tessellate_solid_with_tolerance(&topo, sid, 0.01, 0.5).expect("mesh");
let q = |v: f64| (v * 1e4).round() as i64;
let mut edge_use: HashMap<[i64; 6], usize> = HashMap::new();
for tri in mesh.indices.chunks(3) {
let p: Vec<[i64; 3]> = tri
.iter()
.map(|&i| {
let pt = mesh.positions[i as usize];
[q(pt.x()), q(pt.y()), q(pt.z())]
})
.collect();
for i in 0..3 {
let (a, b) = (p[i], p[(i + 1) % 3]);
let k = if a <= b {
[a[0], a[1], a[2], b[0], b[1], b[2]]
} else {
[b[0], b[1], b[2], a[0], a[1], a[2]]
};
*edge_use.entry(k).or_default() += 1;
}
}
let mesh_bnd = edge_use.values().filter(|&&c| c == 1).count();
let mesh_nm = edge_use.values().filter(|&&c| c > 2).count();
if std::env::var("DUMP_NM").is_ok() {
let mut bad: Vec<_> = edge_use
.iter()
.filter(|&(_, &c)| c > 2)
.map(|(k, &c)| (*k, c))
.collect();
bad.sort_unstable();
for (k, c) in bad.iter().take(40) {
println!(
" nm x{c}: ({:.3},{:.3},{:.3}) -> ({:.3},{:.3},{:.3})",
k[0] as f64 / 1e4,
k[1] as f64 / 1e4,
k[2] as f64 / 1e4,
k[3] as f64 / 1e4,
k[4] as f64 / 1e4,
k[5] as f64 / 1e4,
);
}
}
if let Ok(zs) = std::env::var("MESH_AREA_AT") {
let target: f64 = zs.parse().expect("MESH_AREA_AT");
let mut area = 0.0;
let mut n = 0;
for tri in mesh.indices.chunks(3) {
let p: Vec<_> = tri.iter().map(|&i| mesh.positions[i as usize]).collect();
if p.iter().all(|q| (q.z() - target).abs() < 1e-6) {
let u = p[1] - p[0];
let v = p[2] - p[0];
area += u.cross(v).length() * 0.5;
n += 1;
}
}
println!(" mesh@{target}: tris={n} area={area:.3}");
}
if let Ok(zs) = std::env::var("PLANES_AT") {
let target: f64 = zs.parse().expect("PLANES_AT");
for &fid in &faces {
let face = topo.face(fid).expect("face");
if face.surface().type_tag() != "plane" {
continue;
}
let wire = topo.wire(face.outer_wire()).expect("wire");
let zvals: Vec<f64> = wire
.edges()
.iter()
.flat_map(|oe| {
let e = topo.edge(oe.edge()).expect("edge");
[e.start(), e.end()].map(|v| topo.vertex(v).expect("vertex").point().z())
})
.collect();
let zmin = zvals.iter().copied().fold(f64::MAX, f64::min);
let zmax = zvals.iter().copied().fold(f64::MIN, f64::max);
if (zmax - zmin).abs() < 1e-6 && (zmin - target).abs() < 1e-3 {
let area = brepkit_operations::measure::face_area(&topo, fid, 0.01)
.unwrap_or(f64::NAN);
println!(
" plane@{target}: {fid:?} outer_edges={} inners={} area={area:.3}",
wire.edges().len(),
face.inner_wires().len(),
);
}
}
}
let mut mix: Vec<_> = mix.into_iter().collect();
mix.sort_unstable();
println!(
" solid[{n}]: F={} mix={mix:?} brep_free={free} brep_over={over} tris={} mesh_bnd={mesh_bnd} mesh_nm={mesh_nm}",
faces.len(),
mesh.indices.len() / 3,
);
}
}