Merge pull request #4089 from mlampert/feature/thread-milling
Path: Feature/thread milling
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
|
||||
81
src/Mod/Path/Data/Threads/imperial-internal.csv
Normal file
81
src/Mod/Path/Data/Threads/imperial-internal.csv
Normal 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
|
||||
|
82
src/Mod/Path/Data/Threads/metric-internal.csv
Normal file
82
src/Mod/Path/Data/Threads/metric-internal.csv
Normal 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
|
||||
|
4
src/Mod/Path/Data/Threads/sources.txt
Normal file
4
src/Mod/Path/Data/Threads/sources.txt
Normal 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
|
||||
@@ -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>
|
||||
|
||||
1616
src/Mod/Path/Gui/Resources/icons/Path_ThreadMilling.svg
Normal file
1616
src/Mod/Path/Gui/Resources/icons/Path_ThreadMilling.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 52 KiB |
245
src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui
Normal file
245
src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui
Normal 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>
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2017 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2016 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
|
||||
@@ -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
|
||||
|
||||
344
src/Mod/Path/PathScripts/PathThreadMilling.py
Normal file
344
src/Mod/Path/PathScripts/PathThreadMilling.py
Normal 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
|
||||
|
||||
211
src/Mod/Path/PathScripts/PathThreadMillingGui.py
Normal file
211
src/Mod/Path/PathScripts/PathThreadMillingGui.py
Normal 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")
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#***************************************************************************
|
||||
#* Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
|
||||
#* *
|
||||
|
||||
61
src/Mod/Path/PathTests/TestPathThreadMilling.py
Normal file
61
src/Mod/Path/PathTests/TestPathThreadMilling.py
Normal 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])
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
14
src/Mod/Path/Tools/Bit/5mm-thread-cutter.fctb
Normal file
14
src/Mod/Path/Tools/Bit/5mm-thread-cutter.fctb
Normal 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": {}
|
||||
}
|
||||
@@ -31,6 +31,10 @@
|
||||
{
|
||||
"nr": 8,
|
||||
"path": "probe.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 9,
|
||||
"path": "5mm-thread-cutter.fctb"
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
|
||||
BIN
src/Mod/Path/Tools/Shape/thread-mill.fcstd
Normal file
BIN
src/Mod/Path/Tools/Shape/thread-mill.fcstd
Normal file
Binary file not shown.
Reference in New Issue
Block a user