Files
OneCAD-Draft/tests/ucs_face_plane.rs
T
Matus Mastena 930cdc2df8 Cover the face-to-plane path with tests
UCS FACE had no test at all: it was only known to compile. The plane
it builds has two halves and they sit either side of a visibility
boundary, so the coverage splits to match.

tests/ucs_face_plane.rs takes the public route — extrude a disc, then
check the caps come back as distinct planar faces with opposite
outward normals, that an off-centre pick lands on the same face as a
central one, and that the curved wall is never offered as a plane.

The axis construction is pub(crate), so it is unit-tested next to
ucs_from_normal instead: Z is the normal it was handed, the axes stay
orthonormal, a normal along world Z still yields a plane rather than
crossing with itself, the same normal is reproducible, and a zero
vector is refused rather than silently producing a degenerate UCS.
2026-09-17 16:54:11 +02:00

81 lines
3.3 KiB
Rust

//! `UCS FACE` turns a pick on a solid into a drawing plane. The plane is only
//! as good as the face lookup underneath it, so this covers that half: given a
//! body and a point on one of its faces, does the right face come back, and is
//! its outward normal the one a sketch should be built on?
//!
//! The axis construction on top of that normal is unit-tested beside
//! `ucs_from_normal` in `src/app/helpers.rs`, which is `pub(crate)` and so out
//! of reach from here.
use acadrust::{entities::Circle, types::Vector3, EntityType};
use OpenCADStudio::scene::model::{presspull_model, solid_model};
/// Extrude a disc into a puck 10 units tall, centred on the origin.
fn puck() -> cadkernel::brep::Body {
let entity = EntityType::Circle(Circle::from_center_radius(
Vector3::new(0.0, 0.0, 0.0),
100.0,
));
let body = presspull_model::extrusion_body(&entity, [0.0, 0.0, 10.0])
.expect("a closed circular profile extrudes");
assert!(body.validate().is_empty(), "extrusion produced a valid body");
body
}
/// The two flat ends point away from the material, in opposite directions.
/// Getting this wrong is what would make a sketch land inside the solid, or
/// extrude back into it.
#[test]
fn opposite_faces_report_opposite_outward_normals() {
let body = puck();
let top = solid_model::nearest_planar_face(&body, [0.0, 0.0, 10.0])
.expect("the top cap is planar");
let bottom = solid_model::nearest_planar_face(&body, [0.0, 0.0, 0.0])
.expect("the bottom cap is planar");
assert_ne!(top, bottom, "the caps are distinct faces");
let top_normal = solid_model::planar_face_normal(&body, top).expect("top cap has a normal");
let bottom_normal =
solid_model::planar_face_normal(&body, bottom).expect("bottom cap has a normal");
assert!(
(top_normal[2] - 1.0).abs() < 1e-9,
"top cap should point +Z, got {top_normal:?}"
);
assert!(
(bottom_normal[2] + 1.0).abs() < 1e-9,
"bottom cap should point -Z, got {bottom_normal:?}"
);
}
/// A pick anywhere on a cap resolves to that cap, not merely to the one
/// nearest the origin. A sketch is started by clicking somewhere on a face,
/// rarely at its centre.
#[test]
fn an_off_centre_pick_still_lands_on_its_own_face() {
let body = puck();
let centre = solid_model::nearest_planar_face(&body, [0.0, 0.0, 10.0]).unwrap();
let off_centre = solid_model::nearest_planar_face(&body, [60.0, -25.0, 10.0]).unwrap();
assert_eq!(centre, off_centre, "both picks are on the top cap");
}
/// The curved side is not a plane, so it cannot become a sketch plane. The
/// lookup has to decline rather than hand back a nonsense normal.
#[test]
fn the_curved_side_is_not_offered_as_a_plane() {
let body = puck();
// Halfway up the barrel, on its surface.
let face = solid_model::nearest_planar_face(&body, [100.0, 0.0, 5.0]);
if let Some(face) = face {
// A planar cap may still be the nearest planar face; what must never
// happen is the barrel itself being reported as planar.
let normal = solid_model::planar_face_normal(&body, face)
.expect("whatever came back claims to be planar, so it has a normal");
assert!(
normal[2].abs() > 1e-9,
"a cap normal has Z; a barrel wall wrongly called planar would not"
);
}
}