defaults if objects pre-selected. Fixes

Smarter default for inside outside profiles

first commit
This commit is contained in:
sliptonic 2016-05-13 08:28:27 -05:00 committed by Yorik van Havre
parent bb5165634b
commit be03c2ad26
5 changed files with 513 additions and 32 deletions

View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ToolControlObject</class>
<widget class="QDialog" name="ToolControlObject">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>320</width>
<height>503</height>
</rect>
</property>
<property name="windowTitle">
<string>Tool Control</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLineEdit" name="tcoName"/>
</item>
<item row="1" column="0">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="txtToolType">
<property name="text">
<string>Unknown</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="txtToolName">
<property name="text">
<string>Unknown</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="txtToolMaterial">
<property name="text">
<string>Unknown</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QComboBox" name="cboToolSelect"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="txtToolDiameter">
<property name="text">
<string>Unknown</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Horiz. Feed</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="horizFeed"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Vert. Feed</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="vertFeed"/>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Spindle Speed (RPM)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="spindleSpeed"/>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cboSpindleDirection">
<item>
<property name="text">
<string>Forward</string>
</property>
</item>
<item>
<property name="text">
<string>Reverse</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -120,7 +120,12 @@ class _ViewProviderLoadTool:
def setEdit(self, vobj, mode):
# this is executed when the object is double-clicked in the tree
pass
FreeCADGui.Control.closeDialog()
taskd = TaskPanel()
taskd.obj = vobj.Object
FreeCADGui.Control.showDialog(taskd)
taskd.setupUi()
return True
def unsetEdit(self, vobj, mode):
# this is executed when the user cancels or terminates edit mode
@ -167,6 +172,119 @@ PathUtils.addToProject(obj)
PathUtils.addToProject(obj)
class TaskPanel:
def __init__(self):
#self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolControl.ui")
self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/ToolControl.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, "VertFeed"):
self.obj.Label = self.form.tcoName.text()
if hasattr(self.obj, "VertFeed"):
self.obj.VertFeed = self.form.vertFeed.value()
if hasattr(self.obj, "HorizFeed"):
self.obj.HorizFeed = self.form.horizFeed.value()
if hasattr(self.obj, "SpindleSpeed"):
self.obj.SpindleSpeed = self.form.spindleSpeed.value()
if hasattr(self.obj, "SpindleDir"):
self.obj.SpindleDir = str(self.form.cboSpindleDirection.currentText())
if hasattr(self.obj, "ToolNumber"):
self.obj.ToolNumber = self.form.ToolNumber.value()
self.obj.Proxy.execute(self.obj)
def setFields(self):
self.form.vertFeed.setText(str(self.obj.VertFeed.Value))
self.form.horizFeed.setText(str(self.obj.HorizFeed.Value))
self.form.spindleSpeed.setText(str(self.obj.SpindleSpeed.Value))
self.form.cboSpindleDirection.setText(str(self.obj.SpindleDir.Value))
self.form.ToolNumber.setValue(self.obj.ToolNumber)
def open(self):
self.s = SelObserver()
# install the function mode resident
FreeCADGui.Selection.addObserver(self.s)
def getStandardButtons(self):
return int(QtGui.QDialogButtonBox.Ok)
def edit(self, item, column):
if not self.updating:
self.resetObject()
def resetObject(self, remove=None):
"transfers the values from the widget to the object"
# loc = []
# h = []
# l = []
# a = []
# for i in range(self.form.tagTree.topLevelItemCount()):
# it = self.form.tagTree.findItems(
# str(i+1), QtCore.Qt.MatchExactly, 0)[0]
# if (remove is None) or (remove != i):
# if it.text(1):
# x = float(it.text(1).split()[0].rstrip(","))
# y = float(it.text(1).split()[1].rstrip(","))
# z = float(it.text(1).split()[2].rstrip(","))
# loc.append(Vector(x, y, z))
# else:
# loc.append(0.0)
# if it.text(2):
# h.append(float(it.text(2)))
# else:
# h.append(4.0)
# if it.text(3):
# l.append(float(it.text(3)))
# else:
# l.append(5.0)
# if it.text(4):
# a.append(float(it.text(4)))
# else:
# a.append(45.0)
# self.obj.locs = loc
# self.obj.heights = h
# self.obj.lengths = l
# self.obj.angles = a
# self.obj.touch()
FreeCAD.ActiveDocument.recompute()
def setupUi(self):
pass
# Connect Signals and Slots
# Base Controls
# self.form.baseList.itemSelectionChanged.connect(self.itemActivated)
self.setFields()
class SelObserver:
def __init__(self):
pass
def __del__(self):
pass
if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_LoadTool', CommandPathLoadTool())

