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):