Merge pull request #4143 from mlampert/bugfix/vcarve-depth
Path: Bugfix/vcarve depth
This commit is contained in:
@@ -222,7 +222,7 @@ def speedBetweenPoints(p0, p1, hSpeed, vSpeed):
|
||||
pitch = pitch + 1
|
||||
while pitch > 1:
|
||||
pitch = pitch - 1
|
||||
print(" pitch = %g %g (%.2f, %.2f, %.2f) -> %.2f" % (pitch, math.atan2(xy(d).Length, d.z), d.x, d.y, d.z, xy(d).Length))
|
||||
PathLog.debug(" pitch = %g %g (%.2f, %.2f, %.2f) -> %.2f" % (pitch, math.atan2(xy(d).Length, d.z), d.x, d.y, d.z, xy(d).Length))
|
||||
speed = vSpeed + pitch * (hSpeed - vSpeed)
|
||||
if speed > hSpeed and speed > vSpeed:
|
||||
return max(hSpeed, vSpeed)
|
||||
|
||||
@@ -524,6 +524,10 @@ class ObjectOp(object):
|
||||
|
||||
result = self.opExecute(obj) # pylint: disable=assignment-from-no-return
|
||||
|
||||
if self.commandlist and (FeatureHeights & self.opFeatures(obj)):
|
||||
# Let's finish by rapid to clearance...just for safety
|
||||
self.commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value}))
|
||||
|
||||
path = Path.Path(self.commandlist)
|
||||
obj.Path = path
|
||||
obj.CycleTime = self.getCycleTimeEstimate(obj)
|
||||
|
||||
@@ -203,7 +203,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
Command = PathOpGui.SetupOperation('Thread Milling',
|
||||
PathThreadMilling.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path-ThreadMilling',
|
||||
'Path_ThreadMilling',
|
||||
QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Thread Milling"),
|
||||
QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Creates a Path Thread Milling operation from features of a base object"),
|
||||
PathThreadMilling.SetupProperties)
|
||||
|
||||
@@ -334,7 +334,7 @@ class ToolBitSelector(object):
|
||||
tools = self.selectedOrAllTools()
|
||||
|
||||
for tool in tools:
|
||||
tc = PathToolControllerGui.Create(tool[1].Label, tool[1], tool[0])
|
||||
tc = PathToolControllerGui.Create("TC: {}".format(tool[1].Label), tool[1], tool[0])
|
||||
job.Proxy.addToolController(tc)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
@@ -225,13 +225,13 @@ class ToolController:
|
||||
obj.addProperty("App::PropertyLink", "Tool", "Base", QtCore.QT_TRANSLATE_NOOP("PathToolController", "The tool used by this controller"))
|
||||
|
||||
|
||||
def Create(name='Default Tool', tool=None, toolNumber=1, assignViewProvider=True):
|
||||
def Create(name='TC: Default Tool', tool=None, toolNumber=1, assignViewProvider=True):
|
||||
legacyTool = PathPreferences.toolsReallyUseLegacyTools() if tool is None else isinstance(tool, Path.Tool)
|
||||
|
||||
PathLog.track(tool, toolNumber, legacyTool)
|
||||
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Label = "TC: {}".format(name)
|
||||
obj.Label = name
|
||||
obj.Proxy = ToolController(obj, legacyTool)
|
||||
|
||||
if FreeCAD.GuiUp and assignViewProvider:
|
||||
|
||||
@@ -157,6 +157,45 @@ def _sortVoronoiWires(wires, start=FreeCAD.Vector(0, 0, 0)):
|
||||
|
||||
return result
|
||||
|
||||
class _Geometry(object):
|
||||
'''POD class so the limits only have to be calculated once.'''
|
||||
|
||||
def __init__(self, zStart, zStop, zScale):
|
||||
self.start = zStart
|
||||
self.stop = zStop
|
||||
self.scale = zScale
|
||||
|
||||
@classmethod
|
||||
def FromTool(cls, tool, zStart, zFinal):
|
||||
rMax = float(tool.Diameter) / 2.0
|
||||
rMin = float(tool.TipDiameter) / 2.0
|
||||
toolangle = math.tan(math.radians(tool.CuttingEdgeAngle.Value / 2.0))
|
||||
zScale = 1.0 / toolangle
|
||||
zStop = zStart - rMax * zScale
|
||||
zOff = rMin * zScale
|
||||
|
||||
return _Geometry(zStart + zOff, max(zStop + zOff, zFinal), zScale)
|
||||
|
||||
@classmethod
|
||||
def FromObj(cls, obj, model):
|
||||
zStart = model.Shape.BoundBox.ZMax
|
||||
finalDepth = obj.FinalDepth.Value
|
||||
|
||||
return cls.FromTool(obj.ToolController.Tool, zStart, finalDepth)
|
||||
|
||||
def _calculate_depth(MIC, geom):
|
||||
# given a maximum inscribed circle (MIC) and tool angle,
|
||||
# return depth of cut relative to zStart.
|
||||
depth = geom.start - round(MIC / geom.scale, 4)
|
||||
PathLog.debug('zStart value: {} depth: {}'.format(geom.start, depth))
|
||||
|
||||
return max(depth, geom.stop)
|
||||
|
||||
def _getPartEdge(edge, depths):
|
||||
dist = edge.getDistances()
|
||||
zBegin = _calculate_depth(dist[0], depths)
|
||||
zEnd = _calculate_depth(dist[1], depths)
|
||||
return edge.toShape(zBegin, zEnd)
|
||||
|
||||
class ObjectVcarve(PathEngraveBase.ObjectOp):
|
||||
'''Proxy class for Vcarve operation.'''
|
||||
@@ -197,42 +236,10 @@ class ObjectVcarve(PathEngraveBase.ObjectOp):
|
||||
# upgrade ...
|
||||
self.setupAdditionalProperties(obj)
|
||||
|
||||
def _calculate_depth(self, MIC, zStart, zStop, zScale, finaldepth):
|
||||
# given a maximum inscribed circle (MIC) and tool angle,
|
||||
# return depth of cut relative to zStart.
|
||||
depth = zStart - round(MIC / zScale, 4)
|
||||
PathLog.debug('zStart value: {} depth: {}'.format(zStart, depth))
|
||||
|
||||
# Never go below the operation final depth.
|
||||
zStop = zStop if zStop > finaldepth else finaldepth
|
||||
|
||||
return depth if depth > zStop else zStop
|
||||
|
||||
def _getPartEdge(self, edge, zStart, zStop, zScale, finaldepth):
|
||||
dist = edge.getDistances()
|
||||
return edge.toShape(self._calculate_depth(dist[0],
|
||||
zStart,
|
||||
zStop,
|
||||
zScale,
|
||||
finaldepth),
|
||||
self._calculate_depth(dist[1],
|
||||
zStart,
|
||||
zStop,
|
||||
zScale,
|
||||
finaldepth))
|
||||
|
||||
def _getPartEdges(self, obj, vWire):
|
||||
# pre-calculate the depth limits - pre-mature optimisation ;)
|
||||
r = float(obj.ToolController.Tool.Diameter) / 2
|
||||
toolangle = obj.ToolController.Tool.CuttingEdgeAngle
|
||||
zStart = self.model[0].Shape.BoundBox.ZMin
|
||||
zStop = zStart - r / math.tan(math.radians(toolangle/2))
|
||||
zScale = 1.0 / math.tan(math.radians(toolangle / 2))
|
||||
finaldepth = obj.FinalDepth.Value
|
||||
|
||||
def _getPartEdges(self, obj, vWire, geom):
|
||||
edges = []
|
||||
for e in vWire:
|
||||
edges.append(self._getPartEdge(e, zStart, zStop, zScale, finaldepth))
|
||||
edges.append(_getPartEdge(e, geom))
|
||||
return edges
|
||||
|
||||
def buildPathMedial(self, obj, Faces):
|
||||
@@ -253,14 +260,12 @@ class ObjectVcarve(PathEngraveBase.ObjectOp):
|
||||
path.append(Path.Command("G0 Z{}".format(obj.SafeHeight.Value)))
|
||||
e = edges[0]
|
||||
p = e.valueAt(e.FirstParameter)
|
||||
path.append(Path.Command("G0 X{} Y{} Z{}".format(p.x, p.y,
|
||||
obj.SafeHeight.Value)))
|
||||
c = Path.Command("G1 X{} Y{} Z{} F{}".format(p.x, p.y, p.z,
|
||||
obj.ToolController.HorizFeed.Value))
|
||||
path.append(c)
|
||||
path.append(Path.Command("G0 X{} Y{} Z{}".format(p.x, p.y, obj.SafeHeight.Value)))
|
||||
hSpeed = obj.ToolController.HorizFeed.Value
|
||||
vSpeed = obj.ToolController.VertFeed.Value
|
||||
path.append(Path.Command("G1 X{} Y{} Z{} F{}".format(p.x, p.y, p.z, vSpeed)))
|
||||
for e in edges:
|
||||
path.extend(PathGeom.cmdsForEdge(e,
|
||||
hSpeed=obj.ToolController.HorizFeed.Value))
|
||||
path.extend(PathGeom.cmdsForEdge(e, hSpeed=hSpeed, vSpeed=vSpeed))
|
||||
|
||||
return path
|
||||
|
||||
@@ -290,10 +295,12 @@ class ObjectVcarve(PathEngraveBase.ObjectOp):
|
||||
if _sorting == 'global':
|
||||
voronoiWires = _sortVoronoiWires(voronoiWires)
|
||||
|
||||
geom = _Geometry.FromObj(obj, self.model[0])
|
||||
|
||||
pathlist = []
|
||||
pathlist.append(Path.Command("(starting)"))
|
||||
for w in voronoiWires:
|
||||
pWire = self._getPartEdges(obj, w)
|
||||
pWire = self._getPartEdges(obj, w, geom)
|
||||
if pWire:
|
||||
wires.append(pWire)
|
||||
pathlist.extend(cutWire(pWire))
|
||||
@@ -355,6 +362,9 @@ operation will produce no output.'))
|
||||
else:
|
||||
obj.OpFinalDepth = -0.1
|
||||
|
||||
def isToolSupported(self, obj, tool):
|
||||
'''isToolSupported(obj, tool) ... returns True if v-carve op can work with tool.'''
|
||||
return hasattr(tool, 'Diameter') and hasattr(tool, 'CuttingEdgeAngle') and hasattr(tool, 'TipDiameter')
|
||||
|
||||
def SetupProperties():
|
||||
return ["Discretize"]
|
||||
|
||||
Reference in New Issue
Block a user