Merge pull request #4089 from mlampert/feature/thread-milling

Path: Feature/thread milling
This commit is contained in:
sliptonic
2020-12-05 13:16:53 -06:00
committed by GitHub
26 changed files with 2743 additions and 19 deletions

View File

@@ -111,6 +111,8 @@ SET(PathScripts_SRCS
PathScripts/PathSurface.py
PathScripts/PathSurfaceGui.py
PathScripts/PathSurfaceSupport.py
PathScripts/PathThreadMilling.py
PathScripts/PathThreadMillingGui.py
PathScripts/PathToolBit.py
PathScripts/PathToolBitCmd.py
PathScripts/PathToolBitEdit.py
@@ -176,6 +178,7 @@ SET(Tools_Shape_SRCS
Tools/Shape/endmill.fcstd
Tools/Shape/probe.fcstd
Tools/Shape/slittingsaw.fcstd
Tools/Shape/thread-mill.fcstd
Tools/Shape/v-bit.fcstd
)
@@ -195,6 +198,7 @@ SET(PathTests_SRCS
PathTests/TestPathPreferences.py
PathTests/TestPathSetupSheet.py
PathTests/TestPathStock.py
PathTests/TestPathThreadMilling.py
PathTests/TestPathTool.py
PathTests/TestPathToolBit.py
PathTests/TestPathToolController.py
@@ -223,6 +227,15 @@ SET(Path_Images
${PathImages_Tools}
)
SET(PathData_Threads
Data/Threads/metric-internal.csv
Data/Threads/imperial-internal.csv
)
SET(Path_Data
${PathData_Threads}
)
SET(all_files
${PathScripts_SRCS}
${PathScripts_post_SRCS}
@@ -230,6 +243,7 @@ SET(all_files
${Tools_Library_SRCS}
${Tools_Shape_SRCS}
${Path_Images}
${Path_Data}
)
ADD_CUSTOM_TARGET(PathScripts ALL
@@ -304,3 +318,10 @@ INSTALL(
Mod/Path/Images/Tools
)
INSTALL(
FILES
${PathData_Threads}
DESTINATION
Mod/Path/Data/Threads
)

View File

@@ -0,0 +1,81 @@
name,tpi,class,dMinorMin,dMinorMax,dPitchMin,dPitchMax,dMajorMin,dMajorMax
0-80,80,2B,0.0465,0.0514,0.0519,0.0542,0.06,0.0633
0-80,80,3B,0.0465,0.0514,0.0519,0.0536,0.06,0.0626
1-64,64,2B,0.0561,0.0623,0.0629,0.0655,0.073,0.0768
1-64,64,3B,0.0561,0.0623,0.0629,0.0648,0.073,0.076
1-72,72,2B,0.058,0.0635,0.064,0.0665,0.073,0.0766
1-72,72,3B,0.058,0.0635,0.064,0.0659,0.073,0.0759
2-56,56,2B,0.0667,0.0737,0.0744,0.0772,0.086,0.0901
2-56,56,3B,0.0667,0.0737,0.0744,0.0765,0.086,0.0893
2-64,64,2B,0.0691,0.0753,0.0759,0.0786,0.086,0.0899
2-64,64,3B,0.0691,0.0753,0.0759,0.0779,0.086,0.0891
3-48,48,2B,0.0764,0.0845,0.0855,0.0885,0.099,0.1035
3-48,48,3B,0.0764,0.0845,0.0855,0.0877,0.099,0.1026
3-56,56,2B,0.0797,0.0865,0.0874,0.0902,0.099,0.1032
3-56,56,3B,0.0797,0.0865,0.0874,0.0895,0.099,0.1024
4-40,40,2B,0.0849,0.0939,0.0958,0.0991,0.112,0.1170
4-40,40,3B,0.0849,0.0939,0.0958,0.0982,0.112,0.116
4-48,48,2B,0.0894,0.0968,0.0985,0.1016,0.112,0.1167
4-48,48,3B,0.0894,0.0968,0.0985,0.1008,0.112,0.1158
5-40,40,2B,0.0979,0.1062,0.1088,0.1121,0.125,0.1301
5-40,40,3B,0.0979,0.1062,0.1088,0.1113,0.125,0.1292
5-44,44,2B,0.1004,0.1079,0.1102,0.1134,0.125,0.1299
5-44,44,3B,0.1004,0.1079,0.1102,0.1126,0.125,0.129
6-32,32,2B,0.104,0.114,0.1177,0.1214,0.138,0.1438
6-32,32,3B,0.104,0.114,0.1177,0.1204,0.138,0.1426
6-40,40,2B,0.111,0.119,0.1218,0.1252,0.138,0.1433
6-40,40,3B,0.111,0.1186,0.1218,0.1243,0.138,0.1422
8-32,32,2B,0.13,0.139,0.1437,0.1475,0.164,0.1700
8-32,32,3B,0.13,0.1389,0.1437,0.1465,0.164,0.1689
8-36,36,2B,0.134,0.142,0.146,0.1496,0.164,0.1697
8-36,36,3B,0.134,0.1416,0.146,0.1487,0.164,0.1687
10-24,24,2B,0.145,0.156,0.1629,0.1672,0.19,0.197
10-24,24,3B,0.145,0.1555,0.1629,0.1661,0.19,0.1957
10-32,32,2B,0.156,0.164,0.1697,0.1736,0.19,0.1963
10-32,32,3B,0.156,0.1641,0.1697,0.1726,0.19,0.1952
1/4-20,20,2B,0.196,0.207,0.2175,0.2248,0.25,0.261
1/4-20,20,3B,0.196,0.207,0.2175,0.22,0.25,0.2554
1/4-28,28,2B,0.211,0.22,0.2268,0.2311,0.25,0.2573
1/4-28,28,3B,0.211,0.219,0.2268,0.23,0.25,0.2561
5/16-18,18,2B,0.252,0.265,0.2764,0.2817,0.3125,0.3217
5/16-18,18,3B,0.252,0.263,0.2764,0.2803,0.3125,0.3201
5/16-24,24,2B,0.267,0.277,0.2854,0.2902,0.3125,0.3209
5/16-24,24,3B,0.267,0.2754,0.2854,0.289,0.3125,0.3196
3/8-16,16,2B,0.307,0.321,0.3344,0.3401,0.375,0.3852
3/8-16,16,3B,0.307,0.3182,0.3344,0.3387,0.375,0.3836
3/8-24,24,2B,0.33,0.34,0.3479,0.3528,0.375,0.3841
3/8-24,24,3B,0.33,0.3372,0.3479,0.3516,0.375,0.3828
7/16-14,14,2B,0.36,0.376,0.3911,0.3972,0.4375,0.4488
7/16-14,14,3B,0.36,0.3717,0.3911,0.3957,0.4375,0.4471
7/16-20,20,2B,0.383,0.395,0.405,0.4104,0.4375,0.4478
7/16-20,20,3B,0.383,0.3916,0.405,0.4091,0.4375,0.4463
1/2-13,13,2B,0.417,0.434,0.45,0.4565,0.5,0.5123
1/2-13,13,3B,0.417,0.4284,0.45,0.4548,0.5,0.5104
1/2-20,20,2B,0.446,0.457,0.4675,0.4731,0.5,0.5110
1/2-20,20,3B,0.446,0.4537,0.4675,0.4717,0.5,0.5095
5/8-11,11,2B,0.527,0.546,0.566,0.5732,0.625,0.6393
5/8-11,11,3B,0.527,0.5391,0.566,0.5714,0.625,0.6373
5/8-18,18,2B,0.565,0.578,0.5889,0.5949,0.625,0.6377
5/8-18,18,3B,0.565,0.573,0.5889,0.5934,0.625,0.6361
3/4-10,10,2B,0.642,0.663,0.685,0.6927,0.75,0.7660
3/4-10,10,3B,0.642,0.6545,0.685,0.6907,0.75,0.7638
3/4-16,16,2B,0.682,0.696,0.7094,0.7159,0.75,0.7644
3/4-16,16,3B,0.682,0.6908,0.7094,0.7143,0.75,0.7627
7/8-9,9,2B,0.755,0.778,0.8028,0.811,0.875,0.8928
7/8-9,9,3B,0.755,0.7681,0.8028,0.8089,0.875,0.8905
7/8-14,14,2B,0.798,0.814,0.8286,0.8356,0.875,0.8912
7/8-14,14,3B,0.798,0.8068,0.8286,0.8339,0.875,0.8894
1-8,8,2B,0.865,0.89,0.9188,0.9276,1,1.0197
1-8,8,3B,0.865,0.8797,0.9188,0.9254,1,1.0173
1-12,12,2B,0.91,0.928,0.9459,0.9535,1,1.0181
1-12,12,3B,0.91,0.9198,0.9459,0.9516,1,1.0161
11/8-7,7,2B,0.97,0.998,1.0322,1.0416,1.125,1.1466
11/8-12,12,2B,1.035,1.053,1.0709,1.0787,1.125,1.1445
11/4-7,7,2B,1.095,1.123,1.1572,1.1668,1.25,1.273
11/4-12,12,2B,1.16,1.178,1.1959,1.2039,1.25,1.2709
13/8-6,6,2B,1.195,1.225,1.2667,1.2771,1.375,1.4002
13/8-12,12,2B,1.285,1.303,1.3209,1.3291,1.375,1.3974
11/2-6,6,2B,1.32,1.35,1.3917,1.4022,1.5,1.5264
11/2-12,12,2B,1.41,1.428,1.4459,1.4542,1.5,1.5237
13/4-5,5,2B,1.534,1.568,1.6201,1.6317,1.75,1.7802
2-6,6,2B,1.82,1.85,1.8917,1.9028,2,2.0319
1 name tpi class dMinorMin dMinorMax dPitchMin dPitchMax dMajorMin dMajorMax
2 0-80 80 2B 0.0465 0.0514 0.0519 0.0542 0.06 0.0633
3 0-80 80 3B 0.0465 0.0514 0.0519 0.0536 0.06 0.0626
4 1-64 64 2B 0.0561 0.0623 0.0629 0.0655 0.073 0.0768
5 1-64 64 3B 0.0561 0.0623 0.0629 0.0648 0.073 0.076
6 1-72 72 2B 0.058 0.0635 0.064 0.0665 0.073 0.0766
7 1-72 72 3B 0.058 0.0635 0.064 0.0659 0.073 0.0759
8 2-56 56 2B 0.0667 0.0737 0.0744 0.0772 0.086 0.0901
9 2-56 56 3B 0.0667 0.0737 0.0744 0.0765 0.086 0.0893
10 2-64 64 2B 0.0691 0.0753 0.0759 0.0786 0.086 0.0899
11 2-64 64 3B 0.0691 0.0753 0.0759 0.0779 0.086 0.0891
12 3-48 48 2B 0.0764 0.0845 0.0855 0.0885 0.099 0.1035
13 3-48 48 3B 0.0764 0.0845 0.0855 0.0877 0.099 0.1026
14 3-56 56 2B 0.0797 0.0865 0.0874 0.0902 0.099 0.1032
15 3-56 56 3B 0.0797 0.0865 0.0874 0.0895 0.099 0.1024
16 4-40 40 2B 0.0849 0.0939 0.0958 0.0991 0.112 0.1170
17 4-40 40 3B 0.0849 0.0939 0.0958 0.0982 0.112 0.116
18 4-48 48 2B 0.0894 0.0968 0.0985 0.1016 0.112 0.1167
19 4-48 48 3B 0.0894 0.0968 0.0985 0.1008 0.112 0.1158
20 5-40 40 2B 0.0979 0.1062 0.1088 0.1121 0.125 0.1301
21 5-40 40 3B 0.0979 0.1062 0.1088 0.1113 0.125 0.1292
22 5-44 44 2B 0.1004 0.1079 0.1102 0.1134 0.125 0.1299
23 5-44 44 3B 0.1004 0.1079 0.1102 0.1126 0.125 0.129
24 6-32 32 2B 0.104 0.114 0.1177 0.1214 0.138 0.1438
25 6-32 32 3B 0.104 0.114 0.1177 0.1204 0.138 0.1426
26 6-40 40 2B 0.111 0.119 0.1218 0.1252 0.138 0.1433
27 6-40 40 3B 0.111 0.1186 0.1218 0.1243 0.138 0.1422
28 8-32 32 2B 0.13 0.139 0.1437 0.1475 0.164 0.1700
29 8-32 32 3B 0.13 0.1389 0.1437 0.1465 0.164 0.1689
30 8-36 36 2B 0.134 0.142 0.146 0.1496 0.164 0.1697
31 8-36 36 3B 0.134 0.1416 0.146 0.1487 0.164 0.1687
32 10-24 24 2B 0.145 0.156 0.1629 0.1672 0.19 0.197
33 10-24 24 3B 0.145 0.1555 0.1629 0.1661 0.19 0.1957
34 10-32 32 2B 0.156 0.164 0.1697 0.1736 0.19 0.1963
35 10-32 32 3B 0.156 0.1641 0.1697 0.1726 0.19 0.1952
36 1/4-20 20 2B 0.196 0.207 0.2175 0.2248 0.25 0.261
37 1/4-20 20 3B 0.196 0.207 0.2175 0.22 0.25 0.2554
38 1/4-28 28 2B 0.211 0.22 0.2268 0.2311 0.25 0.2573
39 1/4-28 28 3B 0.211 0.219 0.2268 0.23 0.25 0.2561
40 5/16-18 18 2B 0.252 0.265 0.2764 0.2817 0.3125 0.3217
41 5/16-18 18 3B 0.252 0.263 0.2764 0.2803 0.3125 0.3201
42 5/16-24 24 2B 0.267 0.277 0.2854 0.2902 0.3125 0.3209
43 5/16-24 24 3B 0.267 0.2754 0.2854 0.289 0.3125 0.3196
44 3/8-16 16 2B 0.307 0.321 0.3344 0.3401 0.375 0.3852
45 3/8-16 16 3B 0.307 0.3182 0.3344 0.3387 0.375 0.3836
46 3/8-24 24 2B 0.33 0.34 0.3479 0.3528 0.375 0.3841
47 3/8-24 24 3B 0.33 0.3372 0.3479 0.3516 0.375 0.3828
48 7/16-14 14 2B 0.36 0.376 0.3911 0.3972 0.4375 0.4488
49 7/16-14 14 3B 0.36 0.3717 0.3911 0.3957 0.4375 0.4471
50 7/16-20 20 2B 0.383 0.395 0.405 0.4104 0.4375 0.4478
51 7/16-20 20 3B 0.383 0.3916 0.405 0.4091 0.4375 0.4463
52 1/2-13 13 2B 0.417 0.434 0.45 0.4565 0.5 0.5123
53 1/2-13 13 3B 0.417 0.4284 0.45 0.4548 0.5 0.5104
54 1/2-20 20 2B 0.446 0.457 0.4675 0.4731 0.5 0.5110
55 1/2-20 20 3B 0.446 0.4537 0.4675 0.4717 0.5 0.5095
56 5/8-11 11 2B 0.527 0.546 0.566 0.5732 0.625 0.6393
57 5/8-11 11 3B 0.527 0.5391 0.566 0.5714 0.625 0.6373
58 5/8-18 18 2B 0.565 0.578 0.5889 0.5949 0.625 0.6377
59 5/8-18 18 3B 0.565 0.573 0.5889 0.5934 0.625 0.6361
60 3/4-10 10 2B 0.642 0.663 0.685 0.6927 0.75 0.7660
61 3/4-10 10 3B 0.642 0.6545 0.685 0.6907 0.75 0.7638
62 3/4-16 16 2B 0.682 0.696 0.7094 0.7159 0.75 0.7644
63 3/4-16 16 3B 0.682 0.6908 0.7094 0.7143 0.75 0.7627
64 7/8-9 9 2B 0.755 0.778 0.8028 0.811 0.875 0.8928
65 7/8-9 9 3B 0.755 0.7681 0.8028 0.8089 0.875 0.8905
66 7/8-14 14 2B 0.798 0.814 0.8286 0.8356 0.875 0.8912
67 7/8-14 14 3B 0.798 0.8068 0.8286 0.8339 0.875 0.8894
68 1-8 8 2B 0.865 0.89 0.9188 0.9276 1 1.0197
69 1-8 8 3B 0.865 0.8797 0.9188 0.9254 1 1.0173
70 1-12 12 2B 0.91 0.928 0.9459 0.9535 1 1.0181
71 1-12 12 3B 0.91 0.9198 0.9459 0.9516 1 1.0161
72 11/8-7 7 2B 0.97 0.998 1.0322 1.0416 1.125 1.1466
73 11/8-12 12 2B 1.035 1.053 1.0709 1.0787 1.125 1.1445
74 11/4-7 7 2B 1.095 1.123 1.1572 1.1668 1.25 1.273
75 11/4-12 12 2B 1.16 1.178 1.1959 1.2039 1.25 1.2709
76 13/8-6 6 2B 1.195 1.225 1.2667 1.2771 1.375 1.4002
77 13/8-12 12 2B 1.285 1.303 1.3209 1.3291 1.375 1.3974
78 11/2-6 6 2B 1.32 1.35 1.3917 1.4022 1.5 1.5264
79 11/2-12 12 2B 1.41 1.428 1.4459 1.4542 1.5 1.5237
80 13/4-5 5 2B 1.534 1.568 1.6201 1.6317 1.75 1.7802
81 2-6 6 2B 1.82 1.85 1.8917 1.9028 2 2.0319

View File

@@ -0,0 +1,82 @@
name,pitch,tol,dMinorMin,dMinorMax,dPitchMin,dPitchMax,dMajorMin,dMajorMax
M1.6 x 0.35,0.35,6H,1.221,1.321,1.373,1.458,1.6,1.736
M2 x 0.4,0.4,6H,1.567,1.679,1.74,1.83,2,2.148
M2.5 x 0.45,0.45,6H,2.013,2.138,2.208,2.303,2.5,2.66
M3 x 0.5,0.5,6H,2.459,2.599,2.675,2.775,3,3.172
M3.5 x 0.6,0.6,6H,2.85,3.01,3.11,3.222,3.5,3.698
M4 x 0.7,0.7,6H,3.242,3.422,3.545,3.663,4,4.219
M5 x 0.8,0.8,6H,4.134,4.334,4.48,4.605,5,5.24
M6 x 1,1,6H,4.917,5.153,5.35,5.5,6,6.294
M8 x 1,1,6H,6.917,7.153,7.35,7.5,8,8.294
M8 x 1.25,1.25,6H,6.647,6.912,7.188,7.348,8,8.34
M10 x 0.75,0.75,6H,9.188,9.378,9.513,9.645,10,10.24
M10 x 1,1,6H,8.917,9.153,9.35,9.5,10,10.294
M10 x 1.25,1.25,6H,8.647,8.912,9.188,9.348,10,10.34
M10 x 1.5,1.5,6H,8.376,8.676,9.026,9.206,10,10.397
M12 x 1,1,6H,10.917,11.153,11.35,11.51,12,12.304
M12 x 1.25,1.25,6H,10.647,10.912,11.188,11.368,12,12.36
M12 x 1.5,1.5,6H,10.376,10.676,11.026,11.216,12,12.407
M12 x 1.75,1.75,6H,10.106,10.441,10.863,11.063,12,12.452
M14 x 1.5,1.5,6H,12.376,12.676,13.026,13.216,14,14.407
M14 x 2,2,6H,11.835,12.21,12.701,12.913,14,14.501
M15 x 1,1,6H,13.917,14.153,14.35,14.51,15,15.304
M16 x 1.5,1.5,6H,14.376,14.676,15.026,15.216,16,16.407
M16 x 2,2,6H,13.835,14.21,14.701,14.913,16,16.501
M17 x 1,1,6H,15.917,16.153,16.35,16.51,17,17.304
M18 x 1.5 ,1.5,6H,16.376,16.676,17.026,17.216,18,18.407
M20 x 1,1,6H,18.917,19.153,19.35,19.51,20,20.304
M20 x 1.5,1.5,6H,18.376,18.676,19.026,19.216,20,20.407
M20 x 2.5,2.5,6H,17.294,17.744,18.376,18.6,20,20.585
M22 x 1.5,1.5,6H,20.376,20.676,21.026,21.216,22,22.407
M22 x 2.5,2.5,6H,19.294,19.744,20.376,20.6,22,22.585
M24 x 2,2,6H,21.835,22.21,22.701,22.925,24,24.513
M24 x 3,3,6H,20.752,21.252,22.051,22.316,24,24.698
M25 x 1.5,1.5,6H,23.376,23.676,24.026,24.226,25,25.417
M27 x 2,2,6H,24.835,25.21,25.701,25.925,27,27.513
M27 x 3,3,6H,23.752,24.252,25.051,25.316,27,27.698
M30 x 1.5,1.5,6H,28.376,28.676,29.026,29.226,30,30.417
M30 x 2,2,6H,27.835,28.21,28.701,28.925,30,30.513
M30 x 3.5,3.5,6H,26.211,26.771,27.727,28.007,30,30.786
M33 x 2,2,6H,30.835,31.21,31.701,31.925,33,33.513
M35 x 1.5,1.5,6H,33.376,33.676,34.026,34.226,35,35.417
M36 x 2,2,6H,33.835,34.21,34.701,34.925,36,36.513
M36 x 4,4,6H,31.67,32.27,33.402,33.702,36,36.877
M39 x 2,2,6H,36.835,37.21,37.701,37.925,39,39.513
M40 x 1.5,1.5,6H,38.376,38.676,39.026,39.226,40,40.417
M42 x 2,2,6H,39.835,40.21,40.701,40.925,42,42.513
M42 x 4.5,4.5,6H,37.129,37.799,39.077,39.392,42,42.964
M45 x 1.5,1.5,6H,43.376,43.676,44.026,44.226,45,45.417
M48 x 2,2,6H,45.835,46.21,46.701,46.937,48,48.525
M48 x 5,5,6H,42.587,43.297,44.752,45.087,48,49.056
M50 x 1.5,1.5,6H,48.376,48.676,49.026,49.238,50,50.429
M55 x 1.5,1.5,6H,53.376,53.676,54.026,54.238,55,55.429
M56 x 2,2,6H,53.835,54.21,54.701,54.937,56,56.525
M56 x 5.5,5.5,6H,50.046,50.796,52.428,52.783,56,57.149
M60 x 1.5,1.5,6H,58.376,58.676,59.026,59.238,60,60.429
M64 x 2,2,6H,61.835,62.21,62.701,62.937,64,64.525
M64 x 6,6,6H,57.505,58.305,60.103,60.478,64,65.241
M65 x 1.5,1.5,6H,63.376,63.676,64.026,64.238,65,65.429
M70 x 1.5,1.5,6H,68.376,68.676,69.026,69.238,70,70.429
M72 x 2,2,6H,69.835,70.21,70.701,70.937,72,72.525
M72 x 6,6,6H,65.505,66.305,68.103,68.478,72,73.241
M75 x 1.5,1.5,6H,73.376,73.676,74.026,74.238,75,75.429
M80 x 1.5,1.5,6H,78.376,78.676,79.026,79.238,80,80.429
M80 x 2,2,6H,77.835,78.21,78.701,78.937,80,80.525
M80 x 6,6,6H,73.505,74.305,76.103,76.478,80,81.241
M85 x 2,2,6H,82.835,83.21,83.701,83.937,85,85.525
M90 x 2,2,6H,87.835,88.21,88.701,88.937,90,90.525
M90 x 6,6,6H,83.505,84.305,86.103,86.478,90,91.241
M95 x 2,2,6H,92.835,93.21,93.701,93.951,95,95.539
M100 x 2,2,6H,97.835,98.21,98.701,98.951,100,100.539
M100 x 6,6,6H,93.505,94.305,96.103,96.503,100,101.266
M105 x 2,2,6H,102.835,103.21,103.701,103.951,105,105.539
M110 x 2,2,6H,107.835,108.21,108.701,108.951,110,110.539
M120 x 2,2,6H,117.835,118.21,118.701,118.951,120,120.539
M130 x 2,2,6H,127.835,128.21,128.701,128.951,130,130.539
M140 x 2,2,6H,137.835,138.21,138.701,138.951,140,140.539
M150 x 2,2,6H,147.835,148.21,148.701,148.951,150,150.539
M160 x 3,3,6H,156.752,157.252,158.051,158.351,160,160.733
M170 x 3,3,6H,166.752,167.252,168.051,168.351,170,170.733
M180 x 3,3,6H,176.752,177.252,178.051,178.351,180,180.733
M190 x 3,3,6H,186.752,187.252,188.051,188.386,190,190.768
M200 x 3,3,6H,196.752,197.252,198.051,198.386,200,200.768
1 name pitch tol dMinorMin dMinorMax dPitchMin dPitchMax dMajorMin dMajorMax
2 M1.6 x 0.35 0.35 6H 1.221 1.321 1.373 1.458 1.6 1.736
3 M2 x 0.4 0.4 6H 1.567 1.679 1.74 1.83 2 2.148
4 M2.5 x 0.45 0.45 6H 2.013 2.138 2.208 2.303 2.5 2.66
5 M3 x 0.5 0.5 6H 2.459 2.599 2.675 2.775 3 3.172
6 M3.5 x 0.6 0.6 6H 2.85 3.01 3.11 3.222 3.5 3.698
7 M4 x 0.7 0.7 6H 3.242 3.422 3.545 3.663 4 4.219
8 M5 x 0.8 0.8 6H 4.134 4.334 4.48 4.605 5 5.24
9 M6 x 1 1 6H 4.917 5.153 5.35 5.5 6 6.294
10 M8 x 1 1 6H 6.917 7.153 7.35 7.5 8 8.294
11 M8 x 1.25 1.25 6H 6.647 6.912 7.188 7.348 8 8.34
12 M10 x 0.75 0.75 6H 9.188 9.378 9.513 9.645 10 10.24
13 M10 x 1 1 6H 8.917 9.153 9.35 9.5 10 10.294
14 M10 x 1.25 1.25 6H 8.647 8.912 9.188 9.348 10 10.34
15 M10 x 1.5 1.5 6H 8.376 8.676 9.026 9.206 10 10.397
16 M12 x 1 1 6H 10.917 11.153 11.35 11.51 12 12.304
17 M12 x 1.25 1.25 6H 10.647 10.912 11.188 11.368 12 12.36
18 M12 x 1.5 1.5 6H 10.376 10.676 11.026 11.216 12 12.407
19 M12 x 1.75 1.75 6H 10.106 10.441 10.863 11.063 12 12.452
20 M14 x 1.5 1.5 6H 12.376 12.676 13.026 13.216 14 14.407
21 M14 x 2 2 6H 11.835 12.21 12.701 12.913 14 14.501
22 M15 x 1 1 6H 13.917 14.153 14.35 14.51 15 15.304
23 M16 x 1.5 1.5 6H 14.376 14.676 15.026 15.216 16 16.407
24 M16 x 2 2 6H 13.835 14.21 14.701 14.913 16 16.501
25 M17 x 1 1 6H 15.917 16.153 16.35 16.51 17 17.304
26 M18 x 1.5 1.5 6H 16.376 16.676 17.026 17.216 18 18.407
27 M20 x 1 1 6H 18.917 19.153 19.35 19.51 20 20.304
28 M20 x 1.5 1.5 6H 18.376 18.676 19.026 19.216 20 20.407
29 M20 x 2.5 2.5 6H 17.294 17.744 18.376 18.6 20 20.585
30 M22 x 1.5 1.5 6H 20.376 20.676 21.026 21.216 22 22.407
31 M22 x 2.5 2.5 6H 19.294 19.744 20.376 20.6 22 22.585
32 M24 x 2 2 6H 21.835 22.21 22.701 22.925 24 24.513
33 M24 x 3 3 6H 20.752 21.252 22.051 22.316 24 24.698
34 M25 x 1.5 1.5 6H 23.376 23.676 24.026 24.226 25 25.417
35 M27 x 2 2 6H 24.835 25.21 25.701 25.925 27 27.513
36 M27 x 3 3 6H 23.752 24.252 25.051 25.316 27 27.698
37 M30 x 1.5 1.5 6H 28.376 28.676 29.026 29.226 30 30.417
38 M30 x 2 2 6H 27.835 28.21 28.701 28.925 30 30.513
39 M30 x 3.5 3.5 6H 26.211 26.771 27.727 28.007 30 30.786
40 M33 x 2 2 6H 30.835 31.21 31.701 31.925 33 33.513
41 M35 x 1.5 1.5 6H 33.376 33.676 34.026 34.226 35 35.417
42 M36 x 2 2 6H 33.835 34.21 34.701 34.925 36 36.513
43 M36 x 4 4 6H 31.67 32.27 33.402 33.702 36 36.877
44 M39 x 2 2 6H 36.835 37.21 37.701 37.925 39 39.513
45 M40 x 1.5 1.5 6H 38.376 38.676 39.026 39.226 40 40.417
46 M42 x 2 2 6H 39.835 40.21 40.701 40.925 42 42.513
47 M42 x 4.5 4.5 6H 37.129 37.799 39.077 39.392 42 42.964
48 M45 x 1.5 1.5 6H 43.376 43.676 44.026 44.226 45 45.417
49 M48 x 2 2 6H 45.835 46.21 46.701 46.937 48 48.525
50 M48 x 5 5 6H 42.587 43.297 44.752 45.087 48 49.056
51 M50 x 1.5 1.5 6H 48.376 48.676 49.026 49.238 50 50.429
52 M55 x 1.5 1.5 6H 53.376 53.676 54.026 54.238 55 55.429
53 M56 x 2 2 6H 53.835 54.21 54.701 54.937 56 56.525
54 M56 x 5.5 5.5 6H 50.046 50.796 52.428 52.783 56 57.149
55 M60 x 1.5 1.5 6H 58.376 58.676 59.026 59.238 60 60.429
56 M64 x 2 2 6H 61.835 62.21 62.701 62.937 64 64.525
57 M64 x 6 6 6H 57.505 58.305 60.103 60.478 64 65.241
58 M65 x 1.5 1.5 6H 63.376 63.676 64.026 64.238 65 65.429
59 M70 x 1.5 1.5 6H 68.376 68.676 69.026 69.238 70 70.429
60 M72 x 2 2 6H 69.835 70.21 70.701 70.937 72 72.525
61 M72 x 6 6 6H 65.505 66.305 68.103 68.478 72 73.241
62 M75 x 1.5 1.5 6H 73.376 73.676 74.026 74.238 75 75.429
63 M80 x 1.5 1.5 6H 78.376 78.676 79.026 79.238 80 80.429
64 M80 x 2 2 6H 77.835 78.21 78.701 78.937 80 80.525
65 M80 x 6 6 6H 73.505 74.305 76.103 76.478 80 81.241
66 M85 x 2 2 6H 82.835 83.21 83.701 83.937 85 85.525
67 M90 x 2 2 6H 87.835 88.21 88.701 88.937 90 90.525
68 M90 x 6 6 6H 83.505 84.305 86.103 86.478 90 91.241
69 M95 x 2 2 6H 92.835 93.21 93.701 93.951 95 95.539
70 M100 x 2 2 6H 97.835 98.21 98.701 98.951 100 100.539
71 M100 x 6 6 6H 93.505 94.305 96.103 96.503 100 101.266
72 M105 x 2 2 6H 102.835 103.21 103.701 103.951 105 105.539
73 M110 x 2 2 6H 107.835 108.21 108.701 108.951 110 110.539
74 M120 x 2 2 6H 117.835 118.21 118.701 118.951 120 120.539
75 M130 x 2 2 6H 127.835 128.21 128.701 128.951 130 130.539
76 M140 x 2 2 6H 137.835 138.21 138.701 138.951 140 140.539
77 M150 x 2 2 6H 147.835 148.21 148.701 148.951 150 150.539
78 M160 x 3 3 6H 156.752 157.252 158.051 158.351 160 160.733
79 M170 x 3 3 6H 166.752 167.252 168.051 168.351 170 170.733
80 M180 x 3 3 6H 176.752 177.252 178.051 178.351 180 180.733
81 M190 x 3 3 6H 186.752 187.252 188.051 188.386 190 190.768
82 M200 x 3 3 6H 196.752 197.252 198.051 198.386 200 200.768

View File

@@ -0,0 +1,4 @@
https://www.amesweb.info/Screws/Internal-Metric-Thread-Dimensions-Chart.aspx
https://www.engineersedge.com/thread_strength/internal_screw_threads_chart.htm
dMajorMax = dMajorMin * 1.01 * (dMinorMax - dMinorMin)
formula empirically derived from metric tolerances

View File

@@ -60,6 +60,7 @@
<file>icons/Path_Speed.svg</file>
<file>icons/Path_Stock.svg</file>
<file>icons/Path_Stop.svg</file>
<file>icons/Path_ThreadMilling.svg</file>
<file>icons/Path_ToolBit.svg</file>
<file>icons/Path_ToolChange.svg</file>
<file>icons/Path_ToolController.svg</file>
@@ -114,6 +115,7 @@
<file>panels/PageOpProfileFullEdit.ui</file>
<file>panels/PageOpSlotEdit.ui</file>
<file>panels/PageOpSurfaceEdit.ui</file>
<file>panels/PageOpThreadMillingEdit.ui</file>
<file>panels/PageOpWaterlineEdit.ui</file>
<file>panels/PageOpVcarveEdit.ui</file>
<file>panels/PathEdit.ui</file>

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,245 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>318</width>
<height>756</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Thread</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Orientation</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="threadOrientation">
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Left Hand</string>
</property>
</item>
<item>
<property name="text">
<string>Right Hand</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="threadType">
<item>
<property name="text">
<string>Custom</string>
</property>
</item>
<item>
<property name="text">
<string>Metric - internal</string>
</property>
</item>
<item>
<property name="text">
<string>SAE - internal</string>
</property>
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="threadName"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="threadFitLabel">
<property name="text">
<string>Fit</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSlider" name="threadFit">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Major Diameter</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="Gui::QuantitySpinBox" name="threadMajor">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Minor Diameter</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="Gui::QuantitySpinBox" name="threadMinor">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="threadPitchLabel">
<property name="text">
<string>Pitch</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="Gui::QuantitySpinBox" name="threadPitch">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QSpinBox" name="threadTPI">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="threadTPILabel">
<property name="text">
<string>TPI</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Tool Controller</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="toolController"/>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Operation</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Passes</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="opPasses">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Direction</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="opDirection">
<item>
<property name="text">
<string>Climb</string>
</property>
</item>
<item>
<property name="text">
<string>Conventional</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="leadInOut">
<property name="text">
<string>Lead In/Out</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::QuantitySpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>Gui/QuantitySpinBox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -99,6 +99,7 @@ class PathWorkbench (Workbench):
extracmdlist = []
# modcmdmore = ["Path_Hop",]
# remotecmdlist = ["Path_Remote"]
specialcmdlist = []
if PathPreferences.toolsReallyUseLegacyTools():
@@ -118,6 +119,7 @@ class PathWorkbench (Workbench):
projcmdlist.append("Path_Sanity")
prepcmdlist.append("Path_Shape")
extracmdlist.extend(["Path_Area", "Path_Area_Workplane"])
specialcmdlist.append('Path_Thread_Milling')
try:
import ocl # pylint: disable=unused-variable
@@ -145,6 +147,9 @@ class PathWorkbench (Workbench):
"Path", "Supplemental Commands")], prepcmdlist)
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path"), QtCore.QT_TRANSLATE_NOOP(
"Path", "Path Modification")], modcmdlist)
if specialcmdlist:
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path"), QtCore.QT_TRANSLATE_NOOP(
"Path", "Specialty Operations")], specialcmdlist)
if extracmdlist:
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path")], extracmdlist)

