mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-05 19:45:54 +00:00
Tapped holes: the tap maker's drill for every form, and a drill of the user's within 5%
Every tapped hole now drills the tap maker's tabulated drill. ISO metric takes Yamawa YEU23's drill size column for cutting taps (printed p.704, M coarse; pp.704 to 706, MF fine): M8 drills 6.8 mm and M12 10.3 mm, where the major less the pitch gave 6.75 and 10.25. A custom metric size the table lacks keeps the major less the pitch. UNC/UNF (Guhring), G, Rp, Rc and NPT (Yamawa) were tabulated already. The Thread command's suggested hole for a metric size follows. The Hole dialog's Tap drill diameter is now a field, the tap maker's drill by default, and the command's `diameter` with a thread is checked the same way: a drill is accepted from the internal thread's standard minor diameter (or the tap maker's drill where that lies below it: Rp, Rc, NPT) to 5% over the tap maker's drill. Outside that the command names the range and why. The field's tooltip gives the drill, the minor diameter and the range. Presets keep a chosen drill. A saved design keeps the diameter it stored, so it builds as before, and editing such a hole keeps its drill: no preference and no format change. Scripts that give a thread without a diameter drill the new default; ladder 05/007, 11/003 and rig case threads/2115 follow analytically, and their golden entries are re-measured from runs (tier 05 re-sorted by time). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUXeKMoxpM7Vuv8Rb6E3K2
This commit is contained in:
@@ -3,49 +3,42 @@
|
||||
"thread.form_bspp": "Pipe G (ISO 228-1)",
|
||||
"thread.form_bspt": "Pipe taper R, Rc (ISO 7-1)",
|
||||
"thread.form_npt": "Pipe taper NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "Pipe Rp (ISO 7-1)",
|
||||
"thread.taper_drill_exact": "The standard's minor diameter where the thread ends is {exact} mm; the drill is the tap maker's recommendation"
|
||||
"thread.form_rp": "Pipe Rp (ISO 7-1)"
|
||||
},
|
||||
"ar": {
|
||||
"thread.form_bspp": "سن أنابيب G (ISO 228-1)",
|
||||
"thread.form_bspt": "سن أنابيب مخروطي R، Rc (ISO 7-1)",
|
||||
"thread.form_npt": "سن أنابيب مخروطي NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "سن أنابيب داخلي متوازٍ Rp (ISO 7-1)",
|
||||
"thread.taper_drill_exact": "القطر الأصغر القياسي عند نهاية السن {exact} مم؛ المثقاب هو ما يوصي به صانع اللقمة"
|
||||
"thread.form_rp": "سن أنابيب داخلي متوازٍ Rp (ISO 7-1)"
|
||||
},
|
||||
"he": {
|
||||
"thread.form_bspp": "תבריג צנרת G (ISO 228-1)",
|
||||
"thread.form_bspt": "תבריג צנרת קוני R, Rc (ISO 7-1)",
|
||||
"thread.form_npt": "תבריג צנרת קוני NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "תבריג צנרת פנימי מקביל Rp (ISO 7-1)",
|
||||
"thread.taper_drill_exact": "הקוטר הקטן התקני בסוף התבריג הוא {exact} מ\"מ; המקדח הוא המלצת יצרן המברז"
|
||||
"thread.form_rp": "תבריג צנרת פנימי מקביל Rp (ISO 7-1)"
|
||||
},
|
||||
"ja": {
|
||||
"thread.form_bspp": "管用平行ねじ G(ISO 228-1)",
|
||||
"thread.form_bspt": "管用テーパねじ R、Rc(ISO 7-1)",
|
||||
"thread.form_npt": "管用テーパねじ NPT(ASME B1.20.1)",
|
||||
"thread.form_rp": "管用平行めねじ Rp(ISO 7-1)",
|
||||
"thread.taper_drill_exact": "ねじ終端での規格の谷径は {exact} mm です。下穴ドリルはタップメーカーの推奨値です"
|
||||
"thread.form_rp": "管用平行めねじ Rp(ISO 7-1)"
|
||||
},
|
||||
"zh": {
|
||||
"thread.form_bspp": "管螺纹 G(ISO 228-1)",
|
||||
"thread.form_bspt": "圆锥管螺纹 R、Rc(ISO 7-1)",
|
||||
"thread.form_npt": "圆锥管螺纹 NPT(ASME B1.20.1)",
|
||||
"thread.form_rp": "圆柱内管螺纹 Rp(ISO 7-1)",
|
||||
"thread.taper_drill_exact": "螺纹终端处的标准小径为 {exact} mm;钻头为丝锥制造商的推荐值"
|
||||
"thread.form_rp": "圆柱内管螺纹 Rp(ISO 7-1)"
|
||||
},
|
||||
"de": {
|
||||
"thread.form_bspp": "Rohrgewinde G (ISO 228-1)",
|
||||
"thread.form_bspt": "Kegeliges Rohrgewinde R, Rc (ISO 7-1)",
|
||||
"thread.form_npt": "Kegeliges Rohrgewinde NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "Zylindrisches Innenrohrgewinde Rp (ISO 7-1)",
|
||||
"thread.taper_drill_exact": "Der genormte Kerndurchmesser am Gewindeende ist {exact} mm; der Bohrer ist die Empfehlung des Gewindebohrerherstellers"
|
||||
"thread.form_rp": "Zylindrisches Innenrohrgewinde Rp (ISO 7-1)"
|
||||
},
|
||||
"ko": {
|
||||
"thread.form_bspp": "관용 평행나사 G (ISO 228-1)",
|
||||
"thread.form_bspt": "관용 테이퍼나사 R, Rc (ISO 7-1)",
|
||||
"thread.form_npt": "관용 테이퍼나사 NPT (ASME B1.20.1)",
|
||||
"thread.form_rp": "관용 평행 암나사 Rp (ISO 7-1)",
|
||||
"thread.taper_drill_exact": "나사 끝에서의 규격 골지름은 {exact} mm이며, 드릴은 탭 제조사의 권장값입니다"
|
||||
"thread.form_rp": "관용 평행 암나사 Rp (ISO 7-1)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"en": {
|
||||
"thread.drill_range": "The tap maker's drill is {drill} mm. The internal thread's standard minor diameter is {minor} mm. A drill from {low} to {high} mm is accepted.",
|
||||
"thread.drill_range_custom": "The drill is the major diameter less the pitch, {drill} mm. The internal thread's standard minor diameter is {minor} mm. A drill from {low} to {high} mm is accepted.",
|
||||
"thread.drill_range_taper": "The tap maker's drill is {drill} mm. The standard's minor diameter where the thread ends is {minor} mm. A drill from {low} to {high} mm is accepted."
|
||||
},
|
||||
"ar": {
|
||||
"thread.drill_range": "مثقاب صانع اللقمة {drill} مم. القطر الأصغر القياسي للسن الداخلي {minor} مم. يُقبل مثقاب من {low} إلى {high} مم.",
|
||||
"thread.drill_range_custom": "المثقاب هو القطر الأكبر ناقص الخطوة، {drill} مم. القطر الأصغر القياسي للسن الداخلي {minor} مم. يُقبل مثقاب من {low} إلى {high} مم.",
|
||||
"thread.drill_range_taper": "مثقاب صانع اللقمة {drill} مم. القطر الأصغر القياسي عند نهاية السن {minor} مم. يُقبل مثقاب من {low} إلى {high} مم."
|
||||
},
|
||||
"he": {
|
||||
"thread.drill_range": "מקדח יצרן המברז הוא {drill} מ\"מ. הקוטר הקטן התקני של התבריג הפנימי הוא {minor} מ\"מ. מתקבל מקדח מ-{low} עד {high} מ\"מ.",
|
||||
"thread.drill_range_custom": "המקדח הוא הקוטר הגדול פחות הפסיעה, {drill} מ\"מ. הקוטר הקטן התקני של התבריג הפנימי הוא {minor} מ\"מ. מתקבל מקדח מ-{low} עד {high} מ\"מ.",
|
||||
"thread.drill_range_taper": "מקדח יצרן המברז הוא {drill} מ\"מ. הקוטר הקטן התקני בסוף התבריג הוא {minor} מ\"מ. מתקבל מקדח מ-{low} עד {high} מ\"מ."
|
||||
},
|
||||
"ja": {
|
||||
"thread.drill_range": "タップメーカーの下穴ドリルは {drill} mm です。めねじの規格の内径は {minor} mm です。{low}~{high} mm のドリルを使用できます。",
|
||||
"thread.drill_range_custom": "下穴ドリルは外径からピッチを引いた {drill} mm です。めねじの規格の内径は {minor} mm です。{low}~{high} mm のドリルを使用できます。",
|
||||
"thread.drill_range_taper": "タップメーカーの下穴ドリルは {drill} mm です。ねじ終端での規格の内径は {minor} mm です。{low}~{high} mm のドリルを使用できます。"
|
||||
},
|
||||
"zh": {
|
||||
"thread.drill_range": "丝锥制造商的钻头为 {drill} mm。内螺纹的标准小径为 {minor} mm。可使用 {low} 至 {high} mm 的钻头。",
|
||||
"thread.drill_range_custom": "钻头为大径减螺距,{drill} mm。内螺纹的标准小径为 {minor} mm。可使用 {low} 至 {high} mm 的钻头。",
|
||||
"thread.drill_range_taper": "丝锥制造商的钻头为 {drill} mm。螺纹终端处的标准小径为 {minor} mm。可使用 {low} 至 {high} mm 的钻头。"
|
||||
},
|
||||
"de": {
|
||||
"thread.drill_range": "Der Bohrer des Gewindebohrerherstellers ist {drill} mm. Der genormte Kerndurchmesser des Innengewindes ist {minor} mm. Ein Bohrer von {low} bis {high} mm wird angenommen.",
|
||||
"thread.drill_range_custom": "Der Bohrer ist der Nenndurchmesser minus Steigung, {drill} mm. Der genormte Kerndurchmesser des Innengewindes ist {minor} mm. Ein Bohrer von {low} bis {high} mm wird angenommen.",
|
||||
"thread.drill_range_taper": "Der Bohrer des Gewindebohrerherstellers ist {drill} mm. Der genormte Kerndurchmesser am Gewindeende ist {minor} mm. Ein Bohrer von {low} bis {high} mm wird angenommen."
|
||||
},
|
||||
"ko": {
|
||||
"thread.drill_range": "탭 제조사의 드릴은 {drill} mm입니다. 암나사의 규격 골지름은 {minor} mm입니다. {low}~{high} mm 드릴을 사용할 수 있습니다.",
|
||||
"thread.drill_range_custom": "드릴은 바깥지름에서 피치를 뺀 {drill} mm입니다. 암나사의 규격 골지름은 {minor} mm입니다. {low}~{high} mm 드릴을 사용할 수 있습니다.",
|
||||
"thread.drill_range_taper": "탭 제조사의 드릴은 {drill} mm입니다. 나사 끝에서의 규격 골지름은 {minor} mm입니다. {low}~{high} mm 드릴을 사용할 수 있습니다."
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,9 @@ mod points;
|
||||
mod profile_refs;
|
||||
pub mod progress;
|
||||
mod thread_catalog;
|
||||
pub use thread_catalog::{TaperDrill, TappedThread, TappedThreadFamily, parse_tapped_thread, tapped_thread_sizes, untabulated_taper_tap};
|
||||
pub use thread_catalog::{
|
||||
DRILL_ALLOWANCE, TaperDrill, TappedThread, TappedThreadFamily, metric_tap_drill, parse_tapped_thread, tapped_thread_sizes, untabulated_taper_tap,
|
||||
};
|
||||
mod walk;
|
||||
pub use profile_refs::{capture_profile_selection, profile_fallback_notes, profile_selection_indices, profile_selection_indices_lenient};
|
||||
pub mod plastic;
|
||||
|
||||
@@ -202,14 +202,308 @@ const UNIFIED: &[(&str, TappedThreadFamily, f64, u32, f64)] = &[
|
||||
("1-12 UNF", TappedThreadFamily::Unf, 25.4, 12, 23.3),
|
||||
];
|
||||
|
||||
/// ISO metric cutting-tap drills (nominal diameter, pitch, drill; mm): Yamawa YEU23, "Drill size
|
||||
/// and recommended hole size for cutting taps", printed p.704 (M, coarse) and pp.704 to 706 (MF,
|
||||
/// fine), the drill size column. MF 70 x 1.5 is left out: its printed drill (68.00) lies below
|
||||
/// the printed minimum minor diameter (68.376), a misprint.
|
||||
/// <https://www.yamawa.eu/files/Yamawa_European_Catalogue_YEU23.pdf>
|
||||
const METRIC_DRILLS: &[(f64, f64, f64)] = &[
|
||||
(1.0, 0.2, 0.8),
|
||||
(1.0, 0.25, 0.75),
|
||||
(1.1, 0.2, 0.9),
|
||||
(1.1, 0.25, 0.85),
|
||||
(1.2, 0.2, 1.0),
|
||||
(1.2, 0.25, 0.95),
|
||||
(1.4, 0.2, 1.2),
|
||||
(1.4, 0.3, 1.1),
|
||||
(1.6, 0.2, 1.4),
|
||||
(1.6, 0.35, 1.25),
|
||||
(1.7, 0.2, 1.5),
|
||||
(1.7, 0.35, 1.35),
|
||||
(1.8, 0.2, 1.6),
|
||||
(1.8, 0.35, 1.45),
|
||||
(2.0, 0.25, 1.75),
|
||||
(2.0, 0.4, 1.6),
|
||||
(2.2, 0.25, 1.95),
|
||||
(2.2, 0.45, 1.75),
|
||||
(2.3, 0.25, 2.05),
|
||||
(2.3, 0.4, 1.9),
|
||||
(2.5, 0.35, 2.2),
|
||||
(2.5, 0.45, 2.1),
|
||||
(2.6, 0.35, 2.3),
|
||||
(2.6, 0.45, 2.2),
|
||||
(3.0, 0.35, 2.7),
|
||||
(3.0, 0.5, 2.5),
|
||||
(3.5, 0.35, 3.2),
|
||||
(3.5, 0.6, 2.9),
|
||||
(4.0, 0.5, 3.5),
|
||||
(4.0, 0.7, 3.3),
|
||||
(4.5, 0.5, 4.0),
|
||||
(4.5, 0.75, 3.8),
|
||||
(5.0, 0.5, 4.5),
|
||||
(5.0, 0.8, 4.2),
|
||||
(5.5, 0.5, 5.0),
|
||||
(6.0, 0.5, 5.5),
|
||||
(6.0, 0.75, 5.3),
|
||||
(6.0, 1.0, 5.0),
|
||||
(7.0, 0.5, 6.5),
|
||||
(7.0, 0.75, 6.3),
|
||||
(7.0, 1.0, 6.0),
|
||||
(8.0, 0.5, 7.5),
|
||||
(8.0, 0.75, 7.3),
|
||||
(8.0, 1.0, 7.0),
|
||||
(8.0, 1.25, 6.8),
|
||||
(9.0, 0.75, 8.3),
|
||||
(9.0, 1.0, 8.0),
|
||||
(9.0, 1.25, 7.8),
|
||||
(10.0, 0.5, 9.5),
|
||||
(10.0, 0.75, 9.3),
|
||||
(10.0, 1.0, 9.0),
|
||||
(10.0, 1.25, 8.8),
|
||||
(10.0, 1.5, 8.5),
|
||||
(11.0, 0.5, 10.5),
|
||||
(11.0, 0.75, 10.3),
|
||||
(11.0, 1.0, 10.0),
|
||||
(11.0, 1.5, 9.5),
|
||||
(12.0, 0.5, 11.5),
|
||||
(12.0, 1.0, 11.0),
|
||||
(12.0, 1.25, 10.8),
|
||||
(12.0, 1.5, 10.5),
|
||||
(12.0, 1.75, 10.3),
|
||||
(14.0, 1.0, 13.0),
|
||||
(14.0, 1.5, 12.5),
|
||||
(14.0, 2.0, 12.0),
|
||||
(15.0, 1.0, 14.0),
|
||||
(15.0, 1.5, 13.5),
|
||||
(16.0, 1.0, 15.0),
|
||||
(16.0, 1.5, 14.5),
|
||||
(16.0, 2.0, 14.0),
|
||||
(17.0, 1.0, 16.0),
|
||||
(17.0, 1.5, 15.5),
|
||||
(18.0, 1.0, 17.0),
|
||||
(18.0, 1.5, 16.5),
|
||||
(18.0, 2.0, 16.0),
|
||||
(18.0, 2.5, 15.5),
|
||||
(20.0, 1.0, 19.0),
|
||||
(20.0, 1.5, 18.5),
|
||||
(20.0, 2.0, 18.0),
|
||||
(20.0, 2.5, 17.5),
|
||||
(22.0, 1.0, 21.0),
|
||||
(22.0, 1.5, 20.5),
|
||||
(22.0, 2.0, 20.0),
|
||||
(22.0, 2.5, 19.5),
|
||||
(24.0, 1.0, 23.0),
|
||||
(24.0, 1.5, 22.5),
|
||||
(24.0, 2.0, 22.0),
|
||||
(24.0, 3.0, 21.0),
|
||||
(25.0, 1.0, 24.0),
|
||||
(25.0, 1.5, 23.5),
|
||||
(25.0, 2.0, 23.0),
|
||||
(26.0, 1.5, 24.5),
|
||||
(27.0, 1.0, 26.0),
|
||||
(27.0, 1.5, 25.5),
|
||||
(27.0, 2.0, 25.0),
|
||||
(27.0, 3.0, 24.0),
|
||||
(28.0, 1.0, 27.0),
|
||||
(28.0, 1.5, 26.5),
|
||||
(28.0, 2.0, 26.0),
|
||||
(30.0, 1.0, 29.0),
|
||||
(30.0, 1.5, 28.5),
|
||||
(30.0, 2.0, 28.0),
|
||||
(30.0, 3.0, 27.0),
|
||||
(30.0, 3.5, 26.5),
|
||||
(32.0, 1.5, 30.5),
|
||||
(32.0, 2.0, 30.0),
|
||||
(33.0, 1.5, 31.5),
|
||||
(33.0, 2.0, 31.0),
|
||||
(33.0, 3.0, 30.0),
|
||||
(33.0, 3.5, 29.5),
|
||||
(35.0, 1.5, 33.5),
|
||||
(36.0, 1.5, 34.5),
|
||||
(36.0, 2.0, 34.0),
|
||||
(36.0, 3.0, 33.0),
|
||||
(36.0, 4.0, 32.0),
|
||||
(38.0, 1.5, 36.5),
|
||||
(39.0, 1.5, 37.5),
|
||||
(39.0, 2.0, 37.0),
|
||||
(39.0, 3.0, 36.0),
|
||||
(39.0, 4.0, 35.0),
|
||||
(40.0, 1.5, 38.5),
|
||||
(40.0, 2.0, 38.0),
|
||||
(40.0, 3.0, 37.0),
|
||||
(42.0, 1.5, 40.5),
|
||||
(42.0, 2.0, 40.0),
|
||||
(42.0, 3.0, 39.0),
|
||||
(42.0, 4.0, 38.0),
|
||||
(42.0, 4.5, 37.5),
|
||||
(45.0, 1.5, 43.5),
|
||||
(45.0, 2.0, 43.0),
|
||||
(45.0, 3.0, 42.0),
|
||||
(45.0, 4.0, 41.0),
|
||||
(45.0, 4.5, 40.5),
|
||||
(48.0, 1.5, 46.5),
|
||||
(48.0, 2.0, 46.0),
|
||||
(48.0, 3.0, 45.0),
|
||||
(48.0, 4.0, 44.0),
|
||||
(48.0, 5.0, 43.0),
|
||||
(50.0, 1.5, 48.5),
|
||||
(50.0, 2.0, 48.0),
|
||||
(50.0, 3.0, 47.0),
|
||||
(52.0, 1.5, 50.5),
|
||||
(52.0, 2.0, 50.0),
|
||||
(52.0, 3.0, 49.0),
|
||||
(52.0, 4.0, 48.0),
|
||||
(52.0, 5.0, 47.0),
|
||||
(55.0, 1.5, 53.5),
|
||||
(55.0, 2.0, 53.0),
|
||||
(55.0, 3.0, 52.0),
|
||||
(55.0, 4.0, 51.0),
|
||||
(56.0, 1.5, 54.5),
|
||||
(56.0, 2.0, 54.0),
|
||||
(56.0, 3.0, 53.0),
|
||||
(56.0, 4.0, 52.0),
|
||||
(56.0, 5.5, 50.5),
|
||||
(58.0, 1.5, 56.5),
|
||||
(58.0, 2.0, 56.0),
|
||||
(58.0, 3.0, 55.0),
|
||||
(58.0, 4.0, 54.0),
|
||||
(60.0, 1.5, 58.5),
|
||||
(60.0, 2.0, 58.0),
|
||||
(60.0, 3.0, 57.0),
|
||||
(60.0, 4.0, 56.0),
|
||||
(60.0, 5.5, 54.5),
|
||||
(62.0, 1.5, 60.5),
|
||||
(62.0, 2.0, 60.0),
|
||||
(62.0, 3.0, 59.0),
|
||||
(62.0, 4.0, 58.0),
|
||||
(64.0, 1.5, 62.5),
|
||||
(64.0, 2.0, 62.0),
|
||||
(64.0, 3.0, 61.0),
|
||||
(64.0, 4.0, 60.0),
|
||||
(64.0, 6.0, 58.0),
|
||||
(65.0, 1.5, 63.5),
|
||||
(65.0, 2.0, 63.0),
|
||||
(65.0, 3.0, 62.0),
|
||||
(65.0, 4.0, 61.0),
|
||||
(68.0, 1.5, 66.5),
|
||||
(68.0, 2.0, 66.0),
|
||||
(68.0, 3.0, 65.0),
|
||||
(68.0, 4.0, 64.0),
|
||||
(68.0, 6.0, 62.0),
|
||||
(70.0, 2.0, 68.0),
|
||||
(70.0, 3.0, 67.0),
|
||||
(70.0, 4.0, 66.0),
|
||||
(70.0, 6.0, 64.0),
|
||||
(72.0, 1.5, 70.5),
|
||||
(72.0, 2.0, 70.0),
|
||||
(72.0, 3.0, 69.0),
|
||||
(72.0, 4.0, 68.0),
|
||||
(72.0, 6.0, 66.0),
|
||||
(75.0, 1.5, 73.5),
|
||||
(75.0, 2.0, 73.0),
|
||||
(75.0, 3.0, 72.0),
|
||||
(75.0, 4.0, 71.0),
|
||||
(76.0, 1.5, 74.5),
|
||||
(76.0, 2.0, 74.0),
|
||||
(76.0, 3.0, 73.0),
|
||||
(76.0, 4.0, 72.0),
|
||||
(76.0, 6.0, 70.0),
|
||||
(78.0, 2.0, 76.0),
|
||||
(80.0, 1.5, 78.5),
|
||||
(80.0, 2.0, 78.0),
|
||||
(80.0, 3.0, 77.0),
|
||||
(80.0, 4.0, 76.0),
|
||||
(80.0, 6.0, 74.0),
|
||||
(82.0, 2.0, 80.0),
|
||||
(85.0, 2.0, 83.0),
|
||||
(85.0, 3.0, 82.0),
|
||||
(85.0, 4.0, 81.0),
|
||||
(85.0, 6.0, 79.0),
|
||||
(90.0, 2.0, 88.0),
|
||||
(90.0, 3.0, 87.0),
|
||||
(90.0, 4.0, 86.0),
|
||||
(90.0, 6.0, 84.0),
|
||||
(95.0, 2.0, 93.0),
|
||||
(95.0, 3.0, 92.0),
|
||||
(95.0, 4.0, 91.0),
|
||||
(95.0, 6.0, 89.0),
|
||||
(100.0, 2.0, 98.0),
|
||||
(100.0, 3.0, 97.0),
|
||||
(100.0, 4.0, 96.0),
|
||||
(100.0, 6.0, 94.0),
|
||||
];
|
||||
|
||||
/// The drill of an ISO metric tapped hole: the tap maker's for a tabulated size, else (a custom
|
||||
/// size) the major diameter less the pitch, to the micrometre.
|
||||
pub fn metric_tap_drill(major_diameter: f64, pitch: f64) -> f64 {
|
||||
let same = |a: f64, b: f64| (a - b).abs() < 1e-9;
|
||||
METRIC_DRILLS
|
||||
.iter()
|
||||
.find(|(d, p, _)| same(*d, major_diameter) && same(*p, pitch))
|
||||
.map_or_else(|| ((major_diameter - pitch) * 1000.0).round() / 1000.0, |r| r.2)
|
||||
}
|
||||
|
||||
fn metric_size(major_diameter: f64, pitch: f64) -> TappedThread {
|
||||
TappedThread {
|
||||
family: TappedThreadFamily::Metric,
|
||||
designation: crate::thread_forms::ThreadForm::Metric.format(major_diameter, pitch, pitch),
|
||||
major_diameter,
|
||||
pitch,
|
||||
// Preserve the existing metric estimate and rounding used by solid.hole.
|
||||
tap_drill: ((major_diameter - pitch) * 1000.0).round() / 1000.0,
|
||||
tap_drill: metric_tap_drill(major_diameter, pitch),
|
||||
}
|
||||
}
|
||||
|
||||
/// How much larger than the tap maker's drill a tapped hole may be drilled: 5%.
|
||||
pub const DRILL_ALLOWANCE: f64 = 0.05;
|
||||
|
||||
impl TappedThread {
|
||||
/// Whether the drill is a tap maker's tabulated one (every catalogued size; not a custom
|
||||
/// metric size, which takes the major less the pitch).
|
||||
pub fn drill_is_tabulated(&self) -> bool {
|
||||
self.family != TappedThreadFamily::Metric
|
||||
|| METRIC_DRILLS.iter().any(|(d, p, _)| (d - self.major_diameter).abs() < 1e-9 && (p - self.pitch).abs() < 1e-9)
|
||||
}
|
||||
|
||||
/// The internal thread's standard minor diameter: ISO metric and Unified D1 = D - 1.082532 P
|
||||
/// (the basic profile, ISO 68-1 / ASME B1.1), G and Rp the basic minor of their table, Rc and
|
||||
/// NPT the minor where the thread ends ([`TaperDrill`]).
|
||||
pub fn minor_diameter(&self) -> f64 {
|
||||
match self.family {
|
||||
TappedThreadFamily::Metric | TappedThreadFamily::Unc | TappedThreadFamily::Unf => {
|
||||
self.major_diameter - 1.25 * 3f64.sqrt() / 2.0 * self.pitch
|
||||
}
|
||||
TappedThreadFamily::G | TappedThreadFamily::Rp => crate::pipe_thread_catalog::parse_pipe_thread(&self.designation)
|
||||
.map_or(self.major_diameter - 1.280654 * self.pitch, |t| t.size().minor),
|
||||
TappedThreadFamily::Rc | TappedThreadFamily::Npt => self.taper_drill().map_or(self.tap_drill, |d| d.exact),
|
||||
}
|
||||
}
|
||||
|
||||
/// The drills a tapped hole of this size accepts (mm): from the standard minor diameter (or
|
||||
/// the tap maker's drill, where that lies below it, as a taper or Rp drill does) up to
|
||||
/// [`DRILL_ALLOWANCE`] over the tap maker's drill.
|
||||
pub fn drill_range(&self) -> (f64, f64) {
|
||||
(self.minor_diameter().min(self.tap_drill), self.tap_drill * (1.0 + DRILL_ALLOWANCE))
|
||||
}
|
||||
|
||||
/// Whether a hole of `diameter` mm can be tapped to this size; the error names the range.
|
||||
pub fn check_drill(&self, diameter: f64) -> Result<(), String> {
|
||||
let (lo, hi) = self.drill_range();
|
||||
let (show, d) =
|
||||
(|v: f64| format!("{:.3}", (v * 1000.0).round() / 1000.0).trim_end_matches('0').trim_end_matches('.').to_string(), &self.designation);
|
||||
let why = if !(diameter.is_finite() && diameter >= lo - 1e-9) {
|
||||
"smaller than the thread's minor diameter: the tap cannot cut it"
|
||||
} else if diameter > hi + 1e-9 {
|
||||
"more than 5% over the tap maker's drill: too little of the thread would engage"
|
||||
} else {
|
||||
return Ok(());
|
||||
};
|
||||
Err(format!(
|
||||
"a {d} tapped hole takes a drill from {} to {} mm (the tap maker's drill {} mm): {} mm is {why}",
|
||||
show(lo),
|
||||
show(hi),
|
||||
show(self.tap_drill),
|
||||
show(diameter)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,6 +613,52 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// A tapped hole may be drilled from the standard minor diameter (or the tap maker's drill
|
||||
/// where it lies below, Rc and Rp) up to 5% over the tap maker's drill.
|
||||
#[test]
|
||||
fn a_tapped_hole_accepts_drills_from_its_minor_to_five_percent_over() {
|
||||
let m8 = parse_tapped_thread("M8").unwrap();
|
||||
let (lo, hi) = m8.drill_range();
|
||||
assert!((lo - (8.0 - 1.082532 * 1.25)).abs() < 1e-5 && (hi - 6.8 * 1.05).abs() < 1e-9, "{lo} {hi}");
|
||||
for ok in [6.8, 6.75, 6.648, 7.14] {
|
||||
assert!(m8.check_drill(ok).is_ok(), "{ok}");
|
||||
}
|
||||
assert!(m8.check_drill(6.5).unwrap_err().contains("the tap cannot cut"));
|
||||
let e = m8.check_drill(7.2).unwrap_err();
|
||||
assert!(e.contains("from 6.647 to 7.14 mm") && e.contains("too little of the thread"), "{e}");
|
||||
let rc = parse_tapped_thread("Rc 1/2").unwrap();
|
||||
assert_eq!(rc.drill_range().0, 17.6, "a taper's drill lies under its minor at the end");
|
||||
assert!(rc.check_drill(17.6).is_ok() && rc.check_drill(17.5).is_err());
|
||||
let rp = parse_tapped_thread("Rp 1/2").unwrap();
|
||||
assert_eq!(rp.drill_range().0, 18.5);
|
||||
for family in [
|
||||
TappedThreadFamily::Metric,
|
||||
TappedThreadFamily::Unc,
|
||||
TappedThreadFamily::Unf,
|
||||
TappedThreadFamily::G,
|
||||
TappedThreadFamily::Rp,
|
||||
TappedThreadFamily::Rc,
|
||||
TappedThreadFamily::Npt,
|
||||
] {
|
||||
for s in tapped_thread_sizes(family) {
|
||||
assert!(s.check_drill(s.tap_drill).is_ok(), "{}", s.designation);
|
||||
assert!(s.minor_diameter() < s.major_diameter && s.minor_diameter() > 0.5 * s.major_diameter, "{}", s.designation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The metric drills as Yamawa prints them: each between its thread's minimum (basic) minor
|
||||
/// diameter and the major.
|
||||
#[test]
|
||||
fn metric_drills_lie_between_the_minor_and_the_major() {
|
||||
for &(d, p, drill) in METRIC_DRILLS {
|
||||
let minor = d - 1.25 * 3f64.sqrt() / 2.0 * p;
|
||||
assert!(drill >= minor - 1.5e-3 && drill < d, "M{d}x{p}: {drill}");
|
||||
}
|
||||
assert_eq!(metric_tap_drill(8.0, 1.25), 6.8);
|
||||
assert_eq!(metric_tap_drill(8.5, 1.0), 7.5, "a custom size: the major less the pitch");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tapped_catalogue_has_unique_valid_sizes_and_round_trips() {
|
||||
let mut names = std::collections::HashSet::new();
|
||||
@@ -365,7 +705,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn tapped_catalogue_preserves_metric_custom_and_rejects_unverified_threads() {
|
||||
for (name, d, p, drill) in [("M8", 8.0, 1.25, 6.75), ("M8x1", 8.0, 1.0, 7.0), ("M8.5x1", 8.5, 1.0, 7.5)] {
|
||||
// The tap maker's drill where it tabulates the size (Yamawa p.704: M8 6.8, MF 8 x 1 7.0),
|
||||
// the major less the pitch for a custom one.
|
||||
for (name, d, p, drill) in [("M8", 8.0, 1.25, 6.8), ("M8x1", 8.0, 1.0, 7.0), ("M8.5x1", 8.5, 1.0, 7.5), ("M12", 12.0, 1.75, 10.3)] {
|
||||
let size = parse_tapped_thread(name).unwrap();
|
||||
assert_eq!((size.major_diameter, size.pitch, size.tap_drill), (d, p, drill));
|
||||
}
|
||||
|
||||
@@ -544,11 +544,11 @@ impl ThreadForm {
|
||||
}
|
||||
}
|
||||
|
||||
/// The minor diameter a hole for this size is drilled to: the tap drill (major − pitch) for
|
||||
/// metric, the internal thread's minor diameter for the others.
|
||||
/// The minor diameter a hole for this size is drilled to: the tap maker's drill for metric
|
||||
/// ([`crate::metric_tap_drill`]), the internal thread's minor diameter for the others.
|
||||
pub fn drill(self, d: f64, p: f64) -> f64 {
|
||||
match self {
|
||||
ThreadForm::Metric => d - p,
|
||||
ThreadForm::Metric => crate::metric_tap_drill(d, p),
|
||||
_ => 2.0 * self.groove(d, p, true).crest_r,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
.at("SOLID", "CREATE")
|
||||
.icon("hole")
|
||||
.key("H")
|
||||
.params("position: [x,y,z] on a face (numbers, or length expressions the hole follows) | sketch + points: [sketch point ids] (one hole each, perpendicular to the sketch, into the material on whichever side of the sketch plane it is) | sketch alone (every sketch point: standalone points and circle centres); flip?: bool (sketch points: drill to the other side); single?: bool (with one sketch point: a Single hole that follows the point, drilled into the face there); direction?: [x,y,z] (default: into the face, against its outward normal; one that leaves the face is mirrored into it; ignored with sketch points unless single); diameter | thread (a tapped hole: metric major minus pitch or tabulated UNC/UNF, G or Rp cut-tap drill, or the Rc/NPT tap maker's tabulated drill, Rc 1/16 to 4 and NPT 1/16 to 3) | clearance: \"M6\" with fit?: close|normal|loose (ISO 273); depth? (default through all) | to?: [x,y,z] (To Object: drill to that face or point); references?: [{edge: [x,y,z] on a straight edge of the face, distance: expr}] (at most two; the centre goes that far from each edge's line, in the face's plane, and follows the edges; with a numeric position only); type?: simple|drilled|counterbore|countersink; tip_angle?; cb_diameter?, cb_depth?; cs_diameter?, cs_angle?; thread?: \"M6\", \"1/4-20 UNC\", \"G 1/4\", \"Rp 1/4\", \"Rc 1/4\" or \"1/4-18 NPT\" (cosmetic unless modeled); hand?: right|left (the tapped thread's hand; default right; needs thread); thread_depth?: positive length independent of drill depth (default: full useful cylindrical wall; must fit that wall); modeled?: bool (needs thread: the tapped thread cut into the wall, the groove between its teeth, as solid.thread's modelled one; default false: listed only); ease_out?: expr|standard|short (modelled tapped threads: the inner end, towards the bottom, fades out over this length, ISO 3508's runout, standard 2.5 P, short 1.25 P; without it the groove stops square at thread_depth, or clear of a blind hole's bottom); targets?: [body] (the bodies to drill; holes at the points of a sketch on a body's face default to that body, found along the holes' direction on it alone; explicit targets or participants override, [\"*\"] every body)"),
|
||||
.params("position: [x,y,z] on a face (numbers, or length expressions the hole follows) | sketch + points: [sketch point ids] (one hole each, perpendicular to the sketch, into the material on whichever side of the sketch plane it is) | sketch alone (every sketch point: standalone points and circle centres); flip?: bool (sketch points: drill to the other side); single?: bool (with one sketch point: a Single hole that follows the point, drilled into the face there); direction?: [x,y,z] (default: into the face, against its outward normal; one that leaves the face is mirrored into it; ignored with sketch points unless single); diameter | thread (a tapped hole, drilled with the tap maker's tabulated drill: ISO metric (a custom metric size: major minus pitch), UNC/UNF, G, Rp, Rc 1/16 to 4, NPT 1/16 to 3; a `diameter` given with it chooses another drill, from the thread's minor diameter to 5% over the tap maker's) | clearance: \"M6\" with fit?: close|normal|loose (ISO 273); depth? (default through all) | to?: [x,y,z] (To Object: drill to that face or point); references?: [{edge: [x,y,z] on a straight edge of the face, distance: expr}] (at most two; the centre goes that far from each edge's line, in the face's plane, and follows the edges; with a numeric position only); type?: simple|drilled|counterbore|countersink; tip_angle?; cb_diameter?, cb_depth?; cs_diameter?, cs_angle?; thread?: \"M6\", \"1/4-20 UNC\", \"G 1/4\", \"Rp 1/4\", \"Rc 1/4\" or \"1/4-18 NPT\" (cosmetic unless modeled); hand?: right|left (the tapped thread's hand; default right; needs thread); thread_depth?: positive length independent of drill depth (default: full useful cylindrical wall; must fit that wall); modeled?: bool (needs thread: the tapped thread cut into the wall, the groove between its teeth, as solid.thread's modelled one; default false: listed only); ease_out?: expr|standard|short (modelled tapped threads: the inner end, towards the bottom, fades out over this length, ISO 3508's runout, standard 2.5 P, short 1.25 P; without it the groove stops square at thread_depth, or clear of a blind hole's bottom); targets?: [body] (the bodies to drill; holes at the points of a sketch on a body's face default to that body, found along the holes' direction on it alone; explicit targets or participants override, [\"*\"] every body)"),
|
||||
CommandSpec::new("solid.pipe", "Pipe", pipe)
|
||||
.at("SOLID", "CREATE")
|
||||
.icon("pipe")
|
||||
@@ -2550,6 +2550,14 @@ fn hole(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
(None, None, None) => return Err(bad(cmd, "needs `diameter`, `thread` (a tapped hole) or `clearance` (a bolt size)")),
|
||||
};
|
||||
check_expr(s, &diameter, Kind::Length, cmd, "diameter")?;
|
||||
// A tapped hole drilled to another size than the tap maker's: from the thread's minor
|
||||
// diameter to 5% over that drill.
|
||||
if let (Some(t), Some(_)) = (&thread, expr(p, "diameter"))
|
||||
&& let Some(size) = solidcraft_doc::parse_tapped_thread(t)
|
||||
{
|
||||
let given = s.doc.eval(&diameter, Kind::Length).map_err(|e| bad(cmd, format!("`diameter`: {e}")))?;
|
||||
size.check_drill(given).map_err(|e| bad(cmd, e))?;
|
||||
}
|
||||
let depth = expr(p, "depth");
|
||||
if let Some(d) = &depth {
|
||||
check_expr(s, d, Kind::Length, cmd, "depth")?;
|
||||
|
||||
@@ -386,7 +386,7 @@ fn a_hole_too_narrow_or_a_shaft_too_thick_says_which_and_what_fits() {
|
||||
let before = volume(&mut s, "Nut");
|
||||
let e = s.execute("solid.thread", &json!({"face": [3.0, 0, 2], "designation": "M8", "modeled": true})).err().map(|e| e.to_string());
|
||||
let e = e.unwrap_or_default();
|
||||
assert!(e.contains("this 6 mm hole is too narrow for a modelled M8 thread") && e.contains("drill 6.75 mm for M8"), "{e}");
|
||||
assert!(e.contains("this 6 mm hole is too narrow for a modelled M8 thread") && e.contains("drill 6.8 mm for M8"), "{e}");
|
||||
assert!(!e.contains("too far"), "{e}");
|
||||
assert!((volume(&mut s, "Nut") - before).abs() < 1e-9);
|
||||
|
||||
@@ -1256,4 +1256,14 @@ fn a_modelled_tapped_hole_stops_clear_of_its_bottom_or_fades_out_before_it() {
|
||||
assert!(err(json!({"position":[0,0,0],"diameter":5,"modeled":true})).contains("`modeled` needs `thread`"));
|
||||
assert!(err(json!({"position":[0,0,0],"thread":"M8","ease_out":"standard"})).contains("modelled thread"));
|
||||
assert!(err(json!({"position":[0,0,0],"thread":"Rc 5"})).contains("no tap drill is tabulated"));
|
||||
// Another drill than the tap maker's (M8: 6.8 mm): from the minor diameter to 5% over it.
|
||||
let drill = |d: f64| {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.box", json!({"length":30,"width":30,"height":20,"body_name":"Plate"}));
|
||||
s.execute("solid.hole", &json!({"position":[15,15,20],"thread":"M8","diameter":d})).err().map(|e| e.to_string()).unwrap_or_default()
|
||||
};
|
||||
let e = drill(7.5);
|
||||
assert!(e.contains("from 6.647 to 7.14 mm") && e.contains("too little of the thread"), "{e}");
|
||||
assert!(drill(6.5).contains("the tap cannot cut"));
|
||||
assert!(drill(7.1).is_empty() && drill(6.7).is_empty(), "inside the range");
|
||||
}
|
||||
|
||||
@@ -424,6 +424,9 @@ pub struct HoleOpts {
|
||||
pub modeled: bool,
|
||||
/// A modelled tapped thread's inner end, towards the bottom: square or a fade.
|
||||
pub ease_out: crate::dialogs_thread::Ease,
|
||||
/// A tapped hole's drill when it is not the tap maker's for the size (the command's
|
||||
/// `diameter`); none: the tap maker's.
|
||||
pub drill: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for HoleOpts {
|
||||
@@ -442,6 +445,7 @@ impl Default for HoleOpts {
|
||||
hand: solidcraft_engine::doc::thread_forms::ThreadHand::Right,
|
||||
modeled: false,
|
||||
ease_out: crate::dialogs_thread::Ease::default(),
|
||||
drill: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4478,6 +4482,8 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
let mut params = place;
|
||||
if !opts.tapped {
|
||||
params["diameter"] = json!(diameter);
|
||||
} else if let Some(drill) = opts.drill.as_deref().map(str::trim).filter(|d| !d.is_empty()) {
|
||||
params["diameter"] = json!(drill);
|
||||
}
|
||||
params["type"] = json!(ty);
|
||||
if to_object_on(&d.inputs) {
|
||||
@@ -5457,6 +5463,13 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
opts.hand = *hand;
|
||||
opts.modeled = *modeled;
|
||||
opts.ease_out = crate::dialogs_thread::Ease::of(ease_out);
|
||||
// A saved drill other than the tap maker's for the size stays the hole's
|
||||
// (an older design's d - P metric drill among them).
|
||||
let saved = s.doc.eval(diameter, ValueKind::Length).ok();
|
||||
let tabulated = solidcraft_engine::doc::parse_tapped_thread(th).map(|t| t.tap_drill);
|
||||
if saved.zip(tabulated).is_none_or(|(a, b)| (a - b).abs() > 1e-9) {
|
||||
opts.drill = Some(diameter.to_string());
|
||||
}
|
||||
}
|
||||
opts.thread_full_depth = thread_depth.is_none();
|
||||
if let Some(depth) = thread_depth {
|
||||
|
||||
@@ -566,7 +566,7 @@ pub(super) fn rows(app: &mut SolveApp, ctx: &egui::Context, d: &mut Dialog, ui:
|
||||
opts.tapped = tap == 1;
|
||||
});
|
||||
if opts.tapped {
|
||||
enter |= crate::dialogs_thread::hole_rows(ui, &app.session.doc, &mut opts.thread, &mut opts.custom_thread);
|
||||
enter |= crate::dialogs_thread::hole_rows(ui, &app.session.doc, &mut opts.thread, &mut opts.custom_thread, &mut opts.drill);
|
||||
crate::dialogs_thread::hole_hand_row(ui, &mut opts.hand);
|
||||
ui_kit::checkbox_grid(ui, &mut [("Full thread depth", &mut opts.thread_full_depth), ("Modeled", &mut opts.modeled)], 1);
|
||||
// A modelled thread's inner end, towards the bottom, as the thread dialog's.
|
||||
|
||||
@@ -71,7 +71,10 @@ fn bore_diameter(diameter: &str, opts: &HoleOpts, eval: &dyn Fn(&str, ValueKind)
|
||||
if opts.custom_thread.as_ref().is_some_and(|draft| draft.error.is_some()) {
|
||||
return None;
|
||||
}
|
||||
solidcraft_engine::doc::parse_tapped_thread(&opts.thread).map(|size| size.tap_drill)
|
||||
match opts.drill.as_deref().map(str::trim).filter(|d| !d.is_empty()) {
|
||||
Some(drill) => eval(drill, ValueKind::Length).filter(|v| v.is_finite() && *v > 0.0),
|
||||
None => solidcraft_engine::doc::parse_tapped_thread(&opts.thread).map(|size| size.tap_drill),
|
||||
}
|
||||
} else {
|
||||
eval(diameter, ValueKind::Length).filter(|v| v.is_finite() && *v > 0.0)
|
||||
}
|
||||
@@ -449,7 +452,7 @@ mod tests {
|
||||
fn tapped_section_uses_the_current_nominal_size_and_pitch() {
|
||||
let mut opts = HoleOpts { tapped: true, thread: "M8".into(), ..HoleOpts::default() };
|
||||
let stale_diameter = |_: &str, _: ValueKind| Some(4.0);
|
||||
assert_eq!(bore_diameter("4 mm", &opts, &stale_diameter), Some(6.75));
|
||||
assert_eq!(bore_diameter("4 mm", &opts, &stale_diameter), Some(6.8));
|
||||
opts.thread = "M8x1.2".into();
|
||||
assert_eq!(bore_diameter("4 mm", &opts, &stale_diameter), Some(6.8));
|
||||
for (designation, drill) in [("G 1/4", 11.75), ("Rp 1/4", 11.4), ("1/4-20 UNC", 5.1054)] {
|
||||
|
||||
@@ -68,7 +68,14 @@ impl HoleThreadDraft {
|
||||
|
||||
/// Tapped holes use a shared, finite catalogue; the engine derives the physical drill.
|
||||
/// Custom remains a dialog mode even when its current values match a catalogue size.
|
||||
pub(crate) fn hole_rows(ui: &mut egui::Ui, doc: &Document, designation: &mut String, custom: &mut Option<HoleThreadDraft>) -> bool {
|
||||
pub(crate) fn hole_rows(
|
||||
ui: &mut egui::Ui,
|
||||
doc: &Document,
|
||||
designation: &mut String,
|
||||
custom: &mut Option<HoleThreadDraft>,
|
||||
drill: &mut Option<String>,
|
||||
) -> bool {
|
||||
let before = designation.clone();
|
||||
let designation_id = ui.make_persistent_id("hole_thread_designation");
|
||||
let families = [
|
||||
TappedThreadFamily::Metric,
|
||||
@@ -153,25 +160,39 @@ pub(crate) fn hole_rows(ui: &mut egui::Ui, doc: &Document, designation: &mut Str
|
||||
if response.changed() && custom.is_some() {
|
||||
*custom = Some(HoleThreadDraft::new(designation));
|
||||
}
|
||||
// The drill: the tap maker's for the size, or one the user types, from the thread's minor
|
||||
// diameter to 5% over the tap maker's. A new size goes back to its own drill.
|
||||
if *designation != before {
|
||||
*drill = None;
|
||||
}
|
||||
let mut drill_enter = false;
|
||||
form_row(ui, "Tap drill diameter", |ui| {
|
||||
match parse_tapped_thread(designation).filter(|_| custom.as_ref().is_none_or(|draft| draft.error.is_none())) {
|
||||
// An Rc or NPT hole takes the tap maker's drill; the standard's minor diameter where
|
||||
// the thread ends is the tooltip.
|
||||
Some(size) => match size.taper_drill() {
|
||||
Some(d) => {
|
||||
let exact = format!("{:.3}", d.exact);
|
||||
ui.label(format!("{} mm", size.tap_drill)).on_hover_text(crate::i18n::format("thread.taper_drill_exact", &[("exact", &exact)]));
|
||||
Some(size) => {
|
||||
let default = format!("{} mm", size.tap_drill);
|
||||
let (lo, hi) = size.drill_range();
|
||||
let mm = |v: f64| format!("{:.3}", v);
|
||||
let args = [("drill", mm(size.tap_drill)), ("minor", mm(size.minor_diameter())), ("low", mm(lo)), ("high", mm(hi))];
|
||||
let args: Vec<(&str, &str)> = args.iter().map(|(k, v)| (*k, v.as_str())).collect();
|
||||
let id = match (size.taper_drill().is_some(), size.drill_is_tabulated()) {
|
||||
(true, _) => "thread.drill_range_taper",
|
||||
(false, true) => "thread.drill_range",
|
||||
(false, false) => "thread.drill_range_custom",
|
||||
};
|
||||
let tip = crate::i18n::format(id, &args);
|
||||
let mut text = drill.clone().unwrap_or_else(|| default.clone());
|
||||
let (submitted, changed) = crate::ui_kit::text_field_tip(ui, &mut text, &default, &tip);
|
||||
if changed {
|
||||
*drill = (text.trim() != default && !text.trim().is_empty()).then_some(text);
|
||||
}
|
||||
None => {
|
||||
ui.label(format!("{} mm", size.tap_drill));
|
||||
}
|
||||
},
|
||||
drill_enter = submitted;
|
||||
}
|
||||
None => {
|
||||
ui.label(tr("thread.select_size"));
|
||||
}
|
||||
}
|
||||
});
|
||||
enter || (response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)))
|
||||
enter || drill_enter || (response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)))
|
||||
}
|
||||
|
||||
/// The forms offered. The square thread is left out: no standard defines it, and the command
|
||||
@@ -821,6 +842,53 @@ mod contribution_tests {
|
||||
assert_eq!((&again["modeled"], &again["ease_out"]), (&json!(true), &json!("standard")), "{again}");
|
||||
}
|
||||
|
||||
/// A tapped hole's drill: the tap maker's for the size (M8: 6.8 mm), or a typed one from the
|
||||
/// thread's minor diameter to 5% over it; outside that the command names the range. A new
|
||||
/// size takes its own drill again, and a saved hole drilled otherwise (an older design's
|
||||
/// d - P) reopens with its drill.
|
||||
#[test]
|
||||
fn a_tapped_holes_drill_is_the_tap_makers_or_one_within_its_range() {
|
||||
let (app, dialog) = hole();
|
||||
let mut h = crate::scenario::Harness::new();
|
||||
h.app = app;
|
||||
h.app.dialog = Some(dialog);
|
||||
h.frames(3);
|
||||
for handle in ["dialog:Hole Tap Type", "dialog:Hole Tap Type:Tapped"] {
|
||||
h.step(&json!({"click":{"handle":handle}})).unwrap();
|
||||
}
|
||||
let set = |h: &mut crate::scenario::Harness, field: &str, text: &str| {
|
||||
h.step(&json!({"click":{"handle":format!("dialog:{field}")}})).unwrap();
|
||||
h.step(&json!({"key":"A","cmd":true})).unwrap();
|
||||
h.step(&json!({"text":text})).unwrap();
|
||||
h.frames(2);
|
||||
};
|
||||
set(&mut h, "Designation", "M8");
|
||||
let params = |h: &crate::scenario::Harness| apply_commands(&h.app, h.app.dialog.as_ref().unwrap()).unwrap().remove(0).1;
|
||||
assert!(params(&h).get("diameter").is_none(), "the tap maker's drill is the command's own");
|
||||
set(&mut h, "Tap drill diameter", "7 mm");
|
||||
assert_eq!(params(&h)["diameter"], json!("7 mm"));
|
||||
let (cmd, p) = apply_commands(&h.app, h.app.dialog.as_ref().unwrap()).unwrap().remove(0);
|
||||
let id = h.app.run(&cmd, p.clone()).unwrap()["feature"].as_u64().unwrap();
|
||||
set(&mut h, "Tap drill diameter", "7.5 mm");
|
||||
let (cmd, p) = apply_commands(&h.app, h.app.dialog.as_ref().unwrap()).unwrap().remove(0);
|
||||
let e = h.app.session.execute(&cmd, &p).unwrap_err().to_string();
|
||||
assert!(e.contains("from 6.647 to 7.14 mm") && e.contains("too little of the thread"), "{e}");
|
||||
set(&mut h, "Designation", "M10");
|
||||
assert!(params(&h).get("diameter").is_none(), "a new size takes its own drill");
|
||||
|
||||
// An older design's M8 hole, drilled 6.75 mm (d - P), keeps that drill when edited.
|
||||
let solidcraft_engine::doc::FeatureKind::Hole { diameter, .. } = &mut h.app.session.doc_mut().feature_mut(id).unwrap().kind else {
|
||||
panic!("not Hole")
|
||||
};
|
||||
*diameter = "6.75 mm".into();
|
||||
h.app.dialog = None;
|
||||
h.app.edit_feature(id);
|
||||
h.frames(2);
|
||||
let (_, again) = apply_commands(&h.app, h.app.dialog.as_ref().unwrap()).unwrap().remove(0);
|
||||
let again = if again.get("params").is_some() { again["params"].clone() } else { again };
|
||||
assert_eq!(again["diameter"], json!("6.75 mm"), "{again}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manual_designation_keeps_keyboard_focus_when_custom_fields_appear() {
|
||||
let (app, dialog) = hole();
|
||||
@@ -914,7 +982,7 @@ mod contribution_tests {
|
||||
let doc = Document::default();
|
||||
let _ = ctx.run_ui(Default::default(), |ui| {
|
||||
egui::Grid::new("hole_catalogue").show(ui, |ui| {
|
||||
hole_rows(ui, &doc, &mut designation, &mut custom);
|
||||
hole_rows(ui, &doc, &mut designation, &mut custom, &mut None);
|
||||
});
|
||||
});
|
||||
assert_eq!(designation, original);
|
||||
@@ -960,7 +1028,7 @@ mod contribution_tests {
|
||||
for (name, major, pitch, drill) in [
|
||||
("1/4-20 UNC", 6.35, 1.27, 5.1054),
|
||||
("#10-32 UNF", 4.826, 25.4 / 32.0, 4.0386),
|
||||
("M8", 8.0, 1.25, 6.75),
|
||||
("M8", 8.0, 1.25, 6.8),
|
||||
("M8x1", 8.0, 1.0, 7.0),
|
||||
("M8.5x1", 8.5, 1.0, 7.5),
|
||||
] {
|
||||
@@ -1003,12 +1071,12 @@ mod contribution_tests {
|
||||
let before = app.session.doc.to_json();
|
||||
for designation in ["1/4-20 UNC", "G 1/4", "Rp 1/4"] {
|
||||
for modeled in [false, true] {
|
||||
assert!(app.run("solid.thread", json!({"face":[13.375,10,5],"designation":designation,"modeled":modeled})).is_err());
|
||||
assert!(app.run("solid.thread", json!({"face":[13.4,10,5],"designation":designation,"modeled":modeled})).is_err());
|
||||
assert_eq!(app.session.doc.to_json(), before);
|
||||
}
|
||||
}
|
||||
assert_eq!(app.session.doc.to_json(), before);
|
||||
let out = app.run("solid.thread", json!({"face":[13.375,10,5],"designation":"M8"})).unwrap();
|
||||
let out = app.run("solid.thread", json!({"face":[13.4,10,5],"designation":"M8"})).unwrap();
|
||||
let solidcraft_engine::doc::FeatureKind::Thread { designation, modeled, .. } =
|
||||
&mut app.session.doc_mut().feature_mut(out["feature"].as_u64().unwrap()).unwrap().kind
|
||||
else {
|
||||
@@ -1063,7 +1131,7 @@ mod contribution_tests {
|
||||
let doc = Document::default();
|
||||
let _ = context.run_ui(Default::default(), |ui| {
|
||||
egui::Grid::new("authored_hole").show(ui, |ui| {
|
||||
hole_rows(ui, &doc, &mut hole, &mut custom);
|
||||
hole_rows(ui, &doc, &mut hole, &mut custom, &mut None);
|
||||
});
|
||||
});
|
||||
assert_eq!(hole, original);
|
||||
@@ -1072,7 +1140,7 @@ mod contribution_tests {
|
||||
|
||||
#[test]
|
||||
fn contribution_metric_designation_keeps_domain_pitch_drill_units_and_reopen() {
|
||||
for (diameter, pitch, drill) in [(8., 1.25, 6.75), (8., 1., 7.), (5., 0.5, 4.5)] {
|
||||
for (diameter, pitch, drill) in [(8., 1.25, 6.8), (8., 1., 7.), (5., 0.5, 4.5)] {
|
||||
let (mut app, mut dialog) = hole();
|
||||
app.run("document.units", json!({"units":"in"})).unwrap();
|
||||
if let Kind::Hole { opts, .. } = &mut dialog.kind {
|
||||
|
||||
@@ -46,13 +46,16 @@ struct ThreadOptions {
|
||||
/// runout, any other text a fade length.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
ease_out: Option<String>,
|
||||
/// A drill other than the tap maker's for the size; none: the tap maker's.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
drill: Option<String>,
|
||||
#[serde(flatten)]
|
||||
unknown: BTreeMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
impl Default for ThreadOptions {
|
||||
fn default() -> Self {
|
||||
Self { handedness: "right".into(), full_length: true, depth: None, modeled: false, ease_out: None, unknown: BTreeMap::new() }
|
||||
Self { handedness: "right".into(), full_length: true, depth: None, modeled: false, ease_out: None, drill: None, unknown: BTreeMap::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +124,7 @@ impl Preset {
|
||||
if required.into_iter().any(|text| text.trim().is_empty())
|
||||
|| self.thread_options.depth.as_ref().is_some_and(|text| !valid_text(text, MAX_EXPRESSION))
|
||||
|| self.thread_options.ease_out.as_ref().is_some_and(|text| text.trim().is_empty() || !valid_text(text, MAX_EXPRESSION))
|
||||
|| self.thread_options.drill.as_ref().is_some_and(|text| text.trim().is_empty() || !valid_text(text, MAX_EXPRESSION))
|
||||
|| (self.thread_options.ease_out.is_some() && !(self.tapped && self.thread_options.modeled))
|
||||
|| !["right", "left"].contains(&self.thread_options.handedness.as_str())
|
||||
|| self.thread_options.unknown.len() > 8
|
||||
@@ -210,6 +214,7 @@ impl Preset {
|
||||
Some(_) if opts.modeled => Some(portable_length(&opts.ease_out.length, true)?),
|
||||
_ => None,
|
||||
},
|
||||
drill: opts.drill.as_deref().filter(|d| !d.trim().is_empty()).map(|d| portable_length(d, true)).transpose()?,
|
||||
unknown: BTreeMap::new(),
|
||||
}
|
||||
} else {
|
||||
@@ -254,6 +259,10 @@ impl Preset {
|
||||
|| (self.extent != Extent::All && (self.angled_bottom || self.style == Style::Drilled) && !angle(&self.tip_angle))
|
||||
|| (self.tapped && solidcraft_engine::doc::parse_tapped_thread(&self.thread).is_none())
|
||||
|| self.thread_options.ease_out.as_deref().is_some_and(|text| !named_runout(text) && !length(text))
|
||||
|| self.thread_options.drill.as_deref().is_some_and(|text| {
|
||||
!solidcraft_engine::doc::parse_tapped_thread(&self.thread)
|
||||
.is_some_and(|t| doc.eval(text, expr::Kind::Length).is_ok_and(|v| t.check_drill(v).is_ok()))
|
||||
})
|
||||
|| (self.tapped && !self.thread_options.full_length && self.thread_options.depth.as_deref().is_none_or(|text| !length(text)))
|
||||
{
|
||||
return Err("hole_presets.invalid_parameters");
|
||||
@@ -295,6 +304,7 @@ impl Preset {
|
||||
};
|
||||
opts.thread_depth = self.thread_options.depth.clone().unwrap_or_else(|| crate::dialogs::HoleOpts::default().thread_depth);
|
||||
opts.modeled = self.thread_options.modeled;
|
||||
opts.drill.clone_from(&self.thread_options.drill);
|
||||
opts.ease_out = match self.thread_options.ease_out.as_deref() {
|
||||
None => crate::dialogs_thread::Ease::default(),
|
||||
Some(text) if named_runout(text) => crate::dialogs_thread::Ease { fade: true, short: text == "short", ..Default::default() },
|
||||
@@ -604,6 +614,8 @@ mod tests {
|
||||
opts.thread_depth = "8 mm".into();
|
||||
opts.modeled = modeled;
|
||||
opts.ease_out = ease.clone();
|
||||
// An Rc 1/2 drilled 17.8 mm, inside its 17.6 to 18.48 mm.
|
||||
opts.drill = (thread == "Rc 1/2").then(|| "17.8 mm".to_string());
|
||||
let captured = opts.clone();
|
||||
let preset = Preset::capture(&dialog, "Pipe", &doc).unwrap();
|
||||
let row = serde_json::to_value(&preset).unwrap();
|
||||
@@ -616,8 +628,8 @@ mod tests {
|
||||
read.recall(&mut fresh).unwrap();
|
||||
let Kind::Hole { opts, .. } = &fresh.kind else { panic!("expected Hole") };
|
||||
assert_eq!(
|
||||
(&opts.thread, opts.hand, opts.thread_full_depth, &opts.thread_depth, opts.modeled, &opts.ease_out),
|
||||
(&captured.thread, captured.hand, false, &captured.thread_depth, modeled, &ease),
|
||||
(&opts.thread, opts.hand, opts.thread_full_depth, &opts.thread_depth, opts.modeled, &opts.ease_out, &opts.drill),
|
||||
(&captured.thread, captured.hand, false, &captured.thread_depth, modeled, &ease, &captured.drill),
|
||||
"{thread}"
|
||||
);
|
||||
}
|
||||
@@ -629,5 +641,9 @@ mod tests {
|
||||
let mut preset = Preset::capture(&dialog, "Square", &doc).unwrap();
|
||||
preset.thread_options.ease_out = Some("standard".into());
|
||||
assert!(preset.validate().is_err());
|
||||
// A drill outside the size's range is no preset to recall.
|
||||
preset.thread_options.ease_out = None;
|
||||
preset.thread_options.drill = Some("7.5 mm".into());
|
||||
assert!(preset.validate().is_ok() && preset.validate_active(&doc).is_err(), "M8 takes 6.647 to 7.14 mm");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,13 @@ pub(crate) fn text_field(ui: &mut Ui, value: &mut String, hint: &str) -> (bool,
|
||||
(enter, changed)
|
||||
}
|
||||
|
||||
/// [`text_field`] with a tooltip; returns (Enter, changed). Tokens: `same as field`.
|
||||
pub(crate) fn text_field_tip(ui: &mut Ui, value: &mut String, hint: &str, tip: &str) -> (bool, bool) {
|
||||
let (enter, changed, response) = editor(ui, value, hint, false);
|
||||
response.on_hover_text(tip);
|
||||
(enter, changed)
|
||||
}
|
||||
|
||||
/// Expression field for a single-value popup which keeps ownership of keyboard focus.
|
||||
/// Tokens: field tokens. Preserves the legacy Edit Dimension focus behavior.
|
||||
pub(crate) fn focused_field(ui: &mut Ui, value: &mut String) -> bool {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
The Hole dialog offers ISO metric coarse sizes, 15 additional fine diameter/pitch
|
||||
pairs, a finite set of 14 UNC and 14 UNF sizes, six sizes each for internal
|
||||
G/BSPP and Rp parallel pipe threads, and the ISO 7-1 Rc and ASME B1.20.1 NPT
|
||||
internal taper threads (every size of their tables, `docs/threads.md`). This is a verified subset,
|
||||
G/BSPP and Rp parallel pipe threads, and the ISO 7-1 Rc (1/16 to 4) and ASME
|
||||
B1.20.1 NPT (1/16 to 3) internal taper threads. This is a verified subset,
|
||||
not complete coverage of these standards. Unified sizes range from #4 through
|
||||
#12 and 1/4 through 1 inch. Include the family in a designation, for example
|
||||
`1/4-20 UNC` or `#10-32 UNF`.
|
||||
@@ -21,11 +21,27 @@ depth, tolerances, drilling accuracy and the tap manufacturer's process guidance
|
||||
still determine the appropriate drill; the command's explicit `diameter` can
|
||||
override the recommendation.
|
||||
|
||||
Metric holes retain the existing major-minus-pitch estimate, rounded to 0.001 mm.
|
||||
This preserves existing command behavior, including M8's 6.75 mm default. It is
|
||||
not presented as a tabulated 75% cut-tap recommendation. Existing files retain
|
||||
their saved diameter expressions. Custom metric dimensions remain editable, and
|
||||
Custom stays selected even when its current dimensions match a catalogue entry.
|
||||
Metric holes take the tap maker's drill: Yamawa YEU23's drill size column for
|
||||
cutting taps, printed p.704 (M, coarse) and pp.704 to 706 (MF, fine); M8 drills
|
||||
6.8 mm, M12 10.3 mm. A custom size the table lacks takes the major diameter less
|
||||
the pitch, to 0.001 mm (M8.5x1: 7.5 mm). This replaces the major-minus-pitch
|
||||
estimate every metric size took before (M8 6.75 mm). A saved design keeps the
|
||||
diameter it stored, so it builds as before; editing such a hole keeps its drill.
|
||||
Custom metric dimensions remain editable, and Custom stays selected even when its
|
||||
current dimensions match a catalogue entry.
|
||||
|
||||
## Choosing another drill
|
||||
|
||||
The Hole dialog's Tap drill diameter is the tap maker's drill for the size and can
|
||||
be changed; the command's `diameter` does the same. A drill is accepted from the
|
||||
internal thread's standard minor diameter (ISO metric and Unified D1 = D −
|
||||
1.082532 P, G and Rp their basic minor, Rc and NPT the minor where the thread
|
||||
ends) up to 5% over the tap maker's drill. Where the tap maker's drill lies below
|
||||
that minor (Rp, Rc, NPT), the drill itself is the lower end. A smaller drill would
|
||||
leave the tap cutting inside the thread's minor diameter; a larger one leaves too
|
||||
little of the thread engaged. M8 accepts 6.647 to 7.14 mm. The field's tooltip
|
||||
gives the minor diameter and the range. A new size takes its own tap maker's drill
|
||||
again.
|
||||
|
||||
Tapped holes create the physical pilot bore and the thread, listed (cosmetic) or,
|
||||
with `modeled`, cut into the wall as the Thread command cuts a modelled thread:
|
||||
|
||||
@@ -4,4 +4,4 @@ Generated size choices use the shared metric catalogue. Its coarse pitch is omit
|
||||
|
||||
This changes generated UI text, not the resolved numerical pitch or geometry. Existing authored designation text, including explicit legacy coarse pitches, is retained when a dialog is merely displayed. Custom and fine designations remain editable. Catalogue dimensions are millimetres regardless of the design's display units.
|
||||
|
||||
For ISO metric tapped holes, the existing tap-drill estimate remains nominal diameter minus pitch: M8 gives 6.75 mm, while M8x1 gives 7 mm. Saving and reopening retains the resolved pitch. See the [tapped-hole catalogue](thread-catalogue.md) for the additional metric fine choices and finite UNC/UNF and parallel G/Rp catalogues with their tabulated pilot recommendations. The separate Thread command remains ISO metric only.
|
||||
For ISO metric tapped holes, the drill is the tap maker's for the size (M8 gives 6.8 mm, M8x1 7 mm), and nominal diameter minus pitch for a custom size the table lacks. Saving and reopening retains the resolved pitch. See the [tapped-hole catalogue](thread-catalogue.md) for the additional metric fine choices and finite UNC/UNF and parallel G/Rp catalogues with their tabulated pilot recommendations. The Thread command's forms are in [Threads](threads.md).
|
||||
|
||||
+2
-2
@@ -59,8 +59,8 @@ Handbook gives only historical proportions (depth and width P/2; the "modified s
|
||||
trapezoidal and Acme forms replaced it.
|
||||
|
||||
**Sizes.** Without a designation the thread takes the standard size that fits the face: a shaft
|
||||
its major diameter, a hole its drill (metric: the tap drill d − P; the other forms: the internal
|
||||
minor diameter). Metric uses the ISO 261/262 coarse pitches (M1.6 to M56); trapezoidal and metric
|
||||
its major diameter, a hole its drill (metric: the tap maker's drill, `docs/thread-catalogue.md`;
|
||||
the other forms: the internal minor diameter). Metric uses the ISO 261/262 coarse pitches (M1.6 to M56); trapezoidal and metric
|
||||
buttress the ISO 2902 middle pitch of each diameter (Tr8x1.5 to Tr130x14; buttress from S10x2),
|
||||
the series DIN 513-2 shares; Acme ASME B1.5's preferred sizes (1/4-16 to 5-2). Buttress inch has
|
||||
no size table here: its pitch is always given.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"sources": [],
|
||||
"what": "an M8 tapped blind hole 12 deep with a 118 deg drill point",
|
||||
"source": "analytic",
|
||||
"method": "M8 coarse pitch 1.25: tap drill (major - pitch) 6.75; removed pi r^2 12 to the shoulder + the point cone pi r^2 (r/tan59)/3",
|
||||
"method": "M8 coarse pitch 1.25: the tap maker's drill 6.8 (Yamawa YEU23 p.704); removed pi r^2 12 to the shoulder + the point cone pi r^2 (r/tan59)/3",
|
||||
"body_count": 1,
|
||||
"volume_mm3": 47546.394232462,
|
||||
"volume_mm3": 47539.467413298,
|
||||
"faces": 8
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
"sources": [],
|
||||
"what": "the flange with a modelled M30 thread on its hub, between the round and the chamfer, and M8 tapped holes in the disk",
|
||||
"source": "analytic",
|
||||
"method": "the hub's cylinder (r15) runs from the r3 round at its root (z 13) to the 1.5 chamfer at its top (z 28.5): the M30 groove (P 3.5) stops w = 1.632286 clear of the round, a shoulder (06/007's rule), and runs out through the chamfer, a free end (06/006's): it removes the stock less 2 pi int int r m(r) dr dz over z 13 + w..28.5 at r 15 and the chamfer (r 15 - (z - 28.5)); the bore r8 is inside the root r 13.1. The M8 tap drills (6.75) go through the 10 mm disk at r 28, 90 deg + k 120, clear of the bolt holes (30 deg apart): 3 pi 3.375^2 10. Bbox: the flange's",
|
||||
"method": "the hub's cylinder (r15) runs from the r3 round at its root (z 13) to the 1.5 chamfer at its top (z 28.5): the M30 groove (P 3.5) stops w = 1.632286 clear of the round, a shoulder (06/007's rule), and runs out through the chamfer, a free end (06/006's): it removes the stock less 2 pi int int r m(r) dr dz over z 13 + w..28.5 at r 15 and the chamfer (r 15 - (z - 28.5)); the bore r8 is inside the root r 13.1. The M8 tap drills (6.8, the tap maker's) go through the 10 mm disk at r 28, 90 deg + k 120, clear of the bolt holes (30 deg apart): 3 pi 3.4^2 10. Bbox: the flange's",
|
||||
"body_count": 1,
|
||||
"volume_mm3": 52978.060766654,
|
||||
"volume_mm3": 52962.097548983,
|
||||
"bbox": {
|
||||
"min": [
|
||||
-40,
|
||||
|
||||
@@ -710,14 +710,14 @@
|
||||
},
|
||||
"ladder/05-holes-and-taps/007-tapped-m8-blind": {
|
||||
"measured": {
|
||||
"area_mm2": 9060.431918590197,
|
||||
"area_mm2": 9062.405544647674,
|
||||
"body_count": 1,
|
||||
"edges": 17,
|
||||
"faces": 8,
|
||||
"vertices": 12,
|
||||
"volume_mm3": 47546.393985981646
|
||||
"volume_mm3": 47539.467162202374
|
||||
},
|
||||
"seconds": 0.044542724,
|
||||
"seconds": 0.057993293,
|
||||
"status": "pass"
|
||||
},
|
||||
"ladder/05-holes-and-taps/008-tapped-m5-into-curved-face": {
|
||||
@@ -1638,14 +1638,14 @@
|
||||
},
|
||||
"ladder/11-cumulative-parts/003-threaded-flange": {
|
||||
"measured": {
|
||||
"area_mm2": 17355.838402078,
|
||||
"area_mm2": 17357.35813902275,
|
||||
"body_count": 1,
|
||||
"edges": 104,
|
||||
"faces": 38,
|
||||
"vertices": 69,
|
||||
"volume_mm3": 52978.053115805815
|
||||
"volume_mm3": 52962.08974469333
|
||||
},
|
||||
"seconds": 1.821271078,
|
||||
"seconds": 2.199779541,
|
||||
"status": "pass"
|
||||
},
|
||||
"ladder/11-cumulative-parts/004-housing": {
|
||||
|
||||
@@ -84,9 +84,9 @@
|
||||
"ladder/05-holes-and-taps/009-hole-0.5-from-edge",
|
||||
"ladder/05-holes-and-taps/010-hole-tangent-to-end-face",
|
||||
"ladder/05-holes-and-taps/003-drilled-blind-118",
|
||||
"ladder/05-holes-and-taps/007-tapped-m8-blind",
|
||||
"ladder/05-holes-and-taps/002-simple-blind-flat",
|
||||
"ladder/05-holes-and-taps/004-counterbore",
|
||||
"ladder/05-holes-and-taps/007-tapped-m8-blind",
|
||||
"ladder/05-holes-and-taps/005-countersink",
|
||||
"ladder/05-holes-and-taps/011-crossing-holes-steinmetz",
|
||||
"ladder/05-holes-and-taps/012-crossing-tapped-holes-offset",
|
||||
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
],
|
||||
"what": "tapped holes (Hole with a thread size, no diameter) are drilled at the tap drill, not at the major diameter",
|
||||
"source": "analytic",
|
||||
"method": "Tapped hole rule: the drill is the major diameter minus the pitch (M3 0.5 -> 2.5, M5 0.8 -> 4.2, M8 1.25 -> 6.75, M8.5x1 -> 7.5, M12 1.75 -> 10.25). Plate 100x40x12 with five holes in one row, each a cylinder pi r^2 h: M3: drill 2.5 x 12; M5: drill 4.2 x 12; M8: drill 6.75 x 12; M8.5x1: drill 7.5 x 12; M12: drill 10.25 x 8. V = 48000 - sum = 46155.1547 mm3. Faces: the plate's 6, one wall per through hole (four), a wall and a flat floor for the blind M12: 6 + 4 + 2 = 12.",
|
||||
"method": "Tapped hole rule: the drill is the tap maker's for the size (Yamawa YEU23 p.704: M3 0.5 -> 2.5, M5 0.8 -> 4.2, M8 1.25 -> 6.8, M12 1.75 -> 10.3), the major diameter minus the pitch for a custom size (M8.5x1 -> 7.5). Plate 100x40x12 with five holes in one row, each a cylinder pi r^2 h: M3: drill 2.5 x 12; M5: drill 4.2 x 12; M8: drill 6.8 x 12; M8.5x1: drill 7.5 x 12; M12: drill 10.3 x 8. V = 48000 - sum = 46142.3134 mm3. Faces: the plate's 6, one wall per through hole (four), a wall and a flat floor for the blind M12: 6 + 4 + 2 = 12.",
|
||||
"outcome": "solid",
|
||||
"body_count": 1,
|
||||
"volume_mm3": 46155.1547,
|
||||
"volume_mm3": 46142.3134,
|
||||
"faces": 12,
|
||||
"bbox": {
|
||||
"min": [
|
||||
|
||||
Reference in New Issue
Block a user