* fix: a modelled thread that would cut nothing is refused (#995) An M5 modelled thread in a 5 mm hole cut nothing into the nut: the internal groove runs from the minor diameter out to the major one, so with the hole as wide as the thread it lay entirely in the hole, and the faceted cut only left the wall faceted with slivers ("the thread goes the wrong way"). The fit check takes a 5 mm hole as an M5 hole. - thread_groove measures how deep the groove cuts into the material (hole wall out to the major radius, or shaft surface in to the root); under a quarter of the basic depth 5H/8 the modelled thread is refused with the measured depth, the tap drill (as solid.hole drills it) or shaft size to use, and the size that fits the face, e.g. "this 5.00 mm hole is too wide for a modelled M5 thread: it would cut 0.00 mm of the thread's 0.43 mm depth; drill 4.2 mm for M5, or thread this hole M6". - Cosmetic threads and their fit check are unchanged. * test: pin the cut-nothing limit for coarse and fine pitch; show the hole size with show_mm --------- Co-authored-by: MateuszSychXpl <151755914+MateuszSychXpl@users.noreply.github.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
This commit is contained in:
2 files changed
+84
No files matched your search
@@ -68,6 +68,72 @@ fn modelled_internal_threads_m3_to_m20() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Issue 995: an M5 modelled thread in a 5 mm hole cut nothing (the groove lay in the hole, from
|
||||
/// the minor diameter out to the 5 mm major one) and only left the wall faceted. A modelled thread
|
||||
/// that would cut next to nothing is refused, with the drill to use and the size that fits;
|
||||
/// a cosmetic one is still only a note.
|
||||
#[test]
|
||||
fn a_modelled_thread_that_would_cut_nothing_is_refused() {
|
||||
let mut s = hex_nut(5.0);
|
||||
let before = volume(&mut s, "Nut");
|
||||
let e = s.execute("solid.thread", &json!({"face": [2.5, 0, 2], "designation": "M5", "modeled": true})).err().map(|e| e.to_string());
|
||||
let e = e.unwrap_or_default();
|
||||
assert!(e.contains("this 5 mm hole") && e.contains("drill 4.2 mm for M5") && e.contains("thread this hole M6"), "{e}");
|
||||
assert!((volume(&mut s, "Nut") - before).abs() < 1e-9);
|
||||
run(&mut s, "solid.thread", json!({"face": [2.5, 0, 2], "designation": "M5"}));
|
||||
|
||||
// A shaft no thicker than the thread's root (17.29 mm for M20) is refused the same way.
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.cylinder", json!({"radius": 8.6, "height": 10, "body_name": "Shaft"}));
|
||||
let e = s.execute("solid.thread", &json!({"face": [8.6, 0, 5], "designation": "M20", "modeled": true})).err().map(|e| e.to_string());
|
||||
let e = e.unwrap_or_default();
|
||||
assert!(e.contains("this 17.2 mm shaft") && e.contains("make the shaft 20 mm for M20") && e.contains("thread this shaft M18"), "{e}");
|
||||
}
|
||||
|
||||
/// The limit is a quarter of the basic depth 5H/8: a hole up to 4.78 mm for M5 (4.86 mm for
|
||||
/// M5x0.5) still takes a modelled thread, a wider one is refused with that pitch's drill.
|
||||
#[test]
|
||||
fn the_cut_nothing_limit_holds_for_coarse_and_fine_pitch() {
|
||||
for (m, hole, refused) in [
|
||||
("M5", 4.5, None),
|
||||
("M5", 4.75, None),
|
||||
("M5", 4.8, Some("drill 4.2 mm for M5, or thread this hole M6")),
|
||||
("M5x0.5", 4.5, None),
|
||||
("M5x0.5", 4.85, None),
|
||||
("M5x0.5", 4.9, Some("drill 4.5 mm for M5x0.5, or thread this hole M6")),
|
||||
("M5x0.5", 5.0, Some("drill 4.5 mm for M5x0.5, or thread this hole M6")),
|
||||
] {
|
||||
let mut s = hex_nut(hole);
|
||||
let before = volume(&mut s, "Nut");
|
||||
let made = s.execute("solid.thread", &json!({"face": [hole / 2.0, 0, 2], "designation": m, "modeled": true}));
|
||||
let cut = before - volume(&mut s, "Nut");
|
||||
match (refused, made) {
|
||||
(None, Ok(_)) => assert!(cut > 0.05, "{m} in {hole} mm: removed {cut}"),
|
||||
(Some(want), Err(e)) => {
|
||||
let e = e.to_string();
|
||||
assert!(e.contains(&format!("this {hole} mm hole")) && e.contains(want), "{m} in {hole} mm: {e}");
|
||||
assert!(cut.abs() < 1e-9, "{m} in {hole} mm: removed {cut}");
|
||||
}
|
||||
(want, got) => panic!("{m} in {hole} mm: want refused {want:?}, got {:?}", got.map(|_| ())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A 4 mm hex nut (inscribed radius 5) with a plain hole of this diameter through it.
|
||||
fn hex_nut(hole: f64) -> Session {
|
||||
let mut s = Session::default();
|
||||
for (id, p) in [
|
||||
("sketch.create", json!({"plane": "XY"})),
|
||||
("sketch.polygon.inscribed", json!({"center": [0, 0], "radius": 5, "sides": 6})),
|
||||
("sketch.finish", json!({})),
|
||||
("solid.extrude", json!({"distance": 4, "body_name": "Nut"})),
|
||||
("solid.hole", json!({"position": [0, 0, 4], "diameter": hole})),
|
||||
] {
|
||||
run(&mut s, id, p);
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
/// Issue 602: rounding the edges of a hex boss on a threaded shaft froze the app. The thread's
|
||||
/// thousands of facets were healed pair by pair, once per feature after it, and the fillet's
|
||||
/// preview then compared each of them with the whole old body.
|
||||
|
||||
Reference in new issue
Block a user