Path: Add G84/G74 Tapping Operation (#8069)
This commit is contained in:
132
src/Mod/CAM/CAMTests/TestPathTapGenerator.py
Normal file
132
src/Mod/CAM/CAMTests/TestPathTapGenerator.py
Normal file
@@ -0,0 +1,132 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2021 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENSE 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 Part
|
||||
import Path
|
||||
import Path.Base.Generator.tapping as generator
|
||||
import CAMTests.PathTestUtils as PathTestUtils
|
||||
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
|
||||
|
||||
class TestPathTapGenerator(PathTestUtils.PathTestBase):
|
||||
def test00(self):
|
||||
"""Test Basic Tap Generator Return"""
|
||||
v1 = FreeCAD.Vector(0, 0, 10)
|
||||
v2 = FreeCAD.Vector(0, 0, 0)
|
||||
|
||||
e = Part.makeLine(v1, v2)
|
||||
|
||||
result = generator.generate(e)
|
||||
|
||||
self.assertTrue(type(result) is list)
|
||||
self.assertTrue(type(result[0]) is Path.Command)
|
||||
|
||||
command = result[0]
|
||||
|
||||
self.assertTrue(command.Name == "G84")
|
||||
self.assertTrue(command.Parameters["R"] == 10)
|
||||
self.assertTrue(command.Parameters["X"] == 0)
|
||||
self.assertTrue(command.Parameters["Y"] == 0)
|
||||
self.assertTrue(command.Parameters["Z"] == 0)
|
||||
|
||||
# repeat must be > 0
|
||||
args = {"edge": e, "repeat": 0}
|
||||
self.assertRaises(ValueError, generator.generate, **args)
|
||||
|
||||
# repeat must be integer
|
||||
args = {"edge": e, "repeat": 1.5}
|
||||
self.assertRaises(ValueError, generator.generate, **args)
|
||||
|
||||
def test10(self):
|
||||
"""Test edge alignment check"""
|
||||
v1 = FreeCAD.Vector(0, 10, 10)
|
||||
v2 = FreeCAD.Vector(0, 0, 0)
|
||||
e = Part.makeLine(v1, v2)
|
||||
self.assertRaises(ValueError, generator.generate, e)
|
||||
|
||||
v1 = FreeCAD.Vector(0, 0, 0)
|
||||
v2 = FreeCAD.Vector(0, 0, 10)
|
||||
e = Part.makeLine(v1, v2)
|
||||
|
||||
self.assertRaises(ValueError, generator.generate, e)
|
||||
|
||||
def test30(self):
|
||||
"""Test Basic Dwell Tap Generator Return"""
|
||||
v1 = FreeCAD.Vector(0, 0, 10)
|
||||
v2 = FreeCAD.Vector(0, 0, 0)
|
||||
|
||||
e = Part.makeLine(v1, v2)
|
||||
|
||||
result = generator.generate(e, dwelltime=0.5)
|
||||
|
||||
self.assertTrue(type(result) is list)
|
||||
self.assertTrue(type(result[0]) is Path.Command)
|
||||
|
||||
command = result[0]
|
||||
|
||||
self.assertTrue(command.Name == "G84")
|
||||
self.assertTrue(command.Parameters["P"] == 0.5)
|
||||
|
||||
# dwelltime should be a float
|
||||
args = {"edge": e, "dwelltime": 1}
|
||||
self.assertRaises(ValueError, generator.generate, **args)
|
||||
|
||||
def test40(self):
|
||||
"""Specifying retract height should set R parameter to specified value"""
|
||||
v1 = FreeCAD.Vector(0, 0, 10)
|
||||
v2 = FreeCAD.Vector(0, 0, 0)
|
||||
|
||||
e = Part.makeLine(v1, v2)
|
||||
|
||||
result = generator.generate(e, retractheight=20.0)
|
||||
|
||||
command = result[0]
|
||||
|
||||
self.assertTrue(command.Parameters["R"] == 20.0)
|
||||
|
||||
def test41(self):
|
||||
"""Not specifying retract height should set R parameter to Z position of start point"""
|
||||
v1 = FreeCAD.Vector(0, 0, 10)
|
||||
v2 = FreeCAD.Vector(0, 0, 0)
|
||||
|
||||
e = Part.makeLine(v1, v2)
|
||||
|
||||
result = generator.generate(e)
|
||||
|
||||
command = result[0]
|
||||
|
||||
self.assertTrue(command.Parameters["R"] == 10.0)
|
||||
|
||||
def test44(self):
|
||||
"""Non-float retract height should raise ValueError"""
|
||||
v1 = FreeCAD.Vector(0, 0, 10)
|
||||
v2 = FreeCAD.Vector(0, 0, 0)
|
||||
|
||||
e = Part.makeLine(v1, v2)
|
||||
|
||||
args = {"edge": e, "retractheight": 1}
|
||||
self.assertRaises(ValueError, generator.generate, **args)
|
||||
args = {"edge": e, "retractheight": "1"}
|
||||
self.assertRaises(ValueError, generator.generate, **args)
|
||||
@@ -142,6 +142,7 @@ SET(PathPythonPostScripts_SRCS
|
||||
Path/Post/scripts/comparams_post.py
|
||||
Path/Post/scripts/dxf_post.py
|
||||
Path/Post/scripts/dynapath_post.py
|
||||
Path/Post/scripts/dynapath_4060_post.py
|
||||
Path/Post/scripts/estlcam_post.py
|
||||
Path/Post/scripts/example_pre.py
|
||||
Path/Post/scripts/fablin_post.py
|
||||
@@ -195,6 +196,7 @@ SET(PathPythonOp_SRCS
|
||||
Path/Op/Slot.py
|
||||
Path/Op/Surface.py
|
||||
Path/Op/SurfaceSupport.py
|
||||
Path/Op/Tapping.py
|
||||
Path/Op/ThreadMilling.py
|
||||
Path/Op/Util.py
|
||||
Path/Op/Vcarve.py
|
||||
@@ -226,6 +228,7 @@ SET(PathPythonOpGui_SRCS
|
||||
Path/Op/Gui/Slot.py
|
||||
Path/Op/Gui/Stop.py
|
||||
Path/Op/Gui/Surface.py
|
||||
Path/Op/Gui/Tapping.py
|
||||
Path/Op/Gui/ThreadMilling.py
|
||||
Path/Op/Gui/Vcarve.py
|
||||
Path/Op/Gui/Waterline.py
|
||||
@@ -244,6 +247,7 @@ SET(PathPythonBaseGenerator_SRCS
|
||||
Path/Base/Generator/drill.py
|
||||
Path/Base/Generator/helix.py
|
||||
Path/Base/Generator/rotation.py
|
||||
Path/Base/Generator/tapping.py
|
||||
Path/Base/Generator/threadmilling.py
|
||||
Path/Base/Generator/toolchange.py
|
||||
)
|
||||
@@ -258,6 +262,7 @@ SET(Tools_SRCS
|
||||
)
|
||||
|
||||
SET(Tools_Bit_SRCS
|
||||
Tools/Bit/375-16_Tap.fctb
|
||||
Tools/Bit/45degree_chamfer.fctb
|
||||
Tools/Bit/5mm-thread-cutter.fctb
|
||||
Tools/Bit/5mm_Drill.fctb
|
||||
@@ -282,6 +287,7 @@ SET(Tools_Shape_SRCS
|
||||
Tools/Shape/endmill.fcstd
|
||||
Tools/Shape/probe.fcstd
|
||||
Tools/Shape/slittingsaw.fcstd
|
||||
Tools/Shape/tap.fcstd
|
||||
Tools/Shape/thread-mill.fcstd
|
||||
Tools/Shape/v-bit.fcstd
|
||||
)
|
||||
@@ -329,6 +335,7 @@ SET(Tests_SRCS
|
||||
CAMTests/TestPathRotationGenerator.py
|
||||
CAMTests/TestPathSetupSheet.py
|
||||
CAMTests/TestPathStock.py
|
||||
CAMTests/TestPathTapGenerator.py
|
||||
CAMTests/TestPathToolChangeGenerator.py
|
||||
CAMTests/TestPathThreadMilling.py
|
||||
CAMTests/TestPathThreadMillingGenerator.py
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
<file>icons/CAM_SimulatorGL.svg</file>
|
||||
<file>icons/CAM_Slot.svg</file>
|
||||
<file>icons/CAM_Stop.svg</file>
|
||||
<file>icons/CAM_Tapping.svg</file>
|
||||
<file>icons/CAM_ThreadMilling.svg</file>
|
||||
<file>icons/CAM_ToolBit.svg</file>
|
||||
<file>icons/CAM_ToolChange.svg</file>
|
||||
@@ -108,6 +109,7 @@
|
||||
<file>panels/PageOpProfileFullEdit.ui</file>
|
||||
<file>panels/PageOpSlotEdit.ui</file>
|
||||
<file>panels/PageOpSurfaceEdit.ui</file>
|
||||
<file>panels/PageOpTappingEdit.ui</file>
|
||||
<file>panels/PageOpThreadMillingEdit.ui</file>
|
||||
<file>panels/PageOpWaterlineEdit.ui</file>
|
||||
<file>panels/PageOpVcarveEdit.ui</file>
|
||||
|
||||
688
src/Mod/CAM/Gui/Resources/icons/CAM_Tapping.svg
Normal file
688
src/Mod/CAM/Gui/Resources/icons/CAM_Tapping.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 28 KiB |
145
src/Mod/CAM/Gui/Resources/panels/PageOpTappingEdit.ui
Normal file
145
src/Mod/CAM/Gui/Resources/panels/PageOpTappingEdit.ui
Normal file
@@ -0,0 +1,145 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>532</width>
|
||||
<height>351</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="0" colspan="3">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="4" column="4">
|
||||
<widget class="QLabel" name="dwellTimelabel">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Time</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" rowspan="2" colspan="2">
|
||||
<widget class="QCheckBox" name="dwellEnabled">
|
||||
<property name="text">
|
||||
<string>Dwell</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="6">
|
||||
<widget class="QComboBox" name="ExtraOffset">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Tap Tip</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2x Tap Tip</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="6">
|
||||
<widget class="Gui::QuantitySpinBox" name="dwellTime" native="true">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="4">
|
||||
<widget class="QLabel" name="Offsetlabel">
|
||||
<property name="text">
|
||||
<string>Extend Depth</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="coolantController">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The tool and its settings to be used for this operation.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Coolant Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>ToolController</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="toolController">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The tool and its settings to be used for this operation.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>toolController</tabstop>
|
||||
<tabstop>dwellEnabled</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -113,13 +113,13 @@ class CAMWorkbench(Workbench):
|
||||
twodopcmdlist = [
|
||||
"CAM_Profile",
|
||||
"CAM_Pocket_Shape",
|
||||
"CAM_Drilling",
|
||||
"CAM_MillFace",
|
||||
"CAM_Helix",
|
||||
"CAM_Adaptive",
|
||||
]
|
||||
threedopcmdlist = ["CAM_Pocket3D"]
|
||||
engravecmdlist = ["CAM_Engrave", "CAM_Deburr", "CAM_Vcarve"]
|
||||
drillingcmdlist = ["CAM_Drilling", "CAM_Tapping"]
|
||||
modcmdlist = ["CAM_OperationCopy", "CAM_Array", "CAM_SimpleCopy"]
|
||||
dressupcmdlist = [
|
||||
"CAM_DressupAxisMap",
|
||||
@@ -145,7 +145,14 @@ class CAMWorkbench(Workbench):
|
||||
QT_TRANSLATE_NOOP("CAM_EngraveTools", "Engraving Operations"),
|
||||
),
|
||||
)
|
||||
|
||||
drillingcmdgroup = ["CAM_DrillingTools"]
|
||||
FreeCADGui.addCommand(
|
||||
"CAM_DrillingTools",
|
||||
PathCommandGroup(
|
||||
drillingcmdlist,
|
||||
QT_TRANSLATE_NOOP("CAM_DrillingTools", "Drilling Operations"),
|
||||
),
|
||||
)
|
||||
threedcmdgroup = threedopcmdlist
|
||||
if Path.Preferences.experimentalFeaturesEnabled():
|
||||
prepcmdlist.append("CAM_Shape")
|
||||
@@ -196,7 +203,7 @@ class CAMWorkbench(Workbench):
|
||||
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Tool Commands"), toolcmdlist)
|
||||
self.appendToolbar(
|
||||
QT_TRANSLATE_NOOP("Workbench", "New Operations"),
|
||||
twodopcmdlist + engravecmdgroup + threedcmdgroup,
|
||||
twodopcmdlist + drillingcmdgroup + engravecmdgroup + threedcmdgroup,
|
||||
)
|
||||
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Path Modification"), modcmdlist)
|
||||
if extracmdlist:
|
||||
@@ -210,6 +217,7 @@ class CAMWorkbench(Workbench):
|
||||
+ toolbitcmdlist
|
||||
+ ["Separator"]
|
||||
+ twodopcmdlist
|
||||
+ drillingcmdlist
|
||||
+ engravecmdlist
|
||||
+ ["Separator"]
|
||||
+ threedopcmdlist
|
||||
|
||||
112
src/Mod/CAM/Path/Base/Generator/tapping.py
Normal file
112
src/Mod/CAM/Path/Base/Generator/tapping.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2021 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * Copyright (c) 2023 luvtofish *
|
||||
# * *
|
||||
# * 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 LICENSE 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 Path
|
||||
import numpy
|
||||
|
||||
__title__ = "Tapping Path Generator"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Generates the Tapping toolpath for a single spotshape"
|
||||
__contributors__ = "luvtofish (Dan Henderson)"
|
||||
|
||||
|
||||
if False:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
|
||||
def generate(edge, dwelltime=0.0, repeat=1, retractheight=None, righthand=True):
|
||||
"""
|
||||
Generates Gcode for tapping a single hole.
|
||||
|
||||
Takes as input an edge. It assumes the edge is trivial with just two vectors.
|
||||
The edge must be aligned with the Z axes (Vector(0,0,1)) or it is an error.
|
||||
|
||||
The first vertex of the edge will be the startpoint
|
||||
The second vertex of the edge will be the endpoint.
|
||||
All other vertices are ignored.
|
||||
|
||||
additionally, you can pass in a dwelltime,and repeat value.
|
||||
|
||||
These will result in appropriate G74 and G84 codes.
|
||||
|
||||
"""
|
||||
startPoint = edge.Vertexes[0].Point
|
||||
endPoint = edge.Vertexes[1].Point
|
||||
|
||||
Path.Log.debug(startPoint)
|
||||
Path.Log.debug(endPoint)
|
||||
|
||||
Path.Log.debug(numpy.isclose(startPoint.sub(endPoint).x, 0, rtol=1e-05, atol=1e-06))
|
||||
Path.Log.debug(numpy.isclose(startPoint.sub(endPoint).y, 0, rtol=1e-05, atol=1e-06))
|
||||
Path.Log.debug(endPoint)
|
||||
|
||||
if repeat < 1:
|
||||
raise ValueError("repeat must be 1 or greater")
|
||||
|
||||
if not type(repeat) is int:
|
||||
raise ValueError("repeat value must be an integer")
|
||||
|
||||
if not type(dwelltime) is float:
|
||||
raise ValueError("dwelltime must be a float")
|
||||
|
||||
if retractheight is not None and not type(retractheight) is float:
|
||||
raise ValueError("retractheight must be a float")
|
||||
|
||||
if not (
|
||||
numpy.isclose(startPoint.sub(endPoint).x, 0, rtol=1e-05, atol=1e-06)
|
||||
and (numpy.isclose(startPoint.sub(endPoint).y, 0, rtol=1e-05, atol=1e-06))
|
||||
):
|
||||
raise ValueError("edge is not aligned with Z axis")
|
||||
|
||||
if startPoint.z < endPoint.z:
|
||||
raise ValueError("start point is below end point")
|
||||
|
||||
cmdParams = {}
|
||||
cmdParams["X"] = startPoint.x
|
||||
cmdParams["Y"] = startPoint.y
|
||||
cmdParams["Z"] = endPoint.z
|
||||
cmdParams["R"] = retractheight if retractheight is not None else startPoint.z
|
||||
|
||||
if repeat < 1:
|
||||
raise ValueError("repeat must be 1 or greater")
|
||||
|
||||
if not type(repeat) is int:
|
||||
raise ValueError("repeat value must be an integer")
|
||||
|
||||
if repeat > 1:
|
||||
cmdParams["L"] = repeat
|
||||
|
||||
if dwelltime > 0.0:
|
||||
cmdParams["P"] = dwelltime
|
||||
|
||||
# Check if tool is lefthand or righthand, set appropriate G-code
|
||||
if not (righthand):
|
||||
cmd = "G74"
|
||||
else:
|
||||
cmd = "G84"
|
||||
|
||||
return [Path.Command(cmd, cmdParams)]
|
||||
@@ -73,6 +73,7 @@ def Startup():
|
||||
from Path.Op.Gui import SimpleCopy
|
||||
from Path.Op.Gui import Slot
|
||||
from Path.Op.Gui import Stop
|
||||
from Path.Op.Gui import Tapping
|
||||
from Path.Op.Gui import ThreadMilling
|
||||
from Path.Op.Gui import Vcarve
|
||||
from Path.Post import Command
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# * 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. *
|
||||
# * for detail see the LICENTE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
@@ -29,7 +29,7 @@ import Path
|
||||
import Path.Base.Drillable as Drillable
|
||||
import math
|
||||
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
|
||||
|
||||
@@ -132,6 +132,18 @@ class DRILLGate(PathBaseGate):
|
||||
return Drillable.isDrillable(shape, subobj, vector=None, allowPartial=True)
|
||||
|
||||
|
||||
class TAPGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub):
|
||||
Path.Log.debug("obj: {} sub: {}".format(obj, sub))
|
||||
if not hasattr(obj, "Shape"):
|
||||
return False
|
||||
shape = obj.Shape
|
||||
subobj = shape.getElement(sub)
|
||||
if subobj.ShapeType not in ["Edge", "Face"]:
|
||||
return False
|
||||
return Drillable.isDrillable(shape, subobj, vector=None)
|
||||
|
||||
|
||||
class FACEGate(PathBaseGate):
|
||||
def allow(self, doc, obj, sub):
|
||||
isFace = False
|
||||
@@ -270,6 +282,12 @@ def drillselect():
|
||||
FreeCAD.Console.PrintWarning("Drilling Select Mode\n")
|
||||
|
||||
|
||||
def tapselect():
|
||||
FreeCADGui.Selection.addSelectionGate(TAPGate())
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Tapping Select Mode\n")
|
||||
|
||||
|
||||
def engraveselect():
|
||||
FreeCADGui.Selection.addSelectionGate(ENGRAVEGate())
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
@@ -349,6 +367,7 @@ def select(op):
|
||||
opsel["Contour"] = contourselect # deprecated
|
||||
opsel["Deburr"] = chamferselect
|
||||
opsel["Drilling"] = drillselect
|
||||
opsel["Tapping"] = tapselect
|
||||
opsel["Engrave"] = engraveselect
|
||||
opsel["Helix"] = drillselect
|
||||
opsel["MillFace"] = pocketselect
|
||||
|
||||
187
src/Mod/CAM/Path/Op/Gui/Tapping.py
Normal file
187
src/Mod/CAM/Path/Op/Gui/Tapping.py
Normal file
@@ -0,0 +1,187 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2017 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Path
|
||||
import Path.Base.Gui.Util as PathGuiUtil
|
||||
import Path.Op.Tapping as PathTapping
|
||||
import Path.Op.Gui.Base as PathOpGui
|
||||
import Path.Op.Gui.CircularHoleBase as PathCircularHoleBaseGui
|
||||
import PathGui
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
__title__ = "Path Tapping Operation UI."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "UI and Command for Path Tapping Operation."
|
||||
__contributors__ = "luvtofish"
|
||||
|
||||
if False:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
"""Controller for the tapping operation's page"""
|
||||
|
||||
def initPage(self, obj):
|
||||
# self.peckDepthSpinBox = PathGuiUtil.QuantitySpinBox(
|
||||
# self.form.peckDepth, obj, "PeckDepth"
|
||||
# )
|
||||
# self.peckRetractSpinBox = PathGuiUtil.QuantitySpinBox(
|
||||
# self.form.peckRetractHeight, obj, "RetractHeight"
|
||||
# )
|
||||
self.dwellTimeSpinBox = PathGuiUtil.QuantitySpinBox(self.form.dwellTime, obj, "DwellTime")
|
||||
|
||||
# self.form.chipBreakEnabled.setEnabled(False)
|
||||
|
||||
def registerSignalHandlers(self, obj):
|
||||
# self.form.peckEnabled.toggled.connect(self.form.peckDepth.setEnabled)
|
||||
# self.form.peckEnabled.toggled.connect(self.form.dwellEnabled.setDisabled)
|
||||
# self.form.peckEnabled.toggled.connect(self.setChipBreakControl)
|
||||
|
||||
self.form.dwellEnabled.toggled.connect(self.form.dwellTime.setEnabled)
|
||||
self.form.dwellEnabled.toggled.connect(self.form.dwellTimelabel.setEnabled)
|
||||
# self.form.dwellEnabled.toggled.connect(self.form.peckEnabled.setDisabled)
|
||||
# self.form.dwellEnabled.toggled.connect(self.setChipBreakControl)
|
||||
|
||||
# self.form.peckRetractHeight.setEnabled(True)
|
||||
# self.form.retractLabel.setEnabled(True)
|
||||
|
||||
# if self.form.peckEnabled.isChecked():
|
||||
# self.form.dwellEnabled.setEnabled(False)
|
||||
# self.form.peckDepth.setEnabled(True)
|
||||
# self.form.peckDepthLabel.setEnabled(True)
|
||||
# self.form.chipBreakEnabled.setEnabled(True)
|
||||
# elif self.form.dwellEnabled.isChecked():
|
||||
if self.form.dwellEnabled.isChecked():
|
||||
# self.form.peckEnabled.setEnabled(False)
|
||||
self.form.dwellTime.setEnabled(True)
|
||||
self.form.dwellTimelabel.setEnabled(True)
|
||||
|
||||
# self.form.chipBreakEnabled.setEnabled(False)
|
||||
# else:
|
||||
# self.form.chipBreakEnabled.setEnabled(False)
|
||||
|
||||
# def setChipBreakControl(self):
|
||||
# self.form.chipBreakEnabled.setEnabled(self.form.peckEnabled.isChecked())
|
||||
|
||||
def getForm(self):
|
||||
"""getForm() ... return UI"""
|
||||
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpTappingEdit.ui")
|
||||
|
||||
comboToPropertyMap = [("ExtraOffset", "ExtraOffset")]
|
||||
enumTups = PathTapping.ObjectTapping.propertyEnumerations(dataType="raw")
|
||||
self.populateCombobox(form, enumTups, comboToPropertyMap)
|
||||
|
||||
return form
|
||||
|
||||
def updateQuantitySpinBoxes(self, index=None):
|
||||
# self.peckDepthSpinBox.updateSpinBox()
|
||||
# self.peckRetractSpinBox.updateSpinBox()
|
||||
self.dwellTimeSpinBox.updateSpinBox()
|
||||
|
||||
def getFields(self, obj):
|
||||
"""setFields(obj) ... update obj's properties with values from the UI"""
|
||||
Path.Log.track()
|
||||
# self.peckDepthSpinBox.updateProperty()
|
||||
# self.peckRetractSpinBox.updateProperty()
|
||||
self.dwellTimeSpinBox.updateProperty()
|
||||
|
||||
if obj.DwellEnabled != self.form.dwellEnabled.isChecked():
|
||||
obj.DwellEnabled = self.form.dwellEnabled.isChecked()
|
||||
# if obj.PeckEnabled != self.form.peckEnabled.isChecked():
|
||||
# obj.PeckEnabled = self.form.peckEnabled.isChecked()
|
||||
# if obj.chipBreakEnabled != self.form.chipBreakEnabled.isChecked():
|
||||
# obj.chipBreakEnabled = self.form.chipBreakEnabled.isChecked()
|
||||
if obj.ExtraOffset != str(self.form.ExtraOffset.currentData()):
|
||||
obj.ExtraOffset = str(self.form.ExtraOffset.currentData())
|
||||
|
||||
self.updateToolController(obj, self.form.toolController)
|
||||
self.updateCoolant(obj, self.form.coolantController)
|
||||
|
||||
def setFields(self, obj):
|
||||
"""setFields(obj) ... update UI with obj properties' values"""
|
||||
Path.Log.track()
|
||||
self.updateQuantitySpinBoxes()
|
||||
|
||||
if obj.DwellEnabled:
|
||||
self.form.dwellEnabled.setCheckState(QtCore.Qt.Checked)
|
||||
else:
|
||||
self.form.dwellEnabled.setCheckState(QtCore.Qt.Unchecked)
|
||||
|
||||
# if obj.PeckEnabled:
|
||||
# self.form.peckEnabled.setCheckState(QtCore.Qt.Checked)
|
||||
# else:
|
||||
# self.form.peckEnabled.setCheckState(QtCore.Qt.Unchecked)
|
||||
# self.form.chipBreakEnabled.setEnabled(False)
|
||||
|
||||
# if obj.chipBreakEnabled:
|
||||
# self.form.chipBreakEnabled.setCheckState(QtCore.Qt.Checked)
|
||||
# else:
|
||||
# self.form.chipBreakEnabled.setCheckState(QtCore.Qt.Unchecked)
|
||||
|
||||
self.selectInComboBox(obj.ExtraOffset, self.form.ExtraOffset)
|
||||
|
||||
self.setupToolController(obj, self.form.toolController)
|
||||
self.setupCoolant(obj, self.form.coolantController)
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
"""getSignalsForUpdate(obj) ... return list of signals which cause the receiver to update the model"""
|
||||
signals = []
|
||||
|
||||
# signals.append(self.form.peckRetractHeight.editingFinished)
|
||||
# signals.append(self.form.peckDepth.editingFinished)
|
||||
signals.append(self.form.dwellTime.editingFinished)
|
||||
signals.append(self.form.dwellEnabled.stateChanged)
|
||||
# signals.append(self.form.peckEnabled.stateChanged)
|
||||
# signals.append(self.form.chipBreakEnabled.stateChanged)
|
||||
signals.append(self.form.toolController.currentIndexChanged)
|
||||
signals.append(self.form.coolantController.currentIndexChanged)
|
||||
signals.append(self.form.ExtraOffset.currentIndexChanged)
|
||||
|
||||
return signals
|
||||
|
||||
|
||||
# def updateData(self, obj, prop):
|
||||
# if prop in ["PeckDepth", "RetractHeight"] and not prop in ["Base", "Disabled"]:
|
||||
# self.updateQuantitySpinBoxes()
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"Tapping",
|
||||
PathTapping.Create,
|
||||
TaskPanelOpPage,
|
||||
"CAM_Tapping",
|
||||
QtCore.QT_TRANSLATE_NOOP("CAM_Tapping", "Tapping"),
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"CAM_Tapping",
|
||||
"Creates a Tapping toolpath from the features of a base object",
|
||||
),
|
||||
PathTapping.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathTappingGui... done\n")
|
||||
289
src/Mod/CAM/Path/Op/Tapping.py
Normal file
289
src/Mod/CAM/Path/Op/Tapping.py
Normal file
@@ -0,0 +1,289 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * Copyright (c) 2020 Schildkroet *
|
||||
# * Copyright (c) 2023 luvtofish *
|
||||
# * *
|
||||
# * 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 LICENSE 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 Part
|
||||
import Path
|
||||
import Path.Base.FeedRate as PathFeedRate
|
||||
import Path.Base.Generator.tapping as tapping
|
||||
import Path.Base.MachineState as PathMachineState
|
||||
import Path.Op.Base as PathOp
|
||||
import Path.Op.CircularHoleBase as PathCircularHoleBase
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
__title__ = "Path Tapping Operation"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Path Tapping operation."
|
||||
__contributors__ = "luvtofish (Dan Henderson)"
|
||||
|
||||
if False:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class ObjectTapping(PathCircularHoleBase.ObjectOp):
|
||||
"""Proxy object for Tapping operation."""
|
||||
|
||||
@classmethod
|
||||
def propertyEnumerations(self, dataType="data"):
|
||||
"""helixOpPropertyEnumerations(dataType="data")... return property enumeration lists of specified dataType.
|
||||
Args:
|
||||
dataType = 'data', 'raw', 'translated'
|
||||
Notes:
|
||||
'data' is list of internal string literals used in code
|
||||
'raw' is list of (translated_text, data_string) tuples
|
||||
'translated' is list of translated string literals
|
||||
"""
|
||||
|
||||
# Enumeration lists for App::PropertyEnumeration properties
|
||||
enums = {
|
||||
"ReturnLevel": [
|
||||
(translate("CAM_Tapping", "G98"), "G98"),
|
||||
(translate("CAM_Tapping", "G99"), "G99"),
|
||||
], # How high to retract after a tapping move
|
||||
"ExtraOffset": [
|
||||
(translate("CAM_Tapping", "None"), "None"),
|
||||
(translate("CAM_Tapping", "Drill Tip"), "Drill Tip"),
|
||||
(translate("CAM_Tapping", "2x Drill Tip"), "2x Drill Tip"),
|
||||
], # extra drilling depth to clear tap taper
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
Path.Log.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
Path.Log.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def circularHoleFeatures(self, obj):
|
||||
"""circularHoleFeatures(obj) ... tapping works on anything, turn on all Base geometries and Locations."""
|
||||
return PathOp.FeatureBaseGeometry | PathOp.FeatureLocations | PathOp.FeatureCoolant
|
||||
|
||||
def initCircularHoleOperation(self, obj):
|
||||
"""initCircularHoleOperation(obj) ... add tapping specific properties to obj."""
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"DwellTime",
|
||||
"Tap",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The time to dwell at bottom of tapping cycle"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"DwellEnabled",
|
||||
"Tap",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Enable dwell"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"AddTipLength",
|
||||
"Tap",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Calculate the tip length and subtract from final depth",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"ReturnLevel",
|
||||
"Tap",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Controls how tool retracts Default=G98"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"RetractHeight",
|
||||
"Tap",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The height where feed starts and height during retract tool when path is finished while in a peck operation",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"ExtraOffset",
|
||||
"Tap",
|
||||
QT_TRANSLATE_NOOP("App::Property", "How far the tap depth is extended"),
|
||||
)
|
||||
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
def circularHoleExecute(self, obj, holes):
|
||||
"""circularHoleExecute(obj, holes) ... generate tapping operation for each hole in holes."""
|
||||
Path.Log.track()
|
||||
machine = PathMachineState.MachineState()
|
||||
|
||||
if not hasattr(obj.ToolController.Tool, "Pitch") or not hasattr(
|
||||
obj.ToolController.Tool, "TPI"
|
||||
):
|
||||
Path.Log.error(
|
||||
translate(
|
||||
"Path_Tapping",
|
||||
"Tapping Operation requires a Tap tool with Pitch or TPI",
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
self.commandlist.append(Path.Command("(Begin Tapping)"))
|
||||
|
||||
# rapid to clearance height
|
||||
command = Path.Command("G0", {"Z": obj.ClearanceHeight.Value})
|
||||
machine.addCommand(command)
|
||||
self.commandlist.append(command)
|
||||
|
||||
self.commandlist.append(Path.Command("G90")) # Absolute distance mode
|
||||
|
||||
# Calculate offsets to add to target edge
|
||||
endoffset = 0.0
|
||||
if obj.ExtraOffset == "Drill Tip":
|
||||
endoffset = PathUtils.drillTipLength(self.tool)
|
||||
elif obj.ExtraOffset == "2x Drill Tip":
|
||||
endoffset = PathUtils.drillTipLength(self.tool) * 2
|
||||
|
||||
# http://linuxcnc.org/docs/html/gcode/g-code.html#gcode:g98-g99
|
||||
self.commandlist.append(Path.Command(obj.ReturnLevel))
|
||||
|
||||
holes = PathUtils.sort_locations(holes, ["x", "y"])
|
||||
|
||||
# This section is technical debt. The computation of the
|
||||
# target shapes should be factored out for re-use.
|
||||
# This will likely mean refactoring upstream CircularHoleBase to pass
|
||||
# spot shapes instead of holes.
|
||||
|
||||
startHeight = obj.StartDepth.Value + self.job.SetupSheet.SafeHeightOffset.Value
|
||||
|
||||
edgelist = []
|
||||
for hole in holes:
|
||||
v1 = FreeCAD.Vector(hole["x"], hole["y"], obj.StartDepth.Value)
|
||||
v2 = FreeCAD.Vector(hole["x"], hole["y"], obj.FinalDepth.Value - endoffset)
|
||||
edgelist.append(Part.makeLine(v1, v2))
|
||||
|
||||
# iterate the edgelist and generate gcode
|
||||
for edge in edgelist:
|
||||
|
||||
Path.Log.debug(edge)
|
||||
|
||||
# move to hole location
|
||||
|
||||
startPoint = edge.Vertexes[0].Point
|
||||
|
||||
command = Path.Command(
|
||||
"G0", {"X": startPoint.x, "Y": startPoint.y, "Z": startHeight}
|
||||
) # DLH added Z offset to fix issue with Dynapath Control.
|
||||
self.commandlist.append(command)
|
||||
machine.addCommand(command)
|
||||
|
||||
# command = Path.Command("G0", {"Z": startHeight})
|
||||
# self.commandlist.append(command)
|
||||
# machine.addCommand(command)
|
||||
|
||||
# command = Path.Command("G1", {"Z": obj.StartDepth.Value})
|
||||
# self.commandlist.append(command)
|
||||
# machine.addCommand(command)
|
||||
|
||||
# Technical Debt: We are assuming the edges are aligned.
|
||||
# This assumption should be corrected and the necessary rotations
|
||||
# performed to align the edge with the Z axis for drilling
|
||||
|
||||
# Perform tapping
|
||||
dwelltime = obj.DwellTime if obj.DwellEnabled else 0.0
|
||||
repeat = 1 # technical debt: Add a repeat property for user control
|
||||
|
||||
# Get attribute from obj.tool, assign default and set to bool for passing to generate
|
||||
isRightHand = getattr(obj.ToolController.Tool, "Rotation", "Right Hand") == "Right Hand"
|
||||
|
||||
try:
|
||||
tappingcommands = tapping.generate(
|
||||
edge, dwelltime, repeat, obj.RetractHeight.Value, isRightHand
|
||||
)
|
||||
|
||||
except ValueError as e: # any targets that fail the generator are ignored
|
||||
Path.Log.info(e)
|
||||
continue
|
||||
|
||||
for command in tappingcommands:
|
||||
self.commandlist.append(command)
|
||||
machine.addCommand(command)
|
||||
|
||||
# Cancel canned tapping cycle
|
||||
self.commandlist.append(Path.Command("G80"))
|
||||
# command = Path.Command("G0", {"Z": obj.SafeHeight.Value}) DLH- Not needed, adds unnecessary move to Z SafeHeight.
|
||||
# self.commandlist.append(command)
|
||||
# machine.addCommand(command) DLH - Not needed.
|
||||
|
||||
# Apply feed rates to commands
|
||||
PathFeedRate.setFeedRate(self.commandlist, obj.ToolController)
|
||||
|
||||
def opSetDefaultValues(self, obj, job):
|
||||
"""opSetDefaultValues(obj, job) ... set default value for RetractHeight"""
|
||||
obj.ExtraOffset = "None"
|
||||
|
||||
if hasattr(job.SetupSheet, "RetractHeight"):
|
||||
obj.RetractHeight = job.SetupSheet.RetractHeight
|
||||
elif self.applyExpression(obj, "RetractHeight", "StartDepth+SetupSheet.SafeHeightOffset"):
|
||||
if not job:
|
||||
obj.RetractHeight = 10
|
||||
else:
|
||||
obj.RetractHeight.Value = obj.StartDepth.Value + 1.0
|
||||
|
||||
if hasattr(job.SetupSheet, "DwellTime"):
|
||||
obj.DwellTime = job.SetupSheet.DwellTime
|
||||
else:
|
||||
obj.DwellTime = 1
|
||||
|
||||
|
||||
def SetupProperties():
|
||||
setup = []
|
||||
setup.append("DwellTime")
|
||||
setup.append("DwellEnabled")
|
||||
setup.append("AddTipLength")
|
||||
setup.append("ReturnLevel")
|
||||
setup.append("ExtraOffset")
|
||||
setup.append("RetractHeight")
|
||||
return setup
|
||||
|
||||
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
"""Create(name) ... Creates and returns a Tapping operation."""
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
|
||||
obj.Proxy = ObjectTapping(obj, name, parentJob)
|
||||
if obj.Proxy:
|
||||
obj.Proxy.findAllHoles(obj)
|
||||
|
||||
return obj
|
||||
@@ -58,6 +58,7 @@ from CAMTests.TestPathPropertyBag import TestPathPropertyBag
|
||||
from CAMTests.TestPathRotationGenerator import TestPathRotationGenerator
|
||||
from CAMTests.TestPathSetupSheet import TestPathSetupSheet
|
||||
from CAMTests.TestPathStock import TestPathStock
|
||||
from CAMTests.TestPathTapGenerator import TestPathTapGenerator
|
||||
from CAMTests.TestPathThreadMilling import TestPathThreadMilling
|
||||
from CAMTests.TestPathThreadMillingGenerator import TestPathThreadMillingGenerator
|
||||
from CAMTests.TestPathToolBit import TestPathToolBit
|
||||
@@ -110,6 +111,7 @@ False if TestPathPropertyBag.__name__ else True
|
||||
False if TestPathRotationGenerator.__name__ else True
|
||||
False if TestPathSetupSheet.__name__ else True
|
||||
False if TestPathStock.__name__ else True
|
||||
False if TestPathTapGenerator.__name__ else True
|
||||
False if TestPathThreadMilling.__name__ else True
|
||||
False if TestPathThreadMillingGenerator.__name__ else True
|
||||
False if TestPathToolBit.__name__ else True
|
||||
|
||||
19
src/Mod/CAM/Tools/Bit/375-16_Tap.fctb
Normal file
19
src/Mod/CAM/Tools/Bit/375-16_Tap.fctb
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "375-16_Tap",
|
||||
"shape": "tap.fcstd",
|
||||
"parameter": {
|
||||
"Coating": "None",
|
||||
"CuttingEdgeLength": "1.063 \"",
|
||||
"Diameter": "0.375 \"",
|
||||
"Flutes": "3",
|
||||
"Length": "2.500 \"",
|
||||
"Pitch": "0.000 in",
|
||||
"Rotation": "Right Hand",
|
||||
"ShankDiameter": "0.250 \"",
|
||||
"TPI": "16",
|
||||
"TipAngle": "90.000 \u00b0",
|
||||
"Type": "HSS"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
BIN
src/Mod/CAM/Tools/Shape/tap.fcstd
Normal file
BIN
src/Mod/CAM/Tools/Shape/tap.fcstd
Normal file
Binary file not shown.
Reference in New Issue
Block a user