View File

@ -118,6 +118,23 @@ class ObjectPocket:
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 < bb.ZMax:
# obj.FinalDepth = fbb.ZMax
# else:
# obj.FinalDepth = bb.ZMin
# except:
# obj.StartDepth = 5.0
# obj.ClearanceHeight = 10.0
# obj.SafeHeight = 8.0
try:
bb = ss.Shape.BoundBox # parent boundbox
subobj = ss.Shape.getElement(sub)
@ -126,15 +143,23 @@ class ObjectPocket:
obj.ClearanceHeight = bb.ZMax + 5.0
obj.SafeHeight = bb.ZMax + 3.0
if fbb.ZMax < bb.ZMax:
obj.FinalDepth = fbb.ZMax
else:
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"))
@ -219,9 +244,9 @@ class ObjectPocket:
offsets = []
nextradius = self.radius
result = DraftGeomUtils.pocket2d(shape, nextradius)
print "did we get something: " + str(result)
while result:
# print "Adding " + str(len(result)) + " wires"
print "Adding " + str(len(result)) + " wires"
offsets.extend(result)
nextradius += self.radius
result = DraftGeomUtils.pocket2d(shape, nextradius)
@ -495,7 +520,7 @@ class CommandPathPocket:
FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop))
FreeCADGui.doCommand('obj.FinalDepth =' + str(zbottom))
FreeCADGui.doCommand('obj.ZigZagAngle = 45')
FreeCADGui.doCommand('obj.UseEntry = True')
FreeCADGui.doCommand('obj.UseEntry = False')
FreeCADGui.doCommand('obj.RampAngle = 3.0')
FreeCADGui.doCommand('obj.RampSize = 0.75')
FreeCADGui.doCommand('obj.HelixSize = 0.75')
@ -547,6 +572,24 @@ class TaskPanel:
self.obj.CutMode = str(self.form.cutMode.currentText())
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)
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])
def open(self):
self.s = SelObserver()
# install the function mode resident
@ -556,17 +599,31 @@ class TaskPanel:
# check that the selection contains exactly what we want
selection = FreeCADGui.Selection.getSelectionEx()
if not len(selection) >= 1:
FreeCAD.Console.PrintError(translate("PathProject", "Please select at least one profileable object\n"))
return
for s in selection:
if s.HasSubObjects:
for i in s.SubElementNames:
self.obj.Proxy.addpocketbase(self.obj, s.Object, i)
else:
self.obj.Proxy.addpocketbase(self.obj, s.Object)
# if not len(selection) >= 1:
# FreeCAD.Console.PrintError(translate("PathProject", "Please select at least one profileable object\n"))
# return
# for s in selection:
# if s.HasSubObjects:
# for i in s.SubElementNames:
# self.obj.Proxy.addpocketbase(self.obj, s.Object, i)
# else:
# self.obj.Proxy.addpocketbase(self.obj, s.Object)
self.setupUi() # defaults may have changed. Reload.
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:
self.form.baseList.addItem(i[0].Name + "." + i[1])
@ -618,20 +675,6 @@ class TaskPanel:
self.resetObject()
def setupUi(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)
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])
# Connect Signals and Slots
# Base Controls
@ -655,6 +698,12 @@ class TaskPanel:
self.form.useStartPoint.clicked.connect(self.getFields)
self.form.extraOffset.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):

