Path: Surface op improvements for drop cutter algo
This commit is contained in:
committed by
Yorik van Havre
parent
56cd699183
commit
097f73afcb
@@ -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.'''
|
||||
|
||||
Reference in New Issue
Block a user