From 0405a67c24a2c265c27980c8eb8b3d527380ce52 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Wed, 17 May 2017 16:12:32 -0500 Subject: [PATCH] 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])