mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-04 19:15:57 +00:00
hole: keep a countersink's tool within its rim above the face
The countersink's cone started 1% of the drilled body's size above the entry face, widening on to rc + top tan(half) there, so above the face it cut beyond the countersink's rim: a wall 0.15 mm from the rim was notched (#5041, 12 faces instead of 10). The tool is now one revolved profile: the cone from its rim on the face to just inside the bore, and above the face the rim's own cylinder, as the bore's cylinder runs on above it. The cone stays the profile's first segment (csink:side:0). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+16
-8
@@ -4300,8 +4300,8 @@ fn hole_tools(
|
||||
}
|
||||
|
||||
/// The scale a hole's tools are sized by: the largest body the hole's axis meets (#2904), so a
|
||||
/// large body elsewhere in the model changes neither the tool's reach above the face (and so
|
||||
/// the countersink's outline) nor its depth. A hole whose axis meets no body is sized by the
|
||||
/// large body elsewhere in the model changes neither the tool's reach above the face nor its
|
||||
/// depth. A hole whose axis meets no body is sized by the
|
||||
/// largest body of the model, as before.
|
||||
fn hole_axis_size(st: &ModelState, position: &Vec3, dir: Vec3) -> f64 {
|
||||
let along = |b: &ModelBody| {
|
||||
@@ -4417,13 +4417,21 @@ fn hole_tools_and_prisms(
|
||||
if !(rc > r && half > 1e-3 && half < std::f64::consts::FRAC_PI_2) {
|
||||
return Err(DocError::Invalid("the countersink must be wider than the hole".into()));
|
||||
}
|
||||
// Cone from above the face to just inside the hole radius, then the hole.
|
||||
let r_top = rc + top * half.tan();
|
||||
// One revolved tool: the cone from its rim on the face to just inside the hole
|
||||
// radius, and above the face the rim's cylinder, as the bore's own cylinder runs on
|
||||
// above it. The cone widening on above the face would cut what stands beside the rim
|
||||
// (#5041). The cone is the profile's first segment (`csink:side:0`).
|
||||
let r_end = r * 0.9;
|
||||
let len = (r_top - r_end) / half.tan();
|
||||
let top_plane = plane.offset(top);
|
||||
let csink = kernel::extrude_tapered(&top_plane, &disc(r_top), len, -1.0, -half, kernel::DraftCorner::default())?;
|
||||
tools.push((crate::lineage::part_tool(csink, "csink"), None));
|
||||
let len = (rc - r_end) / half.tan();
|
||||
let up = -dir;
|
||||
let rp = Plane::new(*position, up.any_perp(), up).ok_or_else(|| DocError::Invalid("hole axis".into()))?;
|
||||
let pts = [Vec2::new(rc, 0.0), Vec2::new(r_end, -len), Vec2::new(0.0, -len), Vec2::new(0.0, top), Vec2::new(rc, top)];
|
||||
let region = Region2 { outer: solidcraft_geom::Loop2::polygon(&pts), holes: vec![] };
|
||||
tools.extend(
|
||||
kernel::revolve(&rp, &[region], Vec2::ZERO, Vec2::Y, std::f64::consts::TAU)?
|
||||
.into_iter()
|
||||
.map(|tool| (crate::lineage::part_tool(tool, "csink"), None)),
|
||||
);
|
||||
tools.extend(bore(tip_angle.as_ref())?);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,3 +133,29 @@ fn a_far_body_does_not_change_a_countersunk_hole() {
|
||||
assert_eq!(small_changed, large_changed, "bodies changed");
|
||||
assert_eq!(large_changed, json!(["lid"]), "only the lid is drilled");
|
||||
}
|
||||
|
||||
/// #5041: a countersunk Ø6.4 hole (Ø13.7, 90°) 0.15 mm from a wall standing on the plate cuts
|
||||
/// nothing above the entry face: the countersink's rim (y 29.85) stays clear of the wall (y 30).
|
||||
/// 10 faces: bottom, the two L-shaped ends, front and back, the plate top, the wall's front and
|
||||
/// top, the cone and the bore.
|
||||
#[test]
|
||||
fn a_countersink_beside_a_wall_stays_below_the_face() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.box", json!({"length": 40, "width": 40, "height": 8, "corner": [0, 0, 0]}));
|
||||
run(&mut s, "solid.box", json!({"length": 40, "width": 10, "height": 30, "corner": [0, 30, 8], "operation": "join"}));
|
||||
run(
|
||||
&mut s,
|
||||
"solid.hole",
|
||||
json!({"position": [20, 23, 8], "direction": [0, 0, -1], "diameter": 6.4, "type": "countersink", "cs_diameter": 13.7, "cs_angle": 90}),
|
||||
);
|
||||
let st = s.model.state();
|
||||
let [body] = st.bodies.as_slice() else { panic!("one body, got {}", st.bodies.len()) };
|
||||
assert_eq!(body.body.face_count(), 10, "the wall is not notched");
|
||||
// In the wall's foot, 0.4 mm above the face: still solid.
|
||||
assert!(solid_at(&s, Vec3::new(20.0, 30.2, 8.4)), "the wall's foot is cut");
|
||||
let (rr, r, h) = (6.85_f64, 3.2_f64, 3.65_f64);
|
||||
let removed = PI * h / 3.0 * (rr * rr + rr * r + r * r) + PI * r * r * (8.0 - h);
|
||||
let want = 24800.0 - removed;
|
||||
let got = volume(&mut s);
|
||||
assert!((got - want).abs() < 1e-3, "volume {got}, want {want}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user