cleanup PathUtils.
Bug found profiling single face using occ. workaround. defaults for profile are sane. Automatically add pre-selected faces
This commit is contained in:
parent
c9d6db2173
commit
bb5165634b
|
@ -131,9 +131,15 @@ class ObjectProfile:
|
|||
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
|
||||
|
@ -273,23 +279,21 @@ print "y - " + str(point.y)
|
|||
wires.append(h.OuterWire)
|
||||
|
||||
tempshell = Part.makeShell(vfaces)
|
||||
slices = tempshell.slice(FreeCAD.Base.Vector(0, 0, 1), 1.5)
|
||||
slices = tempshell.slice(FreeCAD.Base.Vector(0, 0, 1), tempshell.CenterOfMass.z )
|
||||
|
||||
wires = wires + slices
|
||||
|
||||
for wire in wires:
|
||||
edgelist = wire.Edges
|
||||
edgelist = Part.__sortEdges__(edgelist)
|
||||
|
||||
if obj.Algorithm == "OCC Native":
|
||||
output += self._buildPathOCC(obj, wire)
|
||||
|
||||
else:
|
||||
try:
|
||||
import area
|
||||
except:
|
||||
FreeCAD.Console.PrintError(translate("Path", "libarea needs to be installed for this command to work.\n"))
|
||||
return
|
||||
edgelist = wire.Edges
|
||||
edgelist = Part.__sortEdges__(edgelist)
|
||||
output += self._buildPathLibarea(obj, edgelist)
|
||||
|
||||
if obj.Active:
|
||||
|
@ -496,6 +500,48 @@ class TaskPanel:
|
|||
self.obj.Direction = str(self.form.direction.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.OffsetExtra.Value)
|
||||
self.form.segLen.setValue(self.obj.SegLen.Value)
|
||||
self.form.rollRadius.setValue(self.obj.RollRadius.Value)
|
||||
self.form.useCompensation.setChecked(self.obj.UseComp)
|
||||
self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
|
||||
self.form.useEndPoint.setChecked(self.obj.UseEndPoint)
|
||||
|
||||
index = self.form.algorithmSelect.findText(
|
||||
self.obj.Algorithm, QtCore.Qt.MatchFixedString)
|
||||
if index >= 0:
|
||||
self.form.algorithmSelect.setCurrentIndex(index)
|
||||
|
||||
index = self.form.cutSide.findText(
|
||||
self.obj.Side, QtCore.Qt.MatchFixedString)
|
||||
if index >= 0:
|
||||
self.form.cutSide.setCurrentIndex(index)
|
||||
|
||||
index = self.form.direction.findText(
|
||||
self.obj.Direction, QtCore.Qt.MatchFixedString)
|
||||
if index >= 0:
|
||||
self.form.direction.setCurrentIndex(index)
|
||||
|
||||
for i in self.obj.Base:
|
||||
self.form.baseList.addItem(i[0].Name + "." + i[1])
|
||||
|
||||
for i in range(len(self.obj.locs)):
|
||||
item = QtGui.QTreeWidgetItem(self.form.tagTree)
|
||||
item.setText(0, str(i+1))
|
||||
l = self.obj.locs[i]
|
||||
item.setText(1, str(l.x)+", " + str(l.y) + ", " + str(l.z))
|
||||
item.setText(2, str(self.obj.heights[i]))
|
||||
item.setText(3, str(self.obj.lengths[i]))
|
||||
item.setText(4, str(self.obj.angles[i]))
|
||||
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
|
||||
item.setTextAlignment(0, QtCore.Qt.AlignLeft)
|
||||
|
||||
def open(self):
|
||||
self.s = SelObserver()
|
||||
# install the function mode resident
|
||||
|
@ -505,16 +551,23 @@ 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"))
|
||||
if len(selection) != 1:
|
||||
FreeCAD.Console.PrintError(translate("PathProject", "Please select only faces from one solid\n"))
|
||||
return
|
||||
for s in selection:
|
||||
if s.HasSubObjects:
|
||||
for i in s.SubElementNames:
|
||||
self.obj.Proxy.addprofilebase(self.obj, s.Object, i)
|
||||
else:
|
||||
self.obj.Proxy.addprofilebase(self.obj, s.Object)
|
||||
self.setupUi() # defaults may have changed. Reload.
|
||||
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
|
||||
|
||||
# if s.HasSubObjects:
|
||||
for i in sel.SubElementNames:
|
||||
self.obj.Proxy.addprofilebase(self.obj, sel.Object, i)
|
||||
#else:
|
||||
#self.obj.Proxy.addprofilebase(self.obj, s.Object)
|
||||
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])
|
||||
|
@ -644,47 +697,8 @@ class TaskPanel:
|
|||
self.updating = False
|
||||
return
|
||||
|
||||
|
||||
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.OffsetExtra.Value)
|
||||
self.form.segLen.setValue(self.obj.SegLen.Value)
|
||||
self.form.rollRadius.setValue(self.obj.RollRadius.Value)
|
||||
self.form.useCompensation.setChecked(self.obj.UseComp)
|
||||
self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
|
||||
self.form.useEndPoint.setChecked(self.obj.UseEndPoint)
|
||||
|
||||
index = self.form.algorithmSelect.findText(
|
||||
self.obj.Algorithm, QtCore.Qt.MatchFixedString)
|
||||
if index >= 0:
|
||||
self.form.algorithmSelect.setCurrentIndex(index)
|
||||
|
||||
index = self.form.cutSide.findText(
|
||||
self.obj.Side, QtCore.Qt.MatchFixedString)
|
||||
if index >= 0:
|
||||
self.form.cutSide.setCurrentIndex(index)
|
||||
|
||||
index = self.form.direction.findText(
|
||||
self.obj.Direction, QtCore.Qt.MatchFixedString)
|
||||
if index >= 0:
|
||||
self.form.direction.setCurrentIndex(index)
|
||||
|
||||
for i in self.obj.Base:
|
||||
self.form.baseList.addItem(i[0].Name + "." + i[1])
|
||||
|
||||
for i in range(len(self.obj.locs)):
|
||||
item = QtGui.QTreeWidgetItem(self.form.tagTree)
|
||||
item.setText(0, str(i+1))
|
||||
l = self.obj.locs[i]
|
||||
item.setText(1, str(l.x)+", " + str(l.y) + ", " + str(l.z))
|
||||
item.setText(2, str(self.obj.heights[i]))
|
||||
item.setText(3, str(self.obj.lengths[i]))
|
||||
item.setText(4, str(self.obj.angles[i]))
|
||||
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
|
||||
item.setTextAlignment(0, QtCore.Qt.AlignLeft)
|
||||
|
||||
# Connect Signals and Slots
|
||||
# Base Controls
|
||||
|
@ -721,6 +735,13 @@ class TaskPanel:
|
|||
self.form.addTag.clicked.connect(self.addElement)
|
||||
self.form.deleteTag.clicked.connect(self.removeElement)
|
||||
|
||||
self.setFields()
|
||||
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
if len(sel) != 0 and sel[0].HasSubObjects:
|
||||
# if sel[0].SubObjects[0].ShapeType == "Face":
|
||||
self.addBase()
|
||||
|
||||
|
||||
class SelObserver:
|
||||
def __init__(self):
|
||||
|
|
|
@ -137,20 +137,21 @@ def filterArcs(arcEdge):
|
|||
if isinstance(s.Curve, Part.Circle):
|
||||
splitlist = []
|
||||
angle = abs(s.LastParameter - s.FirstParameter)
|
||||
overhalfcircle = False
|
||||
# overhalfcircle = False
|
||||
goodarc = False
|
||||
if (angle > math.pi):
|
||||
overhalfcircle = True
|
||||
pass
|
||||
# overhalfcircle = True
|
||||
else:
|
||||
goodarc = True
|
||||
if not goodarc:
|
||||
arcstpt = s.valueAt(s.FirstParameter)
|
||||
arcmid = s.valueAt(
|
||||
(s.LastParameter - s.FirstParameter) * 0.5 + s.FirstParameter)
|
||||
arcquad1 = s.valueAt(
|
||||
(s.LastParameter - s.FirstParameter) * 0.25 + s.FirstParameter) # future midpt for arc1
|
||||
arcquad2 = s.valueAt(
|
||||
(s.LastParameter - s.FirstParameter) * 0.75 + s.FirstParameter) # future midpt for arc2
|
||||
arcquad1 = s.valueAt((s.LastParameter - s.FirstParameter)
|
||||
* 0.25 + s.FirstParameter) # future midpt for arc1
|
||||
arcquad2 = s.valueAt((s.LastParameter - s.FirstParameter)
|
||||
* 0.75 + s.FirstParameter) # future midpt for arc2
|
||||
arcendpt = s.valueAt(s.LastParameter)
|
||||
# reconstruct with 2 arcs
|
||||
arcseg1 = Part.ArcOfCircle(arcstpt, arcquad1, arcmid)
|
||||
|
@ -170,8 +171,7 @@ def filterArcs(arcEdge):
|
|||
def reverseEdge(e):
|
||||
if geomType(e) == "Circle":
|
||||
arcstpt = e.valueAt(e.FirstParameter)
|
||||
arcmid = e.valueAt(
|
||||
(e.LastParameter - e.FirstParameter) * 0.5 + e.FirstParameter)
|
||||
arcmid = e.valueAt((e.LastParameter - e.FirstParameter) * 0.5 + e.FirstParameter)
|
||||
arcendpt = e.valueAt(e.LastParameter)
|
||||
arcofCirc = Part.ArcOfCircle(arcendpt, arcmid, arcstpt)
|
||||
newedge = arcofCirc.toShape()
|
||||
|
@ -183,9 +183,7 @@ def reverseEdge(e):
|
|||
return newedge
|
||||
|
||||
|
||||
def convert(
|
||||
toolpath, Side, radius, clockwise=False, Z=0.0, firstedge=None,
|
||||
vf=1.0, hf=2.0):
|
||||
def convert(toolpath, Side, radius, clockwise=False, Z=0.0, firstedge=None, vf=1.0, hf=2.0):
|
||||
'''convert(toolpath,Side,radius,clockwise=False,Z=0.0,firstedge=None) Converts lines and arcs to G1,G2,G3 moves. Returns a string.'''
|
||||
last = None
|
||||
output = ""
|
||||
|
@ -289,8 +287,8 @@ def SortPath(wire, Side, radius, clockwise, firstedge=None, SegLen=0.5):
|
|||
geomType(e) == "BezierCurve" or \
|
||||
geomType(e) == "Ellipse":
|
||||
edgelist.append(Part.Wire(curvetowire(e, (SegLen))))
|
||||
|
||||
newwire = Part.Wire(edgelist)
|
||||
|
||||
if Side == 'Left':
|
||||
# we use the OCC offset feature
|
||||
offset = newwire.makeOffset(radius) # tool is outside line
|
||||
|
@ -308,6 +306,9 @@ def SortPath(wire, Side, radius, clockwise, firstedge=None, SegLen=0.5):
|
|||
def MakePath(wire, Side, radius, clockwise, ZClearance, StepDown, ZStart, ZFinalDepth, firstedge=None, PathClosed=True, SegLen=0.5, VertFeed=1.0, HorizFeed=2.0):
|
||||
''' makes the path - just a simple profile for now '''
|
||||
offset = SortPath(wire, Side, radius, clockwise, firstedge, SegLen=0.5)
|
||||
if len(offset.Edges) == 0:
|
||||
return ""
|
||||
|
||||
toolpath = offset.Edges[:]
|
||||
paths = ""
|
||||
paths += "G0 Z" + str(ZClearance) + "\n"
|
||||
|
@ -366,10 +367,10 @@ def getLastTool(obj):
|
|||
|
||||
def getLastToolLoad(obj):
|
||||
# This walks up the hierarchy and tries to find the closest preceding
|
||||
# ToolLoadOject (tlo).
|
||||
# toolchange.
|
||||
|
||||
import PathScripts
|
||||
tlo = None
|
||||
tc = None
|
||||
lastfound = None
|
||||
|
||||
try:
|
||||
|
@ -379,30 +380,30 @@ def getLastToolLoad(obj):
|
|||
parent = None
|
||||
|
||||
while parent is not None:
|
||||
|
||||
sibs = parent.Group
|
||||
for g in sibs:
|
||||
if isinstance(g.Proxy, PathScripts.PathLoadTool.LoadTool):
|
||||
lastfound = g
|
||||
if g == child:
|
||||
tlo = lastfound
|
||||
tc = lastfound
|
||||
|
||||
if tlo is None:
|
||||
if tc is None:
|
||||
try:
|
||||
child = parent
|
||||
parent = child.InList[0]
|
||||
parent = parent.InList[0]
|
||||
except:
|
||||
parent = None
|
||||
else:
|
||||
return tlo
|
||||
return tc
|
||||
|
||||
if tlo is None:
|
||||
for g in FreeCAD.ActiveDocument.Objects: # Look in top level
|
||||
if hasattr(g, "Proxy"):
|
||||
if isinstance(g.Proxy, PathScripts.PathLoadTool.LoadTool):
|
||||
lastfound = g
|
||||
if tc is None:
|
||||
for g in FreeCAD.ActiveDocument.Objects: # top level object
|
||||
if isinstance(g.Proxy, PathScripts.PathLoadTool.LoadTool):
|
||||
lastfound = g
|
||||
if g == obj:
|
||||
tlo = lastfound
|
||||
return tlo
|
||||
tc = lastfound
|
||||
return tc
|
||||
|
||||
|
||||
def getTool(obj, number=0):
|
||||
|
@ -482,177 +483,6 @@ def frange(start, stop, step, finish):
|
|||
|
||||
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:
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user