Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
365 changes: 167 additions & 198 deletions crates/difftest/src/units/boolean_merge_p3.rs

Large diffs are not rendered by default.

76 changes: 65 additions & 11 deletions crates/difftest/src/units/primitive_primitive_creation_p1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,16 @@ fn maps_record(maps: &EntityMaps, out: &mut Vec<f64>) {
}
}
}
fn boolean_face(brep: &mut Brep, k: &Kernel, data: &[f64]) -> solidcore::Result<()> {
/// `CreateFaceFromSurface(surface, domain, face)` on the native fixture's canonical
/// plane (its STEP UV domain adjusted first) or unit sphere, as the shim builds it.
fn boolean_face(
brep: &mut Brep,
k: &mut Kernel,
data: &[f64],
notifications: &mut solidcore::topology::edge::NotifyEnv<'_>,
) -> solidcore::Result<()> {
use solidcore::geom::extent2d::Extent2d;
use solidcore::surface::{plane::p1::Plane, sphere::Sphere, surface::Surface};
use solidcore::topology::{Face, ShellId};
let angle = value(data, 1).to_radians();
let frame = Axis2Placement::new(
Vec3::new(0.0, 0.0, value(data, 2)),
Expand All @@ -121,10 +127,20 @@ fn boolean_face(brep: &mut Brep, k: &Kernel, data: &[f64]) -> solidcore::Result<
// IsPlanar uses the revolution base; Surface has no sphere variant yet.
Surface::from(Sphere::create_canonical(k, &frame, 1.0)?.base().clone())
};
let mut face = Face::new(ShellId::from_index(0));
face.surface = Some(surface);
face.uv_domain = domain;
brep.add_face_record(face)?;
let mut marks = solidcore::topology::topology_traverser::Marks::default();
let this_face = |f| topology_key(EntityRef::Face(f));
let mut fenv = solidcore::topology::brep::p8::FaceBuildEnv {
env: notifications,
marks: &mut marks,
this_face: &this_face,
};
solidcore::topology::brep::p8::create_face_from_surface(
k,
brep,
Some(surface),
&domain,
&mut fenv,
)?;
Ok(())
}

Expand Down Expand Up @@ -461,15 +477,25 @@ fn rust(i: &Inputs) -> solidcore::Result<Vec<f64>> {
}

6 => {
let mut a = Brep::new();
let mut b = Brep::new();
// SmBrep(SM_USE_DEFAULT, SM_USE_DEFAULT, context), with its infinite region
let mut a = solidcore::topology::brep::p3::new_brep(
&mut k,
solidcore::geom::tol::USE_DEFAULT,
solidcore::geom::tol::USE_DEFAULT,
)?;
let mut b = solidcore::topology::brep::p3::new_brep(
&mut k,
solidcore::geom::tol::USE_DEFAULT,
solidcore::geom::tol::USE_DEFAULT,
)?;
let split = 7 + 7 * value(v, 4) as usize;
for face in v.get(7..split).unwrap_or_default().as_chunks::<7>().0 {
boolean_face(&mut a, &k, face)?;
boolean_face(&mut a, &mut k, face, &mut notifications)?;
}
for face in v.get(split..).unwrap_or_default().as_chunks::<7>().0 {
boolean_face(&mut b, &k, face)?;
boolean_face(&mut b, &mut k, face, &mut notifications)?;
}
let had_faces = !solidcore::topology::brep::p1::faces(&a, None)?.is_empty();
a.set_tolerance(value(v, 6));
b.set_tolerance(value(v, 6));
a.set_editing_enabled(value(v, 2) != 0.0);
Expand Down Expand Up @@ -500,12 +526,32 @@ fn rust(i: &Inputs) -> solidcore::Result<Vec<f64>> {
Err(solidcore::SolidError::Upstream { code, .. }) => code,
Err(_) => solidcore::error::SM_ERR,
};
// On success Boolean2D returns A (here moved into the output slot) and
// deletes B; on error or empty input both survive.
let consumed = status == 0
&& had_faces
&& solidcore::topology::brep::p1::faces(&a, None)?.is_empty();
record.extend([
f64::from(status),
f64::from(a.editing_enabled()),
f64::from(b.editing_enabled()),
if consumed {
-1.0
} else {
f64::from(b.editing_enabled())
},
f64::from(output.is_some()),
]);
if consumed {
let result = output
.as_ref()
.ok_or_else(|| solidcore::SolidError::null_pointer("difftest::primitive_p1"))?;
record.extend([
solidcore::topology::brep::p1::faces(result, None)?.len() as f64,
solidcore::topology::brep::p1::edges(result, None)?.len() as f64,
solidcore::topology::brep::p1::vertices(result, None)?.len() as f64,
]);
crate::util::primitive::append(&k, result, &mut record)?;
}
}

_ => unreachable!(),
Expand All @@ -522,6 +568,14 @@ fn output(input: &Inputs, data: Vec<f64>) -> Result<Output, HarnessError> {
Ok(Output::new()
.ints("maps", head.iter().map(|v| *v as i64).collect::<Vec<_>>())
.values("geometry", data.get(prefix..).unwrap_or_default().to_vec()))
} else if op == 6 && data.len() > 4 {
// A successful Boolean2D: the statuses, the result's counts, then its geometry.
let head = data
.get(..7)
.ok_or_else(|| HarnessError::new("missing Boolean2D result record"))?;
Ok(Output::new()
.ints("maps", head.iter().map(|v| *v as i64).collect::<Vec<_>>())
.values("geometry", data.get(7..).unwrap_or_default().to_vec()))
} else if op == 0 || op == 5 || op == 6 {
Ok(Output::new().ints(
"maps",
Expand Down
29 changes: 20 additions & 9 deletions crates/solidcore/src/boolean/merge/p3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1104,16 +1104,27 @@ mod tests {
operation,
Some(&mut state.env(None)),
);
assert!(
matches!(
result,
Err(SolidError::Unported {
upstream: "SmTopologyIntersector::IntersectInsertRelate",
unit: "boolean.topology_intersector_p2",
})
),
"{operation:?}: {result:?}"
// IntersectInsertRelate runs (the edge-less fixture faces relate nothing);
// copying an unrelated B face then needs its face use, which a bare face
// record lacks, so Union, ExclusiveOr and Merge stop there
let copies = matches!(
operation,
BooleanOperation::Union | BooleanOperation::ExclusiveOr | BooleanOperation::Merge
);
if copies {
assert!(
matches!(
result,
Err(SolidError::InvalidInput {
operation: "topology::face::p2",
..
})
),
"{operation:?}: {result:?}"
);
} else {
assert!(result.is_ok(), "{operation:?}: {result:?}");
}
assert!(kernel.context.doing_boolean());
}
let mut kernel = Kernel::default();
Expand Down
23 changes: 18 additions & 5 deletions crates/solidcore/src/boolean/merge/p4/kernel_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,9 @@ impl<'keys> ManifoldKernelAdapter<'keys> {
Ok(())
}

/// Clear the two canonical mate maps between piecewise stages.
pub(crate) fn clear_relationships(&mut self) -> Result<()> {
/// Clear the two canonical mate maps between piecewise stages
/// (`m_pBToO->RemoveAll(); m_pOToB->RemoveAll()`).
pub fn clear_relationships(&mut self) -> Result<()> {
self.merge.intersector.with_intersector(
&mut self.intersector,
arena_pair::encode,
Expand All @@ -340,20 +341,20 @@ impl<'keys> ManifoldKernelAdapter<'keys> {
}

/// `m_vTI.m_bIntersectWireEdges`.
pub(crate) fn set_intersect_wire_edges(&mut self, enabled: bool) {
pub fn set_intersect_wire_edges(&mut self, enabled: bool) {
self.intersector.intersect_wire_edges = enabled;
}

/// The empty-array `RegisterExistingSSI` call used by `PiecewiseMerge`.
pub(crate) fn register_empty_ssi(&mut self, primary: FaceId, other: FaceId) {
pub fn register_empty_ssi(&mut self, primary: FaceId, other: FaceId) {
self.intersector.register_empty_ssi(
Some(arena_pair::surface_handle(false, primary)),
Some(arena_pair::surface_handle(true, other)),
);
}

/// Clear the piecewise stage's registered surface pairs.
pub(crate) fn clear_existing_ssi(&mut self) {
pub fn clear_existing_ssi(&mut self) {
let _ = self.intersector.clear_existing_ssi();
}

Expand Down Expand Up @@ -538,6 +539,18 @@ impl<'keys> ManifoldKernelAdapter<'keys> {
self.intersector.intersect_lamina_edges
}

/// `m_vTI.m_bIntersectWireEdges`.
#[must_use]
pub fn intersect_wire_edges(&self) -> bool {
self.intersector.intersect_wire_edges
}

/// The partial-merge surface subset ([`Self::set_surface_subset`]), if any.
#[must_use]
pub fn surface_subset(&self) -> Option<&BTreeSet<ObjectRef>> {
self.subset.as_ref()
}

/// Run the orchestration with the options held in the canonical `MergeState`.
pub fn manifold_boolean(
&mut self,
Expand Down
Loading
Loading