mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 10:35:58 +00:00
boolean2d through boolean_2d, typed edge-vanishes, typed put-back refusal
boolean2d goes through api_brep::boolean_2d, SM_ERR_OFFSET_EDGE_VANISHES through is_offset_edge_vanishes (solidcore#2016). A mitred round that cannot be put back (solidcore#2027) is Error::Unsupported. The tests held for solidcore#1462, #1973, #1980 and #1981 run again. copy_of stays as it is: results of offset_faces on a brep_copy (NamingScheme::Notify) fail check_brep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MNXLXkZLQ6T7TSi4wswcmk
This commit is contained in:
@@ -515,7 +515,6 @@ fn rounds_at_a_concave_face_corner_are_mitred() {
|
||||
/// The back and left top edges are each trimmed 8 by those rounds: (0, 30)-(32, 30) and (0, 8)-(0, 30)
|
||||
/// at z = 10, and each piece keeps its edge's attribute (`Behavior::Copy`), as on the fork.
|
||||
#[test]
|
||||
#[ignore = "solidcore#1980 (PR #1977): SubtractRemovalRegion loses the attributes of target edges it replaces"]
|
||||
fn an_edge_tag_crosses_a_cliff_overflow() {
|
||||
use solidcore::blend::fillet_executive::session::FilletSession;
|
||||
use solidcore::boolean::api_brep::boolean_difference;
|
||||
@@ -560,9 +559,9 @@ fn an_edge_tag_crosses_a_cliff_overflow() {
|
||||
/// Solidcore alone: the pocket rim chamfered 5.5, as `fillet_mitre_concave_face_corners`' own test
|
||||
/// does it, with `keep_operand_uv_trims` on too, on the body the boolean gives:
|
||||
/// 21000 - (50 d² + 4/3 d³) = 19265.666666667 (the fork, and the same body through the flat
|
||||
/// arrays). Refused: error 3, Corner Edge Computation Failure.
|
||||
/// arrays). Without `fillet_run_on_bspline_trims` it is refused: error 3, Corner Edge Computation
|
||||
/// Failure (solidcore#1981).
|
||||
#[test]
|
||||
#[ignore = "solidcore#1981 (PR #1996)"]
|
||||
fn pocket_chamfer_on_a_boolean_body() {
|
||||
use solidcore::blend::fillet_executive::session::FilletSession;
|
||||
use solidcore::boolean::api_brep::boolean_difference;
|
||||
@@ -577,6 +576,7 @@ fn pocket_chamfer_on_a_boolean_body() {
|
||||
k.fixes.fillet_cliff_overflow = true;
|
||||
k.fixes.fillet_mitre_concave_face_corners = true;
|
||||
k.fixes.keep_operand_uv_trims = true;
|
||||
k.fixes.fillet_run_on_bspline_trims = true;
|
||||
let plate = create_box(&mut k, Point3::ZERO, 60.0, 40.0, 10.0).unwrap();
|
||||
let cut = create_box(&mut k, p(15.0, 10.0, 5.0), 30.0, 20.0, 6.0).unwrap();
|
||||
let mut b = boolean_difference(&mut k, plate, cut).unwrap();
|
||||
|
||||
@@ -9,15 +9,14 @@
|
||||
use std::sync::PoisonError;
|
||||
|
||||
use solidcore::SolidError;
|
||||
use solidcore::boolean::api_brep as sm;
|
||||
use solidcore::boolean::api_brep::{self as sm, Boolean2dOutcome};
|
||||
use solidcore::boolean::merge::MergeOptions;
|
||||
use solidcore::geom::a_object::BooleanOperation;
|
||||
use solidcore::geom::attribute::ObjectKey;
|
||||
use solidcore::geom::object::ObjectRef;
|
||||
use solidcore::kernel::Kernel;
|
||||
use solidcore::primitive::primitive_creation::p1::{Boolean2d, PrimitiveCreation};
|
||||
use solidcore::primitive::primitive_creation::p1::Boolean2d;
|
||||
use solidcore::topology::brep::{p1, p2, p3};
|
||||
use solidcore::topology::topology_traverser::Marks;
|
||||
use solidcore::topology::{Brep, edge};
|
||||
use solidcore::{EntityRef, Result as SolidResult};
|
||||
|
||||
@@ -140,7 +139,7 @@ fn operation(op: BooleanOp) -> BooleanOperation {
|
||||
|
||||
/// Whether every body is a manifold solid (`IsManifoldSolid` of each copy).
|
||||
fn all_solid(bodies: &[Brep]) -> SolidResult<bool> {
|
||||
for b in bodies {
|
||||
for (n, b) in bodies.iter().enumerate() {
|
||||
if !p2::is_manifold_solid(b)? {
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -210,7 +209,7 @@ pub(crate) fn boolean2d(a: &Body, b: &Body, op: BooleanOp) -> Result<Option<Body
|
||||
usable("SmApiBoolean2d", &[a, b])?;
|
||||
let d = run("SmApiBoolean2d", |k| {
|
||||
let mut c = copies(k, &[a, b])?.into_iter();
|
||||
let (Some(mut ca), Some(mut cb)) = (c.next(), c.next()) else {
|
||||
let (Some(ca), Some(cb)) = (c.next(), c.next()) else {
|
||||
return Err(SolidError::invalid_input("smlib_boolean2d", "no operand"));
|
||||
};
|
||||
if num_faces(&ca) == 0 || num_faces(&cb) == 0 {
|
||||
@@ -221,17 +220,9 @@ pub(crate) fn boolean2d(a: &Body, b: &Body, op: BooleanOp) -> Result<Option<Body
|
||||
BooleanOp::Subtract => Boolean2d::Difference,
|
||||
BooleanOp::Intersect => Boolean2d::Intersection,
|
||||
};
|
||||
// SmApiBoolean2d calls the static SmPrimitiveCreation::Boolean2D; its notifications are
|
||||
// the operands' arena records, as solidcore's own merge calls it without a caller's.
|
||||
let mut primitive = PrimitiveCreation::new(None, 0, false);
|
||||
let (mut marks_a, mut marks_b) = (Marks::default(), Marks::default());
|
||||
let mut state = p2::ArenaNotifyState::new(&ca);
|
||||
state.seed(&cb);
|
||||
let mut out = None;
|
||||
primitive.boolean_2d(k, &mut ca, &mut cb, kind, &mut out, (&mut marks_a, &mut marks_b), &mut state.env(None))?;
|
||||
match out {
|
||||
Some(r) => finish(r, false, false),
|
||||
None => Ok(Done::Empty),
|
||||
match sm::boolean_2d(k, ca, cb, kind)? {
|
||||
Boolean2dOutcome::Result(r) => finish(r, false, false),
|
||||
Boolean2dOutcome::Unassigned { .. } => Ok(Done::Empty),
|
||||
}
|
||||
})?;
|
||||
result(d, Ok(None))
|
||||
|
||||
@@ -646,7 +646,6 @@ mod history {
|
||||
|
||||
/// Two overlapping squares combined as regions of one plane.
|
||||
#[test]
|
||||
#[ignore = "solidcore#1462 (PR #1977): boolean_2d carries no attributes through its merge"]
|
||||
fn a_boolean2d_has_the_cpp_history() {
|
||||
use crate::sweeps::Curve;
|
||||
let square = |x: f64, y: f64, s: f64, operand: usize| {
|
||||
|
||||
@@ -53,16 +53,13 @@ const INVALID_INPUT: i32 = 1007;
|
||||
/// `SMLIB_ERR_WALLS_TOUCH`.
|
||||
const OFFSET_WALLS_TOUCH: i32 = 1042;
|
||||
|
||||
/// `SM_ERR_OFFSET_EDGE_VANISHES` (fork 34318f4): a vertex moved past an edge's other end, which
|
||||
/// `status` (smlib_shim.h) reports as `SMLIB_ERR_UNSUPPORTED`.
|
||||
const OFFSET_EDGE_VANISHES: i32 = 1043;
|
||||
|
||||
/// `status` (smlib_shim.h): an offset whose walls touch is [`Error::WallsTouch`]; any other
|
||||
/// status as the blends pass it on.
|
||||
/// `status` (smlib_shim.h): an offset whose walls touch is [`Error::WallsTouch`]; a vertex moved
|
||||
/// past an edge's other end (`SM_ERR_OFFSET_EDGE_VANISHES`, fork 34318f4) is
|
||||
/// `SMLIB_ERR_UNSUPPORTED`; any other status as the blends pass it on.
|
||||
fn status(e: SolidError) -> Error {
|
||||
match e {
|
||||
SolidError::Upstream { code: OFFSET_WALLS_TOUCH, .. } => Error::WallsTouch,
|
||||
SolidError::Upstream { code: OFFSET_EDGE_VANISHES, .. } => Error::Unsupported,
|
||||
e if e.is_offset_edge_vanishes() => Error::Unsupported,
|
||||
e => blend_status(e),
|
||||
}
|
||||
}
|
||||
@@ -373,6 +370,9 @@ fn move_faces(k: &mut Kernel, b: &mut Brep, picked: &[FaceId], distance: f64) ->
|
||||
OffsetFacesRefusal::Unsupported => Error::Unsupported,
|
||||
OffsetFacesRefusal::NotExact => Error::NotExact,
|
||||
OffsetFacesRefusal::NoEffect => Error::NoEffect,
|
||||
// Not in the fork (solidcore#2020, #2027): the blends cannot be put back, the fillet's
|
||||
// status kept. SolidCraft has no such error: a move solidcore does not make.
|
||||
OffsetFacesRefusal::NotPutBack => Error::Unsupported,
|
||||
OffsetFacesRefusal::None => status(e),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -775,7 +775,6 @@ mod history {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "solidcore#1973 (PR #1977): the stitch in the shell drops the flat sheet's edge tags"]
|
||||
fn tags_cross_a_thicken_of_a_flat_sheet() {
|
||||
thicken_parity(false);
|
||||
}
|
||||
@@ -837,8 +836,11 @@ fn offset_faces_close_a_step_wall() {
|
||||
|
||||
/// Rig case shell/4492's rounded box: four mitred rounds, r 3, come off a 40 x 30 x 10 box's top
|
||||
/// together and follow it. From height 0 the outline is the 34 x 24 top moved out by the round's
|
||||
/// w(z) = sqrt(9 - (z - axis)^2), or 3 below the axis. With the fork's values at +19, and a body
|
||||
/// whose rounds go all the way down at -7. With SMLib built, the C++ kernel gives the same.
|
||||
/// w(z) = sqrt(9 - (z - axis)^2), or 3 below the axis. With the fork's values at +19, and bodies
|
||||
/// whose rounds go all the way down from -7 and are cut short from -7.5 (solidcore#2027). At -9
|
||||
/// solidcore refuses to put the rounds back (solidcore#2020, open): SolidCraft says it does not
|
||||
/// make that move, where the C++ kernel gives the analytic value, also checked here. With SMLib
|
||||
/// built, the C++ kernel gives the same at every other move.
|
||||
#[test]
|
||||
fn mitred_rounds_follow_a_moved_top() {
|
||||
use super::blend::{Corners, blend};
|
||||
@@ -856,13 +858,23 @@ fn mitred_rounds_follow_a_moved_top() {
|
||||
})
|
||||
.sum::<f64>()
|
||||
};
|
||||
let moves = [(19.0, [10, 20, 12]), (1.0, [10, 20, 12]), (-1.0, [10, 20, 12]), (-6.9, [10, 20, 12]), (-7.0, [6, 12, 8])];
|
||||
let moves = [
|
||||
(19.0, [10, 20, 12]),
|
||||
(1.0, [10, 20, 12]),
|
||||
(-1.0, [10, 20, 12]),
|
||||
(-6.9, [10, 20, 12]),
|
||||
(-7.0, [6, 12, 8]),
|
||||
(-7.5, [6, 12, 8]),
|
||||
(-8.0, [6, 12, 8]),
|
||||
(-9.5, [6, 12, 8]),
|
||||
];
|
||||
for (d, n) in moves {
|
||||
let moved = offset_faces(&rounded, &[top], 0.1, d).unwrap();
|
||||
let v = volume(&moved, 1e-8).unwrap();
|
||||
assert_eq!(counts(&moved).unwrap(), n, "d {d}");
|
||||
assert!(rel(v, analytic(d)) < 1e-6, "d {d}: {v} vs {}", analytic(d));
|
||||
}
|
||||
assert!(matches!(offset_faces(&rounded, &[top], 0.1, -9.0), Err(Error::Unsupported)));
|
||||
#[cfg(feature = "native")]
|
||||
{
|
||||
use crate::ffi;
|
||||
@@ -872,7 +884,13 @@ fn mitred_rounds_follow_a_moved_top() {
|
||||
for (d, _) in moves {
|
||||
let cpp = ffi::ops::pick_op(ffi::ops::Pick::OffsetFaces, &round, &[top], 0.1, d).unwrap();
|
||||
let ours = offset_faces(&rounded, &[top], 0.1, d).unwrap();
|
||||
assert!(rel(volume(&ours, 1e-8).unwrap(), ffi::volume(&cpp, 1e-8).unwrap()) < 1e-9, "d {d}");
|
||||
let (o, c) = (volume(&ours, 1e-8).unwrap(), ffi::volume(&cpp, 1e-8).unwrap());
|
||||
// Below -7 the rounds are cut short, and the two differ in the seventh digit: each is
|
||||
// within 1e-6 of the analytic value, and of the other.
|
||||
let agree = if d < -7.0 { 1e-6 } else { 1e-9 };
|
||||
assert!(rel(o, c) < agree, "d {d}: {o} vs the C++ kernel's {c}");
|
||||
}
|
||||
let cpp = ffi::ops::pick_op(ffi::ops::Pick::OffsetFaces, &round, &[top], 0.1, -9.0).unwrap();
|
||||
assert!(rel(ffi::volume(&cpp, 1e-8).unwrap(), analytic(-9.0)) < 1e-6, "d -9");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user