Merge pull request #5421 from sliptonic/bug/translationEngrave
[PATH] translation cleanup (engrave, millface, pocket)
This commit is contained in:
@@ -62,26 +62,6 @@
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Specify if the facing should be restricted by the actual shape of the selected face (or the part if no face is selected), or if the bounding box should be faced off.</p><p>The latter can be used to face of the entire stock area to ensure uniform heights for the following operations.</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Boundbox</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Face Region</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Perimeter</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Stock</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
|
||||
@@ -112,7 +112,7 @@ class PathWorkbench(Workbench):
|
||||
"Path_Helix",
|
||||
"Path_Adaptive",
|
||||
]
|
||||
threedopcmdlist = ["Path_Pocket_3D"]
|
||||
threedopcmdlist = ["Path_Pocket3D"]
|
||||
engravecmdlist = ["Path_Engrave", "Path_Deburr", "Path_Vcarve"]
|
||||
modcmdlist = ["Path_OperationCopy", "Path_Array", "Path_SimpleCopy"]
|
||||
dressupcmdlist = [
|
||||
@@ -141,7 +141,7 @@ class PathWorkbench(Workbench):
|
||||
FreeCADGui.addCommand(
|
||||
"Path_EngraveTools",
|
||||
PathCommandGroup(
|
||||
engravecmdlist, QT_TRANSLATE_NOOP("Path", "Engraving Operations")
|
||||
engravecmdlist, QT_TRANSLATE_NOOP("Path_EngraveTools", "Engraving Operations")
|
||||
),
|
||||
)
|
||||
|
||||
@@ -165,7 +165,7 @@ class PathWorkbench(Workbench):
|
||||
"Path_3dTools",
|
||||
PathCommandGroup(
|
||||
threedopcmdlist,
|
||||
QT_TRANSLATE_NOOP("Path", "3D Operations"),
|
||||
QT_TRANSLATE_NOOP("Path_3dTools", "3D Operations"),
|
||||
),
|
||||
)
|
||||
except ImportError:
|
||||
|
||||
@@ -27,23 +27,21 @@ import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
__doc__ = "Class and implementation of Path Engrave operation"
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
__doc__ = "Class and implementation of Path Engrave operation"
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
class ObjectEngrave(PathEngraveBase.ObjectOp):
|
||||
"""Proxy class for Engrave operation."""
|
||||
@@ -69,8 +67,8 @@ class ObjectEngrave(PathEngraveBase.ObjectOp):
|
||||
"App::PropertyLinkList",
|
||||
"BaseShapes",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathEngrave", "Additional base objects to be engraved"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Additional base objects to be engraved"
|
||||
),
|
||||
)
|
||||
obj.setEditorMode("BaseShapes", 2) # hide
|
||||
@@ -79,8 +77,8 @@ class ObjectEngrave(PathEngraveBase.ObjectOp):
|
||||
"App::PropertyLink",
|
||||
"BaseObject",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathEngrave", "Additional base objects to be engraved"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Additional base objects to be engraved"
|
||||
),
|
||||
)
|
||||
obj.setEditorMode("BaseObject", 2) # hide
|
||||
@@ -91,8 +89,8 @@ class ObjectEngrave(PathEngraveBase.ObjectOp):
|
||||
"App::PropertyInteger",
|
||||
"StartVertex",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathEngrave", "The vertex index to start the path from"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The vertex index to start the path from"
|
||||
),
|
||||
)
|
||||
self.setupAdditionalProperties(obj)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
import Path
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
@@ -27,24 +28,21 @@ import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathOpTools as PathOpTools
|
||||
import copy
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
DraftGeomUtils = LazyLoader('DraftGeomUtils', globals(), 'DraftGeomUtils')
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
__doc__ = "Base class for all ops in the engrave family."
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
# lazily loaded modules
|
||||
DraftGeomUtils = LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils")
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
class ObjectOp(PathOp.ObjectOp):
|
||||
'''Proxy base class for engrave operations.'''
|
||||
"""Proxy base class for engrave operations."""
|
||||
|
||||
def getZValues(self, obj):
|
||||
zValues = []
|
||||
@@ -62,47 +60,79 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
return zValues
|
||||
|
||||
def buildpathocc(self, obj, wires, zValues, relZ=False, forward=True, start_idx=0):
|
||||
'''buildpathocc(obj, wires, zValues, relZ=False) ... internal helper function to generate engraving commands.'''
|
||||
"""buildpathocc(obj, wires, zValues, relZ=False) ... internal helper function to generate engraving commands."""
|
||||
PathLog.track(obj.Label, len(wires), zValues)
|
||||
|
||||
for wire in wires:
|
||||
offset = wire
|
||||
|
||||
# reorder the wire
|
||||
if hasattr(obj, 'StartVertex'):
|
||||
if hasattr(obj, "StartVertex"):
|
||||
start_idx = obj.StartVertex
|
||||
|
||||
edges = copy.copy(PathOpTools.orientWire(offset, forward).Edges)
|
||||
edges = Part.sortEdges(edges)[0];
|
||||
edges = Part.sortEdges(edges)[0]
|
||||
|
||||
last = None
|
||||
|
||||
for z in zValues:
|
||||
PathLog.debug(z)
|
||||
if last:
|
||||
self.appendCommand(Path.Command('G1', {'X': last.x, 'Y': last.y, 'Z': last.z}), z, relZ, self.vertFeed)
|
||||
self.appendCommand(
|
||||
Path.Command("G1", {"X": last.x, "Y": last.y, "Z": last.z}),
|
||||
z,
|
||||
relZ,
|
||||
self.vertFeed,
|
||||
)
|
||||
|
||||
first = True
|
||||
if start_idx > len(edges)-1:
|
||||
start_idx = len(edges)-1
|
||||
if start_idx > len(edges) - 1:
|
||||
start_idx = len(edges) - 1
|
||||
|
||||
edges = edges[start_idx:] + edges[:start_idx]
|
||||
for edge in edges:
|
||||
PathLog.debug("points: {} -> {}".format(edge.Vertexes[0].Point, edge.Vertexes[-1].Point))
|
||||
PathLog.debug("valueat {} -> {}".format(edge.valueAt(edge.FirstParameter), edge.valueAt(edge.LastParameter)))
|
||||
PathLog.debug(
|
||||
"points: {} -> {}".format(
|
||||
edge.Vertexes[0].Point, edge.Vertexes[-1].Point
|
||||
)
|
||||
)
|
||||
PathLog.debug(
|
||||
"valueat {} -> {}".format(
|
||||
edge.valueAt(edge.FirstParameter),
|
||||
edge.valueAt(edge.LastParameter),
|
||||
)
|
||||
)
|
||||
if first and (not last or not wire.isClosed()):
|
||||
PathLog.debug('processing first edge entry')
|
||||
PathLog.debug("processing first edge entry")
|
||||
# we set the first move to our first point
|
||||
last = edge.Vertexes[0].Point
|
||||
|
||||
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
|
||||
self.commandlist.append(Path.Command('G0', {'X': last.x, 'Y': last.y, 'F': self.horizRapid}))
|
||||
self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid}))
|
||||
self.appendCommand(Path.Command('G1', {'X': last.x, 'Y': last.y, 'Z': last.z}), z, relZ, self.vertFeed)
|
||||
self.commandlist.append(
|
||||
Path.Command(
|
||||
"G0",
|
||||
{"Z": obj.ClearanceHeight.Value, "F": self.vertRapid},
|
||||
)
|
||||
)
|
||||
self.commandlist.append(
|
||||
Path.Command(
|
||||
"G0", {"X": last.x, "Y": last.y, "F": self.horizRapid}
|
||||
)
|
||||
)
|
||||
self.commandlist.append(
|
||||
Path.Command(
|
||||
"G0", {"Z": obj.SafeHeight.Value, "F": self.vertRapid}
|
||||
)
|
||||
)
|
||||
self.appendCommand(
|
||||
Path.Command("G1", {"X": last.x, "Y": last.y, "Z": last.z}),
|
||||
z,
|
||||
relZ,
|
||||
self.vertFeed,
|
||||
)
|
||||
first = False
|
||||
|
||||
if PathGeom.pointsCoincide(last, edge.valueAt(edge.FirstParameter)):
|
||||
#if PathGeom.pointsCoincide(last, edge.Vertexes[0].Point):
|
||||
# if PathGeom.pointsCoincide(last, edge.Vertexes[0].Point):
|
||||
for cmd in PathGeom.cmdsForEdge(edge):
|
||||
self.appendCommand(cmd, z, relZ, self.horizFeed)
|
||||
last = edge.Vertexes[-1].Point
|
||||
@@ -110,17 +140,21 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
for cmd in PathGeom.cmdsForEdge(edge, True):
|
||||
self.appendCommand(cmd, z, relZ, self.horizFeed)
|
||||
last = edge.Vertexes[0].Point
|
||||
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
|
||||
self.commandlist.append(
|
||||
Path.Command(
|
||||
"G0", {"Z": obj.ClearanceHeight.Value, "F": self.vertRapid}
|
||||
)
|
||||
)
|
||||
|
||||
def appendCommand(self, cmd, z, relZ, feed):
|
||||
params = cmd.Parameters
|
||||
if relZ:
|
||||
z = params['Z'] - z
|
||||
params.update({'Z': z, 'F': feed})
|
||||
z = params["Z"] - z
|
||||
params.update({"Z": z, "F": feed})
|
||||
self.commandlist.append(Path.Command(cmd.Name, params))
|
||||
|
||||
def opSetDefaultValues(self, obj, job):
|
||||
'''opSetDefaultValues(obj) ... set depths for engraving'''
|
||||
"""opSetDefaultValues(obj) ... set depths for engraving"""
|
||||
if PathOp.FeatureDepths & self.opFeatures(obj):
|
||||
if job and len(job.Model.Group) > 0:
|
||||
bb = job.Proxy.modelBoundBox(job)
|
||||
@@ -128,4 +162,3 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
obj.OpFinalDepth = bb.ZMax - max(obj.StepDown.Value, 0.1)
|
||||
else:
|
||||
obj.OpFinalDepth = -0.1
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathEngrave as PathEngrave
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
@@ -30,26 +30,35 @@ import PathScripts.PathUtils as PathUtils
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
|
||||
__title__ = "Path Engrave Operation UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Engrave operation page controller and command implementation."
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
|
||||
'''Enhanced base geometry page to also allow special base objects.'''
|
||||
"""Enhanced base geometry page to also allow special base objects."""
|
||||
|
||||
def super(self):
|
||||
return super(TaskPanelBaseGeometryPage, self)
|
||||
|
||||
def selectionSupportedAsBaseGeometry(self, selection, ignoreErrors):
|
||||
# allow selection of an entire 2D object, which is generally not the case
|
||||
if len(selection) == 1 and not selection[0].HasSubObjects and selection[0].Object.isDerivedFrom('Part::Part2DObject'):
|
||||
if (
|
||||
len(selection) == 1
|
||||
and not selection[0].HasSubObjects
|
||||
and selection[0].Object.isDerivedFrom("Part::Part2DObject")
|
||||
):
|
||||
return True
|
||||
# Let general logic handle all other cases.
|
||||
return self.super().selectionSupportedAsBaseGeometry(selection, ignoreErrors)
|
||||
@@ -61,22 +70,31 @@ class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
|
||||
job = PathUtils.findParentJob(self.obj)
|
||||
base = job.Proxy.resourceClone(job, sel.Object)
|
||||
if not base:
|
||||
PathLog.notice((translate("Path", "%s is not a Base Model object of the job %s")+"\n") % (sel.Object.Label, job.Label))
|
||||
PathLog.notice(
|
||||
(
|
||||
translate("Path", "%s is not a Base Model object of the job %s")
|
||||
+ "\n"
|
||||
)
|
||||
% (sel.Object.Label, job.Label)
|
||||
)
|
||||
continue
|
||||
if base in shapes:
|
||||
PathLog.notice((translate("Path", "Base shape %s already in the list")+"\n") % (sel.Object.Label))
|
||||
PathLog.notice(
|
||||
(translate("Path", "Base shape %s already in the list") + "\n")
|
||||
% (sel.Object.Label)
|
||||
)
|
||||
continue
|
||||
if base.isDerivedFrom('Part::Part2DObject'):
|
||||
if base.isDerivedFrom("Part::Part2DObject"):
|
||||
if sel.HasSubObjects:
|
||||
# selectively add some elements of the drawing to the Base
|
||||
for sub in sel.SubElementNames:
|
||||
if 'Vertex' in sub:
|
||||
PathLog.info(translate("Path", "Ignoring vertex"))
|
||||
if "Vertex" in sub:
|
||||
PathLog.info("Ignoring vertex")
|
||||
else:
|
||||
self.obj.Proxy.addBase(self.obj, base, sub)
|
||||
else:
|
||||
# when adding an entire shape to BaseShapes we can take its sub shapes out of Base
|
||||
self.obj.Base = [(p,el) for p,el in self.obj.Base if p != base]
|
||||
self.obj.Base = [(p, el) for p, el in self.obj.Base if p != base]
|
||||
shapes.append(base)
|
||||
self.obj.BaseShapes = shapes
|
||||
added = True
|
||||
@@ -106,32 +124,35 @@ class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
|
||||
sub = item.data(self.super().DataObjectSub)
|
||||
if not sub:
|
||||
shapes.append(obj)
|
||||
PathLog.debug("Setting new base shapes: %s -> %s" % (self.obj.BaseShapes, shapes))
|
||||
PathLog.debug(
|
||||
"Setting new base shapes: %s -> %s" % (self.obj.BaseShapes, shapes)
|
||||
)
|
||||
self.obj.BaseShapes = shapes
|
||||
return self.super().updateBase()
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
'''Page controller class for the Engrave operation.'''
|
||||
"""Page controller class for the Engrave operation."""
|
||||
|
||||
def getForm(self):
|
||||
'''getForm() ... returns UI'''
|
||||
"""getForm() ... returns UI"""
|
||||
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpEngraveEdit.ui")
|
||||
|
||||
def getFields(self, obj):
|
||||
'''getFields(obj) ... transfers values from UI to obj's proprties'''
|
||||
"""getFields(obj) ... transfers values from UI to obj's proprties"""
|
||||
if obj.StartVertex != self.form.startVertex.value():
|
||||
obj.StartVertex = self.form.startVertex.value()
|
||||
self.updateToolController(obj, self.form.toolController)
|
||||
self.updateCoolant(obj, self.form.coolantController)
|
||||
|
||||
def setFields(self, obj):
|
||||
'''setFields(obj) ... transfers obj's property values to UI'''
|
||||
"""setFields(obj) ... transfers obj's property values to UI"""
|
||||
self.form.startVertex.setValue(obj.StartVertex)
|
||||
self.setupToolController(obj, self.form.toolController)
|
||||
self.setupCoolant(obj, self.form.coolantController)
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
|
||||
"""getSignalsForUpdate(obj) ... return list of signals for updating obj"""
|
||||
signals = []
|
||||
signals.append(self.form.startVertex.editingFinished)
|
||||
signals.append(self.form.toolController.currentIndexChanged)
|
||||
@@ -139,15 +160,20 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
return signals
|
||||
|
||||
def taskPanelBaseGeometryPage(self, obj, features):
|
||||
'''taskPanelBaseGeometryPage(obj, features) ... return page for adding base geometries.'''
|
||||
"""taskPanelBaseGeometryPage(obj, features) ... return page for adding base geometries."""
|
||||
return TaskPanelBaseGeometryPage(obj, features)
|
||||
|
||||
Command = PathOpGui.SetupOperation('Engrave',
|
||||
PathEngrave.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_Engrave',
|
||||
QtCore.QT_TRANSLATE_NOOP("PathEngrave", "Engrave"),
|
||||
QtCore.QT_TRANSLATE_NOOP("PathEngrave", "Creates an Engraving Path around a Draft ShapeString"),
|
||||
PathEngrave.SetupProperties)
|
||||
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"Engrave",
|
||||
PathEngrave.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_Engrave",
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Engrave", "Engrave"),
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"Path_Engrave", "Creates an Engraving Path around a Draft ShapeString"
|
||||
),
|
||||
PathEngrave.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathEngraveGui... done\n")
|
||||
|
||||
@@ -26,13 +26,13 @@ import FreeCAD
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPocketBase as PathPocketBase
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import numpy
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
__title__ = "Path Mill Face Operation"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
@@ -41,32 +41,87 @@ __doc__ = "Class and implementation of Mill Facing operation."
|
||||
__contributors__ = "russ4262 (Russell Johnson)"
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule()
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class ObjectFace(PathPocketBase.ObjectPocket):
|
||||
'''Proxy object for Mill Facing operation.'''
|
||||
"""Proxy object for Mill Facing 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
|
||||
"""
|
||||
|
||||
enums = {
|
||||
"BoundaryShape": [
|
||||
(translate("Path_Pocket", "Boundbox"), "Boundbox"),
|
||||
(translate("Path_Pocket", "Face Region"), "Face Region"),
|
||||
(translate("Path_Pocket", "Perimeter"), "Perimeter"),
|
||||
(translate("Path_Pocket", "Stock"), "Stock"),
|
||||
],
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
PathLog.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
PathLog.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def initPocketOp(self, obj):
|
||||
'''initPocketOp(obj) ... create facing specific properties'''
|
||||
obj.addProperty("App::PropertyEnumeration", "BoundaryShape", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Shape to use for calculating Boundary"))
|
||||
obj.addProperty("App::PropertyBool", "ClearEdges", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Clear edges of surface (Only applicable to BoundBox)"))
|
||||
if not hasattr(obj, 'ExcludeRaisedAreas'):
|
||||
obj.addProperty("App::PropertyBool", "ExcludeRaisedAreas", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Exclude milling raised areas inside the face."))
|
||||
PathLog.track()
|
||||
"""initPocketOp(obj) ... create facing specific properties"""
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"BoundaryShape",
|
||||
"Face",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Shape to use for calculating Boundary"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"ClearEdges",
|
||||
"Face",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Clear edges of surface (Only applicable to BoundBox)"
|
||||
),
|
||||
)
|
||||
if not hasattr(obj, "ExcludeRaisedAreas"):
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"ExcludeRaisedAreas",
|
||||
"Face",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Exclude milling raised areas inside the face."
|
||||
),
|
||||
)
|
||||
|
||||
obj.BoundaryShape = ['Boundbox', 'Face Region', 'Perimeter', 'Stock']
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
def pocketInvertExtraOffset(self):
|
||||
return True
|
||||
|
||||
def areaOpOnChanged(self, obj, prop):
|
||||
'''areaOpOnChanged(obj, prop) ... facing specific depths calculation.'''
|
||||
"""areaOpOnChanged(obj, prop) ... facing specific depths calculation."""
|
||||
PathLog.track(prop)
|
||||
if prop == "StepOver" and obj.StepOver == 0:
|
||||
obj.StepOver = 1
|
||||
@@ -78,27 +133,27 @@ class ObjectFace(PathPocketBase.ObjectPocket):
|
||||
obj.OpStartDepth = job.Stock.Shape.BoundBox.ZMax
|
||||
|
||||
if len(obj.Base) >= 1:
|
||||
PathLog.debug('processing')
|
||||
PathLog.debug("processing")
|
||||
sublist = []
|
||||
for i in obj.Base:
|
||||
o = i[0]
|
||||
for s in i[1]:
|
||||
sublist.append(o.Shape.getElement(s))
|
||||
|
||||
# If the operation has a geometry identified the Finaldepth
|
||||
# is the top of the bboundbox which includes all features.
|
||||
# Otherwise, top of part.
|
||||
# If the operation has a geometry identified the Finaldepth
|
||||
# is the top of the bboundbox which includes all features.
|
||||
# Otherwise, top of part.
|
||||
|
||||
obj.OpFinalDepth = Part.makeCompound(sublist).BoundBox.ZMax
|
||||
elif job:
|
||||
obj.OpFinalDepth = job.Proxy.modelBoundBox(job).ZMax
|
||||
|
||||
def areaOpShapes(self, obj):
|
||||
'''areaOpShapes(obj) ... return top face'''
|
||||
"""areaOpShapes(obj) ... return top face"""
|
||||
# Facing is done either against base objects
|
||||
holeShape = None
|
||||
|
||||
PathLog.debug('depthparams: {}'.format([i for i in self.depthparams]))
|
||||
PathLog.debug("depthparams: {}".format([i for i in self.depthparams]))
|
||||
|
||||
if obj.Base:
|
||||
PathLog.debug("obj.Base: {}".format(obj.Base))
|
||||
@@ -120,7 +175,9 @@ class ObjectFace(PathPocketBase.ObjectPocket):
|
||||
# Limit to one model base per operation
|
||||
if oneBase[0] is not b[0]:
|
||||
oneBase[1] = False
|
||||
if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face
|
||||
if numpy.isclose(
|
||||
abs(shape.normalAt(0, 0).z), 1
|
||||
): # horizontal face
|
||||
# Analyze internal closed wires to determine if raised or a recess
|
||||
for wire in shape.Wires[1:]:
|
||||
if obj.ExcludeRaisedAreas:
|
||||
@@ -130,12 +187,18 @@ class ObjectFace(PathPocketBase.ObjectPocket):
|
||||
else:
|
||||
holes.append((b[0].Shape, wire))
|
||||
else:
|
||||
PathLog.warning('The base subobject, "{0}," is not a face. Ignoring "{0}."'.format(sub))
|
||||
PathLog.warning(
|
||||
'The base subobject, "{0}," is not a face. Ignoring "{0}."'.format(
|
||||
sub
|
||||
)
|
||||
)
|
||||
|
||||
if obj.ExcludeRaisedAreas and len(holes) > 0:
|
||||
for shape, wire in holes:
|
||||
f = Part.makeFace(wire, 'Part::FaceMakerSimple')
|
||||
env = PathUtils.getEnvelope(shape, subshape=f, depthparams=self.depthparams)
|
||||
f = Part.makeFace(wire, "Part::FaceMakerSimple")
|
||||
env = PathUtils.getEnvelope(
|
||||
shape, subshape=f, depthparams=self.depthparams
|
||||
)
|
||||
holeEnvs.append(env)
|
||||
holeShape = Part.makeCompound(holeEnvs)
|
||||
|
||||
@@ -161,62 +224,98 @@ class ObjectFace(PathPocketBase.ObjectPocket):
|
||||
bb.XMax = bb.XMax + offset
|
||||
bb.YMax = bb.YMax + offset
|
||||
|
||||
if obj.BoundaryShape == 'Boundbox':
|
||||
bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1))
|
||||
if obj.BoundaryShape == "Boundbox":
|
||||
bbperim = Part.makeBox(
|
||||
bb.XLength,
|
||||
bb.YLength,
|
||||
1,
|
||||
FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin),
|
||||
FreeCAD.Vector(0, 0, 1),
|
||||
)
|
||||
env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams)
|
||||
if obj.ExcludeRaisedAreas and oneBase[1]:
|
||||
includedFaces = self.getAllIncludedFaces(oneBase[0], env, faceZ=minHeight)
|
||||
includedFaces = self.getAllIncludedFaces(
|
||||
oneBase[0], env, faceZ=minHeight
|
||||
)
|
||||
if len(includedFaces) > 0:
|
||||
includedShape = Part.makeCompound(includedFaces)
|
||||
includedEnv = PathUtils.getEnvelope(oneBase[0].Shape, subshape=includedShape, depthparams=self.depthparams)
|
||||
includedEnv = PathUtils.getEnvelope(
|
||||
oneBase[0].Shape,
|
||||
subshape=includedShape,
|
||||
depthparams=self.depthparams,
|
||||
)
|
||||
env = env.cut(includedEnv)
|
||||
elif obj.BoundaryShape == 'Stock':
|
||||
elif obj.BoundaryShape == "Stock":
|
||||
stock = PathUtils.findParentJob(obj).Stock.Shape
|
||||
env = stock
|
||||
|
||||
if obj.ExcludeRaisedAreas and oneBase[1]:
|
||||
includedFaces = self.getAllIncludedFaces(oneBase[0], stock, faceZ=minHeight)
|
||||
includedFaces = self.getAllIncludedFaces(
|
||||
oneBase[0], stock, faceZ=minHeight
|
||||
)
|
||||
if len(includedFaces) > 0:
|
||||
stockEnv = PathUtils.getEnvelope(partshape=stock, depthparams=self.depthparams)
|
||||
stockEnv = PathUtils.getEnvelope(
|
||||
partshape=stock, depthparams=self.depthparams
|
||||
)
|
||||
includedShape = Part.makeCompound(includedFaces)
|
||||
includedEnv = PathUtils.getEnvelope(oneBase[0].Shape, subshape=includedShape, depthparams=self.depthparams)
|
||||
includedEnv = PathUtils.getEnvelope(
|
||||
oneBase[0].Shape,
|
||||
subshape=includedShape,
|
||||
depthparams=self.depthparams,
|
||||
)
|
||||
env = stockEnv.cut(includedEnv)
|
||||
elif obj.BoundaryShape == 'Perimeter':
|
||||
elif obj.BoundaryShape == "Perimeter":
|
||||
if obj.ClearEdges:
|
||||
psZMin = planeshape.BoundBox.ZMin
|
||||
ofstShape = PathUtils.getOffsetArea(planeshape,
|
||||
self.radius * 1.25,
|
||||
plane=planeshape)
|
||||
ofstShape.translate(FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin))
|
||||
env = PathUtils.getEnvelope(partshape=ofstShape, depthparams=self.depthparams)
|
||||
ofstShape = PathUtils.getOffsetArea(
|
||||
planeshape, self.radius * 1.25, plane=planeshape
|
||||
)
|
||||
ofstShape.translate(
|
||||
FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin)
|
||||
)
|
||||
env = PathUtils.getEnvelope(
|
||||
partshape=ofstShape, depthparams=self.depthparams
|
||||
)
|
||||
else:
|
||||
env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams)
|
||||
elif obj.BoundaryShape == 'Face Region':
|
||||
env = PathUtils.getEnvelope(
|
||||
partshape=planeshape, depthparams=self.depthparams
|
||||
)
|
||||
elif obj.BoundaryShape == "Face Region":
|
||||
baseShape = oneBase[0].Shape
|
||||
psZMin = planeshape.BoundBox.ZMin
|
||||
ofst = 0.0
|
||||
if obj.ClearEdges:
|
||||
ofst = self.tool.Diameter * 0.51
|
||||
ofstShape = PathUtils.getOffsetArea(planeshape, ofst, plane=planeshape)
|
||||
ofstShape.translate(FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin))
|
||||
ofstShape.translate(
|
||||
FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin)
|
||||
)
|
||||
|
||||
# Calculate custom depth params for removal shape envelope, with start and final depth buffers
|
||||
custDepthparams = self._customDepthParams(obj, obj.StartDepth.Value + 0.2, obj.FinalDepth.Value - 0.1) # only an envelope
|
||||
ofstShapeEnv = PathUtils.getEnvelope(partshape=ofstShape, depthparams=custDepthparams)
|
||||
if obj.ExcludeRaisedAreas:
|
||||
custDepthparams = self._customDepthParams(
|
||||
obj, obj.StartDepth.Value + 0.2, obj.FinalDepth.Value - 0.1
|
||||
) # only an envelope
|
||||
ofstShapeEnv = PathUtils.getEnvelope(
|
||||
partshape=ofstShape, depthparams=custDepthparams
|
||||
)
|
||||
if obj.ExcludeRaisedAreas:
|
||||
env = ofstShapeEnv.cut(baseShape)
|
||||
env.translate(FreeCAD.Vector(0.0, 0.0, -0.00001)) # lower removal shape into buffer zone
|
||||
env.translate(
|
||||
FreeCAD.Vector(0.0, 0.0, -0.00001)
|
||||
) # lower removal shape into buffer zone
|
||||
else:
|
||||
env = ofstShapeEnv
|
||||
|
||||
if holeShape:
|
||||
PathLog.debug("Processing holes and face ...")
|
||||
holeEnv = PathUtils.getEnvelope(partshape=holeShape, depthparams=self.depthparams)
|
||||
holeEnv = PathUtils.getEnvelope(
|
||||
partshape=holeShape, depthparams=self.depthparams
|
||||
)
|
||||
newEnv = env.cut(holeEnv)
|
||||
tup = newEnv, False, 'pathMillFace'
|
||||
tup = newEnv, False, "pathMillFace"
|
||||
else:
|
||||
PathLog.debug("Processing solid face ...")
|
||||
tup = env, False, 'pathMillFace'
|
||||
tup = env, False, "pathMillFace"
|
||||
|
||||
self.removalshapes.append(tup)
|
||||
obj.removalshape = self.removalshapes[0][0] # save removal shape
|
||||
@@ -224,7 +323,7 @@ class ObjectFace(PathPocketBase.ObjectPocket):
|
||||
return self.removalshapes
|
||||
|
||||
def areaOpSetDefaultValues(self, obj, job):
|
||||
'''areaOpSetDefaultValues(obj, job) ... initialize mill facing properties'''
|
||||
"""areaOpSetDefaultValues(obj, job) ... initialize mill facing properties"""
|
||||
obj.StepOver = 50
|
||||
obj.ZigZagAngle = 45.0
|
||||
obj.ExcludeRaisedAreas = False
|
||||
@@ -260,8 +359,8 @@ class ObjectFace(PathPocketBase.ObjectPocket):
|
||||
return False
|
||||
|
||||
def getAllIncludedFaces(self, base, env, faceZ):
|
||||
'''getAllIncludedFaces(base, env, faceZ)...
|
||||
Return all `base` faces extending above `faceZ` whose boundboxes overlap with the `env` boundbox.'''
|
||||
"""getAllIncludedFaces(base, env, faceZ)...
|
||||
Return all `base` faces extending above `faceZ` whose boundboxes overlap with the `env` boundbox."""
|
||||
included = []
|
||||
|
||||
eXMin = env.BoundBox.XMin
|
||||
@@ -305,7 +404,7 @@ def SetupProperties():
|
||||
|
||||
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Mill Facing operation.'''
|
||||
"""Create(name) ... Creates and returns a Mill Facing operation."""
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectFace(obj, name, parentJob)
|
||||
|
||||
@@ -20,32 +20,79 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathMillFace as PathMillFace
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
import PathScripts.PathPocketBaseGui as PathPocketBaseGui
|
||||
|
||||
from PySide import QtCore
|
||||
import PathScripts.PathPocketShape as PathPocketShape
|
||||
import FreeCADGui
|
||||
|
||||
__title__ = "Path Face Mill Operation UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Face Mill operation page controller and command implementation."
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage):
|
||||
'''Page controller class for the face milling operation.'''
|
||||
"""Page controller class for the face milling operation."""
|
||||
|
||||
def getForm(self):
|
||||
PathLog.track()
|
||||
"""getForm() ... return UI"""
|
||||
|
||||
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpPocketFullEdit.ui")
|
||||
comboToPropertyMap = [
|
||||
("cutMode", "CutMode"),
|
||||
("offsetPattern", "OffsetPattern"),
|
||||
("boundaryShape", "BoundaryShape"),
|
||||
]
|
||||
|
||||
enumTups = PathMillFace.ObjectFace.propertyEnumerations(dataType="raw")
|
||||
enumTups.update(
|
||||
PathPocketShape.ObjectPocket.pocketPropertyEnumerations(dataType="raw")
|
||||
)
|
||||
|
||||
self.populateCombobox(form, enumTups, comboToPropertyMap)
|
||||
return form
|
||||
|
||||
def populateCombobox(self, form, enumTups, comboBoxesPropertyMap):
|
||||
"""fillComboboxes(form, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations
|
||||
** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol.
|
||||
Args:
|
||||
form = UI form
|
||||
enumTups = list of (translated_text, data_string) tuples
|
||||
comboBoxesPropertyMap = list of (translated_text, data_string) tuples
|
||||
"""
|
||||
# Load appropriate enumerations in each combobox
|
||||
for cb, prop in comboBoxesPropertyMap:
|
||||
box = getattr(form, cb) # Get the combobox
|
||||
box.clear() # clear the combobox
|
||||
for text, data in enumTups[prop]: # load enumerations
|
||||
box.addItem(text, data)
|
||||
|
||||
def pocketFeatures(self):
|
||||
'''pocketFeatures() ... return FeatureFacing (see PathPocketBaseGui)'''
|
||||
"""pocketFeatures() ... return FeatureFacing (see PathPocketBaseGui)"""
|
||||
return PathPocketBaseGui.FeatureFacing
|
||||
|
||||
Command = PathOpGui.SetupOperation('MillFace',
|
||||
PathMillFace.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_Face',
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Face", "Face"),
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Face", "Create a Facing Operation from a model or face"),
|
||||
PathMillFace.SetupProperties)
|
||||
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"MillFace",
|
||||
PathMillFace.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_Face",
|
||||
QT_TRANSLATE_NOOP("Path_MillFace", "Face"),
|
||||
QT_TRANSLATE_NOOP(
|
||||
"Path_MillFace", "Create a Facing Operation from a model or face"
|
||||
),
|
||||
PathMillFace.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathMillFaceGui... done\n")
|
||||
|
||||
|
||||
@@ -20,17 +20,17 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathPocketBase as PathPocketBase
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
__title__ = "Path 3D Pocket Operation"
|
||||
__author__ = "Yorik van Havre <yorik@uncreated.net>"
|
||||
@@ -41,42 +41,110 @@ __created__ = "2014"
|
||||
__scriptVersion__ = "2e"
|
||||
__lastModified__ = "2020-02-13 17:22 CST"
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
'''Proxy object for Pocket operation.'''
|
||||
"""Proxy object for Pocket operation."""
|
||||
|
||||
def pocketOpFeatures(self, obj):
|
||||
return PathOp.FeatureNoFinalDepth
|
||||
|
||||
def initPocketOp(self, obj):
|
||||
'''initPocketOp(obj) ... setup receiver'''
|
||||
if not hasattr(obj, 'HandleMultipleFeatures'):
|
||||
obj.addProperty('App::PropertyEnumeration', 'HandleMultipleFeatures', 'Pocket', QtCore.QT_TRANSLATE_NOOP('PathPocket', 'Choose how to process multiple Base Geometry features.'))
|
||||
obj.HandleMultipleFeatures = ['Collectively', 'Individually']
|
||||
if not hasattr(obj, 'AdaptivePocketStart'):
|
||||
obj.addProperty('App::PropertyBool', 'AdaptivePocketStart', 'Pocket', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Use adaptive algorithm to eliminate excessive air milling above planar pocket top.'))
|
||||
if not hasattr(obj, 'AdaptivePocketFinish'):
|
||||
obj.addProperty('App::PropertyBool', 'AdaptivePocketFinish', 'Pocket', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Use adaptive algorithm to eliminate excessive air milling below planar pocket bottom.'))
|
||||
if not hasattr(obj, 'ProcessStockArea'):
|
||||
obj.addProperty('App::PropertyBool', 'ProcessStockArea', 'Pocket', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Process the model and stock in an operation with no Base Geometry selected.'))
|
||||
"""initPocketOp(obj) ... setup receiver"""
|
||||
if not hasattr(obj, "HandleMultipleFeatures"):
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"HandleMultipleFeatures",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Choose how to process multiple Base Geometry features.",
|
||||
),
|
||||
)
|
||||
if not hasattr(obj, "AdaptivePocketStart"):
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"AdaptivePocketStart",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Use adaptive algorithm to eliminate excessive air milling above planar pocket top.",
|
||||
),
|
||||
)
|
||||
if not hasattr(obj, "AdaptivePocketFinish"):
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"AdaptivePocketFinish",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Use adaptive algorithm to eliminate excessive air milling below planar pocket bottom.",
|
||||
),
|
||||
)
|
||||
if not hasattr(obj, "ProcessStockArea"):
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"ProcessStockArea",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Process the model and stock in an operation with no Base Geometry selected.",
|
||||
),
|
||||
)
|
||||
|
||||
# populate the property enumerations
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
@classmethod
|
||||
def propertyEnumerations(self, dataType="data"):
|
||||
"""propertyEnumerations(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
|
||||
"""
|
||||
|
||||
enums = {
|
||||
"HandleMultipleFeatures": [
|
||||
(translate("Path_Pocket", "Collectively"), "Collectively"),
|
||||
(translate("Path_Pocket", "Individually"), "Individually"),
|
||||
],
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
PathLog.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
PathLog.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def opOnDocumentRestored(self, obj):
|
||||
'''opOnDocumentRestored(obj) ... adds the properties if they doesn't exist.'''
|
||||
"""opOnDocumentRestored(obj) ... adds the properties if they doesn't exist."""
|
||||
self.initPocketOp(obj)
|
||||
|
||||
def pocketInvertExtraOffset(self):
|
||||
return False
|
||||
|
||||
def areaOpShapes(self, obj):
|
||||
'''areaOpShapes(obj) ... return shapes representing the solids to be removed.'''
|
||||
"""areaOpShapes(obj) ... return shapes representing the solids to be removed."""
|
||||
PathLog.track()
|
||||
|
||||
subObjTups = []
|
||||
@@ -102,34 +170,51 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
if len(Faces) == 0:
|
||||
allSubsFaceType = False
|
||||
|
||||
if allSubsFaceType is True and obj.HandleMultipleFeatures == 'Collectively':
|
||||
if (
|
||||
allSubsFaceType is True
|
||||
and obj.HandleMultipleFeatures == "Collectively"
|
||||
):
|
||||
(fzmin, fzmax) = self.getMinMaxOfFaces(Faces)
|
||||
if obj.FinalDepth.Value < fzmin:
|
||||
PathLog.warning(translate('PathPocket', 'Final depth set below ZMin of face(s) selected.'))
|
||||
PathLog.warning(
|
||||
translate(
|
||||
"PathPocket",
|
||||
"Final depth set below ZMin of face(s) selected.",
|
||||
)
|
||||
)
|
||||
|
||||
if obj.AdaptivePocketStart is True or obj.AdaptivePocketFinish is True:
|
||||
if (
|
||||
obj.AdaptivePocketStart is True
|
||||
or obj.AdaptivePocketFinish is True
|
||||
):
|
||||
pocketTup = self.calculateAdaptivePocket(obj, base, subObjTups)
|
||||
if pocketTup is not False:
|
||||
obj.removalshape = pocketTup[0]
|
||||
removalshapes.append(pocketTup) # (shape, isHole, detail)
|
||||
else:
|
||||
shape = Part.makeCompound(Faces)
|
||||
env = PathUtils.getEnvelope(base[0].Shape, subshape=shape, depthparams=self.depthparams)
|
||||
env = PathUtils.getEnvelope(
|
||||
base[0].Shape, subshape=shape, depthparams=self.depthparams
|
||||
)
|
||||
obj.removalshape = env.cut(base[0].Shape)
|
||||
# obj.removalshape.tessellate(0.1)
|
||||
removalshapes.append((obj.removalshape, False, '3DPocket')) # (shape, isHole, detail)
|
||||
removalshapes.append(
|
||||
(obj.removalshape, False, "3DPocket")
|
||||
) # (shape, isHole, detail)
|
||||
else:
|
||||
for sub in base[1]:
|
||||
if "Face" in sub:
|
||||
shape = Part.makeCompound([getattr(base[0].Shape, sub)])
|
||||
else:
|
||||
edges = [getattr(base[0].Shape, sub) for sub in base[1]]
|
||||
shape = Part.makeFace(edges, 'Part::FaceMakerSimple')
|
||||
shape = Part.makeFace(edges, "Part::FaceMakerSimple")
|
||||
|
||||
env = PathUtils.getEnvelope(base[0].Shape, subshape=shape, depthparams=self.depthparams)
|
||||
env = PathUtils.getEnvelope(
|
||||
base[0].Shape, subshape=shape, depthparams=self.depthparams
|
||||
)
|
||||
obj.removalshape = env.cut(base[0].Shape)
|
||||
# obj.removalshape.tessellate(0.1)
|
||||
removalshapes.append((obj.removalshape, False, '3DPocket'))
|
||||
removalshapes.append((obj.removalshape, False, "3DPocket"))
|
||||
|
||||
else: # process the job base object as a whole
|
||||
PathLog.debug("processing the whole job base object")
|
||||
@@ -137,36 +222,40 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
if obj.ProcessStockArea is True:
|
||||
job = PathUtils.findParentJob(obj)
|
||||
|
||||
stockEnvShape = PathUtils.getEnvelope(job.Stock.Shape, subshape=None, depthparams=self.depthparams)
|
||||
stockEnvShape = PathUtils.getEnvelope(
|
||||
job.Stock.Shape, subshape=None, depthparams=self.depthparams
|
||||
)
|
||||
|
||||
obj.removalshape = stockEnvShape.cut(base.Shape)
|
||||
# obj.removalshape.tessellate(0.1)
|
||||
else:
|
||||
env = PathUtils.getEnvelope(base.Shape, subshape=None, depthparams=self.depthparams)
|
||||
env = PathUtils.getEnvelope(
|
||||
base.Shape, subshape=None, depthparams=self.depthparams
|
||||
)
|
||||
obj.removalshape = env.cut(base.Shape)
|
||||
# obj.removalshape.tessellate(0.1)
|
||||
|
||||
removalshapes.append((obj.removalshape, False, '3DPocket'))
|
||||
removalshapes.append((obj.removalshape, False, "3DPocket"))
|
||||
|
||||
return removalshapes
|
||||
|
||||
def areaOpSetDefaultValues(self, obj, job):
|
||||
'''areaOpSetDefaultValues(obj, job) ... set default values'''
|
||||
"""areaOpSetDefaultValues(obj, job) ... set default values"""
|
||||
obj.StepOver = 100
|
||||
obj.ZigZagAngle = 45
|
||||
obj.HandleMultipleFeatures = 'Collectively'
|
||||
obj.HandleMultipleFeatures = "Collectively"
|
||||
obj.AdaptivePocketStart = False
|
||||
obj.AdaptivePocketFinish = False
|
||||
obj.ProcessStockArea = False
|
||||
|
||||
# methods for eliminating air milling with some pockets: adpative start and finish
|
||||
def calculateAdaptivePocket(self, obj, base, subObjTups):
|
||||
'''calculateAdaptivePocket(obj, base, subObjTups)
|
||||
"""calculateAdaptivePocket(obj, base, subObjTups)
|
||||
Orient multiple faces around common facial center of mass.
|
||||
Identify edges that are connections for adjacent faces.
|
||||
Attempt to separate unconnected edges into top and bottom loops of the pocket.
|
||||
Trim the top and bottom of the pocket if available and requested.
|
||||
return: tuple with pocket shape information'''
|
||||
return: tuple with pocket shape information"""
|
||||
low = []
|
||||
high = []
|
||||
removeList = []
|
||||
@@ -203,23 +292,27 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
highFaceShape = Part.Face(Part.Wire(Part.__sortEdges__(allEdges)))
|
||||
except Exception as ee:
|
||||
PathLog.warning(ee)
|
||||
PathLog.error(translate("Path", "A planar adaptive start is unavailable. The non-planar will be attempted."))
|
||||
PathLog.error(
|
||||
"A planar adaptive start is unavailable. The non-planar will be attempted."
|
||||
)
|
||||
tryNonPlanar = True
|
||||
else:
|
||||
makeHighFace = 1
|
||||
|
||||
if tryNonPlanar is True:
|
||||
try:
|
||||
highFaceShape = Part.makeFilledFace(Part.__sortEdges__(allEdges)) # NON-planar face method
|
||||
highFaceShape = Part.makeFilledFace(
|
||||
Part.__sortEdges__(allEdges)
|
||||
) # NON-planar face method
|
||||
except Exception as eee:
|
||||
PathLog.warning(eee)
|
||||
PathLog.error(translate("Path", "The non-planar adaptive start is also unavailable.") + "(1)")
|
||||
PathLog.error("The non-planar adaptive start is also unavailable.")
|
||||
isHighFacePlanar = False
|
||||
else:
|
||||
makeHighFace = 2
|
||||
|
||||
if makeHighFace > 0:
|
||||
FreeCAD.ActiveDocument.addObject('Part::Feature', 'topEdgeFace')
|
||||
FreeCAD.ActiveDocument.addObject("Part::Feature", "topEdgeFace")
|
||||
highFace = FreeCAD.ActiveDocument.ActiveObject
|
||||
highFace.Shape = highFaceShape
|
||||
removeList.append(highFace.Name)
|
||||
@@ -228,9 +321,17 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
if makeHighFace == 2:
|
||||
mx = hzmax + obj.StepDown.Value
|
||||
mn = hzmin - obj.StepDown.Value
|
||||
if highFace.Shape.BoundBox.ZMax > mx or highFace.Shape.BoundBox.ZMin < mn:
|
||||
PathLog.warning("ZMaxDiff: {}; ZMinDiff: {}".format(highFace.Shape.BoundBox.ZMax - mx, highFace.Shape.BoundBox.ZMin - mn))
|
||||
PathLog.error(translate("Path", "The non-planar adaptive start is also unavailable.") + "(2)")
|
||||
if (
|
||||
highFace.Shape.BoundBox.ZMax > mx
|
||||
or highFace.Shape.BoundBox.ZMin < mn
|
||||
):
|
||||
PathLog.warning(
|
||||
"ZMaxDiff: {}; ZMinDiff: {}".format(
|
||||
highFace.Shape.BoundBox.ZMax - mx,
|
||||
highFace.Shape.BoundBox.ZMin - mn,
|
||||
)
|
||||
)
|
||||
PathLog.error("The non-planar adaptive start is also unavailable.")
|
||||
isHighFacePlanar = False
|
||||
makeHighFace = 0
|
||||
else:
|
||||
@@ -252,7 +353,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
PathLog.error("An adaptive finish is unavailable.")
|
||||
isLowFacePlanar = False
|
||||
else:
|
||||
FreeCAD.ActiveDocument.addObject('Part::Feature', 'bottomEdgeFace')
|
||||
FreeCAD.ActiveDocument.addObject("Part::Feature", "bottomEdgeFace")
|
||||
lowFace = FreeCAD.ActiveDocument.ActiveObject
|
||||
lowFace.Shape = lowFaceShape
|
||||
removeList.append(lowFace.Name)
|
||||
@@ -279,9 +380,12 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
step_down=obj.StepDown.Value,
|
||||
z_finish_step=finish_step,
|
||||
final_depth=finDep,
|
||||
user_depths=None)
|
||||
user_depths=None,
|
||||
)
|
||||
shape = Part.makeCompound(Faces)
|
||||
env = PathUtils.getEnvelope(base[0].Shape, subshape=shape, depthparams=depthparams)
|
||||
env = PathUtils.getEnvelope(
|
||||
base[0].Shape, subshape=shape, depthparams=depthparams
|
||||
)
|
||||
cuts.append(env.cut(base[0].Shape))
|
||||
|
||||
# Might need to change to .cut(job.Stock.Shape) if pocket has no bottom
|
||||
@@ -305,8 +409,11 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
step_down=obj.StepDown.Value,
|
||||
z_finish_step=finish_step,
|
||||
final_depth=finDep1,
|
||||
user_depths=None)
|
||||
envTop = PathUtils.getEnvelope(base[0].Shape, subshape=highFace.Shape, depthparams=depthparams1)
|
||||
user_depths=None,
|
||||
)
|
||||
envTop = PathUtils.getEnvelope(
|
||||
base[0].Shape, subshape=highFace.Shape, depthparams=depthparams1
|
||||
)
|
||||
cbi = len(cuts) - 1
|
||||
cuts.append(cuts[cbi].cut(envTop))
|
||||
|
||||
@@ -326,18 +433,20 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
step_down=obj.StepDown.Value,
|
||||
z_finish_step=finish_step,
|
||||
final_depth=finDep2,
|
||||
user_depths=None)
|
||||
envBottom = PathUtils.getEnvelope(base[0].Shape, subshape=lowFace.Shape, depthparams=depthparams2)
|
||||
user_depths=None,
|
||||
)
|
||||
envBottom = PathUtils.getEnvelope(
|
||||
base[0].Shape, subshape=lowFace.Shape, depthparams=depthparams2
|
||||
)
|
||||
cbi = len(cuts) - 1
|
||||
cuts.append(cuts[cbi].cut(envBottom))
|
||||
|
||||
# package pocket details into tuple
|
||||
sdi = len(starts) - 1
|
||||
fdi = len(finals) - 1
|
||||
cbi = len(cuts) - 1
|
||||
pocket = (cuts[cbi], False, '3DPocket')
|
||||
pocket = (cuts[cbi], False, "3DPocket")
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
|
||||
for rn in removeList:
|
||||
FreeCADGui.ActiveDocument.getObject(rn).Visibility = False
|
||||
|
||||
@@ -347,11 +456,12 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
return pocket
|
||||
|
||||
def orderFacesAroundCenterOfMass(self, subObjTups):
|
||||
'''orderFacesAroundCenterOfMass(subObjTups)
|
||||
"""orderFacesAroundCenterOfMass(subObjTups)
|
||||
Order list of faces by center of mass in angular order around
|
||||
average center of mass for all faces. Positive X-axis is zero degrees.
|
||||
return: subObjTups [ordered/sorted]'''
|
||||
return: subObjTups [ordered/sorted]"""
|
||||
import math
|
||||
|
||||
newList = []
|
||||
vectList = []
|
||||
comList = []
|
||||
@@ -364,7 +474,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
return vectItem[3]
|
||||
|
||||
def getFaceIdx(sub):
|
||||
return int(sub.replace('Face', '')) - 1
|
||||
return int(sub.replace("Face", "")) - 1
|
||||
|
||||
# get CenterOfMass for each face and add to sumCenterOfMass for average calculation
|
||||
for (sub, face) in subObjTups:
|
||||
@@ -382,22 +492,26 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
|
||||
# calculate vector (mag, direct) for each face from avgCom
|
||||
for (sub, face, com) in comList:
|
||||
adjCom = com.sub(avgCom) # effectively treats avgCom as origin for each face.
|
||||
mag = math.sqrt(adjCom.x**2 + adjCom.y**2) # adjCom.Length without Z values
|
||||
adjCom = com.sub(
|
||||
avgCom
|
||||
) # effectively treats avgCom as origin for each face.
|
||||
mag = math.sqrt(
|
||||
adjCom.x ** 2 + adjCom.y ** 2
|
||||
) # adjCom.Length without Z values
|
||||
drctn = 0.0
|
||||
# Determine direction of vector
|
||||
if adjCom.x > 0.0:
|
||||
if adjCom.y > 0.0: # Q1
|
||||
drctn = math.degrees(math.atan(adjCom.y/adjCom.x))
|
||||
drctn = math.degrees(math.atan(adjCom.y / adjCom.x))
|
||||
elif adjCom.y < 0.0:
|
||||
drctn = -math.degrees(math.atan(adjCom.x/adjCom.y)) + 270.0
|
||||
drctn = -math.degrees(math.atan(adjCom.x / adjCom.y)) + 270.0
|
||||
elif adjCom.y == 0.0:
|
||||
drctn = 0.0
|
||||
elif adjCom.x < 0.0:
|
||||
if adjCom.y < 0.0:
|
||||
drctn = math.degrees(math.atan(adjCom.y/adjCom.x)) + 180.0
|
||||
drctn = math.degrees(math.atan(adjCom.y / adjCom.x)) + 180.0
|
||||
elif adjCom.y > 0.0:
|
||||
drctn = -math.degrees(math.atan(adjCom.x/adjCom.y)) + 90.0
|
||||
drctn = -math.degrees(math.atan(adjCom.x / adjCom.y)) + 90.0
|
||||
elif adjCom.y == 0.0:
|
||||
drctn = 180.0
|
||||
elif adjCom.x == 0.0:
|
||||
@@ -434,8 +548,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
return newList
|
||||
|
||||
def findSharedEdges(self, subObjTups):
|
||||
'''findSharedEdges(self, subObjTups)
|
||||
Find connected edges given a group of faces'''
|
||||
"""findSharedEdges(self, subObjTups)
|
||||
Find connected edges given a group of faces"""
|
||||
checkoutList = []
|
||||
searchedList = []
|
||||
shared = []
|
||||
@@ -471,7 +585,11 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
for ei2 in range(0, len(face2.Edges)):
|
||||
edg2 = face2.Edges[ei2]
|
||||
if edg1.isSame(edg2) is True:
|
||||
PathLog.debug("{}.Edges[{}] connects at {}.Edges[{}]".format(sub1, ei1, sub2, ei2))
|
||||
PathLog.debug(
|
||||
"{}.Edges[{}] connects at {}.Edges[{}]".format(
|
||||
sub1, ei1, sub2, ei2
|
||||
)
|
||||
)
|
||||
shared.append((sub1, face1, ei1))
|
||||
touching[sub1].append(ei1)
|
||||
touching[sub2].append(ei2)
|
||||
@@ -486,8 +604,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
return (shared, touchingCleaned)
|
||||
|
||||
def identifyUnconnectedEdges(self, subObjTups, touching):
|
||||
'''identifyUnconnectedEdges(subObjTups, touching)
|
||||
Categorize unconnected edges into two groups, if possible: low and high'''
|
||||
"""identifyUnconnectedEdges(subObjTups, touching)
|
||||
Categorize unconnected edges into two groups, if possible: low and high"""
|
||||
# Identify unconnected edges
|
||||
# (should be top edge loop if all faces form loop with bottom face(s) included)
|
||||
high = []
|
||||
@@ -518,7 +636,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
com = FreeCAD.Vector(0, 0, 0)
|
||||
com.add(edg0.CenterOfMass)
|
||||
com.add(edg1.CenterOfMass)
|
||||
avgCom = FreeCAD.Vector(com.x/2.0, com.y/2.0, com.z/2.0)
|
||||
avgCom = FreeCAD.Vector(com.x / 2.0, com.y / 2.0, com.z / 2.0)
|
||||
if avgCom.z > face.CenterOfMass.z:
|
||||
high.extend(holding)
|
||||
else:
|
||||
@@ -534,9 +652,9 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
return (low, high)
|
||||
|
||||
def hasCommonVertex(self, edge1, edge2, show=False):
|
||||
'''findCommonVertexIndexes(edge1, edge2, show=False)
|
||||
"""findCommonVertexIndexes(edge1, edge2, show=False)
|
||||
Compare vertexes of two edges to identify a common vertex.
|
||||
Returns the vertex index of edge1 to which edge2 is connected'''
|
||||
Returns the vertex index of edge1 to which edge2 is connected"""
|
||||
if show is True:
|
||||
PathLog.info("New findCommonVertex()... ")
|
||||
|
||||
@@ -561,9 +679,9 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
return -1
|
||||
|
||||
def groupConnectedEdges(self, holding):
|
||||
'''groupConnectedEdges(self, holding)
|
||||
"""groupConnectedEdges(self, holding)
|
||||
Take edges and determine which are connected.
|
||||
Group connected chains/loops into: low and high'''
|
||||
Group connected chains/loops into: low and high"""
|
||||
holds = []
|
||||
grps = []
|
||||
searched = []
|
||||
@@ -607,7 +725,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
|
||||
while len(holds) > 0:
|
||||
if loops > 500:
|
||||
PathLog.error('BREAK --- LOOPS LIMIT of 500 ---')
|
||||
PathLog.error("BREAK --- LOOPS LIMIT of 500 ---")
|
||||
break
|
||||
save = False
|
||||
|
||||
@@ -701,8 +819,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
return (low, high)
|
||||
|
||||
def getMinMaxOfFaces(self, Faces):
|
||||
'''getMinMaxOfFaces(Faces)
|
||||
return the zmin and zmax values for given set of faces or edges.'''
|
||||
"""getMinMaxOfFaces(Faces)
|
||||
return the zmin and zmax values for given set of faces or edges."""
|
||||
zmin = Faces[0].BoundBox.ZMax
|
||||
zmax = Faces[0].BoundBox.ZMin
|
||||
for f in Faces:
|
||||
@@ -718,7 +836,7 @@ def SetupProperties():
|
||||
|
||||
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a Pocket operation.'''
|
||||
"""Create(name) ... Creates and returns a Pocket operation."""
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectPocket(obj, name, parentJob)
|
||||
|
||||
@@ -21,65 +21,164 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import PathScripts.PathAreaOp as PathAreaOp
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
__title__ = "Base Path Pocket Operation"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Base class and implementation for Path pocket operations."
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class ObjectPocket(PathAreaOp.ObjectOp):
|
||||
'''Base class for proxy objects of all pocket operations.'''
|
||||
"""Base class for proxy objects of all pocket operations."""
|
||||
|
||||
@classmethod
|
||||
def pocketPropertyEnumerations(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
|
||||
"""
|
||||
|
||||
enums = {
|
||||
"CutMode": [
|
||||
(translate("Path_Pocket", "Climb"), "Climb"),
|
||||
(translate("Path_Pocket", "Conventional"), "Conventional"),
|
||||
], # this is the direction that the profile runs
|
||||
"StartAt": [
|
||||
(translate("Path_Pocket", "Center"), "Center"),
|
||||
(translate("Path_Pocket", "Edge"), "Edge"),
|
||||
],
|
||||
"OffsetPattern": [
|
||||
(translate("Path_Pocket", "ZigZag"), "ZigZag"),
|
||||
(translate("Path_Pocket", "Offset"), "Offset"),
|
||||
(translate("Path_Pocket", "Spiral"), "Spiral"),
|
||||
(translate("Path_Pocket", "ZigZagOffset"), "ZigZagOffset"),
|
||||
(translate("Path_Pocket", "Line"), "Line"),
|
||||
(translate("Path_Pocket", "Grid"), "Grid"),
|
||||
(translate("Path_Pocket", "Triangle"), "Triangle"),
|
||||
], # Fill Pattern
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
PathLog.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
PathLog.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def areaOpFeatures(self, obj):
|
||||
'''areaOpFeatures(obj) ... Pockets have a FinishDepth and work on Faces'''
|
||||
return PathOp.FeatureBaseFaces | PathOp.FeatureFinishDepth | self.pocketOpFeatures(obj)
|
||||
"""areaOpFeatures(obj) ... Pockets have a FinishDepth and work on Faces"""
|
||||
return (
|
||||
PathOp.FeatureBaseFaces
|
||||
| PathOp.FeatureFinishDepth
|
||||
| self.pocketOpFeatures(obj)
|
||||
)
|
||||
|
||||
def pocketOpFeatures(self, obj):
|
||||
# pylint: disable=unused-argument
|
||||
return 0
|
||||
|
||||
def initPocketOp(self, obj):
|
||||
'''initPocketOp(obj) ... overwrite to initialize subclass.
|
||||
Can safely be overwritten by subclass.'''
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
"""initPocketOp(obj) ... overwrite to initialize subclass.
|
||||
Can safely be overwritten by subclass."""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def pocketInvertExtraOffset(self):
|
||||
'''pocketInvertExtraOffset() ... return True if ExtraOffset's direction is inward.
|
||||
Can safely be overwritten by subclass.'''
|
||||
"""pocketInvertExtraOffset() ... return True if ExtraOffset's direction is inward.
|
||||
Can safely be overwritten by subclass."""
|
||||
return False
|
||||
|
||||
def initAreaOp(self, obj):
|
||||
'''initAreaOp(obj) ... create pocket specific properties.
|
||||
Do not overwrite, implement initPocketOp(obj) instead.'''
|
||||
"""initAreaOp(obj) ... create pocket specific properties.
|
||||
Do not overwrite, implement initPocketOp(obj) instead."""
|
||||
PathLog.track()
|
||||
|
||||
# Pocket Properties
|
||||
obj.addProperty("App::PropertyEnumeration", "CutMode", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise (CW) or CounterClockWise (CCW)"))
|
||||
obj.addProperty("App::PropertyDistance", "ExtraOffset", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra offset to apply to the operation. Direction is operation dependent."))
|
||||
obj.addProperty("App::PropertyEnumeration", "StartAt", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Start pocketing at center or boundary"))
|
||||
obj.addProperty("App::PropertyPercent", "StepOver", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Percent of cutter diameter to step over on each pass"))
|
||||
obj.addProperty("App::PropertyFloat", "ZigZagAngle", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Angle of the zigzag pattern"))
|
||||
obj.addProperty("App::PropertyEnumeration", "OffsetPattern", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Clearing pattern to use"))
|
||||
obj.addProperty("App::PropertyBool", "MinTravel", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Use 3D Sorting of Path"))
|
||||
obj.addProperty("App::PropertyBool", "KeepToolDown", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Attempts to avoid unnecessary retractions."))
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"CutMode",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The direction that the toolpath should go around the part ClockWise (CW) or CounterClockWise (CCW)",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"ExtraOffset",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Extra offset to apply to the operation. Direction is operation dependent.",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"StartAt",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Start pocketing at center or boundary"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyPercent",
|
||||
"StepOver",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Percent of cutter diameter to step over on each pass"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"ZigZagAngle",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Angle of the zigzag pattern"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"OffsetPattern",
|
||||
"Face",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Clearing pattern to use"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"MinTravel",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Use 3D Sorting of Path"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"KeepToolDown",
|
||||
"Pocket",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Attempts to avoid unnecessary retractions."
|
||||
),
|
||||
)
|
||||
|
||||
obj.CutMode = ['Climb', 'Conventional']
|
||||
obj.StartAt = ['Center', 'Edge']
|
||||
obj.OffsetPattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle']
|
||||
for n in self.pocketPropertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
self.initPocketOp(obj)
|
||||
|
||||
@@ -88,40 +187,48 @@ class ObjectPocket(PathAreaOp.ObjectOp):
|
||||
return not obj.KeepToolDown
|
||||
|
||||
def areaOpUseProjection(self, obj):
|
||||
'''areaOpUseProjection(obj) ... return False'''
|
||||
"""areaOpUseProjection(obj) ... return False"""
|
||||
return False
|
||||
|
||||
def areaOpAreaParams(self, obj, isHole):
|
||||
'''areaOpAreaParams(obj, isHole) ... return dictionary with pocket's area parameters'''
|
||||
"""areaOpAreaParams(obj, isHole) ... return dictionary with pocket's area parameters"""
|
||||
params = {}
|
||||
params['Fill'] = 0
|
||||
params['Coplanar'] = 0
|
||||
params['PocketMode'] = 1
|
||||
params['SectionCount'] = -1
|
||||
params['Angle'] = obj.ZigZagAngle
|
||||
params['FromCenter'] = (obj.StartAt == "Center")
|
||||
params['PocketStepover'] = (self.radius * 2) * (float(obj.StepOver)/100)
|
||||
params["Fill"] = 0
|
||||
params["Coplanar"] = 0
|
||||
params["PocketMode"] = 1
|
||||
params["SectionCount"] = -1
|
||||
params["Angle"] = obj.ZigZagAngle
|
||||
params["FromCenter"] = obj.StartAt == "Center"
|
||||
params["PocketStepover"] = (self.radius * 2) * (float(obj.StepOver) / 100)
|
||||
extraOffset = obj.ExtraOffset.Value
|
||||
if self.pocketInvertExtraOffset():
|
||||
extraOffset = 0 - extraOffset
|
||||
params['PocketExtraOffset'] = extraOffset
|
||||
params['ToolRadius'] = self.radius
|
||||
params["PocketExtraOffset"] = extraOffset
|
||||
params["ToolRadius"] = self.radius
|
||||
|
||||
Pattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle']
|
||||
params['PocketMode'] = Pattern.index(obj.OffsetPattern) + 1
|
||||
Pattern = [
|
||||
"ZigZag",
|
||||
"Offset",
|
||||
"Spiral",
|
||||
"ZigZagOffset",
|
||||
"Line",
|
||||
"Grid",
|
||||
"Triangle",
|
||||
]
|
||||
params["PocketMode"] = Pattern.index(obj.OffsetPattern) + 1
|
||||
|
||||
if obj.SplitArcs:
|
||||
params['Explode'] = True
|
||||
params['FitArcs'] = False
|
||||
params["Explode"] = True
|
||||
params["FitArcs"] = False
|
||||
|
||||
return params
|
||||
|
||||
def areaOpPathParams(self, obj, isHole):
|
||||
'''areaOpAreaParams(obj, isHole) ... return dictionary with pocket's path parameters'''
|
||||
"""areaOpAreaParams(obj, isHole) ... return dictionary with pocket's path parameters"""
|
||||
params = {}
|
||||
|
||||
CutMode = ['Conventional', 'Climb']
|
||||
params['orientation'] = CutMode.index(obj.CutMode)
|
||||
CutMode = ["Conventional", "Climb"]
|
||||
params["orientation"] = CutMode.index(obj.CutMode)
|
||||
|
||||
# if MinTravel is turned on, set path sorting to 3DSort
|
||||
# 3DSort shouldn't be used without a valid start point. Can cause
|
||||
@@ -133,19 +240,19 @@ class ObjectPocket(PathAreaOp.ObjectOp):
|
||||
# any problem
|
||||
#
|
||||
if obj.MinTravel and obj.UseStartPoint and obj.StartPoint is not None:
|
||||
params['sort_mode'] = 3
|
||||
params['threshold'] = self.radius * 2
|
||||
params["sort_mode"] = 3
|
||||
params["threshold"] = self.radius * 2
|
||||
return params
|
||||
|
||||
|
||||
def SetupProperties():
|
||||
setup = PathAreaOp.SetupProperties()
|
||||
setup.append('CutMode')
|
||||
setup.append('ExtraOffset')
|
||||
setup.append('StepOver')
|
||||
setup.append('ZigZagAngle')
|
||||
setup.append('OffsetPattern')
|
||||
setup.append('StartAt')
|
||||
setup.append('MinTravel')
|
||||
setup.append('KeepToolDown')
|
||||
setup.append("CutMode")
|
||||
setup.append("ExtraOffset")
|
||||
setup.append("StepOver")
|
||||
setup.append("ZigZagAngle")
|
||||
setup.append("OffsetPattern")
|
||||
setup.append("StartAt")
|
||||
setup.append("MinTravel")
|
||||
setup.append("KeepToolDown")
|
||||
return setup
|
||||
|
||||
@@ -22,50 +22,69 @@
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
|
||||
from PySide import QtCore #, QtGui
|
||||
import PathScripts.PathPocket as PathPocket
|
||||
import PathScripts.PathLog as PathLog
|
||||
|
||||
__title__ = "Path Pocket Base Operation UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Base page controller and command implementation for path pocket operations."
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
FeaturePocket = 0x01
|
||||
FeatureFacing = 0x02
|
||||
FeatureOutline = 0x04
|
||||
|
||||
FeaturePocket = 0x01
|
||||
FeatureFacing = 0x02
|
||||
FeatureOutline = 0x04
|
||||
|
||||
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
'''Page controller class for pocket operations, supports:
|
||||
FeaturePocket ... used for pocketing operation
|
||||
FeatureFacing ... used for face milling operation
|
||||
FeatureOutline ... used for pocket-shape operation
|
||||
'''
|
||||
"""Page controller class for pocket operations, supports:
|
||||
FeaturePocket ... used for pocketing operation
|
||||
FeatureFacing ... used for face milling operation
|
||||
FeatureOutline ... used for pocket-shape operation
|
||||
"""
|
||||
|
||||
def pocketFeatures(self):
|
||||
'''pocketFeatures() ... return which features of the UI are supported by the operation.
|
||||
"""pocketFeatures() ... return which features of the UI are supported by the operation.
|
||||
FeaturePocket ... used for pocketing operation
|
||||
FeatureFacing ... used for face milling operation
|
||||
FeatureOutline ... used for pocket-shape operation
|
||||
Must be overwritten by subclasses'''
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
Must be overwritten by subclasses"""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def getForm(self):
|
||||
'''getForm() ... returns UI, adapted to the results from pocketFeatures()'''
|
||||
"""getForm() ... returns UI, adapted to the results from pocketFeatures()"""
|
||||
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpPocketFullEdit.ui")
|
||||
|
||||
comboToPropertyMap = [
|
||||
("cutMode", "CutMode"),
|
||||
("offsetPattern", "OffsetPattern"),
|
||||
]
|
||||
enumTups = PathPocket.ObjectPocket.pocketPropertyEnumerations(dataType="raw")
|
||||
|
||||
self.populateCombobox(form, enumTups, comboToPropertyMap)
|
||||
|
||||
if not FeatureFacing & self.pocketFeatures():
|
||||
form.facingWidget.hide()
|
||||
form.clearEdges.hide()
|
||||
|
||||
if FeaturePocket & self.pocketFeatures():
|
||||
form.extraOffset_label.setText(translate("PathPocket", "Pass Extension"))
|
||||
form.extraOffset.setToolTip(translate("PathPocket", "The distance the facing operation will extend beyond the boundary shape."))
|
||||
form.extraOffset.setToolTip(
|
||||
translate(
|
||||
"PathPocket",
|
||||
"The distance the facing operation will extend beyond the boundary shape.",
|
||||
)
|
||||
)
|
||||
|
||||
if not (FeatureOutline & self.pocketFeatures()):
|
||||
form.useOutline.hide()
|
||||
@@ -76,6 +95,21 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
|
||||
return form
|
||||
|
||||
def populateCombobox(self, form, enumTups, comboBoxesPropertyMap):
|
||||
"""fillComboboxes(form, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations
|
||||
** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol.
|
||||
Args:
|
||||
form = UI form
|
||||
enumTups = list of (translated_text, data_string) tuples
|
||||
comboBoxesPropertyMap = list of (translated_text, data_string) tuples
|
||||
"""
|
||||
# Load appropriate enumerations in each combobox
|
||||
for cb, prop in comboBoxesPropertyMap:
|
||||
box = getattr(form, cb) # Get the combobox
|
||||
box.clear() # clear the combobox
|
||||
for text, data in enumTups[prop]: # load enumerations
|
||||
box.addItem(text, data)
|
||||
|
||||
def updateMinTravel(self, obj, setModel=True):
|
||||
if obj.UseStartPoint:
|
||||
self.form.minTravel.setEnabled(True)
|
||||
@@ -87,24 +121,24 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
obj.MinTravel = self.form.minTravel.isChecked()
|
||||
|
||||
def updateZigZagAngle(self, obj, setModel=True):
|
||||
if obj.OffsetPattern in ['Offset', 'Spiral']:
|
||||
if obj.OffsetPattern in ["Offset", "Spiral"]:
|
||||
self.form.zigZagAngle.setEnabled(False)
|
||||
else:
|
||||
self.form.zigZagAngle.setEnabled(True)
|
||||
|
||||
if setModel:
|
||||
PathGui.updateInputField(obj, 'ZigZagAngle', self.form.zigZagAngle)
|
||||
PathGui.updateInputField(obj, "ZigZagAngle", self.form.zigZagAngle)
|
||||
|
||||
def getFields(self, obj):
|
||||
'''getFields(obj) ... transfers values from UI to obj's proprties'''
|
||||
if obj.CutMode != str(self.form.cutMode.currentText()):
|
||||
obj.CutMode = str(self.form.cutMode.currentText())
|
||||
"""getFields(obj) ... transfers values from UI to obj's proprties"""
|
||||
if obj.CutMode != str(self.form.cutMode.currentData()):
|
||||
obj.CutMode = str(self.form.cutMode.currentData())
|
||||
if obj.StepOver != self.form.stepOverPercent.value():
|
||||
obj.StepOver = self.form.stepOverPercent.value()
|
||||
if obj.OffsetPattern != str(self.form.offsetPattern.currentText()):
|
||||
obj.OffsetPattern = str(self.form.offsetPattern.currentText())
|
||||
if obj.OffsetPattern != str(self.form.offsetPattern.currentData()):
|
||||
obj.OffsetPattern = str(self.form.offsetPattern.currentData())
|
||||
|
||||
PathGui.updateInputField(obj, 'ExtraOffset', self.form.extraOffset)
|
||||
PathGui.updateInputField(obj, "ExtraOffset", self.form.extraOffset)
|
||||
self.updateToolController(obj, self.form.toolController)
|
||||
self.updateCoolant(obj, self.form.coolantController)
|
||||
self.updateZigZagAngle(obj)
|
||||
@@ -119,20 +153,29 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.updateMinTravel(obj)
|
||||
|
||||
if FeatureFacing & self.pocketFeatures():
|
||||
if obj.BoundaryShape != str(self.form.boundaryShape.currentText()):
|
||||
obj.BoundaryShape = str(self.form.boundaryShape.currentText())
|
||||
print(obj.BoundaryShape)
|
||||
print(self.form.boundaryShape.currentText())
|
||||
print(self.form.boundaryShape.currentData())
|
||||
if obj.BoundaryShape != str(self.form.boundaryShape.currentData()):
|
||||
obj.BoundaryShape = str(self.form.boundaryShape.currentData())
|
||||
if obj.ClearEdges != self.form.clearEdges.isChecked():
|
||||
obj.ClearEdges = self.form.clearEdges.isChecked()
|
||||
|
||||
def setFields(self, obj):
|
||||
'''setFields(obj) ... transfers obj's property values to UI'''
|
||||
"""setFields(obj) ... transfers obj's property values to UI"""
|
||||
self.form.stepOverPercent.setValue(obj.StepOver)
|
||||
self.form.extraOffset.setText(FreeCAD.Units.Quantity(obj.ExtraOffset.Value, FreeCAD.Units.Length).UserString)
|
||||
self.form.extraOffset.setText(
|
||||
FreeCAD.Units.Quantity(
|
||||
obj.ExtraOffset.Value, FreeCAD.Units.Length
|
||||
).UserString
|
||||
)
|
||||
self.form.useStartPoint.setChecked(obj.UseStartPoint)
|
||||
if FeatureOutline & self.pocketFeatures():
|
||||
self.form.useOutline.setChecked(obj.UseOutline)
|
||||
|
||||
self.form.zigZagAngle.setText(FreeCAD.Units.Quantity(obj.ZigZagAngle, FreeCAD.Units.Angle).UserString)
|
||||
self.form.zigZagAngle.setText(
|
||||
FreeCAD.Units.Quantity(obj.ZigZagAngle, FreeCAD.Units.Angle).UserString
|
||||
)
|
||||
self.updateZigZagAngle(obj, False)
|
||||
|
||||
self.form.minTravel.setChecked(obj.MinTravel)
|
||||
@@ -148,7 +191,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.form.clearEdges.setChecked(obj.ClearEdges)
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
|
||||
"""getSignalsForUpdate(obj) ... return list of signals for updating obj"""
|
||||
signals = []
|
||||
|
||||
signals.append(self.form.cutMode.currentIndexChanged)
|
||||
|
||||
@@ -24,27 +24,41 @@ import FreeCAD
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
import PathScripts.PathPocket as PathPocket
|
||||
import PathScripts.PathPocketBaseGui as PathPocketBaseGui
|
||||
import PathScripts.PathLog as PathLog
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
__title__ = "Path Pocket Operation UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Pocket operation page controller and command implementation."
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage):
|
||||
'''Page controller class for Pocket operation'''
|
||||
"""Page controller class for Pocket operation"""
|
||||
|
||||
def pocketFeatures(self):
|
||||
'''pocketFeatures() ... return FeaturePocket (see PathPocketBaseGui)'''
|
||||
"""pocketFeatures() ... return FeaturePocket (see PathPocketBaseGui)"""
|
||||
return PathPocketBaseGui.FeaturePocket
|
||||
|
||||
Command = PathOpGui.SetupOperation('Pocket 3D',
|
||||
PathPocket.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_3DPocket',
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Pocket", "3D Pocket"),
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Pocket", "Creates a Path 3D Pocket object from a face or faces"),
|
||||
PathPocket.SetupProperties)
|
||||
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"Pocket3D",
|
||||
PathPocket.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_3DPocket",
|
||||
QT_TRANSLATE_NOOP("Path_Pocket3D", "3D Pocket"),
|
||||
QT_TRANSLATE_NOOP(
|
||||
"Path_Pocket3D", "Creates a Path 3D Pocket object from a face or faces"
|
||||
),
|
||||
PathPocket.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathPocketGui... done\n")
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathPocketBase as PathPocketBase
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
@@ -46,13 +46,11 @@ __url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Class and implementation of shape based Pocket operation."
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
@@ -68,8 +66,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
"App::PropertyBool",
|
||||
"UseOutline",
|
||||
"Pocket",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathPocketShape", "Uses the outline of the base geometry."
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Uses the outline of the base geometry."
|
||||
),
|
||||
)
|
||||
|
||||
@@ -113,10 +111,9 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
if "Face" in sub:
|
||||
if sub not in avoidFeatures and not self.clasifySub(base, sub):
|
||||
PathLog.error(
|
||||
translate(
|
||||
"PathPocket", "Pocket does not support shape %s.%s"
|
||||
"Pocket does not support shape {}.{}".format(
|
||||
base.Label, sub
|
||||
)
|
||||
% (base.Label, sub)
|
||||
)
|
||||
|
||||
# Convert horizontal faces to use outline only if requested
|
||||
@@ -136,12 +133,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
face = Part.Face(w)
|
||||
# face.tessellate(0.1)
|
||||
if PathGeom.isRoughly(face.Area, 0):
|
||||
PathLog.error(
|
||||
translate(
|
||||
"PathPocket",
|
||||
"Vertical faces do not form a loop - ignoring",
|
||||
)
|
||||
)
|
||||
PathLog.error("Vertical faces do not form a loop - ignoring")
|
||||
else:
|
||||
self.horiz.append(face)
|
||||
|
||||
@@ -271,11 +263,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
|
||||
self.vert.append(face)
|
||||
return True
|
||||
else:
|
||||
PathLog.error(
|
||||
translate(
|
||||
"Path", "Failed to identify vertical face from {}.".format(sub)
|
||||
)
|
||||
)
|
||||
PathLog.error("Failed to identify vertical face from {}".format(sub))
|
||||
|
||||
else:
|
||||
PathLog.debug(" -type(face.Surface): {}".format(type(face.Surface)))
|
||||
|
||||
@@ -26,42 +26,52 @@ import PathScripts.PathOpGui as PathOpGui
|
||||
import PathScripts.PathPocketShape as PathPocketShape
|
||||
import PathScripts.PathPocketBaseGui as PathPocketBaseGui
|
||||
import PathScripts.PathFeatureExtensionsGui as PathFeatureExtensionsGui
|
||||
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
__title__ = "Path Pocket Shape Operation UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Pocket Shape operation page controller and command implementation."
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage):
|
||||
'''Page controller class for Pocket operation'''
|
||||
"""Page controller class for Pocket operation"""
|
||||
|
||||
def pocketFeatures(self):
|
||||
'''pocketFeatures() ... return FeaturePocket (see PathPocketBaseGui)'''
|
||||
"""pocketFeatures() ... return FeaturePocket (see PathPocketBaseGui)"""
|
||||
return PathPocketBaseGui.FeaturePocket | PathPocketBaseGui.FeatureOutline
|
||||
|
||||
def taskPanelBaseLocationPage(self, obj, features):
|
||||
if not hasattr(self, 'extensionsPanel'):
|
||||
self.extensionsPanel = PathFeatureExtensionsGui.TaskPanelExtensionPage(obj, features) # pylint: disable=attribute-defined-outside-init
|
||||
if not hasattr(self, "extensionsPanel"):
|
||||
self.extensionsPanel = PathFeatureExtensionsGui.TaskPanelExtensionPage(
|
||||
obj, features
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
return self.extensionsPanel
|
||||
|
||||
Command = PathOpGui.SetupOperation('Pocket Shape',
|
||||
PathPocketShape.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_Pocket',
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Pocket", "Pocket Shape"),
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Pocket", "Creates a Path Pocket object from a face or faces"),
|
||||
PathPocketShape.SetupProperties)
|
||||
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"Pocket Shape",
|
||||
PathPocketShape.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_Pocket",
|
||||
QT_TRANSLATE_NOOP("Path_Pocket_Shape", "Pocket Shape"),
|
||||
QT_TRANSLATE_NOOP(
|
||||
"Path_Pocket_Shape", "Creates a Path Pocket object from a face or faces"
|
||||
),
|
||||
PathPocketShape.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathPocketShapeGui... done\n")
|
||||
|
||||
@@ -390,7 +390,7 @@ def select(op):
|
||||
opsel["Helix"] = drillselect
|
||||
opsel["MillFace"] = pocketselect
|
||||
opsel["Pocket"] = pocketselect
|
||||
opsel["Pocket 3D"] = pocketselect
|
||||
opsel["Pocket3D"] = pocketselect
|
||||
opsel["Pocket Shape"] = pocketselect
|
||||
opsel["Profile Edges"] = eselect # (depreciated)
|
||||
opsel["Profile Faces"] = fselect # (depreciated)
|
||||
|
||||
Reference in New Issue
Block a user