View File

@ -58,6 +58,7 @@ class ObjectProfile:
obj.addProperty("App::PropertyLinkSubList", "Base", "Path", translate("Parent Object", "The base geometry of this toolpath"))
obj.addProperty("App::PropertyBool", "Active", "Path", translate("Path", "Make False, to prevent operation from generating code"))
obj.addProperty("App::PropertyString", "Comment", "Path", translate("Path", "An optional comment for this profile"))
obj.addProperty("App::PropertyString", "UserLabel", "Path", translate("Path", "User Assigned Label"))
obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm", translate("Path", "The library or algorithm used to generate the path"))
obj.Algorithm = ['OCC Native', 'libarea']
@ -120,6 +121,10 @@ class ObjectProfile:
def __setstate__(self, state):
return None
# def onChanged(self, obj, prop):
# if prop == "Label":
# print "we're here"
def addprofilebase(self, obj, ss, sub=""):
baselist = obj.Base
if len(baselist) == 0: # When adding the first base object, guess at heights
@ -146,6 +151,11 @@ class ObjectProfile:
obj.ClearanceHeight = 10.0
obj.SafeHeight = 8.0
if bb.XLength == fbb.XLength and bb.YLength == fbb.YLength:
obj.Side = "Left"
else:
obj.Side = "Right"
item = (ss, sub)
if item in baselist:
FreeCAD.Console.PrintWarning("this object already in the list" + "\n")
@ -259,6 +269,7 @@ print "y - " + str(point.y)
tool = PathUtils.getTool(obj, toolLoad.ToolNumber)
self.radius = tool.Diameter/2
obj.ToolNumber = toolLoad.ToolNumber
#obj.Label = obj.Label + "(" + toolLoad.Label + ")"
if obj.Base:
hfaces = []
@ -739,7 +750,6 @@ class TaskPanel:
sel = FreeCADGui.Selection.getSelectionEx()
if len(sel) != 0 and sel[0].HasSubObjects:
# if sel[0].SubObjects[0].ShapeType == "Face":
self.addBase()

View File

