FreeCAD/src/Mod/Path/PathScripts/PathPocket.py
sliptonic 0ea9cc8c43 Bug fixes:
fixes reparenting bug when dressup is deleted
Engrave wasn't using final depth value
Dragknife Dressup not correctly calculating spin direction or curve intersection
Per Yorik, fix crash if PartGui hasn't been loaded
Allow selection of bottom face for profiling
Bug # 0002615
2016-08-04 14:28:59 -03:00

735 lines
32 KiB
Python

# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
# * *
# * 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 Path
from PySide import QtCore, QtGui
from PathScripts import PathUtils
FreeCADGui = None
if FreeCAD.GuiUp:
import FreeCADGui
"""Path Pocket object and FreeCAD command"""
# Qt tanslation handling
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def translate(context, text, disambig=None):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def translate(context, text, disambig=None):
return QtGui.QApplication.translate(context, text, disambig)
class ObjectPocket:
def __init__(self, obj):
obj.addProperty("App::PropertyLinkSubList", "Base", "Path", "The base geometry of this object")
obj.addProperty("App::PropertyBool", "Active", "Path", "Make False, to prevent operation from generating code")
obj.addProperty("App::PropertyString", "Comment", "Path", "An optional comment for this profile")
obj.addProperty("App::PropertyString", "UserLabel", "Path", "User Assigned Label")
obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm", "The library to use to generate the path")
obj.Algorithm = ['OCC Native', 'libarea']
# Tool Properties
obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", "The tool number in use")
obj.ToolNumber = (0, 0, 1000, 0)
obj.setEditorMode('ToolNumber', 1) # make this read only
obj.addProperty("App::PropertyString", "ToolDescription", "Tool", "The description of the tool ")
obj.setEditorMode('ToolDescription', 1) # make this read only
# Depth Properties
obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", "The height needed to clear clamps and obstructions")
obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", "Rapid Safety Height between locations.")
obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", "Incremental Step Down of Tool")
obj.StepDown = (0.0, 0.01, 100.0, 0.5)
obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", "Starting Depth of Tool- first cut depth in Z")
obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", "Final Depth of Tool- lowest value in Z")
obj.addProperty("App::PropertyDistance", "FinishDepth", "Depth", "Maximum material removed on final pass.")
# Pocket Properties
obj.addProperty("App::PropertyEnumeration", "CutMode", "Pocket", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")
obj.CutMode = ['Climb', 'Conventional']
obj.addProperty("App::PropertyDistance", "MaterialAllowance", "Pocket", "Amount of material to leave")
obj.addProperty("App::PropertyEnumeration", "StartAt", "Pocket", "Start pocketing at center or boundary")
obj.StartAt = ['Center', 'Edge']
obj.addProperty("App::PropertyPercent", "StepOver", "Pocket", "Percent of cutter diameter to step over on each pass")
# obj.StepOver = (0.0, 0.01, 100.0, 0.5)
obj.addProperty("App::PropertyBool", "KeepToolDown", "Pocket", "Attempts to avoid unnecessary retractions.")
obj.addProperty("App::PropertyBool", "ZigUnidirectional", "Pocket", "Lifts tool at the end of each pass to respect cut mode.")
obj.addProperty("App::PropertyBool", "UseZigZag", "Pocket", "Use Zig Zag pattern to clear area.")
obj.addProperty("App::PropertyFloat", "ZigZagAngle", "Pocket", "Angle of the zigzag pattern")
# Entry Properties
obj.addProperty("App::PropertyBool", "UseEntry", "Entry", "Allow Cutter enter material with a straight plunge.")
obj.addProperty("App::PropertyFloatConstraint", "RampSize", "Entry", "The minimum fraction of tool diameter to use for ramp length")
obj.RampSize = (0.0, 0.01, 100.0, 0.5)
obj.addProperty("App::PropertyFloatConstraint", "HelixSize", "Entry", "The fraction of tool diameter to use for calculating helix size.")
obj.HelixSize = (0.0, 0.01, 100.0, 0.5)
obj.addProperty("App::PropertyFloatConstraint", "RampAngle", "Entry", "The Angle of the ramp entry.")
obj.RampAngle = (0.0, 0.01, 100.0, 0.5)
# Start Point Properties
obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", "The start point of this path")
obj.addProperty("App::PropertyBool", "UseStartPoint", "Start Point", "make True, if specifying a Start Point")
obj.Proxy = self
def onChanged(self, obj, prop):
if prop == "UseEntry":
if obj.UseEntry:
obj.setEditorMode('HelixSize', 0) # make this visible
obj.setEditorMode('RampAngle', 0) # make this visible
obj.setEditorMode('RampSize', 0) # make this visible
else:
obj.setEditorMode('HelixSize', 2) # make this hidden
obj.setEditorMode('RampAngle', 2) # make this hidden
obj.setEditorMode('RampSize', 2) # make this hidden
if prop == "UserLabel":
obj.Label = obj.UserLabel + " :" + obj.ToolDescription
def __getstate__(self):
return None
def __setstate__(self, state):
return None
def addpocketbase(self, obj, ss, sub=""):
baselist = obj.Base
if baselist is None:
baselist = []
if len(baselist) == 0: # When adding the first base object, guess at heights
try:
bb = ss.Shape.BoundBox # parent boundbox
subobj = ss.Shape.getElement(sub)
fbb = subobj.BoundBox # feature boundbox
obj.StartDepth = bb.ZMax
obj.ClearanceHeight = bb.ZMax + 5.0
obj.SafeHeight = bb.ZMax + 3.0
if fbb.ZMax == fbb.ZMin and fbb.ZMax == bb.ZMax: # top face
obj.FinalDepth = bb.ZMin
elif fbb.ZMax > fbb.ZMin and fbb.ZMax == bb.ZMax: # vertical face, full cut
obj.FinalDepth = fbb.ZMin
elif fbb.ZMax > fbb.ZMin and fbb.ZMin > bb.ZMin: # internal vertical wall
obj.FinalDepth = fbb.ZMin
elif fbb.ZMax == fbb.ZMin and fbb.ZMax > bb.ZMin: # face/shelf
obj.FinalDepth = fbb.ZMin
else: # catch all
obj.FinalDepth = bb.ZMin
except:
obj.StartDepth = 5.0
obj.ClearanceHeight = 10.0
obj.SafeHeight = 8.0
item = (ss, sub)
if item in baselist:
FreeCAD.Console.PrintWarning(translate("Path", "this object already in the list" + "\n"))
else:
baselist.append(item)
obj.Base = baselist
print "this base is: " + str(baselist)
self.execute(obj)
def getStock(self, obj):
"""find and return a stock object from hosting project if any"""
for o in obj.InList:
if hasattr(o, "Group"):
for g in o.Group:
if hasattr(g, "Height_Allowance"):
return o
# not found? search one level up
for o in obj.InList:
return self.getStock(o)
return None
def buildpathlibarea(self, obj, a):
"""Build the pocket path using libarea algorithm"""
import PathScripts.PathAreaUtils as PathAreaUtils
from PathScripts.PathUtils import depth_params
FreeCAD.Console.PrintMessage(translate("PathPocket", "Generating toolpath with libarea offsets.\n"))
depthparams = depth_params(
obj.ClearanceHeight.Value,
obj.SafeHeight.Value,
obj.StartDepth.Value,
obj.StepDown,
obj.FinishDepth.Value,
obj.FinalDepth.Value)
extraoffset = obj.MaterialAllowance.Value
stepover = (self.radius * 2) * (float(obj.StepOver)/100)
use_zig_zag = obj.UseZigZag
zig_angle = obj.ZigZagAngle
from_center = (obj.StartAt == "Center")
keep_tool_down = obj.KeepToolDown
zig_unidirectional = obj.ZigUnidirectional
start_point = None
cut_mode = obj.CutMode
PathAreaUtils.flush_nc()
PathAreaUtils.output('mem')
PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed)
if obj.UseStartPoint:
start_point = (obj.StartPoint.x, obj.StartPoint.y)
# print "a," + str(self.radius) + "," + str(extraoffset) + "," + str(stepover) + ",depthparams, " + str(from_center) + "," + str(keep_tool_down) + "," + str(use_zig_zag) + "," + str(zig_angle) + "," + str(zig_unidirectional) + "," + str(start_point) + "," + str(cut_mode)
PathAreaUtils.pocket(
a,
self.radius,
extraoffset,
stepover,
depthparams,
from_center,
keep_tool_down,
use_zig_zag,
zig_angle,
zig_unidirectional,
start_point,
cut_mode)
return PathAreaUtils.retrieve_gcode()
def buildpathocc(self, obj, shape):
"""Build pocket Path using Native OCC algorithm."""
import Part
import DraftGeomUtils
from PathScripts.PathUtils import fmt, helicalPlunge, rampPlunge, depth_params
FreeCAD.Console.PrintMessage(translate("PathPocket", "Generating toolpath with OCC native offsets.\n"))
extraoffset = obj.MaterialAllowance.Value
# Build up the offset loops
output = ""
if obj.Comment != "":
output += '(' + str(obj.Comment)+')\n'
output += 'G0 Z' + fmt(obj.ClearanceHeight.Value) + "\n"
offsets = []
nextradius = self.radius + extraoffset
result = DraftGeomUtils.pocket2d(shape, nextradius)
print "did we get something: " + str(result)
while result:
print "Adding " + str(len(result)) + " wires"
offsets.extend(result)
nextradius += (self.radius * 2) * (float(obj.StepOver)/100)
result = DraftGeomUtils.pocket2d(shape, nextradius)
# revert the list so we start with the outer wires
if obj.StartAt != 'Edge':
offsets.reverse()
plungePos = None
rampEdge = None
if obj.UseEntry:
# Try to find an entry location
toold = self.radius*2
helixBounds = DraftGeomUtils.pocket2d(shape, self.radius * (1 + obj.HelixSize))
if helixBounds:
rampD = obj.RampSize
if obj.StartAt == 'Edge':
plungePos = helixBounds[0].Edges[0].Vertexes[0].Point
else:
plungePos = offsets[0].Edges[0].Vertexes[0].Point
# If it turns out this is invalid for some reason, nuke plungePos
[perp, idx] = DraftGeomUtils.findPerpendicular(plungePos, shape.Edges)
if not perp or perp.Length < self.radius * (1 + obj.HelixSize):
plungePos = None
FreeCAD.Console.PrintError(translate("PathPocket", "Helical Entry location not found.\n"))
# FIXME: Really need to do a point-in-polygon operation to make sure this is within helixBounds
# Or some math to prove that it has to be (doubt that's true)
# Maybe reverse helixBounds and pick off that?
if plungePos is None: # If we didn't find a place to helix, how about a ramp?
FreeCAD.Console.PrintMessage(translate("PathPocket", "Attempting ramp entry.\n"))
if (offsets[0].Edges[0].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[0].Curve, Part.Circle)):
rampEdge = offsets[0].Edges[0]
# The last edge also connects with the starting location- try that
elif (offsets[0].Edges[-1].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[-1].Curve, Part.Circle)):
rampEdge = offsets[0].Edges[-1]
else:
FreeCAD.Console.PrintError(translate("PathPocket", "Ramp Entry location not found.\n"))
# print "Neither edge works: " + str(offsets[0].Edges[0]) + ", " + str(offsets[0].Edges[-1])
# FIXME: There's got to be a smarter way to find a place to ramp
# For helix-ing/ramping, know where we were last time
# FIXME: Can probably get this from the "machine"?
lastZ = obj.ClearanceHeight.Value
startPoint = None
depthparams = depth_params(
obj.ClearanceHeight.Value,
obj.SafeHeight.Value,
obj.StartDepth.Value,
obj.StepDown,
obj.FinishDepth.Value,
obj.FinalDepth.Value)
for vpos in depthparams.get_depths():
first = True
# loop over successive wires
for currentWire in offsets:
last = None
for edge in currentWire.Edges:
if not last:
# we set the base GO to our fast move to our starting pos
if first:
# If we can helix, do so
if plungePos:
output += helicalPlunge(plungePos, obj.RampAngle, vpos, lastZ, self.radius*2, obj.HelixSize, self.horizFeed)
lastZ = vpos
# Otherwise, see if we can ramp
# FIXME: This could be a LOT smarter (eg, searching for a longer leg of the edge to ramp along)
elif rampEdge:
output += rampPlunge(rampEdge, obj.RampAngle, vpos, lastZ)
lastZ = vpos
# Otherwise, straight plunge... Don't want to, but sometimes you might not have a choice.
# FIXME: At least not with the lazy ramp programming above...
else:
print "WARNING: Straight-plunging... probably not good, but we didn't find a place to helix or ramp"
startPoint = edge.Vertexes[0].Point
output += "G0 Z" + fmt(obj.ClearanceHeight.Value) + "\n"
output += "G0 X" + fmt(startPoint.x) + " Y" + fmt(startPoint.y) +\
" Z" + fmt(obj.ClearanceHeight.Value) + "\n"
first = False
# then move slow down to our starting point for our profile
last = edge.Vertexes[0].Point
output += "G1 X" + fmt(last.x) + " Y" + fmt(last.y) + " Z" + fmt(vpos) + "\n"
if DraftGeomUtils.geomType(edge) == "Circle":
point = edge.Vertexes[-1].Point
if point == last: # edges can come flipped
point = edge.Vertexes[0].Point
center = edge.Curve.Center
relcenter = center.sub(last)
v1 = last.sub(center)
v2 = point.sub(center)
if v1.cross(v2).z < 0:
output += "G2"
else:
output += "G3"
output += " X" + fmt(point.x) + " Y" + fmt(point.y) + " Z" + fmt(vpos)
output += " I" + fmt(relcenter.x) + " J" + fmt(relcenter.y) + " K" + fmt(relcenter.z)
output += "\n"
last = point
else:
point = edge.Vertexes[-1].Point
if point == last: # edges can come flipped
point = edge.Vertexes[0].Point
output += "G1 X" + fmt(point.x) + " Y" + fmt(point.y) + " Z" + fmt(vpos) + "\n"
last = point
# move back up
output += "G0 Z" + fmt(obj.ClearanceHeight.Value) + "\n"
return output
# To reload this from FreeCAD, use: import PathScripts.PathPocket; reload(PathScripts.PathPocket)
def execute(self, obj):
output = ""
toolLoad = PathUtils.getLastToolLoad(obj)
if toolLoad is None or toolLoad.ToolNumber == 0:
self.vertFeed = 100
self.horizFeed = 100
self.radius = 0.25
obj.ToolNumber = 0
obj.ToolDescription = "UNDEFINED"
else:
self.vertFeed = toolLoad.VertFeed.Value
self.horizFeed = toolLoad.HorizFeed.Value
tool = PathUtils.getTool(obj, toolLoad.ToolNumber)
if tool.Diameter == 0:
self.radius = 0.25
else:
self.radius = tool.Diameter/2
obj.ToolNumber = toolLoad.ToolNumber
obj.ToolDescription = toolLoad.Name
output += "(" + obj.Label + ")"
if obj.UserLabel == "":
obj.Label = obj.Name + " :" + obj.ToolDescription
else:
obj.Label = obj.UserLabel + " :" + obj.ToolDescription
if obj.Base:
for b in obj.Base:
for sub in b[1]:
print "object base: " + str(b)
import Part
import PathScripts.PathKurveUtils
if "Face" in sub:
print "inside"
shape = getattr(b[0].Shape, sub)
wire = shape.OuterWire
edges = wire.Edges
else:
print "in else"
edges = [getattr(b[0].Shape, sub) for sub in b[1]]
print "myedges: " + str(edges)
wire = Part.Wire(edges)
shape = None
# output = ""
if obj.Algorithm == "OCC Native":
if shape is None:
shape = wire
output += self.buildpathocc(obj, shape)
else:
try:
import area
except:
FreeCAD.Console.PrintError(translate("PathKurve", "libarea needs to be installed for this command to work.\n"))
return
a = area.Area()
if shape is None:
c = PathScripts.PathKurveUtils.makeAreaCurve(wire.Edges, 'CW')
a.append(c)
else:
for w in shape.Wires:
c = PathScripts.PathKurveUtils.makeAreaCurve(w.Edges, 'CW')
a.append(c)
a.Reorder()
output += self.buildpathlibarea(obj, a)
if obj.Active:
path = Path.Path(output)
obj.Path = path
obj.ViewObject.Visibility = True
else:
path = Path.Path("(inactive operation)")
obj.Path = path
obj.ViewObject.Visibility = False
class _CommandSetPocketStartPoint:
def GetResources(self):
return {'Pixmap': 'Path-StartPoint',
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Pick Start Point"),
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Pick Start Point")}
def IsActive(self):
return FreeCAD.ActiveDocument is not None
def setpoint(self, point, o):
obj = FreeCADGui.Selection.getSelection()[0]
obj.StartPoint.x = point.x
obj.StartPoint.y = point.y
def Activated(self):
FreeCADGui.Snapper.getPoint(callback=self.setpoint)
class ViewProviderPocket:
def __init__(self, vobj):
vobj.Proxy = self
def attach(self, vobj):
self.Object = vobj.Object
return
def setEdit(self, vobj, mode=0):
FreeCADGui.Control.closeDialog()
taskd = TaskPanel()
taskd.obj = vobj.Object
FreeCADGui.Control.showDialog(taskd)
taskd.setupUi()
return True
def getIcon(self):
return ":/icons/Path-Pocket.svg"
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class CommandPathPocket:
def GetResources(self):
return {'Pixmap': 'Path-Pocket',
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Pocket"),
'Accel': "P, O",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Creates a Path Pocket object from a face or faces")}
def IsActive(self):
return FreeCAD.ActiveDocument is not None
def Activated(self):
zbottom = 0.0
ztop = 10.0
# if everything is ok, execute and register the transaction in the undo/redo stack
FreeCAD.ActiveDocument.openTransaction(translate("PathPocket", "Create Pocket"))
FreeCADGui.addModule("PathScripts.PathPocket")
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Pocket")')
FreeCADGui.doCommand('PathScripts.PathPocket.ObjectPocket(obj)')
FreeCADGui.doCommand('obj.Active = True')
FreeCADGui.doCommand('PathScripts.PathPocket.ViewProviderPocket(obj.ViewObject)')
FreeCADGui.doCommand('from PathScripts import PathUtils')
FreeCADGui.doCommand('obj.Algorithm = "libarea"')
FreeCADGui.doCommand('obj.StepOver = 100')
FreeCADGui.doCommand('obj.ClearanceHeight = 10') # + str(bb.ZMax + 2.0))
FreeCADGui.doCommand('obj.StepDown = 1.0')
FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop))
FreeCADGui.doCommand('obj.FinalDepth =' + str(zbottom))
FreeCADGui.doCommand('obj.ZigZagAngle = 45')
FreeCADGui.doCommand('obj.UseEntry = False')
FreeCADGui.doCommand('obj.RampAngle = 3.0')
FreeCADGui.doCommand('obj.RampSize = 0.75')
FreeCADGui.doCommand('obj.HelixSize = 0.75')
FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)')
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
FreeCADGui.doCommand('obj.ViewObject.startEditing()')
class TaskPanel:
def __init__(self):
# self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/PocketEdit.ui")
self.form = FreeCADGui.PySideUic.loadUi(":/panels/PocketEdit.ui")
self.updating = False
def accept(self):
self.getFields()
FreeCADGui.ActiveDocument.resetEdit()
FreeCADGui.Control.closeDialog()
FreeCAD.ActiveDocument.recompute()
FreeCADGui.Selection.removeObserver(self.s)
def reject(self):
FreeCADGui.Control.closeDialog()
FreeCAD.ActiveDocument.recompute()
FreeCADGui.Selection.removeObserver(self.s)
def getFields(self):
if self.obj:
if hasattr(self.obj, "StartDepth"):
self.obj.StartDepth = self.form.startDepth.text()
if hasattr(self.obj, "FinalDepth"):
self.obj.FinalDepth = self.form.finalDepth.text()
if hasattr(self.obj, "SafeHeight"):
self.obj.SafeHeight = self.form.safeHeight.text()
if hasattr(self.obj, "ClearanceHeight"):
self.obj.ClearanceHeight = self.form.clearanceHeight.text()
if hasattr(self.obj, "StepDown"):
self.obj.StepDown = self.form.stepDown.value()
if hasattr(self.obj, "MaterialAllowance"):
self.obj.MaterialAllowance = self.form.extraOffset.value()
if hasattr(self.obj, "UseStartPoint"):
self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
if hasattr(self.obj, "Algorithm"):
self.obj.Algorithm = str(self.form.algorithmSelect.currentText())
if hasattr(self.obj, "CutMode"):
self.obj.CutMode = str(self.form.cutMode.currentText())
if hasattr(self.obj, "UseZigZag"):
self.obj.UseZigZag = self.form.useZigZag.isChecked()
if hasattr(self.obj, "ZigUnidirectional"):
self.obj.ZigUnidirectional = self.form.zigZagUnidirectional.isChecked()
if hasattr(self.obj, "ZigZagAngle"):
self.obj.ZigZagAngle = self.form.zigZagAngle.value()
if hasattr(self.obj, "StepOver"):
self.obj.StepOver = self.form.stepOverPercent.value()
self.obj.Proxy.execute(self.obj)
def setFields(self):
self.form.startDepth.setText(str(self.obj.StartDepth.Value))
self.form.finalDepth.setText(str(self.obj.FinalDepth.Value))
self.form.safeHeight.setText(str(self.obj.SafeHeight.Value))
self.form.clearanceHeight.setText(str(self.obj.ClearanceHeight.Value))
self.form.stepDown.setValue(self.obj.StepDown)
self.form.extraOffset.setValue(self.obj.MaterialAllowance.Value)
self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
self.form.useZigZag.setChecked(self.obj.UseZigZag)
self.form.zigZagUnidirectional.setChecked(self.obj.ZigUnidirectional)
self.form.zigZagAngle.setValue(self.obj.ZigZagAngle)
self.form.stepOverPercent.setValue(self.obj.StepOver)
index = self.form.algorithmSelect.findText(self.obj.Algorithm, QtCore.Qt.MatchFixedString)
if index >= 0:
self.form.algorithmSelect.setCurrentIndex(index)
# for i in self.obj.Base:
# self.form.baseList.addItem(i[0].Name + "." + i[1][0])
for i in self.obj.Base:
for sub in i[1]:
self.form.baseList.addItem(i[0].Name + "." + sub)
def open(self):
self.s = SelObserver()
# install the function mode resident
FreeCADGui.Selection.addObserver(self.s)
def addBase(self):
# check that the selection contains exactly what we want
selection = FreeCADGui.Selection.getSelectionEx()
if len(selection) != 1:
FreeCAD.Console.PrintError(translate("PathProject", "Please select only faces from one solid\n"))
return
sel = selection[0]
if not sel.HasSubObjects:
FreeCAD.Console.PrintError(translate("PathProject", "Please select faces from one solid\n"))
return
if not selection[0].SubObjects[0].ShapeType == "Face":
FreeCAD.Console.PrintError(translate("PathProject", "Please select faces from one solid\n"))
return
for i in sel.SubElementNames:
self.obj.Proxy.addpocketbase(self.obj, sel.Object, i)
self.setFields() # defaults may have changed. Reload.
self.form.baseList.clear()
for i in self.obj.Base:
for sub in i[1]:
self.form.baseList.addItem(i[0].Name + "." + sub)
# for i in self.obj.Base:
# self.form.baseList.addItem(i[0].Name + "." + i[1][0])
def deleteBase(self):
dlist = self.form.baseList.selectedItems()
newlist = []
for d in dlist:
for i in self.obj.Base:
if i[0].Name != d.text().partition(".")[0] or i[1] != d.text().partition(".")[2]:
newlist.append(i)
self.form.baseList.takeItem(self.form.baseList.row(d))
self.obj.Base = newlist
self.obj.Proxy.execute(self.obj)
FreeCAD.ActiveDocument.recompute()
def itemActivated(self):
FreeCADGui.Selection.clearSelection()
slist = self.form.baseList.selectedItems()
for i in slist:
objstring = i.text().partition(".")
obj = FreeCAD.ActiveDocument.getObject(objstring[0])
if objstring[2] != "":
FreeCADGui.Selection.addSelection(obj, objstring[2])
else:
FreeCADGui.Selection.addSelection(obj)
FreeCADGui.updateGui()
def reorderBase(self):
newlist = []
for i in range(self.form.baseList.count()):
s = self.form.baseList.item(i).text()
objstring = s.partition(".")
obj = FreeCAD.ActiveDocument.getObject(objstring[0])
item = (obj, str(objstring[2]))
newlist.append(item)
self.obj.Base = newlist
self.obj.Proxy.execute(self.obj)
FreeCAD.ActiveDocument.recompute()
def getStandardButtons(self):
return int(QtGui.QDialogButtonBox.Ok)
def edit(self, item, column):
if not self.updating:
self.resetObject()
def setupUi(self):
# Connect Signals and Slots
# Base Controls
self.form.baseList.itemSelectionChanged.connect(self.itemActivated)
self.form.addBase.clicked.connect(self.addBase)
self.form.deleteBase.clicked.connect(self.deleteBase)
self.form.reorderBase.clicked.connect(self.reorderBase)
# Depths
self.form.startDepth.editingFinished.connect(self.getFields)
self.form.finalDepth.editingFinished.connect(self.getFields)
self.form.stepDown.editingFinished.connect(self.getFields)
# Heights
self.form.safeHeight.editingFinished.connect(self.getFields)
self.form.clearanceHeight.editingFinished.connect(self.getFields)
# operation
self.form.algorithmSelect.currentIndexChanged.connect(self.getFields)
self.form.cutMode.currentIndexChanged.connect(self.getFields)
self.form.useStartPoint.clicked.connect(self.getFields)
self.form.extraOffset.editingFinished.connect(self.getFields)
# Pattern
self.form.stepOverPercent.editingFinished.connect(self.getFields)
self.form.useZigZag.clicked.connect(self.getFields)
self.form.zigZagUnidirectional.clicked.connect(self.getFields)
self.form.zigZagAngle.editingFinished.connect(self.getFields)
self.setFields()
sel = FreeCADGui.Selection.getSelectionEx()
if len(sel) != 0 and sel[0].HasSubObjects:
self.addBase()
class SelObserver:
def __init__(self):
import PathScripts.PathSelection as PST
PST.pocketselect()
def __del__(self):
import PathScripts.PathSelection as PST
PST.clear()
def addSelection(self, doc, obj, sub, pnt):
FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')')
FreeCADGui.updateGui()
if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_Pocket', CommandPathPocket())
FreeCADGui.addCommand('Set_PocketStartPoint', _CommandSetPocketStartPoint())
FreeCAD.Console.PrintLog("Loading PathPocket... done\n")