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
9 changes: 9 additions & 0 deletions main/opengeometry/src/brep/builder/boundary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ pub(crate) fn plane_boundary(
axis_a: local(scale(c.x, *radius)),
axis_b: local(scale(c.y, *radius)),
},
CurveGeometry::Ellipse {
frame: c,
major_radius,
minor_radius,
} => PcurveGeometry::Conic2 {
origin: local(sub(c.origin, frame.origin)),
axis_a: local(scale(c.x, *major_radius)),
axis_b: local(scale(c.y, *minor_radius)),
},
_ => {
return Err(GeometryError::UnsupportedGeometry(
"planar primitive boundary".into(),
Expand Down
54 changes: 41 additions & 13 deletions main/opengeometry/src/operations/modifying/boolean/batch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ mod prismatic_profile;

use super::assembly::{analytic_face_mappings, append_analytic_input};
use super::dispatch::{boolean_brep_outcome_with_trace, record_handler, HandlerId, Trace};
use super::handlers::{boolean_rectilinear, subtract_vertical_arc_extrusion_batch};
use super::handlers::{
boolean_rectilinear, subtract_half_spaces_from_arc_extrusion,
subtract_vertical_arc_extrusion_batch,
};
use super::operands::{all_planar, full_cylinder, unique_sources};
use super::types::{BooleanOp, BooleanResult};
use crate::brep::{Accuracy, BrepEnvelope, GeometryError, PatchBounds, SurfaceGeometry};
Expand Down Expand Up @@ -107,6 +110,27 @@ fn planar_cutters_boolean(
{
return mixed_cutters_boolean(host, cutters, planar, cylinders, id, trace);
}
if let Some(result) = arc_extrusion_batch(host, cutters, &id, trace)? {
return Ok(result);
}
check_disjoint_cutters(cutters, &bounds, accuracy)?;
let mut combined = BrepEnvelope::new(format!("{id}:cutters"), accuracy)?;
for cutter in cutters {
append_analytic_input(&mut combined, cutter)?;
}
combined.validate()?;
let result = boolean_rectilinear(host, &combined, BooleanOp::Subtraction, id)?;
let handlers = &mut trace.handlers;
handlers.push(HandlerId::BooleanRectilinear.as_str().into());
Ok(result)
}

fn arc_extrusion_batch(
host: &BrepEnvelope,
cutters: &[BrepEnvelope],
id: &str,
trace: &mut Trace,
) -> Result<Option<BooleanResult>, GeometryError> {
if host
.geometry
.surfaces
Expand All @@ -117,27 +141,31 @@ fn planar_cutters_boolean(
match subtract_vertical_arc_extrusion_batch(
host,
&cutters.iter().collect::<Vec<_>>(),
id.clone(),
id.to_string(),
) {
Ok(result) => {
let handlers = &mut trace.handlers;
handlers.push(HandlerId::SubtractVerticalArcExtrusionBatch.as_str().into());
return Ok(result);
return Ok(Some(result));
}
Err(GeometryError::CoverageGap { .. }) => {}
Err(error) => return Err(error),
}
match subtract_half_spaces_from_arc_extrusion(
host,
&cutters.iter().collect::<Vec<_>>(),
id.to_string(),
) {
Ok(result) => {
let name = HandlerId::SubtractHalfSpaceFromArcExtrusion.as_str();
trace.handlers.extend(cutters.iter().map(|_| name.into()));
return Ok(Some(result));
}
Err(GeometryError::CoverageGap { .. }) => {}
Err(error) => return Err(error),
}
}
check_disjoint_cutters(cutters, &bounds, accuracy)?;
let mut combined = BrepEnvelope::new(format!("{id}:cutters"), accuracy)?;
for cutter in cutters {
append_analytic_input(&mut combined, cutter)?;
}
combined.validate()?;
let result = boolean_rectilinear(host, &combined, BooleanOp::Subtraction, id)?;
let handlers = &mut trace.handlers;
handlers.push(HandlerId::BooleanRectilinear.as_str().into());
Ok(result)
Ok(None)
}

fn accuracy_and_bounds(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub(crate) enum HandlerId {
BooleanPlanarPolyhedra,
BooleanSpheres,
SubtractVerticalArcExtrusion,
SubtractHalfSpaceFromArcExtrusion,
GenericTwoSidedCutterBandSubtraction,
GenericMultipleClosedLoopsBoolean,
GenericSingleClosedLoopBoolean,
Expand Down Expand Up @@ -59,6 +60,7 @@ impl HandlerId {
Self::BooleanPlanarPolyhedra => "boolean_planar_polyhedra",
Self::BooleanSpheres => "boolean_spheres",
Self::SubtractVerticalArcExtrusion => "subtract_vertical_arc_extrusion",
Self::SubtractHalfSpaceFromArcExtrusion => "subtract_half_space_from_arc_extrusion",
Self::GenericTwoSidedCutterBandSubtraction => {
"generic_two_sided_cutter_band_subtraction"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::operations::modifying::boolean::handlers::{
generic_non_intersecting_boolean, generic_single_closed_loop_boolean,
generic_two_sided_cutter_band_subtraction, prismatic_curved_boolean, sphere_box_boolean,
sphere_conic_containment, sphere_cylinder_boolean, sphere_torus_containment,
subtract_arc_extrusion_from_planar_solid, subtract_half_space_from_arc_extrusion,
subtract_layered_extrusions, subtract_vertical_arc_extrusion, torus_box_containment,
torus_conic_containment, torus_containment_boolean, torus_cylinder_containment,
};
Expand Down Expand Up @@ -253,6 +254,33 @@ fn maybe_vertical_arc_extrusion(
b: &BrepEnvelope,
operation: BooleanOp,
id: String,
) -> Result<Option<BooleanResult>, GeometryError> {
maybe_arc_extrusion_cut(a, b, operation, || {
subtract_vertical_arc_extrusion(a, b, id)
})
}

fn maybe_half_space_arc_extrusion(
a: &BrepEnvelope,
b: &BrepEnvelope,
operation: BooleanOp,
id: String,
) -> Result<Option<BooleanResult>, GeometryError> {
if all_planar(a) {
return maybe_arc_extrusion_cut(b, a, operation, || {
subtract_arc_extrusion_from_planar_solid(a, b, id)
});
}
maybe_arc_extrusion_cut(a, b, operation, || {
subtract_half_space_from_arc_extrusion(a, b, id)
})
}

fn maybe_arc_extrusion_cut(
a: &BrepEnvelope,
b: &BrepEnvelope,
operation: BooleanOp,
cut: impl FnOnce() -> Result<BooleanResult, GeometryError>,
) -> Result<Option<BooleanResult>, GeometryError> {
if operation != BooleanOp::Subtraction
|| !all_planar(b)
Expand All @@ -264,7 +292,7 @@ fn maybe_vertical_arc_extrusion(
{
return Ok(None);
}
match subtract_vertical_arc_extrusion(a, b, id) {
match cut() {
Ok(result) => Ok(Some(result)),
Err(GeometryError::CoverageGap { .. }) => Ok(None),
Err(error) => Err(error),
Expand All @@ -276,6 +304,10 @@ const GENERIC_HANDLERS: &[GenericHandler] = &[
name: HandlerId::SubtractVerticalArcExtrusion,
call: maybe_vertical_arc_extrusion,
},
GenericHandler {
name: HandlerId::SubtractHalfSpaceFromArcExtrusion,
call: maybe_half_space_arc_extrusion,
},
GenericHandler {
name: HandlerId::GenericTwoSidedCutterBandSubtraction,
call: generic_two_sided_cutter_band_subtraction,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,15 @@ fn cuboid_across_or_along_the_axis_keeps_its_handler() {
}

#[test]
fn tilted_cuboid_minus_cylinder_and_their_union_stay_a_coverage_gap() {
fn tilted_cuboid_and_cylinder_union_stays_a_coverage_gap() {
let cylinder = host();
let cuboid = tilted_cuboid(45.0);
for (a, b, operation) in [
(&cuboid, &cylinder, BooleanOp::Subtraction),
(&cylinder, &cuboid, BooleanOp::Union),
] {
let (result, handlers) = boolean_brep_outcome_with_handlers(a, b, operation, "out".into());
assert!(handlers.is_empty(), "{handlers:?}");
assert!(
matches!(result, Err(GeometryError::CoverageGap { .. })),
"{operation:?}: {:?}",
result.map(|_| ())
);
}
let (result, handlers) =
boolean_brep_outcome_with_handlers(&cylinder, &cuboid, BooleanOp::Union, "out".into());
assert!(handlers.is_empty(), "{handlers:?}");
assert!(
matches!(result, Err(GeometryError::CoverageGap { .. })),
"{:?}",
result.map(|_| ())
);
}
Loading
Loading