fix: faces of an extrude drawn over a face's projected edges keep an exact source (#505)
A profile traced over a face sketch's projected boundary left the extrude's caps and sides without an exact planar source, so a sketch or offset plane on the new top face was refused. The drawn curve is now the edge's source where it covers a projection. Clearer fixed-plane message; regression tests in the engine, UI harness and design corpus, plus a corpus-wide flat-face attachment check (known gaps under #491-#495). Fixes #489 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4aaa5457b3
commit
45bc22fc30
11 files changed
+1113
-15
No files matched your search
@@ -2321,7 +2321,7 @@ pub fn start_sketch(app: &mut SolveApp, sel: &Sel) -> Result<(), String> {
|
||||
_ => return Err("Select an origin plane, construction plane or planar body face".into()),
|
||||
};
|
||||
let before = app.cam;
|
||||
app.run("sketch.create", json!({ "plane": plane }))?;
|
||||
app.run("sketch.create", json!({ "plane": plane })).map_err(|error| crate::friendly_error("sketch.create", &error))?;
|
||||
app.pre_sketch_cam = Some(before);
|
||||
look_at_sketch(app);
|
||||
// On a face: look at the picked point, not the plane's origin (a corner of the face),
|
||||
|
||||
@@ -330,3 +330,43 @@ fn nested_anchor_saved_fixed_conversion_preserves_rotated_second_author_plane()
|
||||
h.app.run("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(h.app.session.doc.to_json(), before);
|
||||
}
|
||||
|
||||
/// Issue #489: extrude, sketch a rectangle over the whole top face (its corners snap to the
|
||||
/// face's projected boundary), extrude it up as a Join, then pick the new top face for a sketch.
|
||||
#[test]
|
||||
fn picked_top_face_of_a_stacked_join_extrude_takes_a_sketch() {
|
||||
let mut h = Harness::new();
|
||||
h.app.ui.show_origin = false;
|
||||
h.app.run("sketch.create", json!({"plane":"XY","name":"Base"})).unwrap();
|
||||
h.app.run("sketch.rectangle.two_point", json!({"p0":[0,0],"p1":[40,40]})).unwrap();
|
||||
h.app.run("sketch.finish", json!({})).unwrap();
|
||||
h.app.run("solid.extrude", json!({"distance":10,"name":"First"})).unwrap();
|
||||
h.call("ui.view", json!({"view":"top"}));
|
||||
h.call("ui.view", json!({"view":"fit"}));
|
||||
h.settle();
|
||||
let pick_face = |h: &mut Harness, z: f64| {
|
||||
h.step(&json!({"start":"sketch.create"})).unwrap();
|
||||
h.step(&json!({"click":{"world":[20,20,z]}})).unwrap();
|
||||
h.settle();
|
||||
let error = h.app.dialog.as_ref().and_then(|d| d.error.clone());
|
||||
let sketch = h.app.session.active_sketch.unwrap_or_else(|| panic!("top face at z={z} must take a sketch: {error:?}"));
|
||||
let FeatureKind::Sketch { plane: PlaneRef::Face { anchor: Some(_), .. }, .. } = &h.app.session.doc.feature(sketch).unwrap().kind else {
|
||||
panic!("the top face at z={z} must give a certified attachment");
|
||||
};
|
||||
sketch
|
||||
};
|
||||
pick_face(&mut h, 10.);
|
||||
h.step(&json!({"start":"sketch.rectangle.two_point"})).unwrap();
|
||||
h.step(&json!({"click":{"world":[0,0,10]}})).unwrap();
|
||||
h.step(&json!({"click":{"world":[40,40,10]}})).unwrap();
|
||||
h.step(&json!({"key":"Escape"})).unwrap();
|
||||
h.app.finish_sketch();
|
||||
h.app.run("solid.extrude", json!({"distance":10,"operation":"join","name":"Second"})).unwrap();
|
||||
assert_eq!(h.app.session.model.state().bodies.len(), 1);
|
||||
h.settle();
|
||||
let top = pick_face(&mut h, 20.);
|
||||
let FaceAttachmentStatus::Healthy { plane, .. } = h.app.session.placed_document().sketch_face_attachment(top, &h.app.session.model) else {
|
||||
panic!("attachment must be healthy");
|
||||
};
|
||||
assert!((plane.origin.z - 20.).abs() < 1e-9);
|
||||
}
|
||||
Reference in new issue
Block a user