A hole square to a folded sheet metal panel is flat machining (#656)
A hole drilled square to a flange's panel, not only the base's, is now a flat cut-out of the sheet: it unfolds with its panel to where the bend allowance puts it, shows in the flat pattern and DXF as a circle, and survives unfold and refold. A pocket tool spans the cut-outs already inside it (a counterbore's hole), so the order they are made in no longer matters. Fixes #560.
This commit is contained in:
1 parent
0040ce6bf2
commit
5c98c1e24a
5 files changed
+152
-9
No files matched your search
@@ -2010,7 +2010,7 @@ fn sheet_hole_cut(st: &mut ModelState, f: &Feature, tools: &[(Traced, Option<mor
|
||||
for (tool, prism) in tools {
|
||||
let tool = &tool.body;
|
||||
let done = match prism {
|
||||
Some(prism) => more::sheet_primitive_cut(f, st, tool, *prism, Operation::Cut)?,
|
||||
Some(prism) => more::sheet_primitive_cut(f, st, tool, *prism, Operation::Cut, true)?,
|
||||
None => Vec::new(),
|
||||
};
|
||||
if done.is_empty() {
|
||||
@@ -4017,7 +4017,7 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
let corner = point(vals, corner)?;
|
||||
let b = kernel::box_solid(corner, corner + s)?;
|
||||
let b = if matches!(operation, Operation::NewBody | Operation::Join) { b.with_box_certificate(f.id)? } else { b };
|
||||
let handled = more::sheet_primitive_cut(f, st, &b, more::PrimitivePrism::Box { lo: corner, hi: corner + s }, *operation)?;
|
||||
let handled = more::sheet_primitive_cut(f, st, &b, more::PrimitivePrism::Box { lo: corner, hi: corner + s }, *operation, false)?;
|
||||
let tools = vec![crate::lineage::box_tool(b)];
|
||||
if !handled.is_empty() {
|
||||
return apply_beside_sheet_cut(st, f, tools, *operation, &[], &handled);
|
||||
@@ -4029,7 +4029,7 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
let b = kernel::cylinder(base, *axis, radius, height)?;
|
||||
let b = if matches!(operation, Operation::NewBody | Operation::Join) { b.with_cylinder_certificate(f.id)? } else { b };
|
||||
let prism = more::PrimitivePrism::Cylinder { base, axis: *axis, radius, height };
|
||||
let handled = more::sheet_primitive_cut(f, st, &b, prism, *operation)?;
|
||||
let handled = more::sheet_primitive_cut(f, st, &b, prism, *operation, false)?;
|
||||
let tools = vec![crate::lineage::cylinder_tool(b)];
|
||||
if !handled.is_empty() {
|
||||
return apply_beside_sheet_cut(st, f, tools, *operation, &[], &handled);
|
||||
|
||||
@@ -726,9 +726,17 @@ fn any_eligible_sheet(f: &Feature, st: &ModelState, targets: &[String]) -> bool
|
||||
|
||||
/// A Box or Cylinder cut (or intersect) whose tool is a straight prism along the normal of
|
||||
/// the sheet it meets: machined like the equivalent extrude of its footprint, so the sheet
|
||||
/// keeps it as a flat cut-out or pocket. Returns the sheet bodies it handled; none when the
|
||||
/// keeps it as a flat cut-out or pocket. A hole's drill (`drill`) may also be square to a
|
||||
/// folded panel of the sheet, a flange's. Returns the sheet bodies it handled; none when the
|
||||
/// primitive is not such a prism (the generic preflight then refuses real 3D machining).
|
||||
pub(super) fn sheet_primitive_cut(f: &Feature, st: &mut ModelState, tool: &Body, prism: PrimitivePrism, operation: Operation) -> Result<Vec<String>> {
|
||||
pub(super) fn sheet_primitive_cut(
|
||||
f: &Feature,
|
||||
st: &mut ModelState,
|
||||
tool: &Body,
|
||||
prism: PrimitivePrism,
|
||||
operation: Operation,
|
||||
drill: bool,
|
||||
) -> Result<Vec<String>> {
|
||||
if !matches!(operation, Operation::Cut | Operation::Intersect) || !any_eligible_sheet(f, st, &[]) {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
@@ -738,7 +746,14 @@ pub(super) fn sheet_primitive_cut(f: &Feature, st: &mut ModelState, tool: &Body,
|
||||
if let Some(mb) = st.body(&sh.body)
|
||||
&& kernel::boolean(&mb.body, tool, kernel::BoolOp::Intersect)?.is_some()
|
||||
{
|
||||
normal = Some(sh.frame.normal());
|
||||
normal = Some(match prism {
|
||||
PrimitivePrism::Cylinder { axis, .. }
|
||||
if drill && !sh.flat && Plane::from_normal(Vec3::ZERO, axis).is_some_and(|p| !sh.parallel_panels(&p).is_empty()) =>
|
||||
{
|
||||
axis
|
||||
}
|
||||
_ => sh.frame.normal(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-3
@@ -1267,11 +1267,22 @@ impl SheetBody {
|
||||
}
|
||||
}
|
||||
for (pi, panel) in &panels {
|
||||
let clipped = material_boolean(panel, std::slice::from_ref(outline), kernel::BoolOp::Intersect, work)?;
|
||||
if clipped.is_empty() {
|
||||
if material_boolean(panel, std::slice::from_ref(outline), kernel::BoolOp::Intersect, work)?.is_empty() {
|
||||
continue;
|
||||
}
|
||||
// What the tool takes over from a free-form cut-out of the panel follows its
|
||||
// The tool spans the cut-outs inside it (a counterbore's hole, whichever was made
|
||||
// first): a wall of its own along a cut-out's wall would only touch it, which the
|
||||
// cut cannot resolve. An island of the pocket that holds material stays.
|
||||
let whole: Vec<Region2> = panel.iter().map(|r| Region2 { outer: r.outer.clone(), holes: Vec::new() }).collect();
|
||||
let mut spanning = Region2 { outer: outline.outer.clone(), holes: Vec::new() };
|
||||
for island in &outline.holes {
|
||||
let inside = Region2 { outer: island.ccw(), holes: Vec::new() };
|
||||
if !material_boolean(panel, std::slice::from_ref(&inside), kernel::BoolOp::Intersect, work)?.is_empty() {
|
||||
spanning.holes.push(island.clone());
|
||||
}
|
||||
}
|
||||
let clipped = material_boolean(&whole, std::slice::from_ref(&spanning), kernel::BoolOp::Intersect, work)?;
|
||||
// What the tool takes over from a free-form outline of the panel follows its
|
||||
// fitted arcs: walls of planes and cylinders, which the prism booleans take.
|
||||
let clipped = clipped.iter().map(fitted_region).collect::<Result<Vec<_>>>()?;
|
||||
let w = if self.flat { self.frame_mat() } else { self.world(*pi) };
|
||||
|
||||
Reference in new issue
Block a user