diff --git a/src/Mod/Path/Gui/DrillingEdit.ui b/src/Mod/Path/Gui/DrillingEdit.ui new file mode 100644 index 000000000..f9411e387 --- /dev/null +++ b/src/Mod/Path/Gui/DrillingEdit.ui @@ -0,0 +1,268 @@ + + + TaskPanel + + + + 0 + 0 + 322 + 452 + + + + + 0 + 400 + + + + Drilling + + + + + + 0 + + + + true + + + + 0 + 0 + 304 + 314 + + + + + :/icons/FreeCAD-default/scalable/accessories-calculator.svg:/icons/FreeCAD-default/scalable/accessories-calculator.svg + + + Base Geometry + + + + + + Add item selected in window. + + + add + + + + + + + Drag to reorder, then update. + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + false + + + + + + + Update the path with the removed and reordered items. + + + Update + + + + + + + Remove Item selected in list, then update. + + + Remove + + + + + + + + + 0 + 0 + 116 + 108 + + + + + :/icons/FreeCAD-default/scalable/Part_Measure_Clear_All.svg:/icons/FreeCAD-default/scalable/Part_Measure_Clear_All.svg + + + Depths + + + + + + mm + + + + + + + Start Depth + + + + + + + mm + + + + + + + Final Depth + + + + + + + mm + + + + + + + Peck Depth + + + + + + + + + 0 + 0 + 150 + 108 + + + + + :/icons/FreeCAD-default/scalable/document-open.svg:/icons/FreeCAD-default/scalable/document-open.svg + + + Heights + + + + + + mm + + + + + + + Safe Height + + + + + + + mm + + + + + + + Clearance Height + + + + + + + mm + + + + + + + Retract Height + + + + + + + + + 0 + 0 + 234 + 50 + + + + + :/icons/FreeCAD-default/scalable/user.svg:/icons/FreeCAD-default/scalable/user.svg + + + Operation + + + + + + + OCC Native Outline + + + + + + + + Algorithm + + + + + + + + + + + + Gui::InputField + QLineEdit +
Gui/InputField.h
+
+
+ + + + +
diff --git a/src/Mod/Path/Gui/PocketEdit.ui b/src/Mod/Path/Gui/PocketEdit.ui new file mode 100644 index 000000000..697b3b65b --- /dev/null +++ b/src/Mod/Path/Gui/PocketEdit.ui @@ -0,0 +1,373 @@ + + + TaskPanel + + + + 0 + 0 + 352 + 525 + + + + + 0 + 400 + + + + Pocket + + + + + + 0 + + + + true + + + + 0 + 0 + 334 + 327 + + + + + :/icons/FreeCAD-default/scalable/accessories-calculator.svg:/icons/FreeCAD-default/scalable/accessories-calculator.svg + + + Base Geometry + + + + + + Drag to reorder, then update. + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + false + + + + + + + Add item selected in window. + + + add + + + + + + + Remove Item selected in list, then update. + + + Remove + + + + + + + Update the path with the removed and reordered items. + + + Update + + + + + + + All objects will be profiled using the same depth and speed settings + + + Qt::AutoText + + + true + + + + + + + + + 0 + 0 + 334 + 327 + + + + + :/icons/FreeCAD-default/scalable/Part_Measure_Clear_All.svg:/icons/FreeCAD-default/scalable/Part_Measure_Clear_All.svg + + + Depths + + + + + + mm + + + + + + + Start Depth + + + + + + + mm + + + + + + + Final Depth + + + + + + + mm + + + + + + + Finish Depth + + + + + + + 3 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Step Down + + + + + + + + + 0 + 0 + 334 + 327 + + + + + :/icons/FreeCAD-default/scalable/document-open.svg:/icons/FreeCAD-default/scalable/document-open.svg + + + Heights + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + mm + + + + + + + Safe Height + + + + + + + mm + + + + + + + Clearance Height + + + + + + + + + 0 + 0 + 334 + 327 + + + + Entry + + + + + + Pattern + + + + + + 0 + 0 + 334 + 327 + + + + + :/icons/FreeCAD-default/scalable/user.svg:/icons/FreeCAD-default/scalable/user.svg + + + Operation + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Algorithm + + + + + + + + OCC Native + + + + + libarea + + + + + + + + + + + Cut Mode + + + + + + + + Climb + + + + + Conventional + + + + + + + + + + + + + + Use Start Point + + + + + + + + + + + + + Material Allowance + + + + + + + + + + + + + + + + + + Gui::InputField + QLineEdit +
Gui/InputField.h
+
+
+ + + + +
diff --git a/src/Mod/Path/Gui/ProfileEdit.ui b/src/Mod/Path/Gui/ProfileEdit.ui new file mode 100644 index 000000000..8a968e68b --- /dev/null +++ b/src/Mod/Path/Gui/ProfileEdit.ui @@ -0,0 +1,450 @@ + + + TaskPanel + + + + 0 + 0 + 352 + 525 + + + + + 0 + 400 + + + + Profile + + + + + + 0 + + + + true + + + + 0 + 0 + 334 + 357 + + + + + :/icons/FreeCAD-default/scalable/accessories-calculator.svg:/icons/FreeCAD-default/scalable/accessories-calculator.svg + + + Base Geometry + + + + + + Drag to reorder, then update. + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + false + + + + + + + Add item selected in window. + + + add + + + + + + + Remove Item selected in list, then update. + + + Remove + + + + + + + Update the path with the removed and reordered items. + + + Update + + + + + + + All objects will be profiled using the same depth and speed settings + + + Qt::AutoText + + + true + + + + + + + + + 0 + 0 + 334 + 357 + + + + + :/icons/FreeCAD-default/scalable/Part_Measure_Clear_All.svg:/icons/FreeCAD-default/scalable/Part_Measure_Clear_All.svg + + + Depths + + + + + + mm + + + + + + + Start Depth + + + + + + + mm + + + + + + + Final Depth + + + + + + + 3 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Step Down + + + + + + + + + 0 + 0 + 334 + 357 + + + + + :/icons/FreeCAD-default/scalable/document-open.svg:/icons/FreeCAD-default/scalable/document-open.svg + + + Heights + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + mm + + + + + + + Safe Height + + + + + + + mm + + + + + + + Clearance Height + + + + + + + + Holding + + + + + + + Tag + + + + + Location + + + + + Height + + + + + Length + + + + + Angle + + + + + + + + Add New + + + + + + + Delete + + + + + + + + + 0 + 0 + 334 + 357 + + + + + :/icons/FreeCAD-default/scalable/user.svg:/icons/FreeCAD-default/scalable/user.svg + + + Operation + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Algorithm + + + + + + + + OCC Native + + + + + libarea + + + + + + + + + + + Cut Side + + + + + + + + Left + + + + + Right + + + + + On + + + + + + + + Direction + + + + + + + + CW + + + + + CCW + + + + + + + + + + + + + + Use Start Point + + + + + + + Use Compensation + + + + + + + Use End Point + + + + + + + + + + + + + Extra Offset + + + + + + + + + + Segment Length + + + + + + + + + + Roll Radius + + + + + + + + + + + + + + + + + + Gui::InputField + QLineEdit +
Gui/InputField.h
+
+
+ + + + +
diff --git a/src/Mod/Path/PathScripts/PathAreaUtils.py b/src/Mod/Path/PathScripts/PathAreaUtils.py index f88730fa4..c7e9ee7f5 100644 --- a/src/Mod/Path/PathScripts/PathAreaUtils.py +++ b/src/Mod/Path/PathScripts/PathAreaUtils.py @@ -378,12 +378,9 @@ def pocket(a,tool_radius, extra_offset, stepover, depthparams, from_center, keep use_internal_function = (area.holes_linked() == False) # use internal function, if area module is the Clipper library if use_internal_function: - print "using internal. PathAreaUtils:382 " curve_list = a.MakePocketToolpath(tool_radius, extra_offset, stepover, from_center, use_zig_zag, zig_angle) else: - print "not using internal. PathAreaUtils:386 " - global sin_angle_for_zigs global cos_angle_for_zigs global sin_minus_angle_for_zigs @@ -421,14 +418,12 @@ def pocket(a,tool_radius, extra_offset, stepover, depthparams, from_center, keep depths = depthparams.get_depths() current_start_depth = depthparams.start_depth - print "Startpoint: " + str(start_point) if start_point==None: for depth in depths: cut_curvelist1(curve_list, depthparams.rapid_safety_space, current_start_depth, depth, depthparams.clearance_height, keep_tool_down_if_poss) current_start_depth = depth else: - print "PathAreaUtils:438 I guess it IS used. Who knew?" for depth in depths: cut_curvelist2(curve_list, depthparams.rapid_safety_space, current_start_depth, depth, depthparams.clearance_height, keep_tool_down_if_poss, start_point) current_start_depth = depth diff --git a/src/Mod/Path/PathScripts/PathCompoundExtended.py b/src/Mod/Path/PathScripts/PathCompoundExtended.py index 865db491b..6a9349436 100644 --- a/src/Mod/Path/PathScripts/PathCompoundExtended.py +++ b/src/Mod/Path/PathScripts/PathCompoundExtended.py @@ -22,7 +22,7 @@ #* * #*************************************************************************** -import FreeCAD,FreeCADGui,Path,PathGui +import FreeCAD,FreeCADGui,Path,PathGui, PathUtils from PySide import QtCore,QtGui """Path Compound Extended object and FreeCAD command""" @@ -54,6 +54,13 @@ class ObjectCompoundExtended: def __setstate__(self,state): return None + def onChanged(self,obj,prop): + if prop == "Group": + print 'check order' + for child in obj.Group: + if child.isDerivedFrom("Path::Feature"): + child.touch() + def execute(self,obj): cmds = [] for child in obj.Group: diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py index d9a407210..6b5b9fe3a 100644 --- a/src/Mod/Path/PathScripts/PathDrilling.py +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -49,12 +49,13 @@ class ObjectDrilling: obj.addProperty("App::PropertyLinkSubList","Base","Path",translate("Parent Object(s)","The base geometry of this toolpath")) obj.addProperty("App::PropertyVectorList","locations","Path","The drilling locations") - obj.addProperty("App::PropertyLength", "PeckDepth", "Depth", translate("PeckDepth","Incremental Drill depth before retracting to clear chips")) - #obj.addProperty("App::PropertyFloat", "StartDepth", "Depth", translate("PathProject","Starting Depth of Tool- first cut depth in Z")) - obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Clearance Height","The height needed to clear clamps and obstructions")) - obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Final Depth","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyLength", "PeckDepth", "Depth", translate("PathProject","Incremental Drill depth before retracting to clear chips")) + obj.addProperty("App::PropertyLength", "StartDepth", "Depth", translate("PathProject","Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("PathProject","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("PathProject","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("PathProject","Height to clear top of materil")) obj.addProperty("App::PropertyDistance", "RetractHeight", "Depth", translate("Retract Height","The height where feed starts and height during retract tool when path is finished")) - obj.addProperty("App::PropertyLength", "VertFeed", "Feed",translate("Vert Feed","Feed rate for vertical moves in Z")) + #obj.addProperty("App::PropertyLength", "VertFeed", "Feed",translate("Vert Feed","Feed rate for vertical moves in Z")) obj.addProperty("App::PropertyString","Comment","Path",translate("PathProject","An optional comment for this profile")) obj.addProperty("App::PropertyBool","Active","Path",translate("Active","Make False, to prevent operation from generating code")) obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The tool number in use")) @@ -70,65 +71,75 @@ class ObjectDrilling: return None def execute(self,obj): - locations = [] - for loc in obj.Base: - - if "Face" in loc[1] or "Edge" in loc[1]: - s = getattr(loc[0].Shape,loc[1]) - else: - s = loc[0].Shape - - if s.ShapeType in ['Face', 'Wire', 'Edge' ]: - X = s.Edges[0].Curve.Center.x - Y = s.Edges[0].Curve.Center.y - Z = s.Edges[0].Curve.Center.z - elif s.ShapeType == 'Vertex': - X = s.Point.x - Y = s.Point.y - Z = s.Point.z - - locations.append(FreeCAD.Vector(X,Y,Z)) - - # tie the toolnumber to the PathLoadTool object ToolNumber - if len(obj.InList)>0: #check to see if obj is in the Project group yet - project = obj.InList[0] - tl = int(PathUtils.changeTool(obj,project)) - obj.ToolNumber= tl - - tool = PathUtils.getTool(obj,obj.ToolNumber) - - output = "G90 G98\n" - # rapid to clearance height - output += "G0 Z" + str(obj.ClearanceHeight.Value) - # rapid to first hole location, with spindle still retracted: - p0 = locations[0] - output += "G0 X"+str(p0.x) + " Y" + str(p0.y)+ "\n" - # move tool to clearance plane - output += "G0 Z" + str(obj.ClearanceHeight.Value) + "\n" - if obj.PeckDepth.Value > 0: - cmd = "G83" - qword = " Q"+ str(obj.PeckDepth.Value) + output = "" + toolLoad = PathUtils.getLastToolLoad(obj) + if toolLoad == None: + self.vertFeed = 100 + self.horizFeed = 100 + radius = 0.25 + obj.ToolNumber= 0 else: - cmd = "G81" - qword = "" - for p in locations: + self.vertFeed = toolLoad.VertFeed.Value + self.horizFeed = toolLoad.HorizFeed.Value + tool = PathUtils.getTool(obj, toolLoad.ToolNumber) + radius = tool.Diameter/2 + obj.ToolNumber= toolLoad.ToolNumber + if obj.Base: + locations = [] + for loc in obj.Base: - output += cmd + " X" + str(p.x) + " Y" + str(p.y) + " Z" + str(obj.FinalDepth.Value) + qword + " R" + str(obj.RetractHeight.Value) + " F" + str(obj.VertFeed.Value) + "\n" + if "Face" in loc[1] or "Edge" in loc[1]: + s = getattr(loc[0].Shape,loc[1]) + else: + s = loc[0].Shape - output += "G80\n" + if s.ShapeType in ['Face', 'Wire', 'Edge' ]: + X = s.Edges[0].Curve.Center.x + Y = s.Edges[0].Curve.Center.y + Z = s.Edges[0].Curve.Center.z + elif s.ShapeType == 'Vertex': + X = s.Point.x + Y = s.Point.y + Z = s.Point.z + + locations.append(FreeCAD.Vector(X,Y,Z)) + + output = "G90 G98\n" + # rapid to clearance height + output += "G0 Z" + str(obj.ClearanceHeight.Value) + # rapid to first hole location, with spindle still retracted: + p0 = locations[0] + output += "G0 X"+str(p0.x) + " Y" + str(p0.y)+ "\n" + # move tool to clearance plane + output += "G0 Z" + str(obj.ClearanceHeight.Value) + "\n" + if obj.PeckDepth.Value > 0: + cmd = "G83" + qword = " Q"+ str(obj.PeckDepth.Value) + else: + cmd = "G81" + qword = "" + for p in locations: + + output += cmd + " X" + str(p.x) + " Y" + str(p.y) + " Z" + str(obj.FinalDepth.Value) + qword + " R" + str(obj.RetractHeight.Value) + " F" + str(self.vertFeed) + "\n" + + output += "G80\n" path = Path.Path(output) obj.Path = path - # tie the toolnumber to the PathLoadTool object ToolNumber - if len(obj.InList)>0: #check to see if obj is in the Project group yet - project = obj.InList[0] - tl = int(PathUtils.changeTool(obj,project)) - obj.ToolNumber= tl -class ViewProviderDrill: + def addDrillableLocation(self, obj, ss, sub=""): + baselist = obj.Base + item = (ss, sub) + if item in baselist: + FreeCAD.Console.PrintWarning("Drillable location already in the list"+ "\n") + else: + baselist.append (item) + obj.Base = baselist + self.execute(obj) + +class _ViewProviderDrill: def __init__(self,obj): #mandatory -# obj.addProperty("App::PropertyFloat","SomePropertyName","PropertyGroup","Description of this property") obj.Proxy = self def __getstate__(self): #mandatory @@ -152,19 +163,21 @@ class ViewProviderDrill: # this is executed when a property of the APP OBJECT changes pass - def setEdit(self,vobj,mode): #optional - # this is executed when the object is double-clicked in the tree - pass + def setEdit(self,vobj,mode=0): + FreeCADGui.Control.closeDialog() + taskd = TaskPanel() + taskd.obj = vobj.Object + FreeCADGui.Control.showDialog(taskd) + taskd.setupUi() + return True + def unsetEdit(self,vobj,mode): #optional # this is executed when the user cancels or terminates edit mode pass - - class CommandPathDrilling: - def GetResources(self): return {'Pixmap' : 'Path-Drilling', 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Drilling","Drilling"), @@ -174,96 +187,8 @@ class CommandPathDrilling: def IsActive(self): return not FreeCAD.ActiveDocument is None - def Drillable(self, obj): - import Part - drillable = "" - if obj.ShapeType == 'Vertex': - drillable = 'Vertex' - elif obj.ShapeType == 'Edge': - if isinstance(obj.Curve, Part.Circle): - drillable = 'Circle' - elif obj.ShapeType == 'Face': - if isinstance(obj.Edges[0].Curve, Part.Circle): - drillable = 'Circle' - elif obj.ShapeType == 'Wire': - if isinstance(obj.Edges[0].Curve, Part.Circle): - drillable = 'Circle' - else: - drillable = None - return drillable - def Activated(self): - import Path - import Part - - from PathScripts import PathUtils,PathDrilling,PathProject - prjexists = False - - selection = FreeCADGui.Selection.getSelectionEx() - if not selection: - FreeCAD.Console.PrintError(translate("PathDrilling","Please select points or cirlces for drilling.\n")) - return - - diamcount = 0 #keep track of how many different hole sizes we're asked to deal with - lastradius = 0.0 - locations = [] - vertexcount = 0 #keep track of how many vertices - - for s in selection: - if s.HasSubObjects: - for i in s.SubObjects: - d = self.Drillable (i) - if d == 'Circle': - if i.Edges[0].Curve.Radius != lastradius: - diamcount += 1 - lastradius = i.Edges[0].Curve.Radius - elif d == 'Vertex': - vertexcount += 1 - else: - FreeCAD.Console.PrintError(translate("PathDrilling","No drillable locations were selected.\n")) - return - - #subs = [] - for n in s.SubElementNames: - # subs.append(n) - locations.append((s.ObjectName, s.Object, n)) - - else: - d = self.Drillable (s.Object.Shape) - if d == 'Circle': - if not str(s.Object.Shape.Edges[0].Curve.Radius) == str(lastradius): - diamcount += 1 - lastradius = s.Object.Shape.Edges[0].Curve.Radius - - elif d == 'Vertex': - vertexcount += 1 - else: - FreeCAD.Console.PrintError(translate("PathDrilling","No drillable locations were selected.\n")) - return - - locations.append((s.ObjectName, s.Object, [])) - - if diamcount > 1: - FreeCAD.Console.PrintError(translate("PathDrilling","Circles of different radii found. Select only one size circle at a time.\n")) - return - - if diamcount >= 1 and vertexcount >= 1: - FreeCAD.Console.PrintError(translate("PathDrilling","Please select either points or circles but not both.\n")) - return - - # Take a guess at some reasonable values for Finish depth. - if selection[0].HasSubObjects: - bb = selection[0].Object.Shape.BoundBox #parent boundbox - fbb = selection[0].SubObjects[0].BoundBox #feature boundbox - if fbb.ZMax < bb.ZMax: - zbottom = fbb.ZMax - ztop = bb.ZMax + 1 - else: - zbottom = bb.ZMin - ztop = 5 - else: - zbottom = 0 - ztop = 5 + import Path, Part # if everything is ok, execute and register the transaction in the undo/redo stack @@ -272,15 +197,10 @@ class CommandPathDrilling: FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Drilling")') FreeCADGui.doCommand('PathScripts.PathDrilling.ObjectDrilling(obj)') FreeCADGui.doCommand('obj.Active = True') - FreeCADGui.doCommand('PathScripts.PathDrilling.ViewProviderDrill(obj.ViewObject)') + FreeCADGui.doCommand('PathScripts.PathDrilling._ViewProviderDrill(obj.ViewObject)') - baselist = "[" - for loc in locations: - baselist += "(FreeCAD.ActiveDocument." + str(loc[0]) + ',"' + str(loc[2]) + '"),' - baselist += "]" - - FreeCADGui.doCommand('obj.Base = (' + baselist + ')') - FreeCADGui.doCommand('from PathScripts import PathUtils') + ztop = 10.0 + zbottom = 0.0 FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(ztop)) FreeCADGui.doCommand('obj.RetractHeight= ' + str(ztop)) FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom)) @@ -288,6 +208,147 @@ class CommandPathDrilling: FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() + FreeCADGui.doCommand('obj.ViewObject.startEditing()') + +class TaskPanel: + def __init__(self): + self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DrillingEdit.ui") + #self.form = FreeCADGui.PySideUic.loadUi(":/DrillingEdit.ui") + + def accept(self): + self.getFields() + + FreeCADGui.ActiveDocument.resetEdit() + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + FreeCADGui.Selection.removeObserver(self.s) + + def reject(self): + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + FreeCADGui.Selection.removeObserver(self.s) + + 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,"PeckDepth"): + self.obj.PeckDepth = self.form.peckDepth.text() + if hasattr(self.obj,"SafeHeight"): + self.obj.SafeHeight = self.form.safeHeight.text() + if hasattr(self.obj,"ClearanceHeight"): + self.obj.ClearanceHeight = self.form.clearanceHeight.text() + if hasattr(self.obj,"RetractHeight"): + self.obj.RetractHeight = self.form.retractHeight.text() + + self.obj.Proxy.execute(self.obj) + + def open(self): + self.s =SelObserver() + # install the function mode resident + FreeCADGui.Selection.addObserver(self.s) + + def addBase(self): + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelectionEx() + + if not len(selection) >= 1: + FreeCAD.Console.PrintError(translate("PathProject","Please select at least one Drillable Location\n")) + return + for s in selection: + if s.HasSubObjects: + for i in s.SubElementNames: + self.obj.Proxy.addDrillableLocation(self.obj, s.Object, i) + else: + self.obj.Proxy.addDrillableLocation(self.obj, s.Object) + + self.form.baseList.clear() + for i in self.obj.Base: + self.form.baseList.addItem(i[0].Name + "." + i[1]) + + + def deleteBase(self): + dlist = self.form.baseList.selectedItems() + for d in dlist: + newlist = [] + for i in self.obj.Base: + if not i[0].Name == d.text().partition(".")[0]: + newlist.append (i) + self.obj.Base = newlist + self.form.baseList.takeItem(self.form.baseList.row(d)) + # self.obj.Proxy.execute(self.obj) + # FreeCAD.ActiveDocument.recompute() + + def itemActivated(self): + FreeCADGui.Selection.clearSelection() + slist = self.form.baseList.selectedItems() + for i in slist: + objstring = i.text().partition(".") + obj = FreeCAD.ActiveDocument.getObject(objstring[0]) + # sub = o.Shape.getElement(objstring[2]) + if objstring[2] != "": + FreeCADGui.Selection.addSelection(obj,objstring[2]) + else: + FreeCADGui.Selection.addSelection(obj) + + FreeCADGui.updateGui() + + def reorderBase(self): + newlist = [] + for i in range(self.form.baseList.count()): + s = self.form.baseList.item(i).text() + objstring = s.partition(".") + + obj = FreeCAD.ActiveDocument.getObject(objstring[0]) + item = (obj, str(objstring[2])) + newlist.append(item) + self.obj.Base=newlist + + self.obj.Proxy.execute(self.obj) + FreeCAD.ActiveDocument.recompute() + + def getStandardButtons(self): + return int(QtGui.QDialogButtonBox.Ok) + + def setupUi(self): + self.form.startDepth.setText(str(self.obj.StartDepth.Value)) + self.form.finalDepth.setText(str(self.obj.FinalDepth.Value)) + self.form.peckDepth.setText(str(self.obj.PeckDepth.Value)) + self.form.safeHeight.setText(str(self.obj.SafeHeight.Value)) + self.form.clearanceHeight.setText(str(self.obj.ClearanceHeight.Value)) + self.form.retractHeight.setText(str(self.obj.RetractHeight.Value)) + + + for i in self.obj.Base: + self.form.baseList.addItem(i[0].Name + "." + i[1]) + + #Connect Signals and Slots + self.form.startDepth.editingFinished.connect(self.getFields) #This is newer syntax + #QtCore.QObject.connect(self.form.startDepth, QtCore.SIGNAL("editingFinished()"), self.getFields) + QtCore.QObject.connect(self.form.finalDepth, QtCore.SIGNAL("editingFinished()"), self.getFields) + QtCore.QObject.connect(self.form.safeHeight, QtCore.SIGNAL("editingFinished()"), self.getFields) + QtCore.QObject.connect(self.form.clearanceHeight, QtCore.SIGNAL("editingFinished()"), self.getFields) + + QtCore.QObject.connect(self.form.addBase, QtCore.SIGNAL("clicked()"), self.addBase) + QtCore.QObject.connect(self.form.deleteBase, QtCore.SIGNAL("clicked()"), self.deleteBase) + QtCore.QObject.connect(self.form.reorderBase, QtCore.SIGNAL("clicked()"), self.reorderBase) + + self.form.baseList.itemSelectionChanged.connect(self.itemActivated) + +class SelObserver: + def __init__(self): + import PathScripts.PathSelection as PST + PST.drillselect() + + def __del__(self): + import PathScripts.PathSelection as PST + PST.clear() + + def addSelection(self,doc,obj,sub,pnt): # Selection object + FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj +')') + FreeCADGui.updateGui() if FreeCAD.GuiUp: diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py index a82b1ea68..6ef7ddcef 100644 --- a/src/Mod/Path/PathScripts/PathEngrave.py +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -25,6 +25,7 @@ import FreeCAD,FreeCADGui,Path,PathGui,Draft from PySide import QtCore,QtGui +from PathScripts import PathUtils,PathProject """Path Engrave object and FreeCAD command""" @@ -46,6 +47,10 @@ class ObjectPathEngrave: obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm",translate("Path", "The library or Algorithm used to generate the path")) obj.Algorithm = ['OCC Native'] + + obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("Path","The tool number in use")) + obj.ToolNumber = (0,0,1000,1) + obj.setEditorMode('ToolNumber',1) #make this read only #Depth Properties obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Path","The height needed to clear clamps and obstructions")) @@ -54,8 +59,6 @@ class ObjectPathEngrave: obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Path","Final Depth of Tool- lowest value in Z")) obj.addProperty("App::PropertyInteger","StartVertex","Path","The vertex index to start the path from") #Feed Properties - obj.addProperty("App::PropertySpeed", "VertFeed", "Feed",translate("Path","Feed rate for vertical moves in Z")) - obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Path","Feed rate for horizontal moves")) if FreeCAD.GuiUp: _ViewProviderEngrave(obj.ViewObject) @@ -70,6 +73,20 @@ class ObjectPathEngrave: def execute(self,obj): output = "" + + toolLoad = PathUtils.getLastToolLoad(obj) + if toolLoad == None: + self.vertFeed = 100 + self.horizFeed = 100 + radius = 0.25 + obj.ToolNumber= 0 + else: + self.vertFeed = toolLoad.VertFeed.Value + self.horizFeed = toolLoad.HorizFeed.Value + tool = PathUtils.getTool(obj, toolLoad.ToolNumber) + radius = tool.Diameter/2 + obj.ToolNumber= toolLoad.ToolNumber + if obj.Base: for o in obj.Base: output += "G0 " + str(obj.ClearanceHeight.Value)+"\n" @@ -81,7 +98,6 @@ class ObjectPathEngrave: #print output if output == "": - #output += "G0 Z" + str(obj.ClearanceHeight.Value) output +="G0" path = Path.Path(output) obj.Path = path @@ -106,7 +122,7 @@ class ObjectPathEngrave: # we set the first move to our first point last = edge.Vertexes[0].Point output += "G0" + " X" + str("%f" % last.x) + " Y" + str("%f" % last.y) #Rapid sto starting position - output += "G1" + " Z" + str("%f" % last.z) +"F " + str(obj.VertFeed.Value)+ "\n" #Vertical feed to depth + output += "G1" + " Z" + str("%f" % last.z) +"F " + str(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 @@ -121,7 +137,7 @@ class ObjectPathEngrave: output += "G3" output += " X" + str("%f" % point.x) + " Y" + str("%f" % point.y) + " Z" + str("%f" % point.z) output += " I" + str("%f" % relcenter.x) + " J" + str("%f" % relcenter.y) + " K" + str("%f" % relcenter.z) - output += " F " + str(obj.HorizFeed.Value) + output += " F " + str(self.horizFeed) output += "\n" last = point else: @@ -129,7 +145,7 @@ class ObjectPathEngrave: if point == last: # edges can come flipped point = edge.Vertexes[0].Point output += "G1 X" + str("%f" % point.x) + " Y" + str("%f" % point.y) + " Z" + str("%f" % point.z) - output += " F " + str(obj.HorizFeed.Value) + output += " F " + str(self.horizFeed) output += "\n" last = point output += "G0 Z " + str(obj.SafeHeight.Value) @@ -232,10 +248,7 @@ class TaskPanel: self.obj.SafeHeight = self.form.safeHeight.text() if hasattr(self.obj,"ClearanceHeight"): self.obj.ClearanceHeight = self.form.clearanceHeight.text() - if hasattr(self.obj,"VertFeed"): - self.obj.VertFeed= self.form.vertFeed.text() - if hasattr(self.obj,"HorizFeed"): - self.obj.HorizFeed = self.form.horizFeed.text() + self.obj.Proxy.execute(self.obj) @@ -299,8 +312,7 @@ class TaskPanel: 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)) - self.form.vertFeed.setText(str(self.obj.VertFeed.Value)) - self.form.horizFeed.setText(str(self.obj.HorizFeed.Value)) + for i in self.obj.Base: self.form.baseList.addItem(i[0].Name) @@ -309,8 +321,6 @@ class TaskPanel: self.form.startDepth.editingFinished.connect(self.getFields) #This is newer syntax #QtCore.QObject.connect(self.form.startDepth, QtCore.SIGNAL("editingFinished()"), self.getFields) QtCore.QObject.connect(self.form.finalDepth, QtCore.SIGNAL("editingFinished()"), self.getFields) - QtCore.QObject.connect(self.form.horizFeed, QtCore.SIGNAL("editingFinished()"), self.getFields) - QtCore.QObject.connect(self.form.vertFeed, QtCore.SIGNAL("editingFinished()"), self.getFields) QtCore.QObject.connect(self.form.safeHeight, QtCore.SIGNAL("editingFinished()"), self.getFields) QtCore.QObject.connect(self.form.clearanceHeight, QtCore.SIGNAL("editingFinished()"), self.getFields) diff --git a/src/Mod/Path/PathScripts/PathKurveUtils.py b/src/Mod/Path/PathScripts/PathKurveUtils.py index 7520366ff..1a24d140d 100644 --- a/src/Mod/Path/PathScripts/PathKurveUtils.py +++ b/src/Mod/Path/PathScripts/PathKurveUtils.py @@ -57,8 +57,8 @@ def makeAreaCurve(edges,direction,startpt=None,endpt=None): cleanededges = Part.__sortEdges__(PathUtils.cleanedges(edges, 0.01)) - for e in cleanededges: - print str(e.valueAt(e.FirstParameter)) + "," + str(e.valueAt(e.LastParameter)) + #for e in cleanededges: + #print str(e.valueAt(e.FirstParameter)) + "," + str(e.valueAt(e.LastParameter)) edgelist=[] if len(cleanededges) == 1: #user selected a single edge. @@ -270,31 +270,12 @@ def make_smaller( curve, start = None, finish = None, end_beyond = False ): else: curve.ChangeEnd(curve.NearestPoint(finish)) -# def makePath(edges,side,radius,vertfeed,horizfeed,offset_extra,rapid_safety_space,clearance,start_depth,step_down,final_depth,use_CRC,direction,startpt=None,endpt=None): - -# curve = makeAreaCurve(edges,direction,startpt, endpt) -# path = profile(curve,side,radius,vertfeed,horizfeed,offset_extra,rapid_safety_space,clearance,start_depth,step_down,final_depth,use_CRC) -# del curve -# return path - -# def edgedumper (mylist): -# import Part, PathUtils -# print "The amazing edgedumper" -# print "mylist: " + str(mylist) -# edgs = PathUtils.cleanedges(mylist, 0.01) -# edgs = Part.Wire(edgs) -# for i in edgs.Edges: -# mystring = str(i) -# for v in i.Vertexes: -# mystring = mystring + " : " + str(v.Point) -# print mystring '''The following procedures are copied almost directly from heekscnc kurve_funcs.py. They depend on nc directory existing below PathScripts and have not been throughly optimized, understood, or tested for FreeCAD.''' def profile2(curve, direction = "on", radius = 1.0, vertfeed=0.0,horizfeed=0.0, offset_extra = 0.0, roll_radius = 2.0, roll_on = None, roll_off = None, depthparams = None, extend_at_start = 0.0, extend_at_end = 0.0, lead_in_line_len=0.0,lead_out_line_len= 0.0): - print "we're in" from PathScripts.nc.nc import * global tags direction = direction.lower() diff --git a/src/Mod/Path/PathScripts/PathLoadTool.py b/src/Mod/Path/PathScripts/PathLoadTool.py index 2d1a0668a..a622a5bd7 100644 --- a/src/Mod/Path/PathScripts/PathLoadTool.py +++ b/src/Mod/Path/PathScripts/PathLoadTool.py @@ -70,11 +70,11 @@ class LoadTool: def onChanged(self,obj,prop): mode = 2 obj.setEditorMode('Placement',mode) - if prop == "ToolNumber": - proj = PathUtils.findProj() - for g in proj.Group: - if not(isinstance(g.Proxy, PathScripts.PathLoadTool.LoadTool)): - g.touch() + #if prop == "ToolNumber": + proj = PathUtils.findProj() + for g in proj.Group: + if not(isinstance(g.Proxy, PathScripts.PathLoadTool.LoadTool)): + g.touch() class _ViewProviderLoadTool: @@ -110,9 +110,9 @@ class _ViewProviderLoadTool: vobj.setEditorMode('DisplayMode',mode) vobj.setEditorMode('BoundingBox',mode) vobj.setEditorMode('Selectable',mode) - vobj.setEditorMode('ShapeColor',mode) - vobj.setEditorMode('Transparency',mode) - vobj.setEditorMode('Visibility',mode) + #vobj.setEditorMode('ShapeColor',mode) + #vobj.setEditorMode('Transparency',mode) + #vobj.setEditorMode('Visibility',mode) def updateData(self,vobj,prop): #optional # this is executed when a property of the APP OBJECT changes @@ -137,15 +137,12 @@ class CommandPathLoadTool: return not FreeCAD.ActiveDocument is None def Activated(self): FreeCAD.ActiveDocument.openTransaction(translate("Current Tool","Tool Number to Load")) - FreeCADGui.addModule("PathScripts.PathLoadTool") snippet = ''' -import Path -import PathScripts -from PathScripts import PathProject, PathUtils -prjexists = False +import Path, PathScripts +from PathScripts import PathUtils, PathLoadTool + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Tool") PathScripts.PathLoadTool.LoadTool(obj) - PathScripts.PathLoadTool._ViewProviderLoadTool(obj.ViewObject) PathUtils.addToProject(obj) @@ -154,6 +151,18 @@ PathUtils.addToProject(obj) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() + @staticmethod + def Create(): + #FreeCADGui.addModule("PathScripts.PathLoadTool") + import Path, PathScripts, PathUtils + + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Tool") + PathScripts.PathLoadTool.LoadTool(obj) + PathScripts.PathLoadTool._ViewProviderLoadTool(obj.ViewObject) + + PathUtils.addToProject(obj) + + if FreeCAD.GuiUp: # register the FreeCAD command FreeCADGui.addCommand('Path_LoadTool', CommandPathLoadTool()) diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py index 14af80096..4ee8c193a 100644 --- a/src/Mod/Path/PathScripts/PathPocket.py +++ b/src/Mod/Path/PathScripts/PathPocket.py @@ -47,7 +47,7 @@ class ObjectPocket: def __init__(self,obj): - obj.addProperty("App::PropertyLinkSub","Base","Path",translate("PathProject","The base geometry of this object")) + obj.addProperty("App::PropertyLinkSubList","Base","Path",translate("PathProject","The base geometry of this object")) obj.addProperty("App::PropertyBool","Active","Path",translate("PathProject","Make False, to prevent operation from generating code")) obj.addProperty("App::PropertyString","Comment","Path",translate("PathProject","An optional comment for this profile")) obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm",translate("PathProject", "The library to use to generate the path")) @@ -59,29 +59,18 @@ class ObjectPocket: obj.setEditorMode('ToolNumber',1) #make this read only #Depth Properties - obj.addProperty("App::PropertyFloat", "ClearanceHeight", "Depth", translate("PathProject","The height needed to clear clamps and obstructions")) - obj.addProperty("App::PropertyFloat", "SafeHeight", "Depth", translate("PathProject","Rapid Safety Height between locations.")) + obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("PathProject","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("PathProject","Rapid Safety Height between locations.")) obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", translate("PathProject","Incremental Step Down of Tool")) obj.StepDown = (0.0, 0.01, 100.0, 0.5) - obj.addProperty("App::PropertyFloat", "StartDepth", "Depth", translate("PathProject","Starting Depth of Tool- first cut depth in Z")) - obj.addProperty("App::PropertyFloat", "FinalDepth", "Depth", translate("PathProject","Final Depth of Tool- lowest value in Z")) - obj.addProperty("App::PropertyFloat", "FinishDepth", "Depth", translate("PathProject","Maximum material removed on final pass.")) - #obj.addProperty("App::PropertyFloat", "RetractHeight", "Depth", translate("PathProject","The height desired to retract tool when path is finished")) - - # #Feed Properties - # obj.addProperty("App::PropertyFloatConstraint", "VertFeed", "Feed",translate("Vert Feed","Feed rate for vertical moves in Z")) - # obj.VertFeed = (0.0, 0.0, 100000.0, 1.0) - # obj.addProperty("App::PropertyFloatConstraint", "HorizFeed", "Feed",translate("Horiz Feed","Feed rate for horizontal moves")) - # obj.HorizFeed = (0.0, 0.0, 100000.0, 1.0) - - #Feed Properties - obj.addProperty("App::PropertySpeed", "VertFeed", "Feed",translate("Path","Feed rate for vertical moves in Z")) - obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Path","Feed rate for horizontal moves")) + obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("PathProject","Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("PathProject","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyDistance", "FinishDepth", "Depth", translate("PathProject","Maximum material removed on final pass.")) #Pocket Properties obj.addProperty("App::PropertyEnumeration", "CutMode", "Pocket",translate("PathProject", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) obj.CutMode = ['Climb','Conventional'] - obj.addProperty("App::PropertyFloat", "MaterialAllowance", "Pocket", translate("PathProject","Amount of material to leave")) + obj.addProperty("App::PropertyDistance", "MaterialAllowance", "Pocket", translate("PathProject","Amount of material to leave")) obj.addProperty("App::PropertyEnumeration", "StartAt", "Pocket",translate("PathProject", "Start pocketing at center or boundary")) obj.StartAt = ['Center', 'Edge'] obj.addProperty("App::PropertyFloatConstraint","StepOver","Pocket",translate("PathProject","Amount to step over on each pass")) @@ -122,7 +111,21 @@ class ObjectPocket: def __setstate__(self,state): return None - + + def addpocketbase(self, obj, ss, sub=""): + #sublist = [] + #sublist.append(sub) + baselist = obj.Base + if baselist == None: + baselist = [] + item = (ss, sub) + if item in baselist: + FreeCAD.Console.PrintWarning("this object already in the list"+ "\n") + else: + baselist.append (item) + obj.Base = baselist + print "this base is: " + str(baselist) + self.execute(obj) def getStock(self,obj): "retrieves a stock object from hosting project if any" @@ -144,10 +147,10 @@ class ObjectPocket: FreeCAD.Console.PrintMessage(translate("PathPocket","Generating toolpath with libarea offsets.\n")) - depthparams = depth_params (obj.ClearanceHeight, obj.SafeHeight, obj.StartDepth, obj.StepDown, obj.FinishDepth, obj.FinalDepth) + depthparams = depth_params (obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value) - horizfeed = obj.HorizFeed.Value - extraoffset = obj.MaterialAllowance + horizfeed = self.horizFeed + extraoffset = obj.MaterialAllowance.Value stepover = obj.StepOver use_zig_zag = obj.UseZigZag zig_angle = obj.ZigZagAngle @@ -159,15 +162,26 @@ class ObjectPocket: PathAreaUtils.flush_nc() PathAreaUtils.output('mem') - PathAreaUtils.feedrate_hv(obj.HorizFeed.Value, obj.VertFeed.Value) + PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed) if obj.UseStartPoint: start_point = (obj.StartPoint.x,obj.StartPoint.y) - print "a," + str(self.radius) + "," + str(extraoffset) + "," + str(stepover) + ",depthparams, " + str(from_center) + "," + str(keep_tool_down) + "," + str(use_zig_zag) + "," + str(zig_angle) + "," + str(zig_unidirectional) + "," + str(start_point) + "," + str(cut_mode) + #print "a," + str(self.radius) + "," + str(extraoffset) + "," + str(stepover) + ",depthparams, " + str(from_center) + "," + str(keep_tool_down) + "," + str(use_zig_zag) + "," + str(zig_angle) + "," + str(zig_unidirectional) + "," + str(start_point) + "," + str(cut_mode) - PathAreaUtils.pocket(a,self.radius,extraoffset, stepover,depthparams,from_center,keep_tool_down,use_zig_zag,zig_angle,zig_unidirectional,start_point,cut_mode) + 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() @@ -197,9 +211,9 @@ class ObjectPocket: global feedxy retstr = "G01 F" if(x == None) and (y == None): - retstr += str("%.4f" % obj.HorizFeed.Value) + retstr += str("%.4f" % self.horizFeed) else: - retstr += str("%.4f" % obj.VertFeed.Value) + retstr += str("%.4f" % self.vertFeed) if (x != None) or (y != None) or (z != None): if (x != None): @@ -228,7 +242,7 @@ class ObjectPocket: retstr += "G03 F" else: retstr += "G02 F" - retstr += str(obj.HorizFeed.Value) + retstr += str(self.horizFeed) #End location retstr += " X" + str("%.4f" % ex) + " Y" + str("%.4f" % ey) @@ -350,16 +364,16 @@ class ObjectPocket: # first move will be rapid, subsequent will be at feed rate first = True startPoint = None - fastZPos = max(obj.StartDepth + 2, obj.ClearanceHeight) + fastZPos = max(obj.StartDepth.Value + 2, obj.ClearanceHeight.Value) # revert the list so we start with the outer wires if obj.StartAt != 'Edge': offsets.reverse() -# print "startDepth: " + str(obj.StartDepth) -# print "finalDepth: " + str(obj.FinalDepth) +# print "startDepth: " + str(obj.StartDepth.Value) +# print "finalDepth: " + str(obj.FinalDepth.Value) # print "stepDown: " + str(obj.StepDown) -# print "finishDepth" + str(obj.FinishDepth) +# print "finishDepth" + str(obj.FinishDepth.Value) # print "offsets:", len(offsets) #Fraction of tool radius our plunge helix is to be @@ -421,7 +435,7 @@ class ObjectPocket: #FIXME: Can probably get this from the "machine"? lastZ = fastZPos - for vpos in PathUtils.frange(obj.StartDepth, obj.FinalDepth, obj.StepDown, obj.FinishDepth): + for vpos in PathUtils.frange(obj.StartDepth.Value, obj.FinalDepth.Value, obj.StepDown, obj.FinishDepth.Value): #Every for every depth we should helix down first = True # loop over successive wires @@ -483,71 +497,77 @@ class ObjectPocket: #To reload this from FreeCAD, use: import PathScripts.PathPocket; reload(PathScripts.PathPocket) def execute(self,obj): + output = "" + toolLoad = PathUtils.getLastToolLoad(obj) + if toolLoad == None: + self.vertFeed = 100 + self.horizFeed = 100 + self.radius = 0.25 + obj.ToolNumber = 0 + else: + self.vertFeed = toolLoad.VertFeed.Value + self.horizFeed = toolLoad.HorizFeed.Value + tool = PathUtils.getTool(obj, toolLoad.ToolNumber) + self.radius = tool.Diameter/2 + obj.ToolNumber= toolLoad.ToolNumber + + if obj.Base: - - import Part, PathScripts.PathKurveUtils, DraftGeomUtils - if "Face" in obj.Base[1][0]: - shape = getattr(obj.Base[0].Shape,obj.Base[1][0]) - wire = shape.OuterWire - edges = wire.Edges - else: - edges = [getattr(obj.Base[0].Shape,sub) for sub in obj.Base[1]] - print "myedges: " + str(edges) - wire = Part.Wire(edges) - shape = None - - # tie the toolnumber to the PathLoadTool object ToolNumber - if len(obj.InList)>0: #check to see if obj is in the Project group yet - project = obj.InList[0] - tl = int(PathUtils.changeTool(obj,project)) - obj.ToolNumber= tl - - tool = PathUtils.getTool(obj,obj.ToolNumber) - if tool: - self.radius = tool.Diameter/2 - else: - # temporary value,in case we don't have any tools defined already - self.radius = 0.25 - - output = "" - if obj.Algorithm == "OCC Native": - if shape == None: - shape = wire - output += self.buildpathocc(obj, shape) - else: - try: - import area - except: - FreeCAD.Console.PrintError(translate("PathKurve","libarea needs to be installed for this command to work.\n")) - return - - a = area.Area() - if shape == None: - c = PathScripts.PathKurveUtils.makeAreaCurve(wire.Edges, 'CW') - a.append(c) + for b in obj.Base: + print "object base: " + str(b) + import Part, PathScripts.PathKurveUtils, DraftGeomUtils + if "Face" in b[1]: + print "inside" + shape = getattr(b[0].Shape,b[1]) + wire = shape.OuterWire + edges = wire.Edges else: - for w in shape.Wires: - c = PathScripts.PathKurveUtils.makeAreaCurve(w.Edges, 'CW') - # if w.isSame(shape.OuterWire): - # print "outerwire" - # if c.IsClockwise(): - # c.Reverse() - # print "reverse outterwire" - # else: - # print "inner wire" - # if not c.IsClockwise(): - # c.Reverse() - # print "reverse inner" - a.append(c) + print "in else" + edges = [getattr(b[0].Shape,sub) for sub in b[1]] + print "myedges: " + str(edges) + wire = Part.Wire(edges) + shape = None - ######## - ##This puts out some interesting information from libarea - print a.text() - ######## - - - a.Reorder() - output += self.buildpathlibarea(obj, a) + + # output = "" + if obj.Algorithm == "OCC Native": + if shape == None: + shape = wire + output += self.buildpathocc(obj, shape) + else: + try: + import area + except: + FreeCAD.Console.PrintError(translate("PathKurve","libarea needs to be installed for this command to work.\n")) + return + + a = area.Area() + if shape == None: + c = PathScripts.PathKurveUtils.makeAreaCurve(wire.Edges, 'CW') + a.append(c) + else: + for w in shape.Wires: + c = PathScripts.PathKurveUtils.makeAreaCurve(w.Edges, 'CW') + # if w.isSame(shape.OuterWire): + # print "outerwire" + # if c.IsClockwise(): + # c.Reverse() + # print "reverse outterwire" + # else: + # print "inner wire" + # if not c.IsClockwise(): + # c.Reverse() + # print "reverse inner" + a.append(c) + + ######## + ##This puts out some interesting information from libarea + print a.text() + ######## + + + a.Reorder() + output += self.buildpathlibarea(obj, a) if obj.Active: @@ -586,14 +606,11 @@ class ViewProviderPocket: return def setEdit(self,vobj,mode=0): - import PocketEdit FreeCADGui.Control.closeDialog() - taskd = QtGui.QWidget() - taskd = PocketEdit.Ui_Dialog() + taskd = TaskPanel() taskd.obj = vobj.Object - #taskd.update() FreeCADGui.Control.showDialog(taskd) - + taskd.setupUi() return True def getIcon(self): @@ -621,37 +638,40 @@ class CommandPathPocket: def Activated(self): # check that the selection contains exactly what we want - selection = FreeCADGui.Selection.getSelectionEx() - if len(selection) != 1: - FreeCAD.Console.PrintError(translate("PathPocket","Please select an edges loop from one object, or a single face\n")) - return - if len(selection[0].SubObjects) == 0: - FreeCAD.Console.PrintError(translate("PathPocket","Please select an edges loop from one object, or a single face\n")) - return - for s in selection[0].SubObjects: - if s.ShapeType != "Edge": - if (s.ShapeType != "Face") or (len(selection[0].SubObjects) != 1): - FreeCAD.Console.PrintError(translate("PathPocket","Please select only edges or a single face\n")) - return - if selection[0].SubObjects[0].ShapeType == "Edge": - try: - import Part - w = Part.Wire(selection[0].SubObjects) - if w.isClosed() == False: - FreeCAD.Console.PrintError(translate("PathPocket","The selected edges don't form a loop\n")) - return + # selection = FreeCADGui.Selection.getSelectionEx() + # if len(selection) != 1: + # FreeCAD.Console.PrintError(translate("PathPocket","Please select an edges loop from one object, or a single face\n")) + # return + # if len(selection[0].SubObjects) == 0: + # FreeCAD.Console.PrintError(translate("PathPocket","Please select an edges loop from one object, or a single face\n")) + # return + # for s in selection[0].SubObjects: + # if s.ShapeType != "Edge": + # if (s.ShapeType != "Face") or (len(selection[0].SubObjects) != 1): + # FreeCAD.Console.PrintError(translate("PathPocket","Please select only edges or a single face\n")) + # return + # if selection[0].SubObjects[0].ShapeType == "Edge": + # try: + # import Part + # w = Part.Wire(selection[0].SubObjects) + # if w.isClosed() == False: + # FreeCAD.Console.PrintError(translate("PathPocket","The selected edges don't form a loop\n")) + # return - except: - FreeCAD.Console.PrintError(translate("PathPocket","The selected edges don't form a loop\n")) - return + # except: + # FreeCAD.Console.PrintError(translate("PathPocket","The selected edges don't form a loop\n")) + # return # Take a guess at some reasonable values for Finish depth. - bb = selection[0].Object.Shape.BoundBox #parent boundbox - fbb = selection[0].SubObjects[0].BoundBox #feature boundbox - if fbb.ZMax < bb.ZMax: - zbottom = fbb.ZMax - else: - zbottom = bb.ZMin + # bb = selection[0].Object.Shape.BoundBox #parent boundbox + # fbb = selection[0].SubObjects[0].BoundBox #feature boundbox + # if fbb.ZMax < bb.ZMax: + # zbottom = fbb.ZMax + # else: + # zbottom = bb.ZMin + + zbottom = 0.0 + ztop = 10.0 # if everything is ok, execute and register the transaction in the undo/redo stack FreeCAD.ActiveDocument.openTransaction(translate("PathPocket","Create Pocket")) @@ -663,19 +683,13 @@ class CommandPathPocket: FreeCADGui.doCommand('obj.Active = True') FreeCADGui.doCommand('PathScripts.PathPocket.ViewProviderPocket(obj.ViewObject)') - subs = "[" - for s in selection[0].SubElementNames: - subs += '"' + s + '",' - subs += "]" - FreeCADGui.doCommand('obj.Base = (FreeCAD.ActiveDocument.' + selection[0].ObjectName + ',' + subs + ')') - FreeCADGui.doCommand('from PathScripts import PathUtils') FreeCADGui.doCommand('obj.StepOver = 1.0') - FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(bb.ZMax + 2.0)) + FreeCADGui.doCommand('obj.ClearanceHeight = 10')# + str(bb.ZMax + 2.0)) FreeCADGui.doCommand('obj.StepDown = 1.0') - FreeCADGui.doCommand('obj.StartDepth= ' + str(bb.ZMax)) + FreeCADGui.doCommand('obj.StartDepth= ' + str(ztop)) FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom)) FreeCADGui.doCommand('obj.ZigZagAngle=45') FreeCADGui.doCommand('obj.UseEntry=True') @@ -687,7 +701,172 @@ class CommandPathPocket: FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() + FreeCADGui.doCommand('obj.ViewObject.startEditing()') +class TaskPanel: + def __init__(self): + self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/PocketEdit.ui") + #self.form = FreeCADGui.PySideUic.loadUi(":/PocketEdit.ui") + self.updating = False + + + def accept(self): + self.getFields() + + FreeCADGui.ActiveDocument.resetEdit() + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + FreeCADGui.Selection.removeObserver(self.s) + + def reject(self): + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + FreeCADGui.Selection.removeObserver(self.s) + + 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"): + self.obj.SafeHeight = self.form.safeHeight.text() + if hasattr(self.obj,"ClearanceHeight"): + self.obj.ClearanceHeight = self.form.clearanceHeight.text() + if hasattr(self.obj,"StepDown"): + self.obj.StepDown = self.form.stepDown.value() + if hasattr(self.obj,"MaterialAllowance"): + self.obj.MaterialAllowance = self.form.extraOffset.value() + if hasattr(self.obj,"UseStartPoint"): + self.obj.UseStartPoint = self.form.useStartPoint.isChecked() + if hasattr(self.obj,"Algorithm"): + self.obj.Algorithm = str(self.form.algorithmSelect.currentText()) + if hasattr(self.obj,"CutMode"): + self.obj.CutMode = str(self.form.cutMode.currentText()) + self.obj.Proxy.execute(self.obj) + + def open(self): + self.s =SelObserver() + # install the function mode resident + FreeCADGui.Selection.addObserver(self.s) + + def addBase(self): + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelectionEx() + + if not len(selection) >= 1: + FreeCAD.Console.PrintError(translate("PathProject","Please select at least one profileable object\n")) + return + for s in selection: + if s.HasSubObjects: + for i in s.SubElementNames: + self.obj.Proxy.addpocketbase(self.obj, s.Object, i) + else: + self.obj.Proxy.addpocketbase(self.obj, s.Object) + + self.form.baseList.clear() + for i in self.obj.Base: + self.form.baseList.addItem(i[0].Name + "." + i[1]) + + def deleteBase(self): + dlist = self.form.baseList.selectedItems() + newlist = [] + for d in dlist: + for i in self.obj.Base: + if i[0].Name != d.text().partition(".")[0] or i[1] != d.text().partition(".")[2] : + newlist.append (i) + self.form.baseList.takeItem(self.form.baseList.row(d)) + self.obj.Base = newlist + self.obj.Proxy.execute(self.obj) + FreeCAD.ActiveDocument.recompute() + + def itemActivated(self): + FreeCADGui.Selection.clearSelection() + slist = self.form.baseList.selectedItems() + for i in slist: + objstring = i.text().partition(".") + obj = FreeCAD.ActiveDocument.getObject(objstring[0]) + # sub = o.Shape.getElement(objstring[2]) + if objstring[2] != "": + FreeCADGui.Selection.addSelection(obj,objstring[2]) + else: + FreeCADGui.Selection.addSelection(obj) + + FreeCADGui.updateGui() + + def reorderBase(self): + newlist = [] + for i in range(self.form.baseList.count()): + s = self.form.baseList.item(i).text() + objstring = s.partition(".") + + obj = FreeCAD.ActiveDocument.getObject(objstring[0]) + item = (obj, str(objstring[2])) + newlist.append(item) + self.obj.Base=newlist + + self.obj.Proxy.execute(self.obj) + FreeCAD.ActiveDocument.recompute() + + def getStandardButtons(self): + return int(QtGui.QDialogButtonBox.Ok) + + + def edit(self,item,column): + if not self.updating: + self.resetObject() + + def setupUi(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)) + self.form.stepDown.setValue(self.obj.StepDown) + self.form.extraOffset.setValue(self.obj.MaterialAllowance.Value) + self.form.useStartPoint.setChecked(self.obj.UseStartPoint) + + index = self.form.algorithmSelect.findText(self.obj.Algorithm, QtCore.Qt.MatchFixedString) + if index >= 0: + self.form.algorithmSelect.setCurrentIndex(index) + + for i in self.obj.Base: + self.form.baseList.addItem(i[0].Name + "." + i[1]) + + #Connect Signals and Slots + #Base Controls + self.form.baseList.itemSelectionChanged.connect(self.itemActivated) + self.form.addBase.clicked.connect(self.addBase) + self.form.deleteBase.clicked.connect(self.deleteBase) + self.form.reorderBase.clicked.connect(self.reorderBase) + + #Depths + self.form.startDepth.editingFinished.connect(self.getFields) + self.form.finalDepth.editingFinished.connect(self.getFields) + self.form.stepDown.editingFinished.connect(self.getFields) + + #Heights + self.form.safeHeight.editingFinished.connect(self.getFields) + self.form.clearanceHeight.editingFinished.connect(self.getFields) + + #operation + self.form.algorithmSelect.currentIndexChanged.connect(self.getFields) + self.form.cutMode.currentIndexChanged.connect(self.getFields) + self.form.useStartPoint.clicked.connect(self.getFields) + self.form.extraOffset.editingFinished.connect(self.getFields) + + +class SelObserver: + def __init__(self): + import PathScripts.PathSelection as PST + PST.pocketselect() + + def __del__(self): + import PathScripts.PathSelection as PST + PST.clear() + + def addSelection(self,doc,obj,sub,pnt): # Selection object + FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj +')') + FreeCADGui.updateGui() if FreeCAD.GuiUp: # register the FreeCAD command diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py index 863a0ffbf..aeea2a426 100644 --- a/src/Mod/Path/PathScripts/PathProfile.py +++ b/src/Mod/Path/PathScripts/PathProfile.py @@ -24,13 +24,14 @@ import FreeCAD,Path from FreeCAD import Vector -from PathScripts import PathUtils,PathSelection,PathProject +from PathScripts import PathUtils #,PathSelection,PathProject +from PathScripts.PathUtils import depth_params if FreeCAD.GuiUp: - import FreeCADGui, PathGui + import FreeCADGui from PySide import QtCore, QtGui from DraftTools import translate - from pivy import coin + #from pivy import coin else: def translate(ctxt,txt): return txt @@ -50,38 +51,15 @@ except AttributeError: def translate(context, text, disambig=None): return QtGui.QApplication.translate(context, text, disambig) -def makeProfile(self, name="Profile"): - '''creates a Profile operation''' - - #obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",name) - #obj.Label = translate("Path",name) - obj=self - ObjectProfile(obj) - if FreeCAD.GuiUp: - _ViewProviderProfile(obj.ViewObject) - - locations = [] - angles = [] - lengths = [] - heights = [] - - obj.locs = locations - obj.angles = angles - obj.lengths = lengths - obj.heights = heights - FreeCAD.ActiveDocument.recompute() - return obj - - class ObjectProfile: def __init__(self,obj): - obj.addProperty("App::PropertyLinkSub","Base","Path",translate("Parent Object","The base geometry of this toolpath")) + obj.addProperty("App::PropertyLinkSubList","Base","Path",translate("Parent Object","The base geometry of this toolpath")) obj.addProperty("App::PropertyBool","Active","Path",translate("Path","Make False, to prevent operation from generating code")) obj.addProperty("App::PropertyString","Comment","Path",translate("Path","An optional comment for this profile")) obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm",translate("Path", "The library or algorithm used to generate the path")) - obj.Algorithm = ['OCC Native','libarea'] + obj.Algorithm = ['OCC Native','libareal'] obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("Path","The tool number in use")) obj.ToolNumber = (0,0,1000,1) @@ -94,12 +72,7 @@ class ObjectProfile: obj.StepDown = (1,0.01,1000,0.5) obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("Path","Starting Depth of Tool- first cut depth in Z")) obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Path","Final Depth of Tool- lowest value in Z")) - #obj.addProperty("App::PropertyDistance", "RetractHeight", "Depth", translate("Retract Height","The height desired to retract tool when path is finished")) - - #Feed Properties - obj.addProperty("App::PropertySpeed", "VertFeed", "Feed",translate("Path","Feed rate for vertical moves in Z")) - obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Path","Feed rate for horizontal moves")) - + #Start Point Properties obj.addProperty("App::PropertyVector","StartPoint","Start Point",translate("Path_Profile","The start point of this path")) obj.addProperty("App::PropertyBool","UseStartPoint","Start Point",translate("Path_Profile","make True, if specifying a Start Point")) @@ -128,6 +101,15 @@ class ObjectProfile: obj.addProperty("App::PropertyFloatList","angles","Tags", translate("Path_Profile", "List of angles for the holding tags")) obj.addProperty("App::PropertyFloatList","heights","Tags", translate("Path_Profile", "List of angles for the holding tags")) obj.addProperty("App::PropertyFloatList","lengths","Tags", translate("Path_Profile", "List of angles for the holding tags")) + locations = [] + angles = [] + lengths = [] + heights = [] + + obj.locs = locations + obj.angles = angles + obj.lengths = lengths + obj.heights = heights obj.Proxy = self @@ -137,142 +119,176 @@ class ObjectProfile: def __setstate__(self,state): return None - def execute(self,obj): - import Part, DraftGeomUtils, math + def addprofilebase(self, obj, ss, sub=""): + baselist = obj.Base + item = (ss, sub) + if item in baselist: + FreeCAD.Console.PrintWarning("this object already in the list"+ "\n") + else: + baselist.append (item) + obj.Base = baselist + self.execute(obj) + + def _buildPathOCC(self,obj,wire): + import DraftGeomUtils + output = "" + output += '('+ str(obj.Comment)+')\n' + + if obj.Direction == 'CCW': + clockwise=False + else: + clockwise=True + + FirstEdge= None + PathClosed = DraftGeomUtils.isReallyClosed(wire) + + output += PathUtils.MakePath(wire, \ + obj.Side, \ + self.radius, \ + clockwise, \ + obj.ClearanceHeight.Value, \ + obj.StepDown, \ + obj.StartDepth.Value, \ + obj.FinalDepth.Value, \ + FirstEdge, \ + PathClosed, \ + obj.SegLen.Value, \ + self.vertFeed, \ + self.horizFeed) + + return output + + def _buildPathLibarea(self,obj, edgelist): import PathScripts.PathKurveUtils as PathKurveUtils - from PathScripts.PathUtils import depth_params + import math, area + output = "" + + 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) + curve = PathKurveUtils.makeAreaCurve(edgelist,obj.Direction,startpoint, endpoint) + + '''The following line uses a profile function written for use with FreeCAD. It's clean but incomplete. It doesn't handle +print "x = " + str(point.x) +print "y - " + str(point.y) + holding tags + start location + CRC + or probably other features in heekscnc''' + #output += PathKurveUtils.profile(curve, side, radius, vf, hf, offset_extra, rapid_safety_space, clearance, start_depth, step_down, final_depth, use_CRC) + + '''The following calls the original procedure from heekscnc profile function. This, in turn, calls many other procedures to modify the profile. + This procedure is hacked together from heekscnc and has not been thoroughly reviewed or understood for FreeCAD. It can probably be + thoroughly optimized and improved but it'll take a smarter mind than mine to do it. -sliptonic Feb16''' + roll_radius = 2.0 + extend_at_start = 0.0 + extend_at_end = 0.0 + lead_in_line_len=0.0 + lead_out_line_len= 0.0 + + ''' + + Right here, I need to know the Holding Tags group from the tree that refers to this profile operation and build up the tags for PathKurve Utils. + I need to access the location vector, length, angle in radians and height. + + ''' + PathKurveUtils.clear_tags() + for i in range(len(obj.locs)): + tag = obj.locs[i] + h = obj.heights[i] + l = obj.lengths[i] + a = math.radians(obj.angles[i]) + PathKurveUtils.add_tag(area.Point(tag.x,tag.y), l, a, h) + + depthparams = depth_params (obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, 0.0, obj.FinalDepth.Value, None) + + PathKurveUtils.profile2(curve, \ + obj.Side, \ + self.radius, \ + self.vertFeed, \ + self.horizFeed, \ + 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): + import Part #math #DraftGeomUtils + output = "" + toolLoad = PathUtils.getLastToolLoad(obj) + if toolLoad == None: + self.vertFeed = 100 + self.horizFeed = 100 + self.radius = 0.25 + obj.ToolNumber= 0 + else: + self.vertFeed = toolLoad.VertFeed.Value + self.horizFeed = toolLoad.HorizFeed.Value + tool = PathUtils.getTool(obj, toolLoad.ToolNumber) + self.radius = tool.Diameter/2 + obj.ToolNumber= toolLoad.ToolNumber if obj.Base: - # tie the toolnumber to the PathLoadTool object ToolNumber - if len(obj.InList)>0: #check to see if obj is in the Project group yet - project = obj.InList[0] - tl = int(PathUtils.changeTool(obj,project)) - obj.ToolNumber= tl - - tool = PathUtils.getTool(obj,obj.ToolNumber) - if tool: - radius = tool.Diameter/2 - else: - # temporary value,in case we don't have any tools defined already - radius = 0.25 - - depthparams = depth_params (obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, 0.0, obj.FinalDepth.Value, None) - - clearance = obj.ClearanceHeight.Value - step_down=obj.StepDown - start_depth=obj.StartDepth.Value - final_depth=obj.FinalDepth.Value - rapid_safety_space=obj.SafeHeight.Value - - side=obj.Side - offset_extra=obj.OffsetExtra.Value - use_CRC=obj.UseComp - vf=obj.VertFeed.Value - hf=obj.HorizFeed.Value - seglen=obj.SegLen.Value - direction = obj.Direction - - + for b in obj.Base: + # we only consider the outer wire if this is a Face - shape = getattr(obj.Base[0].Shape,obj.Base[1][0]) - if shape.ShapeType in ["Edge"]: - edges = [getattr(obj.Base[0].Shape,sub) for sub in obj.Base[1]] - wire = Part.Wire(edges) - - if not wire.Edges[0].isSame(shape): - wire.Edges.reverse() + #shape = getattr(obj.Base[0][0].Shape,obj.Base[0][1]) + shape = getattr(b[0].Shape,b[1]) - else: - wire = shape.OuterWire - - edgelist = wire.Edges - #edgelist = Part.__sortEdges__(wire.Edges) - - if obj.StartPoint and obj.UseStartPoint: - startpoint = obj.StartPoint - else: - startpoint = None - - if obj.EndPoint and obj.UseEndPoint: - endpoint = obj.EndPoint - else: - endpoint = None + if shape.ShapeType in ["Edge"]: + edges = [getattr(obj.Base[0].Shape,sub) for sub in obj.Base[1]] + wire = Part.Wire(edges) - edgelist = Part.__sortEdges__(edgelist) - - if obj.Algorithm == "OCC Native": - output = "" - output += '('+ str(obj.Comment)+')\n' + if not wire.Edges[0].isSame(shape): + wire.Edges.reverse() - if obj.Direction == 'CCW': - clockwise=False else: - clockwise=True + wire = shape.OuterWire - FirstEdge= None - PathClosed = DraftGeomUtils.isReallyClosed(wire) + edgelist = wire.Edges + edgelist = Part.__sortEdges__(edgelist) + + if obj.Algorithm == "OCC Native": + output += self._buildPathOCC(obj, wire) + + else: + try: + import area + except: + FreeCAD.Console.PrintError(translate("Path","libarea needs to be installed for this command to work.\n")) + return + output += self._buildPathLibarea(obj,edgelist) - output += PathUtils.MakePath(wire, side, radius, clockwise, clearance, step_down, start_depth, final_depth, FirstEdge, PathClosed, seglen, vf, hf) - - else: - try: - import area - except: - FreeCAD.Console.PrintError(translate("Path","libarea needs to be installed for this command to work.\n")) - return - - PathKurveUtils.output('mem') - PathKurveUtils.feedrate_hv(obj.HorizFeed.Value, obj.VertFeed.Value) - - output = "" - output += "G0 Z" + str(clearance) - curve = PathKurveUtils.makeAreaCurve(edgelist,direction,startpoint, endpoint) - - '''The following line uses a profile function written for use with FreeCAD. It's clean but incomplete. It doesn't handle - print "x = " + str(point.x) - print "y - " + str(point.y) - holding tags - start location - CRC - or probably other features in heekscnc''' - #output += PathKurveUtils.profile(curve, side, radius, vf, hf, offset_extra, rapid_safety_space, clearance, start_depth, step_down, final_depth, use_CRC) - - '''The following calls the original procedure from heekscnc profile function. This, in turn, calls many other procedures to modify the profile. - This procedure is hacked together from heekscnc and has not been thoroughly reviewed or understood for FreeCAD. It can probably be - thoroughly optimized and improved but it'll take a smarter mind than mine to do it. -sliptonic Feb16''' - roll_radius = 2.0 - extend_at_start = 0.0 - extend_at_end = 0.0 - lead_in_line_len=0.0 - lead_out_line_len= 0.0 - - ''' - - Right here, I need to know the Holding Tags group from the tree that refers to this profile operation and build up the tags for PathKurve Utils. - I need to access the location vector, length, angle in radians and height. - - ''' - PathKurveUtils.clear_tags() - for i in range(len(obj.locs)): - tag = obj.locs[i] - h = obj.heights[i] - l = obj.lengths[i] - a = math.radians(obj.angles[i]) - PathKurveUtils.add_tag(area.Point(tag.x,tag.y), l, a, h) - - PathKurveUtils.profile2(curve, side, radius, vf, hf, offset_extra, roll_radius, None,None, depthparams, extend_at_start, extend_at_end, lead_in_line_len,lead_out_line_len) - output += PathKurveUtils.retrieve_gcode() - - if obj.Active: - path = Path.Path(output) - obj.Path = path - obj.ViewObject.Visibility = True - - else: - path = Path.Path("(inactive operation)") - obj.Path = path - obj.ViewObject.Visibility = False + if obj.Active: + path = Path.Path(output) + obj.Path = path + obj.ViewObject.Visibility = True + else: + path = Path.Path("(inactive operation)") + obj.Path = path + obj.ViewObject.Visibility = False class _ViewProviderProfile: @@ -285,10 +301,10 @@ class _ViewProviderProfile: def setEdit(self,vobj,mode=0): FreeCADGui.Control.closeDialog() - taskd = _EditPanel() + taskd = TaskPanel() taskd.obj = vobj.Object - taskd.update() FreeCADGui.Control.showDialog(taskd) + taskd.setupUi() return True def getIcon(self): @@ -332,8 +348,6 @@ class _CommandAddTag: obj.lengths = l obj.angles = a - - def Activated(self): FreeCADGui.Snapper.getPoint(callback=self.setpoint) @@ -384,60 +398,26 @@ class CommandPathProfile: return not FreeCAD.ActiveDocument is None def Activated(self): - import Path - from PathScripts import PathProject, PathUtils, PathKurveUtils + #import Path + #from PathScripts import PathProject, PathUtils, PathKurveUtils - # check that the selection contains exactly what we want - selection = FreeCADGui.Selection.getSelectionEx() - if len(selection) != 1: - FreeCAD.Console.PrintError(translate("Path","Select one or more edges or a face from one Document object.\n")) - return - if len(selection[0].SubObjects) == 0: - FreeCAD.Console.PrintError(translate("Path","Select one or more edges or a face from one Document object.\n")) - return - for s in selection[0].SubObjects: - if s.ShapeType != "Edge": - if (s.ShapeType != "Face") or (len(selection[0].SubObjects) != 1): - FreeCAD.Console.PrintError(translate("Path","Please select only edges or a single face\n")) - return - if selection[0].SubObjects[0].ShapeType == "Edge": - try: - import Part - w = Part.Wire(selection[0].SubObjects) - except: - FreeCAD.Console.PrintError(translate("Path","The selected edges don't form a loop\n")) - return + ztop = 10.0 + zbottom = 0.0 - # if everything is ok, execute and register the transaction in the undo/redo stack - - # Take a guess at some reasonable values for Finish depth. - bb = selection[0].Object.Shape.BoundBox #parent boundbox - fbb = selection[0].SubObjects[0].BoundBox #feature boundbox - if fbb.ZMax < bb.ZMax: - zbottom = fbb.ZMax - else: - zbottom = bb.ZMin - - FreeCAD.ActiveDocument.openTransaction(translate("Path","Create a Profile operation using libarea")) + FreeCAD.ActiveDocument.openTransaction(translate("Path","Create a Profile")) FreeCADGui.addModule("PathScripts.PathProfile") FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Profile")') - FreeCADGui.doCommand('PathScripts.PathProfile.makeProfile(obj)') - #FreeCADGui.doCommand('PathScripts.PathProfile.ObjectProfile(obj)') + FreeCADGui.doCommand('PathScripts.PathProfile.ObjectProfile(obj)') + FreeCADGui.doCommand('PathScripts.PathProfile._ViewProviderProfile(obj.ViewObject)') FreeCADGui.doCommand('obj.Active = True') - subs ="[" - for s in selection[0].SubElementNames: - subs += '"' + s + '",' - subs += "]" - FreeCADGui.doCommand('obj.Base = (FreeCAD.ActiveDocument.' + selection[0].ObjectName + ',' + subs + ')') - #FreeCADGui.doCommand('obj.ViewObject.Proxy = 0') - FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(bb.ZMax + 10.0)) + FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(ztop + 10.0)) FreeCADGui.doCommand('obj.StepDown = 1.0') - FreeCADGui.doCommand('obj.StartDepth= ' + str(bb.ZMax)) + FreeCADGui.doCommand('obj.StartDepth= ' + str(ztop)) FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom)) - FreeCADGui.doCommand('obj.SafeHeight = '+ str(bb.ZMax + 2.0)) + FreeCADGui.doCommand('obj.SafeHeight = '+ str(ztop + 2.0)) FreeCADGui.doCommand('obj.Side = "Left"') FreeCADGui.doCommand('obj.OffsetExtra = 0.0') FreeCADGui.doCommand('obj.Direction = "CW"') @@ -446,100 +426,126 @@ class CommandPathProfile: FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() + FreeCADGui.doCommand('obj.ViewObject.startEditing()') -class _EditPanel: - '''The editmode TaskPanel for profile tags''' +class TaskPanel: def __init__(self): - # the panel has a tree widget that contains categories - # for the subcomponents, such as additions, subtractions. - # the categories are shown only if they are not empty. - + self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/ProfileEdit.ui") + #self.form = FreeCADGui.PySideUic.loadUi(":/ProfileEdit.ui") self.updating = False - self.obj = None - self.form = QtGui.QWidget() - self.form.setObjectName("TaskPanel") - self.grid = QtGui.QGridLayout(self.form) - self.grid.setObjectName("grid") - self.title = QtGui.QLabel(self.form) - self.grid.addWidget(self.title, 0, 0, 1, 2) - # tree - self.tree = QtGui.QTreeWidget(self.form) - self.grid.addWidget(self.tree, 1, 0, 1, 2) - self.tree.setColumnCount(4) - self.tree.header().resizeSection(0,50) - self.tree.header().resizeSection(1,80) - self.tree.header().resizeSection(2,60) - self.tree.header().resizeSection(3,60) + def accept(self): + self.getFields() - # buttons - self.addButton = QtGui.QPushButton(self.form) - self.addButton.setObjectName("addButton") - self.addButton.setIcon(QtGui.QIcon(":/icons/Arch_Add.svg")) - self.grid.addWidget(self.addButton, 3, 0, 1, 1) - self.addButton.setEnabled(True) + FreeCADGui.ActiveDocument.resetEdit() + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + FreeCADGui.Selection.removeObserver(self.s) - self.delButton = QtGui.QPushButton(self.form) - self.delButton.setObjectName("delButton") - self.delButton.setIcon(QtGui.QIcon(":/icons/Arch_Remove.svg")) - self.grid.addWidget(self.delButton, 3, 1, 1, 1) - self.delButton.setEnabled(True) + def reject(self): + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + FreeCADGui.Selection.removeObserver(self.s) - QtCore.QObject.connect(self.addButton, QtCore.SIGNAL("clicked()"), self.addElement) - QtCore.QObject.connect(self.delButton, QtCore.SIGNAL("clicked()"), self.removeElement) - QtCore.QObject.connect(self.tree, QtCore.SIGNAL("itemChanged(QTreeWidgetItem *, int)"), self.edit) - self.update() - self.retranslateUi(self.form) + 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"): + self.obj.SafeHeight = self.form.safeHeight.text() + if hasattr(self.obj,"ClearanceHeight"): + self.obj.ClearanceHeight = self.form.clearanceHeight.text() + if hasattr(self.obj,"StepDown"): + self.obj.StepDown = self.form.stepDown.value() + if hasattr(self.obj,"OffsetExtra"): + self.obj.OffsetExtra = self.form.extraOffset.value() + if hasattr(self.obj,"SegLen"): + self.obj.SegLen = self.form.segLen.value() + if hasattr(self.obj,"RollRadius"): + self.obj.RollRadius = self.form.rollRadius.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,"Algorithm"): + self.obj.Algorithm = str(self.form.algorithmSelect.currentText()) + if hasattr(self.obj,"Side"): + self.obj.Side = str(self.form.cutSide.currentText()) + if hasattr(self.obj,"Direction"): + self.obj.Direction = str(self.form.direction.currentText()) + self.obj.Proxy.execute(self.obj) - def isAllowedAlterSelection(self): - return False + def open(self): + self.s =SelObserver() + # install the function mode resident + FreeCADGui.Selection.addObserver(self.s) - def isAllowedAlterView(self): - return True + def addBase(self): + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelectionEx() + + if not len(selection) >= 1: + FreeCAD.Console.PrintError(translate("PathProject","Please select at least one profileable object\n")) + return + for s in selection: + if s.HasSubObjects: + for i in s.SubElementNames: + self.obj.Proxy.addprofilebase(self.obj, s.Object, i) + else: + self.obj.Proxy.addprofilebase(self.obj, s.Object) + + self.form.baseList.clear() + for i in self.obj.Base: + self.form.baseList.addItem(i[0].Name + "." + i[1]) + + def deleteBase(self): + dlist = self.form.baseList.selectedItems() + newlist = [] + for d in dlist: + for i in self.obj.Base: + if i[0].Name != d.text().partition(".")[0] or i[1] != d.text().partition(".")[2] : + newlist.append (i) + self.form.baseList.takeItem(self.form.baseList.row(d)) + self.obj.Base = newlist + self.obj.Proxy.execute(self.obj) + FreeCAD.ActiveDocument.recompute() + + def itemActivated(self): + FreeCADGui.Selection.clearSelection() + slist = self.form.baseList.selectedItems() + for i in slist: + objstring = i.text().partition(".") + obj = FreeCAD.ActiveDocument.getObject(objstring[0]) + # sub = o.Shape.getElement(objstring[2]) + if objstring[2] != "": + FreeCADGui.Selection.addSelection(obj,objstring[2]) + else: + FreeCADGui.Selection.addSelection(obj) + + FreeCADGui.updateGui() + + def reorderBase(self): + newlist = [] + for i in range(self.form.baseList.count()): + s = self.form.baseList.item(i).text() + objstring = s.partition(".") + + obj = FreeCAD.ActiveDocument.getObject(objstring[0]) + item = (obj, str(objstring[2])) + newlist.append(item) + self.obj.Base=newlist + + self.obj.Proxy.execute(self.obj) + FreeCAD.ActiveDocument.recompute() def getStandardButtons(self): - return int(QtGui.QDialogButtonBox.Close) + return int(QtGui.QDialogButtonBox.Ok) - def update(self): - 'fills the treewidget' - self.updating = True - self.tree.clear() - if self.obj: - for i in range(len(self.obj.locs)): - item = QtGui.QTreeWidgetItem(self.tree) - item.setText(0,str(i+1)) - l = self.obj.locs[i] - item.setText(1,str(l.x)+", " + str(l.y) +", " + str(l.z)) - item.setText(2,str(self.obj.heights[i])) - item.setText(3,str(self.obj.lengths[i])) - item.setText(4,str(self.obj.angles[i])) - item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) - item.setTextAlignment(0,QtCore.Qt.AlignLeft) - self.retranslateUi(self.form) - self.updating = False - return - - def addElement(self): - self.updating = True - - item = QtGui.QTreeWidgetItem(self.tree) - item.setText(0,str(self.tree.topLevelItemCount())) - item.setText(1,"0.0, 0.0, 0.0") - item.setText(2, str(float(4.0))) - item.setText(3, str(float(10.0))) - item.setText(4, str(float(45.0))) - item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) - self.updating = False - - self.resetObject() - - def removeElement(self): - it = self.tree.currentItem() - if it: - nr = int(it.text(0))-1 - self.resetObject(remove=nr) - self.update() def edit(self,item,column): if not self.updating: @@ -552,8 +558,8 @@ class _EditPanel: l = [] a = [] - for i in range(self.tree.topLevelItemCount()): - it = self.tree.findItems(str(i+1),QtCore.Qt.MatchExactly,0)[0] + for i in range(self.form.tagTree.topLevelItemCount()): + it = self.form.tagTree.findItems(str(i+1),QtCore.Qt.MatchExactly,0)[0] if (remove == None) or (remove != i): if it.text(1): x = float(it.text(1).split()[0].rstrip(",")) @@ -584,21 +590,130 @@ class _EditPanel: self.obj.touch() FreeCAD.ActiveDocument.recompute() - def reject(self): - FreeCAD.ActiveDocument.recompute() - FreeCADGui.ActiveDocument.resetEdit() - return True + def addElement(self): + self.updating = True - def retranslateUi(self, TaskPanel=None): - TaskPanel.setWindowTitle(QtGui.QApplication.translate("Path", "Holding Tags", None, QtGui.QApplication.UnicodeUTF8)) - self.delButton.setText(QtGui.QApplication.translate("Path", "Remove", None, QtGui.QApplication.UnicodeUTF8)) - self.addButton.setText(QtGui.QApplication.translate("Path", "Add", None, QtGui.QApplication.UnicodeUTF8)) - self.title.setText(QtGui.QApplication.translate("Path", "Tag Locations and Properties", None, QtGui.QApplication.UnicodeUTF8)) - self.tree.setHeaderLabels([QtGui.QApplication.translate("Path", "", None, QtGui.QApplication.UnicodeUTF8), - QtGui.QApplication.translate("Path", "Location", None, QtGui.QApplication.UnicodeUTF8), - QtGui.QApplication.translate("Path", "Height", None, QtGui.QApplication.UnicodeUTF8), - QtGui.QApplication.translate("Path", "Length", None, QtGui.QApplication.UnicodeUTF8), - QtGui.QApplication.translate("Path", "Angle", None, QtGui.QApplication.UnicodeUTF8)]) + item = QtGui.QTreeWidgetItem(self.form.tagTree) + item.setText(0,str(self.form.tagTree.topLevelItemCount())) + item.setText(1,"0.0, 0.0, 0.0") + item.setText(2, str(float(4.0))) + item.setText(3, str(float(10.0))) + item.setText(4, str(float(45.0))) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + self.updating = False + + self.resetObject() + + def removeElement(self): + it = self.form.tagTree.currentItem() + if it: + nr = int(it.text(0))-1 + self.resetObject(remove=nr) + self.update() + + def update(self): + 'fills the treewidget' + self.updating = True + self.form.tagTree.clear() + if self.obj: + for i in range(len(self.obj.locs)): + item = QtGui.QTreeWidgetItem(self.form.tagTree) + item.setText(0,str(i+1)) + l = self.obj.locs[i] + item.setText(1,str(l.x)+", " + str(l.y) +", " + str(l.z)) + item.setText(2,str(self.obj.heights[i])) + item.setText(3,str(self.obj.lengths[i])) + item.setText(4,str(self.obj.angles[i])) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + item.setTextAlignment(0,QtCore.Qt.AlignLeft) + self.updating = False + return + + def setupUi(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)) + self.form.stepDown.setValue(self.obj.StepDown) + self.form.extraOffset.setValue(self.obj.OffsetExtra.Value) + self.form.segLen.setValue(self.obj.SegLen.Value) + self.form.rollRadius.setValue(self.obj.RollRadius.Value) + self.form.useCompensation.setChecked(self.obj.UseComp) + self.form.useStartPoint.setChecked(self.obj.UseStartPoint) + self.form.useEndPoint.setChecked(self.obj.UseEndPoint) + + index = self.form.algorithmSelect.findText(self.obj.Algorithm, QtCore.Qt.MatchFixedString) + if index >= 0: + self.form.algorithmSelect.setCurrentIndex(index) + + index = self.form.cutSide.findText(self.obj.Side, QtCore.Qt.MatchFixedString) + if index >= 0: + self.form.cutSide.setCurrentIndex(index) + + index = self.form.direction.findText(self.obj.Direction, QtCore.Qt.MatchFixedString) + if index >= 0: + self.form.direction.setCurrentIndex(index) + + + for i in self.obj.Base: + self.form.baseList.addItem(i[0].Name + "." + i[1]) + + for i in range(len(self.obj.locs)): + item = QtGui.QTreeWidgetItem(self.form.tagTree) + item.setText(0,str(i+1)) + l = self.obj.locs[i] + item.setText(1,str(l.x)+", " + str(l.y) +", " + str(l.z)) + item.setText(2,str(self.obj.heights[i])) + item.setText(3,str(self.obj.lengths[i])) + item.setText(4,str(self.obj.angles[i])) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + item.setTextAlignment(0,QtCore.Qt.AlignLeft) + + #Connect Signals and Slots + #Base Controls + self.form.baseList.itemSelectionChanged.connect(self.itemActivated) + self.form.addBase.clicked.connect(self.addBase) + self.form.deleteBase.clicked.connect(self.deleteBase) + self.form.reorderBase.clicked.connect(self.reorderBase) + + #Depths + self.form.startDepth.editingFinished.connect(self.getFields) + self.form.finalDepth.editingFinished.connect(self.getFields) + self.form.stepDown.editingFinished.connect(self.getFields) + + #Heights + self.form.safeHeight.editingFinished.connect(self.getFields) + self.form.clearanceHeight.editingFinished.connect(self.getFields) + + #operation + self.form.algorithmSelect.currentIndexChanged.connect(self.getFields) + self.form.cutSide.currentIndexChanged.connect(self.getFields) + self.form.direction.currentIndexChanged.connect(self.getFields) + self.form.useCompensation.clicked.connect(self.getFields) + self.form.useStartPoint.clicked.connect(self.getFields) + self.form.useEndPoint.clicked.connect(self.getFields) + self.form.extraOffset.editingFinished.connect(self.getFields) + self.form.segLen.editingFinished.connect(self.getFields) + self.form.rollRadius.editingFinished.connect(self.getFields) + + #Tag Form + QtCore.QObject.connect(self.form.tagTree, QtCore.SIGNAL("itemChanged(QTreeWidgetItem *, int)"), self.edit) + self.form.addTag.clicked.connect(self.addElement) + self.form.deleteTag.clicked.connect(self.removeElement) + + +class SelObserver: + def __init__(self): + import PathScripts.PathSelection as PST + PST.profileselect() + + def __del__(self): + import PathScripts.PathSelection as PST + PST.clear() + + def addSelection(self,doc,obj,sub,pnt): # Selection object + FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj +')') + FreeCADGui.updateGui() if FreeCAD.GuiUp: diff --git a/src/Mod/Path/PathScripts/PathProject.py b/src/Mod/Path/PathScripts/PathProject.py index 0e3a043b9..743a66a21 100644 --- a/src/Mod/Path/PathScripts/PathProject.py +++ b/src/Mod/Path/PathScripts/PathProject.py @@ -141,6 +141,11 @@ class CommandProject: def Create(pathChildren = []): """Code to create a project""" #FreeCADGui.addModule("PathScripts.PathProject") + import PathScripts.PathUtils as PU + if not PU.findProj() == None: + FreeCAD.Console.PrintError("A Path project already exists in this document\n") + return + obj = FreeCAD.ActiveDocument.addObject("Path::FeatureCompoundPython","Project") ObjectPathProject(obj) if pathChildren: @@ -152,8 +157,7 @@ class CommandProject: #create a machine obj import PathScripts PathScripts.PathMachine.CommandPathMachine.Create() - PLT = PathScripts.PathLoadTool.CommandPathLoadTool() - PLT.Activated() + PathScripts.PathLoadTool.CommandPathLoadTool.Create() return obj diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index c1bbbc827..f15740644 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -71,6 +71,99 @@ class ENGRAVEGate: def allow(self,doc,obj,sub): return (obj.Name[0:11] == 'ShapeString') +class DRILLGate: + def allow(self,doc,obj,sub): + import Part + drillable = False + try: + obj = obj.Shape + except: + return False + if obj.ShapeType == 'Vertex': + drillable = True + elif obj.ShapeType == 'Edge': + if isinstance(obj.Curve, Part.Circle): + drillable = True + elif obj.ShapeType == 'Face': + if isinstance(obj.Edges[0].Curve, Part.Circle): + drillable = True + elif obj.ShapeType == 'Wire': + if isinstance(obj.Edges[0].Curve, Part.Circle): + drillable = True + elif obj.ShapeType == 'Solid': + if sub[0:4] == 'Face': + o = obj.getElement(sub) + drillable = isinstance(o.Edges[0].Curve, Part.Circle) + if sub[0:4] == 'Edge': + o = obj.getElement(sub) + drillable = isinstance(o.Curve, Part.Circle) + + return drillable + +class PROFILEGate: + def allow(self,doc,obj,sub): + import Part + + profileable = False + try: + obj = obj.Shape + except: + return False + + + if obj.ShapeType == 'Edge': + profileable = True + + elif obj.ShapeType == 'Face': + profileable = True + + elif obj.ShapeType == 'Solid': + if sub[0:4] == 'Face': + profileable = True + + if sub[0:4] == 'Edge': + profileable = True + + elif obj.ShapeType == 'Wire': + profileable = True + + if sub[0:6] == 'Vertex': + print "might be fun to try to derive the loop by hovering near a vertex" + + return profileable + +class POCKETGate: + def allow(self,doc,obj,sub): + import Part + + pocketable = False + try: + obj = obj.Shape + except: + return False + + + if obj.ShapeType == 'Edge': + pocketable = False + + elif obj.ShapeType == 'Face': + pocketable = True + + elif obj.ShapeType == 'Solid': + if sub[0:4] == 'Face': + pocketable = True + + # if sub[0:4] == 'Edge': + # pocketable = True + + # elif obj.ShapeType == 'Wire': + # pocketable = True + + # if sub[0:6] == 'Vertex': + # print "might be fun to try to derive the loop by hovering near a vertex" + + return pocketable + def fselect(): FreeCADGui.Selection.addSelectionGate(FGate()) @@ -84,10 +177,22 @@ def eselect(): FreeCADGui.Selection.addSelectionGate(EGate()) FreeCAD.Console.PrintWarning("Edge Select Mode\n") +def drillselect(): + FreeCADGui.Selection.addSelectionGate(DRILLGate()) + FreeCAD.Console.PrintWarning("Drilling Select Mode\n") def engraveselect(): FreeCADGui.Selection.addSelectionGate(ENGRAVEGate()) - FreeCAD.Console.PrintWarning("ShapeString Select Mode\n") + FreeCAD.Console.PrintWarning("Engrave Select Mode\n") + +def profileselect(): + FreeCADGui.Selection.addSelectionGate(PROFILEGate()) + FreeCAD.Console.PrintWarning("Profile Select Mode\n") + +def pocketselect(): + FreeCADGui.Selection.addSelectionGate(POCKETGate()) + FreeCAD.Console.PrintWarning("Pocket Select Mode\n") + def clear(): FreeCADGui.Selection.removeSelectionGate() diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index ac85ee72a..708bf3bbe 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -2,7 +2,7 @@ #*************************************************************************** #* * -#* Copyright (c) 2016 sliptonic * +#* Copyright (c) 2016 sliptonic * #* * #* This program is free software; you can redistribute it and/or modify * #* it under the terms of the GNU Lesser General Public License (LGPL) * @@ -22,15 +22,24 @@ #* * #*************************************************************************** -import FreeCAD,FreeCADGui,Path,PathGui +import FreeCAD,Path from FreeCAD import Vector -from PySide import QtCore,QtGui from PathScripts import PathUtils,PathSelection,PathProject -"""Path Surface object and FreeCAD command""" -'''Surface operation is used for 3D sculpting, roughing, and finishing -Possible algorithms include waterline, zigzag, and adaptive roughing. -Libraries to consider: opencamlib and libactp''' +if FreeCAD.GuiUp: + import FreeCADGui, PathGui + from PySide import QtCore, QtGui + from DraftTools import translate + from pivy import coin +else: + def translate(ctxt,txt): + return txt + +__title__="Path Surface Operation" +__author__ = "sliptonic (Brad Collette)" +__url__ = "http://www.freecadweb.org" + +"""Path surface object and FreeCAD command""" # Qt tanslation handling try: @@ -42,46 +51,38 @@ except AttributeError: return QtGui.QApplication.translate(context, text, disambig) -class ObjectProfile: +class ObjectSurface: + def __init__(self,obj): - obj.addProperty("App::PropertyLinkSub","Base","Path",translate("Parent Object","The base geometry of this toolpath")) + obj.addProperty("App::PropertyLinkSub","Base","Path",translate("Parent Object(s)","The base geometry of this toolpath")) obj.addProperty("App::PropertyBool","Active","Path",translate("Active","Make False, to prevent operation from generating code")) - obj.addProperty("App::PropertyString","Comment","Path",translate("Comment","An optional comment for this profile")) - + obj.addProperty("App::PropertyString","Comment","Path",translate("PathProject","An optional comment for this profile")) obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm",translate("PathProject", "The library to use to generate the path")) - obj.Algorithm = ['OCL Waterline'] + obj.Algorithm = ['OCL Dropcutter', 'OCL Waterline'] + #Tool Properties obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The tool number in use")) - obj.ToolNumber = (0,0,1000,1) + obj.ToolNumber = (0, 0, 1000, 0) obj.setEditorMode('ToolNumber',1) #make this read only + #Surface Properties + obj.addProperty("App::PropertyFloatConstraint", "SampleInterval", "Surface", translate("PathSurface","The Sample Interval. Small values cause long wait")) + obj.SampleInterval = (0,0,1,0) #Depth Properties - obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", translate("Clearance Height","The height needed to clear clamps and obstructions")) - obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", translate("PathProject","Rapid Safety Height between locations.")) - obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", translate("StepDown","Incremental Step Down of Tool")) - obj.StepDown = (1,0.01,1000,0.5) - obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", translate("Start Depth","Starting Depth of Tool- first cut depth in Z")) - obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", translate("Final Depth","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyFloat", "ClearanceHeight", "Depth", translate("PathProject","The height needed to clear clamps and obstructions")) + obj.addProperty("App::PropertyFloat", "SafeHeight", "Depth", translate("PathProject","Rapid Safety Height between locations.")) + obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", translate("PathProject","Incremental Step Down of Tool")) + obj.StepDown = (0.0, 0.01, 100.0, 0.5) + obj.addProperty("App::PropertyFloat", "StartDepth", "Depth", translate("PathProject","Starting Depth of Tool- first cut depth in Z")) + obj.addProperty("App::PropertyFloat", "FinalDepth", "Depth", translate("PathProject","Final Depth of Tool- lowest value in Z")) + obj.addProperty("App::PropertyFloat", "FinishDepth", "Depth", translate("PathProject","Maximum material removed on final pass.")) #Feed Properties - obj.addProperty("App::PropertySpeed", "VertFeed", "Feed",translate("Vert Feed","Feed rate for vertical moves in Z")) - obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Horiz Feed","Feed rate for horizontal moves")) - - #Start Point Properties - obj.addProperty("App::PropertyVector","StartPoint","Start Point",translate("Start Point","The start point of this path")) - obj.addProperty("App::PropertyBool","UseStartPoint","Start Point",translate("Use Start Point","make True, if specifying a Start Point")) - obj.addProperty("App::PropertyLength", "ExtendAtStart", "Start Point", translate("extend at start", "extra length of tool path before start of part edge")) - obj.addProperty("App::PropertyLength", "LeadInLineLen", "Start Point", translate("lead in length","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",translate("Use End Point","make True, if specifying an End Point")) - obj.addProperty("App::PropertyLength", "ExtendAtEnd", "End Point", translate("extend at end","extra length of tool path after end of part edge")) - obj.addProperty("App::PropertyLength", "LeadOutLineLen", "End Point", translate("lead_out_line_len","length of straight segment of toolpath that comes in at angle to last part edge")) - obj.addProperty("App::PropertyVector","EndPoint","End Point",translate("End Point","The end point of this path")) - - #Surface Properties + obj.addProperty("App::PropertySpeed", "VertFeed", "Feed",translate("Path","Feed rate for vertical moves in Z")) + obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed",translate("Path","Feed rate for horizontal moves")) + obj.Proxy = self def __getstate__(self): @@ -90,140 +91,316 @@ class ObjectProfile: def __setstate__(self,state): return None - def execute(self,obj): - import Part, DraftGeomUtils - from PathScripts.PathUtils import depth_params + def _waterline(self,obj, s, bb): + import ocl +# from PathScripts.PathUtils import fmt + from PathScripts.PathUtils import depth_params, fmt + import time - if obj.Base: - # tie the toolnumber to the PathLoadTool object ToolNumber - if len(obj.InList)>0: #check to see if obj is in the Project group yet - project = obj.InList[0] - tl = int(PathUtils.changeTool(obj,project)) - obj.ToolNumber= tl - - tool = PathUtils.getTool(obj,obj.ToolNumber) - if tool: - radius = tool.Diameter/2 - else: - # temporary value,in case we don't have any tools defined already - radius = 0.25 - - depthparams = depth_params (obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, 0.0, obj.FinalDepth.Value, None) - - clearance = obj.ClearanceHeight.Value - step_down=obj.StepDown - start_depth=obj.StartDepth.Value - final_depth=obj.FinalDepth.Value - rapid_safety_space=obj.SafeHeight.Value + def drawLoops(loops): + nloop = 0 + waterlinestring = "" + waterlinestring += "(waterline begin)" + for loop in loops: + p = loop[0] + loopstring = "(loop begin)" +"\n" + loopstring += "G0 Z" + str(obj.SafeHeight) +"\n" + loopstring += "G0 X" + str(fmt(p.x)) + " Y" + str(fmt(p.y)) +"\n" + loopstring += "G1 Z" + str(fmt(p.z)) +"\n" + for p in loop[1:]: + loopstring += "G1 X" + str(fmt(p.x)) + " Y" + str(fmt(p.y)) + " Z" + str(fmt(p.z)) +"\n" + zheight = p.z + p = loop[0] + loopstring += "G1 X" + str(fmt(p.x)) + " Y" + str(fmt(p.y)) + " Z" + str(fmt(zheight)) +"\n" + loopstring += "(loop end)" +"\n" + print " loop ",nloop, " with ", len(loop), " points" + nloop = nloop+1 + waterlinestring += loopstring + waterlinestring += "(waterline end)" +"\n" + + return waterlinestring - vf=obj.VertFeed.Value - hf=obj.HorizFeed.Value - '''Parse the base and get the necessary geometry here''' + # nloop = 0 + # for lop in loops: + # n = 0 + # N = len(lop) + # first_point=ocl.Point(-1,-1,5) + # previous=ocl.Point(-1,-1,5) + # for p in lop: + # if n==0: # don't draw anything on the first iteration + # previous=p + # first_point = p + # elif n== (N-1): # the last point + # # and a line from p to the first point + # p1=(p.x,p.y,p.z) + # p2=(first_point.x,first_point.y,first_point.z) + # output += "G1 X" + str(p2[0]) + " Y" + str (p2[1]) + " Z" +str(p2[2]) +"\n" + # else: + # p1=(previous.x,previous.y,previous.z) + # p2=(p.x,p.y,p.z) + # output += "G1 X" + str(p2[0]) + " Y" + str (p2[1]) + " Z" +str(p2[2]) +"\n" + # #print "line from: " + str (p1) + "To: " + str (p2) + + # #print "line X: " + str (p1.x) +" Y: " + str(p1.y) + "Z: " + str(p1.z) + " To X: " + str (p2.x) +" Y: " + str(p2.y) + "Z: " + str(p2.z) + # previous=p + # n=n+1 + # print " loop ",nloop, " with ", len(lop), " points" + # nloop = nloop+1 + # return output + depthparams = depth_params (obj.ClearanceHeight, obj.SafeHeight, obj.StartDepth, obj.StepDown, obj.FinishDepth, obj.FinalDepth) - if obj.Algorithm == "OCL Waterline": - try: - import ocl - except: - FreeCAD.Console.PrintError(translate("Path","OpenCAMLib needs to be installed for this command to work.\n")) - return + # stlfile = "../../stl/gnu_tux_mod.stl" + # surface = STLSurfaceSource(stlfile) + + surface = s - output = "" - output += '('+ str(obj.Comment)+')\n' - - output += self.buildoclwaterline() - - else: - return - - - - if obj.Active: - path = Path.Path(output) - obj.Path = path - obj.ViewObject.Visibility = True - - else: - path = Path.Path("(inactive operation)") - obj.Path = path - obj.ViewObject.Visibility = False - - def buildoclwaterline(self): - output = "Some text" + t_before = time.time() + + zheights= depthparams.get_depths() + + wl = ocl.Waterline() + #wl = ocl.AdaptiveWaterline() # this is slower, ca 60 seconds on i7 CPU + wl.setSTL(surface) + diam = 0.5 + length= 10 + cutter = ocl.BallCutter( diam , length ) # any ocl MillingCutter class should work here + wl.setCutter(cutter) + wl.setSampling(obj.SampleInterval) # this should be smaller than the smallest details in the STL file + # AdaptiveWaterline() also has settings for minimum sampling interval (see c++ code) + all_loops=[] + for zh in zheights: + print "calculating Waterline at z= ", zh + wl.reset() + wl.setZ(zh) # height for this waterline + wl.run() + all_loops.append( wl.getLoops() ) + t_after = time.time() + calctime = t_after-t_before + n=0 + output = "" + for loops in all_loops: # at each z-height, we may get many loops + print " %d/%d:" % (n,len(all_loops)) + output += drawLoops(loops) + n=n+1 + print "(" + str(calctime) + ")" return output -class ViewProviderProfile: + def _dropcutter(self,obj, s, bb): + import ocl + import time - def __init__(self,vobj): - vobj.Proxy = self + cutter = ocl.CylCutter(self.radius*2, 5) + pdc = ocl.PathDropCutter() # create a pdc + pdc.setSTL(s) + pdc.setCutter(cutter) + pdc.minimumZ = 0.25 + pdc.setSampling(obj.SampleInterval) + + # some parameters for this "zigzig" pattern + xmin=bb.XMin - cutter.getDiameter() + xmax=bb.XMax + cutter.getDiameter() + ymin=bb.YMin - cutter.getDiameter() + ymax=bb.YMax + cutter.getDiameter() + zmax=bb.ZMax + cutter.getDiameter() + + Ny=int(bb.YLength/cutter.getDiameter()) # number of lines in the y-direction + dy = float(ymax-ymin)/Ny # the y step-over + + path = ocl.Path() # create an empty path object + + # add Line objects to the path in this loop + for n in xrange(0,Ny): + y = ymin+n*dy + p1 = ocl.Point(xmin,y,0) # start-point of line + p2 = ocl.Point(xmax,y,0) # end-point of line + if (n % 2 == 0): #even + l = ocl.Line(p1,p2) # line-object + else: #odd + l = ocl.Line(p2,p1) # line-object - def attach(self,vobj): - self.Object = vobj.Object - return + path.append( l ) # add the line to the path + + pdc.setPath( path ) + + # run drop-cutter on the path + t_before = time.time() + pdc.run() + t_after = time.time() + print "calculation took ", t_after-t_before," s" - def getIcon(self): - return ":/icons/Path-Surface.svg" + #retrieve the points + clp = pdc.getCLPoints() + print "points received: " + str(len(clp)) - def __getstate__(self): + #generate the path commands + output = "" + output += "G0 Z" + str(obj.ClearanceHeight) + "\n" + output += "G0 X" + str(clp[0].x) +" Y" + str(clp[0].y) + "\n" + output += "G1 Z" + str(clp[0].z) + " F" + str(obj.VertFeed.Value) + "\n" + + for c in clp: + output += "G1 X" + str(c.x) +" Y" + str(c.y) +" Z" + str(c.z)+ "\n" + + return output + + + + def execute(self,obj): + import Part + import Mesh + import MeshPart + FreeCAD.Console.PrintWarning(translate("PathSurface","Hold on. This might take a minute.\n")) + + output = "" + + if obj.Algorithm in ['OCL Dropcutter', 'OCL Waterline']: + try: + import ocl + except: + FreeCAD.Console.PrintError(translate("PathSurface","This operation requires OpenCamLib to be installed.\n")) + return + + # tie the toolnumber to the PathLoadTool object ToolNumber + if len(obj.InList)>0: #check to see if obj is in the Project group yet + project = obj.InList[0] + tl = int(PathUtils.changeTool(obj,project)) + obj.ToolNumber= tl + + tool = PathUtils.getTool(obj,obj.ToolNumber) + if tool: + self.radius = tool.Diameter/2 + else: + # temporary value,in case we don't have any tools defined already + self.radius = 0.25 + + + mesh = obj.Base[0] + if mesh.TypeId.startswith('Mesh'): + mesh = mesh.Mesh + bb = mesh.BoundBox + else: + bb = mesh.Shape.BoundBox + mesh = MeshPart.meshFromShape(mesh.Shape,MaxLength=2) + + s= ocl.STLSurf() + for f in mesh.Facets: + p = f.Points[0];q=f.Points[1];r=f.Points[2] + t= ocl.Triangle(ocl.Point(p[0],p[1],p[2]),ocl.Point(q[0],q[1],q[2]),ocl.Point(r[0],r[1],r[2])) + s.addTriangle(t) + + if obj.Algorithm == 'OCL Dropcutter': + output = self._dropcutter(obj, s, bb) + elif obj.Algorithm == 'OCL Waterline': + output = self._waterline(obj, s, bb) + + + path = Path.Path(output) + obj.Path = path + + + +class ViewProviderSurface: + def __init__(self,obj): #mandatory +# obj.addProperty("App::PropertyFloat","SomePropertyName","PropertyGroup","Description of this property") + obj.Proxy = self + + def __getstate__(self): #mandatory return None - def __setstate__(self,state): + def __setstate__(self,state): #mandatory return None + def getIcon(self): #optional + return ":/icons/Path-Surfacing.svg" + +# def attach(self): #optional +# # this is executed on object creation and object load from file +# pass + + def onChanged(self,obj,prop): #optional + # this is executed when a property of the VIEW PROVIDER changes + pass + + def updateData(self,obj,prop): #optional + # this is executed when a property of the APP OBJECT changes + pass + + def setEdit(self,vobj,mode): #optional + # this is executed when the object is double-clicked in the tree + pass + + def unsetEdit(self,vobj,mode): #optional + # this is executed when the user cancels or terminates edit mode + pass + + + +class CommandPathSurfacing: + -class CommandPathProfile: def GetResources(self): - return {'Pixmap' : 'Path-Surface', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathSurface","Surface"), - 'Accel': "P, P", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathSurface","Creates a Path Surface object from selected solid")} + return {'Pixmap' : 'Path-Surfacing', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Surface","Surfacing"), + 'Accel': "P, D", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Surface","Creates a Path Surfacing object")} def IsActive(self): return not FreeCAD.ActiveDocument is None - - def Activated(self): - import Path - from PathScripts import PathProject, PathUtils + def Activated(self): # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelectionEx() if len(selection) != 1: - FreeCAD.Console.PrintError(translate("Path","Select a Solid.\n")) + FreeCAD.Console.PrintError(translate("PathSurface","Please select a single solid object from the project tree\n")) return - if len(selection[0].SubObjects) == 0: - FreeCAD.Console.PrintError(translate("Path","Select a Solid.\n")) + if not len(selection[0].SubObjects) == 0: + FreeCAD.Console.PrintError(translate("PathSurface","Please select a single solid object from the project tree\n")) return - - # if everything is ok, execute and register the transaction in the undo/redo stack + for s in selection[0].SubObjects: + if s.ShapeType != "Edge": + if (s.ShapeType != "Face") or (len(selection[0].SubObjects) != 1): + FreeCAD.Console.PrintError(translate("PathSurface","Please select only edges or a single face\n")) + return - # Take a guess at some reasonable values for Finish depth. - bb = selection[0].Object.Shape.BoundBox #parent boundbox - fbb = selection[0].SubObjects[0].BoundBox #feature boundbox - if fbb.ZMax < bb.ZMax: - zbottom = fbb.ZMax + sel = selection[0].Object + #get type of object + if sel.TypeId.startswith('Mesh'): + #it is a mesh already + print 'was already mesh' + ztop = sel.Mesh.BoundBox.ZMax + zbottom = sel.Mesh.BoundBox.ZMin + + #mesh = sel + elif sel.TypeId.startswith('Part') and \ + (sel.Shape.BoundBox.XLength > 0) and \ + (sel.Shape.BoundBox.YLength > 0) and \ + (sel.Shape.BoundBox.ZLength > 0): + ztop = sel.Shape.BoundBox.ZMax + zbottom = sel.Shape.BoundBox.ZMin + print 'this is a solid Part object' + else: - zbottom = bb.ZMin + FreeCAD.Console.PrintError(translate("PathSurface","Cannot work with this object\n")) + return - FreeCAD.ActiveDocument.openTransaction(translate("Path","Create a Surfacing Operation")) + # if everything is ok, execute and register the transaction in the undo/redo stack + FreeCAD.ActiveDocument.openTransaction(translate("Path_Surfacing","Create Surface")) FreeCADGui.addModule("PathScripts.PathSurface") FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Surface")') - FreeCADGui.doCommand('PathScripts.PathSurface.ObjectProfile(obj)') - + FreeCADGui.doCommand('PathScripts.PathSurface.ObjectSurface(obj)') FreeCADGui.doCommand('obj.Active = True') - # subs ="[" - # for s in selection[0].SubElementNames: - # subs += '"' + s + '",' - # subs += "]" - # FreeCADGui.doCommand('obj.Base = (FreeCAD.ActiveDocument.' + selection[0].ObjectName + ',' + subs + ')') + FreeCADGui.doCommand('PathScripts.PathSurface.ViewProviderSurface(obj.ViewObject)') + FreeCADGui.doCommand('obj.Base = (FreeCAD.ActiveDocument.'+selection[0].ObjectName+',[])') + FreeCADGui.doCommand('from PathScripts import PathUtils') + FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(ztop + 2)) + FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop)) + FreeCADGui.doCommand('obj.SafeHeight = ' + str(ztop + 2)) + FreeCADGui.doCommand('obj.StepDown = ' + str((ztop-zbottom)/8)) + FreeCADGui.doCommand('obj.SampleInterval = 0.4') - FreeCADGui.doCommand('obj.ViewObject.Proxy = 0') - FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(bb.ZMax + 10.0)) - FreeCADGui.doCommand('obj.StepDown = 1.0') - FreeCADGui.doCommand('obj.StartDepth= ' + str(bb.ZMax)) FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom)) - - FreeCADGui.doCommand('obj.SafeHeight = '+ str(bb.ZMax + 2.0)) FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)') FreeCAD.ActiveDocument.commitTransaction() @@ -232,6 +409,6 @@ class CommandPathProfile: if FreeCAD.GuiUp: # register the FreeCAD command - FreeCADGui.addCommand('Path_Surface',CommandPathSurface()) + FreeCADGui.addCommand('Path_Surfacing',CommandPathSurfacing()) -FreeCAD.Console.PrintLog("Loading PathSurface... done\n") +FreeCAD.Console.PrintLog("Loading PathSurfacing... done\n") diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index 669cfbe29..7addc8c1d 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -324,7 +324,6 @@ def changeTool(obj,proj): if g == obj: return tlnum - def getLastTool(obj): toolNum = obj.ToolNumber if obj.ToolNumber == 0: @@ -333,6 +332,44 @@ def getLastTool(obj): toolNum = changeTool(obj, proj) return getTool(obj, toolNum) +def getLastToolLoad(obj): + #This walks up the hierarchy and tries to find the closest preceding toolchange. + + import PathScripts + tc = None + lastfound = None + + try: + child = obj + parent = obj.InList[0] + except: + parent = None + + while parent != None: + + sibs = parent.Group + for g in sibs: + if isinstance(g.Proxy,PathScripts.PathLoadTool.LoadTool): + lastfound = g + if g == child: + tc = lastfound + + if tc == None: + try: + child = parent + parent = parent.InList[0] + except: + parent = None + else: + return tc + + if tc == None: + for g in FreeCAD.ActiveDocument.Objects: #top level object + if isinstance(g.Proxy,PathScripts.PathLoadTool.LoadTool): + lastfound = g + if g == obj: + tc = lastfound + return tc def getTool(obj,number=0): "retrieves a tool from a hosting object with a tooltable, if any" @@ -419,7 +456,6 @@ class depth_params: self.user_depths = user_depths def get_depths(self): - print "in function" depths = [] if self.user_depths != None: depths = self.user_depths