Files
OneCAD/crates/kernel/src/placement_tests.rs
T

96 lines
5.1 KiB
Rust

use super::*;
use crate::body::{p3, v3};
use mt::builder;
fn assert_topology(source: &Body, placed: &Body, matrix: [[f64; 4]; 4]) {
use std::collections::HashMap;
let mut vertices = HashMap::new();
let mut edges = HashMap::new();
for (a, b) in source.solid.face_iter().zip(placed.solid.face_iter()) {
assert_eq!(a.orientation(), b.orientation());
let aw = a.absolute_boundaries();
let bw = b.absolute_boundaries();
assert_eq!(aw.len(), bw.len());
for (a, b) in aw.iter().zip(bw.iter()) {
assert_eq!(a.len(), b.len());
for (a, b) in a.edge_iter().zip(b.edge_iter()) {
assert_eq!(a.orientation(), b.orientation());
assert_ne!(a.id(), b.id());
assert_eq!(*edges.entry(a.id()).or_insert(b.id()), b.id());
for (a, b) in [(a.absolute_front(), b.absolute_front()), (a.absolute_back(), b.absolute_back())] {
assert_ne!(a.id(), b.id());
assert_eq!(*vertices.entry(a.id()).or_insert(b.id()), b.id());
let p = from_p3(a.point());
let expected = Vec3::new(
matrix[0][0] * p.x + matrix[1][0] * p.y + matrix[2][0] * p.z + matrix[3][0],
matrix[0][1] * p.x + matrix[1][1] * p.y + matrix[2][1] * p.z + matrix[3][1],
matrix[0][2] * p.x + matrix[1][2] * p.y + matrix[2][2] * p.z + matrix[3][2],
);
assert!(from_p3(b.point()).dist(expected) < 1e-9);
}
}
}
}
assert_eq!(edges.len(), source.solid.edge_iter().map(|e| e.id()).collect::<std::collections::HashSet<_>>().len());
}
fn periodic_cylinder() -> Body {
let b0 = builder::vertex(p3(Vec3::new(10., 0., 0.)));
let b1 = builder::vertex(p3(Vec3::new(-10., 0., 0.)));
let t0 = builder::vertex(p3(Vec3::new(10., 0., 20.)));
let t1 = builder::vertex(p3(Vec3::new(-10., 0., 20.)));
let bottom: mt::Wire =
vec![builder::circle_arc(&b0, &b1, p3(Vec3::new(0., 10., 0.))), builder::circle_arc(&b1, &b0, p3(Vec3::new(0., -10., 0.)))].into();
let top: mt::Wire =
vec![builder::circle_arc(&t0, &t1, p3(Vec3::new(0., 10., 20.))), builder::circle_arc(&t1, &t0, p3(Vec3::new(0., -10., 20.)))].into();
let seam = builder::line(&b0, &t0);
let mut boundary: Vec<_> = bottom.edge_iter().cloned().collect();
boundary.push(seam.clone());
boundary.extend(top.inverse().edge_iter().cloned());
boundary.push(seam.inverse());
let wall = mt::Face::new_unchecked(
vec![boundary.into()],
mt::Surface::RevolutedCurve(mt::Processor::new(mt::RevolutedCurve::by_revolution(
seam.inverse().oriented_curve(),
p3(Vec3::ZERO),
v3(Vec3::Z),
))),
);
let lower = builder::try_attach_plane(&[bottom.inverse()]).unwrap();
let upper = builder::try_attach_plane(&[top]).unwrap();
Body::new(Solid::new_unchecked(vec![vec![wall, lower, upper].into()])).unwrap()
}
#[test]
fn periodic_step_seam_survives_identity_rotation_and_translation() {
let source = periodic_cylinder();
let bytes = crate::step_export(&[&source], "placement regression").unwrap();
let imported = crate::step_import(&bytes).unwrap();
assert!(imported.warnings.is_empty(), "{:?}", imported.warnings);
let body = &imported.bodies.first().unwrap().body;
assert!(body.solid.face_iter().any(|f| f.absolute_boundaries().iter().any(|w| !w.is_simple())));
let source_vertices: Vec<_> = body.solid.vertex_iter().map(|v| from_p3(v.point())).collect();
let source_faces = body.solid.face_iter().count();
let unchanged = crate::step_export(&[body], "source snapshot").unwrap();
let before = body.tessellate_with(1e-3, true).unwrap().measure().volume;
assert!((before - 2000. * std::f64::consts::PI).abs() < 1.);
for matrix in [
[[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.]],
[[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [10., 10., 0., 1.]],
[[0., 1., 0., 0.], [-1., 0., 0., 0.], [0., 0., 1., 0.], [10., 10., 30., 1.]],
] {
let moved = transform_matrix(body, matrix).unwrap();
assert_topology(body, &moved, matrix);
assert_eq!(moved.solid.face_iter().count(), source_faces);
assert_eq!(moved.solid.vertex_iter().count(), source_vertices.len());
let after = moved.tessellate_with(1e-3, true).unwrap().measure().volume;
assert!((after - before).abs() < 1e-5, "{after} vs {before}");
let roundtrip = crate::step_import(&crate::step_export(&[&moved], "placement regression").unwrap()).unwrap();
assert!(roundtrip.warnings.is_empty(), "{:?}", roundtrip.warnings);
assert!((roundtrip.bodies.first().unwrap().body.tessellate_with(1e-3, true).unwrap().measure().volume - before).abs() < 1e-4);
}
let rigid = transform(body, Vec3::new(10., 10., 30.), Vec3::ZERO, Vec3::Z, std::f64::consts::FRAC_PI_2).unwrap();
assert_topology(body, &rigid, [[0., 1., 0., 0.], [-1., 0., 0., 0.], [0., 0., 1., 0.], [10., 10., 30., 1.]]);
assert_eq!(crate::step_export(&[body], "source snapshot").unwrap(), unchanged);
}