View File

@@ -765,6 +765,23 @@ class ObjectOp(PathOp.ObjectOp):
PathLog.debug(translate("Path", "Rotated to inverse angle."))
return (clnBase, clnStock, angle)
def calculateStartFinalDepths(self, obj, shape, stock):
'''calculateStartFinalDepths(obj, shape, stock)
Calculate correct start and final depths for the shape(face) object provided.'''
finDep = max(obj.FinalDepth.Value, shape.BoundBox.ZMin)
stockTop = stock.Shape.BoundBox.ZMax
if obj.EnableRotation == 'Off':
strDep = obj.StartDepth.Value
if strDep <= finDep:
strDep = stockTop
else:
strDep = min(obj.StartDepth.Value, stockTop)
if strDep <= finDep:
strDep = stockTop
msg = translate('Path', "Start depth <= face depth.\nIncreased to stock top.")
PathLog.error(msg)
return (strDep, finDep)
def sortTuplesByIndex(self, TupleList, tagIdx):
'''sortTuplesByIndex(TupleList, tagIdx)
sort list of tuples based on tag index provided

View File

@@ -73,6 +73,7 @@ def Startup():
from PathScripts import PathSlotGui
from PathScripts import PathStop
# from PathScripts import PathSurfaceGui # Added in initGui.py due to OCL dependency
from PathScripts import PathThreadMillingGui
from PathScripts import PathToolController
from PathScripts import PathToolControllerGui
from PathScripts import PathToolLibraryManager

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * Copyright (c) 2017 sliptonic <shopinthewoods@gmail.com> *
# * *

View File

@@ -324,7 +324,7 @@ class ObjectOp(object):
if 1 < len(job.Operations.Group):
obj.ToolController = PathUtil.toolControllerForOp(job.Operations.Group[-2])
else:
obj.ToolController = PathUtils.findToolController(obj)
obj.ToolController = PathUtils.findToolController(obj, self)
if not obj.ToolController:
return None
obj.OpToolDiameter = obj.ToolController.Tool.Diameter
@@ -585,3 +585,11 @@ class ObjectOp(object):
obj.Base = baselist
else:
PathLog.notice((translate("Path", "Base object %s.%s rejected by operation") + "\n") % (base.Label, sub))
def isToolSupported(self, obj, tool):
'''toolSupported(obj, tool) ... Returns true if the op supports the given tool.
This function can safely be overwritten by subclasses.'''
return True

View File

@@ -383,7 +383,7 @@ class TaskPanelPage(object):
combo.blockSignals(False)
if obj.ToolController is None:
obj.ToolController = PathUtils.findToolController(obj)
obj.ToolController = PathUtils.findToolController(obj, obj.Proxy)
if obj.ToolController is not None:
self.selectInComboBox(obj.ToolController.Label, combo)
@@ -391,7 +391,7 @@ class TaskPanelPage(object):
'''updateToolController(obj, combo) ...
helper function to update obj's ToolController property if a different
one has been selected in the combo box.'''
tc = PathUtils.findToolController(obj, combo.currentText())
tc = PathUtils.findToolController(obj, obj.Proxy, combo.currentText())
if obj.ToolController != tc:
obj.ToolController = tc

View File

@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * Copyright (c) 2016 sliptonic <shopinthewoods@gmail.com> *
# * *

View File

@@ -98,19 +98,16 @@ class ENGRAVEGate(PathBaseGate):
class CHAMFERGate(PathBaseGate):
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
def allow(self, doc, obj, sub): # pylint: disable=unused-argument
try:
shape = obj.Shape
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except
return False
if math.fabs(shape.Volume) < 1e-9 and len(shape.Wires) > 0:
return True
if shape.ShapeType == 'Edge':
return True
if (shape.ShapeType == 'Face' and shape.normalAt(0, 0) == FreeCAD.Vector(0, 0, 1)):
if 'Edge' == shape.ShapeType or 'Face' == shape.ShapeType:
return True
if sub:
@@ -385,6 +382,7 @@ def select(op):
opsel['Vcarve'] = vcarveselect
opsel['Probe'] = probeselect
opsel['Custom'] = customselect
opsel['Thread Milling'] = drillselect
opsel['TurnFace'] = turnselect
opsel['TurnProfile'] = turnselect
opsel['TurnPartoff'] = turnselect

View File

@@ -0,0 +1,344 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
from __future__ import print_function
import FreeCAD
import Path
import PathScripts.PathCircularHoleBase as PathCircularHoleBase
import PathScripts.PathGeom as PathGeom
import PathScripts.PathLog as PathLog
import PathScripts.PathOp as PathOp
import PathScripts.PathUtils as PathUtils
import math
from PySide import QtCore
__title__ = "Path Thread Milling Operation"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Path thread milling operation."
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
#PathLog.trackModule(PathLog.thisModule())
# Qt translation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
def radiiInternal(majorDia, minorDia, toolDia, toolCrest = None):
'''internlThreadRadius(majorDia, minorDia, toolDia, toolCrest) ... returns the maximum radius for thread.'''
PathLog.track(majorDia, minorDia, toolDia, toolCrest)
if toolCrest is None:
toolCrest = 0.0
# As it turns out metric and imperial standard threads follow the same rules.
# The determining factor is the height of the full 60 degree triangle H.
# - The minor diameter is 1/4 * H smaller than the pitch diameter.
# - The major diameter is 3/8 * H bigger than the pitch diameter
# Since we already have the outer diameter it's simpler to just add 1/8 * H
# to get the outer tip of the thread.
H = ((majorDia - minorDia) / 2.0 ) * 1.6 # (D - d)/2 = 5/8 * H
outerTip = majorDia / 2.0 + H / 8.0
# Compensate for the crest of the tool
toolTip = outerTip - toolCrest * 0.8660254037844386 # math.sqrt(3)/2 ... 60deg triangle height
return ((minorDia - toolDia) / 2.0, toolTip - toolDia / 2.0)
def threadPasses(count, radii, majorDia, minorDia, toolDia, toolCrest = None):
PathLog.track(count, radii, majorDia, minorDia, toolDia, toolCrest)
minor, major = radii(majorDia, minorDia, toolDia, toolCrest)
dr = float(major - minor) / count
return [major - dr * (count - (i + 1)) for i in range(count)]
class _InternalThread(object):
'''Helper class for dealing with different thread types'''
def __init__(self, cmd, zStart, zFinal, pitch):
self.cmd = cmd
if zStart < zFinal:
self.pitch = pitch
else:
self.pitch = -pitch
self.hPitch = self.pitch / 2
self.zStart = zStart
self.zFinal = zFinal
def overshoots(self, z):
'''overshoots(z) ... returns true if adding another half helix goes beyond the thread bounds'''
if self.pitch < 0:
return z + self.hPitch < self.zFinal
return z + self.hPitch > self.zFinal
def adjustX(self, x, dx):
'''adjustX(x, dx) ... move x by dx, the direction depends on the thread settings'''
if self.isG3() == (self.pitch > 0):
return x + dx
return x - dx
def adjustY(self, y, dy):
'''adjustY(y, dy) ... move y by dy, the direction depends on the thread settings'''
if self.isG3():
return y - dy
return y - dy
def isG3(self):
'''isG3() ... returns True if this is a G3 command'''
return self.cmd in ['G3', 'G03', 'g3', 'g03']
def isUp(self):
'''isUp() ... returns True if the thread goes from the bottom up'''
return self.pitch > 0
def internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius, leadInOut):
'''internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius) ... returns the g-code to mill the given internal thread'''
thread = _InternalThread(cmd, zStart, zFinal, pitch)
yMin = loc.y - radius
yMax = loc.y + radius
path = []
# at this point the tool is at a safe height (depending on the previous thread), so we can move
# into position first, and then drop to the start height. If there is any material in the way this
# op hasn't been setup properly.
path.append(Path.Command('G0', {'X': loc.x, 'Y': loc.y}))
path.append(Path.Command('G0', {'Z': thread.zStart}))
if leadInOut:
path.append(Path.Command(thread.cmd, {'Y': yMax, 'J': (yMax - loc.y) / 2}))
else:
path.append(Path.Command('G1', {'Y': yMax}))
z = thread.zStart
r = -radius
i = 0
while True:
z = thread.zStart + i*thread.hPitch
if thread.overshoots(z):
break
if 0 == (i & 0x01):
y = yMin
else:
y = yMax
path.append(Path.Command(thread.cmd, {'Y': y, 'Z': z + thread.hPitch, 'J': r}))
r = -r
i = i + 1
z = thread.zStart + i*thread.hPitch
if PathGeom.isRoughly(z, thread.zFinal):
x = loc.x
else:
n = math.fabs(thread.zFinal - thread.zStart) / thread.hPitch
k = n - int(n)
dy = math.cos(k * math.pi)
dx = math.sin(k * math.pi)
y = thread.adjustY(loc.y, r * dy)
x = thread.adjustX(loc.x, r * dx)
path.append(Path.Command(thread.cmd, {'X': x, 'Y': y, 'Z': thread.zFinal, 'J': r}))
if leadInOut:
path.append(Path.Command(thread.cmd, {'X': loc.x, 'Y': loc.y, 'I': (loc.x - x) / 2, 'J': (loc.y - y) / 2}))
else:
path.append(Path.Command('G1', {'X': loc.x, 'Y': loc.y}))
return path
class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
'''Proxy object for thread milling operation.'''
LeftHand = 'LeftHand'
RightHand = 'RightHand'
ThreadTypeCustom = 'Custom'
ThreadTypeMetricInternal = 'Metric - internal'
ThreadTypeImperialInternal = 'Imperial - internal'
DirectionClimb = 'Climb'
DirectionConventional = 'Conventional'
ThreadOrientations = [LeftHand, RightHand]
ThreadTypes = [ThreadTypeCustom, ThreadTypeMetricInternal, ThreadTypeImperialInternal]
Directions = [DirectionClimb, DirectionConventional]
def circularHoleFeatures(self, obj):
return PathOp.FeatureBaseGeometry
def initCircularHoleOperation(self, obj):
obj.addProperty("App::PropertyEnumeration", "ThreadOrientation", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread orientation"))
obj.ThreadOrientation = self.ThreadOrientations
obj.addProperty("App::PropertyEnumeration", "ThreadType", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Currently only internal"))
obj.ThreadType = self.ThreadTypes
obj.addProperty("App::PropertyString", "ThreadName", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Devfines which standard thread was chosen"))
obj.addProperty("App::PropertyLength", "MajorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's major diameter"))
obj.addProperty("App::PropertyLength", "MinorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's minor diameter"))
obj.addProperty("App::PropertyLength", "Pitch", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's pitch - used for metric threads"))
obj.addProperty("App::PropertyInteger", "TPI", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's tpi - used for imperial threads"))
obj.addProperty("App::PropertyInteger", "ThreadFit", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set how many passes are used to cut the thread"))
obj.addProperty("App::PropertyInteger", "Passes", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set how many passes are used to cut the thread"))
obj.addProperty("App::PropertyEnumeration", "Direction", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Direction of thread cutting operation"))
obj.addProperty("App::PropertyBool", "LeadInOut", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set to True to get lead in and lead out arcs at the start and end of the thread cut"))
obj.addProperty("App::PropertyLink", "ClearanceOp", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Operation to clear the inside of the thread"))
obj.Direction = self.Directions
# Rotation related properties
if not hasattr(obj, 'EnableRotation'):
obj.addProperty("App::PropertyEnumeration", "EnableRotation", "Rotation", QtCore.QT_TRANSLATE_NOOP("App::Property", "Enable rotation to gain access to pockets/areas not normal to Z axis."))
obj.EnableRotation = ['Off', 'A(x)', 'B(y)', 'A & B']
def threadStartDepth(self, obj):
if obj.ThreadOrientation == self.RightHand:
if obj.Direction == self.DirectionClimb:
PathLog.track(obj.Label, obj.FinalDepth)
return obj.FinalDepth
PathLog.track(obj.Label, obj.StartDepth)
return obj.StartDepth
if obj.Direction == self.DirectionClimb:
PathLog.track(obj.Label, obj.StartDepth)
return obj.StartDepth
PathLog.track(obj.Label, obj.FinalDepth)
return obj.FinalDepth
def threadFinalDepth(self, obj):
PathLog.track(obj.Label)
if obj.ThreadOrientation == self.RightHand:
if obj.Direction == self.DirectionClimb:
PathLog.track(obj.Label, obj.StartDepth)
return obj.StartDepth
PathLog.track(obj.Label, obj.FinalDepth)
return obj.FinalDepth
if obj.Direction == self.DirectionClimb:
PathLog.track(obj.Label, obj.FinalDepth)
return obj.FinalDepth
PathLog.track(obj.Label, obj.StartDepth)
return obj.StartDepth
def threadDirectionCmd(self, obj):
PathLog.track(obj.Label)
if obj.ThreadOrientation == self.RightHand:
if obj.Direction == self.DirectionClimb:
PathLog.track(obj.Label, 'G2')
return 'G2'
PathLog.track(obj.Label, 'G3')
return 'G3'
if obj.Direction == self.DirectionClimb:
PathLog.track(obj.Label, 'G3')
return 'G3'
PathLog.track(obj.Label, 'G2')
return 'G2'
def threadSetup(self, obj):
# the thing to remember is that Climb, for an internal thread must always be G3
if obj.Direction == self.DirectionClimb:
if obj.ThreadOrientation == self.RightHand:
return ('G3', obj.FinalDepth.Value, obj.StartDepth.Value)
return ('G3', obj.StartDepth.Value, obj.FinalDepth.Value)
if obj.ThreadOrientation == self.RightHand:
return ('G2', obj.StartDepth.Value, obj.FinalDepth.Value)
return ('G2', obj.FinalDepth.Value, obj.StartDepth.Value)
def threadPassRadii(self, obj):
PathLog.track(obj.Label)
rMajor = (obj.MajorDiameter.Value - self.tool.Diameter) / 2.0
rMinor = (obj.MinorDiameter.Value - self.tool.Diameter) / 2.0
if obj.Passes < 1:
obj.Passes = 1
rPass = (rMajor - rMinor) / obj.Passes
passes = [rMajor]
for i in range(1, obj.Passes):
passes.append(rMajor - rPass * i)
return list(reversed(passes))
def executeThreadMill(self, obj, loc, gcode, zStart, zFinal, pitch):
PathLog.track(obj.Label, loc, gcode, zStart, zFinal, pitch)
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
for radius in threadPasses(obj.Passes, radiiInternal, obj.MajorDiameter.Value, obj.MinorDiameter.Value, float(self.tool.Diameter), float(self.tool.Crest)):
commands = internalThreadCommands(loc, gcode, zStart, zFinal, pitch, radius, obj.LeadInOut)
for cmd in commands:
p = cmd.Parameters
if cmd.Name in ['G0']:
p.update({'F': self.vertRapid})
if cmd.Name in ['G1', 'G2', 'G3']:
p.update({'F': self.horizFeed})
cmd.Parameters = p
self.commandlist.extend(commands)
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
def circularHoleExecute(self, obj, holes):
PathLog.track()
if self.isToolSupported(obj, self.tool):
self.commandlist.append(Path.Command("(Begin Thread Milling)"))
(cmd, zStart, zFinal) = self.threadSetup(obj)
pitch = obj.Pitch.Value
if obj.TPI > 0:
pitch = 25.4 / obj.TPI
if pitch <= 0:
PathLog.error("Cannot create thread with pitch {}".format(pitch))
return
# rapid to clearance height
for loc in holes:
self.executeThreadMill(obj, FreeCAD.Vector(loc['x'], loc['y'], 0), cmd, zStart, zFinal, pitch)
else:
PathLog.error("No suitable Tool found for thread milling operation")
def opSetDefaultValues(self, obj, job):
obj.ThreadOrientation = self.RightHand
obj.ThreadType = self.ThreadTypeMetricInternal
obj.ThreadFit = 50
obj.Pitch = 1
obj.TPI = 0
obj.Passes = 1
obj.Direction = self.DirectionClimb
obj.LeadInOut = True
def isToolSupported(self, obj, tool):
'''Thread milling only supports thread milling cutters.'''
return hasattr(tool, 'Diameter') and hasattr(tool, 'Crest')
def SetupProperties():
setup = []
setup.append("ThreadOrientation")
setup.append("ThreadType")
setup.append("ThreadName")
setup.append("ThreadFit")
setup.append("MajorDiameter")
setup.append("MinorDiameter")
setup.append("Pitch")
setup.append("TPI")
setup.append("Passes")
setup.append("Direction")
setup.append("LeadInOut")
return setup
def Create(name, obj=None):
'''Create(name) ... Creates and returns a thread milling operation.'''
if obj is None:
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
obj.Proxy = ObjectThreadMilling(obj, name)
if obj.Proxy:
obj.Proxy.findAllHoles(obj)
return obj

View File

@@ -0,0 +1,211 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
import FreeCAD
import FreeCADGui
import PathScripts.PathCircularHoleBaseGui as PathCircularHoleBaseGui
import PathScripts.PathThreadMilling as PathThreadMilling
import PathScripts.PathGui as PathGui
import PathScripts.PathLog as PathLog
import PathScripts.PathOpGui as PathOpGui
import csv
from PySide import QtCore
__title__ = "Path Thread Milling Operation UI."
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "UI and Command for Path Thread Milling Operation."
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
#PathLog.trackModule(PathLog.thisModule())
def setupCombo(combo, selections):
combo.clear()
for item in selections:
combo.addItem(item)
def fillThreads(combo, dataFile):
combo.blockSignals(True)
combo.clear()
with open("{}Mod/Path/Data/Threads/{}.csv".format(FreeCAD.getHomePath(), dataFile)) as fp:
reader = csv.DictReader(fp)
for row in reader:
combo.addItem(row['name'], row)
combo.setEnabled(True)
combo.blockSignals(False)
class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
'''Controller for the thread milling operation's page'''
def initPage(self, obj):
self.majorDia = PathGui.QuantitySpinBox(self.form.threadMajor, obj, 'MajorDiameter') # pylint: disable=attribute-defined-outside-init
self.minorDia = PathGui.QuantitySpinBox(self.form.threadMinor, obj, 'MinorDiameter') # pylint: disable=attribute-defined-outside-init
self.pitch = PathGui.QuantitySpinBox(self.form.threadPitch, obj, 'Pitch') # pylint: disable=attribute-defined-outside-init
setupCombo(self.form.threadOrientation, obj.Proxy.ThreadOrientations)
setupCombo(self.form.threadType, obj.Proxy.ThreadTypes)
setupCombo(self.form.opDirection, obj.Proxy.Directions)
def getForm(self):
'''getForm() ... return UI'''
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpThreadMillingEdit.ui")
def getFields(self, obj):
'''getFields(obj) ... update obj's properties with values from the UI'''
PathLog.track()
self.majorDia.updateProperty()
self.minorDia.updateProperty()
self.pitch.updateProperty()
obj.ThreadOrientation = self.form.threadOrientation.currentText()
obj.ThreadType = self.form.threadType.currentText()
obj.ThreadName = self.form.threadName.currentText()
obj.Direction = self.form.opDirection.currentText()
obj.Passes = self.form.opPasses.value()
obj.LeadInOut = self.form.leadInOut.checkState() == QtCore.Qt.Checked
obj.TPI = self.form.threadTPI.value()
self.updateToolController(obj, self.form.toolController)
def setFields(self, obj):
'''setFields(obj) ... update UI with obj properties' values'''
PathLog.track()
self.form.threadOrientation.setCurrentText(obj.ThreadOrientation)
self.form.threadType.blockSignals(True)
self.form.threadName.blockSignals(True)
self.form.threadType.setCurrentText(obj.ThreadType)
self._updateFromThreadType()
self.form.threadName.setCurrentText(obj.ThreadName)
self.form.threadType.blockSignals(False)
self.form.threadName.blockSignals(False)
self.form.threadTPI.setValue(obj.TPI)
self.form.opPasses.setValue(obj.Passes)
self.form.opDirection.setCurrentText(obj.Direction)
self.form.leadInOut.setCheckState(QtCore.Qt.Checked if obj.LeadInOut else QtCore.Qt.Unchecked)
self.majorDia.updateSpinBox()
self.minorDia.updateSpinBox()
self.pitch.updateSpinBox()
self.setupToolController(obj, self.form.toolController)
def _isThreadMetric(self):
return self.form.threadType.currentText() == PathThreadMilling.ObjectThreadMilling.ThreadTypeMetricInternal
def _isThreadImperial(self):
return self.form.threadType.currentText() == PathThreadMilling.ObjectThreadMilling.ThreadTypeImperialInternal
def _updateFromThreadType(self):
if self.form.threadType.currentText() == PathThreadMilling.ObjectThreadMilling.ThreadTypeCustom:
self.form.threadName.setEnabled(False)
self.form.threadFit.setEnabled(False)
self.form.threadFitLabel.setEnabled(False)
self.form.threadPitch.setEnabled(True)
self.form.threadPitchLabel.setEnabled(True)
self.form.threadTPI.setEnabled(True)
self.form.threadTPILabel.setEnabled(True)
if self._isThreadMetric():
self.form.threadFit.setEnabled(True)
self.form.threadFitLabel.setEnabled(True)
self.form.threadPitch.setEnabled(True)
self.form.threadPitchLabel.setEnabled(True)
self.form.threadTPI.setEnabled(False)
self.form.threadTPILabel.setEnabled(False)
self.form.threadTPI.setValue(0)
fillThreads(self.form.threadName, 'metric-internal')
if self._isThreadImperial():
self.form.threadFit.setEnabled(True)
self.form.threadFitLabel.setEnabled(True)
self.form.threadPitch.setEnabled(False)
self.form.threadPitchLabel.setEnabled(False)
self.form.threadTPI.setEnabled(True)
self.form.threadTPILabel.setEnabled(True)
self.pitch.updateSpinBox(0)
fillThreads(self.form.threadName, 'imperial-internal')
def _updateFromThreadName(self):
thread = self.form.threadName.currentData()
fit = float(self.form.threadFit.value()) / 100
mamin = float(thread['dMajorMin'])
mamax = float(thread['dMajorMax'])
major = mamin + (mamax - mamin) * fit
mimin = float(thread['dMinorMin'])
mimax = float(thread['dMinorMax'])
minor = mimin + (mimax - mimin) * fit
if self._isThreadMetric():
pitch = float(thread['pitch'])
self.pitch.updateSpinBox(pitch)
if self._isThreadImperial():
tpi = int(thread['tpi'])
self.form.threadTPI.setValue(tpi)
minor = minor * 25.4
major = major * 25.4
self.majorDia.updateSpinBox(major)
self.minorDia.updateSpinBox(minor)
self.setDirty()
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals which cause the receiver to update the model'''
signals = []
signals.append(self.form.threadMajor.editingFinished)
signals.append(self.form.threadMinor.editingFinished)
signals.append(self.form.threadPitch.editingFinished)
signals.append(self.form.threadOrientation.currentIndexChanged)
signals.append(self.form.threadTPI.editingFinished)
signals.append(self.form.opDirection.currentIndexChanged)
signals.append(self.form.opPasses.editingFinished)
signals.append(self.form.leadInOut.stateChanged)
signals.append(self.form.toolController.currentIndexChanged)
return signals
def registerSignalHandlers(self, obj):
self.form.threadType.currentIndexChanged.connect(self._updateFromThreadType)
self.form.threadName.currentIndexChanged.connect(self._updateFromThreadName)
self.form.threadFit.valueChanged.connect(self._updateFromThreadName)
Command = PathOpGui.SetupOperation('Thread Milling',
PathThreadMilling.Create,
TaskPanelOpPage,
'Path-ThreadMilling',
QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Thread Milling"),
QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Creates a Path Thread Milling operation from features of a base object"),
PathThreadMilling.SetupProperties)
FreeCAD.Console.PrintLog("Loading PathThreadMillingGui ... done\n")

View File

@@ -322,11 +322,14 @@ class ToolBit(object):
path = findShape(obj.BitShape)
if path:
with open(path, 'rb') as fd:
zf = zipfile.ZipFile(fd)
pf = zf.open('thumbnails/Thumbnail.png', 'r')
data = pf.read()
pf.close()
return data
try:
zf = zipfile.ZipFile(fd)
pf = zf.open('thumbnails/Thumbnail.png', 'r')
data = pf.read()
pf.close()
return data
except KeyError:
pass
return None
def saveToFile(self, obj, path, setFile=True):

View File

@@ -392,19 +392,22 @@ def reverseEdge(e):
return newedge
def getToolControllers(obj):
def getToolControllers(obj, proxy=None):
'''returns all the tool controllers'''
if proxy is None:
proxy = obj.Proxy
try:
job = findParentJob(obj)
except Exception: # pylint: disable=broad-except
job = None
print("op={} ({})".format(obj.Label, type(obj)))
if job:
return job.ToolController
return [c for c in job.ToolController if proxy.isToolSupported(obj, c.Tool)]
return []
def findToolController(obj, name=None):
def findToolController(obj, proxy, name=None):
'''returns a tool controller with a given name.
If no name is specified, returns the first controller.
if no controller is found, returns None'''
@@ -416,7 +419,7 @@ def findToolController(obj, name=None):
if c is not None:
return c
controllers = getToolControllers(obj)
controllers = getToolControllers(obj, proxy)
if len(controllers) == 0:
return None

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
#***************************************************************************
#* Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
#* *

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
import PathScripts.PathGeom as PathGeom
import PathScripts.PathThreadMilling as PathThreadMilling
import math
from PathTests.PathTestUtils import PathTestBase
def radii(major, minor, toolDia, toolCrest):
'''test radii function for simple testing'''
return (minor, major)
class TestPathThreadMilling(PathTestBase):
'''Test thread milling basics.'''
def assertRadii(self, have, want):
self.assertRoughly(have[0], want[0])
self.assertRoughly(have[1], want[1])
def assertList(self, have, want):
self.assertEqual(len(have), len(want));
for i in range(len(have)):
self.assertRoughly(have[i], want[i])
def test00(self):
'''Verify internal radii.'''
self.assertRadii(PathThreadMilling.radiiInternal(20, 18, 2, 0), (8, 9.2))
self.assertRadii(PathThreadMilling.radiiInternal(20, 19, 2, 0), (8.5, 9.1))
def test01(self):
'''Verify internal radii with tool crest.'''
self.assertRadii(PathThreadMilling.radiiInternal(20, 18, 2, 0.1), (8, 9.113397))
def test10(self):
'''Verify thread passes.'''
self.assertList(PathThreadMilling.threadPasses(1, radii, 10, 9, 0, 0), [10])
self.assertList(PathThreadMilling.threadPasses(2, radii, 10, 9, 0, 0), [9.5, 10])
self.assertList(PathThreadMilling.threadPasses(5, radii, 10, 9, 0, 0), [9.2, 9.4, 9.6, 9.8, 10])

View File

@@ -41,6 +41,7 @@ from PathTests.TestPathSetupSheet import TestPathSetupSheet
from PathTests.TestPathDeburr import TestPathDeburr
from PathTests.TestPathHelix import TestPathHelix
from PathTests.TestPathVoronoi import TestPathVoronoi
from PathTests.TestPathThreadMilling import TestPathThreadMilling
# dummy usage to get flake8 and lgtm quiet
False if TestApp.__name__ else True
@@ -62,4 +63,5 @@ False if TestPathHelix.__name__ else True
False if TestPathPreferences.__name__ else True
False if TestPathToolBit.__name__ else True
False if TestPathVoronoi.__name__ else True
False if TestPathThreadMilling.__name__ else True

View File

@@ -0,0 +1,14 @@
{
"version": 2,
"name": "3mm-thread-cutter",
"shape": "thread-mill.fcstd",
"parameter": {
"Crest": "0.10 mm",
"Diameter": "5.00 mm",
"Length": "50.00 mm",
"NeckDiameter": "3.00 mm",
"NeckHeight": "20.00 mm",
"ShankDiameter": "5.00 mm"
},
"attribute": {}
}

View File

@@ -31,6 +31,10 @@
{
"nr": 8,
"path": "probe.fctb"
},
{
"nr": 9,
"path": "5mm-thread-cutter.fctb"
}
],
"version": 1

Binary file not shown.