@ -482,6 +482,175 @@ def frange(start, stop, step, finish):
x.append(curdepth)
return x
def rapid(x=None, y=None, z=None):
""" Returns gcode string to perform a rapid move."""
retstr = "G00"
if (x is not None) or (y is not None) or (z is not None):
if (x is not None):
retstr += " X" + str("%.4f" % x)
if (y is not None):
retstr += " Y" + str("%.4f" % y)
if (z is not None):
retstr += " Z" + str("%.4f" % z)
else:
return ""
return retstr + "\n"
def feed(x=None, y=None, z=None, horizFeed=0, vertFeed=0):
""" Return gcode string to perform a linear feed."""
global feedxy
retstr = "G01 F"
if(x is None) and (y is None):
retstr += str("%.4f" % horizFeed)
else:
retstr += str("%.4f" % vertFeed)
if (x is not None) or (y is not None) or (z is not None):
if (x is not None):
retstr += " X" + str("%.4f" % x)
if (y is not None):
retstr += " Y" + str("%.4f" % y)
if (z is not None):
retstr += " Z" + str("%.4f" % z)
else:
return ""
return retstr + "\n"
def arc(cx, cy, sx, sy, ex, ey, horizFeed=0, ez=None, ccw=False):
"""
Return gcode string to perform an arc.
Assumes XY plane or helix around Z
Don't worry about starting Z- assume that's dealt with elsewhere
If start/end radii aren't within eps, abort.
cx, cy -- arc center coordinates
sx, sy -- arc start coordinates
ex, ey -- arc end coordinates
ez -- ending Z coordinate. None unless helix.
horizFeed -- horiz feed speed
ccw -- arc direction
"""
eps = 0.01
if (math.sqrt((cx - sx)**2 + (cy - sy)**2) - math.sqrt((cx - ex)**2 + (cy - ey)**2)) >= eps:
print "ERROR: Illegal arc: Start and end radii not equal"
return ""
retstr = ""
if ccw:
retstr += "G03 F" + str(horizFeed)
else:
retstr += "G02 F" + str(horizFeed)
retstr += " X" + str("%.4f" % ex) + " Y" + str("%.4f" % ey)
if ez is not None:
retstr += " Z" + str("%.4f" % ez)
retstr += " I" + str("%.4f" % (cx - sx)) + " J" + str("%.4f" % (cy - sy))
return retstr + "\n"
def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed):
"""
Return gcode string to perform helical entry move.
plungePos -- vector of the helical entry location
destZ -- the lowest Z position or milling level
startZ -- Starting Z position for helical move
rampangle -- entry angle
toold -- tool diameter
plungeR -- the radius of the entry helix
"""
# toold = self.radius * 2
helixCmds = "(START HELICAL PLUNGE)\n"
if(plungePos is None):
raise Exception("Helical plunging requires a position!")
return None
helixX = plungePos.x + toold/2 * plungeR
helixY = plungePos.y
helixCirc = math.pi * toold * plungeR
dzPerRev = math.sin(rampangle/180. * math.pi) * helixCirc
# Go to the start of the helix position
helixCmds += rapid(helixX, helixY)
helixCmds += rapid(z=startZ)
# Helix as required to get to the requested depth
lastZ = startZ
curZ = max(startZ-dzPerRev, destZ)
done = False
while not done:
done = (curZ == destZ)
# NOTE: FreeCAD doesn't render this, but at least LinuxCNC considers it valid
# helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX, helixY, ez = curZ, ccw=True)
# Use two half-helixes; FreeCAD renders that correctly,
# and it fits with the other code breaking up 360-degree arcs
helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX - toold * plungeR, helixY, horizFeed, ez=(curZ + lastZ)/2., ccw=True)
helixCmds += arc(plungePos.x, plungePos.y, helixX - toold * plungeR, helixY, helixX, helixY, horizFeed, ez=curZ, ccw=True)
lastZ = curZ
curZ = max(curZ - dzPerRev, destZ)
return helixCmds
def rampPlunge(edge, rampangle, destZ, startZ):
"""
Return gcode string to linearly ramp down to milling level.
edge -- edge to follow
rampangle -- entry angle
destZ -- Final Z depth
startZ -- Starting Z depth
FIXME: This ramps along the first edge, assuming it's long
enough, NOT just wiggling back and forth by ~0.75 * toolD.
Not sure if that's any worse, but it's simpler
I think this should be changed to be limited to a maximum ramp size. Otherwise machine time will get longer than it needs to be.
"""
rampCmds = "(START RAMP PLUNGE)\n"
if(edge is None):
raise Exception("Ramp plunging requires an edge!")
return None
sPoint = edge.Vertexes[0].Point
ePoint = edge.Vertexes[1].Point
# Evidently edges can get flipped- pick the right one in this case
# FIXME: This is iffy code, based on what already existed in the "for vpos ..." loop below
if ePoint == sPoint:
# print "FLIP"
ePoint = edge.Vertexes[-1].Point
rampDist = edge.Length
rampDZ = math.sin(rampangle/180. * math.pi) * rampDist
rampCmds += rapid(sPoint.x, sPoint.y)
rampCmds += rapid(z=startZ)
# Ramp down to the requested depth
# FIXME: This might be an arc, so handle that as well
curZ = max(startZ-rampDZ, destZ)
done = False
while not done:
done = (curZ == destZ)
# If it's an arc, handle it!
if isinstance(edge.Curve, Part.Circle):
raise Exception("rampPlunge: Screw it, not handling an arc.")
# Straight feed! Easy!
else:
rampCmds += feed(ePoint.x, ePoint.y, curZ)
rampCmds += feed(sPoint.x, sPoint.y)
curZ = max(curZ - rampDZ, destZ)
return rampCmds
class depth_params: