Path adaptive operation added
This commit is contained in:
421
src/Mod/Path/PathScripts/PathAdaptive.py
Normal file
421
src/Mod/Path/PathScripts/PathAdaptive.py
Normal file
@@ -0,0 +1,421 @@
|
||||
import PathScripts.PathOp as PathOp
|
||||
import Path
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
from FreeCAD import Console
|
||||
import time
|
||||
import json
|
||||
import math
|
||||
import area
|
||||
from pivy import coin
|
||||
|
||||
__doc__ = "Class and implementation of the Adaptive path operation."
|
||||
|
||||
def discretize(edge, flipDirection=False):
|
||||
pts=edge.discretize(Deflection=0.01)
|
||||
if flipDirection: pts.reverse()
|
||||
return pts
|
||||
|
||||
def IsEqualInXYPlane(e1, e2):
|
||||
return math.sqrt((e2.x-e1.x)*(e2.x-e1.x) +
|
||||
(e2.y - e1.y) * (e2.y - e1.y))<0.01
|
||||
|
||||
def connectEdges(edges):
|
||||
''' Makes the list of connected discretized paths '''
|
||||
# find edge
|
||||
lastPoint=None
|
||||
remaining = []
|
||||
pathArray = []
|
||||
combined = []
|
||||
#print "Input edges , remove duplicate projections to xy plane"
|
||||
for edge in edges:
|
||||
p1 = edge.valueAt(edge.FirstParameter)
|
||||
p2 = edge.valueAt(edge.LastParameter)
|
||||
duplicate = False
|
||||
for ex in remaining:
|
||||
exp1 = ex.valueAt(ex.FirstParameter)
|
||||
exp2 = ex.valueAt(ex.LastParameter)
|
||||
if IsEqualInXYPlane(exp1, p1) and IsEqualInXYPlane(exp2, p2):
|
||||
duplicate = True
|
||||
if IsEqualInXYPlane(exp1, p2) and IsEqualInXYPlane(exp2, p1):
|
||||
duplicate = True
|
||||
if not duplicate:
|
||||
remaining.append(edge)
|
||||
#print "remaining:", remaining
|
||||
|
||||
newPath=True
|
||||
while len(remaining)>0:
|
||||
if newPath:
|
||||
#print "new iteration"
|
||||
edge=remaining[0]
|
||||
p1 = edge.valueAt(edge.FirstParameter)
|
||||
p2 = edge.valueAt(edge.LastParameter)
|
||||
#print edge, p1, p2
|
||||
if len(combined)>0: pathArray.append(combined)
|
||||
combined = []
|
||||
combined.append(discretize(edge))
|
||||
remaining.remove(edge)
|
||||
lastPoint=p2
|
||||
newPath=False
|
||||
|
||||
anyMatch=False
|
||||
for e in remaining:
|
||||
p1 = e.valueAt(e.FirstParameter)
|
||||
p2 = e.valueAt(e.LastParameter)
|
||||
#print "chk",e, p1, p2
|
||||
if IsEqualInXYPlane(lastPoint,p1):
|
||||
#print "last Point equal p1"
|
||||
combined.append(discretize(e))
|
||||
remaining.remove(e)
|
||||
lastPoint=p2
|
||||
anyMatch=True
|
||||
break
|
||||
elif IsEqualInXYPlane(lastPoint,p2):
|
||||
#print "reversed"
|
||||
combined.append(discretize(e,True))
|
||||
remaining.remove(e)
|
||||
lastPoint=p1
|
||||
anyMatch=True
|
||||
break
|
||||
if not anyMatch:
|
||||
newPath=True
|
||||
|
||||
|
||||
#make sure last path is appended
|
||||
if len(combined)>0: pathArray.append(combined)
|
||||
combined = []
|
||||
return pathArray
|
||||
|
||||
def convertTo2d(pathArray):
|
||||
output = []
|
||||
for path in pathArray:
|
||||
pth2 = []
|
||||
for edge in path:
|
||||
for pt in edge:
|
||||
pth2.append([pt[0],pt[1]])
|
||||
output.append(pth2)
|
||||
return output
|
||||
|
||||
|
||||
sceneGraph = None
|
||||
scenePathNodes = [] #for scene cleanup aftewards
|
||||
topZ = 10
|
||||
|
||||
def sceneDrawPath(path, color=(0, 0, 1)):
|
||||
global sceneGraph
|
||||
global scenePathNodes
|
||||
coPoint = coin.SoCoordinate3()
|
||||
pts = []
|
||||
for pt in path:
|
||||
pts.append([pt[0], pt[1], topZ])
|
||||
|
||||
coPoint.point.setValues(0, len(pts), pts)
|
||||
ma = coin.SoBaseColor()
|
||||
ma.rgb = color
|
||||
li = coin.SoLineSet()
|
||||
li.numVertices.setValue(len(pts))
|
||||
pathNode = coin.SoSeparator()
|
||||
pathNode.addChild(coPoint)
|
||||
pathNode.addChild(ma)
|
||||
pathNode.addChild(li)
|
||||
sceneGraph.addChild(pathNode)
|
||||
scenePathNodes.append(pathNode) #for scene cleanup afterwards
|
||||
|
||||
def sceneClean():
|
||||
global scenePathNodes
|
||||
for n in scenePathNodes:
|
||||
sceneGraph.removeChild(n)
|
||||
del scenePathNodes[:]
|
||||
|
||||
def GenerateGCode(op,obj,adaptiveResults, helixDiameter):
|
||||
if len(adaptiveResults)==0 or len(adaptiveResults[0]["AdaptivePaths"])==0:
|
||||
return
|
||||
|
||||
minLiftDistance = op.tool.Diameter
|
||||
p1 = adaptiveResults[0]["HelixCenterPoint"]
|
||||
p2 = adaptiveResults[0]["StartPoint"]
|
||||
helixRadius =math.sqrt((p1[0]-p2[0]) * (p1[0]-p2[0]) + (p1[1]-p2[1]) * (p1[1]-p2[1]))
|
||||
stepDown = obj.StepDown.Value
|
||||
passStartDepth=obj.StartDepth.Value
|
||||
if stepDown<0.1 : stepDown=0.1
|
||||
length = 2*math.pi * helixRadius
|
||||
if obj.HelixAngle<1: obj.HelixAngle=1
|
||||
helixAngleRad = math.pi * obj.HelixAngle/180.0
|
||||
depthPerOneCircle=length * math.tan(helixAngleRad)
|
||||
stepUp = obj.LiftDistance.Value
|
||||
if stepUp<0:
|
||||
stepUp=0
|
||||
|
||||
lx=adaptiveResults[0]["HelixCenterPoint"][0]
|
||||
ly=adaptiveResults[0]["HelixCenterPoint"][1]
|
||||
|
||||
step=0
|
||||
while passStartDepth>obj.FinalDepth.Value and step<1000:
|
||||
step=step+1
|
||||
passEndDepth=passStartDepth-stepDown
|
||||
if passEndDepth<obj.FinalDepth.Value: passEndDepth=obj.FinalDepth.Value
|
||||
|
||||
for region in adaptiveResults:
|
||||
startAngle = math.atan2(region["StartPoint"][1] - region["HelixCenterPoint"][1], region["StartPoint"][0] - region["HelixCenterPoint"][0])
|
||||
|
||||
lx=region["HelixCenterPoint"][0]
|
||||
ly=region["HelixCenterPoint"][1]
|
||||
|
||||
r = helixRadius - 0.01
|
||||
#helix ramp
|
||||
passDepth = (passStartDepth - passEndDepth)
|
||||
maxfi = passDepth / depthPerOneCircle * 2 * math.pi
|
||||
fi = 0
|
||||
offsetFi =-maxfi + startAngle-math.pi/16
|
||||
|
||||
helixStart = [region["HelixCenterPoint"][0] + r * math.cos(offsetFi), region["HelixCenterPoint"][1] + r * math.sin(offsetFi)]
|
||||
|
||||
op.commandlist.append(Path.Command("(helix to depth: %f)"%passEndDepth))
|
||||
#if step == 1:
|
||||
#rapid move to start point
|
||||
op.commandlist.append(Path.Command(
|
||||
"G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.ClearanceHeight.Value}))
|
||||
#rapid move to safe height
|
||||
op.commandlist.append(Path.Command(
|
||||
"G0", {"X": helixStart[0], "Y": helixStart[1], "Z": obj.SafeHeight.Value}))
|
||||
|
||||
op.commandlist.append(Path.Command("G1", {
|
||||
"X": helixStart[0], "Y": helixStart[1], "Z": passStartDepth, "F": op.vertFeed}))
|
||||
|
||||
while fi<maxfi:
|
||||
x = region["HelixCenterPoint"][0] + r * math.cos(fi+offsetFi)
|
||||
y = region["HelixCenterPoint"][1] + r * math.sin(fi+offsetFi)
|
||||
z = passStartDepth - fi / maxfi * (passStartDepth - passEndDepth)
|
||||
op.commandlist.append(Path.Command("G1", { "X": x, "Y":y, "Z":z, "F": op.vertFeed}))
|
||||
lx=x
|
||||
ly=y
|
||||
fi=fi+math.pi/16
|
||||
op.commandlist.append(Path.Command("(adaptive - depth: %f)"%passEndDepth))
|
||||
#add adaptive paths
|
||||
for pth in region["AdaptivePaths"]:
|
||||
#print pth.Points
|
||||
motionType = pth[0] #[0] contains motion type
|
||||
for pt in pth[1]: #[1] contains list of points
|
||||
x=pt[0]
|
||||
y =pt[1]
|
||||
dist=math.sqrt((x-lx)*(x-lx) + (y-ly)*(y-ly))
|
||||
if motionType == area.AdaptiveMotionType.Cutting:
|
||||
op.commandlist.append(Path.Command("G1", { "X": x, "Y":y, "Z":passEndDepth, "F": op.horizFeed}))
|
||||
elif motionType == area.AdaptiveMotionType.LinkClear:
|
||||
if dist > minLiftDistance:
|
||||
if lx!=x or ly!=y:
|
||||
op.commandlist.append(Path.Command("G0", { "X": lx, "Y":ly, "Z":passEndDepth+stepUp}))
|
||||
op.commandlist.append(Path.Command("G0", { "X": x, "Y":y, "Z":passEndDepth+stepUp}))
|
||||
elif motionType == area.AdaptiveMotionType.LinkNotClear:
|
||||
if lx!=x or ly!=y:
|
||||
op.commandlist.append(Path.Command("G0", { "X": lx, "Y":ly, "Z":obj.ClearanceHeight.Value}))
|
||||
op.commandlist.append(Path.Command("G0", { "X": x, "Y":y, "Z":obj.ClearanceHeight.Value}))
|
||||
elif motionType == area.AdaptiveMotionType.LinkClearAtPrevPass:
|
||||
if lx!=x or ly!=y:
|
||||
op.commandlist.append(Path.Command("G0", { "X": lx, "Y":ly, "Z":passStartDepth+stepUp}))
|
||||
op.commandlist.append(Path.Command("G0", { "X": x, "Y":y, "Z":passStartDepth+stepUp}))
|
||||
lx=x
|
||||
ly=y
|
||||
|
||||
passStartDepth=passEndDepth
|
||||
#return to safe height in this Z pass
|
||||
op.commandlist.append(Path.Command("G0", { "X": lx, "Y":ly, "Z":obj.ClearanceHeight.Value}))
|
||||
|
||||
op.commandlist.append(Path.Command("G0", { "X": lx, "Y":ly, "Z":obj.ClearanceHeight.Value}))
|
||||
|
||||
def Execute(op,obj):
|
||||
global sceneGraph
|
||||
global topZ
|
||||
|
||||
sceneGraph = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
|
||||
|
||||
Console.PrintMessage("*** Adaptive toolpath processing started...\n")
|
||||
|
||||
#hide old toolpaths during recalculation
|
||||
obj.Path = Path.Path("(calculating...)")
|
||||
|
||||
#store old visibility state
|
||||
job = op.getJob(obj)
|
||||
oldObjVisibility = obj.ViewObject.Visibility
|
||||
oldJobVisibility = job.ViewObject.Visibility
|
||||
|
||||
obj.ViewObject.Visibility = False
|
||||
job.ViewObject.Visibility = False
|
||||
|
||||
FreeCADGui.updateGui()
|
||||
try:
|
||||
Console.PrintMessage("Tool diam: %f \n"%op.tool.Diameter)
|
||||
helixDiameter = min(op.tool.Diameter,1000.0 if obj.HelixDiameterLimit.Value==0.0 else obj.HelixDiameterLimit.Value )
|
||||
nestingLimit=0
|
||||
topZ=op.stock.Shape.BoundBox.ZMax
|
||||
|
||||
opType = area.AdaptiveOperationType.Clearing
|
||||
obj.Stopped = False
|
||||
obj.StopProcessing = False
|
||||
if obj.Tolerance<0.001: obj.Tolerance=0.001
|
||||
|
||||
edges=[]
|
||||
for base, subs in obj.Base:
|
||||
#print (base,subs)
|
||||
for sub in subs:
|
||||
shape=base.Shape.getElement(sub)
|
||||
for edge in shape.Edges:
|
||||
edges.append(edge)
|
||||
|
||||
pathArray=connectEdges(edges)
|
||||
|
||||
if obj.OperationType == "Clearing":
|
||||
if obj.Side == "Outside":
|
||||
stockBB = op.stock.Shape.BoundBox
|
||||
v=[]
|
||||
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMin,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMax,stockBB.YMin,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMax,stockBB.YMax,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMax,0))
|
||||
v.append(FreeCAD.Vector(stockBB.XMin,stockBB.YMin,0))
|
||||
pathArray.append([v])
|
||||
if not obj.ProcessHoles: nestingLimit = 2
|
||||
elif not obj.ProcessHoles: nestingLimit = 1
|
||||
opType = area.AdaptiveOperationType.Clearing
|
||||
else: # profiling
|
||||
if obj.Side == "Outside":
|
||||
opType = area.AdaptiveOperationType.ProfilingOutside
|
||||
else:
|
||||
opType = area.AdaptiveOperationType.ProfilingInside
|
||||
if not obj.ProcessHoles: nestingLimit = 1
|
||||
|
||||
path2d = convertTo2d(pathArray)
|
||||
# put here all properties that influence calculation of adaptive base paths,
|
||||
inputStateObject = {
|
||||
"tool": op.tool.Diameter,
|
||||
"tolerance": obj.Tolerance,
|
||||
"geometry" : path2d,
|
||||
"stepover" :obj.StepOver,
|
||||
"effectiveHelixDiameter": helixDiameter,
|
||||
"operationType": obj.OperationType,
|
||||
"side": obj.Side,
|
||||
"processHoles": obj.ProcessHoles
|
||||
|
||||
}
|
||||
|
||||
inputStateChanged=False
|
||||
adaptiveResults=None
|
||||
|
||||
if obj.AdaptiveOutputState !=None and obj.AdaptiveOutputState != "":
|
||||
adaptiveResults = obj.AdaptiveOutputState
|
||||
|
||||
if json.dumps(obj.AdaptiveInputState) != json.dumps(inputStateObject):
|
||||
inputStateChanged=True
|
||||
adaptiveResults=None
|
||||
|
||||
# progress callback fn, if return true it will stop processing
|
||||
def progressFn(tpaths):
|
||||
for path in tpaths: #path[0] contains the MotionType,#path[1] contains list of points
|
||||
sceneDrawPath(path[1])
|
||||
FreeCADGui.updateGui()
|
||||
return obj.StopProcessing
|
||||
|
||||
start=time.time()
|
||||
|
||||
if inputStateChanged or adaptiveResults==None:
|
||||
a2d = area.Adaptive2d()
|
||||
a2d.stepOverFactor = 0.01*obj.StepOver
|
||||
a2d.toolDiameter = op.tool.Diameter
|
||||
a2d.helixRampDiameter = helixDiameter
|
||||
a2d.tolerance = obj.Tolerance
|
||||
a2d.opType = opType
|
||||
a2d.polyTreeNestingLimit = nestingLimit
|
||||
#EXECUTE
|
||||
results = a2d.Execute(path2d,progressFn)
|
||||
|
||||
#need to convert results to python object to be JSON serializable
|
||||
adaptiveResults = []
|
||||
for result in results:
|
||||
adaptiveResults.append({
|
||||
"HelixCenterPoint": result.HelixCenterPoint,
|
||||
"StartPoint": result.StartPoint,
|
||||
"AdaptivePaths": result.AdaptivePaths,
|
||||
"ReturnMotionType": result.ReturnMotionType })
|
||||
|
||||
|
||||
|
||||
GenerateGCode(op,obj,adaptiveResults,helixDiameter)
|
||||
|
||||
if not obj.StopProcessing:
|
||||
Console.PrintMessage("*** Done. Elapsed: %f sec\n\n" %(time.time()-start))
|
||||
obj.AdaptiveOutputState = adaptiveResults
|
||||
obj.AdaptiveInputState=inputStateObject
|
||||
else:
|
||||
Console.PrintMessage("*** Processing cancelled (after: %f sec).\n\n" %(time.time()-start))
|
||||
finally:
|
||||
obj.ViewObject.Visibility = oldObjVisibility
|
||||
job.ViewObject.Visibility = oldJobVisibility
|
||||
sceneClean()
|
||||
|
||||
|
||||
|
||||
class PathAdaptive(PathOp.ObjectOp):
|
||||
def opFeatures(self, obj):
|
||||
'''opFeatures(obj) ... returns the OR'ed list of features used and supported by the operation.
|
||||
The default implementation returns "FeatureTool | FeatureDeptsh | FeatureHeights | FeatureStartPoint"
|
||||
Should be overwritten by subclasses.'''
|
||||
return PathOp.FeatureTool | PathOp.FeatureBaseEdges | PathOp.FeatureDepths | PathOp.FeatureStepDown | PathOp.FeatureHeights | PathOp.FeatureBaseGeometry
|
||||
|
||||
def initOperation(self, obj):
|
||||
'''initOperation(obj) ... implement to create additional properties.
|
||||
Should be overwritten by subclasses.'''
|
||||
obj.addProperty("App::PropertyEnumeration", "Side", "Adaptive", "Side of selected faces that tool should cut")
|
||||
obj.Side = ['Outside', 'Inside'] # side of profile that cutter is on in relation to direction of profile
|
||||
|
||||
obj.addProperty("App::PropertyEnumeration", "OperationType", "Adaptive", "Type of adaptive operation")
|
||||
obj.OperationType = ['Clearing', 'Profiling'] # side of profile that cutter is on in relation to direction of profile
|
||||
|
||||
obj.addProperty("App::PropertyFloat", "Tolerance", "Adaptive", "Influences accuracy and performance")
|
||||
obj.addProperty("App::PropertyPercent", "StepOver", "Adaptive", "Percent of cutter diameter to step over on each pass")
|
||||
obj.addProperty("App::PropertyDistance", "LiftDistance", "Adaptive", "Lift distance for rapid moves")
|
||||
obj.addProperty("App::PropertyBool", "ProcessHoles", "Adaptive","Process holes as well as the face outline")
|
||||
obj.addProperty("App::PropertyBool", "Stopped",
|
||||
"Adaptive", "Stop processing")
|
||||
obj.setEditorMode('Stopped', 2) #hide this property
|
||||
|
||||
obj.addProperty("App::PropertyBool", "StopProcessing",
|
||||
"Adaptive", "Stop processing")
|
||||
obj.setEditorMode('StopProcessing', 2) # hide this property
|
||||
|
||||
obj.addProperty("App::PropertyPythonObject", "AdaptiveInputState",
|
||||
"Adaptive", "Internal input state")
|
||||
obj.addProperty("App::PropertyPythonObject", "AdaptiveOutputState",
|
||||
"Adaptive", "Internal output state")
|
||||
obj.setEditorMode('AdaptiveInputState', 2) #hide this property
|
||||
obj.setEditorMode('AdaptiveOutputState', 2) #hide this property
|
||||
obj.addProperty("App::PropertyAngle", "HelixAngle", "Adaptive", "Helix ramp entry angle (degrees)")
|
||||
obj.addProperty("App::PropertyLength", "HelixDiameterLimit", "Adaptive", "Limit helix entry diameter, if limit larger than tool diameter or 0, tool diameter is used")
|
||||
|
||||
|
||||
def opSetDefaultValues(self, obj):
|
||||
obj.Side="Inside"
|
||||
obj.OperationType = "Clearing"
|
||||
obj.Tolerance = 0.1
|
||||
obj.StepOver = 20
|
||||
obj.LiftDistance=1.0
|
||||
obj.ProcessHoles = True
|
||||
obj.Stopped = False
|
||||
obj.StopProcessing = False
|
||||
obj.HelixAngle = 5
|
||||
obj.HelixDiameterLimit = 0.0
|
||||
obj.AdaptiveInputState =""
|
||||
obj.AdaptiveOutputState = ""
|
||||
|
||||
def opExecute(self, obj):
|
||||
'''opExecute(obj) ... called whenever the receiver needs to be recalculated.
|
||||
See documentation of execute() for a list of base functionality provided.
|
||||
Should be overwritten by subclasses.'''
|
||||
Execute(self,obj)
|
||||
|
||||
|
||||
|
||||
def Create(name):
|
||||
'''Create(name) ... Creates and returns a Pocket operation.'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
proxy = PathAdaptive(obj)
|
||||
return obj
|
||||
168
src/Mod/Path/PathScripts/PathAdaptiveGui.py
Normal file
168
src/Mod/Path/PathScripts/PathAdaptiveGui.py
Normal file
@@ -0,0 +1,168 @@
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
from PySide import QtCore, QtGui
|
||||
import PathAdaptive
|
||||
|
||||
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
def initPage(self, obj):
|
||||
self.setTitle("Adaptive path operation")
|
||||
|
||||
def getForm(self):
|
||||
form = QtGui.QWidget()
|
||||
layout = QtGui.QVBoxLayout()
|
||||
|
||||
#tool contoller
|
||||
hlayout = QtGui.QHBoxLayout()
|
||||
form.ToolController = QtGui.QComboBox()
|
||||
form.ToolControllerLabel=QtGui.QLabel("Tool Controller")
|
||||
hlayout.addWidget(form.ToolControllerLabel)
|
||||
hlayout.addWidget(form.ToolController)
|
||||
layout.addLayout(hlayout)
|
||||
|
||||
#cut region
|
||||
formLayout = QtGui.QFormLayout()
|
||||
form.Side = QtGui.QComboBox()
|
||||
form.Side.addItem("Inside")
|
||||
form.Side.addItem("Outside")
|
||||
form.Side.setToolTip("Cut inside or outside of the selected face")
|
||||
formLayout.addRow(QtGui.QLabel("Cut Region"),form.Side)
|
||||
|
||||
#operation type
|
||||
form.OperationType = QtGui.QComboBox()
|
||||
form.OperationType.addItem("Clearing")
|
||||
form.OperationType.addItem("Profiling")
|
||||
form.OperationType.setToolTip("Type of adaptive operation")
|
||||
formLayout.addRow(QtGui.QLabel("Operation Type"),form.OperationType)
|
||||
|
||||
#step over
|
||||
form.StepOver = QtGui.QSpinBox()
|
||||
form.StepOver.setMinimum(15)
|
||||
form.StepOver.setMaximum(50)
|
||||
form.StepOver.setSingleStep(1)
|
||||
form.StepOver.setValue(25)
|
||||
form.StepOver.setToolTip("Tool step over percentage")
|
||||
formLayout.addRow(QtGui.QLabel("Step Over Percent"),form.StepOver)
|
||||
|
||||
#tolerance
|
||||
form.Tolerance = QtGui.QSlider(QtCore.Qt.Horizontal)
|
||||
form.Tolerance.setMinimum(5)
|
||||
form.Tolerance.setMaximum(15)
|
||||
form.Tolerance.setTickInterval(1)
|
||||
form.Tolerance.setValue(10)
|
||||
form.Tolerance.setTickPosition(QtGui.QSlider.TicksBelow)
|
||||
form.Tolerance.setToolTip("Influences calculation performace vs stability and accuracy")
|
||||
formLayout.addRow(QtGui.QLabel("Accuracy vs Performance"),form.Tolerance)
|
||||
|
||||
#helix angle
|
||||
form.HelixAngle = QtGui.QDoubleSpinBox()
|
||||
form.HelixAngle.setMinimum(0.1)
|
||||
form.HelixAngle.setMaximum(90)
|
||||
form.HelixAngle.setSingleStep(0.1)
|
||||
form.HelixAngle.setValue(5)
|
||||
form.HelixAngle.setToolTip("Angle of the helix ramp entry")
|
||||
formLayout.addRow(QtGui.QLabel("Helix Ramp Angle"),form.HelixAngle)
|
||||
|
||||
#helix diam. limit
|
||||
form.HelixDiameterLimit = QtGui.QDoubleSpinBox()
|
||||
form.HelixDiameterLimit.setMinimum(0.0)
|
||||
form.HelixDiameterLimit.setMaximum(90)
|
||||
form.HelixDiameterLimit.setSingleStep(0.1)
|
||||
form.HelixDiameterLimit.setValue(0)
|
||||
form.HelixDiameterLimit.setToolTip("If non zero it limits the size helix diameter, otherwise the tool radius is taken as the helix diameter")
|
||||
formLayout.addRow(QtGui.QLabel("Helix Max Diameter"),form.HelixDiameterLimit)
|
||||
|
||||
#lift distance
|
||||
form.LiftDistance = QtGui.QDoubleSpinBox()
|
||||
form.LiftDistance.setMinimum(0.0)
|
||||
form.LiftDistance.setMaximum(1000)
|
||||
form.LiftDistance.setSingleStep(0.1)
|
||||
form.LiftDistance.setValue(1.0)
|
||||
form.LiftDistance.setToolTip("How much to lift the tool up during the rapid repositioning moves (used when no obstacles)")
|
||||
formLayout.addRow(QtGui.QLabel("Lift Distance"),form.LiftDistance)
|
||||
|
||||
#process holes
|
||||
form.ProcessHoles = QtGui.QCheckBox()
|
||||
form.ProcessHoles.setChecked(True)
|
||||
formLayout.addRow(QtGui.QLabel("Process Holes"),form.ProcessHoles)
|
||||
|
||||
layout.addLayout(formLayout)
|
||||
|
||||
#stop button
|
||||
form.StopButton=QtGui.QPushButton("Stop")
|
||||
form.StopButton.setCheckable(True)
|
||||
layout.addWidget(form.StopButton)
|
||||
|
||||
form.setLayout(layout)
|
||||
return form
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
|
||||
signals = []
|
||||
#signals.append(self.form.button.clicked)
|
||||
signals.append(self.form.Side.currentIndexChanged)
|
||||
signals.append(self.form.OperationType.currentIndexChanged)
|
||||
signals.append(self.form.ToolController.currentIndexChanged)
|
||||
signals.append(self.form.StepOver.valueChanged)
|
||||
signals.append(self.form.Tolerance.valueChanged)
|
||||
signals.append(self.form.HelixAngle.valueChanged)
|
||||
signals.append(self.form.HelixDiameterLimit.valueChanged)
|
||||
signals.append(self.form.LiftDistance.valueChanged)
|
||||
|
||||
signals.append(self.form.ProcessHoles.stateChanged)
|
||||
signals.append(self.form.StopButton.toggled)
|
||||
return signals
|
||||
|
||||
def setFields(self, obj):
|
||||
self.selectInComboBox(obj.Side, self.form.Side)
|
||||
self.selectInComboBox(obj.OperationType, self.form.OperationType)
|
||||
self.form.StepOver.setValue(obj.StepOver)
|
||||
self.form.Tolerance.setValue(int(obj.Tolerance*100))
|
||||
self.form.HelixAngle.setValue(obj.HelixAngle)
|
||||
self.form.HelixDiameterLimit.setValue(obj.HelixDiameterLimit)
|
||||
self.form.LiftDistance.setValue(obj.LiftDistance)
|
||||
|
||||
self.form.ProcessHoles.setChecked(obj.ProcessHoles)
|
||||
self.setupToolController(obj, self.form.ToolController)
|
||||
self.form.StopButton.setChecked(obj.Stopped)
|
||||
obj.setEditorMode('AdaptiveInputState', 2) #hide this property
|
||||
obj.setEditorMode('AdaptiveOutputState', 2) #hide this property
|
||||
obj.setEditorMode('StopProcessing', 2) # hide this property
|
||||
obj.setEditorMode('Stopped', 2) # hide this property
|
||||
|
||||
def getFields(self, obj):
|
||||
if obj.Side != str(self.form.Side.currentText()):
|
||||
obj.Side = str(self.form.Side.currentText())
|
||||
|
||||
if obj.OperationType != str(self.form.OperationType.currentText()):
|
||||
obj.OperationType = str(self.form.OperationType.currentText())
|
||||
|
||||
obj.StepOver = self.form.StepOver.value()
|
||||
obj.Tolerance = 1.0*self.form.Tolerance.value()/100.0
|
||||
obj.HelixAngle = self.form.HelixAngle.value()
|
||||
obj.HelixDiameterLimit = self.form.HelixDiameterLimit.value()
|
||||
obj.LiftDistance = self.form.LiftDistance.value()
|
||||
|
||||
obj.ProcessHoles = self.form.ProcessHoles.isChecked()
|
||||
obj.Stopped = self.form.StopButton.isChecked()
|
||||
if(obj.Stopped):
|
||||
self.form.StopButton.setChecked(False) #reset the button
|
||||
obj.StopProcessing=True
|
||||
|
||||
self.updateToolController(obj, self.form.ToolController)
|
||||
obj.setEditorMode('AdaptiveInputState', 2) #hide this property
|
||||
obj.setEditorMode('AdaptiveOutputState', 2) #hide this property
|
||||
obj.setEditorMode('StopProcessing', 2) # hide this property
|
||||
obj.setEditorMode('Stopped', 2) # hide this property
|
||||
|
||||
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation('Adaptive',
|
||||
PathAdaptive.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path-Adaptive',
|
||||
QtCore.QT_TRANSLATE_NOOP("PathAdaptive", "Adaptive"),
|
||||
QtCore.QT_TRANSLATE_NOOP("PathPocket", "Adaptive clearing and profiling"))
|
||||
@@ -157,6 +157,31 @@ class POCKETGate:
|
||||
|
||||
return pocketable
|
||||
|
||||
class ADAPTIVEGate:
|
||||
def allow(self, doc, obj, sub):
|
||||
|
||||
adaptive = False
|
||||
try:
|
||||
obj = obj.Shape
|
||||
except:
|
||||
return False
|
||||
|
||||
if obj.ShapeType == 'Edge':
|
||||
adaptive = False
|
||||
|
||||
elif obj.ShapeType == 'Face':
|
||||
adaptive = True
|
||||
|
||||
elif obj.ShapeType == 'Solid':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
adaptive = True
|
||||
|
||||
elif obj.ShapeType == 'Compound':
|
||||
if sub and sub[0:4] == 'Face':
|
||||
adaptive = True
|
||||
|
||||
return adaptive
|
||||
|
||||
class CONTOURGate:
|
||||
def allow(self, doc, obj, sub):
|
||||
pass
|
||||
@@ -189,6 +214,10 @@ def pocketselect():
|
||||
FreeCADGui.Selection.addSelectionGate(POCKETGate())
|
||||
FreeCAD.Console.PrintWarning("Pocketing Select Mode\n")
|
||||
|
||||
def adaptiveselect():
|
||||
FreeCADGui.Selection.addSelectionGate(ADAPTIVEGate())
|
||||
FreeCAD.Console.PrintWarning("Adaptive Select Mode\n")
|
||||
|
||||
def surfaceselect():
|
||||
FreeCADGui.Selection.addSelectionGate(MESHGate())
|
||||
FreeCAD.Console.PrintWarning("Surfacing Select Mode\n")
|
||||
@@ -207,6 +236,7 @@ def select(op):
|
||||
opsel['Profile Edges'] = eselect
|
||||
opsel['Profile Faces'] = profileselect
|
||||
opsel['Surface'] = surfaceselect
|
||||
opsel['Adaptive'] = adaptiveselect
|
||||
return opsel[op]
|
||||
|
||||
def clear():
|
||||
|
||||
Reference in New Issue
Block a user