diff --git a/src/Mod/Path/Gui/Resources/panels/PathEdit.ui b/src/Mod/Path/Gui/Resources/panels/PathEdit.ui index 868d8b26be..6efce1fc23 100644 --- a/src/Mod/Path/Gui/Resources/panels/PathEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PathEdit.ui @@ -7,7 +7,7 @@ 0 0 400 - 608 + 924 @@ -24,7 +24,7 @@ - 0 + 1 @@ -226,67 +226,184 @@ 0 0 378 - 403 + 719 Layout - + Stock - - - - - Ext. X + + + 0 + + + + + 2 + + + Create Box + + + + + Create Cylinder + + + + + Extend Base Bound Box + + + + + Use Existing + + - - - - Ext. Y + + + + Qt::Horizontal + + + 40 + 6 + + + + + + + + + + + - - - - - 0 - 0 - - + + + + + + + + + + Ext. X + + + + + + + + 0 + 0 + + + + + + + + Ext. Y + + + + + + + + + + Ext. Z + + + + + + + + + + + + + - - - - - - - Ext. Z - + + + + + + + + + + + + + Radius + + + + + + + Height + + + + - - - - - - - - - - - - - - + + + + + + + Length + + + + + + + Width + + + + + + + + + + + + + + + + Height + + + + + @@ -612,7 +729,7 @@ - 1 + 0 @@ -862,6 +979,63 @@
Gui/InputField.h
+ + infoLabel + infoModel + infoMaterial + stock + stockExisting + stockExtXneg + stockExtXpos + stockExtYneg + stockExtYpos + stockExtZneg + stockExtZpos + stockCylinderRadius + stockCylinderHeight + stockBoxLength + stockBoxWidth + stockBoxHeight + orientXAxis + orientYAxis + orientZAxis + setOrigin + moveToOrigin + centerInStock + centerInStockXY + postProcessorOutputFile + postProcessorSetOutputFile + postProcessor + postProcessorArguments + templateDefaultValues + templatePostProcessor + buttonBox + templateGeneral + templateMaterial + templateSetup + templateStock + templateOperations + templateTools + toolControllerList + toolControllerEdit + toolControllerAdd + toolControllerDelete + activeToolController + operationsList + operationEdit + operationDelete + groupBox_5 + defaultSafeHeight + defaultClearanceHeight + groupBox_6 + defaultStepDown + groupBox + lineEdit + lineEdit_2 + groupBox_7 + defaultMillingOp + defaultToolCompensation + diff --git a/src/Mod/Path/PathScripts/PathJob.py b/src/Mod/Path/PathScripts/PathJob.py index 3731de666e..1be27313c4 100644 --- a/src/Mod/Path/PathScripts/PathJob.py +++ b/src/Mod/Path/PathScripts/PathJob.py @@ -27,6 +27,7 @@ import Draft import FreeCAD import PathScripts.PathIconViewProvider as PathIconViewProvider import PathScripts.PathLog as PathLog +import PathScripts.PathStock as PathStock import PathScripts.PathToolController as PathToolController import PathScripts.PathUtil as PathUtil import xml.etree.ElementTree as xml @@ -58,6 +59,24 @@ class JobTemplate: Description = 'desc' ToolController = 'ToolController' +def isResourceClone(obj, propName, resourceName): + '''isResourceClone(obj, propName, resourceName) ... Return True if the given property of obj is a clone of type resourceName.''' + if hasattr(obj, propName): + propLink = getattr(obj, propName) + if hasattr(propLink, 'PathResource') and resourceName == propLink.PathResource: + return True + return False + +def createResourceClone(obj, orig, name, icon): + clone = Draft.clone(orig) + clone.Label = "%s-%s" % (name, orig.Label) + clone.addProperty('App::PropertyString', 'PathResource') + clone.PathResource = name + if clone.ViewObject: + PathIconViewProvider.ViewProvider(clone.ViewObject, icon) + clone.ViewObject.Visibility = False + return clone + class ObjectJob: def __init__(self, obj, base, template = None): @@ -91,10 +110,14 @@ class ObjectJob: obj.setEditorMode('Operations', 2) # hide obj.setEditorMode('Placement', 2) - self.createResourceClone(obj, base, 'Base') + obj.Base = createResourceClone(obj, base, 'Base', 'BaseGeometry') obj.Proxy = self self.assignTemplate(obj, template) + if not obj.Stock: + obj.Stock = PathStock.CreateFromBase(obj) + if obj.Stock.ViewObject: + obj.Stock.ViewObject.Visibility = False def onDelete(self, obj, arg2=None): '''Called by the view provider, there doesn't seem to be a callback on the obj itself.''' @@ -115,36 +138,18 @@ class ObjectJob: doc.removeObject(obj.Stock.Name) obj.Stock = None - def isResourceClone(self, obj, propName, resourceName): - if hasattr(obj, propName): - propLink = getattr(obj, propName) - if hasattr(propLink, 'PathResource') and resourceName == propLink.PathResource: - return True - return False - - def createResourceClone(self, obj, orig, name): - clone = Draft.clone(orig) - clone.Label = "%s-%s" % (name, orig.Label) - clone.addProperty('App::PropertyString', 'PathResource') - clone.PathResource = name - if clone.ViewObject: - PathIconViewProvider.ViewProvider(clone.ViewObject, 'BaseGeometry') - clone.ViewObject.Visibility = False - setattr(obj, name, clone) - - def fixupResourceClone(self, obj, name): - if not self.isResourceClone(obj, name, name): + def fixupResourceClone(self, obj, name, icon): + if not isResourceClone(obj, name, name): orig = getattr(obj, name) if orig: - self.createResourceClone(obj, orig, name) + setattr(obj, name, createResourceClone(obj, orig, name, icon)) def onDocumentRestored(self, obj): - self.fixupResourceClone(obj, 'Base') - self.fixupResourceClone(obj, 'Stock') + self.fixupResourceClone(obj, 'Base', 'BaseGeometry') def baseObject(self, obj): '''Return the base object, not its clone.''' - if self.isResourceClone(obj, 'Base', 'Base'): + if isResourceClone(obj, 'Base', 'Base'): return obj.Base.Objects[0] return obj.Base diff --git a/src/Mod/Path/PathScripts/PathJobGui.py b/src/Mod/Path/PathScripts/PathJobGui.py index 65b7b7a309..8defe4fb2c 100644 --- a/src/Mod/Path/PathScripts/PathJobGui.py +++ b/src/Mod/Path/PathScripts/PathJobGui.py @@ -28,8 +28,10 @@ import FreeCAD import FreeCADGui import PathScripts.PathJob as PathJob import PathScripts.PathLog as PathLog +import PathScripts.PathStock as PathStock import PathScripts.PathToolController as PathToolController import PathScripts.PathToolLibraryManager as PathToolLibraryManager +import PathScripts.PathUtil as PathUtil import math import sys @@ -104,6 +106,223 @@ class ViewProvider: self.obj.Proxy.onDelete(self.obj, arg2) return True +class StockEdit(object): + + def __init__(self, obj, form): + self.obj = obj + self.form = form + self.setupUi(obj) + + def activate(self, obj, select = False): + PathLog.track(obj.Label, select) + def showHide(widget, activeWidget): + if widget == activeWidget: + widget.show() + else: + widget.hide() + if select: + self.form.stock.setCurrentIndex(self.Index) + editor = self.editorFrame() + showHide(self.form.stockFromExisting, editor) + showHide(self.form.stockFromBase, editor) + showHide(self.form.stockCreateBox, editor) + showHide(self.form.stockCreateCylinder, editor) + self.setFields(obj) + + def setStock(self, obj, stock): + if obj.Stock: + obj.Document.removeObject(self.obj.Stock.Name) + obj.Stock = stock + + def setLengthField(self, widget, prop): + widget.setText(FreeCAD.Units.Quantity(prop.Value, FreeCAD.Units.Length).UserString) + +class StockFromBaseBoundBoxEdit(StockEdit): + Index = 2 + StockType = 'FromBase' + + @classmethod + def IsStock(cls, obj): + return hasattr(obj.Stock, 'ExtXneg') and hasattr(obj.Stock, 'ExtZpos') + + def editorFrame(self): + return self.form.stockFromBase + + def getFields(self, obj, fields = ['xneg', 'xpos', 'yneg', 'ypos', 'zneg', 'zpos']): + PathLog.track(obj.Label, fields) + if self.IsStock(obj): + if 'xneg' in fields: + obj.Stock.ExtXneg = FreeCAD.Units.Quantity(self.form.stockExtXneg.text()) + if 'xpos' in fields: + obj.Stock.ExtXpos = FreeCAD.Units.Quantity(self.form.stockExtXpos.text()) + if 'yneg' in fields: + obj.Stock.ExtYneg = FreeCAD.Units.Quantity(self.form.stockExtYneg.text()) + if 'ypos' in fields: + obj.Stock.ExtYpos = FreeCAD.Units.Quantity(self.form.stockExtYpos.text()) + if 'zneg' in fields: + obj.Stock.ExtZneg = FreeCAD.Units.Quantity(self.form.stockExtZneg.text()) + if 'zpos' in fields: + obj.Stock.ExtZpos = FreeCAD.Units.Quantity(self.form.stockExtZpos.text()) + else: + PathLog.error(translate('PathJob', 'Stock not from Base bound box!')) + + def setFields(self, obj): + if not self.IsStock(obj): + self.setStock(obj, PathStock.CreateFromBase(obj)) + self.setLengthField(self.form.stockExtXneg, obj.Stock.ExtXneg) + self.setLengthField(self.form.stockExtXpos, obj.Stock.ExtXpos) + self.setLengthField(self.form.stockExtYneg, obj.Stock.ExtYneg) + self.setLengthField(self.form.stockExtYpos, obj.Stock.ExtYpos) + self.setLengthField(self.form.stockExtZneg, obj.Stock.ExtZneg) + self.setLengthField(self.form.stockExtZpos, obj.Stock.ExtZpos) + + def setupUi(self, obj): + self.setFields(obj) + self.checkXpos() + self.checkYpos() + self.checkZpos() + self.form.stockExtXneg.editingFinished.connect(self.updateXpos) + self.form.stockExtYneg.editingFinished.connect(self.updateYpos) + self.form.stockExtZneg.editingFinished.connect(self.updateZpos) + self.form.stockExtXpos.editingFinished.connect(self.checkXpos) + self.form.stockExtYpos.editingFinished.connect(self.checkYpos) + self.form.stockExtZpos.editingFinished.connect(self.checkZpos) + + def checkXpos(self): + self.trackXpos = self.form.stockExtXneg.text() == self.form.stockExtXpos.text() + self.getFields(self.obj, ['xpos']) + def checkYpos(self): + self.trackYpos = self.form.stockExtYneg.text() == self.form.stockExtYpos.text() + self.getFields(self.obj, ['ypos']) + def checkZpos(self): + self.trackZpos = self.form.stockExtZneg.text() == self.form.stockExtZpos.text() + self.getFields(self.obj, ['zpos']) + + def updateXpos(self): + fields = ['xneg'] + if self.trackXpos: + self.form.stockExtXpos.setText(self.form.stockExtXneg.text()) + fields.append('xpos') + self.getFields(self.obj, fields) + def updateYpos(self): + fields = ['yneg'] + if self.trackYpos: + self.form.stockExtYpos.setText(self.form.stockExtYneg.text()) + fields.append('ypos') + self.getFields(self.obj, fields) + def updateZpos(self): + fields = ['zneg'] + if self.trackZpos: + self.form.stockExtZpos.setText(self.form.stockExtZneg.text()) + fields.append('zpos') + self.getFields(self.obj, fields) + +class StockCreateBoxEdit(StockEdit): + Index = 0 + StockType = 'CreateBox' + + @classmethod + def IsStock(cls, obj): + return hasattr(obj.Stock, 'Length') and hasattr(obj.Stock, 'Width') and hasattr(obj.Stock, 'Height') and not StockFromBaseBoundBoxEdit.IsStock(obj) + + def editorFrame(self): + return self.form.stockCreateBox + + def getFields(self, obj, fields = ['length', 'widht', 'height']): + if self.IsStock(obj): + if 'length' in fields: + obj.Stock.Length = FreeCAD.Units.Quantity(self.form.stockBoxLength.text()) + if 'width' in fields: + obj.Stock.Width = FreeCAD.Units.Quantity(self.form.stockBoxWidth.text()) + if 'height' in fields: + obj.Stock.Height = FreeCAD.Units.Quantity(self.form.stockBoxHeight.text()) + else: + PathLog.error(translate('PathJob', 'Stock not a box!')) + + def setFields(self, obj): + if not self.IsStock(obj): + self.setStock(obj, PathStock.CreateBox(obj)) + self.setLengthField(self.form.stockBoxLength, obj.Stock.Length) + self.setLengthField(self.form.stockBoxWidth, obj.Stock.Width) + self.setLengthField(self.form.stockBoxHeight, obj.Stock.Height) + + def setupUi(self, obj): + self.setFields(obj) + self.form.stockBoxLength.editingFinished.connect(lambda: self.getFields(obj, ['length'])) + self.form.stockBoxWidth.editingFinished.connect(lambda: self.getFields(obj, ['width'])) + self.form.stockBoxHeight.editingFinished.connect(lambda: self.getFields(obj, ['height'])) + +class StockCreateCylinderEdit(StockEdit): + Index = 1 + + @classmethod + def IsStock(cls, obj): + return hasattr(obj.Stock, 'Radius') and hasattr(obj.Stock, 'Height') + + def editorFrame(self): + return self.form.stockCreateCylinder + + def getFields(self, obj, fields = ['radius', 'height']): + if self.IsStock(obj): + if 'radius' in fields: + obj.Stock.Radius = FreeCAD.Units.Quantity(self.form.stockCylinderRadius.text()) + if 'height' in fields: + obj.Stock.Height = FreeCAD.Units.Quantity(self.form.stockCylinderHeight.text()) + else: + PathLog.error(translate('PathJob', 'Stock not a cylinder!')) + + def setFields(self, obj): + if not self.IsStock(obj): + self.setStock(obj, PathStock.CreateCylinder(obj)) + self.setLengthField(self.form.stockCylinderRadius, obj.Stock.Radius) + self.setLengthField(self.form.stockCylinderHeight, obj.Stock.Height) + + def setupUi(self, obj): + self.setFields(obj) + self.form.stockCylinderRadius.editingFinished.connect(lambda: self.getFields(obj, ['radius'])) + self.form.stockCylinderHeight.editingFinished.connect(lambda: self.getFields(obj, ['height'])) + +class StockFromExistingEdit(StockEdit): + Index = 3 + + def IsStock(cls, obj): + return PathJob.isResourceClone(obj, 'Stock', 'Stock') + + def editorFrame(self): + return self.form.stockFromExisting + + def getFields(self, obj): + stock = self.form.stockExisting.itemData(self.form.stockExisting.currentIndex()) + if not (hasattr(obj.Stock, 'Objects') and len(obj.Stock.Objects) == 1 and obj.Stock.Objects[0] == stock): + if stock: + stock = PathJob.createResourceClone(obj, stock, 'Stock', 'Stock') + stock.ViewObject.Visibility = True + PathStock.SetupStockObject(stock, False) + stock.Proxy.execute(stock) + self.setStock(obj, stock) + + def setFields(self, obj): + candidates = [o for o in obj.Document.Objects if PathUtil.isSolid(o)] + candidates.remove(obj.Base) + if PathJob.isResourceClone(obj, 'Stock', 'Stock'): + candidates.remove(obj.Stock) + stockName = obj.Stock.Label if obj.Stock else None + + self.form.stockExisting.clear() + index = -1 + for i, c in enumerate(candidates): + self.form.stockExisting.addItem(c.Label, c) + if c.Label == stockName: + index = i + self.form.stockExisting.setCurrentIndex(index if index != -1 else 0) + + if not self.IsStock(obj): + self.getFields(obj) + + def setupUi(self, obj): + self.setFields(obj) + self.form.stockExisting.currentIndexChanged.connect(lambda: self.getFields(obj)) + class TaskPanel: DataObject = QtCore.Qt.ItemDataRole.UserRole DataProperty = QtCore.Qt.ItemDataRole.UserRole + 1 @@ -144,6 +363,17 @@ class TaskPanel: self.baseVisibility = self.obj.Base.ViewObject.Visibility self.baseObjectSaveVisibility(self.obj) + self.stockVisibility = False + if self.obj.Stock and self.obj.Stock.ViewObject: + self.stockVisibility = self.obj.Stock.ViewObject.Visibility + self.obj.Stock.ViewObject.Visibility = True + + self.stockFromBase = None + self.stockFromExisting = None + self.stockCreateBox = None + self.stockCreateCylinder = None + self.stockEdit = None + def baseObjectViewObject(self, obj): base = obj.Proxy.baseObject(obj) body = base.getParentGeoFeatureGroup() @@ -165,6 +395,8 @@ class TaskPanel: if self.obj.Base and self.obj.Base.ViewObject: self.obj.Base.ViewObject.Visibility = self.baseVisibility self.baseObjectRestoreVisibility(self.obj) + if self.obj.Stock and self.obj.Stock.ViewObject: + self.obj.Stock.ViewObject.Visibility = self.stockVisibility def accept(self, resetEdit=True): PathLog.track() @@ -222,8 +454,8 @@ class TaskPanel: self.baseObjectSaveVisibility(self.obj) self.updateTooltips() - - self.obj.Proxy.execute(self.obj) + self.stockEdit.getFields(self.obj) + self.obj.Proxy.execute(self.obj) def selectComboBoxText(self, widget, text): index = widget.findText(text, QtCore.Qt.MatchFixedString) @@ -316,6 +548,8 @@ class TaskPanel: self.form.infoModel.setCurrentIndex(baseindex) self.updateToolController() + self.stockEdit.setFields(self.obj) + def setPostProcessorOutputFile(self): filename = QtGui.QFileDialog.getSaveFileName(self.form, translate("Path_Job", "Select Output File"), None, translate("Path_Job", "All Files (*.*)")) @@ -501,7 +735,58 @@ class TaskPanel: self.form.orientGroup.setEnabled(False) self.form.alignGroup.setEnabled(False) + def updateStockEditor(self, index): + PathLog.track(self.obj.Label, index) + def setupFromBaseEdit(): + PathLog.track() + if not self.stockFromBase: + self.stockFromBase = StockFromBaseBoundBoxEdit(self.obj, self.form) + self.stockEdit = self.stockFromBase + def setupCreateBoxEdit(): + PathLog.track() + if not self.stockCreateBox: + self.stockCreateBox = StockCreateBoxEdit(self.obj, self.form) + self.stockEdit = self.stockCreateBox + def setupCreateCylinderEdit(): + PathLog.track() + if not self.stockCreateCylinder: + self.stockCreateCylinder = StockCreateCylinderEdit(self.obj, self.form) + self.stockEdit = self.stockCreateCylinder + def setupFromExisting(): + PathLog.track() + if not self.stockFromExisting: + self.stockFromExisting = StockFromExistingEdit(self.obj, self.form) + self.stockEdit = self.stockFromExisting + + if index == -1: + PathLog.track('wtf') + if self.obj.Stock is None or StockFromBaseBoundBoxEdit.IsStock(self.obj): + setupFromBaseEdit() + elif StockCreateBoxEdit.IsStock(self.obj): + setupCreateBoxEdit() + elif StockCreateCylinderEdit.IsStock(self.obj): + setupCreateCylinderEdit() + elif StockFromExistingEdit.IsStock(self.obj): + setupFromExisting() + else: + PathLog.error(translate('PathJob', "Unsupported stock object %s") % self.obj.Stock.Label) + else: + PathLog.track(self.obj.Label, index) + if index == StockFromBaseBoundBoxEdit.Index: + setupFromBaseEdit() + elif index == StockCreateBoxEdit.Index: + setupCreateBoxEdit() + elif index == StockCreateCylinderEdit.Index: + setupCreateCylinderEdit() + elif index == StockFromExistingEdit.Index: + setupFromExisting() + else: + PathLog.error(translate('PathJob', "Unsupported stock type %s (%d)") % (self.form.stock.currentText(), index)) + self.stockEdit.activate(self.obj, index == -1) + + def setupUi(self): + self.updateStockEditor(-1) self.setFields() # Info @@ -528,10 +813,11 @@ class TaskPanel: self.toolControllerSelect() # Stock, Orientation and Alignment - self.form.stockGroup.hide() self.form.centerInStock.hide() self.form.centerInStockXY.hide() + self.form.stock.currentIndexChanged.connect(self.updateStockEditor) + self.form.orientXAxis.clicked.connect(lambda: self.orientSelected(FreeCAD.Vector(1, 0, 0))) self.form.orientYAxis.clicked.connect(lambda: self.orientSelected(FreeCAD.Vector(0, 1, 0))) self.form.orientZAxis.clicked.connect(lambda: self.orientSelected(FreeCAD.Vector(0, 0, 1))) diff --git a/src/Mod/Path/PathScripts/PathStock.py b/src/Mod/Path/PathScripts/PathStock.py index b95be0c961..9dbb46c6fc 100644 --- a/src/Mod/Path/PathScripts/PathStock.py +++ b/src/Mod/Path/PathScripts/PathStock.py @@ -24,23 +24,36 @@ import FreeCAD import FreeCADGui -from FreeCAD import Vector -from PySide import QtCore, QtGui import Part +import PathIconViewProvider + +from PySide import QtCore # Qt tanslation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) -class Stock: +class StockFromBase: - def __init__(self, obj): + def __init__(self, obj, base): "Make stock" - obj.addProperty("App::PropertyFloat","Length_Allowance","Stock",QtCore.QT_TRANSLATE_NOOP("App::Property","extra allowance from part width")).Length_Allowance = 1.0 - obj.addProperty("App::PropertyFloat","Width_Allowance","Stock",QtCore.QT_TRANSLATE_NOOP("App::Property","extra allowance from part width")).Width_Allowance = 1.0 - obj.addProperty("App::PropertyFloat","Height_Allowance","Stock",QtCore.QT_TRANSLATE_NOOP("App::Property","extra allowance from part width")).Height_Allowance = 1.0 - obj.addProperty("App::PropertyLink","Base","Base",QtCore.QT_TRANSLATE_NOOP("App::Property","The base object this represents")) + obj.addProperty("App::PropertyLink", "Base", "Base", QtCore.QT_TRANSLATE_NOOP("PathStock", "The base object this stock is derived from")) + obj.addProperty("App::PropertyLength", "ExtXneg", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in negative X direction")) + obj.addProperty("App::PropertyLength", "ExtXpos", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in positive X direction")) + obj.addProperty("App::PropertyLength", "ExtYneg", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in negative Y direction")) + obj.addProperty("App::PropertyLength", "ExtYpos", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in positive Y direction")) + obj.addProperty("App::PropertyLength", "ExtZneg", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in negative Z direction")) + obj.addProperty("App::PropertyLength", "ExtZpos", "Stock", QtCore.QT_TRANSLATE_NOOP("PathStock", "Extra allowance from part bound box in positive Z direction")) + + obj.Base = base + obj.ExtXneg= 1.0 + obj.ExtXpos= 1.0 + obj.ExtYneg= 1.0 + obj.ExtYpos= 1.0 + obj.ExtZneg= 1.0 + obj.ExtZpos= 1.0 + obj.Proxy = self def __getstate__(self): @@ -50,23 +63,70 @@ class Stock: return None def execute(self, obj): - self.Xmin = obj.Base.Shape.BoundBox.XMin - self.Xmax = obj.Base.Shape.BoundBox.XMax + bb = obj.Base.Shape.BoundBox - self.Ymin = obj.Base.Shape.BoundBox.YMin - self.Ymax = obj.Base.Shape.BoundBox.YMax + origin = FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin) + self.origin = origin - FreeCAD.Vector(obj.ExtXneg.Value, obj.ExtYneg.Value, obj.ExtZneg.Value) - self.Zmin = obj.Base.Shape.BoundBox.ZMin - self.Zmax = obj.Base.Shape.BoundBox.ZMax + self.length = bb.XLength + obj.ExtXneg.Value + obj.ExtXpos.Value + self.width = bb.YLength + obj.ExtYneg.Value + obj.ExtYpos.Value + self.height = bb.ZLength + obj.ExtZneg.Value + obj.ExtZpos.Value - self.length = self.Xmax - self.Xmin + obj.Length_Allowance * 2.0 - self.width = self.Ymax - self.Ymin + obj.Width_Allowance * 2.0 - self.height = self.Zmax - self.Zmin + obj.Height_Allowance * 2.0 - self.pnt = Vector(self.Xmin - obj.Length_Allowance, self.Ymin - - obj.Width_Allowance, self.Zmin - obj.Height_Allowance) + obj.Shape = Part.makeBox(self.length, self.width, self.height, self.origin) - obj.Shape = Part.makeBox( - self.length, self.width, self.height, self.pnt) +def SetupStockObject(obj, addVPProxy): + if FreeCAD.GuiUp and obj.ViewObject: + if addVPProxy: + PathIconViewProvider.ViewProvider(obj.ViewObject, 'Stock') + obj.ViewObject.Transparency = 90 + obj.ViewObject.DisplayMode = 'Wireframe' + +def CreateFromBase(job): + obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', 'Stock') + proxy = StockFromBase(obj, job.Base) + SetupStockObject(obj, True) + proxy.execute(obj) + obj.purgeTouched() + return obj + +def CreateBox(job, extent=None, at=None): + obj = FreeCAD.ActiveDocument.addObject('Part::Box', 'Stock') + if extent: + obj.Length = extent.x + obj.Width = extent.y + obj.Height = extent.z + elif job.Base: + bb = job.Base.Shape.BoundBox + obj.Length = bb.XLength + obj.Width = bb.YLength + obj.Height = bb.ZLength + if at: + obj.Placement = FreeCAD.Placement(at, FreeCAD.Vector(), 0) + else: + bb = job.Base.Shape.BoundBox + origin = FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin) + obj.Placement = FreeCAD.Placement(origin, FreeCAD.Vector(), 0) + SetupStockObject(obj, False) + return obj + +def CreateCylinder(job, radius=None, height=None, at=None): + obj = FreeCAD.ActiveDocument.addObject('Part::Cylinder', 'Stock') + if radius: + obj.Radius = radius + if height: + obj.Height = height + elif job.Base: + bb = job.Base.Shape.BoundBox + obj.Radius = max(bb.XLength, bb.YLength) * 0.7072 # 1/sqrt(2) + obj.Height = bb.ZLength + if at: + obj.Placement = FreeCAD.Placement(at, FreeCAD.Vector(), 0) + else: + bb = job.Base.Shape.BoundBox + origin = FreeCAD.Vector((bb.XMin + bb.XMax)/2, (bb.YMin + bb.YMax)/2, bb.ZMin) + obj.Placement = FreeCAD.Placement(origin, FreeCAD.Vector(), 0) + SetupStockObject(obj, False) + return obj class _ViewProviderStock: diff --git a/src/Mod/Path/PathScripts/PathUtil.py b/src/Mod/Path/PathScripts/PathUtil.py index cff2c0230c..7260d62918 100644 --- a/src/Mod/Path/PathScripts/PathUtil.py +++ b/src/Mod/Path/PathScripts/PathUtil.py @@ -37,9 +37,9 @@ import PathScripts.PathLog as PathLog PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) NotValidBaseTypeIds = ['Sketcher::SketchObject'] -def isValidBaseObject(obj): - '''isSolid(obj) ... returns true if an object represents a solid.''' +def isValidBaseObject(obj): + '''isValidBaseObject(obj) ... returns true if the object can be used as a base for a job.''' if not hasattr(obj, 'Shape'): return False if obj.TypeId in NotValidBaseTypeIds: @@ -48,9 +48,23 @@ def isValidBaseObject(obj): return False return True +def isSolid(obj): + '''isSolid(obj) ... return True if the object is a valid solid.''' + if hasattr(obj, 'Tip'): + return isSolid(obj.Tip) + if hasattr(obj, 'Shape'): + if obj.Shape.ShapeType == 'Solid' and obj.Shape.isClosed(): + return True + if obj.Shape.ShapeType == 'Compound': + if hasattr(obj, 'Base') and hasattr(obj, 'Tool'): + return isSolid(obj.Base) and isSolid(obj.Tool) + return False + def toolControllerForOp(op): if hasattr(op, 'ToolController'): return op.ToolController if hasattr(op, 'Base'): return toolControllerForOp(op.Base) return None + +