diff --git a/src/Mod/Path/Gui/Resources/panels/PocketEdit.ui b/src/Mod/Path/Gui/Resources/panels/PocketEdit.ui
index 8825069d42..b1f5db87ba 100644
--- a/src/Mod/Path/Gui/Resources/panels/PocketEdit.ui
+++ b/src/Mod/Path/Gui/Resources/panels/PocketEdit.ui
@@ -26,10 +26,10 @@
Pocket
- -
+
-
- 5
+ 3
@@ -40,7 +40,7 @@
0
0
334
- 331
+ 387
@@ -118,7 +118,7 @@
0
0
334
- 331
+ 387
@@ -196,7 +196,7 @@
0
0
334
- 331
+ 387
@@ -240,108 +240,13 @@
-
-
-
- 0
- 0
- 334
- 331
-
-
-
- Entry
-
-
-
-
-
-
- 0
- 0
- 334
- 331
-
-
-
- Pattern
-
-
- -
-
-
- 1
-
-
- 100
-
-
- 10
-
-
- 100
-
-
-
- -
-
-
- Step Over Percent
-
-
-
- -
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
- QFormLayout::ExpandingFieldsGrow
-
-
-
-
-
- Use ZigZag
-
-
-
- -
-
-
- ZigZag Unidirectional
-
-
-
- -
-
-
- ZigZag Angle
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
0
0
334
- 331
+ 387
@@ -383,48 +288,6 @@
- -
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
- 9
-
-
- 0
-
-
- 0
-
-
-
-
-
- Algorithm
-
-
-
- -
-
-
-
-
- OCC Native
-
-
- -
-
- libarea
-
-
-
-
-
-
-
-
@@ -440,54 +303,27 @@
0
-
-
-
-
- Cut Mode
-
-
-
- -
-
-
-
-
- Climb
-
-
- -
-
- Conventional
-
-
-
-
-
-
-
- 0
-
-
- 0
-
-
-
-
+
+
-
+
- Use Start Point
+ Pattern
- -
+
-
Material Allowance
- -
+
-
@@ -506,9 +342,113 @@
+ -
+
+
+ Cut Mode
+
+
+
+ -
+
+
+ 1
+
+
+ 100
+
+
+ 10
+
+
+ 100
+
+
+
+ -
+
+
+ Step Over Percent
+
+
+
+ -
+
+
-
+
+ Climb
+
+
+ -
+
+ Conventional
+
+
+
+
+ -
+
+
-
+
+ ZigZag
+
+
+ -
+
+ Offset
+
+
+ -
+
+ Spiral
+
+
+ -
+
+ ZigZagOffset
+
+
+ -
+
+ Line
+
+
+ -
+
+ Grid
+
+
+ -
+
+ Triangle
+
+
+
+
+ -
+
+
+ ZigZag Angle
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Use Start Point
+
+
+
-
diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py
index 3152bd1658..c34a8c2c97 100644
--- a/src/Mod/Path/PathScripts/PathPocket.py
+++ b/src/Mod/Path/PathScripts/PathPocket.py
@@ -27,6 +27,8 @@ import Path
from PySide import QtCore, QtGui
from PathScripts import PathUtils
import PathScripts.PathLog as PathLog
+from PathScripts.PathUtils import waiting_effects, depth_params
+import Part
FreeCADGui = None
if FreeCAD.GuiUp:
@@ -35,8 +37,9 @@ if FreeCAD.GuiUp:
"""Path Pocket object and FreeCAD command"""
LOG_MODULE = 'PathPocket'
-PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
-# PathLog.trackModule('PathPocket')
+PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE)
+PathLog.trackModule('PathPocket')
+FreeCAD.setLogLevel('Path.Area',0)
# Qt tanslation handling
def translate(context, text, disambig=None):
@@ -50,9 +53,6 @@ class ObjectPocket:
obj.addProperty("App::PropertyString", "Comment", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "An optional comment for this profile"))
obj.addProperty("App::PropertyString", "UserLabel", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "User Assigned Label"))
- obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm", QtCore.QT_TRANSLATE_NOOP("App::Property", "The library to use to generate the path"))
- obj.Algorithm = ['OCC Native', 'libarea']
-
# Tool Properties
obj.addProperty("App::PropertyLink", "ToolController", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The tool controller that will be used to calculate the path"))
@@ -71,19 +71,10 @@ class ObjectPocket:
obj.addProperty("App::PropertyEnumeration", "StartAt", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Start pocketing at center or boundary"))
obj.StartAt = ['Center', 'Edge']
obj.addProperty("App::PropertyPercent", "StepOver", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Percent of cutter diameter to step over on each pass"))
- obj.addProperty("App::PropertyBool", "KeepToolDown", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Attempts to avoid unnecessary retractions."))
- obj.addProperty("App::PropertyBool", "ZigUnidirectional", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Lifts tool at the end of each pass to respect cut mode."))
- obj.addProperty("App::PropertyBool", "UseZigZag", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Use Zig Zag pattern to clear area."))
obj.addProperty("App::PropertyFloat", "ZigZagAngle", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Angle of the zigzag pattern"))
+ obj.addProperty("App::PropertyEnumeration", "OffsetPattern", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "clearing pattern to use"))
+ obj.OffsetPattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle']
- # Entry Properties
- obj.addProperty("App::PropertyBool", "UseEntry", "Entry", QtCore.QT_TRANSLATE_NOOP("App::Property", "Allow Cutter enter material with a straight plunge."))
- obj.addProperty("App::PropertyFloatConstraint", "RampSize", "Entry", QtCore.QT_TRANSLATE_NOOP("App::Property", "The minimum fraction of tool diameter to use for ramp length"))
- obj.RampSize = (0.0, 0.01, 100.0, 0.5)
- obj.addProperty("App::PropertyFloatConstraint", "HelixSize", "Entry", QtCore.QT_TRANSLATE_NOOP("App::Property", "The fraction of tool diameter to use for calculating helix size."))
- obj.HelixSize = (0.0, 0.01, 100.0, 0.5)
- obj.addProperty("App::PropertyFloatConstraint", "RampAngle", "Entry", QtCore.QT_TRANSLATE_NOOP("App::Property", "The Angle of the ramp entry."))
- obj.RampAngle = (0.0, 0.01, 100.0, 0.5)
# Start Point Properties
obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The start point of this path"))
@@ -92,15 +83,7 @@ class ObjectPocket:
obj.Proxy = self
def onChanged(self, obj, prop):
- if prop == "UseEntry":
- if obj.UseEntry:
- obj.setEditorMode('HelixSize', 0) # make this visible
- obj.setEditorMode('RampAngle', 0) # make this visible
- obj.setEditorMode('RampSize', 0) # make this visible
- else:
- obj.setEditorMode('HelixSize', 2) # make this hidden
- obj.setEditorMode('RampAngle', 2) # make this hidden
- obj.setEditorMode('RampSize', 2) # make this hidden
+ pass
def __getstate__(self):
return None
@@ -108,7 +91,29 @@ class ObjectPocket:
def __setstate__(self, state):
return None
+ def setDepths(proxy, obj):
+ PathLog.track()
+ parentJob = PathUtils.findParentJob(obj)
+ if parentJob is None:
+ return
+ baseobject = parentJob.Base
+ if baseobject is None:
+ return
+
+ try:
+ bb = baseobject.Shape.BoundBox # parent boundbox
+ obj.StartDepth = bb.ZMax
+ obj.ClearanceHeight = bb.ZMax + 5.0
+ obj.SafeHeight = bb.ZMax + 3.0
+ obj.FinalDepth = bb.ZMin
+
+ except:
+ obj.StartDepth = 5.0
+ obj.ClearanceHeight = 10.0
+ obj.SafeHeight = 8.0
+
def addpocketbase(self, obj, ss, sub=""):
+ PathLog.track()
baselist = obj.Base
if baselist is None:
baselist = []
@@ -143,7 +148,6 @@ class ObjectPocket:
else:
baselist.append(item)
obj.Base = baselist
- print("this base is: " + str(baselist))
self.execute(obj)
def getStock(self, obj):
@@ -158,191 +162,68 @@ class ObjectPocket:
return self.getStock(o)
return None
- def buildpathlibarea(self, obj, a):
- """Build the pocket path using libarea algorithm"""
- import PathScripts.PathAreaUtils as PathAreaUtils
- from PathScripts.PathUtils import depth_params
- PathLog.debug("Generating toolpath with libarea offsets.\n")
+ @waiting_effects
+ def _buildPathArea(self, obj, baseobject):
+ PathLog.track()
+ pocket = Path.Area()
+ pocket.setPlane(Part.makeCircle(10))
+ pocket.add(baseobject)
- depthparams = depth_params(
- obj.ClearanceHeight.Value,
- obj.SafeHeight.Value,
- obj.StartDepth.Value,
- obj.StepDown,
- obj.FinishDepth.Value,
- obj.FinalDepth.Value)
-
- extraoffset = obj.MaterialAllowance.Value
stepover = (self.radius * 2) * (float(obj.StepOver)/100)
- use_zig_zag = obj.UseZigZag
- zig_angle = obj.ZigZagAngle
- from_center = (obj.StartAt == "Center")
- keep_tool_down = obj.KeepToolDown
- zig_unidirectional = obj.ZigUnidirectional
- start_point = None
- cut_mode = obj.CutMode
- PathAreaUtils.flush_nc()
- PathAreaUtils.output('mem')
- PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed)
- if obj.UseStartPoint:
- start_point = (obj.StartPoint.x, obj.StartPoint.y)
+ pocketparams = {'Fill': 0,
+ 'Coplanar': 0,
+ 'PocketMode': 1,
+ 'SectionCount': -1,
+ 'Angle': obj.ZigZagAngle,
+ 'FromCenter': (obj.StartAt == "Center"),
+ 'PocketStepover': stepover,
+ 'PocketExtraOffset': obj.MaterialAllowance.Value}
- # print "a," + str(self.radius) + "," + str(extraoffset) + "," + str(stepover) + ",depthparams, " + str(from_center) + "," + str(keep_tool_down) + "," + str(use_zig_zag) + "," + str(zig_angle) + "," + str(zig_unidirectional) + "," + str(start_point) + "," + str(cut_mode)
+ Pattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle']
+ pocketparams['PocketMode'] = Pattern.index(obj.OffsetPattern) + 1
- PathAreaUtils.pocket(
- a,
- self.radius,
- extraoffset,
- stepover,
- depthparams,
- from_center,
- keep_tool_down,
- use_zig_zag,
- zig_angle,
- zig_unidirectional,
- start_point,
- cut_mode)
- return PathAreaUtils.retrieve_gcode()
-
- def buildpathocc(self, obj, shape):
- """Build pocket Path using Native OCC algorithm."""
- import Part
- import DraftGeomUtils
- from PathScripts.PathUtils import fmt, helicalPlunge, rampPlunge, depth_params
-
- PathLog.debug("Generating toolpath with OCC native offsets.\n")
- extraoffset = obj.MaterialAllowance.Value
-
- # Build up the offset loops
- output = ""
- if obj.Comment != "":
- output += '(' + str(obj.Comment)+')\n'
- output += 'G0 Z' + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
-
- offsets = []
- nextradius = self.radius + extraoffset
- result = DraftGeomUtils.pocket2d(shape, nextradius)
- while result:
- offsets.extend(result)
- nextradius += (self.radius * 2) * (float(obj.StepOver)/100)
- result = DraftGeomUtils.pocket2d(shape, nextradius)
-
- # revert the list so we start with the outer wires
- if obj.StartAt != 'Edge':
- offsets.reverse()
-
- plungePos = None
- rampEdge = None
- if obj.UseEntry:
- # Try to find an entry location
- toold = self.radius*2
- helixBounds = DraftGeomUtils.pocket2d(shape, self.radius * (1 + obj.HelixSize))
-
- if helixBounds:
- rampD = obj.RampSize
-
- if obj.StartAt == 'Edge':
- plungePos = helixBounds[0].Edges[0].Vertexes[0].Point
- 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 < self.radius * (1 + obj.HelixSize):
- plungePos = None
- FreeCAD.Console.PrintError(translate("PathPocket", "Helical Entry location not found.\n"))
- # 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 plungePos is None: # If we didn't find a place to helix, how about a ramp?
- FreeCAD.Console.PrintMessage(translate("PathPocket", "Attempting ramp entry.\n"))
- if (offsets[0].Edges[0].Length >= toold * 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 >= toold * rampD) and not (isinstance(offsets[0].Edges[-1].Curve, Part.Circle)):
- rampEdge = offsets[0].Edges[-1]
- else:
- FreeCAD.Console.PrintError(translate("PathPocket", "Ramp Entry location not found.\n"))
- # 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
-
- # For helix-ing/ramping, know where we were last time
- # FIXME: Can probably get this from the "machine"?
- lastZ = obj.ClearanceHeight.Value
-
- startPoint = None
+ pocket.setParams(**pocketparams)
+ PathLog.debug("Pocketing with params: {}".format(pocket.getParams()))
depthparams = depth_params(
- obj.ClearanceHeight.Value,
- obj.SafeHeight.Value,
- obj.StartDepth.Value,
- obj.StepDown,
- obj.FinishDepth.Value,
- obj.FinalDepth.Value)
+ clearance_height=obj.ClearanceHeight.Value,
+ rapid_safety_space=obj.SafeHeight.Value,
+ start_depth=obj.StartDepth.Value,
+ step_down=obj.StepDown.Value,
+ z_finish_step=0.0,
+ final_depth=obj.FinalDepth.Value,
+ user_depths=None)
- for vpos in depthparams.get_depths():
+ sections = pocket.makeSections(mode=0, project=False, heights=depthparams.get_depths())
+ shapelist = [sec.getShape() for sec in sections]
- first = True
- # loop over successive wires
- for currentWire in offsets:
- last = None
- for edge in currentWire.Edges:
- if not last:
- # we set the base GO to our fast move to our starting pos
- if first:
- # If we can helix, do so
- if plungePos:
- output += helicalPlunge(plungePos, obj.RampAngle, vpos, lastZ, self.radius*2, obj.HelixSize, self.horizFeed)
- 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, obj.RampAngle, 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 Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
- output += "G0 X" + fmt(startPoint.x) + " Y" + fmt(startPoint.y) +\
- " Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) + "\n"
- first = False
- # then move slow down to our starting point for our profile
- last = edge.Vertexes[0].Point
- output += "G1 X" + fmt(last.x) + " Y" + fmt(last.y) + " Z" + fmt(vpos) + " F" + fmt(self.vertFeed) + "\n"
- if DraftGeomUtils.geomType(edge) == "Circle":
- point = edge.Vertexes[-1].Point
- if point == last: # edges can come flipped
- point = edge.Vertexes[0].Point
- center = edge.Curve.Center
- relcenter = center.sub(last)
- v1 = last.sub(center)
- v2 = point.sub(center)
- if v1.cross(v2).z < 0:
- output += "G2"
- else:
- output += "G3"
- output += " X" + fmt(point.x) + " Y" + fmt(point.y) + " Z" + fmt(vpos)
- output += " I" + fmt(relcenter.x) + " J" + fmt(relcenter.y) + " K" + fmt(relcenter.z) + " F" + fmt(self.horizFeed)
- output += "\n"
- last = point
- else:
- point = edge.Vertexes[-1].Point
- if point == last: # edges can come flipped
- point = edge.Vertexes[0].Point
- output += "G1 X" + fmt(point.x) + " Y" + fmt(point.y) + " Z" + fmt(vpos) + " F" + fmt(self.horizFeed) + "\n"
- last = point
+ params = {'shapes': shapelist,
+ 'feedrate': self.horizFeed,
+ 'feedrate_v': self.vertFeed,
+ 'verbose': True,
+ 'resume_height': obj.StepDown.Value,
+ 'retraction': obj.ClearanceHeight.Value}
+
+
+ # if obj.UseStartPoint is True and obj.StartPoint is not None:
+ # params['start'] = obj.StartPoint
+
+ pp = Path.fromShapes(**params)
+ PathLog.debug("Generating Path with params: {}".format(params))
+ PathLog.debug(pp)
+ return pp
- # move back up
- output += "G0 Z" + fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
- return output
- # To reload this from FreeCAD, use: import PathScripts.PathPocket; reload(PathScripts.PathPocket)
def execute(self, obj):
- output = ""
+ PathLog.track()
+ commandlist = []
+ commandlist.append(Path.Command("(" + obj.Label + ")"))
+ if not obj.Active:
+ path = Path.Path("(inactive operation)")
+ obj.Path = path
+ obj.ViewObject.Visibility = False
+ return
toolLoad = obj.ToolController
if toolLoad is None or toolLoad.ToolNumber == 0:
@@ -360,51 +241,40 @@ class ObjectPocket:
self.radius = tool.Diameter/2
if obj.Base:
+ PathLog.debug("base items exist. Processing...")
for b in obj.Base:
+ PathLog.debug("Base item: {}".format(b))
for sub in b[1]:
- import Part
- import PathScripts.PathKurveUtils
if "Face" in sub:
shape = getattr(b[0].Shape, sub)
- wire = shape.OuterWire
- edges = wire.Edges
else:
edges = [getattr(b[0].Shape, sub) for sub in b[1]]
- wire = Part.Wire(edges)
- shape = None
+ shape = Part.makeFace(edges, 'Part::FaceMakerSimple')
- # output = ""
- if obj.Algorithm == "OCC Native":
- if shape is None:
- shape = wire
- output += self.buildpathocc(obj, shape)
- else:
- try:
- import area
- except:
- FreeCAD.Console.PrintError(translate("PathKurve", "libarea needs to be installed for this command to work.\n"))
- return
+ env = PathUtils.getEnvelope(shape, obj.StartDepth)
+ try:
+ commandlist.extend(self._buildPathArea(obj, env.cut(b[0].Shape)).Commands)
+ except Exception as e:
+ print(e)
+ FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a pocket path. Check project and tool config.")
+ else: # process the job base object as a whole
+ PathLog.debug("processing the whole job base object")
+ parentJob = PathUtils.findParentJob(obj)
+ if parentJob is None:
+ return
+ baseobject = parentJob.Base
+ if baseobject is None:
+ return
+ env = PathUtils.getEnvelope(baseobject.Shape, obj.StartDepth)
+ try:
+ commandlist.extend(self._buildPathArea(obj, env.cut(baseobject.Shape)).Commands)
+ except Exception as e:
+ print(e)
+ FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a pocket path. Check project and tool config.")
- a = area.Area()
- if shape is None:
- c = PathScripts.PathKurveUtils.makeAreaCurve(wire.Edges, 'CW')
- a.append(c)
- else:
- for w in shape.Wires:
- c = PathScripts.PathKurveUtils.makeAreaCurve(w.Edges, 'CW')
- a.append(c)
-
- a.Reorder()
- output += self.buildpathlibarea(obj, a)
-
- if obj.Active:
- path = Path.Path(output)
- obj.Path = path
- obj.ViewObject.Visibility = True
- else:
- path = Path.Path("(inactive operation)")
- obj.Path = path
- obj.ViewObject.Visibility = False
+ path = Path.Path(commandlist)
+ obj.Path = path
+ obj.ViewObject.Visibility = True
class _CommandSetPocketStartPoint:
@@ -468,6 +338,7 @@ class CommandPathPocket:
return False
def Activated(self):
+ PathLog.track()
zbottom = 0.0
ztop = 10.0
@@ -480,22 +351,18 @@ class CommandPathPocket:
FreeCADGui.doCommand('obj.Active = True')
FreeCADGui.doCommand('PathScripts.PathPocket.ViewProviderPocket(obj.ViewObject)')
FreeCADGui.doCommand('from PathScripts import PathUtils')
- FreeCADGui.doCommand('obj.Algorithm = "libarea"')
FreeCADGui.doCommand('obj.StepOver = 100')
FreeCADGui.doCommand('obj.ClearanceHeight = 10') # + str(bb.ZMax + 2.0))
FreeCADGui.doCommand('obj.StepDown = 1.0')
FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop))
FreeCADGui.doCommand('obj.FinalDepth =' + str(zbottom))
FreeCADGui.doCommand('obj.ZigZagAngle = 45')
- FreeCADGui.doCommand('obj.UseEntry = False')
- FreeCADGui.doCommand('obj.RampAngle = 3.0')
- FreeCADGui.doCommand('obj.RampSize = 0.75')
- FreeCADGui.doCommand('obj.HelixSize = 0.75')
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
+ FreeCADGui.doCommand('PathScripts.PathPocket.ObjectPocket.setDepths(obj.Proxy, obj)')
FreeCADGui.doCommand('obj.ToolController = PathScripts.PathUtils.findToolController(obj)')
FreeCAD.ActiveDocument.commitTransaction()
- FreeCAD.ActiveDocument.recompute()
+ #FreeCAD.ActiveDocument.recompute()
FreeCADGui.doCommand('obj.ViewObject.startEditing()')
@@ -534,14 +401,8 @@ class TaskPanel:
self.obj.MaterialAllowance = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value
if hasattr(self.obj, "UseStartPoint"):
self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
- if hasattr(self.obj, "Algorithm"):
- self.obj.Algorithm = str(self.form.algorithmSelect.currentText())
if hasattr(self.obj, "CutMode"):
self.obj.CutMode = str(self.form.cutMode.currentText())
- if hasattr(self.obj, "UseZigZag"):
- self.obj.UseZigZag = self.form.useZigZag.isChecked()
- if hasattr(self.obj, "ZigUnidirectional"):
- self.obj.ZigUnidirectional = self.form.zigZagUnidirectional.isChecked()
if hasattr(self.obj, "ZigZagAngle"):
self.obj.ZigZagAngle = FreeCAD.Units.Quantity(self.form.zigZagAngle.text()).Value
if hasattr(self.obj, "StepOver"):
@@ -561,16 +422,14 @@ class TaskPanel:
self.form.stepDown.setText(FreeCAD.Units.Quantity(self.obj.StepDown.Value, FreeCAD.Units.Length).UserString)
self.form.extraOffset.setText(FreeCAD.Units.Quantity(self.obj.MaterialAllowance, FreeCAD.Units.Length).UserString)
self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
- self.form.useZigZag.setChecked(self.obj.UseZigZag)
- self.form.zigZagUnidirectional.setChecked(self.obj.ZigUnidirectional)
self.form.zigZagAngle.setText(FreeCAD.Units.Quantity(self.obj.ZigZagAngle, FreeCAD.Units.Angle).UserString)
self.form.stepOverPercent.setValue(self.obj.StepOver)
- index = self.form.algorithmSelect.findText(self.obj.Algorithm, QtCore.Qt.MatchFixedString)
- if index >= 0:
- self.form.algorithmSelect.blockSignals(True)
- self.form.algorithmSelect.setCurrentIndex(index)
- self.form.algorithmSelect.blockSignals(False)
+ # index = self.form.algorithmSelect.findText(self.obj.Algorithm, QtCore.Qt.MatchFixedString)
+ # if index >= 0:
+ # self.form.algorithmSelect.blockSignals(True)
+ # self.form.algorithmSelect.setCurrentIndex(index)
+ # self.form.algorithmSelect.blockSignals(False)
index = self.form.cutMode.findText(
self.obj.CutMode, QtCore.Qt.MatchFixedString)
@@ -579,8 +438,6 @@ class TaskPanel:
self.form.cutMode.setCurrentIndex(index)
self.form.cutMode.blockSignals(False)
- # for i in self.obj.Base:
- # self.form.baseList.addItem(i[0].Name + "." + i[1][0])
self.form.baseList.blockSignals(True)
for i in self.obj.Base:
for sub in i[1]:
@@ -632,9 +489,6 @@ class TaskPanel:
for sub in i[1]:
self.form.baseList.addItem(i[0].Name + "." + sub)
- # for i in self.obj.Base:
- # self.form.baseList.addItem(i[0].Name + "." + i[1][0])
-
def deleteBase(self):
dlist = self.form.baseList.selectedItems()
newlist = []
@@ -701,15 +555,12 @@ class TaskPanel:
self.form.clearanceHeight.editingFinished.connect(self.getFields)
# operation
- self.form.algorithmSelect.currentIndexChanged.connect(self.getFields)
self.form.cutMode.currentIndexChanged.connect(self.getFields)
self.form.useStartPoint.clicked.connect(self.getFields)
self.form.extraOffset.editingFinished.connect(self.getFields)
# Pattern
self.form.stepOverPercent.editingFinished.connect(self.getFields)
- self.form.useZigZag.clicked.connect(self.getFields)
- self.form.zigZagUnidirectional.clicked.connect(self.getFields)
self.form.zigZagAngle.editingFinished.connect(self.getFields)
self.setFields()
diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py
index 69ba9c9342..4b2b7f0fd4 100644
--- a/src/Mod/Path/PathScripts/PathProfile.py
+++ b/src/Mod/Path/PathScripts/PathProfile.py
@@ -25,17 +25,17 @@
import FreeCAD
import Path
import numpy
-import TechDraw
import ArchPanel
+import Part
-from FreeCAD import Vector
from PathScripts import PathUtils
from PathScripts.PathUtils import depth_params
import PathScripts.PathLog as PathLog
LOG_MODULE = 'PathProfile'
-PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE)
+PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
# PathLog.trackModule('PathProfile')
+FreeCAD.setLogLevel('Path.Area', 0)
if FreeCAD.GuiUp:
import FreeCADGui
@@ -73,14 +73,6 @@ class ObjectProfile:
# Start Point Properties
obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The start point of this path"))
obj.addProperty("App::PropertyBool", "UseStartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if specifying a Start Point"))
- obj.addProperty("App::PropertyLength", "ExtendAtStart", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "extra length of tool path before start of part edge"))
- obj.addProperty("App::PropertyLength", "LeadInLineLen", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "length of straight segment of toolpath that comes in at angle to first part edge"))
-
- # End Point Properties
- obj.addProperty("App::PropertyBool", "UseEndPoint", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if specifying an End Point"))
- obj.addProperty("App::PropertyLength", "ExtendAtEnd", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "extra length of tool path after end of part edge"))
- obj.addProperty("App::PropertyLength", "LeadOutLineLen", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "length of straight segment of toolpath that comes in at angle to last part edge"))
- obj.addProperty("App::PropertyVector", "EndPoint", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The end point of this path"))
# Profile Properties
obj.addProperty("App::PropertyEnumeration", "Side", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Side of edge that tool should cut"))
@@ -88,8 +80,6 @@ class ObjectProfile:
obj.addProperty("App::PropertyEnumeration", "Direction", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW"))
obj.Direction = ['CW', 'CCW'] # this is the direction that the profile runs
obj.addProperty("App::PropertyBool", "UseComp", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if using Cutter Radius Compensation"))
-
- obj.addProperty("App::PropertyDistance", "RollRadius", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Radius at start and end"))
obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra value to stay away from final profile- good for roughing toolpath"))
obj.addProperty("App::PropertyBool", "processHoles", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Profile holes as well as the outline"))
obj.addProperty("App::PropertyBool", "processPerimeter", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Profile the outline"))
@@ -145,29 +135,45 @@ class ObjectProfile:
obj.Base = baselist
self.execute(obj)
- def _buildPathLibarea(self, obj, edgelist, isHole):
- import PathScripts.PathKurveUtils as PathKurveUtils
- # import math
- # import area
+ def _buildPathArea(self, obj, baseobject, isHole, start=None):
+ PathLog.track()
+ profile = Path.Area()
+ profile.setPlane(Part.makeCircle(10))
+ profile.add(baseobject)
- PathLog.track("edgelist: {} \n".format(edgelist))
+ profileparams = {'Fill': 0,
+ 'Coplanar': 0,
+ 'Offset': 0.0,
+ 'SectionCount': -1}
- output = ""
- if obj.Comment != "":
- output += '(' + str(obj.Comment)+')\n'
+ if obj.UseComp:
+ if isHole:
+ profileparams['Offset'] = 0 - self.radius+obj.OffsetExtra.Value
+ else:
+ profileparams['Offset'] = self.radius+obj.OffsetExtra.Value
- if obj.StartPoint and obj.UseStartPoint:
- startpoint = obj.StartPoint
- else:
- startpoint = None
+ profile.setParams(**profileparams)
+ # PathLog.debug("About to profile with params: {}".format(profileparams))
+ PathLog.debug("About to profile with params: {}".format(profile.getParams()))
- if obj.EndPoint and obj.UseEndPoint:
- endpoint = obj.EndPoint
- else:
- endpoint = None
+ depthparams = depth_params(
+ clearance_height=obj.ClearanceHeight.Value,
+ rapid_safety_space=obj.SafeHeight.Value,
+ start_depth=obj.StartDepth.Value,
+ step_down=obj.StepDown.Value,
+ z_finish_step=0.0,
+ final_depth=obj.FinalDepth.Value,
+ user_depths=None)
- PathKurveUtils.output('mem')
- PathKurveUtils.feedrate_hv(self.horizFeed, self.vertFeed)
+ sections = profile.makeSections(mode=0, project=True, heights=depthparams.get_depths())
+ shapelist = [sec.getShape() for sec in sections]
+
+ params = {'shapes': shapelist,
+ 'feedrate': self.horizFeed,
+ 'feedrate_v': self.vertFeed,
+ 'verbose': False,
+ 'resume_height': obj.StepDown.Value,
+ 'retraction': obj.ClearanceHeight.Value}
# Reverse the direction for holes
if isHole:
@@ -175,50 +181,28 @@ class ObjectProfile:
else:
direction = obj.Direction
- output = ""
- output += "G0 Z" + str(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
- curve = PathKurveUtils.makeAreaCurve(edgelist, direction, startpoint, endpoint)
+ if direction == 'CCW':
+ params['orientation'] = 1
+ else:
+ params['orientation'] = 0
- '''The following line uses a profile function written for use with FreeCAD. It's clean but incomplete. It doesn't handle
-print "x = " + str(point.x)
-print "y - " + str(point.y)
- holding tags
- start location
- CRC
- or probably other features in heekscnc'''
+ if obj.UseStartPoint is True and obj.StartPoint is not None:
+ params['start'] = obj.StartPoint
- '''The following calls the original procedure from h
- toolLoad = obj.activeTCeekscnc profile function. This, in turn, calls many other procedures to modify the profile.
- This procedure is hacked together from heekscnc and has not been thoroughly reviewed or understood for FreeCAD. It can probably be
- thoroughly optimized and improved but it'll take a smarter mind than mine to do it. -sliptonic Feb16'''
- roll_radius = 2.0
- extend_at_start = 0.0
- extend_at_end = 0.0
- lead_in_line_len = 0.0
- lead_out_line_len = 0.0
+ pp = Path.fromShapes(**params)
+ PathLog.debug("Generating Path with params: {}".format(params))
+ PathLog.debug(pp)
- depthparams = depth_params(
- obj.ClearanceHeight.Value,
- obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown.Value, 0.0,
- obj.FinalDepth.Value, None)
-
- PathKurveUtils.profile2(
- curve, obj.Side, self.radius, self.vertFeed, self.horizFeed,
- self.vertRapid, self.horizRapid, obj.OffsetExtra.Value, roll_radius,
- None, None, depthparams, extend_at_start, extend_at_end,
- lead_in_line_len, lead_out_line_len)
-
- output += PathKurveUtils.retrieve_gcode()
- return output
+ return pp
def execute(self, obj):
- import Part # math #DraftGeomUtils
- output = ""
+ import Part
+ commandlist = []
toolLoad = obj.ToolController
if toolLoad is None or toolLoad.ToolNumber == 0:
FreeCAD.Console.PrintError("No Tool Controller is selected. We need a tool to build a Path.")
- #return
+ return
else:
self.vertFeed = toolLoad.VertFeed.Value
self.horizFeed = toolLoad.HorizFeed.Value
@@ -231,11 +215,12 @@ print "y - " + str(point.y)
else:
self.radius = tool.Diameter/2
- output += "(" + obj.Label + ")"
+ commandlist.append(Path.Command("(" + obj.Label + ")"))
+
if obj.UseComp:
- output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")"
+ commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")"))
else:
- output += "(Uncompensated Tool Path)"
+ commandlist.append(Path.Command("(Uncompensated Tool Path)"))
if obj.Base:
holes = []
@@ -247,25 +232,40 @@ print "y - " + str(point.y)
faces.append(shape)
if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face
holes += shape.Wires[1:]
-
else:
print ("found a base object which is not a face. Can't continue.")
return
- profileshape = Part.makeCompound(faces)
- profilewire = TechDraw.findShapeOutline(profileshape, 1, Vector(0, 0, 1))
if obj.processHoles:
for wire in holes:
- edgelist = wire.Edges
- edgelist = Part.__sortEdges__(edgelist)
- output += self._buildPathLibarea(obj, edgelist, True)
+ f = Part.makeFace(wire, 'Part::FaceMakerSimple')
+
+ # shift the compound to the bottom of the base object for
+ # proper sectioning
+ zShift = b[0].Shape.BoundBox.ZMin - f.BoundBox.ZMin
+ newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), f.Placement.Rotation)
+ f.Placement = newPlace
+ env = PathUtils.getEnvelope(f, obj.StartDepth)
+ try:
+ commandlist.extend(self._buildPathArea(obj, baseobject=env, isHole=True, start=None).Commands)
+ except Exception as e:
+ print(e)
+ FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
+
+ profileshape = Part.makeCompound(faces)
+ zShift = b[0].Shape.BoundBox.ZMin - profileshape.BoundBox.ZMin
+ newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), profileshape.Placement.Rotation)
+ profileshape.Placement = newPlace
if obj.processPerimeter:
- edgelist = profilewire.Edges
- edgelist = Part.__sortEdges__(edgelist)
- output += self._buildPathLibarea(obj, edgelist, False)
+ env = PathUtils.getEnvelope(profileshape, obj.StartDepth)
+ try:
+ commandlist.extend(self._buildPathArea(obj, baseobject=env, isHole=False, start=None).Commands)
+ except Exception as e:
+ print(e)
+ FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
- else: #Try to build targets frorm the job base
+ else: # Try to build targets frorm the job base
parentJob = PathUtils.findParentJob(obj)
if parentJob is None:
return
@@ -279,28 +279,29 @@ print "y - " + str(point.y)
shapes = baseobject.Proxy.getOutlines(baseobject, transform=True)
for shape in shapes:
for wire in shape.Wires:
- edgelist = wire.Edges
- edgelist = Part.__sortEdges__(edgelist)
- PathLog.debug("Processing panel perimeter. edges found: {}".format(len(edgelist)))
- try:
- output += self._buildPathLibarea(obj, edgelist, isHole=False)
- except:
- FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
+ f = Part.makeFace(wire, 'Part::FaceMakerSimple')
+ env = PathUtils.getEnvelope(f, obj.StartDepth)
+ try:
+ commandlist.extend(self._buildPathArea(obj, baseobject=env, isHole=False, start=None).Commands)
+ except Exception as e:
+ print(e)
+ FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
shapes = baseobject.Proxy.getHoles(baseobject, transform=True)
for shape in shapes:
for wire in shape.Wires:
drillable = PathUtils.isDrillable(baseobject.Proxy, wire)
if (drillable and obj.processCircles) or (not drillable and obj.processHoles):
- edgelist = wire.Edges
- edgelist = Part.__sortEdges__(edgelist)
+ f = Part.makeFace(wire, 'Part::FaceMakerSimple')
+ env = PathUtils.getEnvelope(f, obj.StartDepth)
try:
- output += self._buildPathLibarea(obj, edgelist, isHole=True)
- except:
+ commandlist.extend(self._buildPathArea(obj, baseobject=env, isHole=True, start=None).Commands)
+ except Exception as e:
+ print(e)
FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
if obj.Active:
- path = Path.Path(output)
+ path = Path.Path(commandlist)
obj.Path = path
obj.ViewObject.Visibility = True
@@ -356,25 +357,6 @@ class _CommandSetStartPoint:
FreeCADGui.Snapper.getPoint(callback=self.setpoint)
-class _CommandSetEndPoint:
- def GetResources(self):
- return {'Pixmap': 'Path-EndPoint',
- 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Profile", "Pick End Point"),
- 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Profile", "Pick End Point")}
-
- def IsActive(self):
- return FreeCAD.ActiveDocument is not None
-
- def setpoint(self, point, o):
- obj = FreeCADGui.Selection.getSelection()[0]
- obj.EndPoint.x = point.x
- obj.EndPoint.y = point.y
-
- def Activated(self):
-
- FreeCADGui.Snapper.getPoint(callback=self.setpoint)
-
-
class CommandPathProfile:
def GetResources(self):
return {'Pixmap': 'Path-Profile-Face',
@@ -454,14 +436,10 @@ class TaskPanel:
self.obj.StepDown = FreeCAD.Units.Quantity(self.form.stepDown.text()).Value
if hasattr(self.obj, "OffsetExtra"):
self.obj.OffsetExtra = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value
- if hasattr(self.obj, "RollRadius"):
- self.obj.RollRadius = FreeCAD.Units.Quantity(self.form.rollRadius.text()).Value
if hasattr(self.obj, "UseComp"):
self.obj.UseComp = self.form.useCompensation.isChecked()
if hasattr(self.obj, "UseStartPoint"):
self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
- if hasattr(self.obj, "UseEndPoint"):
- self.obj.UseEndPoint = self.form.useEndPoint.isChecked()
if hasattr(self.obj, "Side"):
self.obj.Side = str(self.form.cutSide.currentText())
if hasattr(self.obj, "Direction"):
@@ -486,10 +464,8 @@ class TaskPanel:
self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value, FreeCAD.Units.Length).UserString)
self.form.stepDown.setText(FreeCAD.Units.Quantity(self.obj.StepDown.Value, FreeCAD.Units.Length).UserString)
self.form.extraOffset.setText(FreeCAD.Units.Quantity(self.obj.OffsetExtra.Value, FreeCAD.Units.Length).UserString)
- self.form.rollRadius.setText(FreeCAD.Units.Quantity(self.obj.RollRadius.Value, FreeCAD.Units.Length).UserString)
self.form.useCompensation.setChecked(self.obj.UseComp)
self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
- self.form.useEndPoint.setChecked(self.obj.UseEndPoint)
self.form.processHoles.setChecked(self.obj.processHoles)
self.form.processPerimeter.setChecked(self.obj.processPerimeter)
self.form.processCircles.setChecked(self.obj.processCircles)
@@ -513,6 +489,7 @@ class TaskPanel:
self.form.uiToolController.blockSignals(True)
self.form.uiToolController.addItems(labels)
self.form.uiToolController.blockSignals(False)
+
if self.obj.ToolController is not None:
index = self.form.uiToolController.findText(
self.obj.ToolController.Label, QtCore.Qt.MatchFixedString)
@@ -628,9 +605,7 @@ class TaskPanel:
self.form.direction.currentIndexChanged.connect(self.getFields)
self.form.useCompensation.clicked.connect(self.getFields)
self.form.useStartPoint.clicked.connect(self.getFields)
- self.form.useEndPoint.clicked.connect(self.getFields)
self.form.extraOffset.editingFinished.connect(self.getFields)
- self.form.rollRadius.editingFinished.connect(self.getFields)
self.form.processHoles.clicked.connect(self.getFields)
self.form.processPerimeter.clicked.connect(self.getFields)
self.form.processCircles.clicked.connect(self.getFields)
@@ -660,6 +635,5 @@ if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_Profile', CommandPathProfile())
FreeCADGui.addCommand('Set_StartPoint', _CommandSetStartPoint())
- FreeCADGui.addCommand('Set_EndPoint', _CommandSetEndPoint())
FreeCAD.Console.PrintLog("Loading PathProfile... done\n")
diff --git a/src/Mod/Path/PathScripts/PathProfileEdges.py b/src/Mod/Path/PathScripts/PathProfileEdges.py
index aeff454fc6..ed7cd099fb 100644
--- a/src/Mod/Path/PathScripts/PathProfileEdges.py
+++ b/src/Mod/Path/PathScripts/PathProfileEdges.py
@@ -24,6 +24,7 @@
import FreeCAD
import Path
+import Part
from PathScripts import PathUtils
from PathScripts.PathUtils import depth_params
from DraftGeomUtils import findWires
@@ -33,7 +34,7 @@ from PathScripts.PathUtils import waiting_effects
"""Path Profile from Edges Object and Command"""
LOG_MODULE = 'PathProfileEdges'
-PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
+PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE)
# PathLog.trackModule('PathProfileEdges')
if FreeCAD.GuiUp:
@@ -72,23 +73,14 @@ class ObjectProfile:
# Start Point Properties
obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The start point of this path"))
obj.addProperty("App::PropertyBool", "UseStartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if specifying a Start Point"))
- obj.addProperty("App::PropertyLength", "ExtendAtStart", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "extra length of tool path before start of part edge"))
- obj.addProperty("App::PropertyLength", "LeadInLineLen", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "length of straight segment of toolpath that comes in at angle to first part edge"))
-
- # End Point Properties
- obj.addProperty("App::PropertyBool", "UseEndPoint", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if specifying an End Point"))
- obj.addProperty("App::PropertyLength", "ExtendAtEnd", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "extra length of tool path after end of part edge"))
- obj.addProperty("App::PropertyLength", "LeadOutLineLen", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "length of straight segment of toolpath that comes in at angle to last part edge"))
- obj.addProperty("App::PropertyVector", "EndPoint", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The end point of this path"))
# Profile Properties
obj.addProperty("App::PropertyEnumeration", "Side", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Side of edge that tool should cut"))
- obj.Side = ['Left', 'Right', 'On'] # side of profile that cutter is on in relation to direction of profile
+ obj.Side = ['Left', 'Right'] # side of profile that cutter is on in relation to direction of profile
obj.addProperty("App::PropertyEnumeration", "Direction", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW"))
obj.Direction = ['CW', 'CCW'] # this is the direction that the profile runs
obj.addProperty("App::PropertyBool", "UseComp", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if using Cutter Radius Compensation"))
- obj.addProperty("App::PropertyDistance", "RollRadius", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Radius at start and end"))
obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra value to stay away from final profile- good for roughing toolpath"))
obj.Proxy = self
@@ -134,70 +126,82 @@ class ObjectProfile:
else:
baselist.append(item)
obj.Base = baselist
- self.execute(obj)
+ #self.execute(obj)
+
@waiting_effects
- def _buildPathLibarea(self, obj, edgelist):
- import PathScripts.PathKurveUtils as PathKurveUtils
- # import math
- # import area
- output = ""
- if obj.Comment != "":
- output += '(' + str(obj.Comment)+')\n'
+ def _buildPathArea(self, obj, baseobject, start=None):
+ PathLog.track()
+ profile = Path.Area()
+ profile.setPlane(Part.makeCircle(10))
+ profile.add(baseobject)
- if obj.StartPoint and obj.UseStartPoint:
- startpoint = obj.StartPoint
- else:
- startpoint = None
+ profileparams = {'Fill': 0,
+ 'Coplanar': 0,
+ 'Offset': 0.0,
+ 'SectionCount': -1}
- if obj.EndPoint and obj.UseEndPoint:
- endpoint = obj.EndPoint
- else:
- endpoint = None
+ if obj.UseComp:
+ if obj.Side == 'Right':
+ profileparams['Offset'] = 0 - self.radius+obj.OffsetExtra.Value
+ else:
+ profileparams['Offset'] = self.radius+obj.OffsetExtra.Value
- PathKurveUtils.output('mem')
- PathKurveUtils.feedrate_hv(self.horizFeed, self.vertFeed)
- output = ""
- output += "G0 Z" + str(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
- curve = PathKurveUtils.makeAreaCurve(edgelist, obj.Direction, startpoint, endpoint)
-
- '''The following line uses a profile function written for use with FreeCAD. It's clean but incomplete. It doesn't handle
-print("x = " + str(point.x))
-print("y - " + str(point.y))
- holding tags
- start location
- CRC
- or probably other features in heekscnc'''
- # output += PathKurveUtils.profile(curve, side, radius, vf, hf, offset_extra, rapid_safety_space, clearance, start_depth, step_down, final_depth, use_CRC)
-
- '''The following calls the original procedure from h
- toolLoad = obj.activeTCeekscnc profile function. This, in turn, calls many other procedures to modify the profile.
- This procedure is hacked together from heekscnc and has not been thoroughly reviewed or understood for FreeCAD. It can probably be
- thoroughly optimized and improved but it'll take a smarter mind than mine to do it. -sliptonic Feb16'''
- roll_radius = 2.0
- extend_at_start = 0.0
- extend_at_end = 0.0
- lead_in_line_len = 0.0
- lead_out_line_len = 0.0
+ profile.setParams(**profileparams)
+ # PathLog.debug("About to profile with params: {}".format(profileparams))
+ PathLog.debug("About to profile with params: {}".format(profile.getParams()))
depthparams = depth_params(
- obj.ClearanceHeight.Value,
- obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown.Value, 0.0,
- obj.FinalDepth.Value, None)
+ clearance_height=obj.ClearanceHeight.Value,
+ rapid_safety_space=obj.SafeHeight.Value,
+ start_depth=obj.StartDepth.Value,
+ step_down=obj.StepDown.Value,
+ z_finish_step=0.0,
+ final_depth=obj.FinalDepth.Value,
+ user_depths=None)
- PathKurveUtils.profile2(
- curve, obj.Side, self.radius, self.vertFeed, self.horizFeed,
- self.vertRapid, self.horizRapid, obj.OffsetExtra.Value, roll_radius,
- None, None, depthparams, extend_at_start, extend_at_end,
- lead_in_line_len, lead_out_line_len)
+ sections = profile.makeSections(mode=0, project=True, heights=depthparams.get_depths())
+ shapelist = [sec.getShape() for sec in sections]
+
+ params = {'shapes': shapelist,
+ 'feedrate': self.horizFeed,
+ 'feedrate_v': self.vertFeed,
+ 'verbose': False,
+ 'resume_height': obj.StepDown.Value,
+ 'retraction': obj.ClearanceHeight.Value}
+
+ # Reverse the direction for holes
+ # if isHole:
+ # direction = "CW" if obj.Direction == "CCW" else "CCW"
+ # else:
+ # direction = obj.Direction
+
+ if obj.Direction == 'CCW':
+ params['orientation'] = 0
+ else:
+ params['orientation'] = 1
+
+ if obj.UseStartPoint is True and obj.StartPoint is not None:
+ params['start'] = obj.StartPoint
+
+ pp = Path.fromShapes(**params)
+ PathLog.debug("Generating Path with params: {}".format(params))
+ PathLog.debug(pp)
+
+ return pp
- output += PathKurveUtils.retrieve_gcode()
- return output
def execute(self, obj):
- import Part # math #DraftGeomUtils
- output = ""
+ # import Part # math #DraftGeomUtils
+ commandlist = []
+
+ if not obj.Active:
+ path = Path.Path("(inactive operation)")
+ obj.Path = path
+ obj.ViewObject.Visibility = False
+ return
+
toolLoad = obj.ToolController
if toolLoad is None or toolLoad.ToolNumber == 0:
@@ -214,12 +218,13 @@ print("y - " + str(point.y))
else:
self.radius = tool.Diameter/2
- output += "(" + obj.Label + ")"
+ commandlist.append(Path.Command("(" + obj.Label + ")"))
+
if obj.UseComp:
- output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")"
+ commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")"))
else:
- output += "(Uncompensated Tool Path)"
-
+ commandlist.append(Path.Command("(Uncompensated Tool Path)"))
+
if obj.Base:
wires = []
@@ -230,19 +235,26 @@ print("y - " + str(point.y))
wires.extend(findWires(edgelist))
for wire in wires:
- edgelist = wire.Edges
- edgelist = Part.__sortEdges__(edgelist)
- output += self._buildPathLibarea(obj, edgelist)
+ f = Part.makeFace(wire, 'Part::FaceMakerSimple')
- if obj.Active:
- path = Path.Path(output)
- obj.Path = path
- obj.ViewObject.Visibility = True
+ # shift the compound to the bottom of the base object for
+ # proper sectioning
+ zShift = b[0].Shape.BoundBox.ZMin - f.BoundBox.ZMin
+ newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), f.Placement.Rotation)
+ f.Placement = newPlace
+ env = PathUtils.getEnvelope(f, obj.StartDepth)
- else:
- path = Path.Path("(inactive operation)")
- obj.Path = path
- obj.ViewObject.Visibility = False
+ try:
+ # commandlist.extend(self._buildPathArea(obj, wire).Commands)
+ commandlist.extend(self._buildPathArea(obj, baseobject=env, start=None).Commands)
+
+ except Exception as e:
+ print(e)
+ FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
+
+ path = Path.Path(commandlist)
+ obj.Path = path
+ obj.ViewObject.Visibility = True
class _ViewProviderProfile:
@@ -342,7 +354,7 @@ class CommandPathProfileEdges:
FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom))
FreeCADGui.doCommand('obj.SafeHeight = ' + str(ztop + 2.0))
- FreeCADGui.doCommand('obj.Side = "On"')
+ FreeCADGui.doCommand('obj.Side = "Right"')
FreeCADGui.doCommand('obj.OffsetExtra = 0.0')
FreeCADGui.doCommand('obj.Direction = "CW"')
FreeCADGui.doCommand('obj.UseComp = True')
@@ -390,14 +402,8 @@ class TaskPanel:
self.obj.StepDown = FreeCAD.Units.Quantity(self.form.stepDown.text()).Value
if hasattr(self.obj, "OffsetExtra"):
self.obj.OffsetExtra = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value
- if hasattr(self.obj, "RollRadius"):
- self.obj.RollRadius = FreeCAD.Units.Quantity(self.form.rollRadius.text()).Value
if hasattr(self.obj, "UseComp"):
self.obj.UseComp = self.form.useCompensation.isChecked()
- if hasattr(self.obj, "UseStartPoint"):
- self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
- if hasattr(self.obj, "UseEndPoint"):
- self.obj.UseEndPoint = self.form.useEndPoint.isChecked()
if hasattr(self.obj, "Side"):
self.obj.Side = str(self.form.cutSide.currentText())
if hasattr(self.obj, "Direction"):
@@ -416,10 +422,7 @@ class TaskPanel:
self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value, FreeCAD.Units.Length).UserString)
self.form.stepDown.setText(FreeCAD.Units.Quantity(self.obj.StepDown.Value, FreeCAD.Units.Length).UserString)
self.form.extraOffset.setText(FreeCAD.Units.Quantity(self.obj.OffsetExtra.Value, FreeCAD.Units.Length).UserString)
- self.form.rollRadius.setText(FreeCAD.Units.Quantity(self.obj.RollRadius.Value, FreeCAD.Units.Length).UserString)
self.form.useCompensation.setChecked(self.obj.UseComp)
- self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
- self.form.useEndPoint.setChecked(self.obj.UseEndPoint)
controllers = PathUtils.getToolControllers(self.obj)
labels = [c.Label for c in controllers]
@@ -549,10 +552,7 @@ class TaskPanel:
self.form.cutSide.currentIndexChanged.connect(self.getFields)
self.form.direction.currentIndexChanged.connect(self.getFields)
self.form.useCompensation.clicked.connect(self.getFields)
- self.form.useStartPoint.clicked.connect(self.getFields)
- self.form.useEndPoint.clicked.connect(self.getFields)
self.form.extraOffset.editingFinished.connect(self.getFields)
- self.form.rollRadius.editingFinished.connect(self.getFields)
self.setFields()
sel = FreeCADGui.Selection.getSelectionEx()
@@ -577,7 +577,5 @@ class SelObserver:
if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_Profile_Edges', CommandPathProfileEdges())
- FreeCADGui.addCommand('Set_StartPoint', _CommandSetStartPoint())
- FreeCADGui.addCommand('Set_EndPoint', _CommandSetEndPoint())
FreeCAD.Console.PrintLog("Loading PathProfileEdges... done\n")
diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py
index 8dccbb0234..7a4924d03a 100644
--- a/src/Mod/Path/PathScripts/PathUtils.py
+++ b/src/Mod/Path/PathScripts/PathUtils.py
@@ -38,10 +38,9 @@ from PySide import QtGui
LOG_MODULE = 'PathUtils'
PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
-# PathLog.trackModule('PathUtils')
+PathLog.trackModule('PathUtils')
FreeCAD.setLogLevel('Path.Area', 0)
-
def waiting_effects(function):
def new_function(*args, **kwargs):
if not FreeCAD.GuiUp:
@@ -253,10 +252,16 @@ def getEnvelope(partshape, stockheight=None):
# partshape.BoundBox.ZMin)
area.setPlane(makeWorkplane(partshape))
sec = area.makeSections(heights=[1.0], project=True)[0].getShape()
+
if stockheight is not None:
- return sec.extrude(FreeCAD.Vector(0, 0, stockheight))
+ eLength = float(stockheight)-partshape.BoundBox.ZMin
+ envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, eLength))
else:
- return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZLength))
+ envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZLength))
+ if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
+ removalshape=FreeCAD.ActiveDocument.addObject("Part::Feature","RemovedMaterial")
+ removalshape.Shape = envelopeshape
+ return envelopeshape
def reverseEdge(e):