From fadbd5e42439bbb54aeb53b13de42db43649ec8d Mon Sep 17 00:00:00 2001 From: sliptonic Date: Tue, 2 Aug 2016 14:38:42 -0500 Subject: [PATCH] Bug fixes: fixes reparenting bug when dressup is deleted Engrave wasn't using final depth value Dragknife Dressup not correctly calculating spin direction or curve intersection Per Yorik, fix crash if PartGui hasn't been loaded Allow selection of bottom face for profiling Bug # 0002615 --- src/Mod/Path/Gui/AppPathGui.cpp | 1 + .../Path/Gui/Resources/panels/EngraveEdit.ui | 24 ++---- src/Mod/Path/PathScripts/DragknifeDressup.py | 84 +++++++++++++------ src/Mod/Path/PathScripts/PathDressup.py | 7 +- src/Mod/Path/PathScripts/PathEngrave.py | 16 +--- src/Mod/Path/PathScripts/PathPocket.py | 2 +- src/Mod/Path/PathScripts/PathProfile.py | 2 +- 7 files changed, 77 insertions(+), 59 deletions(-) diff --git a/src/Mod/Path/Gui/AppPathGui.cpp b/src/Mod/Path/Gui/AppPathGui.cpp index 74fe4ce624..3d05456343 100644 --- a/src/Mod/Path/Gui/AppPathGui.cpp +++ b/src/Mod/Path/Gui/AppPathGui.cpp @@ -58,6 +58,7 @@ PyMODINIT_FUNC initPathGui() return; } try { + Base::Interpreter().runString("import PartGui"); Base::Interpreter().runString("import Path"); } catch(const Base::Exception& e) { diff --git a/src/Mod/Path/Gui/Resources/panels/EngraveEdit.ui b/src/Mod/Path/Gui/Resources/panels/EngraveEdit.ui index 9a5423d0c6..783ec11933 100644 --- a/src/Mod/Path/Gui/Resources/panels/EngraveEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/EngraveEdit.ui @@ -34,7 +34,7 @@ 0 0 304 - 379 + 349 @@ -123,28 +123,16 @@ Depths + + QFormLayout::AllNonFixedFieldsGrow + - mm - - - Start Depth - - - - - - - mm - - - - Final Depth @@ -205,8 +193,8 @@ 0 0 - 304 - 349 + 231 + 38 diff --git a/src/Mod/Path/PathScripts/DragknifeDressup.py b/src/Mod/Path/PathScripts/DragknifeDressup.py index d83e4653ea..03a3b1aeb8 100644 --- a/src/Mod/Path/PathScripts/DragknifeDressup.py +++ b/src/Mod/Path/PathScripts/DragknifeDressup.py @@ -25,7 +25,7 @@ import FreeCAD import FreeCADGui import Path -import PathGui +#import PathGui from PySide import QtCore, QtGui import math import DraftVecUtils as D @@ -66,35 +66,67 @@ class ObjectDressup: def __setstate__(self, state): return None - def getIncidentAngle(self, queue): - global currLocation - '''returns in the incident angle in radians between the current and previous moves''' + def shortcut(self, queue): + '''Determines whether its shorter to twist CW or CCW to align with the next move''' # get the vector of the last move + + global arccommands + if queue[1].Name in arccommands: - print queue - print currLocation - arcLoc = FreeCAD.Base.Vector(queue[2].X + queue[1].I, queue[2].Y + queue[1].J, currLocation['Z']) - radvector = queue[1].Placement.Base.sub(arcLoc) # vector of chord from center to point + arcLoc = FreeCAD.Vector(queue[2].x + queue[1].I, queue[2].y + queue[1].J, currLocation['Z']) + radvector = arcLoc.sub(queue[1].Placement.Base) #.sub(arcLoc) # vector of chord from center to point # vector of line perp to chord. - v1 = radvector.cross(FreeCAD.Base.Vector(0, 0, 1)) + v1 = radvector.cross(FreeCAD.Vector(0, 0, 1)) else: v1 = queue[1].Placement.Base.sub(queue[2].Placement.Base) # get the vector of the current move if queue[0].Name in arccommands: - arcLoc = FreeCAD.Base.Vector((queue[1].x + queue[0].I), (queue[1].y + queue[0].J), currLocation['Z']) + arcLoc = FreeCAD.Vector( (queue[1].x + queue[0].I), (queue[1].y + queue[0].J), currLocation['Z']) radvector = queue[1].Placement.Base.sub(arcLoc) # calculate arcangle - - v2 = radvector.cross(FreeCAD.Base.Vector(0, 0, 1)) - - # if switching between G2 and G3, reverse orientation - if queue[1].Name in arccommands: - if queue[0].Name != queue[1].Name: - v2 = D.rotate2D(v2, math.radians(180)) + v2 = radvector.cross(FreeCAD.Vector(0, 0, 1)) else: v2 = queue[0].Placement.Base.sub(queue[1].Placement.Base) - incident_angle = D.angle(v1, v2, FreeCAD.Base.Vector(0, 0, -1)) + if (v2.x * v1.y) - (v2.y * v1.x) >= 0: + return "CW" + else: + return "CCW" + + def segmentAngleXY(self, prevCommand, currCommand, endpos=False, currentZ=0): + '''returns in the starting angle in radians for a Path command. + requires the previous command in order to calculate arcs correctly + if endpos = True, return the angle at the end of the segment.''' + + global arccommands + if currCommand.Name in arccommands: + arcLoc = FreeCAD.Vector( (prevCommand.x + currCommand.I), (prevCommand.y + currCommand.J), currentZ) + if endpos is True: + radvector = arcLoc.sub(currCommand.Placement.Base) #Calculate vector at start of arc + else: + radvector = arcLoc.sub(prevCommand.Placement.Base) #Calculate vector at end of arc + + v1 = radvector.cross(FreeCAD.Vector(0, 0, 1)) + if currCommand.Name in ["G2", "G02"]: + v1 = D.rotate2D(v1, math.radians(180)) + else: + v1 = currCommand.Placement.Base.sub(prevCommand.Placement.Base) #Straight segments are easy + + myAngle = D.angle(v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)) + return myAngle + + def getIncidentAngle(self, queue): + # '''returns in the incident angle in radians between the current and previous moves''' + + angleatend = float(math.degrees(self.segmentAngleXY(queue[2], queue[1], True))) + if angleatend < 0: + angleatend = 360 + angleatend + angleatstart = float(math.degrees(self.segmentAngleXY(queue[1], queue[0]))) + if angleatstart < 0: + angleatstart = 360 + angleatstart + + incident_angle = angleatend-angleatstart + return incident_angle def arcExtension(self, obj, queue): @@ -184,8 +216,6 @@ class ObjectDressup: # calculate IJ offsets of twist arc from current position. offsetvector = C.sub(lastXY) - # I = offsetvector.x - # J = offsetvector.y # add G2/G3 move arcmove = Path.Command( @@ -201,12 +231,8 @@ class ObjectDressup: # The old arc move won't work so calculate a replacement command offsetv = arccenter.sub(endpointvector) - # I = offsetv.x - # J = offsetv.y - replace = Path.Command( queue[0].Name, {"X": queue[0].X, "Y": queue[0].Y, "I": offsetv.x, "J": offsetv.y}) - return (results, replace) def lineExtension(self, obj, queue): @@ -353,10 +379,11 @@ class ObjectDressup: if changedXYFlag and (len(queue) == 3): # check if the inciden angle incident exceeds the filter - incident_angle = math.degrees(self.getIncidentAngle(queue)) + incident_angle = self.getIncidentAngle(queue) if abs(incident_angle) >= obj.filterangle: - if incident_angle >= 0: + if self.shortcut(queue) == "CW": + #if incident_angle >= 0: twistCW = True else: twistCW = False @@ -434,6 +461,11 @@ class ViewProviderDressup: def __setstate__(self, state): return None + def onDelete(self, arg1=None, arg2=None): + FreeCADGui.ActiveDocument.getObject(arg1.Object.Base.Name).Visibility = True + P.addToProject(arg1.Object.Base) + return True + class CommandDragknifeDressup: diff --git a/src/Mod/Path/PathScripts/PathDressup.py b/src/Mod/Path/PathScripts/PathDressup.py index cf2d604f6e..f4ff2cb2a2 100644 --- a/src/Mod/Path/PathScripts/PathDressup.py +++ b/src/Mod/Path/PathScripts/PathDressup.py @@ -24,7 +24,7 @@ import FreeCAD import FreeCADGui import Path -import PathGui +import PathScripts.PathUtils as P from PySide import QtCore, QtGui """Path Dressup object and FreeCAD command""" @@ -99,6 +99,11 @@ class ViewProviderDressup: def __setstate__(self, state): return None + def onDelete(self, arg1=None, arg2=None): + '''this makes sure that the base operation is added back to the project and visible''' + FreeCADGui.ActiveDocument.getObject(arg1.Object.Base.Name).Visibility = True + P.addToProject(arg1.Object.Base) + return True class CommandPathDressup: diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py index 46ee968ba0..70139b040d 100644 --- a/src/Mod/Path/PathScripts/PathEngrave.py +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -65,7 +65,6 @@ class ObjectPathEngrave: # Depth Properties obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", "The height needed to clear clamps and obstructions") obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", "Rapid Safety Height between locations.") - obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", "Starting Depth of Tool- first cut depth in Z") obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", "Final Depth of Tool- lowest value in Z") obj.addProperty("App::PropertyInteger", "StartVertex", "Path", "The vertex index to start the path from") @@ -161,7 +160,7 @@ class ObjectPathEngrave: # 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) # Rapid sto starting position - output += "G1" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(last.z) + "F " + PathUtils.fmt(self.vertFeed) + "\n" # Vertical feed to depth + 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 if point == last: # edges can come flipped @@ -174,7 +173,7 @@ class ObjectPathEngrave: output += "G2" else: output += "G3" - output += " X" + PathUtils.fmt(point.x) + " Y" + PathUtils.fmt(point.y) + " Z" + PathUtils.fmt(point.z) + output += " X" + PathUtils.fmt(point.x) + " Y" + PathUtils.fmt(point.y) + " Z" + PathUtils.fmt(obj.FinalDepth.Value) output += " I" + PathUtils.fmt(relcenter.x) + " J" + PathUtils.fmt(relcenter.y) + " K" + PathUtils.fmt(relcenter.z) output += " F " + PathUtils.fmt(self.horizFeed) output += "\n" @@ -183,7 +182,7 @@ class ObjectPathEngrave: point = edge.Vertexes[-1].Point if point == last: # edges can come flipped point = edge.Vertexes[0].Point - output += "G1 X" + PathUtils.fmt(point.x) + " Y" + PathUtils.fmt(point.y) + " Z" + PathUtils.fmt(point.z) + output += "G1 X" + PathUtils.fmt(point.x) + " Y" + PathUtils.fmt(point.y) + " Z" + PathUtils.fmt(obj.FinalDepth.Value) output += " F " + PathUtils.fmt(self.horizFeed) output += "\n" last = point @@ -195,15 +194,13 @@ class ObjectPathEngrave: if len(baselist) == 0: # When adding the first base object, guess at heights try: bb = ss.Shape.BoundBox # parent boundbox - obj.StartDepth = bb.ZMax obj.ClearanceHeight = bb.ZMax + 5.0 obj.SafeHeight = bb.ZMax + 3.0 obj.FinalDepth = bb.ZMax - 1 except: - obj.StartDepth = 5.0 - obj.FinalDepth = 4.0 obj.ClearanceHeight = 10.0 obj.SafeHeight = 8.0 + obj.FinalDepth = 4.0 item = (ss, "") if item in baselist: @@ -262,7 +259,6 @@ class CommandPathEngrave: FreeCADGui.doCommand('PathScripts.PathEngrave.ObjectPathEngrave(obj)') FreeCADGui.doCommand('obj.ClearanceHeight = 10') - FreeCADGui.doCommand('obj.StartDepth= 0') FreeCADGui.doCommand('obj.FinalDepth= -0.1') FreeCADGui.doCommand('obj.SafeHeight= 5.0') FreeCADGui.doCommand('obj.Active = True') @@ -292,8 +288,6 @@ class TaskPanel: def getFields(self): if self.obj: - if hasattr(self.obj, "StartDepth"): - self.obj.StartDepth = self.form.startDepth.text() if hasattr(self.obj, "FinalDepth"): self.obj.FinalDepth = self.form.finalDepth.text() if hasattr(self.obj, "SafeHeight"): @@ -304,7 +298,6 @@ class TaskPanel: self.obj.Proxy.execute(self.obj) def setFields(self): - self.form.startDepth.setText(str(self.obj.StartDepth.Value)) self.form.finalDepth.setText(str(self.obj.FinalDepth.Value)) self.form.safeHeight.setText(str(self.obj.SafeHeight.Value)) self.form.clearanceHeight.setText(str(self.obj.ClearanceHeight.Value)) @@ -367,7 +360,6 @@ class TaskPanel: def setupUi(self): # Connect Signals and Slots - self.form.startDepth.editingFinished.connect(self.getFields) self.form.finalDepth.editingFinished.connect(self.getFields) self.form.safeHeight.editingFinished.connect(self.getFields) self.form.clearanceHeight.editingFinished.connect(self.getFields) diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py index 15cff974d8..5c1f9bd9fe 100644 --- a/src/Mod/Path/PathScripts/PathPocket.py +++ b/src/Mod/Path/PathScripts/PathPocket.py @@ -487,7 +487,7 @@ class CommandPathPocket: return {'Pixmap': 'Path-Pocket', 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Pocket"), 'Accel': "P, O", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Creates a Path Pocket object from a loop of edges or a face")} + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathPocket", "Creates a Path Pocket object from a face or faces")} def IsActive(self): return FreeCAD.ActiveDocument is not None diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py index 3a6c9407b9..559e77aba6 100644 --- a/src/Mod/Path/PathScripts/PathProfile.py +++ b/src/Mod/Path/PathScripts/PathProfile.py @@ -303,7 +303,7 @@ print "y - " + str(point.y) # we only consider the outer wire if this is a Face # Horizontal and vertical faces are handled differently shape = getattr(b[0].Shape, sub) - if numpy.isclose(shape.normalAt(0, 0).z, 1): # horizontal face + if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face hfaces.append(shape) elif numpy.isclose(shape.normalAt(0, 0).z, 0): # vertical face