From b29d168132c62fe669a6f7a76ec2c635ad85ec7b Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Mon, 12 Aug 2019 22:19:27 -0700 Subject: [PATCH 01/14] Add basic thread milling op --- src/Mod/Path/CMakeLists.txt | 17 + src/Mod/Path/Data/Threads/metric-external.csv | 130 ++ src/Mod/Path/Data/Threads/metric-internal.csv | 82 + src/Mod/Path/Gui/Resources/Path.qrc | 2 + .../Resources/icons/Path_ThreadMilling.svg | 1616 +++++++++++++++++ .../panels/PageOpThreadMillingEdit.ui | 242 +++ src/Mod/Path/InitGui.py | 4 + .../Path/PathScripts/PathCircularHoleBase.py | 17 + src/Mod/Path/PathScripts/PathGuiInit.py | 1 + src/Mod/Path/PathScripts/PathSelection.py | 10 +- src/Mod/Path/PathScripts/PathThreadMilling.py | 121 ++ .../Path/PathScripts/PathThreadMillingGui.py | 167 ++ 12 files changed, 2403 insertions(+), 6 deletions(-) create mode 100644 src/Mod/Path/Data/Threads/metric-external.csv create mode 100644 src/Mod/Path/Data/Threads/metric-internal.csv create mode 100644 src/Mod/Path/Gui/Resources/icons/Path_ThreadMilling.svg create mode 100644 src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui create mode 100644 src/Mod/Path/PathScripts/PathThreadMilling.py create mode 100644 src/Mod/Path/PathScripts/PathThreadMillingGui.py diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index 7046290c7b..9844e431b8 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -223,6 +223,15 @@ SET(Path_Images ${PathImages_Tools} ) +SET(PathData_Threads + Data/Threads/metric-external.csv + Data/Threads/metric-internal.csv +) + +SET(Path_Data + ${PathData_Threads} +) + SET(all_files ${PathScripts_SRCS} ${PathScripts_post_SRCS} @@ -230,6 +239,7 @@ SET(all_files ${Tools_Library_SRCS} ${Tools_Shape_SRCS} ${Path_Images} + ${Path_Data} ) ADD_CUSTOM_TARGET(PathScripts ALL @@ -304,3 +314,10 @@ INSTALL( Mod/Path/Images/Tools ) +INSTALL( + FILES + ${PathData_Threads} + DESTINATION + Mod/Path/Data/Threads +) + diff --git a/src/Mod/Path/Data/Threads/metric-external.csv b/src/Mod/Path/Data/Threads/metric-external.csv new file mode 100644 index 0000000000..24232aa7ed --- /dev/null +++ b/src/Mod/Path/Data/Threads/metric-external.csv @@ -0,0 +1,130 @@ +name,pitch,tol,dMinorMin,dMinorMax,dPitchMin,dPitchMax,dMajorMin,dMajorMax +M1.6 x 0.35,0.35,6g,1.581,1.496,1.354,1.291,1.202,1.075 +M1.6 x 0.35,0.35,6h,1.6,1.515,1.373,1.31,1.221,1.094 +M1.6 x 0.35,0.35,4g6g,1.581,1.496,1.354,1.314,1.202,1.098 +M2 x 0.4,0.4,6g,1.981,1.886,1.721,1.654,1.548,1.408 +M2 x 0.4,0.4,6h,2,1.905,1.74,1.673,1.567,1.427 +M2 x 0.4,0.4,4g6g,1.981,1.886,1.721,1.679,1.548,1.433 +M2.5 x 0.45,0.45,6g,2.48,2.38,2.188,2.117,1.993,1.84 +M2.5 x 0.45,0.45,6h,2.5,2.4,2.208,2.137,2.013,1.86 +M2.5 x 0.45,0.45,4g6g,2.48,2.38,2.188,2.143,1.993,1.866 +M3 x 0.5,0.5,6g,2.98,2.874,2.655,2.58,2.438,2.272 +M3 x 0.5,0.5,6h,3,2.894,2.675,2.6,2.458,2.292 +M3 x 0.5,0.5,4g6g,2.98,2.874,2.655,2.607,2.438,2.299 +M3.5 x 0.6,0.6,6g,3.479,3.354,3.089,3.004,2.829,2.634 +M3.5 x 0.6,0.6,6h,3.5,3.375,3.11,3.025,2.85,2.655 +M3.5 x 0.6,0.6,4g6g,3.479,3.354,3.089,3.036,2.829,2.666 +M4 x 0.7,0.7,6g,3.978,3.838,3.523,3.433,3.22,3.002 +M4 x 0.7,0.7,6h,4,3.86,3.545,3.455,3.242,3.024 +M4 x 0.7,0.7,4g6g,3.978,3.838,3.523,3.467,3.22,3.036 +M5 x 0.8,0.8,6g,4.976,4.826,4.456,4.361,4.11,3.868 +M5 x 0.8,0.8,6h,5,4.85,4.48,4.385,4.134,3.892 +M5 x 0.8,0.8,4g6g,4.976,4.826,4.456,4.396,4.11,3.903 +M6 x 1,1,6g,5.974,5.794,5.324,5.212,4.891,4.596 +M6 x 1,1,6h,6,5.82,5.35,5.238,4.917,4.622 +M6 x 1,1,4g6g,5.974,5.794,5.324,5.253,4.891,4.637 +M8 x 1.25,1.25,6g,7.972,7.76,7.16,7.042,6.619,6.272 +M8 x 1.25,1.25,6h,8,7.788,7.188,7.07,6.647,6.3 +M8 x 1.25,1.25,4g6g,7.972,7.76,7.16,7.085,6.619,6.315 +M8 x 1,1,6g,7.974,7.794,7.324,7.212,6.891,6.596 +M8 x 1,1,6h,8,7.82,7.35,7.238,6.917,6.622 +M8 x 1,1,4g6g,7.974,7.794,7.324,7.253,6.891,6.637 +M10 x 1.5,1.5,6g,9.968,9.732,8.994,8.862,8.344,7.938 +M10 x 1.5,1.5,6h,10,9.764,9.026,8.894,8.376,7.97 +M10 x 1.5,1.5,4g6g,9.968,9.732,8.994,8.909,8.344,7.985 +M10 x 1.25,1.25,6g,9.972,9.76,9.16,9.042,8.619,8.272 +M10 x 1.25,1.25,6h,10,9.788,9.188,9.07,8.647,8.3 +M10 x 1.25,1.25,4g6g,9.972,9.76,9.16,9.085,8.619,8.315 +M10 x 1,1,6g,9.974,9.794,9.324,9.212,8.891,8.596 +M10 x 1,1,6h,10,9.82,9.35,9.238,8.917,8.622 +M10 x 1,1,4g6g,9.974,9.794,9.324,9.253,8.891,8.637 +M10 x 0.75,0.75,6g,9.978,9.838,9.491,9.391,9.166,8.929 +M10 x 0.75,0.75,6h,10,9.86,9.513,9.413,9.188,8.951 +M10 x 0.75,0.75,4g6g,9.978,9.838,9.491,9.428,9.166,8.966 +M12 x 1.75,1.75,6g,11.966,11.701,10.829,10.679,10.071,9.601 +M12 x 1.75,1.75,6h,12,11.735,10.863,10.713,10.105,9.635 +M12 x 1.75,1.75,4g6g,11.966,11.701,10.829,10.734,10.071,9.656 +M12 x 1.5,1.5,6g,11.968,11.732,10.994,10.854,10.344,9.93 +M12 x 1.5,1.5,6h,12,11.764,11.026,10.886,10.376,9.962 +M12 x 1.5,1.5,4g6g,11.968,11.732,10.994,10.904,10.344,9.98 +M12 x 1.25,1.25,6g,11.972,11.76,11.16,11.028,10.619,10.258 +M12 x 1.25,1.25,6h,12,11.788,11.188,11.056,10.647,10.286 +M12 x 1.25,1.25,4g6g,11.972,11.76,11.16,11.075,10.619,10.305 +M12 x 1,1,6g,11.974,11.794,11.324,11.206,10.891,10.59 +M12 x 1,1,6h,12,11.82,11.35,11.232,10.917,10.616 +M12 x 1,1,4g6g,11.974,11.794,11.324,11.249,10.891,10.633 +M14 x 2,2,6g,13.962,13.682,12.663,12.503,11.797,11.271 +M14 x 2,2,6h,14,13.72,12.701,12.541,11.835,11.309 +M14 x 2,2,4g6g,13.962,13.682,12.663,12.563,11.797,11.331 +M14 x 1.5,1.5,6g,13.968,13.732,12.994,12.854,12.344,11.93 +M14 x 1.5,1.5,6h,14,13.764,13.026,12.886,12.376,11.962 +M14 x 1.5,1.5,4g6g,13.968,13.732,12.994,12.904,12.344,11.98 +M15 x 1,1,6g,14.974,14.794,14.324,14.206,13.891,13.59 +M15 x 1,1,6h,15,14.82,14.35,14.232,13.917,13.616 +M15 x 1,1,4g6g,14.974,14.794,14.324,14.249,13.891,13.633 +M16 x 2,2,6g,15.962,15.682,14.663,14.503,13.797,13.271 +M16 x 2,2,6h,16,15.72,14.701,14.541,13.835,13.309 +M16 x 2,2,4g6g,15.962,15.682,14.663,14.563,13.797,13.331 +M16 x 1.5,1.5,6g,15.968,15.732,14.994,14.854,14.344,13.93 +M16 x 1.5,1.5,6h,16,15.764,15.026,14.886,14.376,13.962 +M16 x 1.5,1.5,4g6g,15.968,15.732,14.994,14.904,14.344,13.98 +M17 x 1,1,6g,16.974,16.794,16.324,16.206,15.891,15.59 +M17 x 1,1,6h,17,16.82,16.35,16.232,15.917,15.616 +M17 x 1,1,4g6g,16.974,16.794,16.324,16.249,15.891,15.633 +M18 x 1.5 ,1.5,6g,17.968,17.732,16.994,16.854,16.344,15.93 +M18 x 1.5 ,1.5,6h,18,17.764,17.026,16.886,16.376,15.962 +M18 x 1.5 ,1.5,4g6g,17.968,17.732,16.994,16.904,16.344,15.98 +M20 x 2.5,2.5,6g,19.958,19.623,18.334,18.164,17.251,16.624 +M20 x 2.5,2.5,6h,20,19.665,18.376,18.206,17.293,16.666 +M20 x 2.5,2.5,4g6g,19.958,19.623,18.334,18.228,17.251,16.688 +M20 x 1.5,1.5,6g,19.968,19.732,18.994,18.854,18.344,17.93 +M20 x 1.5,1.5,6h,20,19.764,19.026,18.886,18.376,17.962 +M20 x 1.5,1.5,4g6g,19.968,19.732,18.994,18.904,18.344,17.98 +M20 x 1,1,6g,19.974,19.794,19.324,19.206,18.891,18.59 +M20 x 1,1,6h,20,19.82,19.35,19.232,18.917,18.616 +M20 x 1,1,4g6g,19.974,19.794,19.324,19.249,18.891,18.633 +M22 x 2.5,2.5,6g,21.958,21.623,20.334,20.164,19.251,18.624 +M22 x 2.5,2.5,6h,22,21.665,20.376,20.206,19.293,18.666 +M22 x 1.5,1.5,6g,21.968,21.732,20.994,20.854,20.344,19.93 +M22 x 1.5,1.5,6h,22,21.764,21.026,20.886,20.376,19.962 +M22 x 1.5,1.5,4g6g,21.968,21.732,20.994,20.904,20.344,19.98 +M24 x 3,3,6g,23.952,23.577,22.003,21.803,20.704,19.955 +M24 x 3,3,6h,24,23.625,22.051,21.851,20.752,20.003 +M24 x 3,3,4g6g,23.952,23.577,22.003,21.878,20.704,20.03 +M24 x 2,2,6g,23.962,23.682,22.663,22.493,21.797,21.261 +M24 x 2,2,6h,24,23.72,22.701,22.531,21.835,21.299 +M24 x 2,2,4g6g,23.962,23.682,22.663,22.557,21.797,21.325 +M25 x 1.5,1.5,6g,24.968,24.732,23.994,23.844,23.344,22.92 +M25 x 1.5,1.5,6h,25,24.764,24.026,23.876,23.376,22.952 +M25 x 1.5,1.5,4g6g,24.968,24.732,23.994,23.899,23.344,22.975 +M27 x 3,3,6g,26.952,26.577,25.003,24.803,23.704,22.955 +M27 x 3,3,6h,27,26.625,25.051,24.851,23.752,23.003 +M27 x 2,2,6g,26.962,26.682,25.663,25.493,24.797,24.261 +M27 x 2,2,6h,27,26.72,25.701,25.531,24.835,24.299 +M27 x 2,2,4g6g,26.962,26.682,25.663,25.557,24.797,24.325 +M30 x 3.5,3.5,6g,29.947,29.522,27.674,27.462,26.158,25.306 +M30 x 3.5,3.5,6h,30,29.575,27.727,27.515,26.211,25.359 +M30 x 3.5,3.5,4g6g,29.947,29.522,27.674,27.542,26.158,25.386 +M30 x 2,2,6g,29.962,29.682,28.663,28.493,27.797,27.261 +M30 x 2,2,6h,30,29.72,28.701,28.531,27.835,27.299 +M30 x 2,2,4g6g,29.962,29.682,28.663,28.557,27.797,27.325 +M30 x 1.5,1.5,6g,29.968,29.732,28.994,28.844,28.344,27.92 +M30 x 1.5,1.5,6h,30,29.764,29.026,28.876,28.376,27.952 +M30 x 1.5,1.5,4g6g,29.968,29.732,28.994,28.899,28.344,27.975 +M33 x 2,2,6g,32.962,32.682,31.663,31.493,30.797,30.261 +M33 x 2,2,6h,33,32.72,31.701,31.531,30.835,30.299 +M33 x 2,2,4g6g,32.962,32.682,31.663,31.557,30.797,30.325 +M35 x 1.5,1.5,6g,34.968,34.732,33.994,33.844,33.344,32.92 +M35 x 1.5,1.5,6h,35,34.764,34.026,33.876,33.376,32.952 +M36 x 4,4,6g,35.94,35.465,33.342,33.118,31.61,30.654 +M36 x 4,4,6h,36,35.525,33.402,33.178,31.67,30.714 +M36 x 4,4,4g6g,35.94,35.465,33.342,33.202,31.61,30.738 +M36 x 2,2,6g,35.962,35.682,34.663,34.493,33.797,33.261 +M36 x 2,2,6h,36,35.72,34.701,34.531,33.835,33.299 +M36 x 2,2,4g6g,35.962,35.682,34.663,34.557,33.797,33.325 +M39 x 2,2,6g,38.962,38.682,37.663,37.493,36.797,36.261 +M39 x 2,2,6h,39,38.72,37.701,37.531,36.835,36.299 +M39 x 2,2,4g6g,38.962,38.682,37.663,37.557,36.797,36.325 +M40 x 1.5,1.5,6g,39.968,39.732,38.994,38.844,38.344,37.92 +M40 x 1.5,1.5,6h,40,39.764,39.026,38.876,38.376,37.952 +M40 x 1.5,1.5,4g6g,39.968,39.732,38.994,38.899,38.344,37.975 diff --git a/src/Mod/Path/Data/Threads/metric-internal.csv b/src/Mod/Path/Data/Threads/metric-internal.csv new file mode 100644 index 0000000000..6a821334d5 --- /dev/null +++ b/src/Mod/Path/Data/Threads/metric-internal.csv @@ -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 diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index f121787924..c1383eb7a5 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -60,6 +60,7 @@ icons/Path_Speed.svg icons/Path_Stock.svg icons/Path_Stop.svg + icons/Path_ThreadMilling.svg icons/Path_ToolBit.svg icons/Path_ToolChange.svg icons/Path_ToolController.svg @@ -114,6 +115,7 @@ panels/PageOpProfileFullEdit.ui panels/PageOpSlotEdit.ui panels/PageOpSurfaceEdit.ui + panels/PageOpThreadMillingEdit.ui panels/PageOpWaterlineEdit.ui panels/PageOpVcarveEdit.ui panels/PathEdit.ui diff --git a/src/Mod/Path/Gui/Resources/icons/Path_ThreadMilling.svg b/src/Mod/Path/Gui/Resources/icons/Path_ThreadMilling.svg new file mode 100644 index 0000000000..6cc8d4653a --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path_ThreadMilling.svg @@ -0,0 +1,1616 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [Lorenz Hüdepohl] + + + Path-Helix + 2016-05-10 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Helix.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui new file mode 100644 index 0000000000..f1f1ae882b --- /dev/null +++ b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui @@ -0,0 +1,242 @@ + + + Form + + + + 0 + 0 + 482 + 736 + + + + Form + + + + + + Thread + + + + + + Orientation + + + + + + + 1 + + + + Left Hand + + + + + Right Hand + + + + + + + + Type + + + + + + + + Custom + + + + + Metric - internal + + + + + SAE - internal + + + + + + + + + + + Fit + + + + + + + 50 + + + Qt::Horizontal + + + + + + + Major Diameter + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Minor Diameter + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Pitch + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Tool Controller + + + + + + Tool + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Flat + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Operation + + + + + + Passes + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + + + + + Direction + + + + + + + + Climb + + + + + Conventional + + + + + + + + + + + + Gui::QuantitySpinBox + QDoubleSpinBox +
Gui/QuantitySpinBox.h
+
+
+ + +
diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index 7580f38862..abd207945e 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -118,6 +118,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 +146,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) diff --git a/src/Mod/Path/PathScripts/PathCircularHoleBase.py b/src/Mod/Path/PathScripts/PathCircularHoleBase.py index c88cdf90d4..c12d6c1075 100644 --- a/src/Mod/Path/PathScripts/PathCircularHoleBase.py +++ b/src/Mod/Path/PathScripts/PathCircularHoleBase.py @@ -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 diff --git a/src/Mod/Path/PathScripts/PathGuiInit.py b/src/Mod/Path/PathScripts/PathGuiInit.py index 6123ddce36..cd48d41309 100644 --- a/src/Mod/Path/PathScripts/PathGuiInit.py +++ b/src/Mod/Path/PathScripts/PathGuiInit.py @@ -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 diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index d3b6624214..11a75b31a1 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -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 diff --git a/src/Mod/Path/PathScripts/PathThreadMilling.py b/src/Mod/Path/PathScripts/PathThreadMilling.py new file mode 100644 index 0000000000..66d277648b --- /dev/null +++ b/src/Mod/Path/PathScripts/PathThreadMilling.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2019 sliptonic * +# * * +# * 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.PathLog as PathLog +import PathScripts.PathOp as PathOp +import PathScripts.PathUtils as PathUtils + +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.DEBUG, PathLog.thisModule()) +PathLog.trackModule(PathLog.thisModule()) + + +# Qt translation handling +def translate(context, text, disambig=None): + return QtCore.QCoreApplication.translate(context, text, disambig) + + +class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): + '''Proxy object for thread milling operation.''' + + LeftHand = 'LeftHand' + RightHand = 'RightHand' + ThreadTypeCustom = 'Custom' + ThreadTypeMetricInternal = 'Metric - internal' + DirectionClimb = 'Climb' + + ThreadOrientations = [LeftHand, RightHand] + ThreadTypes = [ThreadTypeCustom, ThreadTypeMetricInternal] + Directions = [DirectionClimb] + + def circularHoleFeatures(self, obj): + return PathOp.FeatureBaseGeometry + + def initCircularHoleOperation(self, obj): + obj.addProperty("App::PropertyEnumeration", "ThreadOrientation", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread orientation")) + obj.ThreadOrientation = self.ThreadOrientations + obj.addProperty("App::PropertyEnumeration", "ThreadType", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Currently only internal")) + obj.ThreadType = self.ThreadTypes + obj.addProperty("App::PropertyString", "ThreadName", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Devfines which standard thread was chosen")) + obj.addProperty("App::PropertyLength", "MajorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's major diameter")) + obj.addProperty("App::PropertyLength", "MinorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's minor diameter")) + obj.addProperty("App::PropertyLength", "Pitch", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's pitch")) + obj.addProperty("App::PropertyInteger", "ThreadFit", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set how many passes are used to cut the thread")) + obj.addProperty("App::PropertyInteger", "Passes", "Mill", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set how many passes are used to cut the thread")) + obj.addProperty("App::PropertyEnumeration", "Direction", "Mill", QtCore.QT_TRANSLATE_NOOP("App::Property", "Direction of thread cutting operation")) + obj.Direction = self.Directions + + + def circularHoleExecute(self, obj, holes): + PathLog.track() + + self.commandlist.append(Path.Command("(Begin Thread Milling)")) + + # rapid to clearance height + self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) + + + def opSetDefaultValues(self, obj, job): + obj.ThreadOrientation = self.RightHand + obj.ThreadType = self.ThreadTypeMetricInternal + obj.ThreadFit = 50 + obj.Pitch = 1 + obj.Passes = 1 + obj.Direction = self.DirectionClimb + + +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("Passes") + setup.append("Direction") + 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 + diff --git a/src/Mod/Path/PathScripts/PathThreadMillingGui.py b/src/Mod/Path/PathScripts/PathThreadMillingGui.py new file mode 100644 index 0000000000..e59f783292 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathThreadMillingGui.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2019 sliptonic * +# * * +# * 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.DEBUG, 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() + + 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.opPasses.setValue(obj.Passes) + self.form.opDirection.setCurrentText(obj.Direction) + + self.majorDia.updateSpinBox() + self.minorDia.updateSpinBox() + self.pitch.updateSpinBox() + + self.setupToolController(obj, self.form.toolController) + + + + def _updateFromThreadType(self): + if self.form.threadType.currentIndex() == 0: + PathLog.track(self.form.threadType.currentIndex()) + self.form.threadName.setEnabled(False) + self.form.threadFit.setEnabled(False) + self.form.threadFitLabel.setEnabled(False) + else: + PathLog.track(self.form.threadType.currentIndex()) + self.form.threadFit.setEnabled(True) + self.form.threadFitLabel.setEnabled(True) + fillThreads(self.form.threadName, 'metric-internal') + + def _updateFromThreadName(self): + thread = self.form.threadName.currentData() + mamin = float(thread['dMajorMin']) + mamax = float(thread['dMajorMax']) + mimin = float(thread['dMinorMin']) + mimax = float(thread['dMinorMax']) + pitch = float(thread['pitch']) + + self.pitch.updateSpinBox(pitch) + self.majorDia.updateSpinBox((mamin + mamax) / 2) + self.minorDia.updateSpinBox((mimin + mimax) / 2) + + 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.toolController.currentIndexChanged) + + return signals + + def registerSignalHandlers(self, obj): + self.form.threadType.currentIndexChanged.connect(self._updateFromThreadType) + self.form.threadName.currentIndexChanged.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") From 0a96a861c39a2861db16a0a39387d5e20cd244c4 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Tue, 13 Aug 2019 22:07:09 -0700 Subject: [PATCH 02/14] Added thread milling radii calculation --- .../panels/PageOpThreadMillingEdit.ui | 6 ++ src/Mod/Path/PathScripts/PathThreadMilling.py | 73 ++++++++++++++++++- .../Path/PathTests/TestPathThreadMilling.py | 61 ++++++++++++++++ src/Mod/Path/TestPathApp.py | 2 + 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 src/Mod/Path/PathTests/TestPathThreadMilling.py diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui index f1f1ae882b..2e98648175 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui @@ -153,6 +153,9 @@ + + false + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -160,6 +163,9 @@ + + false + Flat diff --git a/src/Mod/Path/PathScripts/PathThreadMilling.py b/src/Mod/Path/PathScripts/PathThreadMilling.py index 66d277648b..0feb748825 100644 --- a/src/Mod/Path/PathScripts/PathThreadMilling.py +++ b/src/Mod/Path/PathScripts/PathThreadMilling.py @@ -41,12 +41,26 @@ __doc__ = "Path thread milling operation." PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) PathLog.trackModule(PathLog.thisModule()) - # Qt translation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) +def radiiMetricInternal(majorDia, minorDia, toolDia, toolCrest = None): + '''internlThreadRadius(majorDia, minorDia, toolDia, toolCrest) ... returns the maximum radius for thread.''' + if toolCrest is None: + toolCrest = 0.0 + # see https://www.amesweb.info/Screws/Internal-Metric-Thread-Dimensions-Chart.aspx + H = ((majorDia - minorDia) / 2.0 ) * 1.6 # (D - d)/2 = 5/8 * H + outerTip = majorDia / 2.0 + H / 8.0 + toolTip = outerTip - toolCrest * 0.8660254037844386 # math.sqrt(3)/2 ... 60deg triangle height + return ((minorDia - toolDia) / 2, toolTip - toolDia / 2) + +def threadPasses(count, radii, majorDia, minorDia, toolDia, toolCrest = None): + minor, major = radii(majorDia, minorDia, toolDia, toolCrest) + dr = (major - minor) / count + return [major - dr * (count - (i + 1)) for i in range(count)] + class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): '''Proxy object for thread milling operation.''' @@ -77,13 +91,70 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): obj.addProperty("App::PropertyEnumeration", "Direction", "Mill", QtCore.QT_TRANSLATE_NOOP("App::Property", "Direction of thread cutting operation")) obj.Direction = self.Directions + def threadStartDepth(self, obj): + if self.ThreadDirection == self.RightHand: + if self.Direction == self.DirectionClimb: + return self.FinalDepth + return self.StartDepth + if self.Direction == self.DirectionClimb: + return self.StartDepth + return self.FinalDepth + + def threadFinalDepth(self, obj): + if self.ThreadDirection == self.RightHand: + if self.Direction == self.DirectionClimb: + return self.StartDepth + return self.FinalDepth + if self.Direction == self.DirectionClimb: + return self.FinalDepth + return self.StartDepth + + def threadDirectionCmd(self, obj): + if self.ThreadDirection == self.RightHand: + if self.Direction == self.DirectionClimb: + return 'G2' + return 'G3' + if self.Direction == self.DirectionClimb: + return 'G3' + return 'G2' + + def threadPassRadii(self, obj): + 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, cmd, zStart, zFinal, pitch): + hPitch = obj.Pitch.Value / 2.0 + if zStart > zFinal: + hPitch = -hPitch + + self.commandlist.append(Path.Command('G0', {'Z': zStart, 'F': self.vertRapid})) + for r in threadPasses(obj.Passes, radiiMetricInternal, obj.MajorDiameter, obj.MinorDiameter, self.tool.Diameter, 0): + pass def circularHoleExecute(self, obj, holes): PathLog.track() self.commandlist.append(Path.Command("(Begin Thread Milling)")) + cmd = self.threadDirectionCmd(obj) + zStart = self.threadStartDepth(obj).Value + zFinal = self.threadFinalDepth(obj).Value + pitch = obj.Pitch.Value + if pitch <= 0: + PathLog.error("Cannot create thread with pitch {}".format(pitch)) + return + # rapid to clearance height + for loc in holes: + self.executeThreadMill(obj, loc, cmd, zStart, zFinal, pitch) + self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) diff --git a/src/Mod/Path/PathTests/TestPathThreadMilling.py b/src/Mod/Path/PathTests/TestPathThreadMilling.py new file mode 100644 index 0000000000..8d62c1bf6f --- /dev/null +++ b/src/Mod/Path/PathTests/TestPathThreadMilling.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2019 sliptonic * +# * * +# * 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 metric internal radii.''' + self.assertRadii(PathThreadMilling.radiiMetricInternal(20, 18, 2, 0), (8, 9.2)) + self.assertRadii(PathThreadMilling.radiiMetricInternal(20, 19, 2, 0), (8.5, 9.1)) + + def test01(self): + '''Verify metric internal radii with tool crest.''' + self.assertRadii(PathThreadMilling.radiiMetricInternal(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]) + diff --git a/src/Mod/Path/TestPathApp.py b/src/Mod/Path/TestPathApp.py index 1f4c0247fb..623bf180ee 100644 --- a/src/Mod/Path/TestPathApp.py +++ b/src/Mod/Path/TestPathApp.py @@ -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 From 15141e33dc09fb3219132080403ec1bfe98ee826 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Wed, 14 Aug 2019 23:29:18 -0700 Subject: [PATCH 03/14] Added actual thread milling path generation. --- .../panels/PageOpThreadMillingEdit.ui | 11 +- src/Mod/Path/PathScripts/PathThreadMilling.py | 203 ++++++++++++++---- .../Path/PathScripts/PathThreadMillingGui.py | 6 + 3 files changed, 180 insertions(+), 40 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui index 2e98648175..423c9fbabb 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui @@ -7,7 +7,7 @@ 0 0 482 - 736 + 756 @@ -167,7 +167,7 @@ false - Flat + Crest @@ -231,6 +231,13 @@ + + + + Lead In/Out + + + diff --git a/src/Mod/Path/PathScripts/PathThreadMilling.py b/src/Mod/Path/PathScripts/PathThreadMilling.py index 0feb748825..db226dfeb0 100644 --- a/src/Mod/Path/PathScripts/PathThreadMilling.py +++ b/src/Mod/Path/PathScripts/PathThreadMilling.py @@ -27,6 +27,7 @@ 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 @@ -48,6 +49,7 @@ def translate(context, text, disambig=None): def radiiMetricInternal(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 # see https://www.amesweb.info/Screws/Internal-Metric-Thread-Dimensions-Chart.aspx @@ -57,22 +59,114 @@ def radiiMetricInternal(majorDia, minorDia, toolDia, toolCrest = None): return ((minorDia - toolDia) / 2, toolTip - toolDia / 2) 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 = (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' - DirectionClimb = 'Climb' + LeftHand = 'LeftHand' + RightHand = 'RightHand' + ThreadTypeCustom = 'Custom' + ThreadTypeMetricInternal = 'Metric - internal' + DirectionClimb = 'Climb' + DirectionConventional = 'Conventional' ThreadOrientations = [LeftHand, RightHand] ThreadTypes = [ThreadTypeCustom, ThreadTypeMetricInternal] - Directions = [DirectionClimb] + Directions = [DirectionClimb, DirectionConventional] def circularHoleFeatures(self, obj): return PathOp.FeatureBaseGeometry @@ -87,38 +181,64 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): obj.addProperty("App::PropertyLength", "MinorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's minor diameter")) obj.addProperty("App::PropertyLength", "Pitch", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's pitch")) obj.addProperty("App::PropertyInteger", "ThreadFit", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set how many passes are used to cut the thread")) - obj.addProperty("App::PropertyInteger", "Passes", "Mill", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set how many passes are used to cut the thread")) - obj.addProperty("App::PropertyEnumeration", "Direction", "Mill", QtCore.QT_TRANSLATE_NOOP("App::Property", "Direction of thread cutting operation")) + obj.addProperty("App::PropertyInteger", "Passes", "Operation", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set how many passes are used to cut the thread")) + obj.addProperty("App::PropertyEnumeration", "Direction", "Operation", QtCore.QT_TRANSLATE_NOOP("App::Property", "Direction of thread cutting operation")) + obj.addProperty("App::PropertyBool", "LeadInOut", "Operation", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set to True to get lead in and lead out arcs at the start and end of the thread cut")) obj.Direction = self.Directions def threadStartDepth(self, obj): - if self.ThreadDirection == self.RightHand: - if self.Direction == self.DirectionClimb: - return self.FinalDepth - return self.StartDepth - if self.Direction == self.DirectionClimb: - return self.StartDepth - return self.FinalDepth + 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): - if self.ThreadDirection == self.RightHand: - if self.Direction == self.DirectionClimb: - return self.StartDepth - return self.FinalDepth - if self.Direction == self.DirectionClimb: - return self.FinalDepth - return self.StartDepth + 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): - if self.ThreadDirection == self.RightHand: - if self.Direction == self.DirectionClimb: + 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 self.Direction == self.DirectionClimb: + 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: @@ -129,23 +249,30 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): passes.append(rMajor - rPass * i) return list(reversed(passes)) - def executeThreadMill(self, obj, loc, cmd, zStart, zFinal, pitch): - hPitch = obj.Pitch.Value / 2.0 - if zStart > zFinal: - hPitch = -hPitch + 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': zStart, 'F': self.vertRapid})) - for r in threadPasses(obj.Passes, radiiMetricInternal, obj.MajorDiameter, obj.MinorDiameter, self.tool.Diameter, 0): - pass + self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) + + for radius in threadPasses(obj.Passes, radiiMetricInternal, obj.MajorDiameter.Value, obj.MinorDiameter.Value, self.tool.Diameter, 0): + 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() self.commandlist.append(Path.Command("(Begin Thread Milling)")) - cmd = self.threadDirectionCmd(obj) - zStart = self.threadStartDepth(obj).Value - zFinal = self.threadFinalDepth(obj).Value + (cmd, zStart, zFinal) = self.threadSetup(obj) pitch = obj.Pitch.Value if pitch <= 0: PathLog.error("Cannot create thread with pitch {}".format(pitch)) @@ -153,9 +280,7 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): # rapid to clearance height for loc in holes: - self.executeThreadMill(obj, loc, cmd, zStart, zFinal, pitch) - - self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) + self.executeThreadMill(obj, FreeCAD.Vector(loc['x'], loc['y'], 0), cmd, zStart, zFinal, pitch) def opSetDefaultValues(self, obj, job): @@ -165,6 +290,7 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): obj.Pitch = 1 obj.Passes = 1 obj.Direction = self.DirectionClimb + obj.LeadInOut = True def SetupProperties(): @@ -178,6 +304,7 @@ def SetupProperties(): setup.append("Pitch") setup.append("Passes") setup.append("Direction") + setup.append("LeadInOut") return setup diff --git a/src/Mod/Path/PathScripts/PathThreadMillingGui.py b/src/Mod/Path/PathScripts/PathThreadMillingGui.py index e59f783292..846a5b6e5f 100644 --- a/src/Mod/Path/PathScripts/PathThreadMillingGui.py +++ b/src/Mod/Path/PathScripts/PathThreadMillingGui.py @@ -85,6 +85,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): 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 self.updateToolController(obj, self.form.toolController) @@ -104,6 +105,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): 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() @@ -146,6 +148,10 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): 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.opDirection.currentIndexChanged) + signals.append(self.form.opPasses.editingFinished) + signals.append(self.form.leadInOut.stateChanged) signals.append(self.form.toolController.currentIndexChanged) From c11aaafc5424cb95d4370b83ef225d792a887520 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Sat, 17 Aug 2019 19:27:11 -0700 Subject: [PATCH 04/14] Added support for imperial threads to thread milling --- src/Mod/Path/CMakeLists.txt | 2 +- .../Path/Data/Threads/imperial-internal.csv | 81 +++++++++++ src/Mod/Path/Data/Threads/metric-external.csv | 130 ------------------ src/Mod/Path/Data/Threads/sources.txt | 4 + .../panels/PageOpThreadMillingEdit.ui | 19 ++- src/Mod/Path/PathScripts/PathThreadMilling.py | 23 +++- .../Path/PathScripts/PathThreadMillingGui.py | 55 ++++++-- .../Path/PathTests/TestPathThreadMilling.py | 10 +- 8 files changed, 174 insertions(+), 150 deletions(-) create mode 100644 src/Mod/Path/Data/Threads/imperial-internal.csv delete mode 100644 src/Mod/Path/Data/Threads/metric-external.csv create mode 100644 src/Mod/Path/Data/Threads/sources.txt diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index 9844e431b8..6c9d692156 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -224,8 +224,8 @@ SET(Path_Images ) SET(PathData_Threads - Data/Threads/metric-external.csv Data/Threads/metric-internal.csv + Data/Threads/imperial-internal.csv ) SET(Path_Data diff --git a/src/Mod/Path/Data/Threads/imperial-internal.csv b/src/Mod/Path/Data/Threads/imperial-internal.csv new file mode 100644 index 0000000000..9a171490d9 --- /dev/null +++ b/src/Mod/Path/Data/Threads/imperial-internal.csv @@ -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 diff --git a/src/Mod/Path/Data/Threads/metric-external.csv b/src/Mod/Path/Data/Threads/metric-external.csv deleted file mode 100644 index 24232aa7ed..0000000000 --- a/src/Mod/Path/Data/Threads/metric-external.csv +++ /dev/null @@ -1,130 +0,0 @@ -name,pitch,tol,dMinorMin,dMinorMax,dPitchMin,dPitchMax,dMajorMin,dMajorMax -M1.6 x 0.35,0.35,6g,1.581,1.496,1.354,1.291,1.202,1.075 -M1.6 x 0.35,0.35,6h,1.6,1.515,1.373,1.31,1.221,1.094 -M1.6 x 0.35,0.35,4g6g,1.581,1.496,1.354,1.314,1.202,1.098 -M2 x 0.4,0.4,6g,1.981,1.886,1.721,1.654,1.548,1.408 -M2 x 0.4,0.4,6h,2,1.905,1.74,1.673,1.567,1.427 -M2 x 0.4,0.4,4g6g,1.981,1.886,1.721,1.679,1.548,1.433 -M2.5 x 0.45,0.45,6g,2.48,2.38,2.188,2.117,1.993,1.84 -M2.5 x 0.45,0.45,6h,2.5,2.4,2.208,2.137,2.013,1.86 -M2.5 x 0.45,0.45,4g6g,2.48,2.38,2.188,2.143,1.993,1.866 -M3 x 0.5,0.5,6g,2.98,2.874,2.655,2.58,2.438,2.272 -M3 x 0.5,0.5,6h,3,2.894,2.675,2.6,2.458,2.292 -M3 x 0.5,0.5,4g6g,2.98,2.874,2.655,2.607,2.438,2.299 -M3.5 x 0.6,0.6,6g,3.479,3.354,3.089,3.004,2.829,2.634 -M3.5 x 0.6,0.6,6h,3.5,3.375,3.11,3.025,2.85,2.655 -M3.5 x 0.6,0.6,4g6g,3.479,3.354,3.089,3.036,2.829,2.666 -M4 x 0.7,0.7,6g,3.978,3.838,3.523,3.433,3.22,3.002 -M4 x 0.7,0.7,6h,4,3.86,3.545,3.455,3.242,3.024 -M4 x 0.7,0.7,4g6g,3.978,3.838,3.523,3.467,3.22,3.036 -M5 x 0.8,0.8,6g,4.976,4.826,4.456,4.361,4.11,3.868 -M5 x 0.8,0.8,6h,5,4.85,4.48,4.385,4.134,3.892 -M5 x 0.8,0.8,4g6g,4.976,4.826,4.456,4.396,4.11,3.903 -M6 x 1,1,6g,5.974,5.794,5.324,5.212,4.891,4.596 -M6 x 1,1,6h,6,5.82,5.35,5.238,4.917,4.622 -M6 x 1,1,4g6g,5.974,5.794,5.324,5.253,4.891,4.637 -M8 x 1.25,1.25,6g,7.972,7.76,7.16,7.042,6.619,6.272 -M8 x 1.25,1.25,6h,8,7.788,7.188,7.07,6.647,6.3 -M8 x 1.25,1.25,4g6g,7.972,7.76,7.16,7.085,6.619,6.315 -M8 x 1,1,6g,7.974,7.794,7.324,7.212,6.891,6.596 -M8 x 1,1,6h,8,7.82,7.35,7.238,6.917,6.622 -M8 x 1,1,4g6g,7.974,7.794,7.324,7.253,6.891,6.637 -M10 x 1.5,1.5,6g,9.968,9.732,8.994,8.862,8.344,7.938 -M10 x 1.5,1.5,6h,10,9.764,9.026,8.894,8.376,7.97 -M10 x 1.5,1.5,4g6g,9.968,9.732,8.994,8.909,8.344,7.985 -M10 x 1.25,1.25,6g,9.972,9.76,9.16,9.042,8.619,8.272 -M10 x 1.25,1.25,6h,10,9.788,9.188,9.07,8.647,8.3 -M10 x 1.25,1.25,4g6g,9.972,9.76,9.16,9.085,8.619,8.315 -M10 x 1,1,6g,9.974,9.794,9.324,9.212,8.891,8.596 -M10 x 1,1,6h,10,9.82,9.35,9.238,8.917,8.622 -M10 x 1,1,4g6g,9.974,9.794,9.324,9.253,8.891,8.637 -M10 x 0.75,0.75,6g,9.978,9.838,9.491,9.391,9.166,8.929 -M10 x 0.75,0.75,6h,10,9.86,9.513,9.413,9.188,8.951 -M10 x 0.75,0.75,4g6g,9.978,9.838,9.491,9.428,9.166,8.966 -M12 x 1.75,1.75,6g,11.966,11.701,10.829,10.679,10.071,9.601 -M12 x 1.75,1.75,6h,12,11.735,10.863,10.713,10.105,9.635 -M12 x 1.75,1.75,4g6g,11.966,11.701,10.829,10.734,10.071,9.656 -M12 x 1.5,1.5,6g,11.968,11.732,10.994,10.854,10.344,9.93 -M12 x 1.5,1.5,6h,12,11.764,11.026,10.886,10.376,9.962 -M12 x 1.5,1.5,4g6g,11.968,11.732,10.994,10.904,10.344,9.98 -M12 x 1.25,1.25,6g,11.972,11.76,11.16,11.028,10.619,10.258 -M12 x 1.25,1.25,6h,12,11.788,11.188,11.056,10.647,10.286 -M12 x 1.25,1.25,4g6g,11.972,11.76,11.16,11.075,10.619,10.305 -M12 x 1,1,6g,11.974,11.794,11.324,11.206,10.891,10.59 -M12 x 1,1,6h,12,11.82,11.35,11.232,10.917,10.616 -M12 x 1,1,4g6g,11.974,11.794,11.324,11.249,10.891,10.633 -M14 x 2,2,6g,13.962,13.682,12.663,12.503,11.797,11.271 -M14 x 2,2,6h,14,13.72,12.701,12.541,11.835,11.309 -M14 x 2,2,4g6g,13.962,13.682,12.663,12.563,11.797,11.331 -M14 x 1.5,1.5,6g,13.968,13.732,12.994,12.854,12.344,11.93 -M14 x 1.5,1.5,6h,14,13.764,13.026,12.886,12.376,11.962 -M14 x 1.5,1.5,4g6g,13.968,13.732,12.994,12.904,12.344,11.98 -M15 x 1,1,6g,14.974,14.794,14.324,14.206,13.891,13.59 -M15 x 1,1,6h,15,14.82,14.35,14.232,13.917,13.616 -M15 x 1,1,4g6g,14.974,14.794,14.324,14.249,13.891,13.633 -M16 x 2,2,6g,15.962,15.682,14.663,14.503,13.797,13.271 -M16 x 2,2,6h,16,15.72,14.701,14.541,13.835,13.309 -M16 x 2,2,4g6g,15.962,15.682,14.663,14.563,13.797,13.331 -M16 x 1.5,1.5,6g,15.968,15.732,14.994,14.854,14.344,13.93 -M16 x 1.5,1.5,6h,16,15.764,15.026,14.886,14.376,13.962 -M16 x 1.5,1.5,4g6g,15.968,15.732,14.994,14.904,14.344,13.98 -M17 x 1,1,6g,16.974,16.794,16.324,16.206,15.891,15.59 -M17 x 1,1,6h,17,16.82,16.35,16.232,15.917,15.616 -M17 x 1,1,4g6g,16.974,16.794,16.324,16.249,15.891,15.633 -M18 x 1.5 ,1.5,6g,17.968,17.732,16.994,16.854,16.344,15.93 -M18 x 1.5 ,1.5,6h,18,17.764,17.026,16.886,16.376,15.962 -M18 x 1.5 ,1.5,4g6g,17.968,17.732,16.994,16.904,16.344,15.98 -M20 x 2.5,2.5,6g,19.958,19.623,18.334,18.164,17.251,16.624 -M20 x 2.5,2.5,6h,20,19.665,18.376,18.206,17.293,16.666 -M20 x 2.5,2.5,4g6g,19.958,19.623,18.334,18.228,17.251,16.688 -M20 x 1.5,1.5,6g,19.968,19.732,18.994,18.854,18.344,17.93 -M20 x 1.5,1.5,6h,20,19.764,19.026,18.886,18.376,17.962 -M20 x 1.5,1.5,4g6g,19.968,19.732,18.994,18.904,18.344,17.98 -M20 x 1,1,6g,19.974,19.794,19.324,19.206,18.891,18.59 -M20 x 1,1,6h,20,19.82,19.35,19.232,18.917,18.616 -M20 x 1,1,4g6g,19.974,19.794,19.324,19.249,18.891,18.633 -M22 x 2.5,2.5,6g,21.958,21.623,20.334,20.164,19.251,18.624 -M22 x 2.5,2.5,6h,22,21.665,20.376,20.206,19.293,18.666 -M22 x 1.5,1.5,6g,21.968,21.732,20.994,20.854,20.344,19.93 -M22 x 1.5,1.5,6h,22,21.764,21.026,20.886,20.376,19.962 -M22 x 1.5,1.5,4g6g,21.968,21.732,20.994,20.904,20.344,19.98 -M24 x 3,3,6g,23.952,23.577,22.003,21.803,20.704,19.955 -M24 x 3,3,6h,24,23.625,22.051,21.851,20.752,20.003 -M24 x 3,3,4g6g,23.952,23.577,22.003,21.878,20.704,20.03 -M24 x 2,2,6g,23.962,23.682,22.663,22.493,21.797,21.261 -M24 x 2,2,6h,24,23.72,22.701,22.531,21.835,21.299 -M24 x 2,2,4g6g,23.962,23.682,22.663,22.557,21.797,21.325 -M25 x 1.5,1.5,6g,24.968,24.732,23.994,23.844,23.344,22.92 -M25 x 1.5,1.5,6h,25,24.764,24.026,23.876,23.376,22.952 -M25 x 1.5,1.5,4g6g,24.968,24.732,23.994,23.899,23.344,22.975 -M27 x 3,3,6g,26.952,26.577,25.003,24.803,23.704,22.955 -M27 x 3,3,6h,27,26.625,25.051,24.851,23.752,23.003 -M27 x 2,2,6g,26.962,26.682,25.663,25.493,24.797,24.261 -M27 x 2,2,6h,27,26.72,25.701,25.531,24.835,24.299 -M27 x 2,2,4g6g,26.962,26.682,25.663,25.557,24.797,24.325 -M30 x 3.5,3.5,6g,29.947,29.522,27.674,27.462,26.158,25.306 -M30 x 3.5,3.5,6h,30,29.575,27.727,27.515,26.211,25.359 -M30 x 3.5,3.5,4g6g,29.947,29.522,27.674,27.542,26.158,25.386 -M30 x 2,2,6g,29.962,29.682,28.663,28.493,27.797,27.261 -M30 x 2,2,6h,30,29.72,28.701,28.531,27.835,27.299 -M30 x 2,2,4g6g,29.962,29.682,28.663,28.557,27.797,27.325 -M30 x 1.5,1.5,6g,29.968,29.732,28.994,28.844,28.344,27.92 -M30 x 1.5,1.5,6h,30,29.764,29.026,28.876,28.376,27.952 -M30 x 1.5,1.5,4g6g,29.968,29.732,28.994,28.899,28.344,27.975 -M33 x 2,2,6g,32.962,32.682,31.663,31.493,30.797,30.261 -M33 x 2,2,6h,33,32.72,31.701,31.531,30.835,30.299 -M33 x 2,2,4g6g,32.962,32.682,31.663,31.557,30.797,30.325 -M35 x 1.5,1.5,6g,34.968,34.732,33.994,33.844,33.344,32.92 -M35 x 1.5,1.5,6h,35,34.764,34.026,33.876,33.376,32.952 -M36 x 4,4,6g,35.94,35.465,33.342,33.118,31.61,30.654 -M36 x 4,4,6h,36,35.525,33.402,33.178,31.67,30.714 -M36 x 4,4,4g6g,35.94,35.465,33.342,33.202,31.61,30.738 -M36 x 2,2,6g,35.962,35.682,34.663,34.493,33.797,33.261 -M36 x 2,2,6h,36,35.72,34.701,34.531,33.835,33.299 -M36 x 2,2,4g6g,35.962,35.682,34.663,34.557,33.797,33.325 -M39 x 2,2,6g,38.962,38.682,37.663,37.493,36.797,36.261 -M39 x 2,2,6h,39,38.72,37.701,37.531,36.835,36.299 -M39 x 2,2,4g6g,38.962,38.682,37.663,37.557,36.797,36.325 -M40 x 1.5,1.5,6g,39.968,39.732,38.994,38.844,38.344,37.92 -M40 x 1.5,1.5,6h,40,39.764,39.026,38.876,38.376,37.952 -M40 x 1.5,1.5,4g6g,39.968,39.732,38.994,38.899,38.344,37.975 diff --git a/src/Mod/Path/Data/Threads/sources.txt b/src/Mod/Path/Data/Threads/sources.txt new file mode 100644 index 0000000000..20fb5e92fd --- /dev/null +++ b/src/Mod/Path/Data/Threads/sources.txt @@ -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 diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui index 423c9fbabb..f67744b714 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui @@ -82,6 +82,9 @@ + + 100 + 50 @@ -119,7 +122,7 @@ - + Pitch @@ -132,6 +135,20 @@ + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TPI + + + diff --git a/src/Mod/Path/PathScripts/PathThreadMilling.py b/src/Mod/Path/PathScripts/PathThreadMilling.py index db226dfeb0..f7d7fdb58b 100644 --- a/src/Mod/Path/PathScripts/PathThreadMilling.py +++ b/src/Mod/Path/PathScripts/PathThreadMilling.py @@ -31,6 +31,7 @@ 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 @@ -47,14 +48,20 @@ def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) -def radiiMetricInternal(majorDia, minorDia, toolDia, toolCrest = None): +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 - # see https://www.amesweb.info/Screws/Internal-Metric-Thread-Dimensions-Chart.aspx + # 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, toolTip - toolDia / 2) @@ -161,11 +168,12 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): RightHand = 'RightHand' ThreadTypeCustom = 'Custom' ThreadTypeMetricInternal = 'Metric - internal' + ThreadTypeImperialInternal = 'Imperial - internal' DirectionClimb = 'Climb' DirectionConventional = 'Conventional' ThreadOrientations = [LeftHand, RightHand] - ThreadTypes = [ThreadTypeCustom, ThreadTypeMetricInternal] + ThreadTypes = [ThreadTypeCustom, ThreadTypeMetricInternal, ThreadTypeImperialInternal] Directions = [DirectionClimb, DirectionConventional] def circularHoleFeatures(self, obj): @@ -179,7 +187,8 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): obj.addProperty("App::PropertyString", "ThreadName", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Devfines which standard thread was chosen")) obj.addProperty("App::PropertyLength", "MajorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's major diameter")) obj.addProperty("App::PropertyLength", "MinorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's minor diameter")) - obj.addProperty("App::PropertyLength", "Pitch", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's pitch")) + obj.addProperty("App::PropertyLength", "Pitch", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's pitch - used for metric threads")) + obj.addProperty("App::PropertyInteger", "TPI", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's tpi - used for imperial threads")) obj.addProperty("App::PropertyInteger", "ThreadFit", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set how many passes are used to cut the thread")) obj.addProperty("App::PropertyInteger", "Passes", "Operation", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set how many passes are used to cut the thread")) obj.addProperty("App::PropertyEnumeration", "Direction", "Operation", QtCore.QT_TRANSLATE_NOOP("App::Property", "Direction of thread cutting operation")) @@ -254,7 +263,7 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) - for radius in threadPasses(obj.Passes, radiiMetricInternal, obj.MajorDiameter.Value, obj.MinorDiameter.Value, self.tool.Diameter, 0): + for radius in threadPasses(obj.Passes, radiiInternal, obj.MajorDiameter.Value, obj.MinorDiameter.Value, self.tool.Diameter, 0): commands = internalThreadCommands(loc, gcode, zStart, zFinal, pitch, radius, obj.LeadInOut) for cmd in commands: p = cmd.Parameters @@ -274,6 +283,8 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): (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 @@ -288,6 +299,7 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): obj.ThreadType = self.ThreadTypeMetricInternal obj.ThreadFit = 50 obj.Pitch = 1 + obj.TPI = 0 obj.Passes = 1 obj.Direction = self.DirectionClimb obj.LeadInOut = True @@ -302,6 +314,7 @@ def SetupProperties(): setup.append("MajorDiameter") setup.append("MinorDiameter") setup.append("Pitch") + setup.append("TPI") setup.append("Passes") setup.append("Direction") setup.append("LeadInOut") diff --git a/src/Mod/Path/PathScripts/PathThreadMillingGui.py b/src/Mod/Path/PathScripts/PathThreadMillingGui.py index 846a5b6e5f..9a3e55930e 100644 --- a/src/Mod/Path/PathScripts/PathThreadMillingGui.py +++ b/src/Mod/Path/PathScripts/PathThreadMillingGui.py @@ -86,6 +86,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): 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) @@ -102,6 +103,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): 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) @@ -114,30 +116,65 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): 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.currentIndex() == 0: - PathLog.track(self.form.threadType.currentIndex()) + + if self.form.threadType.currentText() == PathThreadMilling.ObjectThreadMilling.ThreadTypeCustom: self.form.threadName.setEnabled(False) self.form.threadFit.setEnabled(False) self.form.threadFitLabel.setEnabled(False) - else: - PathLog.track(self.form.threadType.currentIndex()) + 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']) - pitch = float(thread['pitch']) + minor = mimin + (mimax - mimin) * fit - self.pitch.updateSpinBox(pitch) - self.majorDia.updateSpinBox((mamin + mamax) / 2) - self.minorDia.updateSpinBox((mimin + mimax) / 2) + 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() @@ -149,6 +186,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): 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) @@ -160,6 +198,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): 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', diff --git a/src/Mod/Path/PathTests/TestPathThreadMilling.py b/src/Mod/Path/PathTests/TestPathThreadMilling.py index 8d62c1bf6f..e8ac9c8a65 100644 --- a/src/Mod/Path/PathTests/TestPathThreadMilling.py +++ b/src/Mod/Path/PathTests/TestPathThreadMilling.py @@ -45,13 +45,13 @@ class TestPathThreadMilling(PathTestBase): self.assertRoughly(have[i], want[i]) def test00(self): - '''Verify metric internal radii.''' - self.assertRadii(PathThreadMilling.radiiMetricInternal(20, 18, 2, 0), (8, 9.2)) - self.assertRadii(PathThreadMilling.radiiMetricInternal(20, 19, 2, 0), (8.5, 9.1)) + '''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 metric internal radii with tool crest.''' - self.assertRadii(PathThreadMilling.radiiMetricInternal(20, 18, 2, 0.1), (8, 9.113397)) + '''Verify internal radii with tool crest.''' + self.assertRadii(PathThreadMilling.radiiInternal(20, 18, 2, 0.1), (8, 9.113397)) def test10(self): '''Verify thread passes.''' From b1c385f909f55006f3d26c886c1b3827ed4c818c Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Tue, 20 Aug 2019 19:58:42 -0700 Subject: [PATCH 05/14] Cleaned up properties --- src/Mod/Path/PathScripts/PathThreadMilling.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathThreadMilling.py b/src/Mod/Path/PathScripts/PathThreadMilling.py index f7d7fdb58b..2804de0daf 100644 --- a/src/Mod/Path/PathScripts/PathThreadMilling.py +++ b/src/Mod/Path/PathScripts/PathThreadMilling.py @@ -180,19 +180,20 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): return PathOp.FeatureBaseGeometry def initCircularHoleOperation(self, obj): - obj.addProperty("App::PropertyEnumeration", "ThreadOrientation", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread orientation")) + 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("App::Property", "Currently only internal")) + 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("App::Property", "Devfines which standard thread was chosen")) - obj.addProperty("App::PropertyLength", "MajorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's major diameter")) - obj.addProperty("App::PropertyLength", "MinorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's minor diameter")) - obj.addProperty("App::PropertyLength", "Pitch", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's pitch - used for metric threads")) - obj.addProperty("App::PropertyInteger", "TPI", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set thread's tpi - used for imperial threads")) - obj.addProperty("App::PropertyInteger", "ThreadFit", "Thread", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set how many passes are used to cut the thread")) - obj.addProperty("App::PropertyInteger", "Passes", "Operation", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set how many passes are used to cut the thread")) - obj.addProperty("App::PropertyEnumeration", "Direction", "Operation", QtCore.QT_TRANSLATE_NOOP("App::Property", "Direction of thread cutting operation")) - obj.addProperty("App::PropertyBool", "LeadInOut", "Operation", QtCore.QT_TRANSLATE_NOOP("App::Property", "Set to True to get lead in and lead out arcs at the start and end of the thread cut")) + 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 def threadStartDepth(self, obj): From 074ad40338c0c11bebb2f9d29251161e6250fc50 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 13 Nov 2020 08:25:30 -0800 Subject: [PATCH 06/14] Fixed merge issue --- src/Mod/Path/InitGui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index abd207945e..d1feaff6a5 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -99,6 +99,7 @@ class PathWorkbench (Workbench): extracmdlist = [] # modcmdmore = ["Path_Hop",] # remotecmdlist = ["Path_Remote"] + specialcmdlist = [] if PathPreferences.toolsReallyUseLegacyTools(): From b7a356cceb71847a8d98ef81129bcd10f031fb9a Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 13 Nov 2020 14:42:02 -0800 Subject: [PATCH 07/14] Catch exception if shape file does not include a thumbnail. --- src/Mod/Path/PathScripts/PathToolBit.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathToolBit.py b/src/Mod/Path/PathScripts/PathToolBit.py index a76150b597..9cd6f488de 100644 --- a/src/Mod/Path/PathScripts/PathToolBit.py +++ b/src/Mod/Path/PathScripts/PathToolBit.py @@ -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): From d5beaf806d61657d60dcd484f5b80bfd85ea5d18 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 13 Nov 2020 14:43:18 -0800 Subject: [PATCH 08/14] Filter all TCs for suitability for a given op. --- src/Mod/Path/PathScripts/PathOp.py | 10 +++++++++- src/Mod/Path/PathScripts/PathOpGui.py | 4 ++-- src/Mod/Path/PathScripts/PathUtils.py | 11 +++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathOp.py b/src/Mod/Path/PathScripts/PathOp.py index 24497f1a58..0a16a1361c 100644 --- a/src/Mod/Path/PathScripts/PathOp.py +++ b/src/Mod/Path/PathScripts/PathOp.py @@ -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 + + diff --git a/src/Mod/Path/PathScripts/PathOpGui.py b/src/Mod/Path/PathScripts/PathOpGui.py index b1643ed119..d0d5c22d83 100644 --- a/src/Mod/Path/PathScripts/PathOpGui.py +++ b/src/Mod/Path/PathScripts/PathOpGui.py @@ -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 diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index e78b1cfb2d..c53b7a2ab6 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -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 From aa2881e08e3325f27baffaa169be5248ed13f7df Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 13 Nov 2020 16:27:08 -0800 Subject: [PATCH 09/14] Use new ToolBits (and only those) for thread milling. --- src/Mod/Path/CMakeLists.txt | 1 + .../panels/PageOpThreadMillingEdit.ui | 33 ++------------ src/Mod/Path/PathScripts/PathThreadMilling.py | 42 +++++++++++------- .../Path/PathScripts/PathThreadMillingGui.py | 4 +- src/Mod/Path/Tools/Shape/thread-mill.fcstd | Bin 0 -> 16361 bytes 5 files changed, 33 insertions(+), 47 deletions(-) create mode 100644 src/Mod/Path/Tools/Shape/thread-mill.fcstd diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index 6c9d692156..bcfa177d88 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -176,6 +176,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 ) diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui index f67744b714..8de86b656a 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui @@ -6,7 +6,7 @@ 0 0 - 482 + 318 756 @@ -157,37 +157,10 @@ Tool Controller - - - - - Tool - - - - + + - - - - false - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - false - - - Crest - - - diff --git a/src/Mod/Path/PathScripts/PathThreadMilling.py b/src/Mod/Path/PathScripts/PathThreadMilling.py index 2804de0daf..3005e7d326 100644 --- a/src/Mod/Path/PathScripts/PathThreadMilling.py +++ b/src/Mod/Path/PathScripts/PathThreadMilling.py @@ -40,8 +40,8 @@ __author__ = "sliptonic (Brad Collette)" __url__ = "http://www.freecadweb.org" __doc__ = "Path thread milling operation." -PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) -PathLog.trackModule(PathLog.thisModule()) +PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) +#PathLog.trackModule(PathLog.thisModule()) # Qt translation handling def translate(context, text, disambig=None): @@ -196,6 +196,12 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): 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: @@ -264,7 +270,7 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): 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, self.tool.Diameter, 0): + 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 @@ -279,20 +285,22 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): def circularHoleExecute(self, obj, holes): PathLog.track() + if self.isToolSupported(obj, self.tool): + self.commandlist.append(Path.Command("(Begin Thread Milling)")) - 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 - (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) + # 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): @@ -305,6 +313,10 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp): 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 = [] diff --git a/src/Mod/Path/PathScripts/PathThreadMillingGui.py b/src/Mod/Path/PathScripts/PathThreadMillingGui.py index 9a3e55930e..61eecbeec7 100644 --- a/src/Mod/Path/PathScripts/PathThreadMillingGui.py +++ b/src/Mod/Path/PathScripts/PathThreadMillingGui.py @@ -38,8 +38,8 @@ __author__ = "sliptonic (Brad Collette)" __url__ = "http://www.freecadweb.org" __doc__ = "UI and Command for Path Thread Milling Operation." -PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) -PathLog.trackModule(PathLog.thisModule()) +PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) +#PathLog.trackModule(PathLog.thisModule()) def setupCombo(combo, selections): combo.clear() diff --git a/src/Mod/Path/Tools/Shape/thread-mill.fcstd b/src/Mod/Path/Tools/Shape/thread-mill.fcstd new file mode 100644 index 0000000000000000000000000000000000000000..ba9294fe6f6d7d5b07576577344fca86e42257f9 GIT binary patch literal 16361 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 literal 0 HcmV?d00001 From c0bdf89f4c7e2dcef31e060e4b26569ed97fdb87 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Fri, 27 Nov 2020 18:50:30 -0800 Subject: [PATCH 10/14] Added example thread cutter to default library --- src/Mod/Path/Tools/Bit/5mm-thread-cutter.fctb | 14 ++++++++++++++ src/Mod/Path/Tools/Library/Default.fctl | 4 ++++ 2 files changed, 18 insertions(+) create mode 100644 src/Mod/Path/Tools/Bit/5mm-thread-cutter.fctb diff --git a/src/Mod/Path/Tools/Bit/5mm-thread-cutter.fctb b/src/Mod/Path/Tools/Bit/5mm-thread-cutter.fctb new file mode 100644 index 0000000000..265978053b --- /dev/null +++ b/src/Mod/Path/Tools/Bit/5mm-thread-cutter.fctb @@ -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": {} +} \ No newline at end of file diff --git a/src/Mod/Path/Tools/Library/Default.fctl b/src/Mod/Path/Tools/Library/Default.fctl index b3b8ea23c4..60de98e08e 100644 --- a/src/Mod/Path/Tools/Library/Default.fctl +++ b/src/Mod/Path/Tools/Library/Default.fctl @@ -31,6 +31,10 @@ { "nr": 8, "path": "probe.fctb" + }, + { + "nr": 9, + "path": "5mm-thread-cutter.fctb" } ], "version": 1 From cc0ba34a4ce2c469d77851dee32d886bd9a248c2 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Sun, 29 Nov 2020 20:00:07 -0800 Subject: [PATCH 11/14] Added thread milling files to cmake --- src/Mod/Path/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index bcfa177d88..b25189233a 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -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 @@ -196,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 From 134fe23c8f6a99083a03799d57a559c252a1cfb3 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Tue, 1 Dec 2020 18:24:48 -0800 Subject: [PATCH 12/14] Fixed unit test breakage for py2 --- src/Mod/Path/PathScripts/PathThreadMilling.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathThreadMilling.py b/src/Mod/Path/PathScripts/PathThreadMilling.py index 3005e7d326..c54740f381 100644 --- a/src/Mod/Path/PathScripts/PathThreadMilling.py +++ b/src/Mod/Path/PathScripts/PathThreadMilling.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - # *************************************************************************** # * * # * Copyright (c) 2019 sliptonic * @@ -63,7 +62,7 @@ def radiiInternal(majorDia, minorDia, toolDia, toolCrest = None): 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, toolTip - toolDia / 2) + 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) From 9eebafbe4bc4c6f2ea2a8c2d4697afa5c5309948 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Tue, 1 Dec 2020 18:25:13 -0800 Subject: [PATCH 13/14] Added consistent encoding headers and fixed whitespaces --- src/Mod/Path/PathScripts/PathJobCmd.py | 1 + src/Mod/Path/PathScripts/PathPreferences.py | 1 - src/Mod/Path/PathScripts/PathSanity.py | 1 + src/Mod/Path/PathScripts/PathThreadMillingGui.py | 1 - src/Mod/Path/PathScripts/PostUtils.py | 1 + 5 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathJobCmd.py b/src/Mod/Path/PathScripts/PathJobCmd.py index ef92826533..0a9efcb765 100644 --- a/src/Mod/Path/PathScripts/PathJobCmd.py +++ b/src/Mod/Path/PathScripts/PathJobCmd.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2017 sliptonic * # * * diff --git a/src/Mod/Path/PathScripts/PathPreferences.py b/src/Mod/Path/PathScripts/PathPreferences.py index 6fe58570ef..6954c4b977 100644 --- a/src/Mod/Path/PathScripts/PathPreferences.py +++ b/src/Mod/Path/PathScripts/PathPreferences.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - # *************************************************************************** # * * # * Copyright (c) 2014 Yorik van Havre * diff --git a/src/Mod/Path/PathScripts/PathSanity.py b/src/Mod/Path/PathScripts/PathSanity.py index f69465c4e1..09664be146 100644 --- a/src/Mod/Path/PathScripts/PathSanity.py +++ b/src/Mod/Path/PathScripts/PathSanity.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # *************************************************************************** # * Copyright (c) 2016 sliptonic * # * * diff --git a/src/Mod/Path/PathScripts/PathThreadMillingGui.py b/src/Mod/Path/PathScripts/PathThreadMillingGui.py index 61eecbeec7..66acac9387 100644 --- a/src/Mod/Path/PathScripts/PathThreadMillingGui.py +++ b/src/Mod/Path/PathScripts/PathThreadMillingGui.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - # *************************************************************************** # * * # * Copyright (c) 2019 sliptonic * diff --git a/src/Mod/Path/PathScripts/PostUtils.py b/src/Mod/Path/PathScripts/PostUtils.py index 920894603b..06bd1914e0 100644 --- a/src/Mod/Path/PathScripts/PostUtils.py +++ b/src/Mod/Path/PathScripts/PostUtils.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- #*************************************************************************** #* Copyright (c) 2014 Yorik van Havre * #* * From 85cd4af06b71a0d15d27a6c559ad4de56659c8b5 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 3 Dec 2020 19:39:33 -0800 Subject: [PATCH 14/14] More py2 special handling for unit tests --- src/Mod/Path/PathScripts/PathThreadMilling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/PathThreadMilling.py b/src/Mod/Path/PathScripts/PathThreadMilling.py index c54740f381..7b15afbc36 100644 --- a/src/Mod/Path/PathScripts/PathThreadMilling.py +++ b/src/Mod/Path/PathScripts/PathThreadMilling.py @@ -67,7 +67,7 @@ def radiiInternal(majorDia, minorDia, toolDia, toolCrest = None): 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 = (major - minor) / count + dr = float(major - minor) / count return [major - dr * (count - (i + 1)) for i in range(count)] class _InternalThread(object):