- A fold's zone clipped across the root of a tab folded before on the same line kept a sliver of no width there, which made the zone look like it crossed a slanted edge. The one real piece is the zone now (#638). - When the tabs a fold line spans can't take the side beyond them along, because it joins the side that stays through other material (a tab on the inside edge of an L, the fixed point on the far leg), the tabs' own ends fold instead, the same part as with the fixed point on the near leg (#639). Co-authored-by: jonaswre <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
3 files changed
+73
-3552
No files matched your search
+34
-2
@@ -33,6 +33,8 @@ const MAX_FLAT_WORK: usize = 500_000_000;
|
||||
|
||||
/// Starts the warning a fold gives when its `fixed` point touches no material: a recompute keeps
|
||||
/// the side the point lies on, and the `sheet.fold` command refuses such a point (#263).
|
||||
/// Tabs folded on one line whose material beyond the bend joins again.
|
||||
const FOLD_TABS_REJOIN: &str = "not supported yet: folding tabs that join again beyond the bend";
|
||||
pub const FOLD_FIXED_OFF_SHEET: &str = "the fold's fixed point is not on the sheet";
|
||||
|
||||
fn flat_budget_error() -> DocError {
|
||||
@@ -2636,6 +2638,30 @@ impl SheetBody {
|
||||
flip: bool,
|
||||
fixed: Option<Vec3>,
|
||||
warning: &mut Option<String>,
|
||||
) -> Result<usize> {
|
||||
match self.fold_side(a, b, angle, radius, position, flip, fixed, warning, false) {
|
||||
// The tabs the line folds can't take the side beyond them along, since it joins
|
||||
// the side that stays through other material (a tab on an inside edge of an L,
|
||||
// the fixed point on the far leg): the tabs' own ends fold instead, and the
|
||||
// fixed point's material still stays (#639).
|
||||
Err(DocError::Invalid(m)) if m == FOLD_TABS_REJOIN => self.fold_side(a, b, angle, radius, position, flip, fixed, warning, true),
|
||||
r => r,
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Self::add_fold`]; `other_side` moves the side the fixed point would keep.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn fold_side(
|
||||
&mut self,
|
||||
a: Vec3,
|
||||
b: Vec3,
|
||||
angle: f64,
|
||||
radius: f64,
|
||||
position: &str,
|
||||
flip: bool,
|
||||
fixed: Option<Vec3>,
|
||||
warning: &mut Option<String>,
|
||||
other_side: bool,
|
||||
) -> Result<usize> {
|
||||
if !(angle > 1e-6 && angle <= std::f64::consts::PI - 1e-6) {
|
||||
return Err(DocError::Invalid("the fold angle must be between 0 and 180°".into()));
|
||||
@@ -2704,7 +2730,7 @@ impl SheetBody {
|
||||
}
|
||||
(None, None) => side_area(n) <= side_area(n * -1.0),
|
||||
};
|
||||
if !moves_n {
|
||||
if moves_n == other_side {
|
||||
n = n * -1.0;
|
||||
}
|
||||
let l = a2.dot(n);
|
||||
@@ -2740,6 +2766,12 @@ impl SheetBody {
|
||||
// Separate tabs on one line (the zone falls apart into several pieces): fold the ones
|
||||
// the line's points span, each as its own bend.
|
||||
let strips = merge_loops(std::slice::from_ref(&strip), &mut FlatWork::new())?;
|
||||
// A zone clipped across the root of a tab folded before on the same line keeps a sliver
|
||||
// of no width there: one real piece is the zone (#638).
|
||||
let strip = match strips.as_slice() {
|
||||
[one] => one.clone(),
|
||||
_ => strip,
|
||||
};
|
||||
if strips.len() > 1 {
|
||||
let span = (a2.dot(d).min(b2.dot(d)), a2.dot(d).max(b2.dot(d)));
|
||||
let angle = if flip { -angle } else { angle };
|
||||
@@ -2857,7 +2889,7 @@ impl SheetBody {
|
||||
h.push(k);
|
||||
}
|
||||
}
|
||||
_ => return Err(DocError::Invalid("not supported yet: folding tabs that join again beyond the bend".into())),
|
||||
_ => return Err(DocError::Invalid(FOLD_TABS_REJOIN.into())),
|
||||
}
|
||||
}
|
||||
let chosen: Vec<usize> = (0..strips.len()).filter(|&i| strip_u.get(i).is_some_and(|&u| overlap(u, span) > EPS)).collect();
|
||||
|
||||
@@ -2217,6 +2217,45 @@ fn fold_across_two_collinear_tabs() {
|
||||
assert!(bb["max"][2].as_f64().unwrap() > 7.0, "both tabs stand up: {bb}");
|
||||
}
|
||||
|
||||
/// After one of two collinear tabs is folded, the other one folds in its own feature too (#638, follow-up to #264).
|
||||
#[test]
|
||||
fn fold_second_collinear_tab_after_the_first() {
|
||||
let mut s = Session::default();
|
||||
two_tab_blank(&mut s);
|
||||
let flat = volume(&mut s);
|
||||
run(&mut s, "sheet.fold", json!({"points": [[107, 5, 0.8], [107, 12, 0.8]], "fixed": [50, 20, 0.8]}));
|
||||
run(&mut s, "sheet.fold", json!({"points": [[107, 28, 0.8], [107, 35, 0.8]], "fixed": [50, 20, 0.8]}));
|
||||
assert!(rel(volume(&mut s), flat) < 1e-3, "folds keep the material");
|
||||
let bb = &run(&mut s, "inspect.measure", json!({}))["bodies"][0]["bbox"];
|
||||
assert!(bb["max"][2].as_f64().unwrap() > 7.0, "the tabs stand up: {bb}");
|
||||
// The tabs stand at the bend, x 106.1 .. 107.9 (centerline at x 107).
|
||||
assert!(bb["max"][0].as_f64().unwrap() < 110.0, "neither tab still lies flat out to x 115: {bb}");
|
||||
}
|
||||
|
||||
/// An L blank (100 x 50 bottom leg, 40 x 50 upright leg on the right) with a tab x 45..60, y 60..90 on the inside
|
||||
/// edge x 60. The fold line x 59, extended, runs into the bottom leg.
|
||||
fn l_blank_with_inside_tab(s: &mut Session) {
|
||||
outline_blank(s, json!([[0, 0], [100, 0], [100, 100], [60, 100], [60, 90], [45, 90], [45, 60], [60, 60], [60, 50], [0, 50]]));
|
||||
}
|
||||
|
||||
/// The tab on the inside edge folds wherever on the L sheet `fixed` lies: also when it is on the leg the extended
|
||||
/// fold line runs into (#639: "folding tabs that join again beyond the bend").
|
||||
#[test]
|
||||
fn fold_tab_on_inside_edge_of_l_blank_with_fixed_on_the_far_leg() {
|
||||
let mut near = Session::default();
|
||||
l_blank_with_inside_tab(&mut near);
|
||||
run(&mut near, "sheet.fold", json!({"points": [[59, 60, 0.8], [59, 90, 0.8]], "fixed": [80, 20, 0.8]}));
|
||||
let mut far = Session::default();
|
||||
l_blank_with_inside_tab(&mut far);
|
||||
let flat = volume(&mut far);
|
||||
run(&mut far, "sheet.fold", json!({"points": [[59, 60, 0.8], [59, 90, 0.8]], "fixed": [40, 30, 0.8]}));
|
||||
assert!(rel(volume(&mut far), flat) < 1e-3, "a fold keeps the material");
|
||||
let a = &run(&mut near, "inspect.measure", json!({}))["bodies"][0]["bbox"];
|
||||
let b = &run(&mut far, "inspect.measure", json!({}))["bodies"][0]["bbox"];
|
||||
assert!(b["max"][2].as_f64().unwrap() > 7.0, "the tab stands up: {b}");
|
||||
assert_eq!(a, b, "both fixed points are on the same L sheet, so the fold must give the same part");
|
||||
}
|
||||
|
||||
/// Edge flanges on both tab ends in one feature (two separate features already work) (#264).
|
||||
#[test]
|
||||
fn edge_flanges_on_two_collinear_tab_ends_in_one_feature() {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user