mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-02 18:16:19 +00:00
A pattern or mirror replays its source hole's definition, so a hole with reference edges finds its centre again from them. That happens after the hole is drilled: through the part, the centre is then in the opening, and the check that the centre lies on the face refused every copy. The replay now skips that check; placing the hole itself still makes it. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4665,7 +4665,7 @@ fn hole_spots(vals: &BTreeMap<String, Value>, st: &ModelState, f: &Feature) -> R
|
||||
_ => {
|
||||
let dir = direction.normalized().ok_or_else(|| DocError::Invalid("hole direction".into()))?;
|
||||
let at = hole_on_surface(st, single_hole_position(vals, st, position, at, points)?, dir, &[]);
|
||||
let at = with_references(vals, st, f, at, dir)?;
|
||||
let at = with_references(vals, st, f, at, dir, true)?;
|
||||
let dir = hole_dir_into_face(st, at, dir);
|
||||
return Ok((vec![hole_on_surface(st, at, dir, &[])], dir));
|
||||
}
|
||||
@@ -4713,7 +4713,7 @@ fn hole_spots_raw(vals: &BTreeMap<String, Value>, st: &ModelState, f: &Feature)
|
||||
_ => {
|
||||
let dir = direction.normalized().ok_or_else(|| DocError::Invalid("hole direction".into()))?;
|
||||
let p = single_hole_position(vals, st, position, at, points)?;
|
||||
let p = if hole_references(f).is_empty() { p } else { with_references(vals, st, f, hole_on_surface(st, p, dir, &[]), dir)? };
|
||||
let p = if hole_references(f).is_empty() { p } else { with_references(vals, st, f, hole_on_surface(st, p, dir, &[]), dir, false)? };
|
||||
Ok((vec![p], hole_dir_into_face(st, hole_on_surface(st, p, dir, &[]), dir)))
|
||||
}
|
||||
}
|
||||
@@ -4747,13 +4747,15 @@ fn hole_references(f: &Feature) -> &[crate::HoleReference] {
|
||||
/// plane of the flat face at its base, square to its axis.
|
||||
fn cylinder_base(vals: &BTreeMap<String, Value>, st: &ModelState, f: &Feature, base: &crate::PointExpr, axis: Vec3) -> Result<Vec3> {
|
||||
let at = point(vals, base)?;
|
||||
with_references(vals, st, f, at, -axis)
|
||||
with_references(vals, st, f, at, -axis, true)
|
||||
}
|
||||
|
||||
/// Where a Single hole (or a cylinder) with reference edges (#37) is placed: at its distances
|
||||
/// from the edges' lines, in the plane of the face at `at` (on the surface), the edges found by
|
||||
/// their names, else their points. A feature without reference edges stays at `at`.
|
||||
fn with_references(vals: &BTreeMap<String, Value>, st: &ModelState, f: &Feature, at: Vec3, dir: Vec3) -> Result<Vec3> {
|
||||
/// their names, else their points. A feature without reference edges stays at `at`. With
|
||||
/// `on_face` the centre must lie on that face; a pattern or mirror replaying a hole that is
|
||||
/// already drilled finds its centre in the opening and doesn't ask (#8055).
|
||||
fn with_references(vals: &BTreeMap<String, Value>, st: &ModelState, f: &Feature, at: Vec3, dir: Vec3, on_face: bool) -> Result<Vec3> {
|
||||
let references = hole_references(f);
|
||||
if references.is_empty() {
|
||||
return Ok(at);
|
||||
@@ -4765,7 +4767,7 @@ fn with_references(vals: &BTreeMap<String, Value>, st: &ModelState, f: &Feature,
|
||||
let edges: Vec<(Vec3, Option<&str>)> = references.iter().enumerate().map(|(i, r)| (r.edge.0, f.edge_names.get(i).map(String::as_str))).collect();
|
||||
let frame = crate::hole_reference::reference_frame(st, at, dir, &edges, false)?;
|
||||
let centre = frame.place(at, &distances)?;
|
||||
if !crate::hole_reference::on_reference_face(st, &frame, centre) {
|
||||
if on_face && !crate::hole_reference::on_reference_face(st, &frame, centre) {
|
||||
return Err(DocError::Invalid("the reference distances put the hole's centre off its face".into()));
|
||||
}
|
||||
Ok(centre)
|
||||
|
||||
@@ -105,6 +105,32 @@ fn a_hole_follows_its_edges_when_the_box_grows() {
|
||||
assert_eq!(hole(&t).1, hole(&s).1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_referenced_hole_can_be_patterned_and_mirrored() {
|
||||
// #8055, #6754: a pattern or mirror places its copies from the hole's own spot, found again
|
||||
// after the hole has been drilled. Through the plate, its centre is then in the opening, with
|
||||
// no face under it.
|
||||
let mut s = plate();
|
||||
let mut through = referenced(json!([{"edge": [0, 20, 10], "distance": 10}, {"edge": [25, 0, 10], "distance": 12}]));
|
||||
through.as_object_mut().unwrap().remove("depth");
|
||||
run(&mut s, "solid.hole", through);
|
||||
run(
|
||||
&mut s,
|
||||
"solid.pattern.rectangular",
|
||||
json!({"features": ["H"], "dir1": [1, 0, 0], "count1": 2, "spacing1": 12, "dir2": [0, 1, 0], "count2": 2, "spacing2": 12, "name": "P"}),
|
||||
);
|
||||
for (x, y) in [(10.0, 12.0), (22.0, 12.0), (10.0, 24.0), (22.0, 24.0)] {
|
||||
bore_at(&s, x, y, 10.0);
|
||||
}
|
||||
run(&mut s, "solid.mirror", json!({"features": ["H"], "plane": {"origin": [25, 0, 0], "normal": [1, 0, 0]}, "name": "M"}));
|
||||
bore_at(&s, 40.0, 12.0, 10.0);
|
||||
// The copies still follow the hole's edges.
|
||||
run(&mut s, "parameters.change", json!({"name": "len", "expression": "60 mm"}));
|
||||
let p = s.doc.find_feature("P").unwrap().id;
|
||||
assert!(s.model.result(p).is_some_and(|r| r.error.is_none()), "{:?}", s.model.result(p).map(|r| &r.error));
|
||||
bore_at(&s, 22.0, 24.0, 10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_bad_reference_is_refused() {
|
||||
let mut s = plate();
|
||||
|
||||
Reference in New Issue
Block a user