From 59987231ae087f620d00d63f3e4df07ce38b82cf Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Sat, 17 Dec 2016 14:39:45 -0200 Subject: [PATCH] Arch: Extended panel tools --- src/Mod/Arch/ArchComponent.py | 11 +- src/Mod/Arch/ArchPanel.py | 416 ++++++++++++++- src/Mod/Arch/InitGui.py | 2 +- src/Mod/Arch/Resources/Arch.qrc | 2 + .../Arch/Resources/icons/Arch_Panel_Cut.svg | 139 +++++ .../Arch/Resources/icons/Arch_Panel_Sheet.svg | 484 ++++++++++++++++++ src/Mod/Draft/Draft.py | 8 +- src/Mod/Draft/DraftTools.py | 18 + src/Mod/Draft/importDXF.py | 180 +++++-- 9 files changed, 1195 insertions(+), 65 deletions(-) create mode 100644 src/Mod/Arch/Resources/icons/Arch_Panel_Cut.svg create mode 100644 src/Mod/Arch/Resources/icons/Arch_Panel_Sheet.svg diff --git a/src/Mod/Arch/ArchComponent.py b/src/Mod/Arch/ArchComponent.py index 22f697baec31..ed1aa7df74ff 100644 --- a/src/Mod/Arch/ArchComponent.py +++ b/src/Mod/Arch/ArchComponent.py @@ -63,7 +63,7 @@ def addToComponent(compobject,addobject,mod=None): if compobject == addobject: return # first check zis already there found = False - attribs = ["Additions","Objects","Components","Subtractions","Base"] + attribs = ["Additions","Objects","Components","Subtractions","Base","Group"] for a in attribs: if hasattr(compobject,a): if a == "Base": @@ -89,6 +89,8 @@ def addToComponent(compobject,addobject,mod=None): setattr(compobject,mod,l) if mod != "Objects": addobject.ViewObject.hide() + if Draft.getType(compobject) == "PanelSheet": + addobject.Placement.move(compobject.Placement.Base.negative()) else: for a in attribs[:3]: if hasattr(compobject,a): @@ -106,7 +108,7 @@ def removeFromComponent(compobject,subobject): it is added as a subtraction.''' if compobject == subobject: return found = False - attribs = ["Additions","Subtractions","Objects","Components","Base","Axes","Fixtures"] + attribs = ["Additions","Subtractions","Objects","Components","Base","Axes","Fixtures","Group"] for a in attribs: if hasattr(compobject,a): if a == "Base": @@ -120,6 +122,8 @@ def removeFromComponent(compobject,subobject): l.remove(subobject) setattr(compobject,a,l) subobject.ViewObject.show() + if Draft.getType(compobject) == "PanelSheet": + subobject.Placement.move(compobject.Placement.Base) found = True if not found: if hasattr(compobject,"Subtractions"): @@ -154,7 +158,7 @@ def __init__(self): # the categories are shown only if they are not empty. self.obj = None - self.attribs = ["Base","Additions","Subtractions","Objects","Components","Axes","Fixtures","Armatures"] + self.attribs = ["Base","Additions","Subtractions","Objects","Components","Axes","Fixtures","Armatures","Group"] self.baseform = QtGui.QWidget() self.baseform.setObjectName("TaskPanel") self.grid = QtGui.QGridLayout(self.baseform) @@ -300,6 +304,7 @@ def retranslateUi(self, TaskPanel): self.treeComponents.setText(0,QtGui.QApplication.translate("Arch", "Components", None, QtGui.QApplication.UnicodeUTF8)) self.treeFixtures.setText(0,QtGui.QApplication.translate("Arch", "Fixtures", None, QtGui.QApplication.UnicodeUTF8)) self.treeArmatures.setText(0,QtGui.QApplication.translate("Arch", "Armatures", None, QtGui.QApplication.UnicodeUTF8)) + self.treeGroup.setText(0,QtGui.QApplication.translate("Arch", "Group", None, QtGui.QApplication.UnicodeUTF8)) class Component: "The default Arch Component object" diff --git a/src/Mod/Arch/ArchPanel.py b/src/Mod/Arch/ArchPanel.py index 01193ba53e2c..ebe3e9867563 100644 --- a/src/Mod/Arch/ArchPanel.py +++ b/src/Mod/Arch/ArchPanel.py @@ -57,7 +57,8 @@ def QT_TRANSLATE_NOOP(ctxt,txt): ["Plywood 18mm, 1220 x 2440",1200,2400,18], ["Plywood 25mm, 1220 x 2440",1200,2400,25], ["MDF 3mm, 900 x 600", 900, 600, 3], - ["MDF 6mm, 900 x 600", 900, 600, 6]] + ["MDF 6mm, 900 x 600", 900, 600, 6], + ["OSB 18mm, 1200 x 2400", 1200,2400,18]] def makePanel(baseobj=None,length=0,width=0,thickness=0,placement=None,name="Panel"): '''makePanel([obj],[length],[width],[thickness],[placement]): creates a @@ -67,7 +68,8 @@ def makePanel(baseobj=None,length=0,width=0,thickness=0,placement=None,name="Pan obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) obj.Label = translate("Arch",name) _Panel(obj) - _ViewProviderPanel(obj.ViewObject) + if FreeCAD.GuiUp: + _ViewProviderPanel(obj.ViewObject) if baseobj: obj.Base = baseobj obj.Base.ViewObject.hide() @@ -93,13 +95,37 @@ def makePanelView(panel,page=None,name="PanelView"): page.Template = Draft.getParam("template",FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg') view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython",name) page.addObject(view) - _PanelView(view) + PanelView(view) view.Source = panel - view.Label = translate("Arch","View of")+" "+panel.Name + view.Label = translate("Arch","View of")+" "+panel.Label return view -class _CommandPanel: +def makePanelCut(panel,name="PanelView"): + """makePanelCut(panel) : Creates a 2D view of the given panel + in the 3D space, positioned at the origin.""" + view = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) + PanelCut(view) + view.Source = panel + view.Label = translate("Arch","View of")+" "+panel.Label + if FreeCAD.GuiUp: + ViewProviderPanelCut(view.ViewObject) + return view + + +def makePanelSheet(panels=[],name="PanelSheet"): + """makePanelSheet([panels]) : Creates a sheet with the given panel cuts + in the 3D space, positioned at the origin.""" + sheet = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) + PanelSheet(sheet) + if panels: + sheet.Group = panels + if FreeCAD.GuiUp: + ViewProviderPanelSheet(sheet.ViewObject) + return sheet + + +class CommandPanel: "the Arch Panel command definition" def GetResources(self): return {'Pixmap' : 'Arch_Panel', @@ -264,6 +290,59 @@ def rotate(self): self.rotated = not self.rotated +class CommandPanelCut: + "the Arch Panel Cut command definition" + def GetResources(self): + return {'Pixmap' : 'Arch_Panel_Cut', + 'MenuText': QT_TRANSLATE_NOOP("Arch_Panel_Cut","Panel Cut"), + 'Accel': "P, C", + 'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Creates 2D views of selected panels")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + if FreeCADGui.Selection.getSelection(): + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel Cut"))) + FreeCADGui.addModule("Arch") + for obj in FreeCADGui.Selection.getSelection(): + if Draft.getType(obj) == "Panel": + FreeCADGui.doCommand("Arch.makePanelCut(FreeCAD.ActiveDocument."+obj.Name+")") + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + +class CommandPanelSheet: + "the Arch Panel Sheet command definition" + def GetResources(self): + return {'Pixmap' : 'Arch_Panel_Sheet', + 'MenuText': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Panel Sheet"), + 'Accel': "P, S", + 'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Creates a 2D sheet which can contain panel cuts")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel Sheet"))) + FreeCADGui.addModule("Arch") + if FreeCADGui.Selection.getSelection(): + l = "[" + for obj in FreeCADGui.Selection.getSelection(): + l += "FreeCAD.ActiveDocument."+obj.Name+"," + l += "]" + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + FreeCADGui.doCommand("__objs__ = "+l) + FreeCADGui.doCommand("Arch.makePanelSheet(__objs__)") + FreeCADGui.doCommand("del __objs__") + else: + FreeCADGui.doCommand("Arch.makePanelSheet()") + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + + class _Panel(ArchComponent.Component): "The Panel object" def __init__(self,obj): @@ -485,7 +564,7 @@ def getIcon(self): return ":/icons/Arch_Panel_Tree.svg" -class _PanelView: +class PanelView: "A Drawing view for Arch Panels" def __init__(self, obj): @@ -559,5 +638,328 @@ def getDisplayModes(self,vobj): def setDisplayMode(self,mode): return mode + +class PanelCut(Draft._DraftObject): + "A flat, 2D view of an Arch Panel" + + def __init__(self, obj): + Draft._DraftObject.__init__(self,obj) + obj.addProperty("App::PropertyLink","Source","Arch",QT_TRANSLATE_NOOP("App::Property","The linked object")) + obj.addProperty("App::PropertyString","TagText","Arch",QT_TRANSLATE_NOOP("App::Property","The text to display. Can be %tag%, %label% or %description% to display the panel tag or label")) + obj.addProperty("App::PropertyLength","TagSize","Arch",QT_TRANSLATE_NOOP("App::Property","The size of the tag text")) + obj.addProperty("App::PropertyVector","TagPosition","Arch",QT_TRANSLATE_NOOP("App::Property","The position of the tag text. Keep (0,0,0) for automatic center position")) + obj.addProperty("App::PropertyAngle","TagRotation","Arch",QT_TRANSLATE_NOOP("App::Property","The rotation of the tag text")) + obj.addProperty("App::PropertyFile","FontFile","Arch",QT_TRANSLATE_NOOP("App::Property","The font of the tag text")) + obj.Proxy = self + self.Type = "PanelCut" + obj.TagText = "%tag%" + obj.TagSize = 10 + obj.FontFile = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetString("FontFile","") + + def execute(self, obj): + pl = obj.Placement + if obj.Source: + base = None + if Draft.getType(obj.Source) == "Panel": + import Part,DraftGeomUtils + baseobj = None + if obj.Source.CloneOf: + baseobj = obj.Source.CloneOf.Base + if obj.Source.Base: + baseobj = obj.Source.Base + if baseobj: + if baseobj.isDerivedFrom("Part::Feature"): + if baseobj.Shape.Solids: + return + else: + base = Part.makeCompound(baseobj.Shape.Wires) + n = None + for w in base.Wires: + n = DraftGeomUtils.getNormal(w) + if n: + break + if not n: + n = Vector(0,0,1) + base.translate(base.Vertexes[0].Point.negative()) + r = FreeCAD.Rotation(n,Vector(0,0,1)) + base.rotate(Vector(0,0,0),r.Axis,math.degrees(r.Angle)) + elif baseobj.isDerivedFrom("Mesh::Feature"): + return + else: + l2 = obj.Source.Length/2 + w2 = obj.Source.Width/2 + v1 = Vector(-l2,-w2,0) + v2 = Vector(l2,-w2,0) + v3 = Vector(l2,w2,0) + v4 = Vector(-l2,w2,0) + base = Part.makePolygon([v1,v2,v3,v4,v1]) + if base: + self.outline = base + if obj.FontFile and obj.TagText and obj.TagSize.Value: + if obj.TagPosition.Length == 0: + pos = base.BoundBox.Center + else: + pos = obj.TagPosition + if obj.TagText == "%tag%": + string = obj.Source.Tag + elif obj.TagText == "%label%": + string = obj.Source.Label + elif obj.TagText == "%description%": + string = obj.Source.Description + else: + string = obj.TagText + chars = [] + for char in Part.makeWireString(string,obj.FontFile,obj.TagSize.Value,0): + chars.extend(char) + textshape = Part.Compound(chars) + textshape.translate(pos.sub(textshape.BoundBox.Center)) + textshape.rotate(textshape.BoundBox.Center,Vector(0,0,1),obj.TagRotation.Value) + self.tag = textshape + base = Part.Compound([base,textshape]) + else: + base = Part.Compound([base]) + obj.Shape = base + obj.Placement = pl + + +class ViewProviderPanelCut(Draft._ViewProviderDraft): + "a view provider for the panel cut object" + + def __init__(self,vobj): + Draft._ViewProviderDraft.__init__(self,vobj) + vobj.addProperty("App::PropertyLength","Margin","Arch",QT_TRANSLATE_NOOP("App::Property","A margin inside the boundary")) + vobj.addProperty("App::PropertyBool","ShowMargin","Arch",QT_TRANSLATE_NOOP("App::Property","Turns the display of the margin on/off")) + + def attach(self,vobj): + Draft._ViewProviderDraft.attach(self,vobj) + from pivy import coin + self.coords = coin.SoCoordinate3() + self.lineset = coin.SoLineSet() + self.lineset.numVertices.setValue(-1) + lineStyle = coin.SoDrawStyle() + lineStyle.linePattern = 0x0f0f + self.color = coin.SoBaseColor() + self.switch = coin.SoSwitch() + sep = coin.SoSeparator() + self.switch.whichChild = -1 + sep.addChild(self.color) + sep.addChild(lineStyle) + sep.addChild(self.coords) + sep.addChild(self.lineset) + self.switch.addChild(sep) + vobj.Annotation.addChild(self.switch) + self.onChanged(vobj,"ShowMargin") + self.onChanged(vobj,"LineColor") + + def onChanged(self,vobj,prop): + if prop in ["Margin","ShowMargin"]: + if hasattr(vobj,"Margin") and hasattr(vobj,"ShowMargin"): + if (vobj.Margin.Value > 0) and vobj.Object.Shape and vobj.ShowMargin: + self.lineset.numVertices.setValue(-1) + if vobj.Object.Shape.Wires: + d = 0 + dw = None + for w in vobj.Object.Shape.Wires: + if w.BoundBox.DiagonalLength > d: + d = w.BoundBox.DiagonalLength + dw = w + if dw: + ow = dw.makeOffset2D(vobj.Margin.Value) + verts = [] + for v in ow.OrderedVertexes: + v = vobj.Object.Placement.inverse().multVec(v.Point) + verts.append((v.x,v.y,v.z)) + if dw.isClosed(): + verts.append(verts[0]) + self.coords.point.setValues(verts) + self.lineset.numVertices.setValue(len(verts)) + self.switch.whichChild = 0 + else: + self.switch.whichChild = -1 + elif prop == "LineColor": + if hasattr(vobj,"LineColor"): + c = vobj.LineColor + self.color.rgb.setValue(c[0],c[1],c[2]) + Draft._ViewProviderDraft.onChanged(self,vobj,prop) + + def updateData(self,obj,prop): + if prop in ["Shape"]: + self.onChanged(obj.ViewObject,"Margin") + Draft._ViewProviderDraft.updateData(self,obj,prop) + + +class PanelSheet(Draft._DraftObject): + "A collection of Panel cuts under a sheet" + + def __init__(self, obj): + Draft._DraftObject.__init__(self,obj) + obj.addProperty("App::PropertyLinkList","Group","Arch",QT_TRANSLATE_NOOP("App::Property","The linked Panel cuts")) + obj.addProperty("App::PropertyString","TagText","Arch",QT_TRANSLATE_NOOP("App::Property","The tag text to display")) + obj.addProperty("App::PropertyLength","TagSize","Arch",QT_TRANSLATE_NOOP("App::Property","The size of the tag text")) + obj.addProperty("App::PropertyVector","TagPosition","Arch",QT_TRANSLATE_NOOP("App::Property","The position of the tag text. Keep (0,0,0) for automatic center position")) + obj.addProperty("App::PropertyAngle","TagRotation","Arch",QT_TRANSLATE_NOOP("App::Property","The rotation of the tag text")) + obj.addProperty("App::PropertyFile","FontFile","Arch",QT_TRANSLATE_NOOP("App::Property","The font of the tag text")) + obj.addProperty("App::PropertyLength","Width","Arch",QT_TRANSLATE_NOOP("App::Property","The width of the sheet")) + obj.addProperty("App::PropertyLength","Height","Arch",QT_TRANSLATE_NOOP("App::Property","The height of the sheet")) + obj.addProperty("App::PropertyPercent","FillRatio","Arch",QT_TRANSLATE_NOOP("App::Property","The fill ratio of this sheet")) + obj.Proxy = self + self.Type = "PanelSheet" + obj.TagSize = 10 + obj.Width = 1000 + obj.Height = 1000 + obj.FontFile = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetString("FontFile","") + obj.setEditorMode("FillRatio",2) + + def execute(self, obj): + import Part + pl = obj.Placement + if obj.Width.Value and obj.Height.Value: + l2 = obj.Width.Value/2 + w2 = obj.Height.Value/2 + v1 = Vector(-l2,-w2,0) + v2 = Vector(l2,-w2,0) + v3 = Vector(l2,w2,0) + v4 = Vector(-l2,w2,0) + base = Part.makePolygon([v1,v2,v3,v4,v1]) + self.sheetborder = base + wires = [] + area = obj.Width.Value * obj.Height.Value + subarea = 0 + for v in obj.Group: + if v.isDerivedFrom("Part::Feature"): + wires.extend(v.Shape.Wires) + if Draft.getType(v) == "PanelCut": + if v.Source: + subarea += v.Source.Area.Value + else: + for w in v.Shape.Wires: + if w.isClosed(): + f = Part.Face(w) + subarea += f.Area + if wires: + base = Part.Compound([base]+wires) + if obj.FontFile and obj.TagText and obj.TagSize.Value: + chars = [] + for char in Part.makeWireString(obj.TagText,obj.FontFile,obj.TagSize.Value,0): + chars.extend(char) + textshape = Part.Compound(chars) + textshape.translate(obj.TagPosition) + textshape.rotate(textshape.BoundBox.Center,Vector(0,0,1),obj.TagRotation.Value) + self.sheettag = textshape + base = Part.Compound([base,textshape]) + obj.Shape = base + obj.Placement = pl + obj.FillRatio = int((subarea/area)*100) + + +class ViewProviderPanelSheet(Draft._ViewProviderDraft): + "a view provider for the panel sheet object" + + def __init__(self,vobj): + Draft._ViewProviderDraft.__init__(self,vobj) + vobj.addProperty("App::PropertyLength","Margin","Arch",QT_TRANSLATE_NOOP("App::Property","A margin inside the boundary")) + vobj.addProperty("App::PropertyBool","ShowMargin","Arch",QT_TRANSLATE_NOOP("App::Property","Turns the display of the margin on/off")) + + + def getIcon(self): + return ":/icons/Draft_Drawing.svg" + + def setEdit(self,vobj,mode): + if mode == 0: + taskd = SheetTaskPanel(vobj.Object) + taskd.update() + FreeCADGui.Control.showDialog(taskd) + return True + return False + + def unsetEdit(self,vobj,mode): + FreeCADGui.Control.closeDialog() + return False + + def attach(self,vobj): + Draft._ViewProviderDraft.attach(self,vobj) + from pivy import coin + self.coords = coin.SoCoordinate3() + self.lineset = coin.SoLineSet() + self.lineset.numVertices.setValue(-1) + lineStyle = coin.SoDrawStyle() + lineStyle.linePattern = 0x0f0f + self.color = coin.SoBaseColor() + self.switch = coin.SoSwitch() + sep = coin.SoSeparator() + self.switch.whichChild = -1 + sep.addChild(self.color) + sep.addChild(lineStyle) + sep.addChild(self.coords) + sep.addChild(self.lineset) + self.switch.addChild(sep) + vobj.Annotation.addChild(self.switch) + self.onChanged(vobj,"ShowMargin") + self.onChanged(vobj,"LineColor") + + def onChanged(self,vobj,prop): + if prop in ["Margin","ShowMargin"]: + if hasattr(vobj,"Margin") and hasattr(vobj,"ShowMargin"): + if (vobj.Margin.Value > 0) and (vobj.Margin.Value < vobj.Object.Width.Value/2) and (vobj.Margin.Value < vobj.Object.Height.Value/2): + l2 = vobj.Object.Width.Value/2 + w2 = vobj.Object.Height.Value/2 + v = vobj.Margin.Value + v1 = (-l2+v,-w2+v,0) + v2 = (l2-v,-w2+v,0) + v3 = (l2-v,w2-v,0) + v4 = (-l2+v,w2-v,0) + self.coords.point.setValues([v1,v2,v3,v4,v1]) + self.lineset.numVertices.setValue(5) + if vobj.ShowMargin: + self.switch.whichChild = 0 + else: + self.switch.whichChild = -1 + elif prop == "LineColor": + if hasattr(vobj,"LineColor"): + c = vobj.LineColor + self.color.rgb.setValue(c[0],c[1],c[2]) + Draft._ViewProviderDraft.onChanged(self,vobj,prop) + + def updateData(self,obj,prop): + if prop in ["Width","Height"]: + self.onChanged(obj.ViewObject,"Margin") + Draft._ViewProviderDraft.updateData(self,obj,prop) + + +class SheetTaskPanel(ArchComponent.ComponentTaskPanel): + + def __init__(self,obj): + ArchComponent.ComponentTaskPanel.__init__(self) + self.obj = obj + self.optwid = QtGui.QWidget() + self.optwid.setWindowTitle(QtGui.QApplication.translate("Arch", "Tools", None, QtGui.QApplication.UnicodeUTF8)) + lay = QtGui.QVBoxLayout(self.optwid) + self.editButton = QtGui.QPushButton(self.optwid) + self.editButton.setIcon(QtGui.QIcon(":/icons/Draft_Edit.svg")) + self.editButton.setText(QtGui.QApplication.translate("Arch", "Edit views positions", None, QtGui.QApplication.UnicodeUTF8)) + lay.addWidget(self.editButton) + QtCore.QObject.connect(self.editButton, QtCore.SIGNAL("clicked()"), self.editNodes) + self.form = [self.form,self.optwid] + + def editNodes(self): + FreeCADGui.Control.closeDialog() + FreeCADGui.runCommand("Draft_Edit") + + if FreeCAD.GuiUp: - FreeCADGui.addCommand('Arch_Panel',_CommandPanel()) + + class CommandPanelGroup: + + def GetCommands(self): + return tuple(['Arch_Panel','Arch_Panel_Cut','Arch_Panel_Sheet']) + def GetResources(self): + return { 'MenuText': QT_TRANSLATE_NOOP("Arch_PanelTools",'Panel tools'), + 'ToolTip': QT_TRANSLATE_NOOP("Arch_PanelTools",'Panel tools') + } + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + FreeCADGui.addCommand('Arch_Panel',CommandPanel()) + FreeCADGui.addCommand('Arch_Panel_Cut',CommandPanelCut()) + FreeCADGui.addCommand('Arch_Panel_Sheet',CommandPanelSheet()) + FreeCADGui.addCommand('Arch_PanelTools', CommandPanelGroup()) diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py index f86c3314afe4..0459f3cf3f2c 100644 --- a/src/Mod/Arch/InitGui.py +++ b/src/Mod/Arch/InitGui.py @@ -37,7 +37,7 @@ def Initialize(self): "Arch_Floor","Arch_Building","Arch_Site", "Arch_Window","Arch_Roof","Arch_Axis", "Arch_SectionPlane","Arch_Space","Arch_Stairs", - "Arch_Panel","Arch_Equipment", + "Arch_PanelTools","Arch_Equipment", "Arch_Frame","Arch_Material","Arch_Schedule","Arch_PipeTools", "Arch_CutPlane","Arch_Add","Arch_Remove","Arch_Survey"] self.utilities = ["Arch_Component","Arch_SplitMesh","Arch_MeshToShape", diff --git a/src/Mod/Arch/Resources/Arch.qrc b/src/Mod/Arch/Resources/Arch.qrc index 673c2800b6ec..2cb830ae3f62 100644 --- a/src/Mod/Arch/Resources/Arch.qrc +++ b/src/Mod/Arch/Resources/Arch.qrc @@ -45,6 +45,8 @@ icons/Arch_Panel.svg icons/Arch_Panel_Tree.svg icons/Arch_Panel_Clone.svg + icons/Arch_Panel_Cut.svg + icons/Arch_Panel_Sheet.svg icons/Arch_Equipment.svg icons/Arch_Equipment_Tree.svg icons/Arch_Equipment_Clone.svg diff --git a/src/Mod/Arch/Resources/icons/Arch_Panel_Cut.svg b/src/Mod/Arch/Resources/icons/Arch_Panel_Cut.svg new file mode 100644 index 000000000000..52048f7ebab0 --- /dev/null +++ b/src/Mod/Arch/Resources/icons/Arch_Panel_Cut.svg @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/Mod/Arch/Resources/icons/Arch_Panel_Sheet.svg b/src/Mod/Arch/Resources/icons/Arch_Panel_Sheet.svg new file mode 100644 index 000000000000..b256ecfb160f --- /dev/null +++ b/src/Mod/Arch/Resources/icons/Arch_Panel_Sheet.svg @@ -0,0 +1,484 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index efdb817a8b7b..7003d9f5ee91 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -581,8 +581,10 @@ def getMovableChildren(objectslist,recursive=True): with all child objects that have a "MoveWithHost" property set to True. If recursive is True, all descendents are considered, otherwise only direct children.''' added = [] + if not isinstance(objectslist,list): + objectslist = [objectslist] for obj in objectslist: - if not (getType(obj) in ["Clone","SectionPlane"]): + if not (getType(obj) in ["Clone","SectionPlane","Facebinder"]): # objects that should never move their children children = obj.OutList if hasattr(obj,"Proxy"): @@ -2708,6 +2710,8 @@ def clone(obj,delta=None): cl.Placement = obj[0].Placement try: cl.Role = base.Role + cl.Description = base.Description + cl.Tag = base.Tag except: pass return cl @@ -3563,6 +3567,8 @@ def claimChildren(self): objs.extend(self.Object.Objects) if hasattr(self.Object,"Components"): objs.extend(self.Object.Components) + if hasattr(self.Object,"Group"): + objs.extend(self.Object.Group) return objs class _ViewProviderDraftAlt(_ViewProviderDraft): diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 0862239cb6f1..8b9a5d21d808 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -3501,6 +3501,16 @@ def proceed(self): if self.pl: p = self.pl.multVec(p) self.editpoints.append(p) + elif Draft.getType(self.obj) == "PanelCut": + if self.obj.TagPosition.Length == 0: + pos = self.obj.Shape.BoundBox.Center + else: + pos = self.pl.multVec(self.obj.TagPosition) + self.editpoints.append(pos) + elif Draft.getType(self.obj) == "PanelSheet": + self.editpoints.append(self.pl.multVec(self.obj.TagPosition)) + for o in self.obj.Group: + self.editpoints.append(self.pl.multVec(o.Placement.Base)) if Draft.getType(self.obj) != "BezCurve": self.trackers = [] if self.editpoints: @@ -3724,6 +3734,14 @@ def update(self,v): nodes = self.obj.Nodes nodes[self.editing] = self.invpl.multVec(v) self.obj.Nodes = nodes + elif Draft.getType(self.obj) == "PanelCut": + if self.editing == 0: + self.obj.TagPosition = self.invpl.multVec(v) + elif Draft.getType(self.obj) == "PanelSheet": + if self.editing == 0: + self.obj.TagPosition = self.invpl.multVec(v) + else: + self.obj.Group[self.editing-1].Placement.Base = self.invpl.multVec(v) def numericInput(self,v,numy=None,numz=None): '''this function gets called by the toolbar diff --git a/src/Mod/Draft/importDXF.py b/src/Mod/Draft/importDXF.py index 39b0fecfc46e..9d2b864c1cd7 100644 --- a/src/Mod/Draft/importDXF.py +++ b/src/Mod/Draft/importDXF.py @@ -1548,6 +1548,9 @@ def insert(filename,docname): getDXFlibs() if dxfReader: groupname = os.path.splitext(os.path.basename(filename))[0] + if isinstance(groupname,unicode): + import sys #workaround since newDocument currently can't handle unicode filenames + groupname = groupname.encode(sys.getfilesystemencoding()) importgroup = doc.addObject("App::DocumentObjectGroup",groupname) importgroup.Label = decodeName(groupname) processdxf(doc,filename) @@ -1642,7 +1645,7 @@ def getSplineSegs(edge): points.append(edge.valueAt(edge.LastParameter)) return points -def getWire(wire,nospline=False,lw=True): +def getWire(wire,nospline=False,lw=True,asis=False): "returns an array of dxf-ready points and bulges from a wire" def fmt(v,b=0.0): if lw: @@ -1651,32 +1654,35 @@ def fmt(v,b=0.0): else: # Polyline format return ((v.x,v.y,v.z),None,[None,None],b) - edges = Part.__sortEdges__(wire.Edges) points = [] - # print("processing wire ",wire.Edges) - for edge in edges: - v1 = edge.Vertexes[0].Point - if DraftGeomUtils.geomType(edge) == "Circle": - # polyline bulge -> negative makes the arc go clockwise - angle = edge.LastParameter-edge.FirstParameter - bul = math.tan(angle/4) - #if cross1[2] < 0: + if asis: + points = [fmt(v.Point) for v in wire.OrderedVertexes] + else: + edges = Part.__sortEdges__(wire.Edges) + # print("processing wire ",wire.Edges) + for edge in edges: + v1 = edge.Vertexes[0].Point + if DraftGeomUtils.geomType(edge) == "Circle": # polyline bulge -> negative makes the arc go clockwise - #bul = -bul - if edge.Curve.Axis.dot(FreeCAD.Vector(0,0,1)) < 0: - bul = -bul - points.append(fmt(v1,bul)) - elif (DraftGeomUtils.geomType(edge) in ["BSplineCurve","BezierCurve","Ellipse"]) and (not nospline): - spline = getSplineSegs(edge) - spline.pop() - for p in spline: - points.append(fmt(p)) - else: - points.append(fmt(v1)) - if not DraftGeomUtils.isReallyClosed(wire): - v = edges[-1].Vertexes[-1].Point - points.append(fmt(v)) - # print("wire verts: ",points) + angle = edge.LastParameter-edge.FirstParameter + bul = math.tan(angle/4) + #if cross1[2] < 0: + # polyline bulge -> negative makes the arc go clockwise + #bul = -bul + if edge.Curve.Axis.dot(FreeCAD.Vector(0,0,1)) < 0: + bul = -bul + points.append(fmt(v1,bul)) + elif (DraftGeomUtils.geomType(edge) in ["BSplineCurve","BezierCurve","Ellipse"]) and (not nospline): + spline = getSplineSegs(edge) + spline.pop() + for p in spline: + points.append(fmt(p)) + else: + points.append(fmt(v1)) + if not DraftGeomUtils.isReallyClosed(wire): + v = edges[-1].Vertexes[-1].Point + points.append(fmt(v)) + # print("wire verts: ",points) return points def getBlock(sh,obj,lwPoly=False): @@ -1685,35 +1691,43 @@ def getBlock(sh,obj,lwPoly=False): writeShape(sh,obj,block,lwPoly) return block -def writeShape(sh,ob,dxfobject,nospline=False,lwPoly=False): +def writeShape(sh,ob,dxfobject,nospline=False,lwPoly=False,layer=None,color=None,asis=False): "writes the object's shape contents in the given dxf object" processededges = [] + if not layer: + layer=getGroup(ob) + if not color: + color = getACI(ob) for wire in sh.Wires: # polylines - for e in wire.Edges: + if asis: + edges = wire.Edges + else: + edges = Part.__sortEdges__(wire.Edges) + for e in edges: processededges.append(e.hashCode()) if (len(wire.Edges) == 1) and (DraftGeomUtils.geomType(wire.Edges[0]) == "Circle"): center, radius, ang1, ang2 = getArcData(wire.Edges[0]) if center != None: if len(wire.Edges[0].Vertexes) == 1: # circle dxfobject.append(dxfLibrary.Circle(center, radius, - color=getACI(ob), - layer=getGroup(ob))) + color=color, + layer=layer)) else: # arc dxfobject.append(dxfLibrary.Arc(center, radius, - ang1, ang2, color=getACI(ob), - layer=getGroup(ob))) + ang1, ang2, color=color, + layer=layer)) else: if (lwPoly): if hasattr(dxfLibrary,"LwPolyLine"): - dxfobject.append(dxfLibrary.LwPolyLine(getWire(wire,nospline), [0.0,0.0], - int(DraftGeomUtils.isReallyClosed(wire)), color=getACI(ob), - layer=getGroup(ob))) + dxfobject.append(dxfLibrary.LwPolyLine(getWire(wire,nospline,asis=asis), [0.0,0.0], + int(DraftGeomUtils.isReallyClosed(wire)), color=color, + layer=layer)) else: FreeCAD.Console.PrintWarning("LwPolyLine support not found. Please delete dxfLibrary.py from your FreeCAD user directory to force auto-update\n") else : - dxfobject.append(dxfLibrary.PolyLine(getWire(wire,nospline,lw=False), [0.0,0.0,0.0], - int(DraftGeomUtils.isReallyClosed(wire)), color=getACI(ob), - layer=getGroup(ob))) + dxfobject.append(dxfLibrary.PolyLine(getWire(wire,nospline,lw=False,asis=asis), [0.0,0.0,0.0], + int(DraftGeomUtils.isReallyClosed(wire)), color=color, + layer=layer)) if len(processededges) < len(sh.Edges): # lone edges loneedges = [] for e in sh.Edges: @@ -1726,16 +1740,16 @@ def writeShape(sh,ob,dxfobject,nospline=False,lwPoly=False): c = DraftGeomUtils.getCircleFromSpline(edge) if c: dxfobject.append(dxfLibrary.Circle(DraftVecUtils.tup(c.Curve.Center), c.Curve.Radius, - color=getACI(ob), - layer=getGroup(ob))) + color=color, + layer=layer)) else: points = [] spline = getSplineSegs(edge) for p in spline: points.append(((p.x,p.y,p.z),None,[None,None],0.0)) dxfobject.append(dxfLibrary.PolyLine(points, [0.0,0.0,0.0], - 0, color=getACI(ob), - layer=getGroup(ob))) + 0, color=color, + layer=layer)) elif DraftGeomUtils.geomType(edge) == "Circle": # curves center, radius, ang1, ang2 = getArcData(edge) if center != None: @@ -1743,12 +1757,12 @@ def writeShape(sh,ob,dxfobject,nospline=False,lwPoly=False): center = DraftVecUtils.tup(center) if len(edge.Vertexes) == 1: # circles dxfobject.append(dxfLibrary.Circle(center, radius, - color=getACI(ob), - layer=getGroup(ob))) + color=color, + layer=layer)) else : # arcs dxfobject.append(dxfLibrary.Arc(center, radius, ang1, ang2, color=getACI(ob), - layer=getGroup(ob))) + layer=layer)) elif DraftGeomUtils.geomType(edge) == "Ellipse": # ellipses: if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("DiscretizeEllipses",True): points = [] @@ -1756,8 +1770,8 @@ def writeShape(sh,ob,dxfobject,nospline=False,lwPoly=False): for p in spline: points.append(((p.x,p.y,p.z),None,[None,None],0.0)) dxfobject.append(dxfLibrary.PolyLine(points, [0.0,0.0,0.0], - 0, color=getACI(ob), - layer=getGroup(ob))) + 0, color=color, + layer=layer)) else: if hasattr(dxfLibrary,"Ellipse"): center = DraftVecUtils.tup(edge.Curve.Center) @@ -1771,8 +1785,8 @@ def writeShape(sh,ob,dxfobject,nospline=False,lwPoly=False): dxfobject.append(dxfLibrary.Ellipse(center=center,majorAxis=major,normalAxis=norm, minorAxisRatio=minor,startParameter=start, endParameter=end, - color=getACI(ob), - layer=getGroup(ob))) + color=color, + layer=layer)) else: FreeCAD.Console.PrintWarning("Ellipses support not found. Please delete dxfLibrary.py from your FreeCAD user directory to force auto-update\n") else: # anything else is treated as lines @@ -1780,10 +1794,10 @@ def writeShape(sh,ob,dxfobject,nospline=False,lwPoly=False): ve1=edge.Vertexes[0].Point ve2=edge.Vertexes[1].Point dxfobject.append(dxfLibrary.Line([DraftVecUtils.tup(ve1), DraftVecUtils.tup(ve2)], - color=getACI(ob), - layer=getGroup(ob))) + color=color, + layer=layer)) -def writeMesh(ob,dxfobject): +def writeMesh(ob,dxf): "export a shape as a polyface mesh" meshdata = ob.Shape.tessellate(0.5) # print(meshdata) @@ -1794,10 +1808,52 @@ def writeMesh(ob,dxfobject): for f in meshdata[1]: faces.append([f[0]+1,f[1]+1,f[2]+1]) # print(len(points),len(faces)) - dxfobject.append(dxfLibrary.PolyLine([points,faces], [0.0,0.0,0.0], + dxf.append(dxfLibrary.PolyLine([points,faces], [0.0,0.0,0.0], 64, color=getACI(ob), layer=getGroup(ob))) +def writePanelCut(ob,dxf,nospline,lwPoly,parent=None): + if not hasattr(ob.Proxy,"outline"): + ob.Proxy.execute(ob) + if hasattr(ob.Proxy,"outline"): + outl = ob.Proxy.outline + tag = None + if hasattr(ob.Proxy,"tag"): + tag = ob.Proxy.tag + if tag: + tag.Placement = ob.Placement.multiply(tag.Placement) + if parent: + tag.Placement = parent.Placement.multiply(tag.Placement) + outl.Placement = ob.Placement.multiply(outl.Placement) + if parent: + outl.Placement = parent.Placement.multiply(outl.Placement) + else: + parent = ob + if len(outl.Wires) > 1: + # separate outline + d = 0 + ow = None + for w in outl.Wires: + if w.BoundBox.DiagonalLength > d: + d = w.BoundBox.DiagonalLength + ow = w + if ow: + inl = Part.Compound([w for w in outl.Wires if w.hashCode() != ow.hashCode()]) + outl = ow + else: + inl = None + outl = outl.Wires[0] + + writeShape(outl,parent,dxf,nospline,lwPoly,layer="Outlines",color=5) + if inl: + writeShape(inl,parent,dxf,nospline,lwPoly,layer="Cuts",color=4) + if tag: + writeShape(tag,parent,dxf,nospline,lwPoly,layer="Tags",color=2,asis=True) + # sticky fonts can render very odd wires... + #for w in tag.Edges: + # pts = [(v.X,v.Y,v.Z) for v in w.Vertexes] + # dxf.append(dxfLibrary.Line(pts,color=getACI(ob),layer="Tags")) + def export(objectslist,filename,nospline=False,lwPoly=False): "called when freecad exports a file. If nospline=True, bsplines are exported as straight segs lwPoly=True for OpenSCAD DXF" readPreferences() @@ -1828,7 +1884,25 @@ def export(objectslist,filename,nospline=False,lwPoly=False): dxf = dxfLibrary.Drawing() for ob in exportList: print("processing "+str(ob.Name)) - if ob.isDerivedFrom("Part::Feature"): + if Draft.getType(ob) == "PanelSheet": + if not hasattr(ob.Proxy,"sheetborder"): + ob.Proxy.execute(ob) + sb = ob.Proxy.sheetborder + sb.Placement = ob.Placement + ss = ob.Proxy.sheettag + ss.Placement = ob.Placement.multiply(ss.Placement) + writeShape(sb,ob,dxf,nospline,lwPoly,layer="Sheets",color=1) + writeShape(ss,ob,dxf,nospline,lwPoly,layer="SheetTags",color=1) + for subob in ob.Group: + if Draft.getType(subob) == "PanelCut": + writePanelCut(subob,dxf,nospline,lwPoly,parent=ob) + elif subob.isDerivedFrom("Part::Feature"): + shp = subob.Shape.copy() + shp.Placement = ob.Placement.multiply(shp.Placement) + writeShape(shp,ob,dxf,nospline,lwPoly,layer="Outlines",color=5) + elif Draft.getType(ob) == "PanelCut": + writePanelCut(ob,dxf,nospline,lwPoly) + elif ob.isDerivedFrom("Part::Feature"): if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("dxfmesh"): sh = None if not ob.Shape.isNull():