The guard that refuses machining through bends sewed every bend zone and nonparallel panel into one solid. Two bends meeting at an open corner relief share only an edge, so that sew failed with "nonmanifold edge contact" and any cut on the base was refused. Sew and test each piece on its own instead. Reference case 75-sm-bracket-holes now passes; its known_failure mark is removed. Fixes #296 Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7db66bb824
commit
2ebceb6918
6 files changed
+42
-13
No files matched your search
+1
-1
@@ -9,7 +9,7 @@ features → parametric timeline) as an open, pure-Rust, agent-drivable applicat
|
||||
|---|---|
|
||||
| Where are we? | **M0 and M13 done; M1, M2, M5–M7, M9, M10, M14 in progress.** Sketch with solver and the full SKETCH create/modify set; extrude, revolve (two sides, symmetric, to object), fillet/chamfer (variable radius, chord length, a radius per edge, corner patches), holes, threads (cosmetic and modelled), patterns, mirror, shell (inside/outside/both), draft, loft, sweep, coil, split body and split face, delete face, construction planes, axes and points; persistent naming of faces and edges; parameters and configurations; components, joints, motion studies, exploded views; sheet metal and a plastic enclosure subset; STEP/IGES/3MF/STL/OBJ/DXF; autosave and crash recovery; desktop and browser (wasm) app, CLI, MCP server. |
|
||||
| Features | Every registered command by area — [docs/features.md](docs/features.md) (`cargo xtask features`); registry breadth does not establish workflow or geometric depth |
|
||||
| Reference tests | **75 / 76 parts match** — [docs/oracle.md](docs/oracle.md): public cases in `tests/oracle/` with analytic (55) or FreeCAD-computed (21) expected values, run by CI on every PR; 75-sm-bracket-holes fails (#296). |
|
||||
| Reference tests | **76 / 76 parts match** — [docs/oracle.md](docs/oracle.md): public cases in `tests/oracle/` with analytic (55) or FreeCAD-computed (21) expected values, run by CI on every PR. |
|
||||
| Tests | Solver, profiles, kernel booleans/blends/measures, expressions, timeline, persistent naming, file formats and migrations, assemblies, sheet metal, engine end-to-end, hostile-input fuzz over every command and headless UI scenarios. `cargo xtask ci` reports the current passing counts. |
|
||||
| Gates | `cargo xtask ci`: fmt, clippy -D warnings, tests, asset attribution, layering, wasm32 build — green |
|
||||
| Time to a useful alpha (M0–M6) | ≈ **82 agent hours** remain (sum of the M1–M6 estimates below) |
|
||||
|
||||
@@ -687,7 +687,7 @@ pub(super) fn sheet_cut(
|
||||
plans.push((sh.body.clone(), Some((sh, mb.body.clone()))));
|
||||
continue;
|
||||
}
|
||||
if let Some(bends) = sh.unmodeled_solid(&ss.plane)? {
|
||||
for bends in sh.unmodeled_solids(&ss.plane)? {
|
||||
for tool in tools {
|
||||
if kernel::boolean(&bends, tool, kernel::BoolOp::Intersect)?.is_some() {
|
||||
return Err(DocError::Invalid(
|
||||
|
||||
@@ -879,10 +879,13 @@ impl SheetBody {
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
/// Actual curved material, used to certify unsupported folded machining before mutation.
|
||||
pub(crate) fn unmodeled_solid(&self, plane: &Plane) -> Result<Option<Body>> {
|
||||
/// Actual curved material, used to certify unsupported folded machining before mutation:
|
||||
/// one solid per bend zone or nonparallel panel. Each is sewn on its own, because the
|
||||
/// selected pieces need not form a manifold solid together (two bends meeting at an open
|
||||
/// corner relief touch only along an edge).
|
||||
pub(crate) fn unmodeled_solids(&self, plane: &Plane) -> Result<Vec<Body>> {
|
||||
if self.flat || self.flanges.is_empty() {
|
||||
return Ok(None);
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let mut work = FlatWork::new();
|
||||
let cells: Vec<_> = self
|
||||
@@ -893,10 +896,7 @@ impl SheetBody {
|
||||
Piece::Panel(pi) => apply_vector(&self.world(pi), Vec3::Z).cross(plane.normal()).len() > 1e-6,
|
||||
})
|
||||
.collect();
|
||||
if cells.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(kernel::sew(&self.faces(&cells, 0.0, self.t, &mut work)?)?))
|
||||
cells.iter().map(|c| Ok(kernel::sew(&self.faces(std::slice::from_ref(c), 0.0, self.t, &mut work)?)?)).collect()
|
||||
}
|
||||
|
||||
fn through_pocket_regions(&self, work: &mut FlatWork) -> Result<Vec<Region2>> {
|
||||
|
||||
@@ -1685,3 +1685,33 @@ fn fold_at_a_tab_root_names_the_real_cause() {
|
||||
outline_blank(&mut ok, json!([[0, 0], [100, 0], [100, 50], [60, 50], [60, 70], [40, 70], [40, 50], [0, 50]]));
|
||||
run(&mut ok, "sheet.fold", json!({"points": [[40, 51, 0.8], [60, 51, 0.8]], "fixed": [50, 25, 0.8]}));
|
||||
}
|
||||
|
||||
/// #296: two bends meeting at an open corner relief touch only along an edge, so the folded
|
||||
/// material the machining guard checks must not be sewn into one solid.
|
||||
#[test]
|
||||
fn hole_beside_two_adjacent_edge_flanges_cuts_and_bends_stay_guarded() {
|
||||
let build = |s: &mut Session| {
|
||||
base(s, 80.0, 40.0);
|
||||
run(s, "sheet.flange", json!({"edges":[[40.0, 0.0, 2.5]],"height":30,"angle":90}));
|
||||
run(s, "sheet.flange", json!({"edges":[[80.0, 22.5, 2.5]],"height":15,"angle":90}));
|
||||
};
|
||||
let hole = |s: &mut Session, name: &str, centre: [f64; 2]| {
|
||||
run(s, "sketch.create", json!({"plane":"XY","name":name}));
|
||||
run(s, "sketch.circle.center", json!({"center":centre,"radius":3}));
|
||||
run(s, "sketch.finish", json!({}));
|
||||
s.execute("solid.extrude", &json!({"sketch":name,"through_all":true,"operation":"cut"}))
|
||||
};
|
||||
let mut s = Session::default();
|
||||
build(&mut s);
|
||||
let before = volume(&mut s);
|
||||
assert!((before - 14057.2).abs() < 0.1, "{before}");
|
||||
if let Err(e) = hole(&mut s, "Hole", [25.0, 20.0]) {
|
||||
panic!("hole beside two flanges: {e}");
|
||||
}
|
||||
let after = volume(&mut s);
|
||||
assert!((before - after - 9.0 * PI * 2.5).abs() < 0.05, "{before} -> {after}");
|
||||
let mut bend = Session::default();
|
||||
build(&mut bend);
|
||||
let error = hole(&mut bend, "Bend", [78.5, 20.0]).unwrap_err().to_string();
|
||||
assert!(error.contains("bend"), "{error}");
|
||||
}
|
||||
+2
-2
@@ -9,7 +9,7 @@ Where the expected values come from (the **Source** column):
|
||||
|
||||
No expected value comes from a commercial CAD product.
|
||||
|
||||
**75 / 76 cases pass**; sources: 55 analytic, 21 freecad. 1 known failure(s) tracked in issues; 0 case(s) without expected values yet.
|
||||
**76 / 76 cases pass**; sources: 55 analytic, 21 freecad. 0 known failure(s) tracked in issues; 0 case(s) without expected values yet.
|
||||
|
||||
| Case | Source | Result | Surface distance mm (max / mean) | Method | Notes |
|
||||
|---|---|---|---|---|---|
|
||||
@@ -87,7 +87,7 @@ No expected value comes from a commercial CAD product.
|
||||
| 72-sm-contour-flange | analytic | pass | | contour flange, width 50 along +Y, XZ section: plate x 0..35 z 0..2.5; bend R 2.5..5 about (35, 5) (sketch corner = outer virtual sharp, setback R+t); wall x 37.5..40 z 5..27.5; bend R 2.5..5 about (42.5, 27.5) (sketch corner = inner virtual sharp, setback R); plate x 42.5..70 z 30..32.5 | SolidCraft convention: the sketch lines are the sheet face away from the material (material right of travel); thickness grows to the material side |
|
||||
| 73-sm-hem | analytic | pass | | 68's part (height 20) plus a flat hem on the flange top edge: 180 deg bend, inner radius = hem gap 0.01, outer 2.51, about (y 2.51, z 20), folded toward +y (inside); hem leg 8 - (0.01 + 2.5) = 5.49: y 2.52..5.02, z 14.51..20; extrusion along x 100 | SolidCraft conventions: hem gap 0.01 mm (rule hem_gap), hem length measured to the outer face of the hem bend (top at z 22.51), hem folds over the flange's inner face. |
|
||||
| 74-sm-unfold-refold | analytic | pass | | U channel 80x50 (t 2.5, R 2.5, base y 5..45, flanges z 5..20) with the flat-pattern slot x 35..45, y -12..10 refolded: BA = (pi/2)(R + K t) = 5.654866776, so the slot removes base y 5..10, the whole y=0 bend and the flange from z 5 to 5 + (12 - (BA - 5)) = 16.345133224; x-segments 0..35 / 35..45 / 45..80 | unfold/refold maps flat distance through the K-factor neutral layer (K 0.44) |
|
||||
| 75-sm-bracket-holes | analytic | known failure (#296) | | base x 0..75, y 5..40, z 0..2.5 (trimmed 5 by each flange's setback); bend 1 (R 2.5..5 about y 5, z 5) x 0..75; flange 1 y 0..2.5, z 5..30, x 0..80; bend 2 (R 2.5..5 about x 75, z 5) y 5..40; flange 2 x 77.5..80, z 5..15, y 5..40; minus two d6 through holes at (25,20), (55,20) in the base. A = sum of prism areas - 2 x 4 joint areas (75t, 75t, 35t, 35t) + hole walls - hole discs | step 14 (solid.extrude): invalid input: sew: nonmanifold edge contact; SolidCraft convention: the second flange trims the base under the first bend, so bend 1 stops at x 75 while flange 1's panel runs to x 80 (open corner relief, no fill). |
|
||||
| 75-sm-bracket-holes | analytic | pass | | base x 0..75, y 5..40, z 0..2.5 (trimmed 5 by each flange's setback); bend 1 (R 2.5..5 about y 5, z 5) x 0..75; flange 1 y 0..2.5, z 5..30, x 0..80; bend 2 (R 2.5..5 about x 75, z 5) y 5..40; flange 2 x 77.5..80, z 5..15, y 5..40; minus two d6 through holes at (25,20), (55,20) in the base. A = sum of prism areas - 2 x 4 joint areas (75t, 75t, 35t, 35t) + hole walls - hole discs | SolidCraft convention: the second flange trims the base under the first bend, so bend 1 stops at x 75 while flange 1's panel runs to x 80 (open corner relief, no fill). |
|
||||
| 76-sm-thickness-variant | analytic | pass | | final rule thickness 3 (bend radius = Thickness = 3): base y 6..60 x 3; bend annulus R 3..6 about (y 6, z 6); flange y 0..3, z 6..20; extrusion along x 100 | the rule's thickness edits regenerate the whole sheet body; only the final 3 mm state matters |
|
||||
|
||||
## Not covered by a reference case
|
||||
|
||||
@@ -21,6 +21,5 @@
|
||||
30.0
|
||||
]
|
||||
},
|
||||
"note": "SolidCraft convention: the second flange trims the base under the first bend, so bend 1 stops at x 75 while flange 1's panel runs to x 80 (open corner relief, no fill).",
|
||||
"known_failure": "#296"
|
||||
"note": "SolidCraft convention: the second flange trims the base under the first bend, so bend 1 stops at x 75 while flange 1's panel runs to x 80 (open corner relief, no fill)."
|
||||
}
|
||||
Reference in new issue
Block a user