smlib: a face section on solidcore cuts planes, cylinders, cones and spheres too (solidcore#1769)

The port asks CreatePlanarSectionCurves for UV curves, so it now forces the UV classify: an
analytic face's intersector makes no UV curve, and without the flag the trim dropped the whole
section and the call answered Empty. SmBrep.h documents the flag for exactly this case. The
capi still leaves it FALSE, so the C++ kernel keeps answering Empty on analytic faces; the
parity test now holds the port to the section there and to the C++ curves on the loft.

a_cylinder_side_section_is_its_circle is un-ignored; a_box_top_section_is_the_line_across_it
checks the line (5,0,10)-(5,10,10) across a 10 mm box's top.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MNXLXkZLQ6T7TSi4wswcmk
This commit is contained in:
Brett Lawson
2026-10-30 16:25:29 +01:00
parent c3f5bb40b9
commit cd25e9e8f2
2 changed files with 26 additions and 7 deletions
+5
View File
@@ -1516,9 +1516,14 @@ pub(crate) fn face_section(
Ok(f) => f,
Err(e) => return Ok(Err(e)),
};
// UV curves are asked for, so the classify is forced: an analytic face's intersector
// makes none, and without it the trim drops the whole section (SmBrep.h documents the
// flag for this). The capi leaves it FALSE, so the C++ kernel answers Empty on planes,
// cylinders, cones and spheres (solidcore#1769).
let opts = PlanarSectionOptions {
approx_tol_3d: Some(tolerance),
outputs: SectionOutputs { curves_3d: true, uv_curves: true, faces: true },
force_uv_classify: true,
..PlanarSectionOptions::default()
};
let made = match create_planar_section_curves(k, b, v3(plane_point), normal, &opts) {
+21 -7
View File
@@ -555,7 +555,6 @@ fn a_lofted_wall_section_is_the_line_across_it() {
/// The plane z = 3 through the side of a cylinder of radius 5 and height 10 on the xy plane: the
/// circle of radius 5 at z = 3, 2π·5 long.
#[test]
#[ignore = "smlib_face_section calls CreatePlanarSectionCurves with bForceUVClassify left FALSE, so an analytic face's section (no UV curve) is trimmed away and the capi reports SMLIB_ERR_EMPTY, on the C++ kernel too (the_cpp_kernel_sections_a_face_alike)"]
fn a_cylinder_side_section_is_its_circle() {
let got = timed("section", || {
let b = make_cylinder([0.0; 3], 5.0, 10.0).unwrap();
@@ -570,9 +569,24 @@ fn a_cylinder_side_section_is_its_circle() {
assert!((len - TAU * 5.0).abs() < 1e-4, "{len} against {}", TAU * 5.0);
}
/// The same sections on the C++ kernel: the lofted wall's line along the same points, and on a
/// box's top and a cylinder's side, whose analytic intersectors make no UV curves, the same
/// [`Error::Empty`].
/// The plane x = 5 through the top of a 10 mm box: the line (5, 0, 10)-(5, 10, 10) across it.
#[test]
fn a_box_top_section_is_the_line_across_it() {
let got = timed("section", || {
let b = make_box([0.0; 3], [10.0; 3]).unwrap();
face_section(&b, [5.0, 5.0, 10.0], 0.01, [5.0, 5.0, 5.0], [1.0, 0.0, 0.0], 1e-7)
})
.unwrap();
let p = samples(&got, 8);
assert!(p.iter().flatten().all(|q| (q[0] - 5.0).abs() < 1e-9 && (q[2] - 10.0).abs() < 1e-9), "{p:?}");
let len = length(&p);
assert!((len - 10.0).abs() < 1e-6, "{len} against 10");
}
/// The same sections on the C++ kernel: the lofted wall's line along the same points. On a box's
/// top and a cylinder's side, whose analytic intersectors make no UV curves, the capi leaves
/// `bForceUVClassify` FALSE and answers [`Error::Empty`] (solidcore#1769), where the port has the
/// section.
#[cfg(feature = "native")]
#[test]
fn the_cpp_kernel_sections_a_face_alike() {
@@ -610,11 +624,11 @@ fn the_cpp_kernel_sections_a_face_alike() {
let got = face_section(&core(), face, 0.01, through, n, 1e-6);
let (got, want) = match (got, want) {
(Ok(g), Ok(w)) => (g, w),
(g, w) => {
assert_eq!(g.err(), w.err(), "{name}");
assert!(name != "loft", "the loft's wall has its section");
(Ok(g), Err(Error::Empty)) if name != "loft" => {
assert!(!g.is_empty(), "{name}: the port has the section the capi drops");
continue;
}
(g, w) => panic!("{name}: {:?} against {:?}", g.err(), w.err()),
};
assert_eq!(got.len(), want.len(), "{name}: {got:?} against {want:?}");
let (g, w) = (samples(&got, 64), samples(&want, 64));