Merge pull request #97 from dbtayl/master

Helical/linear ramp plunging
This commit is contained in:
Yorik van Havre 2016-03-01 17:08:19 -03:00
commit 1ec066553e
3 changed files with 250 additions and 21 deletions

View File

@ -2075,20 +2075,8 @@ def rebaseWire(wire,vidx):
if vidx > len(wire.Vertexes):
#print("Vertex index above maximum\n")
return wire
basepoint = wire.Vertexes[vidx-1].Point
#wire = Part.__sortEdges__(wire)
edges = []
start = False
for i in range(len(wire.Edges)):
if wire.Edges[i].Vertexes[0].Point == basepoint:
start = True
edges.append(wire.Edges[i])
elif start:
edges.append(wire.Edges[i])
if len(edges) < len(wire.Edges):
f = len(wire.Edges) - len(edges)
edges.extend(wire.Edges[0:f])
return Part.Wire(edges)
#This can be done in one step
return Part.Wire(wire.Edges[vidx-1:] + wire.Edges[:vidx-1])
# circle functions *********************************************************

View File

@ -288,12 +288,14 @@ void ViewProviderPath::updateData(const App::Property* prop)
if (angle == 0)
angle = M_PI * 2;
int segments = 3/(deviation/angle); //we use a rather simple rule here, provisorily
double dZ = (next.z - last.z)/segments; //How far each sigment will helix in Z
for (int j = 1; j < segments; j++) {
//std::cout << "vector " << j << std::endl;
Base::Vector3d inter;
Base::Rotation rot(norm,(angle/segments)*j);
//std::cout << "angle " << (angle/segments)*j << std::endl;
rot.multVec((last - center),inter);
inter.z = dZ * j; //Enable displaying helices
//std::cout << "result " << inter.x << " , " << inter.y << " , " << inter.z << std::endl;
points.push_back( center + inter);
colorindex.push_back(1);

View File

@ -25,6 +25,7 @@
import FreeCAD,Path
from PySide import QtCore,QtGui
from PathScripts import PathUtils
import math
FreeCADGui = None
if FreeCAD.GuiUp:
@ -129,6 +130,7 @@ class ObjectPocket:
#To reload this from FreeCAD, use: import PathScripts.PathPocket; reload(PathScripts.PathPocket)
def execute(self,obj):
if obj.Base:
tool = PathUtils.getLastTool(obj)
@ -154,12 +156,13 @@ class ObjectPocket:
# save tool
if obj.ToolNumber > 0 and tool.ToolNumber != obj.ToolNumber:
output += "M06 T" + str(tool.ToolNumber) + "\n"
# build offsets
offsets = []
nextradius = radius
result = DraftGeomUtils.pocket2d(shape,nextradius)
while result:
#print "Adding " + str(len(result)) + " wires"
offsets.extend(result)
nextradius += radius
result = DraftGeomUtils.pocket2d(shape,nextradius)
@ -179,10 +182,231 @@ class ObjectPocket:
# print "finishDepth" + str(obj.FinishDepth)
# print "offsets:", len(offsets)
def prnt(vlu): return str(round(vlu, 4))
def prnt(vlu): return str("%.4f" % round(vlu, 4))
#Fraction of tool radius our plunge helix is to be
#FIXME: This should be configurable
plungeR = 0.75
#(minimum) Fraction of tool DIAMETER to go back and forth while ramp-plunging
#FIXME: This should be configurable
#FIXME: The ramp plunging should maybe even be limited to this distance; I don't know what's best
rampD = 0.75
#Total offset from the desired pocket edge is tool radius plus the plunge helix radius
#Any point on these curves could be the center of a plunge
helixBounds = DraftGeomUtils.pocket2d(shape, tool.Diameter / 2. * (1 + plungeR))
#Try to find a location to nicely plunge, starting with a helix, then ramp
#Can't do it without knowledge of a tool
plungePos = None
rampEdge = None
if not tool:
raise Error("Ramp plunge location-finding requires a tool")
return
else:
#Since we're going to start machining either the inner-most
#edge or the outer (depending on StartAt setting), try to
#plunge near that location
if helixBounds:
#Edge is easy- pick a point on helixBounds and go with it
if obj.StartAt == 'Edge':
plungePos = helixBounds[0].Edges[0].Vertexes[0].Point
#Center is harder- use a point from the first offset, check if it works
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 < tool.Diameter / 2. * (1 + plungeR):
plungePos = None
#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 we didn't find a place to helix, how about a ramp?
if not plungePos:
#Check first edge of our offsets
if (offsets[0].Edges[0].Length >= tool.Diameter * 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 >= tool.Diameter * rampD) and not (isinstance(offsets[0].Edges[-1].Curve, Part.Circle)):
rampEdge = offsets[0].Edges[-1]
else:
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
#Returns gcode to perform a rapid move
def rapid(x=None, y=None, z=None):
retstr = "G00"
if (x != None) or (y != None) or (z != None):
if (x != None):
retstr += " X" + str("%.4f" % x)
if (y != None):
retstr += " Y" + str("%.4f" % y)
if (z != None):
retstr += " Z" + str("%.4f" % z)
else:
return ""
return retstr + "\n"
#Returns gcode to perform a linear feed
def feed(x=None, y=None, z=None):
global feedxy
retstr = "G01 F"
if(x == None) and (y == None):
retstr += str("%.4f" % obj.HorizFeed)
else:
retstr += str("%.4f" % obj.VertFeed)
if (x != None) or (y != None) or (z != None):
if (x != None):
retstr += " X" + str("%.4f" % x)
if (y != None):
retstr += " Y" + str("%.4f" % y)
if (z != None):
retstr += " Z" + str("%.4f" % z)
else:
return ""
return retstr + "\n"
#Returns gcode to perform an arc
#Assumes XY plane or helix around Z
#Don't worry about starting Z- assume that's dealt with elsewhere
def arc(cx, cy, sx, sy, ex, ey, ez=None, ccw=False):
#If start/end radii aren't within eps, abort
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: Stand and end radii not equal"
return ""
#Set [C]CW and feed
retstr = ""
if ccw:
retstr += "G03 F"
else:
retstr += "G02 F"
retstr += str(obj.HorizFeed)
#End location
retstr += " X" + str("%.4f" % ex) + " Y" + str("%.4f" % ey)
#Helix if requested
if ez != None:
retstr += " Z" + str("%.4f" % ez)
#Append center offsets
retstr += " I" + str("%.4f" % (cx - sx)) + " J" + str("%.4f" % (cy - sy))
return retstr + "\n"
#Returns gcode to helically plunge
#destZ is the milling level
#startZ is the height we can safely feed down to before helix-ing
def helicalPlunge(plungePos, rampangle, destZ, startZ):
helixCmds = "(START HELICAL PLUNGE)\n"
if(plungePos == None):
raise Error("Helical plunging requires a position!")
return None
if(not tool):
raise Error("Helical plunging requires a tool!")
return None
helixX = plungePos.x + tool.Diameter/2. * plungeR
helixY = plungePos.y;
helixCirc = math.pi * tool.Diameter * 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 - tool.Diameter * plungeR, helixY, ez = (curZ + lastZ)/2., ccw=True)
helixCmds += arc(plungePos.x, plungePos.y, helixX - tool.Diameter * plungeR, helixY, helixX, helixY, ez = curZ, ccw=True)
lastZ = curZ
curZ = max(curZ - dzPerRev, destZ)
return helixCmds
#Returns commands to linearly ramp into a cut
#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
#FIXME: This code is untested
def rampPlunge(edge, rampangle, destZ, startZ):
rampCmds = "(START RAMP PLUNGE)\n"
if(edge == None):
raise Error("Ramp plunging requires an edge!")
return None
if(not tool):
raise Error("Ramp plunging requires a tool!")
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
#print "Start: " + str(sPoint) + " End: " + str(ePoint) + " Zhigh: " + prnt(startZ) + " ZLow: " + prnt(destZ)
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
lastZ = startZ
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 Error("rampPlunge: Screw it, not handling an arc.")
#Straight feed! Easy!
else:
rampCmds += feed(ePoint.x, ePoint.y, curZ)
rampCmds += feed(sPoint.x, sPoint.y)
lastZ = curZ
curZ = max(curZ - rampDZ, destZ)
return rampCmds
#For helix-ing/ramping, know where we were last time
#FIXME: Can probably get this from the "machine"?
lastZ = fastZPos
for vpos in frange(obj.StartDepth, obj.FinalDepth, obj.StepDown, obj.FinishDepth):
# print "vpos: " + str(vpos)
#Every for every depth we should helix down
first = True
# loop over successive wires
for currentWire in offsets:
# print "new line (offset)"
@ -192,14 +416,29 @@ class ObjectPocket:
if not last:
# we set the base GO to our fast move to our starting pos
if first:
startPoint = edge.Vertexes[0].Point
output += "G0 X" + prnt(startPoint.x) + " Y" + prnt(startPoint.y) +\
" Z" + prnt(fastZPos) + "\n"
#If we can helix, do so
if plungePos:
output += helicalPlunge(plungePos, 3, vpos, lastZ)
#print output
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, 3, 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 X" + prnt(startPoint.x) + " Y" + prnt(startPoint.y) +\
" Z" + prnt(fastZPos) + "\n"
first = False
#then move slow down to our starting point for our profile
last = edge.Vertexes[0].Point
output += "G1 X" + prnt(last.x) + " Y" + prnt(last.y) + " Z" + prnt(vpos) + "\n"
if isinstance(edge.Curve,Part.Circle):
#if isinstance(edge.Curve,Part.Circle):
if DraftGeomUtils.geomType(edge) == "Circle":
point = edge.Vertexes[-1].Point
if point == last: # edges can come flipped
point = edge.Vertexes[0].Point
@ -224,7 +463,7 @@ class ObjectPocket:
last = point
#move back up
output += "G1 Z" + prnt(fastZPos) + "\n"
output += "G0 Z" + prnt(fastZPos) + "\n"
# print output
# path = Path.Path(output)
# obj.Path = path