diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index a2fbad13a..848159391 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -57,6 +57,8 @@ SET(PathScripts_SRCS PathScripts/PathInspect.py PathScripts/PathSimpleCopy.py PathScripts/PathEngrave.py + PathScripts/PathSurface.py + ) SET(PathScripts_NC_SRCS diff --git a/src/Mod/Path/Gui/CMakeLists.txt b/src/Mod/Path/Gui/CMakeLists.txt index 43df8f04c..6a39864e1 100644 --- a/src/Mod/Path/Gui/CMakeLists.txt +++ b/src/Mod/Path/Gui/CMakeLists.txt @@ -88,7 +88,7 @@ add_library(PathGui SHARED ${PathGui_SRCS}) target_link_libraries(PathGui ${PathGui_LIBS}) -fc_target_copy_resource(PathGui +fc_target_copy_resource(PathGui ${CMAKE_SOURCE_DIR}/src/Mod/Path ${CMAKE_BINARY_DIR}/Mod/Path InitGui.py) diff --git a/src/Mod/Path/Gui/DrillingEdit.ui b/src/Mod/Path/Gui/DrillingEdit.ui index f9411e387..21d5c0d2a 100644 --- a/src/Mod/Path/Gui/DrillingEdit.ui +++ b/src/Mod/Path/Gui/DrillingEdit.ui @@ -98,8 +98,8 @@ 0 0 - 116 - 108 + 304 + 314 @@ -159,8 +159,8 @@ 0 0 - 150 - 108 + 304 + 314 @@ -220,13 +220,13 @@ 0 0 - 234 - 50 + 304 + 314 - - :/icons/FreeCAD-default/scalable/user.svg:/icons/FreeCAD-default/scalable/user.svg + + :/icons/Path-Hamburger.svg:/icons/Path-Hamburger.svg Operation @@ -263,6 +263,7 @@ + diff --git a/src/Mod/Path/Gui/EngraveEdit.ui b/src/Mod/Path/Gui/EngraveEdit.ui index 7e18f163d..635566c58 100644 --- a/src/Mod/Path/Gui/EngraveEdit.ui +++ b/src/Mod/Path/Gui/EngraveEdit.ui @@ -34,7 +34,7 @@ 0 0 304 - 314 + 284 @@ -99,7 +99,7 @@ 0 0 304 - 314 + 284 @@ -146,7 +146,7 @@ 0 0 304 - 314 + 284 @@ -187,18 +187,65 @@ + + + + 0 + 0 + 304 + 284 + + + + + :/icons/FreeCAD-default/scalable/document-save-as.svg:/icons/FreeCAD-default/scalable/document-save-as.svg + + + Speeds + + + + + + mm/(s) + + + + + + + Horiz Feed + + + + + + + mm/(s) + + + + + + + Vert Feed + + + + + 0 0 304 - 314 + 284 - - :/icons/FreeCAD-default/scalable/user.svg:/icons/FreeCAD-default/scalable/user.svg + + :/icons/Path-Hamburger.svg:/icons/Path-Hamburger.svg Operation @@ -235,6 +282,7 @@ + diff --git a/src/Mod/Path/Gui/PocketEdit.ui b/src/Mod/Path/Gui/PocketEdit.ui index 697b3b65b..35e634a3b 100644 --- a/src/Mod/Path/Gui/PocketEdit.ui +++ b/src/Mod/Path/Gui/PocketEdit.ui @@ -252,6 +252,14 @@ + + + 0 + 0 + 334 + 327 + + Pattern @@ -266,8 +274,8 @@ - - :/icons/FreeCAD-default/scalable/user.svg:/icons/FreeCAD-default/scalable/user.svg + + :/icons/Path-Hamburger.svg:/icons/Path-Hamburger.svg Operation @@ -368,6 +376,7 @@ + diff --git a/src/Mod/Path/Gui/ProfileEdit.ui b/src/Mod/Path/Gui/ProfileEdit.ui index 8a968e68b..ab0b740ac 100644 --- a/src/Mod/Path/Gui/ProfileEdit.ui +++ b/src/Mod/Path/Gui/ProfileEdit.ui @@ -224,6 +224,14 @@ + + + 0 + 0 + 334 + 357 + + Holding @@ -283,8 +291,8 @@ - - :/icons/FreeCAD-default/scalable/user.svg:/icons/FreeCAD-default/scalable/user.svg + + :/icons/Path-Hamburger.svg:/icons/Path-Hamburger.svg Operation @@ -445,6 +453,7 @@ + diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index 442006295..9da94a228 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -31,6 +31,8 @@ icons/Path-ToolChange.svg icons/Path-SimpleCopy.svg icons/Path-Engrave.svg + icons/Path-Surfacing.svg + icons/Path-Hamburger.svg translations/Path_de.qm translations/Path_af.qm translations/Path_zh-CN.qm diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Surfacing.svg b/src/Mod/Path/Gui/Resources/icons/Path-Surfacing.svg new file mode 100644 index 000000000..295f78626 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Surfacing.svg @@ -0,0 +1,644 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Path/Gui/SurfaceEdit.ui b/src/Mod/Path/Gui/SurfaceEdit.ui new file mode 100644 index 000000000..bbdaf94ed --- /dev/null +++ b/src/Mod/Path/Gui/SurfaceEdit.ui @@ -0,0 +1,299 @@ + + + TaskPanel + + + + 0 + 0 + 352 + 525 + + + + + 0 + 400 + + + + Surface + + + + + + 0 + + + + true + + + + 0 + 0 + 334 + 387 + + + + + :/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 + 387 + + + + + :/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 + 387 + + + + + :/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 + 387 + + + + + :/icons/Path-Hamburger.svg:/icons/Path-Hamburger.svg + + + Operation + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Algorithm + + + + + + + + OCL Dropcutter + + + + + OCL Waterline + + + + + + + + + + + + + Gui::InputField + QLineEdit +
Gui/InputField.h
+
+
+ + + + + +
diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py index 6b5b9fe3a..a9fc7edcc 100644 --- a/src/Mod/Path/PathScripts/PathDrilling.py +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -24,7 +24,7 @@ import FreeCAD,Path from PySide import QtCore,QtGui -from PathScripts import PathUtils,PathSelection,PathProject +from PathScripts import PathUtils FreeCADGui = None if FreeCAD.GuiUp: @@ -43,25 +43,25 @@ except AttributeError: class ObjectDrilling: - + def __init__(self,obj): - 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::PropertyLinkSubList","Base","Path",translate("PathProject","The base geometry of this toolpath")) + 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::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::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::PropertyDistance", "RetractHeight", "Depth", translate("PathProject","The height where feed starts and height during retract tool when path is finished")) + + #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,1) obj.setEditorMode('ToolNumber',1) #make this read only - + obj.Proxy = self def __getstate__(self): @@ -69,26 +69,24 @@ class ObjectDrilling: def __setstate__(self,state): return None - + def execute(self,obj): output = "" toolLoad = PathUtils.getLastToolLoad(obj) if toolLoad == None: self.vertFeed = 100 self.horizFeed = 100 - radius = 0.25 - obj.ToolNumber= 0 + 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 + obj.ToolNumber= toolLoad.ToolNumber if obj.Base: locations = [] for loc in obj.Base: - if "Face" in loc[1] or "Edge" in loc[1]: + if "Face" in loc[1] or "Edge" in loc[1]: s = getattr(loc[0].Shape,loc[1]) else: s = loc[0].Shape @@ -131,6 +129,26 @@ class ObjectDrilling: def addDrillableLocation(self, obj, ss, sub=""): baselist = obj.Base item = (ss, sub) + if len(baselist) == 0: #When adding the first base object, guess at heights + try: + bb = ss.Shape.BoundBox #parent boundbox + subobj = ss.Shape.getElement(sub) + fbb = subobj.BoundBox #feature boundbox + obj.StartDepth = bb.ZMax + obj.ClearanceHeight = bb.ZMax + 5.0 + obj.SafeHeight = bb.ZMax + 3.0 + obj.RetractHeight = bb.ZMax + 1.0 + + if fbb.ZMax < bb.ZMax: + obj.FinalDepth = fbb.ZMax + else: + obj.FinalDepth = bb.ZMin + except: + obj.StartDepth = 5.0 + obj.ClearanceHeight = 10.0 + obj.SafeHeight = 8.0 + obj.RetractHeight = 6.0 + if item in baselist: FreeCAD.Console.PrintWarning("Drillable location already in the list"+ "\n") else: @@ -151,10 +169,6 @@ class _ViewProviderDrill: def getIcon(self): #optional return ":/icons/Path-Drilling.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 @@ -188,8 +202,6 @@ class CommandPathDrilling: return not FreeCAD.ActiveDocument is None def Activated(self): - import Path, Part - # if everything is ok, execute and register the transaction in the undo/redo stack FreeCAD.ActiveDocument.openTransaction(translate("Path_Drilling","Create Drilling")) @@ -221,14 +233,14 @@ class TaskPanel: FreeCADGui.ActiveDocument.resetEdit() FreeCADGui.Control.closeDialog() FreeCAD.ActiveDocument.recompute() - FreeCADGui.Selection.removeObserver(self.s) + FreeCADGui.Selection.removeObserver(self.s) def reject(self): FreeCADGui.Control.closeDialog() FreeCAD.ActiveDocument.recompute() - FreeCADGui.Selection.removeObserver(self.s) + FreeCADGui.Selection.removeObserver(self.s) - def getFields(self): + def getFields(self): if self.obj: if hasattr(self.obj,"StartDepth"): self.obj.StartDepth = self.form.startDepth.text() @@ -248,7 +260,7 @@ class TaskPanel: def open(self): self.s =SelObserver() # install the function mode resident - FreeCADGui.Selection.addObserver(self.s) + FreeCADGui.Selection.addObserver(self.s) def addBase(self): # check that the selection contains exactly what we want @@ -261,14 +273,15 @@ class TaskPanel: if s.HasSubObjects: for i in s.SubElementNames: self.obj.Proxy.addDrillableLocation(self.obj, s.Object, i) - else: + else: self.obj.Proxy.addDrillableLocation(self.obj, s.Object) + self.setupUi() #defaults may have changed. Reload. self.form.baseList.clear() - for i in self.obj.Base: + 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: @@ -288,7 +301,7 @@ class TaskPanel: objstring = i.text().partition(".") obj = FreeCAD.ActiveDocument.getObject(objstring[0]) # sub = o.Shape.getElement(objstring[2]) - if objstring[2] != "": + if objstring[2] != "": FreeCADGui.Selection.addSelection(obj,objstring[2]) else: FreeCADGui.Selection.addSelection(obj) @@ -321,29 +334,28 @@ class TaskPanel: self.form.retractHeight.setText(str(self.obj.RetractHeight.Value)) - for i in self.obj.Base: + 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) + self.form.finalDepth.editingFinished.connect(self.getFields) + self.form.safeHeight.editingFinished.connect(self.getFields) + self.form.clearanceHeight.editingFinished.connect(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.addBase.clicked.connect(self.addBase) + self.form.deleteBase.clicked.connect(self.deleteBase) + self.form.reorderBase.clicked.connect(self.reorderBase) self.form.baseList.itemSelectionChanged.connect(self.itemActivated) class SelObserver: def __init__(self): - import PathScripts.PathSelection as PST + import PathScripts.PathSelection as PST PST.drillselect() def __del__(self): - import PathScripts.PathSelection as PST + import PathScripts.PathSelection as PST PST.clear() def addSelection(self,doc,obj,sub,pnt): # Selection object @@ -351,7 +363,7 @@ class SelObserver: FreeCADGui.updateGui() -if FreeCAD.GuiUp: +if FreeCAD.GuiUp: # register the FreeCAD command FreeCADGui.addCommand('Path_Drilling',CommandPathDrilling()) diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py index 6ef7ddcef..b74ec914f 100644 --- a/src/Mod/Path/PathScripts/PathEngrave.py +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -22,10 +22,10 @@ #* * #*************************************************************************** -import FreeCAD,FreeCADGui,Path,PathGui,Draft +import FreeCAD,FreeCADGui,Path,Draft from PySide import QtCore,QtGui -from PathScripts import PathUtils,PathProject +from PathScripts import PathUtils """Path Engrave object and FreeCAD command""" @@ -40,16 +40,19 @@ except AttributeError: class ObjectPathEngrave: - + def __init__(self,obj): obj.addProperty("App::PropertyLinkSubList","Base","Path","The base geometry of this object") + 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'] - + + #Tool Properties obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("Path","The tool number in use")) - obj.ToolNumber = (0,0,1000,1) + obj.ToolNumber = (0,0,1000,1) obj.setEditorMode('ToolNumber',1) #make this read only #Depth Properties @@ -58,7 +61,6 @@ class ObjectPathEngrave: 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::PropertyInteger","StartVertex","Path","The vertex index to start the path from") - #Feed Properties if FreeCAD.GuiUp: _ViewProviderEngrave(obj.ViewObject) @@ -70,22 +72,22 @@ class ObjectPathEngrave: def __setstate__(self,state): return None - + def execute(self,obj): output = "" - + toolLoad = PathUtils.getLastToolLoad(obj) if toolLoad == None: self.vertFeed = 100 self.horizFeed = 100 - radius = 0.25 - obj.ToolNumber= 0 + self.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 + self.radius = tool.Diameter/2 + obj.ToolNumber= toolLoad.ToolNumber if obj.Base: for o in obj.Base: @@ -111,10 +113,10 @@ class ObjectPathEngrave: for wire in wires: offset = wire - + # reorder the wire offset = DraftGeomUtils.rebaseWire(offset,obj.StartVertex) - + # we create the path from the offset shape last = None for edge in offset.Edges: @@ -153,6 +155,24 @@ class ObjectPathEngrave: def addShapeString(self, obj, ss): baselist = obj.Base + if len(baselist) == 0: #When adding the first base object, guess at heights + try: + bb = ss.Shape.BoundBox #parent boundbox + subobj = ss.Shape.getElement(sub) + fbb = subobj.BoundBox #feature boundbox + obj.StartDepth = bb.ZMax + obj.ClearanceHeight = bb.ZMax + 5.0 + obj.SafeHeight = bb.ZMax + 3.0 + + if fbb.ZMax < bb.ZMax: + obj.FinalDepth = fbb.ZMax + else: + obj.FinalDepth = bb.ZMin + except: + obj.StartDepth = 5.0 + obj.ClearanceHeight = 10.0 + obj.SafeHeight = 8.0 + item = (ss, "") if item in baselist: FreeCAD.Console.PrintWarning("ShapeString already in the Engraving list"+ "\n") @@ -198,9 +218,9 @@ class CommandPathEngrave: def IsActive(self): return not FreeCAD.ActiveDocument is None - + def Activated(self): - + # if everything is ok, execute and register the transaction in the undo/redo stack FreeCAD.ActiveDocument.openTransaction("Create Engrave Path") @@ -231,14 +251,14 @@ class TaskPanel: FreeCADGui.ActiveDocument.resetEdit() FreeCADGui.Control.closeDialog() FreeCAD.ActiveDocument.recompute() - FreeCADGui.Selection.removeObserver(self.s) + FreeCADGui.Selection.removeObserver(self.s) def reject(self): FreeCADGui.Control.closeDialog() FreeCAD.ActiveDocument.recompute() - FreeCADGui.Selection.removeObserver(self.s) + FreeCADGui.Selection.removeObserver(self.s) - def getFields(self): + def getFields(self): if self.obj: if hasattr(self.obj,"StartDepth"): self.obj.StartDepth = self.form.startDepth.text() @@ -255,7 +275,7 @@ class TaskPanel: def open(self): self.s =SelObserver() # install the function mode resident - FreeCADGui.Selection.addObserver(self.s) + FreeCADGui.Selection.addObserver(self.s) def addBase(self): # check that the selection contains exactly what we want @@ -271,7 +291,7 @@ class TaskPanel: self.obj.Proxy.addShapeString(self.obj, s.Object) self.form.baseList.clear() - for i in self.obj.Base: + for i in self.obj.Base: self.form.baseList.addItem(i[0].Name) def deleteBase(self): @@ -313,38 +333,35 @@ class TaskPanel: self.form.safeHeight.setText(str(self.obj.SafeHeight.Value)) self.form.clearanceHeight.setText(str(self.obj.ClearanceHeight.Value)) - - for i in self.obj.Base: + for i in self.obj.Base: self.form.baseList.addItem(i[0].Name) #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) + self.form.startDepth.editingFinished.connect(self.getFields) + self.form.finalDepth.editingFinished.connect(self.getFields) + self.form.safeHeight.editingFinished.connect(self.getFields) + self.form.clearanceHeight.editingFinished.connect(self.getFields) - 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) - - QtCore.QObject.connect(self.form.baseList, QtCore.SIGNAL("itemSelectionChanged()"), self.itemActivated) + self.form.addBase.clicked.connect(self.addBase) + self.form.deleteBase.clicked.connect(self.deleteBase) + self.form.reorderBase.clicked.connect(self.reorderBase) + self.form.baseList.itemSelectionChanged.connect(self.itemActivated) class SelObserver: def __init__(self): - import PathScripts.PathSelection as PST + import PathScripts.PathSelection as PST PST.engraveselect() def __del__(self): - import PathScripts.PathSelection as PST + 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: +if FreeCAD.GuiUp: # register the FreeCAD command FreeCADGui.addCommand('Path_Engrave',CommandPathEngrave()) diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py index 4ee8c193a..0096be32d 100644 --- a/src/Mod/Path/PathScripts/PathPocket.py +++ b/src/Mod/Path/PathScripts/PathPocket.py @@ -44,12 +44,12 @@ except AttributeError: class ObjectPocket: - def __init__(self,obj): 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")) obj.Algorithm = ['OCC Native','libarea'] @@ -113,19 +113,35 @@ class ObjectPocket: return None def addpocketbase(self, obj, ss, sub=""): - #sublist = [] - #sublist.append(sub) baselist = obj.Base if baselist == None: baselist = [] + if len(baselist) == 0: #When adding the first base object, guess at heights + try: + bb = ss.Shape.BoundBox #parent boundbox + subobj = ss.Shape.getElement(sub) + fbb = subobj.BoundBox #feature boundbox + obj.StartDepth = bb.ZMax + obj.ClearanceHeight = bb.ZMax + 5.0 + obj.SafeHeight = bb.ZMax + 3.0 + + if fbb.ZMax < bb.ZMax: + obj.FinalDepth = fbb.ZMax + else: + obj.FinalDepth = bb.ZMin + except: + obj.StartDepth = 5.0 + obj.ClearanceHeight = 10.0 + obj.SafeHeight = 8.0 + 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) + print "this base is: " + str(baselist) + self.execute(obj) def getStock(self,obj): "retrieves a stock object from hosting project if any" @@ -140,16 +156,14 @@ class ObjectPocket: return None def buildpathlibarea(self, obj, a): - import PathScripts.PathUtils as PathUtils import PathScripts.PathAreaUtils as PathAreaUtils from PathScripts.PathUtils import depth_params import area - + FreeCAD.Console.PrintMessage(translate("PathPocket","Generating toolpath with libarea offsets.\n")) - + depthparams = depth_params (obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value) - - horizfeed = self.horizFeed + extraoffset = obj.MaterialAllowance.Value stepover = obj.StepOver use_zig_zag = obj.UseZigZag @@ -159,7 +173,7 @@ class ObjectPocket: zig_unidirectional = obj.ZigUnidirectional start_point = None cut_mode = obj.CutMode - + PathAreaUtils.flush_nc() PathAreaUtils.output('mem') PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed) @@ -214,7 +228,7 @@ class ObjectPocket: retstr += str("%.4f" % self.horizFeed) else: retstr += str("%.4f" % self.vertFeed) - + if (x != None) or (y != None) or (z != None): if (x != None): retstr += " X" + str("%.4f" % x) @@ -235,7 +249,7 @@ class ObjectPocket: if (math.sqrt((cx - sx)**2 + (cy - sy)**2) - math.sqrt((cx - ex)**2 + (cy - ey)**2)) >= eps: print "ERROR: Illegal arc: Stand and end radii not equal" return "" - + #Set [C]CW and feed retstr = "" if ccw: @@ -243,17 +257,17 @@ class ObjectPocket: else: retstr += "G02 F" retstr += str(self.horizFeed) - + #End location retstr += " X" + str("%.4f" % ex) + " Y" + str("%.4f" % ey) - + #Helix if requested if ez != None: retstr += " Z" + str("%.4f" % ez) - + #Append center offsets retstr += " I" + str("%.4f" % (cx - sx)) + " J" + str("%.4f" % (cy - sy)) - + return retstr + "\n" def helicalPlunge(plungePos, rampangle, destZ, startZ): @@ -270,14 +284,14 @@ class ObjectPocket: helixX = plungePos.x + tool.Diameter/2. * plungeR helixY = plungePos.y; - + helixCirc = math.pi * tool.Diameter * plungeR dzPerRev = math.sin(rampangle/180. * math.pi) * helixCirc #Go to the start of the helix position helixCmds += rapid(helixX, helixY) helixCmds += rapid(z=startZ) - + #Helix as required to get to the requested depth lastZ = startZ curZ = max(startZ-dzPerRev, destZ) @@ -286,14 +300,14 @@ class ObjectPocket: done = (curZ == destZ) #NOTE: FreeCAD doesn't render this, but at least LinuxCNC considers it valid #helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX, helixY, ez = curZ, ccw=True) - + #Use two half-helixes; FreeCAD renders that correctly, #and it fits with the other code breaking up 360-degree arcs helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX - tool.Diameter * plungeR, helixY, ez = (curZ + lastZ)/2., ccw=True) helixCmds += arc(plungePos.x, plungePos.y, helixX - tool.Diameter * plungeR, helixY, helixX, helixY, ez = curZ, ccw=True) lastZ = curZ curZ = max(curZ - dzPerRev, destZ) - + return helixCmds def rampPlunge(edge, rampangle, destZ, startZ): @@ -310,8 +324,8 @@ class ObjectPocket: return None if(not tool): raise Error("Ramp plunging requires a tool!") - - + + sPoint = edge.Vertexes[0].Point ePoint = edge.Vertexes[1].Point #Evidently edges can get flipped- pick the right one in this case @@ -320,21 +334,21 @@ class ObjectPocket: #print "FLIP" ePoint = edge.Vertexes[-1].Point #print "Start: " + str(sPoint) + " End: " + str(ePoint) + " Zhigh: " + prnt(startZ) + " ZLow: " + prnt(destZ) - + rampDist = edge.Length rampDZ = math.sin(rampangle/180. * math.pi) * rampDist - + rampCmds += rapid(sPoint.x, sPoint.y) rampCmds += rapid(z=startZ) - + #Ramp down to the requested depth #FIXME: This might be an arc, so handle that as well - lastZ = startZ + curZ = max(startZ-rampDZ, destZ) done = False while not done: done = (curZ == destZ) - + #If it's an arc, handle it! if isinstance(edge.Curve,Part.Circle): raise Error("rampPlunge: Screw it, not handling an arc.") @@ -343,12 +357,9 @@ class ObjectPocket: rampCmds += feed(ePoint.x, ePoint.y, curZ) rampCmds += feed(sPoint.x, sPoint.y) - lastZ = curZ curZ = max(curZ - rampDZ, destZ) - - return rampCmds - + return rampCmds output = "" offsets = [] @@ -360,12 +371,12 @@ class ObjectPocket: offsets.extend(result) nextradius += self.radius result = DraftGeomUtils.pocket2d(shape,nextradius) - + # first move will be rapid, subsequent will be at feed rate first = True startPoint = None 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() @@ -378,7 +389,7 @@ class ObjectPocket: #Fraction of tool radius our plunge helix is to be plungeR = obj.HelixSize - + #(minimum) Fraction of tool DIAMETER to go back and forth while ramp-plunging #FIXME: The ramp plunging should maybe even be limited to this distance; I don't know what's best rampD = obj.RampSize @@ -386,8 +397,8 @@ class ObjectPocket: #Total offset from the desired pocket edge is tool radius plus the plunge helix radius #Any point on these curves could be the center of a plunge helixBounds = DraftGeomUtils.pocket2d(shape, self.radius * (1 + plungeR)) - - + + #Try to find a location to nicely plunge, starting with a helix, then ramp #Can't do it without knowledge of a tool plungePos = None @@ -400,7 +411,7 @@ class ObjectPocket: #Since we're going to start machining either the inner-most #edge or the outer (depending on StartAt setting), try to #plunge near that location - + if helixBounds and obj.UseEntry: #Edge is easy- pick a point on helixBounds and go with it if obj.StartAt == 'Edge': @@ -408,7 +419,7 @@ class ObjectPocket: #Center is harder- use a point from the first offset, check if it works else: plungePos = offsets[0].Edges[0].Vertexes[0].Point - + #If it turns out this is invalid for some reason, nuke plungePos [perp,idx] = DraftGeomUtils.findPerpendicular(plungePos, shape.Edges) if not perp or perp.Length < self.radius * (1 + plungeR): @@ -416,8 +427,7 @@ class ObjectPocket: #FIXME: Really need to do a point-in-polygon operation to make sure this is within helixBounds #Or some math to prove that it has to be (doubt that's true) #Maybe reverse helixBounds and pick off that? - - + #If we didn't find a place to helix, how about a ramp? if not plungePos and obj.UseEntry: #Check first edge of our offsets @@ -429,7 +439,6 @@ class ObjectPocket: else: print "Neither edge works: " + str(offsets[0].Edges[0]) + ", " + str(offsets[0].Edges[-1]) #FIXME: There's got to be a smarter way to find a place to ramp - #For helix-ing/ramping, know where we were last time #FIXME: Can probably get this from the "machine"? @@ -509,13 +518,13 @@ class ObjectPocket: self.horizFeed = toolLoad.HorizFeed.Value tool = PathUtils.getTool(obj, toolLoad.ToolNumber) self.radius = tool.Diameter/2 - obj.ToolNumber= toolLoad.ToolNumber + obj.ToolNumber= toolLoad.ToolNumber if obj.Base: for b in obj.Base: print "object base: " + str(b) - import Part, PathScripts.PathKurveUtils, DraftGeomUtils + import Part, PathScripts.PathKurveUtils if "Face" in b[1]: print "inside" shape = getattr(b[0].Shape,b[1]) @@ -528,7 +537,6 @@ class ObjectPocket: wire = Part.Wire(edges) shape = None - # output = "" if obj.Algorithm == "OCC Native": if shape == None: @@ -540,7 +548,7 @@ class ObjectPocket: 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') @@ -564,8 +572,7 @@ class ObjectPocket: ##This puts out some interesting information from libarea print a.text() ######## - - + a.Reorder() output += self.buildpathlibarea(obj, a) @@ -587,7 +594,7 @@ class _CommandSetPocketStartPoint: def IsActive(self): return not FreeCAD.ActiveDocument is None - + def setpoint(self,point,o): obj=FreeCADGui.Selection.getSelection()[0] obj.StartPoint.x = point.x @@ -636,7 +643,7 @@ class CommandPathPocket: return not FreeCAD.ActiveDocument is None def Activated(self): - + # check that the selection contains exactly what we want # selection = FreeCADGui.Selection.getSelectionEx() # if len(selection) != 1: @@ -672,18 +679,18 @@ class CommandPathPocket: 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")) FreeCADGui.addModule("PathScripts.PathPocket") - - FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Pocket")') + + FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Pocket")') FreeCADGui.doCommand('PathScripts.PathPocket.ObjectPocket(obj)') FreeCADGui.doCommand('obj.Active = True') FreeCADGui.doCommand('PathScripts.PathPocket.ViewProviderPocket(obj.ViewObject)') - + FreeCADGui.doCommand('from PathScripts import PathUtils') FreeCADGui.doCommand('obj.StepOver = 1.0') @@ -698,7 +705,7 @@ class CommandPathPocket: FreeCADGui.doCommand('obj.HelixSize = 0.75') FreeCADGui.doCommand('PathScripts.PathUtils.addToProject(obj)') - + FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() FreeCADGui.doCommand('obj.ViewObject.startEditing()') @@ -716,14 +723,14 @@ class TaskPanel: FreeCADGui.ActiveDocument.resetEdit() FreeCADGui.Control.closeDialog() FreeCAD.ActiveDocument.recompute() - FreeCADGui.Selection.removeObserver(self.s) + FreeCADGui.Selection.removeObserver(self.s) def reject(self): FreeCADGui.Control.closeDialog() FreeCAD.ActiveDocument.recompute() - FreeCADGui.Selection.removeObserver(self.s) + FreeCADGui.Selection.removeObserver(self.s) - def getFields(self): + def getFields(self): if self.obj: if hasattr(self.obj,"StartDepth"): self.obj.StartDepth = self.form.startDepth.text() @@ -748,7 +755,7 @@ class TaskPanel: def open(self): self.s =SelObserver() # install the function mode resident - FreeCADGui.Selection.addObserver(self.s) + FreeCADGui.Selection.addObserver(self.s) def addBase(self): # check that the selection contains exactly what we want @@ -761,13 +768,14 @@ class TaskPanel: if s.HasSubObjects: for i in s.SubElementNames: self.obj.Proxy.addpocketbase(self.obj, s.Object, i) - else: + else: self.obj.Proxy.addpocketbase(self.obj, s.Object) + self.setupUi() #defaults may have changed. Reload. self.form.baseList.clear() - for i in self.obj.Base: + for i in self.obj.Base: self.form.baseList.addItem(i[0].Name + "." + i[1]) - + def deleteBase(self): dlist = self.form.baseList.selectedItems() newlist = [] @@ -787,7 +795,7 @@ class TaskPanel: objstring = i.text().partition(".") obj = FreeCAD.ActiveDocument.getObject(objstring[0]) # sub = o.Shape.getElement(objstring[2]) - if objstring[2] != "": + if objstring[2] != "": FreeCADGui.Selection.addSelection(obj,objstring[2]) else: FreeCADGui.Selection.addSelection(obj) @@ -829,7 +837,7 @@ class TaskPanel: if index >= 0: self.form.algorithmSelect.setCurrentIndex(index) - for i in self.obj.Base: + for i in self.obj.Base: self.form.baseList.addItem(i[0].Name + "." + i[1]) #Connect Signals and Slots @@ -840,35 +848,35 @@ class TaskPanel: self.form.reorderBase.clicked.connect(self.reorderBase) #Depths - self.form.startDepth.editingFinished.connect(self.getFields) + 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 + import PathScripts.PathSelection as PST PST.pocketselect() def __del__(self): - import PathScripts.PathSelection as PST + 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: +if FreeCAD.GuiUp: # register the FreeCAD command FreeCADGui.addCommand('Path_Pocket',CommandPathPocket()) FreeCADGui.addCommand('Set_PocketStartPoint',_CommandSetPocketStartPoint()) diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py index aeea2a426..42327dce3 100644 --- a/src/Mod/Path/PathScripts/PathProfile.py +++ b/src/Mod/Path/PathScripts/PathProfile.py @@ -59,10 +59,10 @@ class ObjectProfile: 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','libareal'] + obj.Algorithm = ['OCC Native','libarea'] obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("Path","The tool number in use")) - obj.ToolNumber = (0,0,1000,1) + obj.ToolNumber = (0,0,1000,1) obj.setEditorMode('ToolNumber',1) #make this read only #Depth Properties @@ -72,7 +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")) - + #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")) @@ -121,6 +121,24 @@ class ObjectProfile: def addprofilebase(self, obj, ss, sub=""): baselist = obj.Base + if len(baselist) == 0: #When adding the first base object, guess at heights + try: + bb = ss.Shape.BoundBox #parent boundbox + subobj = ss.Shape.getElement(sub) + fbb = subobj.BoundBox #feature boundbox + obj.StartDepth = bb.ZMax + obj.ClearanceHeight = bb.ZMax + 5.0 + obj.SafeHeight = bb.ZMax + 3.0 + + if fbb.ZMax < bb.ZMax: + obj.FinalDepth = fbb.ZMax + else: + obj.FinalDepth = bb.ZMin + except: + obj.StartDepth = 5.0 + obj.ClearanceHeight = 10.0 + obj.SafeHeight = 8.0 + item = (ss, sub) if item in baselist: FreeCAD.Console.PrintWarning("this object already in the list"+ "\n") @@ -164,7 +182,7 @@ class ObjectProfile: output = "" if obj.StartPoint and obj.UseStartPoint: - startpoint = obj.StartPoint + startpoint = obj.StartPoint else: startpoint = None @@ -198,7 +216,7 @@ print "y - " + str(point.y) 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. @@ -212,7 +230,7 @@ print "y - " + str(point.y) 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, \ @@ -241,17 +259,17 @@ print "y - " + str(point.y) self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 - obj.ToolNumber= 0 + 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 + obj.ToolNumber= toolLoad.ToolNumber if obj.Base: for b in obj.Base: - + # we only consider the outer wire if this is a Face #shape = getattr(obj.Base[0][0].Shape,obj.Base[0][1]) shape = getattr(b[0].Shape,b[1]) @@ -259,7 +277,7 @@ print "y - " + str(point.y) 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() @@ -268,10 +286,10 @@ print "y - " + str(point.y) edgelist = wire.Edges edgelist = Part.__sortEdges__(edgelist) - + if obj.Algorithm == "OCC Native": output += self._buildPathOCC(obj, wire) - + else: try: import area @@ -325,7 +343,7 @@ class _CommandAddTag: def IsActive(self): return not FreeCAD.ActiveDocument is None - + def setpoint(self,point,o): obj=FreeCADGui.Selection.getSelection()[0] obj.StartPoint.x = point.x @@ -334,7 +352,7 @@ class _CommandAddTag: h = obj.heights l = obj.lengths a = obj.angles - + x = point.x y = point.y z = float(0.0) @@ -360,7 +378,7 @@ class _CommandSetStartPoint: def IsActive(self): return not FreeCAD.ActiveDocument is None - + def setpoint(self,point,o): obj=FreeCADGui.Selection.getSelection()[0] obj.StartPoint.x = point.x @@ -377,7 +395,7 @@ class _CommandSetEndPoint: def IsActive(self): return not FreeCAD.ActiveDocument is None - + def setpoint(self,point,o): obj=FreeCADGui.Selection.getSelection()[0] obj.EndPoint.x = point.x @@ -396,7 +414,7 @@ class CommandPathProfile: def IsActive(self): return not FreeCAD.ActiveDocument is None - + def Activated(self): #import Path #from PathScripts import PathProject, PathUtils, PathKurveUtils @@ -416,7 +434,7 @@ class CommandPathProfile: FreeCADGui.doCommand('obj.StepDown = 1.0') FreeCADGui.doCommand('obj.StartDepth= ' + str(ztop)) FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom)) - + FreeCADGui.doCommand('obj.SafeHeight = '+ str(ztop + 2.0)) FreeCADGui.doCommand('obj.Side = "Left"') FreeCADGui.doCommand('obj.OffsetExtra = 0.0') @@ -441,14 +459,14 @@ class TaskPanel: FreeCADGui.ActiveDocument.resetEdit() FreeCADGui.Control.closeDialog() FreeCAD.ActiveDocument.recompute() - FreeCADGui.Selection.removeObserver(self.s) + FreeCADGui.Selection.removeObserver(self.s) def reject(self): FreeCADGui.Control.closeDialog() FreeCAD.ActiveDocument.recompute() - FreeCADGui.Selection.removeObserver(self.s) + FreeCADGui.Selection.removeObserver(self.s) - def getFields(self): + def getFields(self): if self.obj: if hasattr(self.obj,"StartDepth"): self.obj.StartDepth = self.form.startDepth.text() @@ -483,7 +501,7 @@ class TaskPanel: def open(self): self.s =SelObserver() # install the function mode resident - FreeCADGui.Selection.addObserver(self.s) + FreeCADGui.Selection.addObserver(self.s) def addBase(self): # check that the selection contains exactly what we want @@ -496,13 +514,13 @@ class TaskPanel: if s.HasSubObjects: for i in s.SubElementNames: self.obj.Proxy.addprofilebase(self.obj, s.Object, i) - else: + else: self.obj.Proxy.addprofilebase(self.obj, s.Object) - + self.setupUi() #defaults may have changed. Reload. self.form.baseList.clear() - for i in self.obj.Base: + for i in self.obj.Base: self.form.baseList.addItem(i[0].Name + "." + i[1]) - + def deleteBase(self): dlist = self.form.baseList.selectedItems() newlist = [] @@ -522,7 +540,7 @@ class TaskPanel: objstring = i.text().partition(".") obj = FreeCAD.ActiveDocument.getObject(objstring[0]) # sub = o.Shape.getElement(objstring[2]) - if objstring[2] != "": + if objstring[2] != "": FreeCADGui.Selection.addSelection(obj,objstring[2]) else: FreeCADGui.Selection.addSelection(obj) @@ -557,7 +575,7 @@ class TaskPanel: h = [] l = [] a = [] - + 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): @@ -655,7 +673,7 @@ class TaskPanel: self.form.direction.setCurrentIndex(index) - for i in self.obj.Base: + for i in self.obj.Base: self.form.baseList.addItem(i[0].Name + "." + i[1]) for i in range(len(self.obj.locs)): @@ -677,14 +695,14 @@ class TaskPanel: self.form.reorderBase.clicked.connect(self.reorderBase) #Depths - self.form.startDepth.editingFinished.connect(self.getFields) + 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) @@ -695,7 +713,7 @@ class TaskPanel: 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) @@ -704,11 +722,11 @@ class TaskPanel: class SelObserver: def __init__(self): - import PathScripts.PathSelection as PST + import PathScripts.PathSelection as PST PST.profileselect() def __del__(self): - import PathScripts.PathSelection as PST + import PathScripts.PathSelection as PST PST.clear() def addSelection(self,doc,obj,sub,pnt): # Selection object @@ -716,7 +734,7 @@ class SelObserver: FreeCADGui.updateGui() -if FreeCAD.GuiUp: +if FreeCAD.GuiUp: # register the FreeCAD command FreeCADGui.addCommand('Path_Profile',CommandPathProfile()) FreeCADGui.addCommand('Add_Tag',_CommandAddTag()) diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index f15740644..bcac2670b 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -35,10 +35,7 @@ def equals(p1,p2): v = Vector(p2.X,p2.Y,p2.Z) vector = (u.sub(v)) isNull = (round(vector.x,p)==0 and round(vector.y,p)==0 and round(vector.z,p)==0) - return isNull - - - + return isNull def segments(poly): ''' A sequence of (x,y) numeric coordinates pairs ''' @@ -67,6 +64,11 @@ class EGate: def allow(self,doc,obj,sub): return (sub[0:4] == 'Edge') +class MESHGate: + def allow(self,doc,obj,sub): + print obj.TypeId[0:4] == 'Mesh' + return (obj.TypeId[0:4] == 'Mesh') + class ENGRAVEGate: def allow(self,doc,obj,sub): return (obj.Name[0:11] == 'ShapeString') @@ -79,15 +81,15 @@ class DRILLGate: obj = obj.Shape except: return False - if obj.ShapeType == 'Vertex': + if obj.ShapeType == 'Vertex': drillable = True - elif obj.ShapeType == 'Edge': + elif obj.ShapeType == 'Edge': if isinstance(obj.Curve, Part.Circle): - drillable = True - elif obj.ShapeType == 'Face': + drillable = True + elif obj.ShapeType == 'Face': if isinstance(obj.Edges[0].Curve, Part.Circle): - drillable = True - elif obj.ShapeType == 'Wire': + drillable = True + elif obj.ShapeType == 'Wire': if isinstance(obj.Edges[0].Curve, Part.Circle): drillable = True elif obj.ShapeType == 'Solid': @@ -111,7 +113,7 @@ class PROFILEGate: return False - if obj.ShapeType == 'Edge': + if obj.ShapeType == 'Edge': profileable = True elif obj.ShapeType == 'Face': @@ -124,12 +126,12 @@ class PROFILEGate: if sub[0:4] == 'Edge': profileable = True - elif obj.ShapeType == 'Wire': + 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: @@ -143,7 +145,7 @@ class POCKETGate: return False - if obj.ShapeType == 'Edge': + if obj.ShapeType == 'Edge': pocketable = False elif obj.ShapeType == 'Face': @@ -156,12 +158,12 @@ class POCKETGate: # if sub[0:4] == 'Edge': # pocketable = True - # elif obj.ShapeType == 'Wire': + # 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 @@ -183,19 +185,20 @@ def drillselect(): def engraveselect(): FreeCADGui.Selection.addSelectionGate(ENGRAVEGate()) - FreeCAD.Console.PrintWarning("Engrave Select Mode\n") + FreeCAD.Console.PrintWarning("Engraving Select Mode\n") def profileselect(): FreeCADGui.Selection.addSelectionGate(PROFILEGate()) - FreeCAD.Console.PrintWarning("Profile Select Mode\n") + FreeCAD.Console.PrintWarning("Profiling Select Mode\n") def pocketselect(): FreeCADGui.Selection.addSelectionGate(POCKETGate()) - FreeCAD.Console.PrintWarning("Pocket Select Mode\n") + FreeCAD.Console.PrintWarning("Pocketing Select Mode\n") +def surfaceselect(): + FreeCADGui.Selection.addSelectionGate(MESHGate()) + FreeCAD.Console.PrintWarning("Surfacing Select Mode\n") def clear(): FreeCADGui.Selection.removeSelectionGate() FreeCAD.Console.PrintWarning("Free Select\n") - - diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 708bf3bbe..a84070b27 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -23,11 +23,10 @@ #*************************************************************************** import FreeCAD,Path -from FreeCAD import Vector -from PathScripts import PathUtils,PathSelection,PathProject +from PathScripts import PathUtils if FreeCAD.GuiUp: - import FreeCADGui, PathGui + import FreeCADGui from PySide import QtCore, QtGui from DraftTools import translate from pivy import coin @@ -52,12 +51,12 @@ except AttributeError: class ObjectSurface: - def __init__(self,obj): - obj.addProperty("App::PropertyLinkSub","Base","Path",translate("Parent Object(s)","The base geometry of this toolpath")) + obj.addProperty("App::PropertyLinkSubList","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("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 Dropcutter', 'OCL Waterline'] @@ -69,22 +68,48 @@ class ObjectSurface: #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::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::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.")) - #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.Proxy = self + + def addsurfacebase(self, obj, ss, sub=""): + baselist = obj.Base + if len(baselist) == 0: #When adding the first base object, guess at heights + try: + bb = ss.Shape.BoundBox #parent boundbox + subobj = ss.Shape.getElement(sub) + fbb = subobj.BoundBox #feature boundbox + obj.StartDepth = bb.ZMax + obj.ClearanceHeight = bb.ZMax + 5.0 + obj.SafeHeight = bb.ZMax + 3.0 + + if fbb.ZMax < bb.ZMax: + obj.FinalDepth = fbb.ZMax + else: + obj.FinalDepth = bb.ZMin + except: + obj.StartDepth = 5.0 + obj.ClearanceHeight = 10.0 + obj.SafeHeight = 8.0 + + 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 __getstate__(self): return None @@ -93,7 +118,6 @@ class ObjectSurface: def _waterline(self,obj, s, bb): import ocl -# from PathScripts.PathUtils import fmt from PathScripts.PathUtils import depth_params, fmt import time @@ -104,7 +128,7 @@ class ObjectSurface: for loop in loops: p = loop[0] loopstring = "(loop begin)" +"\n" - loopstring += "G0 Z" + str(obj.SafeHeight) +"\n" + loopstring += "G0 Z" + str(obj.SafeHeight.Value) +"\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:]: @@ -117,54 +141,21 @@ class ObjectSurface: nloop = nloop+1 waterlinestring += loopstring waterlinestring += "(waterline end)" +"\n" - return waterlinestring - # 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) - + depthparams = depth_params (obj.ClearanceHeight.Value, obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value) # stlfile = "../../stl/gnu_tux_mod.stl" # surface = STLSurfaceSource(stlfile) - surface = s - t_before = time.time() - + t_before = time.time() zheights= depthparams.get_depths() - - wl = ocl.Waterline() + wl = ocl.Waterline() #wl = ocl.AdaptiveWaterline() # this is slower, ca 60 seconds on i7 CPU wl.setSTL(surface) diam = 0.5 - length= 10 + length= 10.0 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 @@ -194,36 +185,35 @@ class ObjectSurface: cutter = ocl.CylCutter(self.radius*2, 5) pdc = ocl.PathDropCutter() # create a pdc pdc.setSTL(s) - pdc.setCutter(cutter) - pdc.minimumZ = 0.25 + pdc.setCutter(cutter) + pdc.minimumZ = 0.25 pdc.setSampling(obj.SampleInterval) - - # some parameters for this "zigzig" pattern + + # 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 + if (n % 2 == 0): #even l = ocl.Line(p1,p2) # line-object else: #odd l = ocl.Line(p2,p1) # line-object path.append( l ) # add the line to the path - + pdc.setPath( path ) - + # run drop-cutter on the path t_before = time.time() pdc.run() @@ -231,70 +221,66 @@ class ObjectSurface: print "calculation took ", t_after-t_before," s" #retrieve the points - clp = pdc.getCLPoints() + clp = pdc.getCLPoints() print "points received: " + str(len(clp)) #generate the path commands output = "" - output += "G0 Z" + str(obj.ClearanceHeight) + "\n" + output += "G0 Z" + str(obj.ClearanceHeight.Value) + "\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" - + output += "G1 Z" + str(clp[0].z) + " F" + str(self.vertFeed) + "\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 + toolLoad = PathUtils.getLastToolLoad(obj) + if toolLoad == None: + self.vertFeed = 100 + self.horizFeed = 100 self.radius = 0.25 - - - mesh = obj.Base[0] - if mesh.TypeId.startswith('Mesh'): - mesh = mesh.Mesh - bb = mesh.BoundBox + obj.ToolNumber= 0 else: - bb = mesh.Shape.BoundBox - mesh = MeshPart.meshFromShape(mesh.Shape,MaxLength=2) + 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 - 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) + if obj.Base: + for b in obj.Base: + 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 + + mesh = b[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 @@ -315,10 +301,6 @@ class ViewProviderSurface: 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 @@ -327,9 +309,13 @@ class ViewProviderSurface: # 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 @@ -351,48 +337,51 @@ class CommandPathSurfacing: def Activated(self): # check that the selection contains exactly what we want - selection = FreeCADGui.Selection.getSelectionEx() - if len(selection) != 1: - FreeCAD.Console.PrintError(translate("PathSurface","Please select a single solid object from the project tree\n")) - return - if not len(selection[0].SubObjects) == 0: - FreeCAD.Console.PrintError(translate("PathSurface","Please select a single solid object from the project tree\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("PathSurface","Please select only edges or a single face\n")) - return + # selection = FreeCADGui.Selection.getSelectionEx() + # if len(selection) != 1: + # FreeCAD.Console.PrintError(translate("PathSurface","Please select a single solid object from the project tree\n")) + # return + # if not len(selection[0].SubObjects) == 0: + # FreeCAD.Console.PrintError(translate("PathSurface","Please select a single solid object from the project tree\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("PathSurface","Please select only edges or a single face\n")) + # return - 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 + # 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: - FreeCAD.Console.PrintError(translate("PathSurface","Cannot work with this object\n")) - return + # #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: + # FreeCAD.Console.PrintError(translate("PathSurface","Cannot work with this object\n")) + # return # if everything is ok, execute and register the transaction in the undo/redo stack + + ztop = 10 + zbottom = 0 + 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.ObjectSurface(obj)') FreeCADGui.doCommand('obj.Active = True') 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)) @@ -405,9 +394,153 @@ class CommandPathSurfacing: FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() + FreeCADGui.doCommand('obj.ViewObject.startEditing()') -if FreeCAD.GuiUp: +class TaskPanel: + def __init__(self): + self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/SurfaceEdit.ui") + #self.form = FreeCADGui.PySideUic.loadUi(":/SurfaceEdit.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,"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,"Algorithm"): + self.obj.Algorithm = str(self.form.algorithmSelect.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 len(selection) != 1: + FreeCAD.Console.PrintError(translate("PathSurface","Please select a single solid object from the project tree\n")) + return + + if not len(selection[0].SubObjects) == 0: + FreeCAD.Console.PrintError(translate("PathSurface","Please select a single solid object from the project tree\n")) + return + + sel = selection[0].Object + #get type of object + if sel.TypeId.startswith('Mesh'): + #it is a mesh already + print 'was already mesh' + + #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): + print 'this is a solid Part object' + + else: + FreeCAD.Console.PrintError(translate("PathSurface","Cannot work with this object\n")) + return + + self.obj.Proxy.addsurfacebase(self.obj, sel) + + self.setupUi() #defaults may have changed. Reload. + self.form.baseList.clear() + for i in self.obj.Base: + self.form.baseList.addItem(i[0].Name) + + 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(): + 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: + o = FreeCAD.ActiveDocument.getObject(i.text()) + FreeCADGui.Selection.addSelection(o) + FreeCADGui.updateGui() + + def reorderBase(self): + newlist = [] + for i in range(self.form.baseList.count()): + s = self.form.baseList.item(i).text() + obj = FreeCAD.ActiveDocument.getObject(s) + newlist.append(obj) + 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.safeHeight.setText(str(self.obj.SafeHeight.Value)) + self.form.clearanceHeight.setText(str(self.obj.ClearanceHeight.Value)) + + for i in self.obj.Base: + self.form.baseList.addItem(i[0].Name) + + #Connect Signals and Slots + self.form.startDepth.editingFinished.connect(self.getFields) + self.form.finalDepth.editingFinished.connect(self.getFields) + self.form.safeHeight.editingFinished.connect(self.getFields) + self.form.clearanceHeight.editingFinished.connect(self.getFields) + + self.form.addBase.clicked.connect(self.addBase) + self.form.deleteBase.clicked.connect(self.deleteBase) + self.form.reorderBase.clicked.connect(self.reorderBase) + + self.form.baseList.itemSelectionChanged.connect(self.itemActivated) + +class SelObserver: + def __init__(self): + import PathScripts.PathSelection as PST + PST.surfaceselect() + + 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 FreeCADGui.addCommand('Path_Surfacing',CommandPathSurfacing())