From bccc959f2de8a952542ba99643882d472b5f30c0 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Fri, 14 Apr 2017 08:36:21 -0500 Subject: [PATCH 01/20] Path: Convert Contour and MillFace to use PathArea --- .../Path/Gui/Resources/panels/MillFaceEdit.ui | 172 +++++++------ src/Mod/Path/PathScripts/PathContour.py | 164 +++++++----- src/Mod/Path/PathScripts/PathMillFace.py | 233 +++++++++++++----- 3 files changed, 371 insertions(+), 198 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui index c9bb25b85e..ec11749cf0 100644 --- a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui @@ -6,8 +6,8 @@ 0 0 - 323 - 482 + 518 + 754 @@ -33,8 +33,8 @@ 0 0 - 305 - 277 + 500 + 282 @@ -111,8 +111,8 @@ 0 0 - 305 - 124 + 500 + 241 @@ -192,8 +192,8 @@ 0 0 - 305 - 109 + 500 + 241 @@ -241,9 +241,9 @@ 0 - 0 - 289 - 297 + -34 + 486 + 361 @@ -263,20 +263,6 @@ QFrame::Raised - - - - - - - - - - - Use ZigZag - - - @@ -284,7 +270,14 @@ - + + + + + + + + ZigZag Angle @@ -294,54 +287,6 @@ - - - - - - - Cut Mode - - - - - - - Boundary Shape - - - - - - - - Perimeter - - - - - Boundbox - - - - - - - - - Climb - - - - - Conventional - - - - - - - @@ -410,6 +355,85 @@ + + + + + + + Cut Mode + + + + + + + + Climb + + + + + Conventional + + + + + + + + Boundary Shape + + + + + + + + Perimeter + + + + + Boundbox + + + + + + + + + ZigZag + + + + + Offset + + + + + Spiral + + + + + ZigZagOffset + + + + + + + + Pattern + + + + + + diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py index e6000e25f9..99cef5a55c 100644 --- a/src/Mod/Path/PathScripts/PathContour.py +++ b/src/Mod/Path/PathScripts/PathContour.py @@ -31,6 +31,7 @@ from PathScripts.PathUtils import depth_params from PySide import QtCore import TechDraw import ArchPanel +import Part LOG_MODULE = 'PathContour' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) @@ -131,64 +132,99 @@ class ObjectContour: obj.ClearanceHeight = 10.0 obj.SafeHeight = 8.0 - def _buildPathLibarea(self, obj, edgelist): - import PathScripts.PathKurveUtils as PathKurveUtils - PathLog.track() - # import math - # import area - output = "" - if obj.Comment != "": - output += '(' + str(obj.Comment)+')\n' + def _buildPathArea(self, obj, edgelist, start = None): - if obj.StartPoint and obj.UseStartPoint: - startpoint = obj.StartPoint - else: - startpoint = None + c = Part.Wire(edgelist) + f = Part.makeFace([c], 'Part::FaceMakerSimple') - if obj.EndPoint and obj.UseEndPoint: - endpoint = obj.EndPoint - else: - endpoint = None - - 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) - - 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 - - if obj.UseComp is False: - obj.Side = 'On' - else: - if obj.Direction == 'CW': - obj.Side = 'Left' - else: - obj.Side = 'Right' + profile = Path.Area(Offset=self.radius,SectionCount=1) + profile.setPlane(Part.makeCircle(10)) + profile.add(f) + lshapes = [profile.getShape()] 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) + print (depthparams.get_depths()) + for l in depthparams.get_depths(): + c = lshapes[0].copy() + c.Placement.Base.z = l + lshapes.append(c) - output += PathKurveUtils.retrieve_gcode() - return output + if start is None: + pp = Path.fromShapes(lshapes) + else: + pp = Path.fromShapes(lshapes, start) + return pp + + + # def _buildPathLibarea(self, obj, edgelist): + # import PathScripts.PathKurveUtils as PathKurveUtils + # PathLog.track() + # # import math + # # import area + # output = "" + # if obj.Comment != "": + # output += '(' + str(obj.Comment)+')\n' + + # if obj.StartPoint and obj.UseStartPoint: + # startpoint = obj.StartPoint + # else: + # startpoint = None + + # if obj.EndPoint and obj.UseEndPoint: + # endpoint = obj.EndPoint + # else: + # endpoint = None + + # 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) + + # 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 # obj.FinalDepth.Value, None) + + # PathKurveUtils.profile2( + + # if obj.UseComp is False: + # obj.Side = 'On' + # else: + # if obj.Direction == 'CW': + # obj.Side = 'Left' + # else: + # obj.Side = 'Right' + + # 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 def execute(self, obj): PathLog.track() import Part # math #DraftGeomUtils - output = "" + commandlist = [] + #output = "" toolLoad = obj.ToolController @@ -204,16 +240,20 @@ class ObjectContour: if not tool or tool.Diameter == 0: FreeCAD.Console.PrintError("No Tool found or diameter is zero. We need a tool to build a Path.") return - #self.radius = 0.25 else: self.radius = tool.Diameter/2 - output += "(" + obj.Label + ")" + #output += "(" + obj.Label + ")" + commandlist.append(Path.Command("(" + obj.Label + ")")) + if not obj.UseComp: - output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")" + #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)" + #output += "(Uncompensated Tool Path)" + commandlist.append(Path.Command("(Uncompensated Tool Path)")) parentJob = PathUtils.findParentJob(obj) if parentJob is None: @@ -234,21 +274,29 @@ class ObjectContour: edgelist = Part.__sortEdges__(edgelist) PathLog.debug("Processing panel perimeter. edges found: {}".format(len(edgelist))) # subobj.Proxy.execute(subobj) try: - output += self._buildPathLibarea(obj, edgelist) - except: + #output += self._buildPathLibarea(obj, edgelist) + commandlist.extend(self._buildPathArea(obj, edgelist).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: - contourwire = TechDraw.findShapeOutline(baseobject.Shape, 1, Vector(0, 0, 1)) + fixbase = baseobject.Shape.copy() + fixbase.fix(0.00001,0.00001,0.00001) + contourwire = TechDraw.findShapeOutline(fixbase, 1, Vector(0, 0, 1)) edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) try: - output += self._buildPathLibarea(obj, edgelist) - except: + commandlist.extend(self._buildPathArea(obj, edgelist).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) + # for c in contourcommands.Commands: + # path.addCommands(c) + obj.Path = path if obj.ViewObject: obj.ViewObject.Visibility = True diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index 7487b1b46a..9f2daed93b 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -88,6 +88,8 @@ class ObjectFace: obj.addProperty("App::PropertyFloat", "ZigZagAngle", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Angle of the zigzag pattern")) obj.addProperty("App::PropertyEnumeration", "BoundaryShape", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Shape to use for calculating Boundary")) obj.BoundaryShape = ['Perimeter', 'Boundbox'] + obj.addProperty("App::PropertyEnumeration", "OffsetPattern", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "clearing pattern to use")) + obj.OffsetPattern = ['Offset', 'ZigZag', 'Spiral', 'ZigZagOffset'] # Start Point Properties obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The start point of this path")) @@ -159,60 +161,119 @@ class ObjectFace: return self.getStock(o) return None - def buildpathlibarea(self, obj, a): - """Build the face path using libarea algorithm""" - - import PathScripts.PathAreaUtils as PathAreaUtils + def _buildPathArea(self, obj, edgelist): + """build the face path using PathArea""" from PathScripts.PathUtils import depth_params PathLog.track() - for p in a.getCurves(): - PathLog.debug(p.text()) + c = Part.Wire(edgelist) + f = Part.makeFace([c], 'Part::FaceMakerSimple') - FreeCAD.Console.PrintMessage(translate("PathMillFace", "Generating toolpath with libarea offsets.\n")) + boundary = Path.Area(PocketMode=4,SectionCount=1) + boundary.setPlane(Part.makeCircle(10)) + boundary.add(f) + #toolDiameter = 1.0 + + #use_zig_zag = obj.UseZigZag + #keep_tool_down = obj.KeepToolDown + #zig_unidirectional = obj.ZigUnidirectional + #start_point = None + #cut_mode = obj.CutMode + stepover = (self.radius * 2) * (float(obj.StepOver)/100) + + boundary.setParams(ZigAngle=obj.ZigZagAngle, + FromCenter=(obj.StartAt == "Center"), + PocketStepOver = stepover, #(self.radius * 2) * (float(obj.StepOver)/100), + PocketExtraOffset=obj.PassExtension.Value, + PocketMode=4) 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, + z_finish_step = obj.FinishDepth.Value, + final_depth = obj.FinalDepth.Value, + user_depths = None) - extraoffset = - obj.PassExtension.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 + print (depthparams.get_depths()) + lshapes = [boundary.getShape()] - PathAreaUtils.flush_nc() - PathAreaUtils.output('mem') - PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed) - if obj.UseStartPoint: - start_point = (obj.StartPoint.x, obj.StartPoint.y) + # depthparams = depth_params( + # obj.ClearanceHeight.Value, + # obj.SafeHeight.Value, + # obj.StartDepth.Value, + # obj.StepDown, + # obj.FinishDepth.Value, + # obj.FinalDepth.Value) - 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() + for l in depthparams.get_depths(): + c = lshapes[0].copy() + c.Placement.Base.z = l + lshapes.append(c) + + #if start is None: + pp = Path.fromShapes(lshapes) + #else: + # pp = Path.fromShapes(lshapes, start) + return pp + + # def buildpathlibarea(self, obj, a): + # """Build the face path using libarea algorithm""" + + # import PathScripts.PathAreaUtils as PathAreaUtils + # from PathScripts.PathUtils import depth_params + + # PathLog.track() + # for p in a.getCurves(): + # PathLog.debug(p.text()) + + # FreeCAD.Console.PrintMessage(translate("PathMillFace", "Generating toolpath with libarea offsets.\n")) + + # depthparams = depth_params( + # obj.ClearanceHeight.Value, + # obj.SafeHeight.Value, + # obj.StartDepth.Value, + # obj.StepDown, + # obj.FinishDepth.Value, + # obj.FinalDepth.Value) + + # extraoffset = - obj.PassExtension.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) + + # 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 execute(self, obj): PathLog.track() - output = "" + commandlist = [] + + #output = "" toolLoad = obj.ToolController @@ -233,8 +294,12 @@ class ObjectFace: self.radius = tool.Diameter/2 # Build preliminary comments - output = "" - output += "(" + obj.Label + ")" + # output = "" + # output += "(" + obj.Label + ")" + + + commandlist.append(Path.Command("(" + obj.Label + ")")) + # Facing is done either against base objects if obj.Base: @@ -282,20 +347,44 @@ class ObjectFace: edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) - # use libarea to build the pattern - a = area.Area() - c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW') - PathLog.debug(c.text()) - a.append(c) - a.Reorder() - output += self.buildpathlibarea(obj, a) - path = Path.Path(output) - if len(path.Commands) == 0: - FreeCAD.Console.PrintMessage(translate("PathMillFace", "The selected settings did not produce a valid path.\n")) - obj.Path = path - obj.ViewObject.Visibility = True + # # use libarea to build the pattern + # a = area.Area() + # c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW') + # PathLog.debug(c.text()) + # a.append(c) + # a.Reorder() + # output += self.buildpathlibarea(obj, a) + + + try: + commandlist.extend(self._buildPathArea(obj, edgelist).Commands) + except Exception as e: + print(e) + FreeCAD.Console.PrintWarning(translate("PathMillFace", "The selected settings did not produce a valid path.\n")) + + #FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") + + if obj.Active: + path = Path.Path(commandlist) + obj.Path = path + if obj.ViewObject: + obj.ViewObject.Visibility = True + else: + path = Path.Path("(inactive operation)") + obj.Path = path + obj.ViewObject.Visibility = False + + + + +# path = Path.Path(output) +# if len(path.Commands) == 0: +# FreeCAD.Console.PrintMessage(translate("PathMillFace", "The selected settings did not produce a valid path.\n")) + +# obj.Path = path +# obj.ViewObject.Visibility = True class _CommandSetFaceStartPoint: @@ -375,8 +464,8 @@ class CommandPathMillFace: FreeCADGui.doCommand('obj.StepDown = 1.0') FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop + 1)) FreeCADGui.doCommand('obj.FinalDepth =' + str(ztop)) - FreeCADGui.doCommand('obj.ZigZagAngle = 0.0') - FreeCADGui.doCommand('obj.UseZigZag = True') + FreeCADGui.doCommand('obj.ZigZagAngle = 45.0') + #FreeCADGui.doCommand('obj.UseZigZag = True') FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)') FreeCADGui.doCommand('obj.ToolController = PathScripts.PathUtils.findToolController(obj)') snippet = ''' @@ -436,16 +525,19 @@ class TaskPanel: self.obj.PassExtension = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value 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, "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"): self.obj.StepOver = self.form.stepOverPercent.value() if hasattr(self.obj, "BoundaryShape"): self.obj.BoundaryShape = str(self.form.boundaryShape.currentText()) + if hasattr(self.obj, "OffsetPattern"): + self.obj.OffsetPattern = str(self.form.offsetpattern.currentText()) + if hasattr(self.obj, "ToolController"): tc = PathUtils.findToolController(self.obj, self.form.uiToolController.currentText()) self.obj.ToolController = tc @@ -460,8 +552,8 @@ class TaskPanel: self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value, FreeCAD.Units.Length).UserString) self.form.stepOverPercent.setValue(self.obj.StepOver) - self.form.useZigZag.setChecked(self.obj.UseZigZag) - self.form.zigZagUnidirectional.setChecked(self.obj.ZigUnidirectional) + #self.form.useZigZag.setChecked(self.obj.UseZigZag) + #self.form.zigZagUnidirectional.setChecked(self.obj.ZigUnidirectional) self.form.zigZagAngle.setValue(FreeCAD.Units.Quantity(self.obj.ZigZagAngle, FreeCAD.Units.Angle)) self.form.extraOffset.setValue(self.obj.PassExtension.Value) @@ -480,6 +572,14 @@ class TaskPanel: self.form.boundaryShape.setCurrentIndex(index) self.form.boundaryShape.blockSignals(False) + + index = self.form.offsetpattern.findText( + self.obj.OffsetPattern, QtCore.Qt.MatchFixedString) + if index >= 0: + self.form.offsetpattern.blockSignals(True) + self.form.offsetpattern.setCurrentIndex(index) + self.form.offsetpattern.blockSignals(False) + for i in self.obj.Base: for sub in i[1]: self.form.baseList.addItem(i[0].Name + "." + sub) @@ -616,8 +716,9 @@ class TaskPanel: self.form.extraOffset.editingFinished.connect(self.getFields) self.form.boundaryShape.currentIndexChanged.connect(self.getFields) self.form.stepOverPercent.editingFinished.connect(self.getFields) - self.form.useZigZag.clicked.connect(self.getFields) - self.form.zigZagUnidirectional.clicked.connect(self.getFields) + self.form.offsetpattern.currentIndexChanged.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.form.uiToolController.currentIndexChanged.connect(self.getFields) From ac9a05368b724d3c28ab3f829a82b247a5e3b062 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Sun, 23 Apr 2017 10:05:42 -0500 Subject: [PATCH 02/20] PATH: Smoothie post for laser includes spindle speed (power) on every line --- src/Mod/Path/PathScripts/smoothie_post.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/smoothie_post.py b/src/Mod/Path/PathScripts/smoothie_post.py index bd691f0270..f07d151762 100644 --- a/src/Mod/Path/PathScripts/smoothie_post.py +++ b/src/Mod/Path/PathScripts/smoothie_post.py @@ -56,6 +56,8 @@ OUTPUT_LINE_NUMBERS = False IP_ADDR = None VERBOSE = False +SPINDLE_SPEED = 0.0 + if FreeCAD.GuiUp: SHOW_EDITOR = True else: @@ -298,6 +300,7 @@ def linenumber(): def parse(pathobj): out = "" lastcommand = None + global SPINDLE_SPEED # params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control # the order of parameters @@ -339,9 +342,14 @@ def parse(pathobj): param + format(float(speed.getValueAs(UNIT_FORMAT)), '.2f') ) elif param == 'T': outstring.append(param + str(c.Parameters['T'])) + elif param == 'S': + outstring.append(param + str(c.Parameters['S'])) + SPINDLE_SPEED = c.Parameters['S'] else: outstring.append( param + format(c.Parameters[param], '.4f')) + if command in ['G1', 'G01', 'G2', 'G02', 'G3', 'G03']: + outstring.append('S' + str(SPINDLE_SPEED)) # store the latest command lastcommand = command @@ -366,7 +374,7 @@ def parse(pathobj): # append the line to the final output for w in outstring: - out += w + COMMAND_SPACE + out += w + COMMAND_SPACE out = out.strip() + "\n" return out From c7acb2db3d76a2f7aff12186b37b2ece46e14e2a Mon Sep 17 00:00:00 2001 From: sliptonic Date: Mon, 24 Apr 2017 09:07:27 -0500 Subject: [PATCH 03/20] Path: use verbose mode for fromShapes() to get relative arc centers --- src/Mod/Path/PathScripts/PathContour.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py index 99cef5a55c..863942c4bb 100644 --- a/src/Mod/Path/PathScripts/PathContour.py +++ b/src/Mod/Path/PathScripts/PathContour.py @@ -158,9 +158,9 @@ class ObjectContour: lshapes.append(c) if start is None: - pp = Path.fromShapes(lshapes) + pp = Path.fromShapes(lshapes, verbose=False) else: - pp = Path.fromShapes(lshapes, start) + pp = Path.fromShapes(lshapes, start, verbose=False) return pp From 27b097c570aa8599d0ef1bce501f0022bbc47052 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Mon, 1 May 2017 23:56:36 -0500 Subject: [PATCH 04/20] PATH: conversion of PathContour to PathArea nearly complete Path: improve contour to use makeSections --- .../Path/Gui/Resources/panels/ContourEdit.ui | 40 +--- src/Mod/Path/InitGui.py | 2 +- src/Mod/Path/PathScripts/PathContour.py | 210 +++++------------- src/Mod/Path/PathScripts/PathUtils.py | 17 ++ 4 files changed, 87 insertions(+), 182 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui b/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui index 5e18b18dec..2ce88aaeee 100644 --- a/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui @@ -30,8 +30,8 @@ 0 0 - 334 - 292 + 108 + 92 @@ -82,7 +82,7 @@ - + 0.000000000000000 @@ -97,8 +97,8 @@ 0 0 - 334 - 292 + 141 + 65 @@ -147,8 +147,8 @@ 0 0 - 334 - 292 + 336 + 303 @@ -212,13 +212,6 @@ - - - - Use End Point - - - @@ -238,19 +231,9 @@ - - - - 0.000000000000000 - - - mm - - - - + 0.000000000000000 @@ -258,13 +241,6 @@ - - - - Roll Radius - - - diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index d375ecff65..f60387bb5c 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -140,7 +140,7 @@ class PathWorkbench (Workbench): if "Profile" or "Contour" in FreeCADGui.Selection.getSelection()[0].Name: #self.appendContextMenu("", ["Add_Tag"]) self.appendContextMenu("", ["Set_StartPoint"]) - self.appendContextMenu("", ["Set_EndPoint"]) + #self.appendContextMenu("", ["Set_EndPoint"]) if "Remote" in FreeCADGui.Selection.getSelection()[0].Name: self.appendContextMenu("", ["Refresh_Path"]) diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py index 863942c4bb..6b7b78790b 100644 --- a/src/Mod/Path/PathScripts/PathContour.py +++ b/src/Mod/Path/PathScripts/PathContour.py @@ -23,19 +23,19 @@ # *************************************************************************** import FreeCAD -from FreeCAD import Vector +#from FreeCAD import Vector import Path import PathScripts.PathLog as PathLog from PathScripts import PathUtils from PathScripts.PathUtils import depth_params from PySide import QtCore -import TechDraw +#import TechDraw import ArchPanel import Part LOG_MODULE = 'PathContour' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) -#PathLog.trackModule('PathContour') +PathLog.trackModule('PathContour') if FreeCAD.GuiUp: import FreeCADGui @@ -80,14 +80,6 @@ class ObjectContour: # 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")) # Contour Properties obj.addProperty("App::PropertyEnumeration", "Direction", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) @@ -97,7 +89,6 @@ class ObjectContour: obj.Side = ['Left', 'Right', 'On'] # side of profile that cutter is on in relation to direction of profile obj.setEditorMode('Side', 2) # hide - obj.addProperty("App::PropertyDistance", "RollRadius", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Radius at start and end")) obj.addProperty("App::PropertyDistance", "OffsetExtra", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra value to stay away from final Contour- good for roughing toolpath")) obj.Proxy = self @@ -132,100 +123,62 @@ class ObjectContour: obj.ClearanceHeight = 10.0 obj.SafeHeight = 8.0 - def _buildPathArea(self, obj, edgelist, start = None): + def _buildPathArea(self, obj, baseobject, start=None): + PathLog.track() - c = Part.Wire(edgelist) - f = Part.makeFace([c], 'Part::FaceMakerSimple') - - profile = Path.Area(Offset=self.radius,SectionCount=1) + profile = Path.Area() profile.setPlane(Part.makeCircle(10)) - profile.add(f) - lshapes = [profile.getShape()] + profile.add(baseobject) + + profileparams = {'Fill': 0, + 'Coplanar' : 0} + + if obj.UseComp is False: + profileparams['Offset'] = 0.0 + else: + profileparams['Offset'] = self.radius+obj.OffsetExtra.Value + + profile.setParams(**profileparams) + PathLog.debug("About to profile with params: {}".format(profileparams)) 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) + 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) - print (depthparams.get_depths()) - for l in depthparams.get_depths(): - c = lshapes[0].copy() - c.Placement.Base.z = l - lshapes.append(c) + sections = profile.makeSections(mode=0, project=True, heights=depthparams.get_depths()) + shapelist = [sec.getShape() for sec in sections] - if start is None: - pp = Path.fromShapes(lshapes, verbose=False) + params = {'shapes': shapelist, + 'feedrate': self.horizFeed, + 'feedrate_v': self.vertFeed, + 'verbose': True, + 'resume_height': obj.StepDown.Value, + 'retraction': obj.ClearanceHeight.Value} + + if obj.Direction == 'CCW': + params['orientation'] = 1 else: - pp = Path.fromShapes(lshapes, start, verbose=False) + params['orientation'] = 0 + + if obj.UseStartPoint is True and obj.StartPoint is not None: + params['start'] = obj.StartPoint + + PathLog.debug("Generating Path with params: {}".format(params)) + pp = Path.fromShapes(**params) + PathLog.debug(pp) + return pp - - # def _buildPathLibarea(self, obj, edgelist): - # import PathScripts.PathKurveUtils as PathKurveUtils - # PathLog.track() - # # import math - # # import area - # output = "" - # if obj.Comment != "": - # output += '(' + str(obj.Comment)+')\n' - - # if obj.StartPoint and obj.UseStartPoint: - # startpoint = obj.StartPoint - # else: - # startpoint = None - - # if obj.EndPoint and obj.UseEndPoint: - # endpoint = obj.EndPoint - # else: - # endpoint = None - - # 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) - - # 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 # obj.FinalDepth.Value, None) - - # PathKurveUtils.profile2( - - # if obj.UseComp is False: - # obj.Side = 'On' - # else: - # if obj.Direction == 'CW': - # obj.Side = 'Left' - # else: - # obj.Side = 'Right' - - # 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 - def execute(self, obj): PathLog.track() - import Part # math #DraftGeomUtils - commandlist = [] - #output = "" + import Part + commandlist = [] toolLoad = obj.ToolController if toolLoad is None or toolLoad.ToolNumber == 0: @@ -236,23 +189,18 @@ class ObjectContour: self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value - tool = toolLoad.Proxy.getTool(toolLoad) #PathUtils.getTool(obj, toolLoad.ToolNumber) + tool = toolLoad.Proxy.getTool(toolLoad) if not tool or tool.Diameter == 0: FreeCAD.Console.PrintError("No Tool found or diameter is zero. We need a tool to build a Path.") return - #self.radius = 0.25 else: self.radius = tool.Diameter/2 - #output += "(" + obj.Label + ")" commandlist.append(Path.Command("(" + obj.Label + ")")) if not 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)")) parentJob = PathUtils.findParentJob(obj) @@ -269,34 +217,27 @@ class ObjectContour: if isinstance(subobj.Proxy, ArchPanel.PanelCut): 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))) # subobj.Proxy.execute(subobj) + f = Part.makeFace([shape], 'Part::FaceMakerSimple') + thickness = baseobject.Group[0].Source.Thickness + contourshape = f.extrude(FreeCAD.Vector(0,0, thickness)) try: - #output += self._buildPathLibarea(obj, edgelist) - commandlist.extend(self._buildPathArea(obj, edgelist).Commands) - except Exception as e: - print(e) + commandlist.extend(self._buildPathArea(obj, contourshape).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: - fixbase = baseobject.Shape.copy() - fixbase.fix(0.00001,0.00001,0.00001) - contourwire = TechDraw.findShapeOutline(fixbase, 1, Vector(0, 0, 1)) - - edgelist = contourwire.Edges - edgelist = Part.__sortEdges__(edgelist) + #fixbase = baseobject.Shape.copy() + #fixbase.fix(0.00001, 0.00001, 0.00001) + env = PathUtils.getEnvelope(baseobject.Shape, obj.StartDepth) try: - commandlist.extend(self._buildPathArea(obj, edgelist).Commands) - except Exception as e: - print(e) + commandlist.extend(self._buildPathArea(obj, env).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(commandlist) - # for c in contourcommands.Commands: - # path.addCommands(c) - obj.Path = path if obj.ViewObject: obj.ViewObject.Visibility = True @@ -348,26 +289,6 @@ class _CommandSetStartPoint: obj.StartPoint.y = point.y def Activated(self): - - FreeCADGui.Snapper.getPoint(callback=self.setpoint) - - -class _CommandSetEndPoint: - def GetResources(self): - return {'Pixmap': 'Path-EndPoint', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Contour", "Pick End Point"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Contour", "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) @@ -450,14 +371,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, "Direction"): self.obj.Direction = str(self.form.direction.currentText()) if hasattr(self.obj, "ToolController"): @@ -473,10 +390,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) index = self.form.direction.findText( self.obj.Direction, QtCore.Qt.MatchFixedString) @@ -529,9 +444,7 @@ class TaskPanel: self.form.uiToolController.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() @@ -554,6 +467,5 @@ if FreeCAD.GuiUp: # register the FreeCAD command FreeCADGui.addCommand('Path_Contour', CommandPathContour()) FreeCADGui.addCommand('Set_StartPoint', _CommandSetStartPoint()) - FreeCADGui.addCommand('Set_EndPoint', _CommandSetEndPoint()) FreeCAD.Console.PrintLog("Loading PathContour... done\n") diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index fd6aab8be4..10f4d05d9c 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -33,6 +33,7 @@ import numpy import PathLog #from math import pi from FreeCAD import Vector +import Path LOG_MODULE = 'PathUtils' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) @@ -193,6 +194,22 @@ def filterArcs(arcEdge): pass return splitlist +def getEnvelope(partshape, stockheight=None): + ''' + getEnvelop(partshape, stockheight=None) + returns a shape corresponding to the partshape silhouette extruded to height. + if stockheight is given, the returned shape is extruded to that height otherwise the returned shape + is the height of the original shape boundbox + partshape = solid object + stockheight = float + ''' + area = Path.Area(Fill=1, Coplanar=0).add(partshape) + area.setPlane(Part.makeCircle(10)) + sec = area.makeSections(heights=[1.0], project=True)[0].getShape(rebuild=True) + if stockheight is not None: + return sec.extrude(FreeCAD.Vector(0, 0, stockheight)) + else: + return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZMax)) def reverseEdge(e): if geomType(e) == "Circle": From 67efac641f58a3c379b324f61b835fa78bae2f84 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Mon, 8 May 2017 16:45:02 -0500 Subject: [PATCH 05/20] Path: engrave will do arrays --- src/Mod/Path/PathScripts/PathEngrave.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py index 45605e2605..d825b2d151 100644 --- a/src/Mod/Path/PathScripts/PathEngrave.py +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -118,7 +118,8 @@ class ObjectPathEngrave: return try: if baseobject.isDerivedFrom('Sketcher::SketchObject') or \ - baseobject.isDerivedFrom('Part::Part2DObject'): + baseobject.isDerivedFrom('Part::Part2DObject') or \ + hasattr(baseobject, 'ArrayType'): output += "G0 Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n" From 59fd1d0c810dae51b12733c473db0f67274690a6 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Mon, 8 May 2017 17:42:46 -0500 Subject: [PATCH 06/20] Path: Engrave was using safeheight and clearance incorrectly. --- src/Mod/Path/PathScripts/PathEngrave.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py index d825b2d151..982992aad6 100644 --- a/src/Mod/Path/PathScripts/PathEngrave.py +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -182,7 +182,8 @@ class ObjectPathEngrave: if not last: # we set the first move to our first point last = edge.Vertexes[0].Point - output += "G0" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.SafeHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) # Rapid sto starting position + output += "G0" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) # Rapid to starting position + output += "G0" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.SafeHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) # Rapid to safe height output += "G1" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.FinalDepth.Value) + "F " + PathUtils.fmt(self.vertFeed) + "\n" # Vertical feed to depth if isinstance(edge.Curve, Part.Circle): point = edge.Vertexes[-1].Point @@ -209,7 +210,7 @@ class ObjectPathEngrave: output += " F " + PathUtils.fmt(self.horizFeed) output += "\n" last = point - output += "G0 Z " + PathUtils.fmt(obj.SafeHeight.Value) + output += "G0 Z " + PathUtils.fmt(obj.ClearanceHeight.Value) return output From 0d9b2fc3e739a057985bff0c99c01c40f55c2589 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Tue, 9 May 2017 14:01:46 -0500 Subject: [PATCH 07/20] Path: TD version of getEnvelope for testing. --- src/Mod/Path/PathScripts/PathUtils.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index 10f4d05d9c..7bd3ad0b3c 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -211,6 +211,23 @@ def getEnvelope(partshape, stockheight=None): else: return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZMax)) +def getEnvelopeTD(partshape, stockheight=None): + ''' + getEnvelopTD(partshape, stockheight=None) + Uses the TechDraw findShapeOutline to return a shape corresponding to the + partshape silhouette extruded to height. if stockheight is given, the + returned shape is extruded to that height otherwise the returned shape + is the height of the original shape boundbox + partshape = solid object + stockheight = float + ''' + import TechDraw + sec = Part.Face(TechDraw.findShapeOutline(partshape, 1 , FreeCAD.Vector(0,0,1))) + if stockheight is not None: + return sec.extrude(FreeCAD.Vector(0, 0, stockheight)) + else: + return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZMax)) + def reverseEdge(e): if geomType(e) == "Circle": arcstpt = e.valueAt(e.FirstParameter) From 7bccfff0de4bfbbc1856cf84cbdc33027c9f82b0 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Tue, 9 May 2017 16:51:24 -0500 Subject: [PATCH 08/20] Path: More work on PathMillFace to remove old heeks dependency --- src/Mod/Path/PathScripts/PathMillFace.py | 173 +++++------------------ 1 file changed, 35 insertions(+), 138 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index 9f2daed93b..e7c8c97c73 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -28,9 +28,6 @@ import Path from PySide import QtCore, QtGui from PathScripts import PathUtils import Part -import PathScripts.PathKurveUtils -import area -import TechDraw from FreeCAD import Vector import PathScripts.PathLog as PathLog @@ -161,31 +158,25 @@ class ObjectFace: return self.getStock(o) return None - def _buildPathArea(self, obj, edgelist): + def _buildPathArea(self, obj, baseobject): """build the face path using PathArea""" from PathScripts.PathUtils import depth_params PathLog.track() - c = Part.Wire(edgelist) - f = Part.makeFace([c], 'Part::FaceMakerSimple') - - boundary = Path.Area(PocketMode=4,SectionCount=1) + boundary = Path.Area() boundary.setPlane(Part.makeCircle(10)) - boundary.add(f) - #toolDiameter = 1.0 + boundary.add(baseobject) - #use_zig_zag = obj.UseZigZag - #keep_tool_down = obj.KeepToolDown - #zig_unidirectional = obj.ZigUnidirectional - #start_point = None - #cut_mode = obj.CutMode stepover = (self.radius * 2) * (float(obj.StepOver)/100) - boundary.setParams(ZigAngle=obj.ZigZagAngle, - FromCenter=(obj.StartAt == "Center"), - PocketStepOver = stepover, #(self.radius * 2) * (float(obj.StepOver)/100), - PocketExtraOffset=obj.PassExtension.Value, - PocketMode=4) + pocketparams = {'Fill': 0, + 'Coplanar' : 0, + 'PocketMode': 4, + 'SectionCount': -1, + 'Angle': obj.ZigZagAngle, + 'FromCenter': (obj.StartAt == "Center"), + 'PocketStepover': stepover, + 'PocketExtraOffset': obj.PassExtension.Value } depthparams = depth_params( clearance_height = obj.ClearanceHeight.Value, @@ -196,85 +187,33 @@ class ObjectFace: final_depth = obj.FinalDepth.Value, user_depths = None) - print (depthparams.get_depths()) - lshapes = [boundary.getShape()] + boundary.setParams(**pocketparams) + sections = boundary.makeSections(mode=0, project=False, heights=depthparams.get_depths()) + shapelist = [sec.getShape() for sec in sections] - # depthparams = depth_params( - # obj.ClearanceHeight.Value, - # obj.SafeHeight.Value, - # obj.StartDepth.Value, - # obj.StepDown, - # obj.FinishDepth.Value, - # obj.FinalDepth.Value) - for l in depthparams.get_depths(): - c = lshapes[0].copy() - c.Placement.Base.z = l - lshapes.append(c) + params = {'shapes': shapelist, + 'feedrate': self.horizFeed, + 'feedrate_v': self.vertFeed, + 'verbose': True, + 'resume_height': obj.StepDown, + 'retraction': obj.ClearanceHeight.Value} + + # if obj.Direction == 'CCW': + # params['orientation'] = 1 + # else: + # params['orientation'] = 0 + + + PathLog.debug("Generating Path with params: {}".format(params)) + pp = Path.fromShapes(**params) - #if start is None: - pp = Path.fromShapes(lshapes) - #else: - # pp = Path.fromShapes(lshapes, start) return pp - # def buildpathlibarea(self, obj, a): - # """Build the face path using libarea algorithm""" - - # import PathScripts.PathAreaUtils as PathAreaUtils - # from PathScripts.PathUtils import depth_params - - # PathLog.track() - # for p in a.getCurves(): - # PathLog.debug(p.text()) - - # FreeCAD.Console.PrintMessage(translate("PathMillFace", "Generating toolpath with libarea offsets.\n")) - - # depthparams = depth_params( - # obj.ClearanceHeight.Value, - # obj.SafeHeight.Value, - # obj.StartDepth.Value, - # obj.StepDown, - # obj.FinishDepth.Value, - # obj.FinalDepth.Value) - - # extraoffset = - obj.PassExtension.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) - - # 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 execute(self, obj): PathLog.track() commandlist = [] - #output = "" - toolLoad = obj.ToolController if toolLoad is None or toolLoad.ToolNumber == 0: @@ -293,14 +232,8 @@ class ObjectFace: else: self.radius = tool.Diameter/2 - # Build preliminary comments - # output = "" - # output += "(" + obj.Label + ")" - - commandlist.append(Path.Command("(" + obj.Label + ")")) - # Facing is done either against base objects if obj.Base: PathLog.debug("obj.Base: {}".format(obj.Base)) @@ -334,36 +267,16 @@ class ObjectFace: if obj.BoundaryShape == 'Boundbox': bb = planeshape.BoundBox bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, Vector(bb.XMin, bb.YMin, bb.ZMin), Vector(0, 0, 1)) - contourwire = TechDraw.findShapeOutline(bbperim, 1, Vector(0, 0, 1)) + env = PathUtils.getEnvelopeTD(bbperim, obj.StartDepth) else: - contourwire = TechDraw.findShapeOutline(planeshape, 1, Vector(0, 0, 1)) - - # pocket = Path.Area(PocketMode=4,SectionCount=-1,SectionMode=1,Stepdown=0.499) - # pocket.setParams(PocketExtraOffset = obj.PassExtension.Value, ToolRadius = self.radius) - # pocket.add(planeshape, op=1) - # #Part.show(contourwire) - # path = Path.fromShapes(pocket.getShape()) - - edgelist = contourwire.Edges - edgelist = Part.__sortEdges__(edgelist) - - - - # # use libarea to build the pattern - # a = area.Area() - # c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW') - # PathLog.debug(c.text()) - # a.append(c) - # a.Reorder() - # output += self.buildpathlibarea(obj, a) - + env = PathUtils.getEnvelopeTD(planeshape, obj.StartDepth) try: - commandlist.extend(self._buildPathArea(obj, edgelist).Commands) - except Exception as e: - print(e) + commandlist.extend(self._buildPathArea(obj, env).Commands) + except Exception as e: + print(e) FreeCAD.Console.PrintWarning(translate("PathMillFace", "The selected settings did not produce a valid path.\n")) - + #FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") if obj.Active: @@ -377,16 +290,6 @@ class ObjectFace: obj.ViewObject.Visibility = False - - -# path = Path.Path(output) -# if len(path.Commands) == 0: -# FreeCAD.Console.PrintMessage(translate("PathMillFace", "The selected settings did not produce a valid path.\n")) - -# obj.Path = path -# obj.ViewObject.Visibility = True - - class _CommandSetFaceStartPoint: def GetResources(self): return {'Pixmap': 'Path-StartPoint', @@ -525,10 +428,6 @@ class TaskPanel: self.obj.PassExtension = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value 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"): @@ -552,8 +451,6 @@ class TaskPanel: self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value, FreeCAD.Units.Length).UserString) self.form.stepOverPercent.setValue(self.obj.StepOver) - #self.form.useZigZag.setChecked(self.obj.UseZigZag) - #self.form.zigZagUnidirectional.setChecked(self.obj.ZigUnidirectional) self.form.zigZagAngle.setValue(FreeCAD.Units.Quantity(self.obj.ZigZagAngle, FreeCAD.Units.Angle)) self.form.extraOffset.setValue(self.obj.PassExtension.Value) From 29130de609c55e3870a3293e5c03e82b72ffc491 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Wed, 10 May 2017 13:44:31 -0500 Subject: [PATCH 09/20] Path: make cursor reflect busy Path: fix label buy in PathProfileEdges Path: cleanup --- src/Mod/Path/PathScripts/PathContour.py | 12 +-- src/Mod/Path/PathScripts/PathMillFace.py | 38 ++++------ src/Mod/Path/PathScripts/PathProfileEdges.py | 5 +- src/Mod/Path/PathScripts/PathUtils.py | 80 +++++++++++++++----- 4 files changed, 82 insertions(+), 53 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py index 6b7b78790b..fedef35959 100644 --- a/src/Mod/Path/PathScripts/PathContour.py +++ b/src/Mod/Path/PathScripts/PathContour.py @@ -23,15 +23,14 @@ # *************************************************************************** import FreeCAD -#from FreeCAD import Vector import Path import PathScripts.PathLog as PathLog from PathScripts import PathUtils from PathScripts.PathUtils import depth_params from PySide import QtCore -#import TechDraw import ArchPanel import Part +from PathScripts.PathUtils import waiting_effects LOG_MODULE = 'PathContour' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) @@ -123,15 +122,15 @@ class ObjectContour: obj.ClearanceHeight = 10.0 obj.SafeHeight = 8.0 + @waiting_effects def _buildPathArea(self, obj, baseobject, start=None): PathLog.track() - profile = Path.Area() profile.setPlane(Part.makeCircle(10)) profile.add(baseobject) profileparams = {'Fill': 0, - 'Coplanar' : 0} + 'Coplanar': 0} if obj.UseComp is False: profileparams['Offset'] = 0.0 @@ -219,17 +218,14 @@ class ObjectContour: for shape in shapes: f = Part.makeFace([shape], 'Part::FaceMakerSimple') thickness = baseobject.Group[0].Source.Thickness - contourshape = f.extrude(FreeCAD.Vector(0,0, thickness)) + contourshape = f.extrude(FreeCAD.Vector(0, 0, thickness)) try: commandlist.extend(self._buildPathArea(obj, contourshape).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: - #fixbase = baseobject.Shape.copy() - #fixbase.fix(0.00001, 0.00001, 0.00001) env = PathUtils.getEnvelope(baseobject.Shape, obj.StartDepth) - try: commandlist.extend(self._buildPathArea(obj, env).Commands) except Exception as e: diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index e7c8c97c73..5d33445400 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -30,6 +30,7 @@ from PathScripts import PathUtils import Part from FreeCAD import Vector import PathScripts.PathLog as PathLog +from PathScripts.PathUtils import waiting_effects LOG_MODULE = 'PathMillFace' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) @@ -158,6 +159,7 @@ class ObjectFace: return self.getStock(o) return None + @waiting_effects def _buildPathArea(self, obj, baseobject): """build the face path using PathArea""" from PathScripts.PathUtils import depth_params @@ -170,28 +172,27 @@ class ObjectFace: stepover = (self.radius * 2) * (float(obj.StepOver)/100) pocketparams = {'Fill': 0, - 'Coplanar' : 0, - 'PocketMode': 4, - 'SectionCount': -1, - 'Angle': obj.ZigZagAngle, - 'FromCenter': (obj.StartAt == "Center"), - 'PocketStepover': stepover, - 'PocketExtraOffset': obj.PassExtension.Value } + 'Coplanar': 0, + 'PocketMode': 4, + 'SectionCount': -1, + 'Angle': obj.ZigZagAngle, + 'FromCenter': (obj.StartAt == "Center"), + 'PocketStepover': stepover, + 'PocketExtraOffset': obj.PassExtension.Value} depthparams = depth_params( - clearance_height = obj.ClearanceHeight.Value, - rapid_safety_space = obj.SafeHeight.Value, - start_depth = obj.StartDepth.Value, - step_down = obj.StepDown, - z_finish_step = obj.FinishDepth.Value, - final_depth = obj.FinalDepth.Value, - user_depths = None) + clearance_height=obj.ClearanceHeight.Value, + rapid_safety_space=obj.SafeHeight.Value, + start_depth=obj.StartDepth.Value, + step_down=obj.StepDown, + z_finish_step=obj.FinishDepth.Value, + final_depth=obj.FinalDepth.Value, + user_depths=None) boundary.setParams(**pocketparams) sections = boundary.makeSections(mode=0, project=False, heights=depthparams.get_depths()) shapelist = [sec.getShape() for sec in sections] - params = {'shapes': shapelist, 'feedrate': self.horizFeed, 'feedrate_v': self.vertFeed, @@ -204,7 +205,6 @@ class ObjectFace: # else: # params['orientation'] = 0 - PathLog.debug("Generating Path with params: {}".format(params)) pp = Path.fromShapes(**params) @@ -277,8 +277,6 @@ class ObjectFace: print(e) FreeCAD.Console.PrintWarning(translate("PathMillFace", "The selected settings did not produce a valid path.\n")) - #FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") - if obj.Active: path = Path.Path(commandlist) obj.Path = path @@ -368,7 +366,6 @@ class CommandPathMillFace: FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop + 1)) FreeCADGui.doCommand('obj.FinalDepth =' + str(ztop)) FreeCADGui.doCommand('obj.ZigZagAngle = 45.0') - #FreeCADGui.doCommand('obj.UseZigZag = True') FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)') FreeCADGui.doCommand('obj.ToolController = PathScripts.PathUtils.findToolController(obj)') snippet = ''' @@ -469,7 +466,6 @@ class TaskPanel: self.form.boundaryShape.setCurrentIndex(index) self.form.boundaryShape.blockSignals(False) - index = self.form.offsetpattern.findText( self.obj.OffsetPattern, QtCore.Qt.MatchFixedString) if index >= 0: @@ -614,8 +610,6 @@ class TaskPanel: self.form.boundaryShape.currentIndexChanged.connect(self.getFields) self.form.stepOverPercent.editingFinished.connect(self.getFields) self.form.offsetpattern.currentIndexChanged.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.form.uiToolController.currentIndexChanged.connect(self.getFields) diff --git a/src/Mod/Path/PathScripts/PathProfileEdges.py b/src/Mod/Path/PathScripts/PathProfileEdges.py index f2ae43cd6b..4f797bb3c6 100644 --- a/src/Mod/Path/PathScripts/PathProfileEdges.py +++ b/src/Mod/Path/PathScripts/PathProfileEdges.py @@ -28,6 +28,7 @@ from PathScripts import PathUtils from PathScripts.PathUtils import depth_params from DraftGeomUtils import findWires import PathScripts.PathLog as PathLog +from PathScripts.PathUtils import waiting_effects """Path Profile from Edges Object and Command""" @@ -107,8 +108,7 @@ class ObjectProfile: return None def onChanged(self, obj, prop): - if prop == "UserLabel": - obj.Label = obj.UserLabel + " :" + obj.ToolDescription + pass def addprofilebase(self, obj, ss, sub=""): baselist = obj.Base @@ -144,6 +144,7 @@ class ObjectProfile: obj.Base = baselist self.execute(obj) + @waiting_effects def _buildPathLibarea(self, obj, edgelist): import PathScripts.PathKurveUtils as PathKurveUtils # import math diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index 7bd3ad0b3c..b8433fe4c5 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -31,13 +31,30 @@ import PathScripts from PathScripts import PathJob import numpy import PathLog -#from math import pi from FreeCAD import Vector import Path +from PySide import QtCore +from PySide import QtGui LOG_MODULE = 'PathUtils' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) -#PathLog.trackModule('PathUtils') +# PathLog.trackModule('PathUtils') + + +def waiting_effects(function): + def new_function(*args, **kwargs): + QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) + res = None + try: + res = function(*args, **kwargs) + except Exception as e: + raise e + print("Error {}".format(e.args[0])) + finally: + QtGui.QApplication.restoreOverrideCursor() + return res + return new_function + def cleanedges(splines, precision): '''cleanedges([splines],precision). Convert BSpline curves, Beziers, to arcs that can be used for cnc paths. @@ -74,8 +91,10 @@ def cleanedges(splines, precision): return edges + def curvetowire(obj, steps): '''adapted from DraftGeomUtils, because the discretize function changed a bit ''' + points = obj.copy().discretize(Distance=eval('steps')) p0 = points[0] edgelist = [] @@ -85,6 +104,7 @@ def curvetowire(obj, steps): p0 = p return edgelist + def isDrillable(obj, candidate, tooldiameter=None): PathLog.track('obj: {} candidate: {} tooldiameter {}'.format(obj, candidate, tooldiameter)) drillable = False @@ -99,13 +119,13 @@ def isDrillable(obj, candidate, tooldiameter=None): v1 = edge.Vertexes[1].Point if (v1.sub(v0).x == 0) and (v1.sub(v0).y == 0): # vector of top center - lsp = Vector(face.BoundBox.Center.x,face.BoundBox.Center.y, face.BoundBox.ZMax) + lsp = Vector(face.BoundBox.Center.x, face.BoundBox.Center.y, face.BoundBox.ZMax) # vector of bottom center - lep = Vector(face.BoundBox.Center.x,face.BoundBox.Center.y, face.BoundBox.ZMin) + lep = Vector(face.BoundBox.Center.x, face.BoundBox.Center.y, face.BoundBox.ZMin) if obj.isInside(lsp, 0, False) or obj.isInside(lep, 0, False): drillable = False # eliminate elliptical holes - elif not hasattr(face.Surface, "Radius"): #abs(face.BoundBox.XLength - face.BoundBox.YLength) > 0.05: + elif not hasattr(face.Surface, "Radius"): drillable = False else: if tooldiameter is not None: @@ -116,7 +136,7 @@ def isDrillable(obj, candidate, tooldiameter=None): for edge in candidate.Edges: if isinstance(edge.Curve, Part.Circle) and edge.isClosed(): PathLog.debug("candidate is a circle or ellipse") - if not hasattr(edge.Curve, "Radius"): #bbdiff > 0.05: + if not hasattr(edge.Curve, "Radius"): PathLog.debug("No radius. Ellipse.") drillable = False else: @@ -128,13 +148,16 @@ def isDrillable(obj, candidate, tooldiameter=None): PathLog.debug("candidate is drillable: {}".format(drillable)) return drillable + # fixme set at 4 decimal places for testing def fmt(val): return format(val, '.4f') + def segments(poly): ''' A sequence of (x,y) numeric coordinates pairs ''' return zip(poly, poly[1:] + [poly[0]]) + def loopdetect(obj, edge1, edge2): ''' Returns a loop wire that includes the two edges. @@ -149,15 +172,16 @@ def loopdetect(obj, edge1, edge2): for wire in obj.Shape.Wires: for e in wire.Edges: if e.hashCode() == edge1.hashCode(): - candidates.append((wire.hashCode(),wire)) + candidates.append((wire.hashCode(), wire)) if e.hashCode() == edge2.hashCode(): - candidates.append((wire.hashCode(),wire)) - loop = set([x for x in candidates if candidates.count(x) > 1]) #return the duplicate item + candidates.append((wire.hashCode(), wire)) + loop = set([x for x in candidates if candidates.count(x) > 1]) # return the duplicate item if len(loop) != 1: return None loopwire = next(x for x in loop)[1] return loopwire + def filterArcs(arcEdge): '''filterArcs(Edge) -used to split arcs that over 180 degrees. Returns list ''' s = arcEdge @@ -175,10 +199,10 @@ def filterArcs(arcEdge): arcstpt = s.valueAt(s.FirstParameter) arcmid = s.valueAt( (s.LastParameter - s.FirstParameter) * 0.5 + s.FirstParameter) - arcquad1 = s.valueAt((s.LastParameter - s.FirstParameter) - * 0.25 + s.FirstParameter) # future midpt for arc1 - arcquad2 = s.valueAt((s.LastParameter - s.FirstParameter) - * 0.75 + s.FirstParameter) # future midpt for arc2 + arcquad1 = s.valueAt((s.LastParameter - s.FirstParameter) * + 0.25 + s.FirstParameter) # future midpt for arc1 + arcquad2 = s.valueAt((s.LastParameter - s.FirstParameter) * + 0.75 + s.FirstParameter) # future midpt for arc2 arcendpt = s.valueAt(s.LastParameter) # reconstruct with 2 arcs arcseg1 = Part.ArcOfCircle(arcstpt, arcquad1, arcmid) @@ -194,6 +218,7 @@ def filterArcs(arcEdge): pass return splitlist + def getEnvelope(partshape, stockheight=None): ''' getEnvelop(partshape, stockheight=None) @@ -211,6 +236,7 @@ def getEnvelope(partshape, stockheight=None): else: return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZMax)) + def getEnvelopeTD(partshape, stockheight=None): ''' getEnvelopTD(partshape, stockheight=None) @@ -222,12 +248,13 @@ def getEnvelopeTD(partshape, stockheight=None): stockheight = float ''' import TechDraw - sec = Part.Face(TechDraw.findShapeOutline(partshape, 1 , FreeCAD.Vector(0,0,1))) + sec = Part.Face(TechDraw.findShapeOutline(partshape, 1, FreeCAD.Vector(0, 0, 1))) if stockheight is not None: return sec.extrude(FreeCAD.Vector(0, 0, stockheight)) else: return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZMax)) + def reverseEdge(e): if geomType(e) == "Circle": arcstpt = e.valueAt(e.FirstParameter) @@ -242,6 +269,7 @@ def reverseEdge(e): return newedge + def changeTool(obj, job): tlnum = 0 for p in job.Group: @@ -257,6 +285,7 @@ def changeTool(obj, job): if g == obj: return tlnum + def getToolControllers(obj): '''returns all the tool controllers''' controllers = [] @@ -272,6 +301,7 @@ def getToolControllers(obj): controllers.append(g) return controllers + def findToolController(obj, name=None): '''returns a tool controller with a given name. If no name is specified, returns the first controller. @@ -279,7 +309,7 @@ def findToolController(obj, name=None): PathLog.track('name: {}'.format(name)) c = None - #First check if a user has selected a tool controller in the tree. Return the first one and remove all from selection + # First check if a user has selected a tool controller in the tree. Return the first one and remove all from selection for sel in FreeCADGui.Selection.getSelectionEx(): if hasattr(sel.Object, 'Proxy'): if isinstance(sel.Object.Proxy, PathScripts.PathLoadTool.LoadTool): @@ -294,16 +324,16 @@ def findToolController(obj, name=None): if len(controllers) == 0: return None - #If there's only one in the job, use it. + # If there's only one in the job, use it. if len(controllers) == 1: if name is None or name == controllers[0].Label: tc = controllers[0] else: tc = None - elif name is not None: #More than one, make the user choose. + elif name is not None: # More than one, make the user choose. tc = [i for i in controllers if i.Label == name][0] else: - #form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgTCChooser.ui") + # form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgTCChooser.ui") form = FreeCADGui.PySideUic.loadUi(":/panels/DlgTCChooser.ui") mylist = [i.Label for i in controllers] form.uiToolController.addItems(mylist) @@ -314,6 +344,7 @@ def findToolController(obj, name=None): tc = [i for i in controllers if i.Label == form.uiToolController.currentText()][0] return tc + def findParentJob(obj): '''retrieves a parent job object for an operation or other Path object''' PathLog.track() @@ -326,7 +357,8 @@ def findParentJob(obj): return grandParent return None -def GetJobs(jobname = None): + +def GetJobs(jobname=None): '''returns all jobs in the current document. If name is given, returns that job''' PathLog.track() jobs = [] @@ -340,7 +372,8 @@ def GetJobs(jobname = None): jobs.append(o) return jobs -def addToJob(obj, jobname = None): + +def addToJob(obj, jobname=None): '''adds a path object to a job obj = obj jobname = None''' @@ -360,7 +393,7 @@ def addToJob(obj, jobname = None): elif len(jobs) == 1: job = jobs[0] else: - #form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui") + # form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui") form = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobChooser.ui") mylist = [i.Name for i in jobs] form.cboProject.addItems(mylist) @@ -376,6 +409,7 @@ def addToJob(obj, jobname = None): job.Group = g return job + def rapid(x=None, y=None, z=None): """ Returns gcode string to perform a rapid move.""" retstr = "G00" @@ -390,6 +424,7 @@ def rapid(x=None, y=None, z=None): return "" return retstr + "\n" + def feed(x=None, y=None, z=None, horizFeed=0, vertFeed=0): """ Return gcode string to perform a linear feed.""" global feedxy @@ -410,6 +445,7 @@ def feed(x=None, y=None, z=None, horizFeed=0, vertFeed=0): return "" return retstr + "\n" + def arc(cx, cy, sx, sy, ex, ey, horizFeed=0, ez=None, ccw=False): """ Return gcode string to perform an arc. @@ -446,6 +482,7 @@ def arc(cx, cy, sx, sy, ex, ey, horizFeed=0, ez=None, ccw=False): return retstr + "\n" + def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed): """ Return gcode string to perform helical entry move. @@ -492,6 +529,7 @@ def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed return helixCmds + def rampPlunge(edge, rampangle, destZ, startZ): """ Return gcode string to linearly ramp down to milling level. From 38aeb8f8de5f3db43d6da41cc2ed41630cad107a Mon Sep 17 00:00:00 2001 From: sliptonic Date: Wed, 10 May 2017 15:47:38 -0500 Subject: [PATCH 10/20] Path: TC panel bug. Loses decimal part of float spindle rate --- src/Mod/Path/Gui/Resources/panels/ToolControl.ui | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/ToolControl.ui b/src/Mod/Path/Gui/Resources/panels/ToolControl.ui index 857f71a3d7..fd50c4536c 100644 --- a/src/Mod/Path/Gui/Resources/panels/ToolControl.ui +++ b/src/Mod/Path/Gui/Resources/panels/ToolControl.ui @@ -166,13 +166,6 @@ QFormLayout::AllNonFixedFieldsGrow - - - - 99999 - - - @@ -187,6 +180,9 @@ + + + From 6bd6cc0526d241f02a6a9cf0a124c5ebe2ba2216 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Thu, 11 May 2017 09:47:33 -0500 Subject: [PATCH 11/20] Path: add cursor waiting decorator to long running functions. Remove obsolete ToolDescription code from label change. --- src/Mod/Path/PathScripts/PathDrilling.py | 8 ++------ src/Mod/Path/PathScripts/PathPocket.py | 3 --- src/Mod/Path/PathScripts/PathSurface.py | 5 +++-- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py index 8655906f7c..df58190634 100644 --- a/src/Mod/Path/PathScripts/PathDrilling.py +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -25,14 +25,11 @@ from __future__ import print_function import sys import FreeCAD -# from FreeCAD import Vector import Path import PathScripts.PathLog as PathLog -# import Part from PySide import QtCore, QtGui from PathScripts import PathUtils -from PathScripts.PathUtils import fmt -# from math import pi +from PathScripts.PathUtils import fmt, waiting_effects import ArchPanel @@ -105,9 +102,8 @@ class ObjectDrilling: def onChanged(self, obj, prop): pass - # if prop == "UserLabel": - # obj.Label = obj.UserLabel + " :" + obj.ToolDescription + @waiting_effects def execute(self, obj): PathLog.track() output = "" diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py index c21bfcc8e7..f4a4c8a8fc 100644 --- a/src/Mod/Path/PathScripts/PathPocket.py +++ b/src/Mod/Path/PathScripts/PathPocket.py @@ -109,9 +109,6 @@ class ObjectPocket: obj.setEditorMode('RampAngle', 2) # make this hidden obj.setEditorMode('RampSize', 2) # make this hidden - if prop == "UserLabel": - obj.Label = obj.UserLabel + " :" + obj.ToolDescription - def __getstate__(self): return None diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 5f5899ffe3..3851e0ab9a 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -27,6 +27,7 @@ import FreeCAD import Path from PathScripts import PathUtils import PathScripts.PathLog as PathLog +from PathScripts.PathUtils import waiting_effects import sys # xrange is not available in python3 @@ -129,8 +130,7 @@ class ObjectSurface: return None def onChanged(self, obj, prop): - if prop == "UserLabel": - obj.Label = obj.UserLabel + " :" + obj.ToolDescription + pass def _waterline(self, obj, s, bb): import ocl @@ -262,6 +262,7 @@ class ObjectSurface: return output + @waiting_effects def execute(self, obj): import MeshPart FreeCAD.Console.PrintWarning( From 4c5c439963e4e54b05ff8599d5fc6acaed3e0128 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Sat, 13 May 2017 10:47:10 -0500 Subject: [PATCH 12/20] Path: minor fixes to PathMillFace --- .../Path/Gui/Resources/panels/MillFaceEdit.ui | 35 +++++++++++++------ src/Mod/Path/PathScripts/PathMillFace.py | 12 ++++--- src/Mod/Path/PathScripts/PathUtils.py | 18 ---------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui index ec11749cf0..3f5c381fd6 100644 --- a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui @@ -33,8 +33,8 @@ 0 0 - 500 - 282 + 285 + 277 @@ -111,8 +111,8 @@ 0 0 - 500 - 241 + 125 + 124 @@ -192,8 +192,8 @@ 0 0 - 500 - 241 + 154 + 68 @@ -241,9 +241,9 @@ 0 - -34 - 486 - 361 + 0 + 500 + 329 @@ -271,7 +271,7 @@ - + @@ -422,6 +422,21 @@ ZigZagOffset + + + Line + + + + + Grid + + + + + Triangle + + diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index 5d33445400..edd9631237 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -87,7 +87,7 @@ class ObjectFace: obj.addProperty("App::PropertyEnumeration", "BoundaryShape", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Shape to use for calculating Boundary")) obj.BoundaryShape = ['Perimeter', 'Boundbox'] obj.addProperty("App::PropertyEnumeration", "OffsetPattern", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "clearing pattern to use")) - obj.OffsetPattern = ['Offset', 'ZigZag', 'Spiral', 'ZigZagOffset'] + obj.OffsetPattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle'] # Start Point Properties obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The start point of this path")) @@ -173,13 +173,16 @@ class ObjectFace: pocketparams = {'Fill': 0, 'Coplanar': 0, - 'PocketMode': 4, + 'PocketMode': 1, 'SectionCount': -1, 'Angle': obj.ZigZagAngle, 'FromCenter': (obj.StartAt == "Center"), 'PocketStepover': stepover, 'PocketExtraOffset': obj.PassExtension.Value} + Pattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle'] + pocketparams['PocketMode'] = Pattern.index(obj.OffsetPattern) + 1 + depthparams = depth_params( clearance_height=obj.ClearanceHeight.Value, rapid_safety_space=obj.SafeHeight.Value, @@ -200,6 +203,7 @@ class ObjectFace: 'resume_height': obj.StepDown, 'retraction': obj.ClearanceHeight.Value} + # if obj.Direction == 'CCW': # params['orientation'] = 1 # else: @@ -267,9 +271,9 @@ class ObjectFace: if obj.BoundaryShape == 'Boundbox': bb = planeshape.BoundBox bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, Vector(bb.XMin, bb.YMin, bb.ZMin), Vector(0, 0, 1)) - env = PathUtils.getEnvelopeTD(bbperim, obj.StartDepth) + env = PathUtils.getEnvelope(bbperim, obj.StartDepth) else: - env = PathUtils.getEnvelopeTD(planeshape, obj.StartDepth) + env = PathUtils.getEnvelope(planeshape, obj.StartDepth) try: commandlist.extend(self._buildPathArea(obj, env).Commands) diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index b8433fe4c5..b72b76ef8b 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -237,24 +237,6 @@ def getEnvelope(partshape, stockheight=None): return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZMax)) -def getEnvelopeTD(partshape, stockheight=None): - ''' - getEnvelopTD(partshape, stockheight=None) - Uses the TechDraw findShapeOutline to return a shape corresponding to the - partshape silhouette extruded to height. if stockheight is given, the - returned shape is extruded to that height otherwise the returned shape - is the height of the original shape boundbox - partshape = solid object - stockheight = float - ''' - import TechDraw - sec = Part.Face(TechDraw.findShapeOutline(partshape, 1, FreeCAD.Vector(0, 0, 1))) - if stockheight is not None: - return sec.extrude(FreeCAD.Vector(0, 0, stockheight)) - else: - return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZMax)) - - def reverseEdge(e): if geomType(e) == "Circle": arcstpt = e.valueAt(e.FirstParameter) From b865eba1e24590a452c929dcda91f2b53d905a0a Mon Sep 17 00:00:00 2001 From: sliptonic Date: Mon, 15 May 2017 18:26:05 -0500 Subject: [PATCH 13/20] Path: Area logging --- src/Mod/Path/PathScripts/PathContour.py | 9 ++++++--- src/Mod/Path/PathScripts/PathMillFace.py | 1 + src/Mod/Path/PathScripts/PathUtils.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py index fedef35959..c5b4db72fa 100644 --- a/src/Mod/Path/PathScripts/PathContour.py +++ b/src/Mod/Path/PathScripts/PathContour.py @@ -125,7 +125,9 @@ class ObjectContour: @waiting_effects def _buildPathArea(self, obj, baseobject, start=None): PathLog.track() + #Path.Area.setDefaultParams(LogLevel=0) profile = Path.Area() + #profile.setDefaultParams(LogLevel=3) profile.setPlane(Part.makeCircle(10)) profile.add(baseobject) @@ -138,7 +140,8 @@ class ObjectContour: profileparams['Offset'] = self.radius+obj.OffsetExtra.Value profile.setParams(**profileparams) - PathLog.debug("About to profile with params: {}".format(profileparams)) + # PathLog.debug("About to profile with params: {}".format(profileparams)) + PathLog.debug("About to profile with params: {}".format(profile.getParams())) depthparams = depth_params( clearance_height=obj.ClearanceHeight.Value, @@ -167,8 +170,8 @@ class ObjectContour: if obj.UseStartPoint is True and obj.StartPoint is not None: params['start'] = obj.StartPoint - PathLog.debug("Generating Path with params: {}".format(params)) pp = Path.fromShapes(**params) + PathLog.debug("Generating Path with params: {}".format(params)) PathLog.debug(pp) return pp @@ -197,7 +200,7 @@ class ObjectContour: commandlist.append(Path.Command("(" + obj.Label + ")")) - if not obj.UseComp: + if obj.UseComp: commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: commandlist.append(Path.Command("(Uncompensated Tool Path)")) diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index edd9631237..a9f9bbb55c 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -35,6 +35,7 @@ from PathScripts.PathUtils import waiting_effects LOG_MODULE = 'PathMillFace' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) PathLog.trackModule() +FreeCAD.setLogLevel('Path.Area',0) FreeCADGui = None if FreeCAD.GuiUp: diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index b72b76ef8b..f2e2457072 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -39,7 +39,7 @@ from PySide import QtGui LOG_MODULE = 'PathUtils' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) # PathLog.trackModule('PathUtils') - +FreeCAD.setLogLevel('Path.Area',0) def waiting_effects(function): def new_function(*args, **kwargs): From 2bcfe3cb1fcd981ebd88c7c9ebccbf75f24ac4ca Mon Sep 17 00:00:00 2001 From: sliptonic Date: Mon, 15 May 2017 19:02:30 -0500 Subject: [PATCH 14/20] Path: Cleanup MillFace Dialog --- .../Path/Gui/Resources/panels/MillFaceEdit.ui | 63 +++++++------------ 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui index 3f5c381fd6..254d763508 100644 --- a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui @@ -6,8 +6,8 @@ 0 0 - 518 - 754 + 379 + 523 @@ -33,7 +33,7 @@ 0 0 - 285 + 361 277 @@ -111,8 +111,8 @@ 0 0 - 125 - 124 + 361 + 130 @@ -192,8 +192,8 @@ 0 0 - 154 - 68 + 361 + 130 @@ -242,7 +242,7 @@ 0 0 - 500 + 361 329 @@ -254,39 +254,6 @@ Operation - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - ZigZag Unidirectional - - - - - - - - - - - - - - ZigZag Angle - - - - - - @@ -446,6 +413,20 @@ + + + + + + + + + + + ZigZag Angle + + + From 5e2126820c0d2e34f4607fdb237902cb2af47aee Mon Sep 17 00:00:00 2001 From: sliptonic Date: Tue, 16 May 2017 12:49:07 -0500 Subject: [PATCH 15/20] Path: PathArea logging --- src/Mod/Path/PathScripts/PathContour.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py index c5b4db72fa..44d4fc5fe9 100644 --- a/src/Mod/Path/PathScripts/PathContour.py +++ b/src/Mod/Path/PathScripts/PathContour.py @@ -34,7 +34,8 @@ from PathScripts.PathUtils import waiting_effects LOG_MODULE = 'PathContour' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) -PathLog.trackModule('PathContour') +#PathLog.trackModule('PathContour') +FreeCAD.setLogLevel('Path.Area',0) if FreeCAD.GuiUp: import FreeCADGui @@ -125,9 +126,7 @@ class ObjectContour: @waiting_effects def _buildPathArea(self, obj, baseobject, start=None): PathLog.track() - #Path.Area.setDefaultParams(LogLevel=0) profile = Path.Area() - #profile.setDefaultParams(LogLevel=3) profile.setPlane(Part.makeCircle(10)) profile.add(baseobject) From 31a697a35002cf1c95b27e2fe32fcf3594f0373e Mon Sep 17 00:00:00 2001 From: sliptonic Date: Tue, 16 May 2017 16:26:28 -0500 Subject: [PATCH 16/20] Path: fixes for unittest Path: precision fix to unittest pathpost --- src/Mod/Path/PathScripts/PathUtils.py | 2 + src/Mod/Path/PathTests/test_linuxcnc_00.ngc | 210 +++++++++++--------- 2 files changed, 118 insertions(+), 94 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index f2e2457072..420af641b7 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -43,6 +43,8 @@ FreeCAD.setLogLevel('Path.Area',0) def waiting_effects(function): def new_function(*args, **kwargs): + if not FreeCAD.GuiUp: + return function(*args, **kwargs) QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) res = None try: diff --git a/src/Mod/Path/PathTests/test_linuxcnc_00.ngc b/src/Mod/Path/PathTests/test_linuxcnc_00.ngc index 79a55e10e0..5df4c3231f 100644 --- a/src/Mod/Path/PathTests/test_linuxcnc_00.ngc +++ b/src/Mod/Path/PathTests/test_linuxcnc_00.ngc @@ -7,101 +7,123 @@ M3 S0.0000 M6 T2.0 M3 S0.0000 (Contour) -(Uncompensated Tool Path) +(Compensated Tool Path. Diameter: 5.0) G0 Z15.0000 -G00 X-2.5000 Y0.0000 -G00 Z23.0000 -G01 X-2.5000 Y0.0000 Z9.0000 F0.00 -G01 X-2.5000 Y10.0000 Z9.0000 F0.00 -G02 X0.0000 Y12.5000 Z9.0000 I2.5000 J0.0000 F0.00 -G01 X10.0000 Y12.5000 Z9.0000 F0.00 -G02 X12.5000 Y10.0000 Z9.0000 I0.0000 J-2.5000 F0.00 -G01 X12.5000 Y0.0000 Z9.0000 F0.00 -G02 X10.0000 Y-2.5000 Z9.0000 I-2.5000 J0.0000 F0.00 -G01 X0.0000 Y-2.5000 Z9.0000 F0.00 -G02 X-2.5000 Y0.0000 Z9.0000 I0.0000 J2.5000 F0.00 -G01 X-2.5000 Y0.0000 Z8.0000 F0.00 -G01 X-2.5000 Y10.0000 Z8.0000 F0.00 -G02 X0.0000 Y12.5000 Z8.0000 I2.5000 J0.0000 F0.00 -G01 X10.0000 Y12.5000 Z8.0000 F0.00 -G02 X12.5000 Y10.0000 Z8.0000 I0.0000 J-2.5000 F0.00 -G01 X12.5000 Y0.0000 Z8.0000 F0.00 -G02 X10.0000 Y-2.5000 Z8.0000 I-2.5000 J0.0000 F0.00 -G01 X0.0000 Y-2.5000 Z8.0000 F0.00 -G02 X-2.5000 Y0.0000 Z8.0000 I0.0000 J2.5000 F0.00 -G01 X-2.5000 Y0.0000 Z7.0000 F0.00 -G01 X-2.5000 Y10.0000 Z7.0000 F0.00 -G02 X0.0000 Y12.5000 Z7.0000 I2.5000 J0.0000 F0.00 -G01 X10.0000 Y12.5000 Z7.0000 F0.00 -G02 X12.5000 Y10.0000 Z7.0000 I0.0000 J-2.5000 F0.00 -G01 X12.5000 Y0.0000 Z7.0000 F0.00 -G02 X10.0000 Y-2.5000 Z7.0000 I-2.5000 J0.0000 F0.00 -G01 X0.0000 Y-2.5000 Z7.0000 F0.00 -G02 X-2.5000 Y0.0000 Z7.0000 I0.0000 J2.5000 F0.00 -G01 X-2.5000 Y0.0000 Z6.0000 F0.00 -G01 X-2.5000 Y10.0000 Z6.0000 F0.00 -G02 X0.0000 Y12.5000 Z6.0000 I2.5000 J0.0000 F0.00 -G01 X10.0000 Y12.5000 Z6.0000 F0.00 -G02 X12.5000 Y10.0000 Z6.0000 I0.0000 J-2.5000 F0.00 -G01 X12.5000 Y0.0000 Z6.0000 F0.00 -G02 X10.0000 Y-2.5000 Z6.0000 I-2.5000 J0.0000 F0.00 -G01 X0.0000 Y-2.5000 Z6.0000 F0.00 -G02 X-2.5000 Y0.0000 Z6.0000 I0.0000 J2.5000 F0.00 -G01 X-2.5000 Y0.0000 Z5.0000 F0.00 -G01 X-2.5000 Y10.0000 Z5.0000 F0.00 -G02 X0.0000 Y12.5000 Z5.0000 I2.5000 J0.0000 F0.00 -G01 X10.0000 Y12.5000 Z5.0000 F0.00 -G02 X12.5000 Y10.0000 Z5.0000 I0.0000 J-2.5000 F0.00 -G01 X12.5000 Y0.0000 Z5.0000 F0.00 -G02 X10.0000 Y-2.5000 Z5.0000 I-2.5000 J0.0000 F0.00 -G01 X0.0000 Y-2.5000 Z5.0000 F0.00 -G02 X-2.5000 Y0.0000 Z5.0000 I0.0000 J2.5000 F0.00 -G01 X-2.5000 Y0.0000 Z4.0000 F0.00 -G01 X-2.5000 Y10.0000 Z4.0000 F0.00 -G02 X0.0000 Y12.5000 Z4.0000 I2.5000 J0.0000 F0.00 -G01 X10.0000 Y12.5000 Z4.0000 F0.00 -G02 X12.5000 Y10.0000 Z4.0000 I0.0000 J-2.5000 F0.00 -G01 X12.5000 Y0.0000 Z4.0000 F0.00 -G02 X10.0000 Y-2.5000 Z4.0000 I-2.5000 J0.0000 F0.00 -G01 X0.0000 Y-2.5000 Z4.0000 F0.00 -G02 X-2.5000 Y0.0000 Z4.0000 I0.0000 J2.5000 F0.00 -G01 X-2.5000 Y0.0000 Z3.0000 F0.00 -G01 X-2.5000 Y10.0000 Z3.0000 F0.00 -G02 X0.0000 Y12.5000 Z3.0000 I2.5000 J0.0000 F0.00 -G01 X10.0000 Y12.5000 Z3.0000 F0.00 -G02 X12.5000 Y10.0000 Z3.0000 I0.0000 J-2.5000 F0.00 -G01 X12.5000 Y0.0000 Z3.0000 F0.00 -G02 X10.0000 Y-2.5000 Z3.0000 I-2.5000 J0.0000 F0.00 -G01 X0.0000 Y-2.5000 Z3.0000 F0.00 -G02 X-2.5000 Y0.0000 Z3.0000 I0.0000 J2.5000 F0.00 -G01 X-2.5000 Y0.0000 Z2.0000 F0.00 -G01 X-2.5000 Y10.0000 Z2.0000 F0.00 -G02 X0.0000 Y12.5000 Z2.0000 I2.5000 J0.0000 F0.00 -G01 X10.0000 Y12.5000 Z2.0000 F0.00 -G02 X12.5000 Y10.0000 Z2.0000 I0.0000 J-2.5000 F0.00 -G01 X12.5000 Y0.0000 Z2.0000 F0.00 -G02 X10.0000 Y-2.5000 Z2.0000 I-2.5000 J0.0000 F0.00 -G01 X0.0000 Y-2.5000 Z2.0000 F0.00 -G02 X-2.5000 Y0.0000 Z2.0000 I0.0000 J2.5000 F0.00 -G01 X-2.5000 Y0.0000 Z1.0000 F0.00 -G01 X-2.5000 Y10.0000 Z1.0000 F0.00 -G02 X0.0000 Y12.5000 Z1.0000 I2.5000 J0.0000 F0.00 -G01 X10.0000 Y12.5000 Z1.0000 F0.00 -G02 X12.5000 Y10.0000 Z1.0000 I0.0000 J-2.5000 F0.00 -G01 X12.5000 Y0.0000 Z1.0000 F0.00 -G02 X10.0000 Y-2.5000 Z1.0000 I-2.5000 J0.0000 F0.00 -G01 X0.0000 Y-2.5000 Z1.0000 F0.00 -G02 X-2.5000 Y0.0000 Z1.0000 I0.0000 J2.5000 F0.00 -G01 X-2.5000 Y0.0000 Z0.0000 F0.00 -G01 X-2.5000 Y10.0000 Z0.0000 F0.00 -G02 X0.0000 Y12.5000 Z0.0000 I2.5000 J0.0000 F0.00 -G01 X10.0000 Y12.5000 Z0.0000 F0.00 -G02 X12.5000 Y10.0000 Z0.0000 I0.0000 J-2.5000 F0.00 -G01 X12.5000 Y0.0000 Z0.0000 F0.00 -G02 X10.0000 Y-2.5000 Z0.0000 I-2.5000 J0.0000 F0.00 -G01 X0.0000 Y-2.5000 Z0.0000 F0.00 -G02 X-2.5000 Y0.0000 Z0.0000 I0.0000 J2.5000 F0.00 -G00 Z15.0000 +G90 +G17 +G0 X0.0000 Y0.0000 Z15.0000 +G0 X11.7677 Y11.7676 Z15.0000 +G0 X11.7677 Y11.7676 Z10.0000 +G1 X11.7677 Y11.7676 Z9.0000 +G3 X9.9999 Y12.4999 Z9.0000 I-1.7677 J-1.7677 +G1 X0.0001 Y12.4999 Z9.0000 +G3 X-2.4999 Y9.9999 Z9.0000 I-0.0000 J-2.5000 +G1 X-2.4999 Y0.0001 Z9.0000 +G3 X0.0001 Y-2.4999 Z9.0000 I2.5000 J-0.0000 +G1 X9.9999 Y-2.4999 Z9.0000 +G1 X10.4340 Y-2.4619 Z9.0000 +G3 X12.4999 Y0.0001 Z9.0000 I-0.4340 J2.4620 +G1 X12.4999 Y9.9999 Z9.0000 +G3 X11.7677 Y11.7676 Z9.0000 I-2.5000 J0.0000 +G1 X11.7677 Y11.7676 Z8.0000 +G3 X9.9999 Y12.4999 Z8.0000 I-1.7677 J-1.7677 +G1 X0.0001 Y12.4999 Z8.0000 +G3 X-2.4999 Y9.9999 Z8.0000 I-0.0000 J-2.5000 +G1 X-2.4999 Y0.0001 Z8.0000 +G3 X0.0001 Y-2.4999 Z8.0000 I2.5000 J-0.0000 +G1 X9.9999 Y-2.4999 Z8.0000 +G1 X10.4340 Y-2.4619 Z8.0000 +G3 X12.4999 Y0.0001 Z8.0000 I-0.4340 J2.4620 +G1 X12.4999 Y9.9999 Z8.0000 +G3 X11.7677 Y11.7676 Z8.0000 I-2.5000 J0.0000 +G1 X11.7677 Y11.7676 Z7.0000 +G3 X9.9999 Y12.4999 Z7.0000 I-1.7677 J-1.7677 +G1 X0.0001 Y12.4999 Z7.0000 +G3 X-2.4999 Y9.9999 Z7.0000 I-0.0000 J-2.5000 +G1 X-2.4999 Y0.0001 Z7.0000 +G3 X0.0001 Y-2.4999 Z7.0000 I2.5000 J-0.0000 +G1 X9.9999 Y-2.4999 Z7.0000 +G1 X10.4340 Y-2.4619 Z7.0000 +G3 X12.4999 Y0.0001 Z7.0000 I-0.4340 J2.4620 +G1 X12.4999 Y9.9999 Z7.0000 +G3 X11.7677 Y11.7676 Z7.0000 I-2.5000 J0.0000 +G1 X11.7677 Y11.7676 Z6.0000 +G3 X9.9999 Y12.4999 Z6.0000 I-1.7677 J-1.7677 +G1 X0.0001 Y12.4999 Z6.0000 +G3 X-2.4999 Y9.9999 Z6.0000 I-0.0000 J-2.5000 +G1 X-2.4999 Y0.0001 Z6.0000 +G3 X0.0001 Y-2.4999 Z6.0000 I2.5000 J-0.0000 +G1 X9.9999 Y-2.4999 Z6.0000 +G1 X10.4340 Y-2.4619 Z6.0000 +G3 X12.4999 Y0.0001 Z6.0000 I-0.4340 J2.4620 +G1 X12.4999 Y9.9999 Z6.0000 +G3 X11.7677 Y11.7676 Z6.0000 I-2.5000 J0.0000 +G1 X11.7677 Y11.7676 Z5.0000 +G3 X9.9999 Y12.4999 Z5.0000 I-1.7677 J-1.7677 +G1 X0.0001 Y12.4999 Z5.0000 +G3 X-2.4999 Y9.9999 Z5.0000 I-0.0000 J-2.5000 +G1 X-2.4999 Y0.0001 Z5.0000 +G3 X0.0001 Y-2.4999 Z5.0000 I2.5000 J-0.0000 +G1 X9.9999 Y-2.4999 Z5.0000 +G1 X10.4340 Y-2.4619 Z5.0000 +G3 X12.4999 Y0.0001 Z5.0000 I-0.4340 J2.4620 +G1 X12.4999 Y9.9999 Z5.0000 +G3 X11.7677 Y11.7676 Z5.0000 I-2.5000 J0.0000 +G1 X11.7677 Y11.7676 Z4.0000 +G3 X9.9999 Y12.4999 Z4.0000 I-1.7677 J-1.7677 +G1 X0.0001 Y12.4999 Z4.0000 +G3 X-2.4999 Y9.9999 Z4.0000 I-0.0000 J-2.5000 +G1 X-2.4999 Y0.0001 Z4.0000 +G3 X0.0001 Y-2.4999 Z4.0000 I2.5000 J-0.0000 +G1 X9.9999 Y-2.4999 Z4.0000 +G1 X10.4340 Y-2.4619 Z4.0000 +G3 X12.4999 Y0.0001 Z4.0000 I-0.4340 J2.4620 +G1 X12.4999 Y9.9999 Z4.0000 +G3 X11.7677 Y11.7676 Z4.0000 I-2.5000 J0.0000 +G1 X11.7677 Y11.7676 Z3.0000 +G3 X9.9999 Y12.4999 Z3.0000 I-1.7677 J-1.7677 +G1 X0.0001 Y12.4999 Z3.0000 +G3 X-2.4999 Y9.9999 Z3.0000 I-0.0000 J-2.5000 +G1 X-2.4999 Y0.0001 Z3.0000 +G3 X0.0001 Y-2.4999 Z3.0000 I2.5000 J-0.0000 +G1 X9.9999 Y-2.4999 Z3.0000 +G1 X10.4340 Y-2.4619 Z3.0000 +G3 X12.4999 Y0.0001 Z3.0000 I-0.4340 J2.4620 +G1 X12.4999 Y9.9999 Z3.0000 +G3 X11.7677 Y11.7676 Z3.0000 I-2.5000 J0.0000 +G1 X11.7677 Y11.7676 Z2.0000 +G3 X9.9999 Y12.4999 Z2.0000 I-1.7677 J-1.7677 +G1 X0.0001 Y12.4999 Z2.0000 +G3 X-2.4999 Y9.9999 Z2.0000 I-0.0000 J-2.5000 +G1 X-2.4999 Y0.0001 Z2.0000 +G3 X0.0001 Y-2.4999 Z2.0000 I2.5000 J-0.0000 +G1 X9.9999 Y-2.4999 Z2.0000 +G1 X10.4340 Y-2.4619 Z2.0000 +G3 X12.4999 Y0.0001 Z2.0000 I-0.4340 J2.4620 +G1 X12.4999 Y9.9999 Z2.0000 +G3 X11.7677 Y11.7676 Z2.0000 I-2.5000 J0.0000 +G1 X11.7677 Y11.7676 Z1.0000 +G3 X9.9999 Y12.4999 Z1.0000 I-1.7677 J-1.7677 +G1 X0.0001 Y12.4999 Z1.0000 +G3 X-2.4999 Y9.9999 Z1.0000 I-0.0000 J-2.5000 +G1 X-2.4999 Y0.0001 Z1.0000 +G3 X0.0001 Y-2.4999 Z1.0000 I2.5000 J-0.0000 +G1 X9.9999 Y-2.4999 Z1.0000 +G1 X10.4340 Y-2.4619 Z1.0000 +G3 X12.4999 Y0.0001 Z1.0000 I-0.4340 J2.4620 +G1 X12.4999 Y9.9999 Z1.0000 +G3 X11.7677 Y11.7676 Z1.0000 I-2.5000 J0.0000 +G1 X11.7677 Y11.7676 Z0.0000 +G3 X9.9999 Y12.4999 Z0.0000 I-1.7677 J-1.7677 +G1 X0.0001 Y12.4999 Z0.0000 +G3 X-2.4999 Y9.9999 Z0.0000 I-0.0000 J-2.5000 +G1 X-2.4999 Y0.0001 Z0.0000 +G3 X0.0001 Y-2.4999 Z0.0000 I2.5000 J-0.0000 +G1 X9.9999 Y-2.4999 Z0.0000 +G1 X10.4340 Y-2.4619 Z0.0000 +G3 X12.4999 Y0.0001 Z0.0000 I-0.4340 J2.4620 +G1 X12.4999 Y9.9999 Z0.0000 +G3 X11.7677 Y11.7676 Z0.0000 I-2.5000 J0.0000 M05 G00 X-1.0 Y1.0 G17 G90 From a4b801e17808fdb0f99e3252f5d423657138e75f Mon Sep 17 00:00:00 2001 From: sliptonic Date: Wed, 17 May 2017 16:12:32 -0500 Subject: [PATCH 17/20] Path: added use of return_last to contour. Path: bug fixes --- src/Mod/Path/PathScripts/PathContour.py | 70 ++++++++++++++---------- src/Mod/Path/PathScripts/PathMillFace.py | 12 ++-- src/Mod/Path/PathScripts/PathUtils.py | 29 ++++++++-- 3 files changed, 71 insertions(+), 40 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py index 44d4fc5fe9..b9c9b5a963 100644 --- a/src/Mod/Path/PathScripts/PathContour.py +++ b/src/Mod/Path/PathScripts/PathContour.py @@ -31,11 +31,12 @@ from PySide import QtCore import ArchPanel import Part from PathScripts.PathUtils import waiting_effects +from PathScripts.PathUtils import makeWorkplane LOG_MODULE = 'PathContour' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) #PathLog.trackModule('PathContour') -FreeCAD.setLogLevel('Path.Area',0) +FreeCAD.setLogLevel('Path.Area', 0) if FreeCAD.GuiUp: import FreeCADGui @@ -79,7 +80,6 @@ class ObjectContour: # 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")) # Contour Properties obj.addProperty("App::PropertyEnumeration", "Direction", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) @@ -92,6 +92,7 @@ class ObjectContour: obj.addProperty("App::PropertyDistance", "OffsetExtra", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra value to stay away from final Contour- good for roughing toolpath")) obj.Proxy = self + self.endVector = None def __getstate__(self): return None @@ -101,6 +102,8 @@ class ObjectContour: def onChanged(self, obj, prop): pass + # if prop in ['ClearanceHeight', 'StartPoint']: + # obj.StartPoint.z = obj.ClearanceHeight.Value def setDepths(proxy, obj): PathLog.track() @@ -127,7 +130,7 @@ class ObjectContour: def _buildPathArea(self, obj, baseobject, start=None): PathLog.track() profile = Path.Area() - profile.setPlane(Part.makeCircle(10)) + profile.setPlane(makeWorkplane(baseobject)) profile.add(baseobject) profileparams = {'Fill': 0, @@ -138,10 +141,6 @@ class ObjectContour: else: profileparams['Offset'] = self.radius+obj.OffsetExtra.Value - 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( clearance_height=obj.ClearanceHeight.Value, rapid_safety_space=obj.SafeHeight.Value, @@ -151,6 +150,10 @@ class ObjectContour: final_depth=obj.FinalDepth.Value, user_depths=None) + PathLog.debug('depths: {}'.format(depthparams.get_depths())) + profile.setParams(**profileparams) + PathLog.debug("Contour with params: {}".format(profile.getParams())) + sections = profile.makeSections(mode=0, project=True, heights=depthparams.get_depths()) shapelist = [sec.getShape() for sec in sections] @@ -159,25 +162,29 @@ class ObjectContour: 'feedrate_v': self.vertFeed, 'verbose': True, 'resume_height': obj.StepDown.Value, - 'retraction': obj.ClearanceHeight.Value} + 'retraction': obj.ClearanceHeight.Value, + 'return_end': True} if obj.Direction == 'CCW': params['orientation'] = 1 else: params['orientation'] = 0 - if obj.UseStartPoint is True and obj.StartPoint is not None: - params['start'] = obj.StartPoint + if self.endVector is not None: + params['start'] = self.endVector + elif start is not None: + params['start'] = start - pp = Path.fromShapes(**params) + (pp, end_vector) = Path.fromShapes(**params) PathLog.debug("Generating Path with params: {}".format(params)) - PathLog.debug(pp) + PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector)) + self.endVector = end_vector return pp def execute(self, obj): PathLog.track() - import Part + self.endVector = None commandlist = [] toolLoad = obj.ToolController @@ -211,25 +218,27 @@ class ObjectContour: if baseobject is None: return + # Let's always start by rapid to clearance...just for safety + commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) + if hasattr(baseobject, "Proxy"): if isinstance(baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet baseobject.Proxy.execute(baseobject) - for subobj in baseobject.Group: # process the group of panels - if isinstance(subobj.Proxy, ArchPanel.PanelCut): - shapes = baseobject.Proxy.getOutlines(baseobject, transform=True) - for shape in shapes: - f = Part.makeFace([shape], 'Part::FaceMakerSimple') - thickness = baseobject.Group[0].Source.Thickness - contourshape = f.extrude(FreeCAD.Vector(0, 0, thickness)) - try: - commandlist.extend(self._buildPathArea(obj, contourshape).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.getOutlines(baseobject, transform=True) + for shape in shapes: + f = Part.makeFace([shape], 'Part::FaceMakerSimple') + thickness = baseobject.Group[0].Source.Thickness + contourshape = f.extrude(FreeCAD.Vector(0, 0, thickness)) + try: + commandlist.extend(self._buildPathArea(obj, contourshape, start=obj.StartPoint).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: - env = PathUtils.getEnvelope(baseobject.Shape, obj.StartDepth) + bb = baseobject.Shape.BoundBox + env = PathUtils.getEnvelope(baseobject.Shape, bb.ZLength + (obj.StartDepth.Value-bb.ZMax)) try: - commandlist.extend(self._buildPathArea(obj, env).Commands) + commandlist.extend(self._buildPathArea(obj, env, start=obj.StartPoint).Commands) except Exception as e: print(e) FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") @@ -285,6 +294,7 @@ class _CommandSetStartPoint: obj = FreeCADGui.Selection.getSelection()[0] obj.StartPoint.x = point.x obj.StartPoint.y = point.y + obj.StartPoint.z = obj.ClearanceHeight.Value def Activated(self): FreeCADGui.Snapper.getPoint(callback=self.setpoint) @@ -371,8 +381,8 @@ class TaskPanel: self.obj.OffsetExtra = FreeCAD.Units.Quantity(self.form.extraOffset.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, "UseStartPoint"): + # self.obj.UseStartPoint = self.form.useStartPoint.isChecked() if hasattr(self.obj, "Direction"): self.obj.Direction = str(self.form.direction.currentText()) if hasattr(self.obj, "ToolController"): @@ -389,7 +399,7 @@ 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.OffsetExtra.Value, FreeCAD.Units.Length).UserString) self.form.useCompensation.setChecked(self.obj.UseComp) - self.form.useStartPoint.setChecked(self.obj.UseStartPoint) + # self.form.useStartPoint.setChecked(self.obj.UseStartPoint) index = self.form.direction.findText( self.obj.Direction, QtCore.Qt.MatchFixedString) diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index a9f9bbb55c..5175d71741 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -31,11 +31,12 @@ import Part from FreeCAD import Vector import PathScripts.PathLog as PathLog from PathScripts.PathUtils import waiting_effects +from PathScripts.PathUtils import makeWorkplane LOG_MODULE = 'PathMillFace' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) PathLog.trackModule() -FreeCAD.setLogLevel('Path.Area',0) +FreeCAD.setLogLevel('Path.Area', 0) FreeCADGui = None if FreeCAD.GuiUp: @@ -167,7 +168,7 @@ class ObjectFace: PathLog.track() boundary = Path.Area() - boundary.setPlane(Part.makeCircle(10)) + boundary.setPlane(makeWorkplane(baseobject)) boundary.add(baseobject) stepover = (self.radius * 2) * (float(obj.StepOver)/100) @@ -204,7 +205,6 @@ class ObjectFace: 'resume_height': obj.StepDown, 'retraction': obj.ClearanceHeight.Value} - # if obj.Direction == 'CCW': # params['orientation'] = 1 # else: @@ -269,12 +269,12 @@ class ObjectFace: # if user wants the boundbox, calculate that PathLog.info("Boundary Shape: {}".format(obj.BoundaryShape)) + bb = planeshape.BoundBox if obj.BoundaryShape == 'Boundbox': - bb = planeshape.BoundBox bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, Vector(bb.XMin, bb.YMin, bb.ZMin), Vector(0, 0, 1)) - env = PathUtils.getEnvelope(bbperim, obj.StartDepth) + env = PathUtils.getEnvelope(bbperim, bb.ZLength + (obj.StartDepth.Value-bb.ZMax)) else: - env = PathUtils.getEnvelope(planeshape, obj.StartDepth) + env = PathUtils.getEnvelope(planeshape, bb.ZLength + (obj.StartDepth.Value-bb.ZMax)) try: commandlist.extend(self._buildPathArea(obj, env).Commands) diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index 420af641b7..8dccbb0234 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -39,7 +39,8 @@ from PySide import QtGui LOG_MODULE = 'PathUtils' PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE) # PathLog.trackModule('PathUtils') -FreeCAD.setLogLevel('Path.Area',0) +FreeCAD.setLogLevel('Path.Area', 0) + def waiting_effects(function): def new_function(*args, **kwargs): @@ -61,6 +62,7 @@ def waiting_effects(function): def cleanedges(splines, precision): '''cleanedges([splines],precision). Convert BSpline curves, Beziers, to arcs that can be used for cnc paths. Returns Lines as is. Filters Circle and Arcs for over 180 degrees. Discretizes Ellipses. Ignores other geometry. ''' + PathLog.track() edges = [] for spline in splines: if geomType(spline) == "BSplineCurve": @@ -97,6 +99,7 @@ def cleanedges(splines, precision): def curvetowire(obj, steps): '''adapted from DraftGeomUtils, because the discretize function changed a bit ''' + PathLog.track() points = obj.copy().discretize(Distance=eval('steps')) p0 = points[0] edgelist = [] @@ -186,6 +189,7 @@ def loopdetect(obj, edge1, edge2): def filterArcs(arcEdge): '''filterArcs(Edge) -used to split arcs that over 180 degrees. Returns list ''' + PathLog.track() s = arcEdge if isinstance(s.Curve, Part.Circle): splitlist = [] @@ -221,6 +225,18 @@ def filterArcs(arcEdge): return splitlist +def makeWorkplane(shape): + """ + Creates a workplane circle at the ZMin level. + """ + PathLog.track() + loc = FreeCAD.Vector(shape.BoundBox.Center.x, + shape.BoundBox.Center.y, + shape.BoundBox.ZMin) + c = Part.makeCircle(10, loc) + return c + + def getEnvelope(partshape, stockheight=None): ''' getEnvelop(partshape, stockheight=None) @@ -230,13 +246,17 @@ def getEnvelope(partshape, stockheight=None): partshape = solid object stockheight = float ''' + PathLog.track(partshape, stockheight) area = Path.Area(Fill=1, Coplanar=0).add(partshape) - area.setPlane(Part.makeCircle(10)) - sec = area.makeSections(heights=[1.0], project=True)[0].getShape(rebuild=True) + # loc = FreeCAD.Vector(partshape.BoundBox.Center.x, + # partshape.BoundBox.Center.y, + # 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)) else: - return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZMax)) + return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZLength)) def reverseEdge(e): @@ -618,6 +638,7 @@ def sort_jobs(locations, keys, attractors=[]): return out + class depth_params: '''calculates the intermediate depth values for various operations given the starting, ending, and stepdown parameters (self, clearance_height, rapid_safety_space, start_depth, step_down, z_finish_depth, final_depth, [user_depths=None]) From 78983d9dd27bfb14dd07f7fc8f7740e374b21d46 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Fri, 26 May 2017 14:42:16 -0500 Subject: [PATCH 18/20] Path: trying to fix unit test again --- src/Mod/Path/PathTests/TestPathPost.py | 2 +- src/Mod/Path/PathTests/test_linuxcnc_00.ngc | 166 +++++++------------- 2 files changed, 54 insertions(+), 114 deletions(-) diff --git a/src/Mod/Path/PathTests/TestPathPost.py b/src/Mod/Path/PathTests/TestPathPost.py index a33f4fad7c..f2b3739ae1 100644 --- a/src/Mod/Path/PathTests/TestPathPost.py +++ b/src/Mod/Path/PathTests/TestPathPost.py @@ -78,7 +78,7 @@ class PathPostTestCases(unittest.TestCase): contour.OffsetExtra = 0.0 contour.Direction = 'CW' contour.ToolController = tc - contour.UseComp = True + contour.UseComp = False PathScripts.PathUtils.addToJob(contour) PathScripts.PathContour.ObjectContour.setDepths(contour.Proxy, contour) self.doc.recompute() diff --git a/src/Mod/Path/PathTests/test_linuxcnc_00.ngc b/src/Mod/Path/PathTests/test_linuxcnc_00.ngc index 5df4c3231f..4859ee3d72 100644 --- a/src/Mod/Path/PathTests/test_linuxcnc_00.ngc +++ b/src/Mod/Path/PathTests/test_linuxcnc_00.ngc @@ -7,123 +7,63 @@ M3 S0.0000 M6 T2.0 M3 S0.0000 (Contour) -(Compensated Tool Path. Diameter: 5.0) +(Uncompensated Tool Path) G0 Z15.0000 G90 G17 G0 X0.0000 Y0.0000 Z15.0000 -G0 X11.7677 Y11.7676 Z15.0000 -G0 X11.7677 Y11.7676 Z10.0000 -G1 X11.7677 Y11.7676 Z9.0000 -G3 X9.9999 Y12.4999 Z9.0000 I-1.7677 J-1.7677 -G1 X0.0001 Y12.4999 Z9.0000 -G3 X-2.4999 Y9.9999 Z9.0000 I-0.0000 J-2.5000 -G1 X-2.4999 Y0.0001 Z9.0000 -G3 X0.0001 Y-2.4999 Z9.0000 I2.5000 J-0.0000 -G1 X9.9999 Y-2.4999 Z9.0000 -G1 X10.4340 Y-2.4619 Z9.0000 -G3 X12.4999 Y0.0001 Z9.0000 I-0.4340 J2.4620 -G1 X12.4999 Y9.9999 Z9.0000 -G3 X11.7677 Y11.7676 Z9.0000 I-2.5000 J0.0000 -G1 X11.7677 Y11.7676 Z8.0000 -G3 X9.9999 Y12.4999 Z8.0000 I-1.7677 J-1.7677 -G1 X0.0001 Y12.4999 Z8.0000 -G3 X-2.4999 Y9.9999 Z8.0000 I-0.0000 J-2.5000 -G1 X-2.4999 Y0.0001 Z8.0000 -G3 X0.0001 Y-2.4999 Z8.0000 I2.5000 J-0.0000 -G1 X9.9999 Y-2.4999 Z8.0000 -G1 X10.4340 Y-2.4619 Z8.0000 -G3 X12.4999 Y0.0001 Z8.0000 I-0.4340 J2.4620 -G1 X12.4999 Y9.9999 Z8.0000 -G3 X11.7677 Y11.7676 Z8.0000 I-2.5000 J0.0000 -G1 X11.7677 Y11.7676 Z7.0000 -G3 X9.9999 Y12.4999 Z7.0000 I-1.7677 J-1.7677 -G1 X0.0001 Y12.4999 Z7.0000 -G3 X-2.4999 Y9.9999 Z7.0000 I-0.0000 J-2.5000 -G1 X-2.4999 Y0.0001 Z7.0000 -G3 X0.0001 Y-2.4999 Z7.0000 I2.5000 J-0.0000 -G1 X9.9999 Y-2.4999 Z7.0000 -G1 X10.4340 Y-2.4619 Z7.0000 -G3 X12.4999 Y0.0001 Z7.0000 I-0.4340 J2.4620 -G1 X12.4999 Y9.9999 Z7.0000 -G3 X11.7677 Y11.7676 Z7.0000 I-2.5000 J0.0000 -G1 X11.7677 Y11.7676 Z6.0000 -G3 X9.9999 Y12.4999 Z6.0000 I-1.7677 J-1.7677 -G1 X0.0001 Y12.4999 Z6.0000 -G3 X-2.4999 Y9.9999 Z6.0000 I-0.0000 J-2.5000 -G1 X-2.4999 Y0.0001 Z6.0000 -G3 X0.0001 Y-2.4999 Z6.0000 I2.5000 J-0.0000 -G1 X9.9999 Y-2.4999 Z6.0000 -G1 X10.4340 Y-2.4619 Z6.0000 -G3 X12.4999 Y0.0001 Z6.0000 I-0.4340 J2.4620 -G1 X12.4999 Y9.9999 Z6.0000 -G3 X11.7677 Y11.7676 Z6.0000 I-2.5000 J0.0000 -G1 X11.7677 Y11.7676 Z5.0000 -G3 X9.9999 Y12.4999 Z5.0000 I-1.7677 J-1.7677 -G1 X0.0001 Y12.4999 Z5.0000 -G3 X-2.4999 Y9.9999 Z5.0000 I-0.0000 J-2.5000 -G1 X-2.4999 Y0.0001 Z5.0000 -G3 X0.0001 Y-2.4999 Z5.0000 I2.5000 J-0.0000 -G1 X9.9999 Y-2.4999 Z5.0000 -G1 X10.4340 Y-2.4619 Z5.0000 -G3 X12.4999 Y0.0001 Z5.0000 I-0.4340 J2.4620 -G1 X12.4999 Y9.9999 Z5.0000 -G3 X11.7677 Y11.7676 Z5.0000 I-2.5000 J0.0000 -G1 X11.7677 Y11.7676 Z4.0000 -G3 X9.9999 Y12.4999 Z4.0000 I-1.7677 J-1.7677 -G1 X0.0001 Y12.4999 Z4.0000 -G3 X-2.4999 Y9.9999 Z4.0000 I-0.0000 J-2.5000 -G1 X-2.4999 Y0.0001 Z4.0000 -G3 X0.0001 Y-2.4999 Z4.0000 I2.5000 J-0.0000 -G1 X9.9999 Y-2.4999 Z4.0000 -G1 X10.4340 Y-2.4619 Z4.0000 -G3 X12.4999 Y0.0001 Z4.0000 I-0.4340 J2.4620 -G1 X12.4999 Y9.9999 Z4.0000 -G3 X11.7677 Y11.7676 Z4.0000 I-2.5000 J0.0000 -G1 X11.7677 Y11.7676 Z3.0000 -G3 X9.9999 Y12.4999 Z3.0000 I-1.7677 J-1.7677 -G1 X0.0001 Y12.4999 Z3.0000 -G3 X-2.4999 Y9.9999 Z3.0000 I-0.0000 J-2.5000 -G1 X-2.4999 Y0.0001 Z3.0000 -G3 X0.0001 Y-2.4999 Z3.0000 I2.5000 J-0.0000 -G1 X9.9999 Y-2.4999 Z3.0000 -G1 X10.4340 Y-2.4619 Z3.0000 -G3 X12.4999 Y0.0001 Z3.0000 I-0.4340 J2.4620 -G1 X12.4999 Y9.9999 Z3.0000 -G3 X11.7677 Y11.7676 Z3.0000 I-2.5000 J0.0000 -G1 X11.7677 Y11.7676 Z2.0000 -G3 X9.9999 Y12.4999 Z2.0000 I-1.7677 J-1.7677 -G1 X0.0001 Y12.4999 Z2.0000 -G3 X-2.4999 Y9.9999 Z2.0000 I-0.0000 J-2.5000 -G1 X-2.4999 Y0.0001 Z2.0000 -G3 X0.0001 Y-2.4999 Z2.0000 I2.5000 J-0.0000 -G1 X9.9999 Y-2.4999 Z2.0000 -G1 X10.4340 Y-2.4619 Z2.0000 -G3 X12.4999 Y0.0001 Z2.0000 I-0.4340 J2.4620 -G1 X12.4999 Y9.9999 Z2.0000 -G3 X11.7677 Y11.7676 Z2.0000 I-2.5000 J0.0000 -G1 X11.7677 Y11.7676 Z1.0000 -G3 X9.9999 Y12.4999 Z1.0000 I-1.7677 J-1.7677 -G1 X0.0001 Y12.4999 Z1.0000 -G3 X-2.4999 Y9.9999 Z1.0000 I-0.0000 J-2.5000 -G1 X-2.4999 Y0.0001 Z1.0000 -G3 X0.0001 Y-2.4999 Z1.0000 I2.5000 J-0.0000 -G1 X9.9999 Y-2.4999 Z1.0000 -G1 X10.4340 Y-2.4619 Z1.0000 -G3 X12.4999 Y0.0001 Z1.0000 I-0.4340 J2.4620 -G1 X12.4999 Y9.9999 Z1.0000 -G3 X11.7677 Y11.7676 Z1.0000 I-2.5000 J0.0000 -G1 X11.7677 Y11.7676 Z0.0000 -G3 X9.9999 Y12.4999 Z0.0000 I-1.7677 J-1.7677 -G1 X0.0001 Y12.4999 Z0.0000 -G3 X-2.4999 Y9.9999 Z0.0000 I-0.0000 J-2.5000 -G1 X-2.4999 Y0.0001 Z0.0000 -G3 X0.0001 Y-2.4999 Z0.0000 I2.5000 J-0.0000 -G1 X9.9999 Y-2.4999 Z0.0000 -G1 X10.4340 Y-2.4619 Z0.0000 -G3 X12.4999 Y0.0001 Z0.0000 I-0.4340 J2.4620 -G1 X12.4999 Y9.9999 Z0.0000 -G3 X11.7677 Y11.7676 Z0.0000 I-2.5000 J0.0000 +G0 X10.0000 Y10.0000 Z15.0000 +G0 X10.0000 Y10.0000 Z10.0000 +G1 X10.0000 Y10.0000 Z9.0000 +G1 X10.0000 Y0.0000 Z9.0000 +G1 X0.0000 Y0.0000 Z9.0000 +G1 X0.0000 Y10.0000 Z9.0000 +G1 X10.0000 Y10.0000 Z9.0000 +G1 X10.0000 Y10.0000 Z8.0000 +G1 X10.0000 Y0.0000 Z8.0000 +G1 X0.0000 Y0.0000 Z8.0000 +G1 X0.0000 Y10.0000 Z8.0000 +G1 X10.0000 Y10.0000 Z8.0000 +G1 X10.0000 Y10.0000 Z7.0000 +G1 X10.0000 Y0.0000 Z7.0000 +G1 X0.0000 Y0.0000 Z7.0000 +G1 X0.0000 Y10.0000 Z7.0000 +G1 X10.0000 Y10.0000 Z7.0000 +G1 X10.0000 Y10.0000 Z6.0000 +G1 X10.0000 Y0.0000 Z6.0000 +G1 X0.0000 Y0.0000 Z6.0000 +G1 X0.0000 Y10.0000 Z6.0000 +G1 X10.0000 Y10.0000 Z6.0000 +G1 X10.0000 Y10.0000 Z5.0000 +G1 X10.0000 Y0.0000 Z5.0000 +G1 X0.0000 Y0.0000 Z5.0000 +G1 X0.0000 Y10.0000 Z5.0000 +G1 X10.0000 Y10.0000 Z5.0000 +G1 X10.0000 Y10.0000 Z4.0000 +G1 X10.0000 Y0.0000 Z4.0000 +G1 X0.0000 Y0.0000 Z4.0000 +G1 X0.0000 Y10.0000 Z4.0000 +G1 X10.0000 Y10.0000 Z4.0000 +G1 X10.0000 Y10.0000 Z3.0000 +G1 X10.0000 Y0.0000 Z3.0000 +G1 X0.0000 Y0.0000 Z3.0000 +G1 X0.0000 Y10.0000 Z3.0000 +G1 X10.0000 Y10.0000 Z3.0000 +G1 X10.0000 Y10.0000 Z2.0000 +G1 X10.0000 Y0.0000 Z2.0000 +G1 X0.0000 Y0.0000 Z2.0000 +G1 X0.0000 Y10.0000 Z2.0000 +G1 X10.0000 Y10.0000 Z2.0000 +G1 X10.0000 Y10.0000 Z1.0000 +G1 X10.0000 Y0.0000 Z1.0000 +G1 X0.0000 Y0.0000 Z1.0000 +G1 X0.0000 Y10.0000 Z1.0000 +G1 X10.0000 Y10.0000 Z1.0000 +G1 X10.0000 Y10.0000 Z0.0000 +G1 X10.0000 Y0.0000 Z0.0000 +G1 X0.0000 Y0.0000 Z0.0000 +G1 X0.0000 Y10.0000 Z0.0000 +G1 X10.0000 Y10.0000 Z0.0000 M05 G00 X-1.0 Y1.0 G17 G90 From c2d5a63e2d97cae2aa40b326fabfe95603217225 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Wed, 31 May 2017 13:52:50 -0500 Subject: [PATCH 19/20] Path: add output precision option to linuxcnc post. Modified unit tests to use it. --- src/Mod/Path/PathScripts/linuxcnc_post.py | 12 ++- src/Mod/Path/PathTests/TestPathCore.py | 3 + src/Mod/Path/PathTests/TestPathPost.py | 2 +- src/Mod/Path/PathTests/test_linuxcnc_00.ngc | 112 ++++++++++---------- 4 files changed, 70 insertions(+), 59 deletions(-) diff --git a/src/Mod/Path/PathScripts/linuxcnc_post.py b/src/Mod/Path/PathScripts/linuxcnc_post.py index 4b161c6024..b39f1ddf96 100644 --- a/src/Mod/Path/PathScripts/linuxcnc_post.py +++ b/src/Mod/Path/PathScripts/linuxcnc_post.py @@ -39,6 +39,7 @@ Arguments for linuxcnc: --comments,--no-comments ... output comments (--comments) --line-numbers,--no-line-numbers ... prefix with line numbers (--no-lin-numbers) --show-editor, --no-show-editor ... pop up editor before writing output(--show-editor) + --output-precision=4 ... number of digits of precision. Default=4 ''' import FreeCAD from FreeCAD import Units @@ -66,6 +67,7 @@ UNIT_FORMAT = 'mm/min' MACHINE_NAME = "LinuxCNC" CORNER_MIN = {'x': 0, 'y': 0, 'z': 0} CORNER_MAX = {'x': 500, 'y': 300, 'z': 300} +PRECISION=4 # Preamble text will appear at the beginning of the GCODE output file. PREAMBLE = '''G17 G90 @@ -98,6 +100,8 @@ def processArguments(argstring): global OUTPUT_COMMENTS global OUTPUT_LINE_NUMBERS global SHOW_EDITOR + global PRECISION + for arg in argstring.split(): if arg == '--header': OUTPUT_HEADER = True @@ -115,6 +119,8 @@ def processArguments(argstring): SHOW_EDITOR = True elif arg == '--no-show-editor': SHOW_EDITOR = False + elif arg.split('=')[0] == '--output-precision': + PRECISION = arg.split('=')[1] def export(objectslist, filename, argstring): processArguments(argstring) @@ -211,8 +217,10 @@ def linenumber(): return "" def parse(pathobj): + global PRECISION out = "" lastcommand = None + precision_string = '.' + str(PRECISION) +'f' # params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control # the order of parameters @@ -251,12 +259,12 @@ def parse(pathobj): if c.Name not in ["G0", "G00"]: #linuxcnc doesn't use rapid speeds speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) outstring.append( - param + format(float(speed.getValueAs(UNIT_FORMAT)), '.2f') ) + param + format(float(speed.getValueAs(UNIT_FORMAT)), precision_string) ) elif param == 'T': outstring.append(param + str(c.Parameters['T'])) else: outstring.append( - param + format(c.Parameters[param], '.4f')) + param + format(c.Parameters[param], precision_string)) # store the latest command lastcommand = command diff --git a/src/Mod/Path/PathTests/TestPathCore.py b/src/Mod/Path/PathTests/TestPathCore.py index da9ee3cfcd..77ccbb0941 100644 --- a/src/Mod/Path/PathTests/TestPathCore.py +++ b/src/Mod/Path/PathTests/TestPathCore.py @@ -109,6 +109,7 @@ G1X-0.5905Y-0.3937 G0Z0.5 ''' + output = '''G0 S3000.000000 X-0.590500 Y-0.393700 M03 G0 Z0.125000 @@ -119,6 +120,8 @@ G1 X-0.590500 Y0.433000 G1 X-0.590500 Y-0.393700 G0 Z0.500000 ''' + + #create a path directly form a piece of gcode. p = Path.Path() p.setFromGCode(lines) diff --git a/src/Mod/Path/PathTests/TestPathPost.py b/src/Mod/Path/PathTests/TestPathPost.py index f2b3739ae1..284969596a 100644 --- a/src/Mod/Path/PathTests/TestPathPost.py +++ b/src/Mod/Path/PathTests/TestPathPost.py @@ -84,7 +84,7 @@ class PathPostTestCases(unittest.TestCase): self.doc.recompute() job.PostProcessor = 'linuxcnc' - job.PostProcessorArgs = '--no-header --no-line-numbers --no-comments --no-show-editor' + job.PostProcessorArgs = '--no-header --no-line-numbers --no-comments --no-show-editor --output-precision=2' post = PathScripts.PathPost.CommandPathPost() (fail, gcode) = post.exportObjectsWith([job], job, False) diff --git a/src/Mod/Path/PathTests/test_linuxcnc_00.ngc b/src/Mod/Path/PathTests/test_linuxcnc_00.ngc index 4859ee3d72..9f3c6e3cca 100644 --- a/src/Mod/Path/PathTests/test_linuxcnc_00.ngc +++ b/src/Mod/Path/PathTests/test_linuxcnc_00.ngc @@ -2,68 +2,68 @@ G17 G90 G21 (Default_Tool) M6 T1.0 -M3 S0.0000 +M3 S0.00 (TC) M6 T2.0 -M3 S0.0000 +M3 S0.00 (Contour) (Uncompensated Tool Path) -G0 Z15.0000 +G0 Z15.00 G90 G17 -G0 X0.0000 Y0.0000 Z15.0000 -G0 X10.0000 Y10.0000 Z15.0000 -G0 X10.0000 Y10.0000 Z10.0000 -G1 X10.0000 Y10.0000 Z9.0000 -G1 X10.0000 Y0.0000 Z9.0000 -G1 X0.0000 Y0.0000 Z9.0000 -G1 X0.0000 Y10.0000 Z9.0000 -G1 X10.0000 Y10.0000 Z9.0000 -G1 X10.0000 Y10.0000 Z8.0000 -G1 X10.0000 Y0.0000 Z8.0000 -G1 X0.0000 Y0.0000 Z8.0000 -G1 X0.0000 Y10.0000 Z8.0000 -G1 X10.0000 Y10.0000 Z8.0000 -G1 X10.0000 Y10.0000 Z7.0000 -G1 X10.0000 Y0.0000 Z7.0000 -G1 X0.0000 Y0.0000 Z7.0000 -G1 X0.0000 Y10.0000 Z7.0000 -G1 X10.0000 Y10.0000 Z7.0000 -G1 X10.0000 Y10.0000 Z6.0000 -G1 X10.0000 Y0.0000 Z6.0000 -G1 X0.0000 Y0.0000 Z6.0000 -G1 X0.0000 Y10.0000 Z6.0000 -G1 X10.0000 Y10.0000 Z6.0000 -G1 X10.0000 Y10.0000 Z5.0000 -G1 X10.0000 Y0.0000 Z5.0000 -G1 X0.0000 Y0.0000 Z5.0000 -G1 X0.0000 Y10.0000 Z5.0000 -G1 X10.0000 Y10.0000 Z5.0000 -G1 X10.0000 Y10.0000 Z4.0000 -G1 X10.0000 Y0.0000 Z4.0000 -G1 X0.0000 Y0.0000 Z4.0000 -G1 X0.0000 Y10.0000 Z4.0000 -G1 X10.0000 Y10.0000 Z4.0000 -G1 X10.0000 Y10.0000 Z3.0000 -G1 X10.0000 Y0.0000 Z3.0000 -G1 X0.0000 Y0.0000 Z3.0000 -G1 X0.0000 Y10.0000 Z3.0000 -G1 X10.0000 Y10.0000 Z3.0000 -G1 X10.0000 Y10.0000 Z2.0000 -G1 X10.0000 Y0.0000 Z2.0000 -G1 X0.0000 Y0.0000 Z2.0000 -G1 X0.0000 Y10.0000 Z2.0000 -G1 X10.0000 Y10.0000 Z2.0000 -G1 X10.0000 Y10.0000 Z1.0000 -G1 X10.0000 Y0.0000 Z1.0000 -G1 X0.0000 Y0.0000 Z1.0000 -G1 X0.0000 Y10.0000 Z1.0000 -G1 X10.0000 Y10.0000 Z1.0000 -G1 X10.0000 Y10.0000 Z0.0000 -G1 X10.0000 Y0.0000 Z0.0000 -G1 X0.0000 Y0.0000 Z0.0000 -G1 X0.0000 Y10.0000 Z0.0000 -G1 X10.0000 Y10.0000 Z0.0000 +G0 X0.00 Y0.00 Z15.00 +G0 X10.00 Y10.00 Z15.00 +G0 X10.00 Y10.00 Z10.00 +G1 X10.00 Y10.00 Z9.00 +G1 X10.00 Y0.00 Z9.00 +G1 X0.00 Y0.00 Z9.00 +G1 X0.00 Y10.00 Z9.00 +G1 X10.00 Y10.00 Z9.00 +G1 X10.00 Y10.00 Z8.00 +G1 X10.00 Y0.00 Z8.00 +G1 X0.00 Y0.00 Z8.00 +G1 X0.00 Y10.00 Z8.00 +G1 X10.00 Y10.00 Z8.00 +G1 X10.00 Y10.00 Z7.00 +G1 X10.00 Y0.00 Z7.00 +G1 X0.00 Y0.00 Z7.00 +G1 X0.00 Y10.00 Z7.00 +G1 X10.00 Y10.00 Z7.00 +G1 X10.00 Y10.00 Z6.00 +G1 X10.00 Y0.00 Z6.00 +G1 X0.00 Y0.00 Z6.00 +G1 X0.00 Y10.00 Z6.00 +G1 X10.00 Y10.00 Z6.00 +G1 X10.00 Y10.00 Z5.00 +G1 X10.00 Y0.00 Z5.00 +G1 X0.00 Y0.00 Z5.00 +G1 X0.00 Y10.00 Z5.00 +G1 X10.00 Y10.00 Z5.00 +G1 X10.00 Y10.00 Z4.00 +G1 X10.00 Y0.00 Z4.00 +G1 X0.00 Y0.00 Z4.00 +G1 X0.00 Y10.00 Z4.00 +G1 X10.00 Y10.00 Z4.00 +G1 X10.00 Y10.00 Z3.00 +G1 X10.00 Y0.00 Z3.00 +G1 X0.00 Y0.00 Z3.00 +G1 X0.00 Y10.00 Z3.00 +G1 X10.00 Y10.00 Z3.00 +G1 X10.00 Y10.00 Z2.00 +G1 X10.00 Y0.00 Z2.00 +G1 X0.00 Y0.00 Z2.00 +G1 X0.00 Y10.00 Z2.00 +G1 X10.00 Y10.00 Z2.00 +G1 X10.00 Y10.00 Z1.00 +G1 X10.00 Y0.00 Z1.00 +G1 X0.00 Y0.00 Z1.00 +G1 X0.00 Y10.00 Z1.00 +G1 X10.00 Y10.00 Z1.00 +G1 X10.00 Y10.00 Z0.00 +G1 X10.00 Y0.00 Z0.00 +G1 X0.00 Y0.00 Z0.00 +G1 X0.00 Y10.00 Z0.00 +G1 X10.00 Y10.00 Z0.00 M05 G00 X-1.0 Y1.0 G17 G90 From 318db40af88d8c38ee5685e8e049ac72504f32b1 Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Sun, 21 May 2017 23:28:46 +0200 Subject: [PATCH 20/20] Fixes #0002735 --- src/Mod/Sketcher/App/SketchObject.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 55eaa58dbf..ee1780ebc8 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -960,7 +960,8 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG std::vector changed; for (int i=0; i < int(newVals.size()); i++) { if (vals[i]->First == fromGeoId && vals[i]->FirstPos == fromPosId && - !(vals[i]->Second == toGeoId && vals[i]->SecondPos == toPosId)) { + !(vals[i]->Second == toGeoId && vals[i]->SecondPos == toPosId) && + !(toGeoId < 0 && vals[i]->Second <0) ) { Constraint *constNew = newVals[i]->clone(); constNew->First = toGeoId; constNew->FirstPos = toPosId; @@ -968,7 +969,8 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG changed.push_back(constNew); } else if (vals[i]->Second == fromGeoId && vals[i]->SecondPos == fromPosId && - !(vals[i]->First == toGeoId && vals[i]->FirstPos == toPosId)) { + !(vals[i]->First == toGeoId && vals[i]->FirstPos == toPosId) && + !(toGeoId < 0 && vals[i]->First< 0)) { Constraint *constNew = newVals[i]->clone(); constNew->Second = toGeoId; constNew->SecondPos = toPosId;