Path: Surface op improvements for drop cutter algo

This commit is contained in:
pekkaroi
2018-07-07 12:05:33 +03:00
committed by Yorik van Havre
parent 56cd699183
commit 097f73afcb

View File

@@ -26,10 +26,10 @@ from __future__ import print_function
import FreeCAD
import MeshPart
#import Part
# import Part
import Path
import PathScripts.PathLog as PathLog
#import PathScripts.PathPocketBase as PathPocketBase
# import PathScripts.PathPocketBase as PathPocketBase
import PathScripts.PathUtils as PathUtils
import PathScripts.PathOp as PathOp
@@ -52,6 +52,7 @@ else:
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
class ObjectSurface(PathOp.ObjectOp):
'''Proxy object for Surfacing operation.'''
def baseObject(self):
@@ -64,28 +65,51 @@ class ObjectSurface(PathOp.ObjectOp):
# return 0
def opFeatures(self, obj):
'''opFeatures(obj) ... return all standard features and edges based geomtries'''
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureHeights | PathOp.FeatureStepDown | PathOp.FeatureFinishDepth;
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureHeights | PathOp.FeatureStepDown
def initOperation(self, obj):
'''initPocketOp(obj) ... create facing specific properties'''
obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm", QtCore.QT_TRANSLATE_NOOP("App::Property", "The library to use to generate the path"))
obj.Algorithm = ['OCL Dropcutter', 'OCL Waterline']
obj.addProperty("App::PropertyEnumeration", "DropCutterDir", "Algorithm", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction along which dropcutter lines are created"))
obj.addProperty("App::PropertyEnumeration", "BoundBox", "Algorithm", QtCore.QT_TRANSLATE_NOOP("App::Property", "Should the operation be limited by the stock object or by the bounding box of the base object"))
obj.addProperty("App::PropertyVector", "DropCutterExtraOffset", "Algorithm", QtCore.QT_TRANSLATE_NOOP("App::Property", "Additional offset to the selected bounding box"))
obj.addProperty("App::PropertyFloat", "StepOver", "Surface", QtCore.QT_TRANSLATE_NOOP("App::Property", "Step over percentage of the drop cutter path"))
obj.addProperty("App::PropertyDistance", "DepthOffset", "Surface", QtCore.QT_TRANSLATE_NOOP("App::Property", "Z-axis offset from the surface of the object"))
obj.addProperty("App::PropertyFloatConstraint", "SampleInterval", "Surface", QtCore.QT_TRANSLATE_NOOP("App::Property", "The Sample Interval. Small values cause long wait times"))
obj.BoundBox = ['Stock', 'BaseBoundBox']
obj.DropCutterDir = ['X', 'Y']
obj.Algorithm = ['OCL Dropcutter', 'OCL Waterline']
obj.SampleInterval = (0.04, 0.01, 1.0, 0.01)
def opExecute(self, obj):
'''opExecute(obj) ... process engraving operation'''
PathLog.track()
self.setEditorProperties(obj)
def setEditorProperties(self, obj):
if obj.Algorithm == 'OCL Dropcutter':
obj.setEditorMode('DropCutterDir', 0)
obj.setEditorMode('DropCutterExtraOffset', 0)
else:
obj.setEditorMode('DropCutterDir', 2)
obj.setEditorMode('DropCutterExtraOffset', 2)
def onChanged(self, obj, prop):
if prop == "Algorithm":
self.setEditorProperties(obj)
def opExecute(self, obj):
'''opExecute(obj) ... process surface operation'''
PathLog.track()
print("StepOver is " + str(obj.StepOver))
if obj.StepOver > 100:
obj.StepOver = 100
if obj.StepOver < 0.001:
obj.StepOver = 0.001
output = ""
if obj.Comment != "":
output += '(' + str(obj.Comment)+')\n'
output += '(' + str(obj.Comment) + ')\n'
output += "(" + obj.Label + ")"
output += "(Compensated Tool Path. Diameter: " + str(obj.ToolController.Tool.Diameter) + ")"
parentJob = PathUtils.findParentJob(obj)
if parentJob is None:
return
@@ -97,7 +121,7 @@ class ObjectSurface(PathOp.ObjectOp):
import ocl
except:
FreeCAD.Console.PrintError(
translate("Path_Surface", "This operation requires OpenCamLib to be installed.")+"\n")
translate("Path_Surface", "This operation requires OpenCamLib to be installed.") + "\n")
return
if self.baseobject.TypeId.startswith('Mesh'):
@@ -111,16 +135,20 @@ class ObjectSurface(PathOp.ObjectOp):
deflection = PathPreferences.defaultGeometryTolerance()
self.baseobject.Shape.tessellate(0.5)
mesh = MeshPart.meshFromShape(self.baseobject.Shape, Deflection=deflection)
bb = mesh.BoundBox
if obj.BoundBox == "BaseBoundBox":
bb = mesh.BoundBox
else:
bb = parentJob.Stock.Shape.BoundBox
s = ocl.STLSurf()
for f in mesh.Facets:
p = f.Points[0]
q = f.Points[1]
r = f.Points[2]
t = ocl.Triangle(ocl.Point(p[0], p[1], p[2]), ocl.Point(
q[0], q[1], q[2]), ocl.Point(r[0], r[1], r[2]))
# offset the triangle in Z with DepthOffset
t = ocl.Triangle(ocl.Point(p[0], p[1], p[2] + obj.DepthOffset.Value),
ocl.Point(q[0], q[1], q[2] + obj.DepthOffset.Value),
ocl.Point(r[0], r[1], r[2] + obj.DepthOffset.Value))
s.addTriangle(t)
if obj.Algorithm == 'OCL Dropcutter':
@@ -137,25 +165,25 @@ class ObjectSurface(PathOp.ObjectOp):
def drawLoops(loops):
nloop = 0
pp = []
pp.append(Path.Command("(waterline begin)" ))
pp.append(Path.Command("(waterline begin)"))
for loop in loops:
p = loop[0]
pp.append(Path.Command("(loop begin)" ))
pp.append(Path.Command("(loop begin)"))
pp.append(Path.Command('G0', {"Z": obj.SafeHeight.Value, 'F': self.vertRapid}))
pp.append(Path.Command('G0', {'X': p.x, "Y": p.y, 'F': self.horizRapid}))
pp.append(Path.Command('G1', {"Z": p.z, 'F': self.vertFeed}))
for p in loop[1:]:
pp.append(Path.Command('G1', {'X': p.x, "Y": p.y, "Z": p.z, 'F': self.horizFeed}))
# zheight = p.z
# zheight = p.z
p = loop[0]
pp.append(Path.Command('G1', {'X': p.x, "Y": p.y, "Z": p.z, 'F': self.horizFeed}))
pp.append(Path.Command("(loop end)" ))
pp.append(Path.Command("(loop end)"))
print(" loop ", nloop, " with ", len(loop), " points")
nloop = nloop + 1
pp.append(Path.Command("(waterline end)" ))
pp.append(Path.Command("(waterline end)"))
return pp
@@ -167,7 +195,12 @@ class ObjectSurface(PathOp.ObjectOp):
wl = ocl.Waterline()
wl.setSTL(s)
cutter = ocl.CylCutter(obj.ToolController.Tool.Diameter, 5)
if obj.ToolController.Tool.ToolType == 'BallEndMill':
cutter = ocl.BallCutter(obj.ToolController.Tool.Diameter, 5) # TODO: 5 represents cutting edge height. Should be replaced with the data from toolcontroller?
else:
cutter = ocl.CylCutter(obj.ToolController.Tool.Diameter, 5)
wl.setCutter(cutter)
# this should be smaller than the smallest details in the STL file
wl.setSampling(obj.SampleInterval)
@@ -195,37 +228,55 @@ class ObjectSurface(PathOp.ObjectOp):
def _dropcutter(self, obj, s, bb):
import ocl
import time
if obj.ToolController.Tool.ToolType == 'BallEndMill':
cutter = ocl.BallCutter(obj.ToolController.Tool.Diameter, 5) # TODO: 5 represents cutting edge height. Should be replaced with the data from toolcontroller?
else:
cutter = ocl.CylCutter(obj.ToolController.Tool.Diameter, 5)
cutter = ocl.CylCutter(obj.ToolController.Tool.Diameter, 5)
pdc = ocl.PathDropCutter() # create a pdc
pdc.setSTL(s)
pdc.setCutter(cutter)
pdc.minimumZ = 0.25
pdc.setZ(obj.FinalDepth.Value + obj.DepthOffset.Value) # set minimumZ
pdc.setSampling(obj.SampleInterval)
# some parameters for this "zigzig" pattern
xmin = bb.XMin - cutter.getDiameter()
xmax = bb.XMax + cutter.getDiameter()
ymin = bb.YMin - cutter.getDiameter()
ymax = bb.YMax + cutter.getDiameter()
# number of lines in the y-direction
Ny = int(bb.YLength / cutter.getDiameter())
dy = float(ymax - ymin) / Ny # the y step-over
# the max and min XY area of the operation
xmin = bb.XMin - obj.DropCutterExtraOffset.x
xmax = bb.XMax + obj.DropCutterExtraOffset.x
ymin = bb.YMin - obj.DropCutterExtraOffset.y
ymax = bb.YMax + obj.DropCutterExtraOffset.y
path = ocl.Path() # create an empty path object
# add Line objects to the path in this loop
for n in xrange(0, Ny):
y = ymin + n * dy
p1 = ocl.Point(xmin, y, 0) # start-point of line
p2 = ocl.Point(xmax, y, 0) # end-point of line
if (n % 2 == 0): # even
l = ocl.Line(p1, p2) # line-object
else: # odd
l = ocl.Line(p2, p1) # line-object
if obj.DropCutterDir == 'Y':
Ny = int(bb.YLength / (cutter.getDiameter() * (obj.StepOver / 100)))
dy = float(ymax - ymin) / Ny # the y step-over
path.append(l) # add the line to the path
# add Line objects to the path in this loop
for n in xrange(0, Ny):
y = ymin + n * dy
p1 = ocl.Point(xmin, y, 0) # start-point of line
p2 = ocl.Point(xmax, y, 0) # end-point of line
if (n % 2 == 0): # even
l = ocl.Line(p1, p2) # line-object
else: # odd
l = ocl.Line(p2, p1) # line-object
path.append(l) # add the line to the path
else:
Nx = int(bb.XLength / (cutter.getDiameter() * (obj.StepOver / 100)))
dx = float(xmax - xmin) / Nx # the y step-over
# add Line objects to the path in this loop
for n in xrange(0, Nx):
x = xmin + n * dx
p1 = ocl.Point(x, ymin, 0) # start-point of line
p2 = ocl.Point(x, ymax, 0) # end-point of line
if (n % 2 == 0): # even
l = ocl.Line(p1, p2) # line-object
else: # odd
l = ocl.Line(p2, p1) # line-object
path.append(l) # add the line to the path
pdc.setPath(path)
@@ -253,17 +304,18 @@ class ObjectSurface(PathOp.ObjectOp):
def pocketInvertExtraOffset(self):
return True
def areaOpSetDefaultValues(self, obj):
'''areaOpSetDefaultValues(obj) ... initialize mill facing properties'''
# obj.StepOver = 50
# obj.ZigZagAngle = 45.0
def opSetDefaultValues(self, obj):
'''opSetDefaultValues(obj) ... initialize defauts'''
# obj.ZigZagAngle = 45.0
obj.StepOver = 50
# need to overwrite the default depth calculations for facing
job = PathUtils.findParentJob(obj)
if job and job.Base:
d = PathUtils.guessDepths(job.Base.Shape, None)
obj.OpStartDepth = d.safe_height
obj.OpFinalDepth = d.start_depth
obj.OpStartDepth = d.start_depth
obj.OpFinalDepth = d.final_depth
def Create(name):
'''Create(name) ... Creates and returns a Surface operation.'''