From 892b7ae509172cee5e8bfeeded776ec05df09d7e Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 8 Apr 2015 12:34:48 -0300 Subject: [PATCH] Arch: Misc improvements for IFC workflow * Added utility to make non-parametric Arch component * Ability for all Arch components to be a clone of another Arch component of same type * Modified the Draft Clone tool to produce Arch Clones if applicable * Fixed Arch Roofs so they can be based on a solid shape like other Arch objects * Ability to change the Root element to be imported in IFC preferences * Ability to import IFC files also as compounds, Part shapes or non-parametric Arch objects * Added an "only" parameter to importIFC.open() to import only a certain object ID. * Ability to read colors (IfcSurfaceStyle) from IFC objects --- src/Mod/Arch/ArchCommands.py | 41 +++ src/Mod/Arch/ArchComponent.py | 21 +- src/Mod/Arch/ArchEquipment.py | 4 + src/Mod/Arch/ArchFrame.py | 4 + src/Mod/Arch/ArchPanel.py | 3 + src/Mod/Arch/ArchRebar.py | 4 + src/Mod/Arch/ArchRoof.py | 15 +- src/Mod/Arch/ArchSpace.py | 4 + src/Mod/Arch/ArchStairs.py | 5 +- src/Mod/Arch/ArchStructure.py | 3 + src/Mod/Arch/ArchWall.py | 3 + src/Mod/Arch/ArchWindow.py | 4 + src/Mod/Arch/InitGui.py | 2 +- src/Mod/Arch/Resources/Arch.qrc | 1 + .../Arch/Resources/icons/Arch_Component.svg | 96 +++++ src/Mod/Arch/Resources/ui/archprefs-import.ui | 116 +++++- src/Mod/Arch/importIFC.py | 344 +++++++++++++----- src/Mod/Draft/Draft.py | 7 + 18 files changed, 579 insertions(+), 98 deletions(-) create mode 100644 src/Mod/Arch/Resources/icons/Arch_Component.svg diff --git a/src/Mod/Arch/ArchCommands.py b/src/Mod/Arch/ArchCommands.py index c098f9371..d140a8dd6 100644 --- a/src/Mod/Arch/ArchCommands.py +++ b/src/Mod/Arch/ArchCommands.py @@ -192,6 +192,23 @@ def removeComponents(objectsList,host=None): if o in a: a.remove(o) h.Objects = a + +def makeComponent(baseobj=None,name="Component"): + '''makeComponent([baseobj]): creates an undefined, non-parametric Arch + component from the given base object''' + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) + obj.Label = translate("Arch",name) + ArchComponent.Component(obj) + if FreeCAD.GuiUp: + ArchComponent.ViewProviderComponent(obj.ViewObject) + if baseobj: + import Part + if baseobj.isDerivedFrom("Part::Feature"): + obj.Shape = baseobj.Shape + obj.Placement = baseobj.Placement + elif isinstance(baseobj,Part.Shape): + obj.Shape = baseobj + return obj def fixWindow(obj): '''fixWindow(object): Fixes non-DAG problems in windows @@ -1114,6 +1131,29 @@ class _ToggleIfcBrepFlag: def Activated(self): for o in FreeCADGui.Selection.getSelection(): toggleIfcBrepFlag(o) + + +class _CommandComponent: + "the Arch Component command definition" + def GetResources(self): + return {'Pixmap' : 'Arch_Component', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_Component","Component"), + 'Accel': "C, M", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Component","Creates an undefined architectural component")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + sel = FreeCADGui.Selection.getSelection() + if sel: + FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Component")) + FreeCADGui.addModule("Arch") + FreeCADGui.Control.closeDialog() + for o in sel: + FreeCADGui.doCommand("Arch.makeComponent(FreeCAD.ActiveDocument."+o.Name+")") + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() if FreeCAD.GuiUp: @@ -1128,3 +1168,4 @@ if FreeCAD.GuiUp: FreeCADGui.addCommand('Arch_IfcExplorer',_CommandIfcExplorer()) FreeCADGui.addCommand('Arch_Survey',_CommandSurvey()) FreeCADGui.addCommand('Arch_ToggleIfcBrepFlag',_ToggleIfcBrepFlag()) + FreeCADGui.addCommand('Arch_Component',_CommandComponent()) diff --git a/src/Mod/Arch/ArchComponent.py b/src/Mod/Arch/ArchComponent.py index 93adf7ac0..a5f229ec7 100644 --- a/src/Mod/Arch/ArchComponent.py +++ b/src/Mod/Arch/ArchComponent.py @@ -40,6 +40,7 @@ if FreeCAD.GuiUp: else: def translate(ctxt,txt): return txt + def addToComponent(compobject,addobject,mod=None): '''addToComponent(compobject,addobject,mod): adds addobject @@ -132,8 +133,8 @@ class SelectionTaskPanel: FreeCADGui.Selection.removeObserver(FreeCAD.ArchObserver) del FreeCAD.ArchObserver return True - - + + class ComponentTaskPanel: '''The default TaskPanel for all Arch components''' def __init__(self): @@ -289,6 +290,7 @@ class Component: "The default Arch Component object" def __init__(self,obj): obj.addProperty("App::PropertyLink","Base","Arch",translate("Arch","The base object this component is built upon")) + obj.addProperty("App::PropertyLink","CloneOf","Arch",translate("Arch","The object this component is cloning")) obj.addProperty("App::PropertyLinkList","Additions","Arch",translate("Arch","Other shapes that are appended to this object")) obj.addProperty("App::PropertyLinkList","Subtractions","Arch",translate("Arch","Other shapes that are subtracted from this object")) obj.addProperty("App::PropertyString","Description","Arch",translate("Arch","An optional description for this component")) @@ -316,6 +318,17 @@ class Component: def onChanged(self,obj,prop): pass + def clone(self,obj): + "if this object is a clone, sets the shape. Returns True if this is the case" + if hasattr(obj,"CloneOf"): + if obj.CloneOf: + if Draft.getType(obj.CloneOf) == Draft.getType(obj): + pl = obj.Placement + obj.Shape = obj.CloneOf.Shape.copy() + obj.Placement = pl + return True + return False + def getSiblings(self,obj): "returns a list of objects with the same type and same base as this object" if not hasattr(obj,"Base"): @@ -642,6 +655,10 @@ class ViewProviderComponent: def updateData(self,obj,prop): return + + def getIcon(self): + import Arch_rc + return ":/icons/Arch_Component.svg" def onChanged(self,vobj,prop): if prop == "Visibility": diff --git a/src/Mod/Arch/ArchEquipment.py b/src/Mod/Arch/ArchEquipment.py index 3add4e60d..4b8914f66 100644 --- a/src/Mod/Arch/ArchEquipment.py +++ b/src/Mod/Arch/ArchEquipment.py @@ -248,6 +248,10 @@ class _Equipment(ArchComponent.Component): self.hideSubobjects(obj,prop) def execute(self,obj): + + if self.clone(obj): + return + pl = obj.Placement if obj.Base: if obj.isDerivedFrom("Mesh::Feature"): diff --git a/src/Mod/Arch/ArchFrame.py b/src/Mod/Arch/ArchFrame.py index 1baa5bc2f..3e00ac1d4 100644 --- a/src/Mod/Arch/ArchFrame.py +++ b/src/Mod/Arch/ArchFrame.py @@ -90,6 +90,10 @@ class _Frame(ArchComponent.Component): obj.Role = Roles def execute(self,obj): + + if self.clone(obj): + return + if not obj.Base: return if not obj.Base.Shape: diff --git a/src/Mod/Arch/ArchPanel.py b/src/Mod/Arch/ArchPanel.py index 78c75ed13..38e122d53 100644 --- a/src/Mod/Arch/ArchPanel.py +++ b/src/Mod/Arch/ArchPanel.py @@ -265,6 +265,9 @@ class _Panel(ArchComponent.Component): def execute(self,obj): "creates the panel shape" + + if self.clone(obj): + return import Part, DraftGeomUtils diff --git a/src/Mod/Arch/ArchRebar.py b/src/Mod/Arch/ArchRebar.py index f3e982799..4f1bcd953 100644 --- a/src/Mod/Arch/ArchRebar.py +++ b/src/Mod/Arch/ArchRebar.py @@ -158,6 +158,10 @@ class _Rebar(ArchComponent.Component): return None,None def execute(self,obj): + + if self.clone(obj): + return + if len(obj.InList) != 1: return if Draft.getType(obj.InList[0]) != "Structure": diff --git a/src/Mod/Arch/ArchRoof.py b/src/Mod/Arch/ArchRoof.py index 645b57cba..13b455a9e 100644 --- a/src/Mod/Arch/ArchRoof.py +++ b/src/Mod/Arch/ArchRoof.py @@ -35,19 +35,28 @@ __title__="FreeCAD Roof" __author__ = "Yorik van Havre", "Jonathan Wiedemann" __url__ = "http://www.freecadweb.org" -def makeRoof(baseobj=None,facenr=1, angles=[45.,], run = [], idrel = [0,],thickness = [1.,], overhang=[2.,], name="Roof"): +def makeRoof(baseobj=None,facenr=0, angles=[45.,], run = [], idrel = [0,],thickness = [1.,], overhang=[2.,], name="Roof"): '''makeRoof(baseobj,[facenr],[angle],[name]) : Makes a roof based on a closed wire. face from an existing object. You can provide a list of angles, run, idrel, thickness, overhang for each edges in the wire to define the roof shape. The default for angle is 45 and the list is automatically complete to match with number of edges in the wire.''' obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) obj.Label = translate("Arch",name) + w = None _Roof(obj) if FreeCAD.GuiUp: _ViewProviderRoof(obj.ViewObject) if baseobj: obj.Base = baseobj if obj.Base.isDerivedFrom("Part::Feature"): + if (facenr == 0) and obj.Base.Shape.Solids: + # the base shape is a solid and facenr hasn't been set: + # assume its shape is copied over + if FreeCAD.GuiUp: + obj.Base.ViewObject.hide() + else: + # set facenr to 1 + facenr = 1 if (obj.Base.Shape.Faces and obj.Face): w = obj.Base.Shape.Faces[obj.Face-1].Wires[0] elif obj.Base.Shape.Wires: @@ -509,6 +518,10 @@ class _Roof(ArchComponent.Component): print("PROFIL " + str(i) + " : End calculs") def execute(self,obj): + + if self.clone(obj): + return + import Part, math, DraftGeomUtils pl = obj.Placement self.baseface = None diff --git a/src/Mod/Arch/ArchSpace.py b/src/Mod/Arch/ArchSpace.py index 2f2837a4e..762b4c728 100644 --- a/src/Mod/Arch/ArchSpace.py +++ b/src/Mod/Arch/ArchSpace.py @@ -236,6 +236,10 @@ class _Space(ArchComponent.Component): obj.Role = Roles def execute(self,obj): + + if self.clone(obj): + return + self.getShape(obj) def onChanged(self,obj,prop): diff --git a/src/Mod/Arch/ArchStairs.py b/src/Mod/Arch/ArchStairs.py index c36ff29aa..31f6e3e14 100644 --- a/src/Mod/Arch/ArchStairs.py +++ b/src/Mod/Arch/ArchStairs.py @@ -130,7 +130,10 @@ class _Stairs(ArchComponent.Component): def execute(self,obj): "constructs the shape of the stairs" - + + if self.clone(obj): + return + import Part self.steps = [] self.pseudosteps = [] diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py index 0a2636381..dd15ad164 100644 --- a/src/Mod/Arch/ArchStructure.py +++ b/src/Mod/Arch/ArchStructure.py @@ -577,6 +577,9 @@ class _Structure(ArchComponent.Component): "creates the structure shape" import Part, DraftGeomUtils + + if self.clone(obj): + return normal,length,width,height = self.getDefaultValues(obj) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 12dc747df..bbce19072 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -402,6 +402,9 @@ class _Wall(ArchComponent.Component): def execute(self,obj): "builds the wall shape" + + if self.clone(obj): + return import Part, DraftGeomUtils pl = obj.Placement diff --git a/src/Mod/Arch/ArchWindow.py b/src/Mod/Arch/ArchWindow.py index a91d41c2e..830aafd9a 100644 --- a/src/Mod/Arch/ArchWindow.py +++ b/src/Mod/Arch/ArchWindow.py @@ -636,6 +636,10 @@ class _Window(ArchComponent.Component): def execute(self,obj): + + if self.clone(obj): + return + import Part, DraftGeomUtils pl = obj.Placement base = None diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py index c45bcce85..655001bae 100644 --- a/src/Mod/Arch/InitGui.py +++ b/src/Mod/Arch/InitGui.py @@ -75,7 +75,7 @@ class ArchWorkbench(Workbench): "Arch_SectionPlane","Arch_Space","Arch_Stairs", "Arch_Panel","Arch_Equipment", "Arch_Frame","Arch_CutPlane","Arch_Add","Arch_Remove","Arch_Survey"] - self.utilities = ["Arch_SplitMesh","Arch_MeshToShape", + self.utilities = ["Arch_Component","Arch_SplitMesh","Arch_MeshToShape", "Arch_SelectNonSolidMeshes","Arch_RemoveShape", "Arch_CloseHoles","Arch_MergeWalls","Arch_Check", "Arch_IfcExplorer","Arch_ToggleIfcBrepFlag","Arch_3Views", diff --git a/src/Mod/Arch/Resources/Arch.qrc b/src/Mod/Arch/Resources/Arch.qrc index 10a0487d2..733740346 100644 --- a/src/Mod/Arch/Resources/Arch.qrc +++ b/src/Mod/Arch/Resources/Arch.qrc @@ -53,6 +53,7 @@ icons/Arch_CutPlane.svg icons/Arch_Bimserver.svg icons/Git.svg + icons/Arch_Component.svg ui/archprefs-base.ui ui/archprefs-defaults.ui ui/archprefs-import.ui diff --git a/src/Mod/Arch/Resources/icons/Arch_Component.svg b/src/Mod/Arch/Resources/icons/Arch_Component.svg new file mode 100644 index 000000000..82c6c6d7a --- /dev/null +++ b/src/Mod/Arch/Resources/icons/Arch_Component.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/Mod/Arch/Resources/ui/archprefs-import.ui b/src/Mod/Arch/Resources/ui/archprefs-import.ui index bafd51c26..912c2c8b4 100644 --- a/src/Mod/Arch/Resources/ui/archprefs-import.ui +++ b/src/Mod/Arch/Resources/ui/archprefs-import.ui @@ -6,8 +6,8 @@ 0 0 - 503 - 526 + 463 + 516 @@ -26,12 +26,83 @@ IFC import + + + + + + Import IFC objects as + + + + + + + Specifies what kind of objects will be created in FreeCAD + + + ifcImportMode + + + Mod/Arch + + + + Parametric Arch objects + + + + + Non-parametric Arch objects + + + + + Simple Part shapes + + + + + One compound per floor + + + + + + + + + + + + Root element: + + + + + + + Only subtypes of this element will be imported. Keep value as "IfcProduct" to import all building elements. + + + IfcProduct + + + ifcRootElement + + + Mod/Arch + + + + + - Check this to display debug messages while importing IFC files + Show verbose information during import and export of IFC files Show debug messages @@ -86,6 +157,29 @@ + + + + + + Check this to retrieve object colors. + + + Read colors + + + true + + + ifcReadColors + + + Mod/Arch + + + + + @@ -125,6 +219,17 @@ IFC export + + + + + + "Show debug messages" option above will apply to export too + + + + + @@ -250,6 +355,11 @@ QCheckBox
Gui/PrefWidgets.h
+ + Gui::PrefComboBox + QComboBox +
Gui/PrefWidgets.h
+
Gui::PrefLineEdit QLineEdit diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index f856b0a42..3bc6e687f 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -246,24 +246,36 @@ def explore(filename=None): return -def open(filename,skip=[]): +def open(filename,skip=[],only=[],root=None): "opens an IFC file in a new document" docname = os.path.splitext(os.path.basename(filename))[0] doc = FreeCAD.newDocument(docname) doc.Label = docname - doc = insert(filename,doc.Name,skip) + doc = insert(filename,doc.Name,skip,only,root) return doc -def insert(filename,docname,skip=[]): - "imports the contents of an IFC file" +def insert(filename,docname,skip=[],only=[],root=None): + """insert(filename,docname,skip=[],only=[],root=None): imports the contents of an IFC file. + skip can contain a list of ids of objects to be skipped, only can restrict the import to + certain object ids (will also get their children) and root can be used to + import only the derivates of a certain element type (default = ifcProduct).""" p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") DEBUG = p.GetBool("ifcDebug",False) PREFIX_NUMBERS = p.GetBool("ifcPrefixNumbers",False) - SKIP = p.GetString("ifcSkip","") + SKIP = p.GetString("ifcSkip","").split(",") SEPARATE_OPENINGS = p.GetBool("ifcSeparateOpenings",False) + ROOT_ELEMENT = p.GetString("ifcRootElement","IfcProduct") + if root: + ROOT_ELEMENT = root + MERGE_MODE = p.GetInt("ifcImportMode",0) + if MERGE_MODE > 0: + SEPARATE_OPENINGS = False + READ_COLORS = p.GetBool("ifcReadColors",True) + if not SEPARATE_OPENINGS: + SKIP.append("IfcOpeningElement") try: import ifcopenshell @@ -271,13 +283,15 @@ def insert(filename,docname,skip=[]): FreeCAD.Console.PrintError("IfcOpenShell was not found on this system. IFC support is disabled\n") return - if DEBUG: print "opening ",filename,"..." + if DEBUG: print "Opening ",filename,"...", try: doc = FreeCAD.getDocument(docname) except: doc = FreeCAD.newDocument(docname) FreeCAD.ActiveDocument = doc + if DEBUG: print "done." + global ifcfile # keeping global for debugging purposes if isinstance(filename,unicode): import sys #workaround since ifcopenshell currently can't handle unicode filenames @@ -293,15 +307,19 @@ def insert(filename,docname,skip=[]): sites = ifcfile.by_type("IfcSite") buildings = ifcfile.by_type("IfcBuilding") floors = ifcfile.by_type("IfcBuildingStorey") - products = ifcfile.by_type("IfcProduct") + products = ifcfile.by_type(ROOT_ELEMENT) openings = ifcfile.by_type("IfcOpeningElement") annotations = ifcfile.by_type("IfcAnnotation") + + if DEBUG: print "Building relationships table...", # building relations tables objects = {} # { id:object, ... } additions = {} # { host:[child,...], ... } subtractions = [] # [ [opening,host], ... ] properties = {} # { host:[property, ...], ... } + colors = {} # { id:(r,g,b) } + shapes = {} # { id:shaoe } only used for merge mode for r in ifcfile.by_type("IfcRelContainedInSpatialStructure"): additions.setdefault(r.RelatingStructure.id(),[]).extend([e.id() for e in r.RelatedElements]) for r in ifcfile.by_type("IfcRelAggregates"): @@ -312,101 +330,239 @@ def insert(filename,docname,skip=[]): for obj in r.RelatedObjects: if r.RelatingPropertyDefinition.is_a("IfcPropertySet"): properties.setdefault(obj.id(),[]).extend([e.id() for e in r.RelatingPropertyDefinition.HasProperties]) + if READ_COLORS: + for r in ifcfile.by_type("IfcStyledItem"): + if r.Item and r.Styles[0].is_a("IfcPresentationStyleAssignment"): + if r.Styles[0].Styles[0].is_a("IfcSurfaceStyle"): + if r.Styles[0].Styles[0].Styles[0].is_a("IfcSurfaceStyleRendering"): + if r.Styles[0].Styles[0].Styles[0].SurfaceColour: + c = r.Styles[0].Styles[0].Styles[0].SurfaceColour + for p in ifcfile.by_type("IfcProduct"): + if p.Representation: + for it in p.Representation.Representations: + if it.Items: + if it.Items[0].id() == r.Item.id(): + colors[p.id()] = (c.Red,c.Green,c.Blue) + elif it.Items[0].is_a("IfcBooleanResult"): + if (it.Items[0].FirstOperand.id() == r.Item.id()): + colors[p.id()] = (c.Red,c.Green,c.Blue) + if only: # only import a list of IDs and their children + ids = [] + while only: + currentid = only.pop() + ids.append(currentid) + if currentid in additions.keys(): + only.extend(additions[currentid]) + products = [ifcfile[currentid] for currentid in ids] + + if DEBUG: print "done." + count = 0 + from FreeCAD import Base + progressbar = Base.ProgressIndicator() + progressbar.start("Importing IFC objects...",len(products)) # products for product in products: + pid = product.id() guid = product.GlobalId ptype = product.is_a() + if DEBUG: print count,"/",len(products)," creating object ",pid," : ",ptype, name = product.Name or str(ptype[3:]) if PREFIX_NUMBERS: name = "ID" + str(pid) + " " + name obj = None baseobj = None brep = None - - if (ptype == "IfcOpeningElement") and (not SEPARATE_OPENINGS): continue - if pid in skip: continue # user given id skip list - if ptype in SKIP: continue # preferences-set type skip list + + if pid in skip: # user given id skip list + if DEBUG: print " skipped." + continue + if ptype in SKIP: # preferences-set type skip list + if DEBUG: print " skipped." + continue + try: cr = ifcopenshell.geom.create_shape(settings,product) brep = cr.geometry.brep_data except: - pass + pass # IfcOpenShell will yield an error if a given product has no shape, but we don't care + if brep: + if DEBUG: print " ",str(len(brep)/1000),"k ", + shape = Part.Shape() shape.importBrepFromString(brep) + shape.scale(1000.0) # IfcOpenShell always outputs in meters + if not shape.isNull(): - baseobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") - baseobj.Shape = shape - for freecadtype,ifctypes in typesmap.iteritems(): - if ptype in ifctypes: - obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name) + if MERGE_MODE > 0: + if ptype == "IfcSpace": # do not add spaces to compounds + if DEBUG: print "skipping space ",pid + else: + shapes[pid] = shape + if DEBUG: print shape.Solids + baseobj = shape + else: + baseobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + baseobj.Shape = shape + else: + if DEBUG: print "null shape ", + if not shape.isValid(): + if DEBUG: print "invalid shape. Skipping" + continue + + else: + if DEBUG: print " no brep " + + if MERGE_MODE == 0: + + # full Arch objects + for freecadtype,ifctypes in typesmap.items(): + if ptype in ifctypes: + obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name) + obj.Label = name + # setting role + try: + r = ptype[3:] + tr = dict((v,k) for k, v in translationtable.iteritems()) + if r in tr.keys(): + r = tr[r] + # remove the "StandardCase" + if "StandardCase" in r: + r = r[:-12] + obj.Role = r + except: + pass + # setting uid + if hasattr(obj,"IfcAttributes"): + a = obj.IfcAttributes + a["IfcUID"] = str(guid) + obj.IfcAttributes = a + break + if not obj: + obj = baseobj + if obj: + sols = str(baseobj.Shape.Solids) if hasattr(baseobj,"Shape") else "[]" + if DEBUG: print sols + objects[pid] = obj + + elif MERGE_MODE == 1: + + # non-parametric Arch objects + if ptype in ["IfcSite","IfcBuilding","IfcBuildingStorey"]: + for freecadtype,ifctypes in typesmap.items(): + if ptype in ifctypes: + obj = getattr(Arch,"make"+freecadtype)(baseobj=None,name=name) + obj.Label = name + objects[pid] = obj + elif baseobj: + obj = Arch.makeComponent(baseobj,name=name) obj.Label = name - # setting role - try: - r = ptype[3:] - tr = dict((v,k) for k, v in translationtable.iteritems()) - if r in tr.keys(): - r = tr[r] - # remove the "StandardCase" - if "StandardCase" in r: - r = r[:-12] - obj.Role = r - except: - pass - # setting uid + objects[pid] = obj + + elif MERGE_MODE == 2: + + # Part shapes + if ptype in ["IfcSite","IfcBuilding","IfcBuildingStorey"]: + for freecadtype,ifctypes in typesmap.items(): + if ptype in ifctypes: + obj = getattr(Arch,"make"+freecadtype)(baseobj=None,name=name) + obj.Label = name + objects[pid] = obj + elif baseobj: + obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name) + obj.Label = name + obj.Shape = shape + + + if obj: + + # properties + if pid in properties: if hasattr(obj,"IfcAttributes"): a = obj.IfcAttributes - a["IfcUID"] = str(guid) + for p in properties[pid]: + o = ifcfile[p] + if o.is_a("IfcPropertySingleValue"): + a[o.Name] = str(o.NominalValue) obj.IfcAttributes = a - break - if not obj: - obj = baseobj - if obj: - sh = baseobj.Shape.ShapeType if hasattr(baseobj,"Shape") else "None" - sols = str(baseobj.Shape.Solids) if hasattr(baseobj,"Shape") else "" - pc = str(int((float(count)/(len(products)+len(annotations))*100)))+"% " - if DEBUG: print pc,"creating object ",pid," : ",ptype, " with shape: ",sh," ",sols - objects[pid] = obj - # properties - if pid in properties: - if hasattr(obj,"IfcAttributes"): - a = obj.IfcAttributes - for p in properties[pid]: - o = ifcfile[p] - if o.is_a("IfcPropertySingleValue"): - a[o.Name] = str(o.NominalValue) - obj.IfcAttributes = a + # color + if FreeCAD.GuiUp and (pid in colors): + if DEBUG: print " setting color: ",colors[pid] + obj.ViewObject.ShapeColor = colors[pid] + + # if DEBUG is on, recompute after each shape + if DEBUG: FreeCAD.ActiveDocument.recompute() + count += 1 - + progressbar.next() + + progressbar.stop() FreeCAD.ActiveDocument.recompute() - if DEBUG: print "Processing relationships..." + if MERGE_MODE == 3: + + if DEBUG: print "Joining shapes..." + + for host,children in additions.items(): + if ifcfile[host].is_a("IfcBuildingStorey"): + compound = [] + for c in children: + if c in shapes.keys(): + compound.append(shapes[c]) + del shapes[c] + if c in additions.keys(): + for c2 in additions[c]: + if c2 in shapes.keys(): + compound.append(shapes[c2]) + del shapes[c2] + if compound: + name = ifcfile[host].Name or "Floor" + if PREFIX_NUMBERS: name = "ID" + str(host) + " " + name + obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name) + obj.Label = name + obj.Shape = Part.makeCompound(compound) + if shapes: # remaining shapes + obj = FreeCAD.ActiveDocument.addObject("Part::Feature","Unclaimed") + obj.Shape = Part.makeCompound(shapes.values()) - # subtractions - if SEPARATE_OPENINGS: - for subtraction in subtractions: - if (subtraction[0] in objects.keys()) and (subtraction[1] in objects.keys()): - #print objects[subtraction[0]].Name, objects[subtraction[1]].Name - Arch.removeComponents(objects[subtraction[0]],objects[subtraction[1]]) - - # additions - for host,children in additions.iteritems(): - if host in objects.keys(): - cobs = [objects[child] for child in children if child in objects.keys()] - if cobs: - Arch.addComponents(cobs,objects[host]) - - FreeCAD.ActiveDocument.recompute() + else: - # cleaning bad shapes - for obj in objects.values(): - if obj.isDerivedFrom("Part::Feature"): - if obj.Shape.isNull(): - Arch.rebuildArchShape(obj) + if DEBUG: print "Processing relationships..." + + # subtractions + if SEPARATE_OPENINGS: + for subtraction in subtractions: + if (subtraction[0] in objects.keys()) and (subtraction[1] in objects.keys()): + if DEBUG: print "subtracting ",objects[subtraction[0]].Name, " from ", objects[subtraction[1]].Name + Arch.removeComponents(objects[subtraction[0]],objects[subtraction[1]]) + if DEBUG: FreeCAD.ActiveDocument.recompute() + + # additions + for host,children in additions.items(): + if host in objects.keys(): + cobs = [objects[child] for child in children if child in objects.keys()] + if cobs: + if DEBUG and (len(cobs) > 10) and ( not(Draft.getType(objects[host]) in ["Site","Building","Floor"])): + # avoid huge fusions + print "more than 10 shapes to add: skipping." + else: + if DEBUG: print "adding ",cobs, " to ", objects[host].Name + Arch.addComponents(cobs,objects[host]) + if DEBUG: FreeCAD.ActiveDocument.recompute() + + FreeCAD.ActiveDocument.recompute() + + if DEBUG: print "Cleaning..." + + # cleaning bad shapes + for obj in objects.values(): + if obj.isDerivedFrom("Part::Feature"): + if obj.Shape.isNull(): + Arch.rebuildArchShape(obj) FreeCAD.ActiveDocument.recompute() @@ -433,10 +589,11 @@ def insert(filename,docname,skip=[]): count += 1 FreeCAD.ActiveDocument.recompute() - + if FreeCAD.GuiUp: import FreeCADGui FreeCADGui.SendMsgToActiveView("ViewFit") + print "Finished importing." return doc @@ -552,7 +709,7 @@ def export(exportList,filename): elif ifctype == "IfcBuilding": args = args + ["ELEMENT",None,None,None] elif ifctype == "IfcBuildingStorey": - args = args + ["ELEMENT",None] + args = args + ["ELEMENT",obj.Placement.Base.z] # creating the product product = getattr(ifcfile,"create"+ifctype)(*args) @@ -580,22 +737,26 @@ def export(exportList,filename): for key in obj.IfcAttributes: if not (key in ["IfcUID","FlagForceBrep"]): r = obj.IfcAttributes[key].strip(")").split("(") - tp = r[0] - val = "(".join(r[1:]) - val = val.strip("'") - val = val.strip('"') - if DEBUG: print " property ",key," : ",str(val), " (", str(tp), ")" - if tp in ["IfcLabel","IfcText","IfcIdentifier"]: - val = str(val) - elif tp == "IfcBoolean": - if val == ".T.": - val = True - else: - val = False - elif tp == "IfcInteger": - val = int(val) + if len(r) == 1: + tp = "IfcText" + val = r[0] else: - val = float(val) + tp = r[0] + val = "(".join(r[1:]) + val = val.strip("'") + val = val.strip('"') + if DEBUG: print " property ",key," : ",str(val), " (", str(tp), ")" + if tp in ["IfcLabel","IfcText","IfcIdentifier"]: + val = str(val) + elif tp == "IfcBoolean": + if val == ".T.": + val = True + else: + val = False + elif tp == "IfcInteger": + val = int(val) + else: + val = float(val) props.append(ifcfile.createIfcPropertySingleValue(str(key),None,ifcfile.create_entity(str(tp),val),None)) if props: pset = ifcfile.createIfcPropertySet(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'PropertySet',None,props) @@ -781,8 +942,11 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess for fcface in fcsolid.Faces: for e in fcface.Edges: if not isinstance(e.Curve,Part.Line): - curves = True + if e.curvatureAt(e.FirstParameter+(e.LastParameter-e.FirstParameter)/2) > 0.0001: + curves = True + break if curves: + shapetype = "triangulated" tris = fcsolid.tessellate(tessellation) for tri in tris[1]: pts = [ifcfile.createIfcCartesianPoint(tuple(tris[0][i])) for i in tri] @@ -791,6 +955,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess face = ifcfile.createIfcFace([bound]) faces.append(face) else: + shapetype = "brep" for fcface in fcsolid.Faces: loops = [] verts = [v.Point for v in Part.Wire(DraftGeomUtils.sortEdges(fcface.OuterWire.Edges)).Vertexes] @@ -820,7 +985,6 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess shell = ifcfile.createIfcClosedShell(faces) shape = ifcfile.createIfcFacetedBrep(shell) shapes.append(shape) - shapetype = "brep" if shapes: diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 124c532a4..834586a9c 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -2430,6 +2430,13 @@ def clone(obj,delta=None): if (len(obj) == 1) and obj[0].isDerivedFrom("Part::Part2DObject"): cl = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","Clone2D") cl.Label = "Clone of " + obj[0].Label + " (2D)" + elif (len(obj) == 1) and hasattr(obj[0],"IfcAttributes"): + # arch objects can be clones + import Arch + cl = getattr(Arch,"make"+obj[0].Proxy.Type)() + cl.Label = "Clone of " + obj[0].Label + cl.CloneOf = obj[0] + return cl else: cl = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Clone") cl.Label = "Clone of " + obj[0].Label