A stored fold whose fixed point drifts off the sheet after a parameter change keeps the side the point lies on and shows a warning instead of breaking the timeline. A new fold still refuses a fixed point off the sheet, atomically. Un-ignores the #370 regression test. Fixes #370. Co-authored-by: jonaswre <[email protected]>
This commit is contained in:
1 parent
f9c19ade16
commit
c727e2d0c2
4 files changed
+70
-10
No files matched your search
@@ -505,7 +505,7 @@ pub(super) fn sheet_eval(
|
||||
return Err(DocError::Invalid("refold the sheet before folding it".into()));
|
||||
}
|
||||
let n0 = sh.flanges.len();
|
||||
sh.add_fold(a, b, ang, rr, position, *flip, *fixed)?;
|
||||
sh.add_fold(a, b, ang, rr, position, *flip, *fixed, warning)?;
|
||||
tag_flanges(sh, n0, f.id);
|
||||
rebuild_sheet(st, i)
|
||||
}
|
||||
|
||||
+20
-6
@@ -27,6 +27,10 @@ const MAX_FLAT_SAMPLES: usize = 65_536;
|
||||
const MAX_FLAT_PIECES: usize = 4096;
|
||||
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).
|
||||
pub const FOLD_FIXED_OFF_SHEET: &str = "the fold's fixed point is not on the sheet";
|
||||
|
||||
fn flat_budget_error() -> DocError {
|
||||
DocError::Invalid("sheet metal flat geometry exceeds the bounded processing budget".into())
|
||||
}
|
||||
@@ -1789,9 +1793,21 @@ impl SheetBody {
|
||||
/// (`centerline`), start or end (`start`/`end`, from the fixed side), or on the mould line
|
||||
/// (`mould`: where the outer faces meet, so the fixed side measures the same to it folded as
|
||||
/// flat). On the base the side holding `fixed` (default: the larger side) stays put; on a
|
||||
/// flange the side toward its own bend does. `flip` folds the other way.
|
||||
/// flange the side toward its own bend does. `flip` folds the other way. A `fixed` point that
|
||||
/// touches no material still picks the side it lies on, and sets `warning` (see
|
||||
/// [`FOLD_FIXED_OFF_SHEET`]).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn add_fold(&mut self, a: Vec3, b: Vec3, angle: f64, radius: f64, position: &str, flip: bool, fixed: Option<Vec3>) -> Result<usize> {
|
||||
pub fn add_fold(
|
||||
&mut self,
|
||||
a: Vec3,
|
||||
b: Vec3,
|
||||
angle: f64,
|
||||
radius: f64,
|
||||
position: &str,
|
||||
flip: bool,
|
||||
fixed: Option<Vec3>,
|
||||
warning: &mut Option<String>,
|
||||
) -> 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()));
|
||||
}
|
||||
@@ -1852,10 +1868,8 @@ impl SheetBody {
|
||||
let in_slab = q.z > -1e-4 && q.z < self.t + 1e-4;
|
||||
let in_hole = self.holes.iter().any(|h| h.contains(q2) && !near(&h.outer.ccw().polyline(1e-3)));
|
||||
if in_slab && ((!point_in(&pts, q2) && !near(&pts)) || in_hole) {
|
||||
return Err(DocError::Invalid(format!(
|
||||
"the fixed point ({:.3}, {:.3}, {:.3}) is not on the sheet: pick a point on the face of the side that stays",
|
||||
p.x, p.y, p.z
|
||||
)));
|
||||
*warning =
|
||||
Some(format!("{FOLD_FIXED_OFF_SHEET} at ({:.3}, {:.3}, {:.3}); the side of the fold line it lies on stays", p.x, p.y, p.z));
|
||||
}
|
||||
q2.dot(n) < l0
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use solidcraft_doc::expr::Kind;
|
||||
use solidcraft_doc::sheet::{SheetBody, SheetRule};
|
||||
use solidcraft_doc::sheet::{self, SheetBody, SheetRule};
|
||||
use solidcraft_doc::{FeatureKind, FlangeEdge};
|
||||
use solidcraft_geom::{Vec2, Vec3};
|
||||
|
||||
@@ -218,7 +218,25 @@ fn fold(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
None => None,
|
||||
};
|
||||
let body = str_(p, "body").map(str::to_string);
|
||||
add_feature(s, p, FeatureKind::SheetFold { sketch, curve, a, b, angle, radius, position, flip: bool_(p, "flip").unwrap_or(false), fixed, body })
|
||||
let r = add_feature(
|
||||
s,
|
||||
p,
|
||||
FeatureKind::SheetFold { sketch, curve, a, b, angle, radius, position, flip: bool_(p, "flip").unwrap_or(false), fixed, body },
|
||||
)?;
|
||||
// A recompute keeps a stored fold whose fixed point no longer touches the sheet (with a
|
||||
// warning); a new fold refuses one, since it picks no side (#263).
|
||||
if let Some(f) = fixed {
|
||||
let id = r.get("feature").and_then(Value::as_u64);
|
||||
let off =
|
||||
id.and_then(|id| s.model.result(id)).and_then(|res| res.warning.as_deref()).is_some_and(|w| w.contains(sheet::FOLD_FIXED_OFF_SHEET));
|
||||
if off {
|
||||
return Err(bad(
|
||||
cmd,
|
||||
format!("the fixed point ({:.3}, {:.3}, {:.3}) is not on the sheet: pick a point on the face of the side that stays", f.x, f.y, f.z),
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn unfold(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
|
||||
@@ -1969,7 +1969,6 @@ fn hole_beside_a_bend_with_a_round_cut_out_cuts_and_the_bend_stays_guarded() {
|
||||
/// A fold whose stored `fixed` point ends up off the sheet after a parameter change keeps working:
|
||||
/// the point still says which side stays (#370). Only a new pick off the sheet is refused (#263).
|
||||
#[test]
|
||||
#[ignore = "fails until #370 is fixed"]
|
||||
fn fold_keeps_working_when_a_parameter_moves_the_sheet_off_its_fixed_point() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "parameters.change", json!({"name": "w", "expression": "100 mm"}));
|
||||
@@ -2579,3 +2578,32 @@ fn a_flange_asked_for_0_8_mm_is_0_8_mm_or_refused_never_silently_2_5_mm() {
|
||||
Err(e) => assert!(e.to_string().contains("thickness"), "{e}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// A new fold still refuses a `fixed` point in a cut-out or past the outline, leaving the design
|
||||
/// untouched (#263); a stored one that drifts off the sheet keeps its side with a warning (#370).
|
||||
#[test]
|
||||
fn fold_refuses_a_new_fixed_point_off_the_sheet_but_warns_on_recompute() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "parameters.change", json!({"name": "w", "expression": "100 mm"}));
|
||||
run(&mut s, "sketch.create", json!({"plane": "XY", "name": "Blank"}));
|
||||
run(&mut s, "sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [100, 60]}));
|
||||
run(&mut s, "sketch.dimension", json!({"entities": ["l1"], "value": "w"}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "sheet.flange", json!({"sketch": "Blank"}));
|
||||
rectangle_tool(&mut s, "Window", [10.0, 15.0], [30.0, 45.0]);
|
||||
run(&mut s, "solid.extrude", json!({"sketch": "Window", "through_all": true, "direction": "symmetric", "operation": "cut"}));
|
||||
let before = s.doc.to_json();
|
||||
let undo = s.undo.len();
|
||||
for fixed in [json!([20, 30, 2.5]), json!([150, 30, 2.5])] {
|
||||
let e = s.execute("sheet.fold", &json!({"points": [[40, 0, 2.5], [40, 60, 2.5]], "fixed": fixed})).unwrap_err().to_string();
|
||||
assert!(e.contains("fixed") && e.contains("not on the sheet"), "{fixed}: {e}");
|
||||
assert_eq!(s.doc.to_json(), before);
|
||||
assert_eq!(s.undo.len(), undo);
|
||||
}
|
||||
run(&mut s, "sheet.fold", json!({"points": [[40, 0, 2.5], [40, 60, 2.5]], "fixed": [90, 30, 2.5], "name": "Fold1"}));
|
||||
assert!(run(&mut s, "timeline.inspect", json!({"feature": "Fold1"}))["warning"].is_null());
|
||||
run(&mut s, "parameters.change", json!({"name": "w", "expression": "80 mm"}));
|
||||
let fold = run(&mut s, "timeline.inspect", json!({"feature": "Fold1"}));
|
||||
assert!(fold["error"].is_null(), "{fold}");
|
||||
assert!(fold["warning"].as_str().is_some_and(|w| w.contains("fixed point is not on the sheet") && w.contains("stays")), "{fold}");
|
||||
}
|
||||
Reference in new issue
Block a user