diff --git a/src/Mod/Arch/CMakeLists.txt b/src/Mod/Arch/CMakeLists.txt index f3db81da7882..3281168ea43d 100644 --- a/src/Mod/Arch/CMakeLists.txt +++ b/src/Mod/Arch/CMakeLists.txt @@ -5,7 +5,7 @@ SET(Arch_SRCS ArchComponent.py ArchWall.py importIFC.py - ifcReader.py + importIFClegacy.py Arch_rc.py Arch.py ArchBuilding.py @@ -25,7 +25,6 @@ SET(Arch_SRCS ArchSpace.py ArchRebar.py TestArch.py - ifcWriter.py ArchFrame.py ArchPanel.py ) diff --git a/src/Mod/Arch/ifcReader.py b/src/Mod/Arch/ifcReader.py deleted file mode 100644 index 647f5206b8b6..000000000000 --- a/src/Mod/Arch/ifcReader.py +++ /dev/null @@ -1,535 +0,0 @@ -#*************************************************************************** -#* * -#* Copyright (c) 2011 * -#* Yorik van Havre, Marijn van Aerle * -#* * -#* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (LGPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* This program is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -#* GNU Library General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * -#* License along with this program; if not, write to the Free Software * -#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * -#*************************************************************************** - -import os, re, copy - -__title__="FreeCAD IFC parser" -__author__ = "Yorik van Havre, Marijn van Aerle" -__url__ = "http://www.freecadweb.org" - -''' -FreeCAD IFC parser, by Yorik van Havre, based on work by Marijn van Aerle - -Usage: - import ifcReader - ifcdoc = ifcReader.IfcDocument("path/to/file.ifc") - print ifcdoc.Entities - myent = ifcdoc.Entities[20] # returns one entity - myent = ifcdoc.getEnt(20) # alternative way - polylines = ifcdoc.getEnt("IFCPOLYLINE") # returns a list - print myent.attributes - -The ifc document contains a list of entities, that can be retrieved -by iterating the list (indices corresponds to the entities ids) -or by using the getEnt() method. All entities have id, type -and attributes. Attributes can have values such as text or number, -or a link to another entity. - -Important note: - -1) For this reader to function, you need an IFC Schema Express file (.exp) -available here: -http://www.steptools.com/support/stdev_docs/express/ifc2x3/ifc2x3_tc1.exp -For licensing reasons we are not allowed to ship that file with FreeCAD. -Just place the .exp file together with this script. - -2) IFC files can have ordered content (ordered list, no entity number missing) -or be much messier (entity numbers missing, etc). The performance of the reader -will be drastically different. -''' - -IFCLINE_RE = re.compile("#(\d+)[ ]?=[ ]?(.*?)\((.*)\);[\\r]?$") -DEBUG = False - -class IfcSchema: - SIMPLETYPES = ["INTEGER", "REAL", "STRING", "NUMBER", "LOGICAL", "BOOLEAN"] - NO_ATTR = ["WHERE", "INVERSE","WR2","WR3", "WR4", "WR5", "UNIQUE", "DERIVE"] - - def __init__(self, filename): - self.filename = filename - if not os.path.exists(filename): - raise ImportError("no IFCSchema file found!") - else: - self.file = open(self.filename) - self.data = self.file.read() - self.types = self.readTypes() - self.entities = self.readEntities() - if DEBUG: print "Parsed from schema %s: %s entities and %s types" % (self.filename, len(self.entities), len(self.types)) - - def readTypes(self): - """ - Parse all the possible types from the schema, - returns a dictionary Name -> Type - """ - types = {} - for m in re.finditer("TYPE (.*) = (.*);", self.data): - typename, typetype = m.groups() - if typetype in self.SIMPLETYPES: - types[typename] = typetype - else: - types[typename] = "#" + typetype - - return types - - def readEntities(self): - """ - Parse all the possible entities from the schema, - returns a dictionary of the form: - { name: { - "supertype": supertype, - "attributes": [{ key: value }, ..] - }} - """ - entities = {} - - # Regexes must be greedy to prevent matching outer entity and end_entity strings - # Regexes have re.DOTALL to match newlines - for m in re.finditer("ENTITY (.*?)END_ENTITY;", self.data, re.DOTALL): - entity = {} - raw_entity_str = m.groups()[0] - - entity["name"] = re.search("(.*?)[;|\s]", raw_entity_str).groups()[0].upper() - - subtypeofmatch = re.search(".*SUBTYPE OF \((.*?)\);", raw_entity_str) - entity["supertype"] = subtypeofmatch.groups()[0].upper() if subtypeofmatch else None - - # find the shortest string matched from the end of the entity type header to the - # first occurence of a NO_ATTR string (when it occurs on a new line) - inner_str = re.search(";(.*?)$", raw_entity_str, re.DOTALL).groups()[0] - - attrs_str = min([inner_str.partition("\r\n "+a)[0] for a in self.NO_ATTR]) - attrs = [] - for am in re.finditer("(.*?) : (.*?);", attrs_str, re.DOTALL): - name, attr_type = [s.replace("\r\n\t","") for s in am.groups()] - attrs.append((name, attr_type)) - - entity["attributes"] = attrs - entities[entity["name"]] = entity - - - return entities - - def getAttributes(self, name): - """ - Get all attributes af an entity, including supertypes - """ - ent = self.entities[name] - - attrs = [] - while ent != None: - this_ent_attrs = copy.copy(ent["attributes"]) - this_ent_attrs.reverse() - attrs.extend(this_ent_attrs) - ent = self.entities.get(ent["supertype"], None) - - attrs.reverse() - return attrs - - def capitalize(self, name): - "returns a capitalized version of a type" - if name.upper() in self.data.upper(): - i1 = self.data.upper().index(name.upper()) - i2 = i1 + len(name) - name = self.data[i1:i2] - return name - -class IfcFile: - """ - Parses an ifc file given by filename, entities can be retrieved by name and id - The whole file is stored in a dictionary (in memory) - """ - - entsById = {} - entsByName = {} - - def __init__(self, filename,schema): - self.filename = filename - self.schema = IfcSchema(schema) - self.file = open(self.filename) - self.entById, self.entsByName, self.header = self.read() - self.file.close() - if DEBUG: print "Parsed from file %s: %s entities" % (self.filename, len(self.entById)) - - def getEntityById(self, id): - return self.entById.get(id, None) - - def getEntitiesByName(self, name): - return self.entsByName.get(name, None) - - def read(self): - """ - Returns 2 dictionaries, entById and entsByName - """ - entById = {} - entsByName = {} - header = 'HEADER ' - readheader = False - for line in self.file: - e = self.parseLine(line) - if e: - entById[int(e["id"])] = e - ids = e.get(e["name"],[]) - ids.append(e["id"]) - entsByName[e["name"]] = list(set(ids)) - elif 'HEADER' in line: - readheader = True - elif readheader: - if 'ENDSEC' in line: - readheader = False - else: - header += line - - return [entById, entsByName, header] - - def parseLine(self, line): - """ - Parse a line - """ - m = IFCLINE_RE.search(line) # id,name,attrs - if m: - id, name, attrs = m.groups() - id = id.strip() - name = name.strip() - attrs = attrs.strip() - else: - return False - - return {"id": id, "name": name, "attributes": self.parseAttributes(name, attrs)} - - def parseAttributes(self, ent_name, attrs_str): - """ - Parse the attributes of a line - """ - parts = [] - lastpos = 0 - - while lastpos < len(attrs_str): - newpos = self.nextString(attrs_str, lastpos) - parts.extend(self.parseAttribute(attrs_str[lastpos:newpos-1])) - lastpos = newpos - - schema_attributes = self.schema.getAttributes(ent_name) - - assert len(schema_attributes) == len(parts), \ - "Expected %s attributes, got %s (entity: %s" % \ - (len(schema_attributes), len(parts), ent_name) - - attribute_names = [a[0] for a in schema_attributes] - - return dict(zip(attribute_names, parts)) - - def parseAttribute(self, attr_str): - """ - Map a single attribute to a python type (recursively) - """ - parts = [] - lastpos = 0 - while lastpos < len(attr_str): - newpos = self.nextString(attr_str, lastpos) - s = attr_str[lastpos:newpos-1] - if (s[0] == "(" and s[-1] == ")"): # list, recurse - parts.append(self.parseAttribute(s[1:-1])) - else: - try: - parts.append(float(s)) # number, any kind - except ValueError: - if s[0] == "'" and s[-1] == "'": # string - parts.append(s[1:-1]) - elif s == "$": - parts.append(None) - else: - parts.append(s) # ref, enum or other - - lastpos = newpos - - return parts - - - def nextString(self, s, start): - """ - Parse the data part of a line - """ - parens = 0 - quotes = 0 - - for pos in range(start,len(s)): - c = s[pos] - if c == "," and parens == 0 and quotes == 0: - return pos+1 - elif c == "(" and quotes == 0: - parens += 1 - elif c == ")" and quotes == 0: - parens -= 1 - elif c == "\'" and quotes == 0: - quotes = 1 - elif c =="\'" and quotes == 1: - quotes = 0 - - return len(s)+1 - -class IfcEntity: - "a container for an IFC entity" - def __init__(self,ent,doc=None): - self.data = ent - self.id = int(ent['id']) - self.type = ent['name'].upper().strip(",[]()") - self.attributes = ent['attributes'] - self.doc = doc - - def __repr__(self): - return str(self.id) + ' : ' + self.type + ' ' + str(self.attributes) - - def getProperties(self): - return self.doc.find('IFCRELDEFINESBYPROPERTIES','RelatedObjects',self) - - def getProperty(self,propName): - "finds the value of the given property or quantity in this object, if exists" - propsets = self.doc.find('IFCRELDEFINESBYPROPERTIES','RelatedObjects',self) - if not propsets: return None - propset = [] - for p in propsets: - if hasattr(p.RelatingPropertyDefinition,"HasProperties"): - propset.extend(p.RelatingPropertyDefinition.HasProperties) - elif hasattr(p.RelatingPropertyDefinition,"Quantities"): - propset.extend(p.RelatingPropertyDefinition.Quantities) - for prop in propset: - if prop.Name == propName: - print "found valid",prop - if hasattr(prop,"LengthValue"): - return prop.LengthValue - elif hasattr(prop,"AreaValue"): - return prop.AreaValue - elif hasattr(prop,"VolumeValue"): - return prop.VolumeValue - elif hasattr(prop,"NominalValue"): - return prop.NominalValue - return None - - def getAttribute(self,attr): - "returns the value of the given attribute, if exists" - if hasattr(self,attr): - return self.__dict__[attr] - return None - -class IfcDocument: - "an object representing an IFC document" - def __init__(self,filename,schema="IFC2X3_TC1.exp"): - f = IfcFile(filename,schema) - self.filename = filename - self.data = f.entById - self.Entities = {0:f.header} - for k,e in self.data.iteritems(): - eid = int(e['id']) - self.Entities[eid] = IfcEntity(e,self) - if DEBUG: print len(self.Entities),"entities created. Creating attributes..." - for k,ent in self.Entities.iteritems(): - if DEBUG: print "attributing entity ",ent - if hasattr(ent,"attributes"): - for k,v in ent.attributes.iteritems(): - if DEBUG: print "parsing attribute: ",k," value ",v - if isinstance(v,str): - val = self.__clean__(v) - elif isinstance(v,list): - val = [] - for item in v: - if isinstance(item,str): - val.append(self.__clean__(item)) - else: - val.append(item) - else: - val = v - setattr(ent,k.strip(),val) - if DEBUG: print "Document successfully created" - - def __clean__(self,value): - "turns an attribute value into something usable" - try: - val = value.strip(" ()'") - if val[:3].upper() == "IFC": - if "IFCTEXT" in val.upper(): - l = val.split("'") - if len(l) == 3: val = l[1] - elif "IFCBOOLEAN" in value.upper(): - l = val.split(".") - if len(l) == 3: val = l[1] - if val.upper() == "F": val = False - elif val.upper() == "T": val = True - elif "IFCREAL" in val.upper(): - l = val.split("(") - if len(l) == 2: val = float(l[1].strip(")")) - else: - if '#' in val: - if "," in val: - val = val.split(",") - l = [] - for subval in val: - if '#' in subval: - s = subval.strip(" #") - if DEBUG: print "referencing ",s," : ",self.getEnt(int(s)) - l.append(self.getEnt(int(s))) - val = l - else: - val = val.strip() - val = val.replace("#","") - if DEBUG: print "referencing ",val," : ",self.getEnt(int(val)) - val = self.getEnt(int(val)) - if not val: - val = value - except: - if DEBUG: print "error parsing attribute",value - val = value - return val - - def __repr__(self): - return "IFC Document: " + self.filename + ', ' + str(len(self.Entities)) + " entities " - - def getEnt(self,ref): - "gets an entity by id number, or a list of entities by type" - if isinstance(ref,int): - if ref in self.Entities: - return self.Entities[ref] - elif isinstance(ref,str): - l = [] - ref = ref.upper() - for k,ob in self.Entities.iteritems(): - if hasattr(ob,"type"): - if ob.type == ref: - l.append(ob) - return l - return None - - def search(self,pat): - "searches entities types for partial match" - l = [] - pat = pat.upper() - for k,ob in self.Entities.iteritems(): - if hasattr(ob,"type"): - if pat in ob.type: - if not ob.type in l: - l.append(ob.type) - return l - - def find(self,pat1,pat2=None,pat3=None): - '''finds objects in the current IFC document. - arguments can be of the following form: - - (pattern): returns object types matching the given pattern (same as search) - - (type,property,value): finds, in all objects of type "type", those whose - property "property" has the given value - ''' - if pat3: - bobs = self.getEnt(pat1) - obs = [] - for bob in bobs: - if hasattr(bob,pat2): - if bob.getAttribute(pat2) == pat3: - obs.append(bob) - return obs - elif pat1: - ll = self.search(pat1) - obs = [] - for l in ll: - obs.extend(self.getEnt(l)) - return obs - return None - -def explorer(filename,schema="IFC2X3_TC1.exp"): - "returns a PySide dialog showing the contents of an IFC file" - from PySide import QtCore,QtGui - ifc = IfcDocument(filename,schema) - schema = IfcSchema(schema) - tree = QtGui.QTreeWidget() - tree.setColumnCount(3) - tree.setWordWrap(True) - tree.header().setDefaultSectionSize(60) - tree.header().resizeSection(0,60) - tree.header().resizeSection(1,30) - tree.header().setStretchLastSection(True) - tree.headerItem().setText(0, "ID") - tree.headerItem().setText(1, "") - tree.headerItem().setText(2, "Item and Properties") - bold = QtGui.QFont() - bold.setWeight(75) - bold.setBold(True) - - #print ifc.Entities - - for i in ifc.Entities.keys(): - e = ifc.Entities[i] - item = QtGui.QTreeWidgetItem(tree) - if hasattr(e,"id"): - item.setText(0,str(e.id)) - if e.type in ["IFCWALL","IFCWALLSTANDARDCASE"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Wall_Tree.svg")) - elif e.type in ["IFCCOLUMN","IFCBEAM","IFCSLAB","IFCFOOTING"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Structure_Tree.svg")) - elif e.type in ["IFCSITE"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Site_Tree.svg")) - elif e.type in ["IFCBUILDING"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Building_Tree.svg")) - elif e.type in ["IFCSTOREY"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Floor_Tree.svg")) - elif e.type in ["IFCWINDOW"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Window_Tree.svg")) - elif e.type in ["IFCROOF"]: - item.setIcon(1,QtGui.QIcon(":icons/Arch_Roof_Tree.svg")) - elif e.type in ["IFCEXTRUDEDAREASOLID","IFCCLOSEDSHELL"]: - item.setIcon(1,QtGui.QIcon(":icons/Tree_Part.svg")) - elif e.type in ["IFCFACE"]: - item.setIcon(1,QtGui.QIcon(":icons/Draft_SwitchMode.svg")) - elif e.type in ["IFCARBITRARYCLOSEDPROFILEDEF","IFCPOLYLOOP"]: - item.setIcon(1,QtGui.QIcon(":icons/Draft_Draft.svg")) - item.setText(2,str(schema.capitalize(e.type))) - item.setFont(2,bold); - for a in e.attributes.keys(): - if hasattr(e,a): - if not a.upper() in ["ID", "GLOBALID"]: - v = getattr(e,a) - if isinstance(v,IfcEntity): - t = "Entity #" + str(v.id) + ": " + str(v.type) - elif isinstance(v,list): - t = "" - else: - t = str(v) - t = " " + str(a) + " : " + str(t) - item = QtGui.QTreeWidgetItem(tree) - item.setText(2,str(t)) - if isinstance(v,list): - for vi in v: - if isinstance(vi,IfcEntity): - t = "Entity #" + str(vi.id) + ": " + str(vi.type) - else: - t = vi - t = " " + str(t) - item = QtGui.QTreeWidgetItem(tree) - item.setText(2,str(t)) - d = QtGui.QDialog() - d.setObjectName("IfcExplorer") - d.setWindowTitle("Ifc Explorer") - d.resize(640, 480) - layout = QtGui.QVBoxLayout(d) - layout.addWidget(tree) - return d - -if __name__ == "__main__": - print __doc__ - diff --git a/src/Mod/Arch/ifcWriter.py b/src/Mod/Arch/ifcWriter.py deleted file mode 100644 index 5508435674c3..000000000000 --- a/src/Mod/Arch/ifcWriter.py +++ /dev/null @@ -1,587 +0,0 @@ -#*************************************************************************** -#* * -#* Copyright (c) 2013 * -#* Yorik van Havre * -#* * -#* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (LGPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* This program is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -#* GNU Library General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * -#* License along with this program; if not, write to the Free Software * -#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * -#*************************************************************************** - -# this is a python convenience interface to IfcOpenShell, made to ease the -# creation of IFC files from scratch. -# currently using a test build of IfcOpenShell with export capabilities from -# https://github.com/aothms/IfcOpenShell - -# see examples on how to use this module at the bottom of this file - -import sys, uuid, time, math - -# if you already have another version of IfcOpenShell: -# adding here the path to the ifcwrap folder of the ifcopenshell build. That -# folder must also contain an __init__.py file. This is to differentiate with -# systemwide-installed IfcOpenShell, which has the same name. -# if you have such setup, uncomment the following 2 lines and comment out the -# third one. -#sys.path.append("/home/yorik/Sources/build/ifcopenshell-dev") -#from ifcwrap import IfcImport - -try: - import IfcImport as ifcw -except: - import ifc_wrapper as ifcw -else: - print "error: IfcOpenShell not found!" - sys.exit() - -# checking that we got the right importer, with export capabilities -if (not hasattr(ifcw,"IfcFile")) and (not hasattr(ifcw,"file")): - print "Wrong version of IfcOpenShell" - sys.exit() - -PRECISION = 8 # rounding value, in number of digits -APPLYFIX = True # if true, the ifcopenshell bug-fixing function is applied when saving files - -# Basic functions ################################################################# - - -class _tempEntityHolder: - """a temporary object to store entity references - to be made into something nicer later...""" - def __init__(self): - self.refs = [] - -holder = _tempEntityHolder() - -def uid(): - """returns a suitable GlobalID""" - u = str(uuid.uuid4())[:22] - u = u.replace("-","_") - return u - -def now(string=False): - "returns a suitable Ifc Time" - if string: - return time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()) - else: - return int(time.time()) - -def getPropertyNames(entity): - """getPropertyNames(entity): Returns a dictionary with - the numbers and names of the pythonproperties available for - this entity""" - ents = {} - if hasattr(entity,"get_argument_count"): - l = entity.get_argument_count() - else: - l = len(entity) - for i in range(l): - ents[i] = entity.get_argument_name(i) - return ents - -def getTuple(vec): - """getTuple(vec): returns a tuple from other coordinate - structures: tuple, list, 3d vector, or occ vertex""" - def fmt(t): - t = float(t) - t = round(t,PRECISION) - return t - if isinstance(vec,tuple): - return tuple([fmt(v) for v in vec]) - elif isinstance(vec,list): - return tuple([fmt(v) for v in vec]) - elif hasattr(vec,"x") and hasattr(vec,"y") and hasattr(vec,"z"): - return (fmt(vec.x),fmt(vec.y),fmt(vec.z)) - elif hasattr(vec,"X") and hasattr(vec,"Y") and hasattr(vec,"Z"): - return (fmt(vec.X),fmt(vec.Y),fmt(vec.Z)) - -def getValueAndDirection(vec): - """getValueAndDirection(vec): returns a length and a tuple - representing a normalized vector from a tuple""" - vec = getTuple(vec) - length = round(math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2),PRECISION) - ratio = 1/length - x = round(vec[0]*ratio,PRECISION) - y = round(vec[1]*ratio,PRECISION) - z = round(vec[2]*ratio,PRECISION) - normal = (x,y,z) - return length,normal - -def create(ifcdoc=None,ifcname=None,arguments=[]): - """create(ifcdoc,ifcname,[arguments]):creates an entity - of the given name in the given document and optionally - gives it an ordered list of arguments""" - if hasattr(ifcw,"Entity"): - entity = ifcw.Entity(ifcname) - else: - entity = ifcw.entity_instance(ifcname) - if ifcdoc: - ifcdoc.add(entity) - # this is a temporary hack while ifcopenshell has no ref counting - holder.refs.append(entity) - if not isinstance(arguments,list): - arguments = [arguments] - for i in range(len(arguments)): - arg = arguments[i] - if isinstance(arg,tuple): - if len(arg) in [2,3]: - if hasattr(ifcw,"Doubles"): - arg = ifcw.Doubles(arg) - else: - arg = ifcw.doubles(arg) - entity.set_argument(i,arg) - return entity - - -# IfcDocument Object ################################################################# - - -class IfcDocument(object): - """IfcDocument([filepath,name,owner,organization,application,version]) - Creates an empty IFC document.""" - - def __init__(self,filepath="",name="",owner="",organization="",application="Python IFC exporter",version="0.0"): - if hasattr(ifcw,"IfcFile"): - self._fileobject = ifcw.IfcFile() - else: - self._fileobject = ifcw.file() - self._person = create(self._fileobject,"IfcPerson",[None,None,"",None,None,None,None,None]) - self._org = create(self._fileobject,"IfcOrganization",[None,"",None,None,None]) - pno = create(self._fileobject,"IfcPersonAndOrganization",[self._person,self._org,None]) - app = create(self._fileobject,"IfcApplication",[self._org,version,application,uid()]) - self._owner = create(self._fileobject,"IfcOwnerHistory",[pno,app,None,"ADDED",None,pno,app,now()]) - axp = self.addPlacement(local=False) - dim0 = create(self._fileobject,"IfcDirection",getTuple((0,1,0))) - self._repcontext = create(self._fileobject,"IfcGeometricRepresentationContext",['Plan','Model',3,1.E-05,axp,dim0]) - dim1 = create(self._fileobject,"IfcDimensionalExponents",[0,0,0,0,0,0,0]) - dim2 = create(self._fileobject,"IfcSIUnit",[dim1,"LENGTHUNIT","MILLI","METRE"]) - dim3 = create(self._fileobject,"IfcSIUnit",[dim1,"AREAUNIT",None,"SQUARE_METRE"]) - dim4 = create(self._fileobject,"IfcSIUnit",[dim1,"VOLUMEUNIT",None,"CUBIC_METRE"]) - dim6 = create(self._fileobject,"IfcSIUnit",[dim1,"PLANEANGLEUNIT",None,"RADIAN"]) - dim7 = create(None,"IfcPlaneAngleMeasure",[1.745E-2]) - dim8 = create(self._fileobject,"IfcMeasureWithUnit",[dim7,dim6]) - dim9 = create(self._fileobject,"IfcConversionBasedUnit",[dim1,"PLANEANGLEUNIT","DEGREE",dim8]) - units = create(self._fileobject,"IfcUnitAssignment",[[dim2,dim3,dim4,dim9]]) - self.Project = create(self._fileobject,"IfcProject",[uid(),self._owner,None,None,None,None,None,[self._repcontext],units]) - self.Site = None - self._storeyRelations = {} - self.BuildingProducts = [] - self.Storeys = [] - self.Buildings = [] - self.FilePath = filepath - self.Owner = owner - self.Organization = organization - self.Name = name - - def __repr__(self): - return "IFC document " + self.Name #+ " containing " + str(len(holder)) + " entities" - - def __setattr__(self,key,value): - if value: - if key == "Owner": - self._person.set_argument(2,str(value)) - elif key == "Organization": - self._org.set_argument(1,str(value)) - elif key == "Name": - self.Project.set_argument(2,str(value)) - self.__dict__.__setitem__(key,value) - - def findByName(self,ifctype,name): - "finds an entity of a given ifctype by name" - objs = self._fileobject.by_type(ifctype) - for obj in objs: - if hasattr(obj,"get_argument_count"): - l = obj.get_argument_count() - else: - l = len(obj) - for i in range(l): - if obj.get_argument_name(i) == "Name": - if obj.get_argument(i) == name: - return obj - return None - - def write(self,fp=None): - "writes the document to its file" - if fp: - path = fp - else: - path = self.FilePath - if path: - try: - self._fileobject.write(path) - if APPLYFIX: - print ("IfcWriter: Applying fix...") - self._fix(path) - except: - print ("IfcWriter: Error writing to "+path) - else: - print ("IfcWriter: Successfully written to "+path) - else: - print ("IfcWriter: Error: File path is not defined, unable to save") - - def _fix(self,path): - "dirty hack to fix bugs in ifcopenshell" - import os - if os.path.exists(path): - f = open(path,"rb") - lines = [] - for l in f.readlines(): - if "(=IFC" in l: - # bug 1: adding an ifc entity without ID adds an unwanted = sign - l = l.replace("(=IFC","(IFC") - #elif ("FACEBOUND" in l) or ("FACEOUTERBOUND" in l): # FIXED - # bug 2: booleans are exported as ints - #l = l.replace(",1);",",.T.);") - #l = l.replace(",0);",",.F.);") - #elif "FILE_DESCRIPTION" in l: # FIXED - # bug 3: incomplete file description header - #l = l.replace("ViewDefinition []","ViewDefinition [CoordinationView_V2.0]") - #elif "FILE_NAME" in l: # FIXED - # bug 4: incomplete file name entry - #l = l.replace("FILE_NAME('','',(''),('',''),'IfcOpenShell','IfcOpenShell','');","FILE_NAME('"+path+"','"+now(string=True)+"',('"+self.Owner+"'),('',''),'IfcOpenShell','IfcOpenShell','');") - elif "IFCSIUNIT" in l: - # bug 5: no way to insert * character - l = l.replace("IFCSIUNIT(#13,","IFCSIUNIT(*,") - lines.append(l) - f.close() - f = open(path,"wb") - for l in lines: - f.write(l) - f.close() - - def union(self,solids): - """union(solids): creates a boolean union between all the solids of the list""" - if len(solids) == 1: - return solids[0] - else: - s1 = solids.pop(0) - s2 = solids.pop(0) - base = create(self._fileobject,"IfcBooleanResult",["UNION",s1,s2]) - for s in solids: - base = create(self._fileobject,"IfcBooleanResult",["UNION",base,s]) - return base - - def addPlacement(self,reference=None,origin=(0,0,0),xaxis=(1,0,0),zaxis=(0,0,1),local=True,flat=False): - """addPlacement([reference,origin,xaxis,zaxis,local]): adds a placement. origin, - xaxis and zaxis can be either tuples or 3d vectors. If local is False, a global - placement is returned, otherwise a local one.""" - if flat: - xvc = create(self._fileobject,"IfcDirection",getTuple(xaxis)[:2]) - ovc = create(self._fileobject,"IfcCartesianPoint",getTuple(origin)[:2]) - gpl = create(self._fileobject,"IfcAxis2Placement2D",[ovc,xvc]) - else: - xvc = create(self._fileobject,"IfcDirection",getTuple(xaxis)) - zvc = create(self._fileobject,"IfcDirection",getTuple(zaxis)) - ovc = create(self._fileobject,"IfcCartesianPoint",getTuple(origin)) - gpl = create(self._fileobject,"IfcAxis2Placement3D",[ovc,zvc,xvc]) - if local: - lpl = create(self._fileobject,"IfcLocalPlacement",[reference,gpl]) - return lpl - else: - return gpl - - def addSite(self,placement=None,name="Site",description=None,latitude=None,longitude=None,elevation=None,landtitlenumber=None,address=None): - """makeSite(ifcdoc,project,owner,[placement,name,description]): creates a site - in the given ifc document""" - if self.Site: - return - if not placement: - placement = self.addPlacement() - self.Site = create(self._fileobject,"IfcSite",[uid(),self._owner,str(name),description,None,placement,None,None,"ELEMENT",latitude,longitude,elevation,landtitlenumber,address]) - self._relate(self.Project,self.Site) - - def addBuilding(self,placement=None,name="Default building",description=None): - """addBuilding([placement,name,description]): adds a building""" - if not placement: - placement = self.addPlacement() - if not self.Site: - self.addSite() - bdg = create(self._fileobject,"IfcBuilding",[uid(),self._owner,str(name),description,None,placement,None,None,"ELEMENT",None,None,None]) - self._relate(self.Site,bdg) - self.Buildings.append(bdg) - return bdg - - def addStorey(self,building=None,placement=None,name="Default storey",description=None): - """addStorey([building,placement,name,description]): adds a storey""" - if not placement: - placement = self.addPlacement() - sto = create(self._fileobject,"IfcBuildingStorey",[uid(),self._owner,str(name),description,None,placement,None,None,"ELEMENT",None]) - if not building: - if self.Buildings: - building = self.Buildings[0] - else: - building = self.addBuilding() - self._relate(building,sto) - self.Storeys.append(sto) - return sto - - def addGroup(self,entities,name="Default group",description=None): - """addGroup(entities,[name,description]): adds a group with the given entities""" - if not isinstance(entities,list): - entities = [entities] - gro = create(self._fileobject,"IfcGroup",[uid(),self._owner,str(name),description,None]) - rel = create(self._fileobject,"IfcRelAssignsToGroup",[uid(),self._owner,str(name)+"-relation",None,entities,"PRODUCT",gro]) - return gro - - def _relate(self,container,entities): - """relate(container,entities): relates the given entities to the given - container""" - if not isinstance(entities,list): - entities = [entities] - if container.is_a("IfcBuildingStorey"): - sid = container.get_argument(0) - if sid in self._storeyRelations: - prods = self._storeyRelations[sid].get_argument(4) - self._storeyRelations[sid].set_argument(4,prods+entities) - else: - rel = create(self._fileobject,"IfcRelContainedInSpatialStructure",[uid(),self._owner,'StoreyLink','',entities,container]) - self._storeyRelations[sid] = rel - else: - if entities[0].is_a("IfcOpeningElement"): - create(self._fileobject,"IfcRelVoidsElement",[uid(),self._owner,'Opening','',container,entities[0]]) - else: - create(self._fileobject,"IfcRelAggregates",[uid(),self._owner,'Relationship','',container,entities]) - - def addProduct(self,elttype,shapes,storey=None,placement=None,name="Unnamed element",description=None,extra=None): - """addProduct(elttype,representations,[storey,placement,name,description,extra]): creates an element of the given type - (IfcWall, IfcBeam, etc...) with the given attributes, plus the given extra attributes.""" - elttype = str(elttype) - if not extra: - extra = [] - if not description: - description = None - if not placement: - placement = self.addPlacement() - representations = self.addRepresentations(shapes) - prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,representations]) - try: - elt = create(self._fileobject,elttype,[uid(),self._owner,name,description,None,placement,prd,None]+extra) - except: - print "unable to create an ",elttype, " with attributes: ",[uid(),self._owner,str(name),description,None,placement,prd,None]+extra - try: - if hasattr(ifcw,"Entity"): - o = ifcw.Entity(elttype) - else: - o = ifcw.entity_instance(elttype) - print "supported attributes are: " - print getPropertyNames(o) - except: - print "unable to create an element of type '"+elttype+"'" - print "WARNING: skipping object '"+name+"' of type "+elttype - return None - self.BuildingProducts.append(elt) - if not storey: - if self.Storeys: - storey = self.Storeys[0] - else: - storey = self.addStorey() - self._relate(storey,elt) - return elt - - def addRepresentations(self,shapes): - """addRepresentations(shapes,[solidType]): creates a representation from the given shape""" - solidType = "Brep" - if not isinstance(shapes,list): - if shapes.is_a("IfcExtrudedAreaSolid"): - solidType = "SweptSolid" - shapes = [shapes] - reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body',solidType,[shape for shape in shapes]])] - return reps - - def addColor(self,rgb,rep): - """addColor(rgb,rep): adds a RGB color definition tuple (float,float,float) to a given representation""" - col = create(self._fileobject,"IfcColourRgb",[None]+list(rgb)) - ssr = create(self._fileobject,"IfcSurfaceStyleRendering",[col,None,None,None,None,None,None,None,"FLAT"]) - iss = create(self._fileobject,"IfcSurfaceStyle",[None,"BOTH",[ssr]]) - psa = create(self._fileobject,"IfcPresentationStyleAssignment",[[iss]]) - isi = create(self._fileobject,"IfcStyledItem",[rep,[psa],None]) - return isi - - def addProfile(self,ifctype,data,curvetype="AREA"): - """addProfile(ifctype,data): creates a 2D profile of the given type, with the given - data as arguments, which must be formatted correctly according to the type.""" - - # Expected ifctype and corresponding data formatting: - # IfcPolyLine: [ (0,0,0), (2,1,0), (3,3,0) ] # list of points - # IfcCompositeCurve: [ ["line",[ (0,0,0), (2,1,0) ] ], # list of points - # ["arc", (0,0,0), 15, [0.76, 3.1416], True, "PARAMETER"] # center, radius, [trim1, trim2], SameSense, trimtype - # ... ] - # IfcCircleProfileDef: [ (0,0,0), 15 ] # center, radius - # IfcEllipseProfileDef: [ (0,0,0), 15, 7 ] # center, radiusX, radiusY - - if ifctype == "IfcPolyline": - pts = [create(self._fileobject,"IfcCartesianPoint",getTuple(p)[:2]) for p in data] - pol = create(self._fileobject,"IfcPolyline",[pts]) - profile = create(self._fileobject,"IfcArbitraryClosedProfileDef",[curvetype,None,pol]) - elif ifctype == "IfcCompositeCurve": - curves = [] - for curve in data: - cur = None - if curve[0] == "line": - pts = [create(self._fileobject,"IfcCartesianPoint",getTuple(p)[:2]) for p in curve[1]] - cur = create(self._fileobject,"IfcPolyline",[pts]) - elif curve[0] == "arc": - pla = self.addPlacement(origin=curve[1],local=False,flat=True) - cir = create(self._fileobject,"IfcCircle",[pla,curve[2]]) - if curve[5] == "CARTESIAN": - # BUGGY! Impossible to add cartesian points as "embedded" entity - trim1 = create(None,"IfcCartesianPoint",getTuple(curve[3][0])[:2]) - trim2 = create(None,"IfcCartesianPoint",getTuple(curve[3][1])[:2]) - else: - trim1 = create(None,"IfcParameterValue",[curve[3][0]]) - trim2 = create(None,"IfcParameterValue",[curve[3][1]]) - cur = create(self._fileobject,"IfcTrimmedCurve",[cir,[trim1],[trim2],curve[4],curve[5]]) - if cur: - seg = create(self._fileobject,"IfcCompositeCurveSegment",["CONTINUOUS",True,cur]) - curves.append(seg) - ccu = create(self._fileobject,"IfcCompositeCurve",[curves,False]) - profile = create(self._fileobject,"IfcArbitraryClosedProfileDef",[curvetype,None,ccu]) - else: - if not isinstance(data,list): - data = [data] - p = self.addPlacement(local=False,flat=True) - profile = create(self._fileobject,ifctype,[curvetype,None,p]+data) - return profile - - def addExtrusion(self,profile,extrusion,placement=None): - """addExtrusion(profile,extrusion,[placement]): makes an - extrusion of the given polyline with the given extrusion vector""" - if not placement: - placement = self.addPlacement(local=False) - value,norm = getValueAndDirection(extrusion) - edir = create(self._fileobject,"IfcDirection",[norm]) - solid = create(self._fileobject,"IfcExtrudedAreaSolid",[profile,placement,edir,value]) - return solid - - def addExtrudedPolyline(self,points,extrusion,placement=None,color=None): - """addExtrudedPolyline(points,extrusion,[placement,color]): makes an extruded polyline - from the given points and the given extrusion vector""" - pol = self.addProfile("IfcPolyline",points) - if not placement: - placement = self.addPlacement(local=False) - exp = self.addExtrusion(pol,extrusion,placement) - if color: - self.addColor(color,exp) - return exp - - def addExtrudedCircle(self,data,extrusion,placement=None,color=None): - """addExtrudedCircle(data,extrusion,[placement,color]): makes an extruded circle - from the given data (center,radius) and the given extrusion vector""" - cir = self.addProfile("IfcCircleProfileDef",data[1]) - if not placement: - placement = self.addPlacement(origin=data[0],local=False) - exp = self.addExtrusion(cir,extrusion,placement) - if color: - self.addColor(color,exp) - return exp - - def addExtrudedEllipse(self,data,extrusion,placement=None,color=None): - """addExtrudedEllipse(data,extrusion,[placement,color]): makes an extruded ellipse - from the given data (center,radiusx,radiusy) and the given extrusion vector""" - cir = self.addProfile("IfcEllipseProfileDef",[data[1],data[2]]) - if not placement: - placement = self.addPlacement(origin=data[0],local=False) - exp = self.addExtrusion(cir,extrusion,placement) - if color: - self.addColor(color,exp) - return exp - - def addExtrudedCompositeCurve(self,curves,extrusion,placement=None,color=None): - """addExtrudedCompositeCurve(curves,extrusion,[placement,color]): makes an extruded polyline - from the given curves and the given extrusion vector""" - if not placement: - placement = self.addPlacement(local=False) - ccu = self.addProfile("IfcCompositeCurve",curves) - exp = self.addExtrusion(ccu,extrusion,placement) - if color: - self.addColor(color,exp) - return exp - - def addFace(self,face): - """addFace(face): creates a face from the given face data (a list of lists of points). - The first is the outer wire, the next are optional inner wires. They must be reversed in order""" - ifb = [] - idx = 0 - for f in face: - pts = [] - for p in f: - #print p - if p in self.fpoints: - #print self.fpoints.index(p) - #print self.frefs - pts.append(self.frefs[self.fpoints.index(p)]) - else: - pt = create(self._fileobject,"IfcCartesianPoint",getTuple(p)) - pts.append(pt) - self.fpoints.append(p) - self.frefs.append(pt) - #print pts - loop = create(self._fileobject,"IfcPolyLoop",[pts]) - if idx == 0: - fb = create(self._fileobject,"IfcFaceOuterBound",[loop,True]) - else: - fb = create(self._fileobject,"IfcFaceBound",[loop,True]) - ifb.append(fb) - idx += 1 - iface = create(self._fileobject,"IfcFace",[ifb]) - return iface - - def addFacetedBrep(self,faces,color=None): - """addFacetedBrep(self,faces,[color]): creates a faceted brep object from the given list - of faces (each face is a list of lists of points, inner wires are reversed)""" - self.fpoints = [] - self.frefs = [] - #print "adding ",len(faces)," faces" - #print faces - ifaces = [self.addFace(face) for face in faces] - sh = create(self._fileobject,"IfcClosedShell",[ifaces]) - brp = create(self._fileobject,"IfcFacetedBrep",[sh]) - if color: - self.addColor(color,brp) - return brp - - -# EXAMPLE ################################################################# - -def example(): - - ifc = IfcDocument("/home/yorik/test2.ifc") - ifc.Name = "Test Project" - ifc.Owner = "Yorik van Havre" - ifc.Organization = "FreeCAD" - w1 = ifc.addProduct( "IfcWall", ifc.addExtrudedPolyline([(0,0,0),(0,200,0),(5000,200,0),(5000,0,0),(0,0,0)], (0,0,3500)) ) - ifc.addProduct( "IfcWall", ifc.addExtrudedPolyline([(0,200,0),(0,2000,0),(200,2000,0),(200,200,0),(0,200,0)],(0,0,3500)) ) - ifc.addProduct( "IfcWall", ifc.addExtrudedPolyline([(0,2000,0),(0,2200,0),(5000,2200,0),(5000,2000,0),(0,2000,0)],(0,0,3500)) ) - ifc.addProduct( "IfcWall", ifc.addExtrudedPolyline([(5000,200,0),(5000,2000,0),(4800,2000,0),(4800,200,0),(5000,200,0)],(0,0,3500)) ) - ifc.addProduct( "IfcWall", ifc.addFacetedBrep([[[(0,0,0),(100,0,0),(100,-1000,0),(0,-1000,0)]], - [[(0,0,0),(100,0,0),(100,0,1000),(0,0,1000)]], - [[(0,0,0),(0,0,1000),(0,-1000,1000),(0,-1000,0)]], - [[(0,-1000,0),(0,-1000,1000),(100,-1000,1000),(100,-1000,0)]], - [[(100,-1000,0),(100,-1000,1000),(100,0,1000),(100,0,0)]], - [[(0,0,1000),(0,-1000,1000),(100,-1000,1000),(100,0,1000)]]]) ) - ifc.addProduct( "IfcColumn", ifc.addExtrudedPolyline([(0,0,0),(0,-200,0),(-500,-200,0),(-500,0,0),(0,0,0)], (0,0,3500)) ) - ifc.addProduct( "IfcDoor", ifc.addExtrudedPolyline([(200,200,0),(200,400,0),(400,400,0),(400,200,0),(200,200,0)], (0,0,200)), w1, [200, 200] ) - ifc.write() - - print dir(ifc._fileobject) - print ifc._fileobject.by_type("IfcDoor") - w = ifc._fileobject.by_type("IfcDoor")[0] - print w - print dir(w) - print w.is_a("IfcDoor") - for i in range(w.get_argument_count()): - print i,": ",w.get_argument_name(i)," : ",w.get_argument(i) - diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 26dc8c0d83be..7c3131d7643d 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -1,6 +1,6 @@ #*************************************************************************** #* * -#* Copyright (c) 2011 * +#* Copyright (c) 2014 * #* Yorik van Havre * #* * #* This program is free software; you can redistribute it and/or modify * @@ -21,1370 +21,564 @@ #* * #*************************************************************************** -import ifcReader, FreeCAD, Arch, Draft, os, sys, time, Part, DraftVecUtils -from DraftTools import translate - -__title__="FreeCAD IFC importer" +__title__= "FreeCAD IFC importer - Enhanced ifcopenshell-only version" __author__ = "Yorik van Havre" -__url__ = "http://www.freecadweb.org" - -# config -subtractiveTypes = ["IfcOpeningElement"] # elements that must be subtracted from their parents -SCHEMA = "http://www.steptools.com/support/stdev_docs/ifcbim/ifc4.exp" # only for internal prser -MAKETEMPFILES = False # if True, shapes are passed from ifcopenshell to freecad through temp files -DEBUG = True # this is only for the python console, this value is overridden when importing through the GUI -SKIP = ["IfcBuildingElementProxy","IfcFlowTerminal","IfcFurnishingElement"] # default. overwritten by the GUI options -# end config +__url__ = "http://www.freecadweb.org" -# supported ifc products (export only): -supportedIfcTypes = ["IfcSite", "IfcBuilding", "IfcBuildingStorey", "IfcBeam", "IfcBeamStandardCase", - "IfcChimney", "IfcColumn", "IfcColumnStandardCase", "IfcCovering", "IfcCurtainWall", - "IfcDoor", "IfcDoorStandardCase", "IfcMember", "IfcMemberStandardCase", "IfcPlate", - "IfcPlateStandardCase", "IfcRailing", "IfcRamp", "IfcRampFlight", "IfcRoof", - "IfcSlab", "IfcStair", "IfcStairFlight", "IfcWall","IfcSpace", - "IfcWallStandardCase", "IfcWindow", "IfcWindowStandardCase", "IfcBuildingElementProxy", - "IfcPile", "IfcFooting", "IfcReinforcingBar", "IfcTendon"] -# TODO : shading device not supported? +import os,time,tempfile,uuid,FreeCAD,Part,Draft,Arch if open.__module__ == '__builtin__': pyopen = open # because we'll redefine open below -def open(filename,skip=None): - "called when freecad opens a file" - docname = os.path.splitext(os.path.basename(filename))[0] - doc = FreeCAD.newDocument(docname) - doc.Label = decode(docname) - FreeCAD.ActiveDocument = doc - getConfig() - read(filename,skip) - return doc +typesmap = { "Site": ["IfcSite"], + "Building": ["IfcBuilding"], + "Floor": ["IfcBuildingStorey"], + "Structure": ["IfcBeam", "IfcBeamStandardCase", "IfcColumn", "IfcColumnStandardCase", "IfcSlab", "IfcFooting", "IfcPile", "IfcTendon"], + "Wall": ["IfcWall", "IfcWallStandardCase", "IfcCurtainWall"], + "Window": ["IfcWindow", "IfcWindowStandardCase", "IfcDoor", "IfcDoorStandardCase"], + "Roof": ["IfcRoof"], + "Stairs": ["IfcStair", "IfcStairFlight", "IfcRamp", "IfcRampFlight"], + "Space": ["IfcSpace"], + "Rebar": ["IfcReinforcingBar"] + } + +ifctemplate = """ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1'); +FILE_NAME('$filename','$timestamp',('$owner','$email'),('$company'),'IfcOpenShell','IfcOpenShell',''); +FILE_SCHEMA(('IFC2X3')); +ENDSEC; +DATA; +#1=IFCPERSON($,$,'$owner',$,$,$,$,$); +#2=IFCORGANIZATION($,'$company',$,$,$); +#3=IFCPERSONANDORGANIZATION(#1,#2,$); +#4=IFCAPPLICATION(#2,'$version','FreeCAD','118df2cf_ed21_438e_a41'); +#5=IFCOWNERHISTORY(#3,#4,$,.ADDED.,$,#3,#4,$now); +#6=IFCDIRECTION((1.,0.,0.)); +#7=IFCDIRECTION((0.,0.,1.)); +#8=IFCCARTESIANPOINT((0.,0.,0.)); +#9=IFCAXIS2PLACEMENT3D(#8,#7,#6); +#10=IFCDIRECTION((0.,1.,0.)); +#11=IFCGEOMETRICREPRESENTATIONCONTEXT('Plan','Model',3,1.E-05,#9,#10); +#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0); +#13=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.); +#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.); +#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.); +#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.); +#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.01745),#16); +#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17); +#19=IFCUNITASSIGNMENT((#13,#14,#15,#18)); +#20=IFCPROJECT('$projectid',#5,'$project',$,$,$,$,(#11),#19); +ENDSEC; +END-ISO-10303-21; +""" + +ifctypes = ["IfcSite", "IfcBuilding", "IfcBuildingStorey", "IfcBeam", "IfcBeamStandardCase", + "IfcChimney", "IfcColumn", "IfcColumnStandardCase", "IfcCovering", "IfcCurtainWall", + "IfcDoor", "IfcDoorStandardCase", "IfcMember", "IfcMemberStandardCase", "IfcPlate", + "IfcPlateStandardCase", "IfcRailing", "IfcRamp", "IfcRampFlight", "IfcRoof", + "IfcSlab", "IfcStair", "IfcStairFlight", "IfcWall","IfcSpace", + "IfcWallStandardCase", "IfcWindow", "IfcWindowStandardCase", "IfcBuildingElementProxy", + "IfcPile", "IfcFooting", "IfcReinforcingBar", "IfcTendon", "IfcGroup"] -def insert(filename,docname,skip=None): - "called when freecad wants to import a file" - try: - doc = FreeCAD.getDocument(docname) - except: - doc = FreeCAD.newDocument(docname) - FreeCAD.ActiveDocument = doc - getConfig() - read(filename,skip) - return doc - -def getConfig(): - "Gets Arch IFC import preferences" - global SKIP, CREATE_IFC_GROUPS, ASMESH, PREFIX_NUMBERS, FORCE_PYTHON_PARSER, SEPARATE_OPENINGS, SEPARATE_PLACEMENTS, JOINSOLIDS, AGGREGATE_WINDOWS - IMPORT_IFC_FURNITURE = False - ASMESH = ["IfcFurnishingElement"] - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - CREATE_IFC_GROUPS = p.GetBool("createIfcGroups",False) - FORCE_PYTHON_PARSER = p.GetBool("forceIfcPythonParser",False) - DEBUG = p.GetBool("ifcDebug",False) - SEPARATE_OPENINGS = p.GetBool("ifcSeparateOpenings",False) - SEPARATE_PLACEMENTS = p.GetBool("ifcSeparatePlacements",False) - PREFIX_NUMBERS = p.GetBool("ifcPrefixNumbers",False) - JOINSOLIDS = p.GetBool("ifcJoinSolids",False) - AGGREGATE_WINDOWS = p.GetBool("ifcAggregateWindows",False) - skiplist = p.GetString("ifcSkip","") - if skiplist: - SKIP = skiplist.split(",") - asmeshlist = p.GetString("ifcAsMesh","") - if asmeshlist: - ASMESH = asmeshlist.split(",") - -def getIfcOpenShell(): - "locates and imports ifcopenshell" - global IFCOPENSHELL5 - global IfcImport - IFCOPENSHELL5 = False - try: - import IfcImport - except: - try: - import ifc_wrapper as IfcImport - except: - FreeCAD.Console.PrintMessage(translate("Arch","Couldn't locate IfcOpenShell\n")) - return False - else: - IFCOPENSHELL5 = True - return True - else: - if hasattr(IfcImport,"IfcFile"): - IFCOPENSHELL5 = True - return True - -def read(filename,skip=None): - "Parses an IFC file" - # parsing the IFC file - t1 = time.time() - - processedIds = [] - skipIds = skip - if not skipIds: - skipIds = [] - elif isinstance(skipIds,int): - skipIds = [skipIds] +def explore(filename=None): + "opens a dialog showing the contents of an IFC file" - if getIfcOpenShell() and not FORCE_PYTHON_PARSER: - # use the IfcOpenShell parser - - # preparing IfcOpenShell - if DEBUG: global ifcObjects,ifcParents - ifcObjects = {} # a table to relate ifc id with freecad object - ifcParents = {} # a table to relate ifc id with parent id - if SEPARATE_OPENINGS: - if not IFCOPENSHELL5: - if hasattr(IfcImport,"DISABLE_OPENING_SUBTRACTIONS"): - IfcImport.Settings(IfcImport.DISABLE_OPENING_SUBTRACTIONS,True) - else: - SKIP.append("IfcOpeningElement") - useShapes = False - if IFCOPENSHELL5: - useShapes = True - if hasattr(IfcImport,"clean"): - IfcImport.clean() - elif hasattr(IfcImport,"USE_BREP_DATA"): - IfcImport.Settings(IfcImport.USE_BREP_DATA,True) - useShapes = True - else: - if DEBUG: print "Warning: IfcOpenShell version very old, unable to handle Brep data" - - # opening file - if IFCOPENSHELL5: - global ifc - ifc = IfcImport.open(filename) - objects = ifc.by_type("IfcProduct") - num_lines = len(objects) - relations = ifc.by_type("IfcRelAggregates") + ifc.by_type("IfcRelContainedInSpatialStructure") + ifc.by_type("IfcRelVoidsElement") - if not objects: - print "Error opening IFC file" - return - else: - num_lines = sum(1 for line in pyopen(filename)) - if not IfcImport.Init(filename): - print "Error opening IFC file" - return - - # processing geometry - idx = 0 - while True: - objparentid = [] - if IFCOPENSHELL5: - obj = objects[idx] - idx += 1 - objid = int(str(obj).split("=")[0].strip("#")) - objname = obj.get_argument(obj.get_argument_index("Name")) - objtype = str(obj).split("=")[1].split("(")[0] - for r in relations: - if r.is_a("IfcRelAggregates"): - for c in getAttr(r,"RelatedObjects"): - if str(obj) == str(c): - objparentid.append(int(str(getAttr(r,"RelatingObject")).split("=")[0].strip("#"))) - elif r.is_a("IfcRelContainedInSpatialStructure"): - for c in getAttr(r,"RelatedElements"): - if str(obj) == str(c): - objparentid.append(int(str(getAttr(r,"RelatingStructure")).split("=")[0].strip("#"))) - elif r.is_a("IfcRelVoidsElement"): - if str(obj) == str(getAttr(r,"RelatedOpeningElement")): - objparentid.append(int(str(getAttr(r,"RelatingBuildingElement")).split("=")[0].strip("#"))) - - else: - if hasattr(IfcImport, 'GetBrepData'): - obj = IfcImport.GetBrepData() - else: - obj = IfcImport.Get() - objid = obj.id - idx = objid - objname = obj.name - objtype = obj.type - objparentid.append(obj.parent_id) - if DEBUG: print "["+str(int((float(idx)/num_lines)*100))+"%] parsing ",objid,": ",objname," of type ",objtype - - # retrieving name - n = getCleanName(objname,objid,objtype) - - # skip IDs - if objid in skipIds: - if DEBUG: print " skipping because object ID is in skip list" - nobj = None - - # skip types - elif objtype in SKIP: - if DEBUG: print " skipping because type is in skip list" - nobj = None - - # check if object was already processed, to workaround an ifcopenshell bug - elif objid in processedIds: - if DEBUG: print " skipping because this object was already processed" - - else: - # build shape - shape = None - if useShapes: - shape = getShape(obj,objid) - - # walls - if objtype in ["IfcWallStandardCase","IfcWall"]: - nobj = makeWall(objid,shape,n) - - # windows - elif objtype in ["IfcWindow","IfcDoor"]: - nobj = makeWindow(objid,shape,n) - - # structs - elif objtype in ["IfcBeam","IfcColumn","IfcSlab","IfcFooting"]: - nobj = makeStructure(objid,shape,objtype,n) - - # roofs - elif objtype in ["IfcRoof"]: - nobj = makeRoof(objid,shape,n) - - # furniture - elif objtype in ["IfcFurnishingElement"]: - nobj = FreeCAD.ActiveDocument.addObject("Part::Feature",n) - nobj.Shape = shape - - # sites - elif objtype in ["IfcSite"]: - nobj = makeSite(objid,shape,n) - - # floors - elif objtype in ["IfcBuildingStorey"]: - nobj = Arch.makeFloor(name=n) - nobj.Label = n - - # floors - elif objtype in ["IfcBuilding"]: - nobj = Arch.makeBuilding(name=n) - nobj.Label = n - - # spaces - elif objtype in ["IfcSpace"]: - nobj = makeSpace(objid,shape,n) - - elif shape: - # treat as dumb parts - if DEBUG: print "Fixme: Shape-containing object not handled: ",objid, " ", objtype - nobj = FreeCAD.ActiveDocument.addObject("Part::Feature",n) - nobj.Label = n - nobj.Shape = shape - - else: - # treat as meshes - if DEBUG: print "Warning: Object without shape: ",objid, " ", objtype - if hasattr(obj,"mesh"): - if not hasattr(obj.mesh, 'verts'): - obj = IfcImport.Get() # Get triangulated rep of same product - me,pl = getMesh(obj) - nobj = FreeCAD.ActiveDocument.addObject("Mesh::Feature",n) - nobj.Label = n - nobj.Mesh = me - nobj.Placement = pl - else: - if DEBUG: print "Error: Skipping object without mesh: ",objid, " ", objtype - - # registering object number and parent - if objparentid: - ifcParents[objid] = [] - for p in objparentid: - ifcParents[objid].append([p,not (objtype in subtractiveTypes)]) - ifcObjects[objid] = nobj - processedIds.append(objid) - - if IFCOPENSHELL5: - if idx >= len(objects): - break - else: - if not IfcImport.Next(): - break - - - # processing non-geometry and relationships - parents_temp = dict(ifcParents) - import ArchCommands - #print parents_temp - - while parents_temp: - id, comps = parents_temp.popitem() - for c in comps: - parent_id = c[0] - additive = c[1] - - if (id <= 0) or (parent_id <= 0): - # root dummy object - parent = None + if not filename: + from PySide import QtGui + filename = QtGui.QFileDialog.getOpenFileName(QtGui.qApp.activeWindow(),'IFC files','*.ifc') + if filename: + filename = filename[0] + if filename: + import importIFClegacy + importIFClegacy.getConfig() + schema=importIFClegacy.getSchema() + importIFClegacy.DEBUG = DEBUG + d = importIFClegacy.explorer(filename,schema) + d.show() + return d + return - elif parent_id in ifcObjects: - parent = ifcObjects[parent_id] - # check if parent is a subtraction, if yes parent to grandparent - if parent_id in ifcParents: - for p in ifcParents[parent_id]: - if p[1] == False: - grandparent_id = p[0] - if grandparent_id in ifcObjects: - parent = ifcObjects[grandparent_id] - else: - # creating parent if needed - if IFCOPENSHELL5: - obj = ifc.by_id(parent_id) - parentid = int(str(obj).split("=")[0].strip("#")) - parentname = obj.get_argument(obj.get_argument_index("Name")) - parenttype = str(obj).split("=")[1].split("(")[0] - else: - obj = IfcImport.GetObject(parent_id) - parentid = obj.id - parentname = obj.name - parenttype = obj.type - #if DEBUG: print "["+str(int((float(idx)/num_lines)*100))+"%] parsing ",parentid,": ",parentname," of type ",parenttype - n = getCleanName(parentname,parentid,parenttype) - if parentid <= 0: - parent = None - elif parenttype == "IfcBuildingStorey": - parent = Arch.makeFloor(name=n) - parent.Label = n - elif parenttype == "IfcBuilding": - parent = Arch.makeBuilding(name=n) - parent.Label = n - elif parenttype == "IfcSite": - parent = Arch.makeSite(name=n) - parent.Label = n - elif parenttype == "IfcWindow": - parent = Arch.makeWindow(name=n) - parent.Label = n - elif parenttype == "IfcProject": - parent = None - else: - if DEBUG: print "Fixme: skipping unhandled parent: ", parentid, " ", parenttype - parent = None - # registering object number and parent - if not IFCOPENSHELL5: - if parent_ifcobj.parent_id > 0: - ifcParents[parentid] = [parent_ifcobj.parent_id,True] - parents_temp[parentid] = [parent_ifcobj.parent_id,True] - if parent and (not parentid in ifcObjects): - ifcObjects[parentid] = parent - - # attributing parent - if parent and (id in ifcObjects): - if ifcObjects[id] and (ifcObjects[id].Name != parent.Name): - if additive: - if DEBUG: print "adding ",ifcObjects[id].Name, " to ",parent.Name - ArchCommands.addComponents(ifcObjects[id],parent) - else: - if DEBUG: print "removing ",ifcObjects[id].Name, " from ",parent.Name - ArchCommands.removeComponents(ifcObjects[id],parent) - if not IFCOPENSHELL5: - IfcImport.CleanUp() - - else: - # use only the internal python parser - - FreeCAD.Console.PrintWarning(translate("Arch","IfcOpenShell not found or disabled, falling back on internal parser.\n")) - schema=getSchema() - if schema: - if DEBUG: print "opening",filename,"..." - ifcReader.DEBUG = DEBUG - ifc = ifcReader.IfcDocument(filename,schema=schema) - else: - FreeCAD.Console.PrintWarning(translate("Arch","IFC Schema not found, IFC import disabled.\n")) - return None - t2 = time.time() - if DEBUG: print "Successfully loaded",ifc,"in %s s" % ((t2-t1)) - - # getting walls - for w in ifc.getEnt("IfcWallStandardCase"): - nobj = makeWall(w) - - # getting windows and doors - for w in (ifc.getEnt("IfcWindow") + ifc.getEnt("IfcDoor")): - nobj = makeWindow(w) - - # getting structs - for w in (ifc.getEnt("IfcSlab") + ifc.getEnt("IfcBeam") + ifc.getEnt("IfcColumn") \ - + ifc.getEnt("IfcFooting")): - nobj = makeStructure(w) - - # getting floors - for f in ifc.getEnt("IfcBuildingStorey"): - group(f,ifc,"Floor") - - # getting buildings - for b in ifc.getEnt("IfcBuilding"): - group(b,ifc,"Building") - - # getting sites - for s in ifc.getEnt("IfcSite"): - group(s,ifc,"Site") - if DEBUG: print "done parsing. Recomputing..." - FreeCAD.ActiveDocument.recompute() - t3 = time.time() - if DEBUG: print "done processing IFC file in %s s" % ((t3-t1)) +def open(filename,skip=[]): + "opens an IFC file in a new document" - return None - - -def getCleanName(name,ifcid,ifctype): - "Get a clean name from an ifc object" - #print "getCleanName called",name,ifcid,ifctype - n = name - if not n: - n = ifctype - if PREFIX_NUMBERS: - n = "ID"+str(ifcid)+" "+n - #for c in ",.!?;:": - # n = n.replace(c,"_") - return n - - -def makeWall(entity,shape=None,name="Wall"): - "makes a wall in the freecad document" - try: - if shape: - # use ifcopenshell - if isinstance(shape,Part.Shape): - body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") - body.Shape = shape - else: - body = FreeCAD.ActiveDocument.addObject("Mesh::Feature",name+"_body") - body.Mesh = shape - wall = Arch.makeWall(body,name=name) - wall.Label = name - if DEBUG: print " made wall object ",entity,":",wall - return wall - - # use internal parser - if DEBUG: print "=====> making wall",entity.id - placement = wall = wire = body = width = height = None - placement = getPlacement(entity.ObjectPlacement) - if DEBUG: print " got wall placement",entity.id,":",placement - width = entity.getProperty("Width") - height = entity.getProperty("Height") - if width and height: - if DEBUG: print " got width, height ",entity.id,":",width,"/",height - for r in entity.Representation.Representations: - if r.RepresentationIdentifier == "Axis": - wire = getWire(r.Items,placement) - wall = Arch.makeWall(wire,width,height,align="Center",name="Wall"+str(entity.id)) - else: - if DEBUG: print " no height or width properties found..." - for r in entity.Representation.Representations: - if r.RepresentationIdentifier == "Body": - for b in r.Items: - if b.type == "IFCEXTRUDEDAREASOLID": - norm = getVector(b.ExtrudedDirection) - norm.normalize() - wire = getWire(b.SweptArea,placement) - wall = Arch.makeWall(wire,width=0,height=b.Depth,name="Wall"+str(entity.id)) - wall.Normal = norm - if wall: - if DEBUG: print " made wall object ",entity.id,":",wall - return wall - if DEBUG: print " error: skipping wall",entity.id - return None - except: - if DEBUG: print " error: skipping wall",entity - return None - - -def makeWindow(entity,shape=None,name="Window"): - "makes a window in the freecad document" - try: - if shape: - # use ifcopenshell - if isinstance(shape,Part.Shape): - window = Arch.makeWindow(name=name) - window.Shape = shape - window.Label = name - if DEBUG: print " made window object ",entity,":",window - return window - - # use internal parser - if DEBUG: print "=====> making window",entity.id - placement = window = wire = body = width = height = None - placement = getPlacement(entity.ObjectPlacement) - if DEBUG: print "got window placement",entity.id,":",placement - width = entity.getProperty("Width") - height = entity.getProperty("Height") - for r in entity.Representation.Representations: - if r.RepresentationIdentifier == "Body": - for b in r.Items: - if b.type == "IFCEXTRUDEDAREASOLID": - wire = getWire(b.SweptArea,placement) - window = Arch.makeWindow(wire,width=b.Depth,name=objtype+str(entity.id)) - if window: - if DEBUG: print " made window object ",entity.id,":",window - return window - if DEBUG: print " error: skipping window",entity.id - return None - except: - if DEBUG: print " error: skipping window",entity - return None - - -def makeStructure(entity,shape=None,ifctype=None,name="Structure"): - "makes a structure in the freecad document" - try: - if shape: - # use ifcopenshell - if isinstance(shape,Part.Shape): - body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") - body.Shape = shape - else: - body = FreeCAD.ActiveDocument.addObject("Mesh::Feature",name+"_body") - body.Mesh = shape - structure = Arch.makeStructure(body,name=name) - structure.Label = name - if ifctype == "IfcBeam": - structure.Role = "Beam" - elif ifctype == "IfcColumn": - structure.Role = "Column" - elif ifctype == "IfcSlab": - structure.Role = "Slab" - elif ifctype == "IfcFooting": - structure.Role = "Foundation" - if DEBUG: print " made structure object ",entity,":",structure," (type: ",ifctype,")" - return structure - - # use internal parser - if DEBUG: print "=====> making struct",entity.id - placement = structure = wire = body = width = height = None - placement = getPlacement(entity.ObjectPlacement) - if DEBUG: print "got window placement",entity.id,":",placement - width = entity.getProperty("Width") - height = entity.getProperty("Height") - for r in entity.Representation.Representations: - if r.RepresentationIdentifier == "Body": - for b in r.Items: - if b.type == "IFCEXTRUDEDAREASOLID": - wire = getWire(b.SweptArea,placement) - structure = Arch.makeStructure(wire,height=b.Depth,name=objtype+str(entity.id)) - if structure: - if DEBUG: print " made structure object ",entity.id,":",structure - return structure - if DEBUG: print " error: skipping structure",entity.id - return None - except: - if DEBUG: print " error: skipping structure",entity - return None + docname = os.path.splitext(os.path.basename(filename))[0] + doc = FreeCAD.newDocument(docname) + doc.Label = docname + doc = insert(filename,doc.Name,skip) + return doc -def makeSite(entity,shape=None,name="Site"): - "makes a site in the freecad document" - try: - body = None - if shape: - # use ifcopenshell - if isinstance(shape,Part.Shape): - body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") - body.Shape = shape - else: - body = FreeCAD.ActiveDocument.addObject("Mesh::Feature",name+"_body") - body.Mesh = shape - site = Arch.makeSite(name=name) - site.Label = name - if body: - site.Terrain = body - if DEBUG: print " made site object ",entity,":",site - return site - except: - return None - -def makeSpace(entity,shape=None,name="Space"): - "makes a space in the freecad document" +def insert(filename,docname,skip=[]): + "imports the contents of an IFC file" + try: - if shape: - # use ifcopenshell - if isinstance(shape,Part.Shape): - space = Arch.makeSpace(name=name) - space.Label = name - body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") - body.Shape = shape - space.Base = body - body.ViewObject.hide() - if DEBUG: print " made space object ",entity,":",space - return space + import ifcopenshell except: - return None + if DEBUG: print "using legacy importer" + import importIFClegacy + return importIFClegacy.insert(filename,docname,skip) + p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") + DEBUG = p.GetBool("ifcDebug",False) + PREFIX_NUMBERS = p.GetBool("ifcPrefixNumbers",False) + SKIP = p.GetString("ifcSkip","") + SEPARATE_OPENINGS = p.GetBool("ifcSeparateOpenings",False) -def makeRoof(entity,shape=None,name="Roof"): - "makes a roof in the freecad document" + if DEBUG: print "opening ",filename,"..." try: - if shape: - # use ifcopenshell - if isinstance(shape,Part.Shape): - roof = Arch.makeRoof(name=name) - roof.Label = name - roof.Shape = shape - if DEBUG: print " made roof object ",entity,":",roof - return roof + doc = FreeCAD.getDocument(docname) except: - return None - -# geometry helpers ################################################################### - -def getMesh(obj): - "gets mesh and placement from an IfcOpenShell object" - if IFCOPENSHELL5: - return None,None - print "fixme: mesh data not yet supported" # TODO implement this with OCC tessellate - import Mesh - meshdata = [] - print obj.mesh.faces - print obj.mesh.verts - f = obj.mesh.faces - v = obj.mesh.verts - for i in range(0, len(f), 3): - face = [] - for j in range(3): - vi = f[i+j]*3 - face.append([v[vi],v[vi+1],v[vi+2]]) - meshdata.append(face) - print meshdata - me = Mesh.Mesh(meshdata) - # get transformation matrix - m = obj.matrix - mat = FreeCAD.Matrix(m[0], m[3], m[6], m[9], - m[1], m[4], m[7], m[10], - m[2], m[5], m[8], m[11], - 0, 0, 0, 1) - pl = FreeCAD.Placement(mat) - return me,pl - -def getShape(obj,objid): - "gets a shape from an IfcOpenShell object" - #print "retrieving shape from obj ",objid - import Part - sh=Part.Shape() - brep_data = None - if IFCOPENSHELL5: - try: - if hasattr(IfcImport,"SEW_SHELLS"): - ss = IfcImport.SEW_SHELLS - else: - ss = 0 - if SEPARATE_OPENINGS and hasattr(IfcImport,"DISABLE_OPENING_SUBTRACTIONS"): - if SEPARATE_PLACEMENTS and hasattr(IfcImport,"DISABLE_OBJECT_PLACEMENT"): - brep_data = IfcImport.create_shape(obj,IfcImport.DISABLE_OPENING_SUBTRACTIONS | IfcImport.DISABLE_OBJECT_PLACEMENT | ss) - else: - brep_data = IfcImport.create_shape(obj,IfcImport.DISABLE_OPENING_SUBTRACTIONS | ss) - else: - if SEPARATE_PLACEMENTS and hasattr(IfcImport,"DISABLE_OBJECT_PLACEMENT"): - brep_data = IfcImport.create_shape(obj,IfcImport.DISABLE_OBJECT_PLACEMENT | ss) - else: - brep_data = IfcImport.create_shape(obj, ss) - except: - print "Unable to retrieve shape data" - else: - brep_data = obj.mesh.brep_data - if brep_data: - try: - if MAKETEMPFILES: - import tempfile - tf = tempfile.mkstemp(suffix=".brp")[1] - of = pyopen(tf,"wb") - of.write(brep_data) - of.close() - sh = Part.read(tf) - os.remove(tf) - else: - sh.importBrepFromString(brep_data) - except: - print " error: malformed shape" - return None - else: - if IFCOPENSHELL5 and SEPARATE_PLACEMENTS: - p = getPlacement(getAttr(obj,"ObjectPlacement")) - if p: - sh.Placement = p - if not sh.Solids: - # try to extract a solid shape - if sh.Faces: - try: - if DEBUG: print " malformed solid. Attempting to fix..." - shell = Part.makeShell(sh.Faces) - if shell: - solid = Part.makeSolid(shell) - if solid: - sh = solid - except: - if DEBUG: print " failed to retrieve solid from object ",objid - else: - if DEBUG: print " object ", objid, " doesn't contain any geometry" - if not IFCOPENSHELL5: - m = obj.matrix - mat = FreeCAD.Matrix(m[0], m[3], m[6], m[9], - m[1], m[4], m[7], m[10], - m[2], m[5], m[8], m[11], - 0, 0, 0, 1) - sh.Placement = FreeCAD.Placement(mat) - # if DEBUG: print "getting Shape from ",obj - #print "getting shape: ",sh,sh.Solids,sh.Volume,sh.isValid(),sh.isNull() - #for v in sh.Vertexes: print v.Point - if sh: - if not sh.isNull(): - return sh - return None - -def getPlacement(entity): - "returns a placement from the given entity" - if not entity: - return None - if DEBUG: print " getting placement ",entity - if IFCOPENSHELL5: - if isinstance(entity,int): - entity = ifc.by_id(entity) - entitytype = str(entity).split("=")[1].split("(")[0].upper() - entityid = int(str(entity).split("=")[0].strip("#")) - else: - entitytype = entity.type.upper() - entityid = entity.id - pl = None - if entitytype == "IFCAXIS2PLACEMENT3D": - x = getVector(getAttr(entity,"RefDirection")) - z = getVector(getAttr(entity,"Axis")) - if not(x) or not(z): - return None - y = z.cross(x) - loc = getVector(getAttr(entity,"Location")) - m = DraftVecUtils.getPlaneRotation(x,y,z) - pl = FreeCAD.Placement(m) - pl.move(loc) - elif entitytype == "IFCLOCALPLACEMENT": - pl = getPlacement(getAttr(entity,"PlacementRelTo")) - relpl = getPlacement(getAttr(entity,"RelativePlacement")) - if pl and relpl: - pl = relpl.multiply(pl) - elif relpl: - pl = relpl - elif entitytype == "IFCCARTESIANPOINT": - loc = getVector(entity) - pl = FreeCAD.Placement() - pl.move(loc) - if DEBUG: print " made placement for ",entityid,":",pl - return pl - -def getAttr(entity,attr): - "returns the given attribute from the given entity" - if IFCOPENSHELL5: - if isinstance(entity,int): - entity = ifc.by_id(entity) - i = entity.get_argument_index(attr) - return entity.get_argument(i) - else: - return getattr(entity,attr) - -def getVector(entity): - "returns a vector from the given entity" - if not entity: - return None - if DEBUG: print " getting point from ",entity - if IFCOPENSHELL5: - if isinstance(entity,int): - entity = ifc.by_id(entity) - entitytype = str(entity).split("=")[1].split("(")[0].upper() - else: - entitytype = entity.type.upper() - if entitytype == "IFCDIRECTION": - DirectionRatios = getAttr(entity,"DirectionRatios") - if len(DirectionRatios) == 3: - return FreeCAD.Vector(tuple(DirectionRatios)) - else: - return FreeCAD.Vector(tuple(DirectionRatios+[0])) - elif entitytype == "IFCCARTESIANPOINT": - Coordinates = getAttr(entity,"Coordinates") - if len(Coordinates) == 3: - return FreeCAD.Vector(tuple(Coordinates)) - else: - return FreeCAD.Vector(tuple(Coordinates+[0])) - return None - -# below is only used by the internal parser ######################################### - -def decode(name): - "decodes encoded strings" - try: - decodedName = (name.decode("utf8")) - except UnicodeDecodeError: - try: - decodedName = (name.decode("latin1")) - except UnicodeDecodeError: - FreeCAD.Console.PrintError(translate("Arch", "Error: Couldn't determine character encoding\n")) - decodedName = name - return decodedName - -def getSchema(): - "retrieves the express schema" - custom = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetString("CustomIfcSchema","") - if custom: - if os.path.exists(custom): - if DEBUG: print "Using custom schema: ",custom.split(os.sep)[-1] - return custom - p = None - p = os.path.join(FreeCAD.ConfigGet("UserAppData"),SCHEMA.split(os.sep)[-1]) - if os.path.exists(p): - return p - import ArchCommands - p = ArchCommands.download(SCHEMA) - if p: - return p - return None - -def group(entity,ifc,mode=None): - "gathers the children of the given entity" - # only used by the internal parser + doc = FreeCAD.newDocument(docname) + FreeCAD.ActiveDocument = doc - try: - if DEBUG: print "=====> making group",entity.id - placement = None - placement = getPlacement(entity.ObjectPlacement) - if DEBUG: print "got cell placement",entity.id,":",placement - subelements = ifc.find("IFCRELCONTAINEDINSPATIALSTRUCTURE","RelatingStructure",entity) - subelements.extend(ifc.find("IFCRELAGGREGATES","RelatingObject",entity)) - elts = [] - for s in subelements: - if hasattr(s,"RelatedElements"): - s = s.RelatedElements - if not isinstance(s,list): s = [s] - elts.extend(s) - elif hasattr(s,"RelatedObjects"): - s = s.RelatedObjects - if not isinstance(s,list): s = [s] - elts.extend(s) - elif hasattr(s,"RelatedObject"): - s = s.RelatedObject - if not isinstance(s,list): s = [s] - elts.extend(s) - print "found dependent elements: ",elts + global ifcfile # keeping global for debugging purposes + ifcopenshell.clean() + ifcfile = ifcopenshell.open(filename) + shape_attributes = ifcopenshell.SEW_SHELLS + if SEPARATE_OPENINGS: shape_attributes += ifcopenshell.DISABLE_OPENING_SUBTRACTIONS + sites = ifcfile.by_type("IfcSite") + buildings = ifcfile.by_type("IfcBuilding") + floors = ifcfile.by_type("IfcBuildingStorey") + products = ifcfile.by_type("IfcProduct") + openings = ifcfile.by_type("IfcOpeningElement") + + # building relations tables + objects = {} # { id:object, ... } + additions = {} # { host:[child,...], ... } + subtractions = [] # [ [opening,host], ... ] + 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"): + additions.setdefault(r.RelatingObject.id(),[]).extend([e.id() for e in r.RelatedObjects]) + for r in ifcfile.by_type("IfcRelVoidsElement"): + subtractions.append([r.RelatedOpeningElement.id(), r.RelatingBuildingElement.id()]) + + # products + for product in products: + pid = product.id() + guid = product.GlobalId + ptype = product.is_a() + name = product.Name or str(ptype[3:]) + if PREFIX_NUMBERS: name = "ID" + str(pid) + " " + name + obj = None + baseobj = None - groups = [['Wall',['IfcWallStandardCase'],[]], - ['Window',['IfcWindow','IfcDoor'],[]], - ['Structure',['IfcSlab','IfcFooting','IfcBeam','IfcColumn'],[]], - ['Floor',['IfcBuildingStorey'],[]], - ['Building',['IfcBuilding'],[]], - ['Furniture',['IfcFurnishingElement'],[]]] + if (ptype == "IfcOpeningElement") and (not SEPARATE_OPENINGS): break + if pid in skip: break + if ptype in SKIP: break - for e in elts: - for g in groups: - for t in g[1]: - if e.type.upper() == t.upper(): - if hasattr(FreeCAD.ActiveDocument,g[0]+str(e.id)): - g[2].append(FreeCAD.ActiveDocument.getObject(g[0]+str(e.id))) - print "groups:",groups - - comps = [] - if CREATE_IFC_GROUPS: - if DEBUG: print "creating subgroups" - for g in groups: - if g[2]: - if g[0] in ['Building','Floor']: - comps.extend(g[2]) - else: - fcg = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup",g[0]+"s") - for o in g[2]: - fcg.addObject(o) - comps.append(fcg) - else: - for g in groups: - comps.extend(g[2]) + brep = ifcopenshell.create_shape(product,shape_attributes) + if brep: + shape = Part.Shape() + shape.importBrepFromString(brep) + 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) + obj.Label = name + # setting uid + if hasattr(obj,"IfcAttributes"): + a = obj.IfcAttributes + a["IfcUID"] = str(guid) + obj.IfcAttributes = a + break + if not obj: + obj = baseobj + if obj: + if DEBUG: print "creating object ",pid," : ",ptype, " with shape: ",baseobj + objects[pid] = obj + + # subtractions + if SEPARATE_OPENINGS: + for subtraction in subtractions: + if (subtraction[0] in objects.keys()) and (subtraction[1] in objects.keys()): + 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]) - label = entity.Name - name = mode + str(entity.id) - cell = None - if mode == "Site": - cell = Arch.makeSite(comps,name=name) - elif mode == "Floor": - cell = Arch.makeFloor(comps,name=name) - elif mode == "Building": - cell = Arch.makeBuilding(comps,name=name) - if label and cell: - cell.Label = label - except: - if DEBUG: print "error: skipping group ",entity.id - -def getWire(entity,placement=None): - "returns a wire (created in the freecad document) from the given entity" - # only used by the internal parser - if DEBUG: print "making Wire from :",entity - if not entity: return None - if entity.type == "IFCPOLYLINE": - pts = [] - for p in entity.Points: - pts.append(getVector(p)) - return Draft.getWire(pts,placement=placement) - elif entity.type == "IFCARBITRARYCLOSEDPROFILEDEF": - pts = [] - for p in entity.OuterCurve.Points: - pts.append(getVector(p)) - return Draft.getWire(pts,closed=True,placement=placement) + FreeCAD.ActiveDocument.recompute() + if FreeCAD.GuiUp: + import FreeCADGui + FreeCADGui.SendMsgToActiveView("ViewFit") + return doc - -# EXPORT ########################################################## def export(exportList,filename): - "called when freecad exports a file" + "exports FreeCAD contents to an IFC file" + try: - import IfcImport as ifcw + global ifcopenshell + import ifcopenshell except: - try: - import ifc_wrapper as ifcw - except: - FreeCAD.Console.PrintError(translate("Arch","Error: IfcOpenShell is not installed\n")) - print """importIFC: ifcOpenShell is not installed. IFC export is unavailable. - Note: IFC export currently requires an experimental version of IfcOpenShell - available from https://github.com/aothms/IfcOpenShell""" - return + if DEBUG: print "using legacy exporter" + import importIFClegacy + return importIFClegacy.export(exportList,filename) - if (not hasattr(ifcw,"IfcFile")) and (not hasattr(ifcw,"file")): - FreeCAD.Console.PrintError(translate("Arch","Error: your IfcOpenShell version is too old\n")) - print """importIFC: The version of ifcOpenShell installed on this system doesn't - have IFC export capabilities. IFC export currently requires an experimental - version of IfcOpenShell available from https://github.com/aothms/IfcOpenShell""" - return - import ifcWriter,Arch,Draft - - # creating base IFC project - getConfig() - ifcWriter.PRECISION = Draft.precision() p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - scaling = p.GetFloat("IfcScalingFactor",1.0) - exporttxt = p.GetBool("IfcExportList",False) - forcebrep = p.GetBool("ifcExportAsBrep",False) - application = "FreeCAD" - ver = FreeCAD.Version() - version = ver[0]+"."+ver[1]+" build"+ver[2] + FORCEBREP = p.GetBool("ifcExportAsBrep",False) + DEBUG = p.GetBool("ifcDebug",False) + version = FreeCAD.Version() owner = FreeCAD.ActiveDocument.CreatedBy - company = FreeCAD.ActiveDocument.Company - project = FreeCAD.ActiveDocument.Name - ifc = ifcWriter.IfcDocument(filename,project,owner,company,application,version) - txt = [] - - # get all children and reorder list to get buildings and floors processed first + email = '' + if ("@" in owner) and ("<" in owner): + s = owner.split("<") + owner = s[0] + email = s[1].strip(">") + global ifctemplate + ifctemplate = ifctemplate.replace("$version",version[0]+"."+version[1]+" build "+version[2]) + ifctemplate = ifctemplate.replace("$owner",owner) + ifctemplate = ifctemplate.replace("$company",FreeCAD.ActiveDocument.Company) + ifctemplate = ifctemplate.replace("$email",email) + ifctemplate = ifctemplate.replace("$now",str(int(time.time()))) + ifctemplate = ifctemplate.replace("$projectid",FreeCAD.ActiveDocument.Uid[:22].replace("-","_")) + ifctemplate = ifctemplate.replace("$project",FreeCAD.ActiveDocument.Name) + ifctemplate = ifctemplate.replace("$filename",filename) + ifctemplate = ifctemplate.replace("$timestamp",str(time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()))) + template = tempfile.mkstemp(suffix=".ifc")[1] + of = pyopen(template,"wb") + of.write(ifctemplate) + of.close() + global ifcfile + ifcfile = ifcopenshell.open(template) + history = ifcfile.by_type("IfcOwnerHistory")[0] + context = ifcfile.by_type("IfcGeometricRepresentationContext")[0] + project = ifcfile.by_type("IfcProject")[0] objectslist = Draft.getGroupContents(exportList,walls=True,addgroups=True) objectslist = Arch.pruneIncluded(objectslist) - - sites = [] - buildings = [] - floors = [] - groups = {} - others = [] + products = {} + count = 1 + + # products for obj in objectslist: - otype = Draft.getType(obj) - if otype == "Site": - sites.append(obj) - elif otype == "Building": - buildings.append(obj) - elif otype == "Floor": - floors.append(obj) - elif otype == "Group": - groups[obj.Name] = [] - else: - others.append(obj) - objectslist = buildings + floors + others - if DEBUG: print "adding ", len(objectslist), " objects" - global unprocessed - unprocessed = [] - - # process objects - for obj in objectslist: - - otype = Draft.getType(obj) + # getting generic data name = str(obj.Label) - parent = Arch.getHost(obj) - gdata = None - fdata = None - placement = None - color = None - representation = None - descr = None - extra = None + description = str(obj.Description) if hasattr(obj,"Description") else "" + + # getting uid + uid = None + if hasattr(obj,"IfcAttributes"): + if "IfcUID" in obj.IfcAttributes.keys(): + uid = obj.IfcAttributes["IfcUID"] + if not uid: + uid = ifcopenshell.guid.compress(uuid.uuid1().hex) - # setting the IFC type + # setting the IFC type + name conversions if hasattr(obj,"Role"): ifctype = obj.Role.replace(" ","") else: - ifctype = otype + ifctype = Draft.getType(obj) if ifctype == "Foundation": ifctype = "Footing" + elif ifctype == "Floor": + ifctype = "BuildingStorey" elif ifctype == "Rebar": ifctype = "ReinforcingBar" - elif ifctype in ["Part","Undefined"]: - ifctype = "BuildingElementProxy" - + ifctype = "Ifc" + ifctype + if ifctype == "IfcGroup": + continue + if not ifctype in ifctypes: + ifctype = "IfcBuildingElementProxy" + # getting the "Force BREP" flag brepflag = False if hasattr(obj,"IfcAttributes"): if "FlagForceBrep" in obj.IfcAttributes.keys(): if obj.IfcAttributes["FlagForceBrep"] == "True": brepflag = True - - if DEBUG: print "Adding " + obj.Label + " as Ifc" + ifctype - - # writing IFC data - if obj.isDerivedFrom("App::DocumentObjectGroup"): + + # getting the representation + representation,placement,shapetype = getRepresentation(ifcfile,context,obj,forcebrep=(brepflag or FORCEBREP)) + + if DEBUG: print str(count).ljust(3)," : ", ifctype, " (",shapetype,") : ",name + + # setting the arguments + args = [uid,history,name,description,None,placement,representation,None] + if ifctype in ["IfcSlab","IfcFooting"]: + args = args + ["NOTDEFINED"] + elif ifctype in ["IfcWindow","IfcDoor"]: + args = args + [obj.Width.Value, obj.Height.Value] + elif ifctype == "IfcSpace": + args = args + ["ELEMENT","INTERNAL",obj.Shape.BoundBox.ZMin] + elif ifctype == "IfcBuildingElementProxy": + args = args + ["ELEMENT"] + elif ifctype == "IfcSite": + latitude = None + longitude = None + elevation = None + landtitlenumber = None + address = None + args = args + ["ELEMENT",latitude,longitude,elevation,landtitlenumber,address] + elif ifctype == "IfcBuilding": + args = args + ["ELEMENT",None,None,None] + elif ifctype == "IfcBuildingStorey": + args = args + ["ELEMENT",None] - # getting parent building - if parent: - parent = ifc.findByName("IfcBuilding",str(parent.Label)) - - if otype == "Site": - print " Skipping (not implemented yet)" # TODO manage sites - elif otype == "Building": - ifc.addBuilding( name=name ) - elif otype == "Floor": - ifc.addStorey( building=parent, name=name ) - - elif obj.isDerivedFrom("Part::Feature"): - - # get color - if FreeCAD.GuiUp: - color = obj.ViewObject.ShapeColor[:3] - - # get parent floor - if parent: - parent = ifc.findByName("IfcBuildingStorey",str(parent.Label)) - - # get representation - if (not forcebrep) and (not brepflag): - gdata = getIfcExtrusionData(obj,scaling,SEPARATE_OPENINGS) - #if DEBUG: print " extrusion data for ",obj.Label," : ",gdata - if not gdata: - fdata = getIfcBrepFacesData(obj,scaling) - #if DEBUG: print " brep data for ",obj.Label," : ",fdata - if not fdata: - if obj.isDerivedFrom("Part::Feature"): - print " Error retrieving the shape of object ", obj.Label - unprocessed.append(obj) - continue - else: - if DEBUG: print " No geometry" - else: - if DEBUG: print " Brep" - else: - if DEBUG: print " Extrusion" - if gdata: - # gdata = [ type, profile data, extrusion data, placement data ] - placement = ifc.addPlacement(origin=gdata[3][0],xaxis=gdata[3][1],zaxis=gdata[3][2]) - if gdata[0] == "polyline": - representation = ifc.addExtrudedPolyline(gdata[1], gdata[2], color=color) - elif gdata[0] == "circle": - representation = ifc.addExtrudedCircle(gdata[1], gdata[2], color=color) - elif gdata[0] == "ellipse": - representation = ifc.addExtrudedEllipse(gdata[1], gdata[2], color=color) - elif gdata[0] == "composite": - representation = ifc.addExtrudedCompositeCurve(gdata[1], gdata[2], color=color) - else: - print "debug: unknow extrusion type" - elif fdata: - representation = [ifc.addFacetedBrep(f, color=color) for f in fdata] - - # create ifc object - ifctype = "Ifc" + ifctype - if hasattr(obj,"Description"): - descr = obj.Description - if otype == "Wall": - if gdata: - if gdata[0] == "polyline": - ifctype = "IfcWallStandardCase" - elif otype == "Structure": - if ifctype in ["IfcSlab","IfcFooting"]: - extra = ["NOTDEFINED"] - elif otype == "Window": - extra = [obj.Width.Value*scaling, obj.Height.Value*scaling] - elif otype == "Space": - extra = ["ELEMENT","INTERNAL",getIfcElevation(obj)] - elif otype == "Part": - extra = ["ELEMENT"] - if not ifctype in supportedIfcTypes: - if DEBUG: print " Type ",ifctype," is not supported yet. Exporting as IfcBuildingElementProxy instead" - ifctype = "IfcBuildingElementProxy" - extra = ["ELEMENT"] - - product = ifc.addProduct( ifctype, representation, storey=parent, placement=placement, name=name, description=descr, extra=extra ) - - if product: - # removing openings - if SEPARATE_OPENINGS and gdata: - for o in obj.Subtractions: - print "Subtracting ",o.Label - fdata = getIfcBrepFacesData(o,scaling,sub=True) - representation = [ifc.addFacetedBrep(f, color=color) for f in fdata] - p2 = ifc.addProduct( "IfcOpeningElement", representation, storey=product, placement=None, name=str(o.Label), description=None) - - # writing text log - spacer = "" - for i in range(36-len(obj.Label)): - spacer += " " - txt.append(obj.Label + spacer + ifctype) - - # adding object to group, if any - for g in groups.keys(): - group = FreeCAD.ActiveDocument.getObject(g) - if group: - for o in group.Group: - if o.Name == obj.Name: - groups[g].append(product) - - else: - unprocessed.append(obj) - else: - if DEBUG: print "Object type ", otype, " is not supported yet." - - # processing groups - for name,entities in groups.iteritems(): - if entities: - o = FreeCAD.ActiveDocument.getObject(name) - if o: - if DEBUG: print "Adding group ", o.Label, " with ",len(entities)," elements" - grp = ifc.addGroup( entities, o.Label ) + # creating the product + product = getattr(ifcfile,"create"+ifctype)(*args) + products[obj.Name] = product - ifc.write() - - if exporttxt: - import time, os - txtstring = "List of objects exported by FreeCAD in file\n" - txtstring += filename + "\n" - txtstring += "On " + time.ctime() + "\n" - txtstring += "\n" - txtstring += str(len(txt)) + " objects exported:\n" - txtstring += "\n" - txtstring += "Nr Name Type\n" - txtstring += "\n" - for i in range(len(txt)): - idx = str(i+1) - sp = "" - for j in range(8-len(idx)): - sp += " " - txtstring += idx + sp + txt[i] + "\n" - txtfile = os.path.splitext(filename)[0]+".txt" - f = pyopen(txtfile,"wb") - f.write(txtstring) - f.close() - - FreeCAD.ActiveDocument.recompute() - - if unprocessed: - print "" - print "WARNING: " + str(len(unprocessed)) + " objects were not exported (stored in importIFC.unprocessed):" - for o in unprocessed: - print " " + o.Label - - -def getTuples(data,scale=1,placement=None,normal=None,close=True): - """getTuples(data,[scale,placement,normal,close]): returns a tuple or a list of tuples from a vector - or from the vertices of a shape. Scale can indicate a scale factor""" - rnd = False - import Part - if isinstance(data,FreeCAD.Vector): - if placement: - data = placement.multVec(data) - if rnd: - data = DraftVecUtils.rounded(data) - return (data.x*scale,data.y*scale,data.z*scale) - elif isinstance(data,Part.Shape): - t = [] - if len(data.Wires) == 1: - import Part,DraftGeomUtils - data = Part.Wire(DraftGeomUtils.sortEdges(data.Wires[0].Edges)) - verts = data.Vertexes - try: - c = data.CenterOfMass - v1 = verts[0].Point.sub(c) - v2 = verts[1].Point.sub(c) - if DraftVecUtils.angle(v2,v1,normal) >= 0: - # inverting verts order if the direction is couterclockwise - verts.reverse() - except: - pass - for v in verts: - pt = v.Point - if placement: - if not placement.isNull(): - pt = placement.multVec(pt) - if rnd: - pt = DraftVecUtils.rounded(pt) - t.append((pt.x*scale,pt.y*scale,pt.z*scale)) - - if close: # faceloops must not be closed, but ifc profiles must. - t.append(t[0]) - else: - print "Arch.getTuples(): Wrong profile data" - return t - -def getIfcExtrusionData(obj,scale=1,nosubs=False): - """getIfcExtrusionData(obj,[scale,nosubs]): returns a closed path (a list of tuples), a tuple expressing an extrusion - vector, and a list of 3 tuples for base position, x axis and z axis. Or returns None, if a base loop and - an extrusion direction cannot be extracted. Scale can indicate a scale factor.""" + # additions + if hasattr(obj,"Additions") and (shapetype == "extrusion"): + for o in obj.Additions: + r2,p2,c2 = getRepresentation(ifcfile,context,o,forcebrep=True) + if DEBUG: print " adding ",c2," : ",str(o.Label) + prod2 = ifcfile.createIfcBuildingElementProxy(ifcopenshell.guid.compress(uuid.uuid1().hex),history,str(o.Label),None,None,p2,r2,None,"ELEMENT") + ifcfile.createIfcRelAggregates(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'Addition','',product,[prod2]) + + # subtractions + if hasattr(obj,"Subtractions") and (shapetype == "extrusion"): + for o in obj.Subtractions: + r2,p2,c2 = getRepresentation(ifcfile,context,o,forcebrep=True,subtraction=True) + if DEBUG: print " subtracting ",c2," : ",str(o.Label) + prod2 = ifcfile.createIfcOpeningElement(ifcopenshell.guid.compress(uuid.uuid1().hex),history,str(o.Label),None,None,p2,r2,None) + ifcfile.createIfcRelVoidsElement(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'Subtraction','',product,prod2) - CURVEMODE = "PARAMETER" # For trimmed curves. CARTESIAN or PARAMETER + count += 1 + + # relationships + sites = [] + buildings = [] + floors = [] + for site in Draft.getObjectsOfType(objectslist,"Site"): + for building in Draft.getObjectsOfType(site.Group,"Building"): + for floor in Draft.getObjectsOfType(building.Group,"Floor"): + children = Draft.getGroupContents(floor,walls=True) + children = Arch.pruneIncluded(children) + children = [products[c.Name] for c in children if c.Name in products.keys()] + floor = products[floor.Name] + ifcfile.createIfcRelContainedInSpatialStructure(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'StoreyLink','',children,floor) + floors.append(floor) + building = products[building.Name] + if floors: + ifcfile.createIfcRelAggregates(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'BuildingLink','',building,floors) + buildings.append(building) + site = products[site.Name] + if buildings: + ifcfile.createIfcRelAggregates(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'SiteLink','',site,buildings) + sites.append(site) + if not sites: + if DEBUG: print "adding default site" + sites = [ifcfile.createIfcSite(ifcopenshell.guid.compress(uuid.uuid1().hex),history,"Default Site",'',None,None,None,None,"ELEMENT",None,None,None,None,None)] + ifcfile.createIfcRelAggregates(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'ProjectLink','',project,sites) + if not buildings: + if DEBUG: print "adding default building" + buildings = [ifcfile.createIfcBuilding(ifcopenshell.guid.compress(uuid.uuid1().hex),history,"Default Building",'',None,None,None,None,"ELEMENT",None,None,None)] + ifcfile.createIfcRelAggregates(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'SiteLink','',sites[0],buildings) + ifcfile.createIfcRelContainedInSpatialStructure(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'BuildingLink','',products.values(),buildings[0]) + + + if DEBUG: print "writing ",filename,"..." + ifcfile.write(filename) + + +def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tessellation=1): + """returns an IfcShapeRepresentation object or None""" - if hasattr(obj,"Additions"): - if obj.Additions: - # TODO provisorily treat objs with additions as breps - return None - if hasattr(obj,"Subtractions") and not nosubs: - if obj.Subtractions: - return None - if hasattr(obj,"Proxy"): - if hasattr(obj.Proxy,"getProfiles"): - p = obj.Proxy.getProfiles(obj,noplacement=True) - v = obj.Proxy.getExtrusionVector(obj,noplacement=True) - if (len(p) == 1) and v: - p = p[0] - r = FreeCAD.Placement() - #b = p.CenterOfMass - r = obj.Proxy.getPlacement(obj) - #b = obj.Placement.multVec(FreeCAD.Vector()) - #r.Rotation = DraftVecUtils.getRotation(v,FreeCAD.Vector(0,0,1)) - d = [r.Base,DraftVecUtils.rounded(r.Rotation.multVec(FreeCAD.Vector(1,0,0))),DraftVecUtils.rounded(r.Rotation.multVec(FreeCAD.Vector(0,0,1)))] - #r = r.inverse() - #print "getExtrusionData: computed placement:",r - import Part - if len(p.Edges) == 1: - if isinstance(p.Edges[0].Curve,Part.Circle): - # Circle profile - r1 = p.Edges[0].Curve.Radius*scale - return "circle", [getTuples(p.Edges[0].Curve.Center,scale), r1], getTuples(v,scale), d - elif isinstance(p.Edges[0].Curve,Part.Ellipse): - # Ellipse profile - r1 = p.Edges[0].Curve.MajorRadius*scale - r2 = p.Edges[0].Curve.MinorRadius*scale - return "ellipse", [getTuples(p.Edges[0].Curve.Center,scale), r1, r2], getTuples(v,scale), d - curves = False - for e in p.Edges: - if isinstance(e.Curve,Part.Circle): - curves = True - elif not isinstance(e.Curve,Part.Line): - print "Arch.getIfcExtrusionData: Warning: unsupported edge type in profile" - if curves: - # Composite profile - ecurves = [] - last = None - import DraftGeomUtils - edges = DraftGeomUtils.sortEdges(p.Edges) - for e in edges: - if isinstance(e.Curve,Part.Circle): - import math - follow = True - if last: - if not DraftVecUtils.equals(last,e.Vertexes[0].Point): - follow = False - last = e.Vertexes[0].Point - else: - last = e.Vertexes[-1].Point - else: - last = e.Vertexes[-1].Point - p1 = math.degrees(-DraftVecUtils.angle(e.Vertexes[0].Point.sub(e.Curve.Center))) - p2 = math.degrees(-DraftVecUtils.angle(e.Vertexes[-1].Point.sub(e.Curve.Center))) - da = DraftVecUtils.angle(e.valueAt(e.FirstParameter+0.1).sub(e.Curve.Center),e.Vertexes[0].Point.sub(e.Curve.Center)) - if p1 < 0: - p1 = 360 + p1 - if p2 < 0: - p2 = 360 + p2 - if da > 0: - follow = not(follow) - if CURVEMODE == "CARTESIAN": - # BUGGY - p1 = getTuples(e.Vertexes[0].Point,scale) - p2 = getTuples(e.Vertexes[-1].Point,scale) - ecurves.append(["arc",getTuples(e.Curve.Center,scale),e.Curve.Radius*scale,[p1,p2],follow,CURVEMODE]) + import Part,math,DraftGeomUtils,DraftVecUtils + shapes = [] + placement = None + productdef = None + shapetype = "no shape" + + if not forcebrep: + profile = None + if hasattr(obj,"Proxy"): + if hasattr(obj.Proxy,"getProfiles"): + p = obj.Proxy.getProfiles(obj,noplacement=True) + extrusionv = obj.Proxy.getExtrusionVector(obj,noplacement=True) + if (len(p) == 1) and extrusionv: + p = p[0] + r = obj.Proxy.getPlacement(obj) + + if len(p.Edges) == 1: + + pxvc = ifcfile.createIfcDirection((1.0,0.0)) + povc = ifcfile.createIfcCartesianPoint((0.0,0.0)) + pt = ifcfile.createIfcAxis2Placement2D(povc,pxvc) + + # extruded circle + if isinstance(p.Edges[0].Curve,Part.Circle): + profile = ifcfile.createIfcCircleProfileDef("AREA",None,pt, p.Edges[0].Curve.Radius) + + # extruded ellipse + elif isinstance(p.Edges[0].Curve,Part.Ellipse): + profile = ifcfile.createIfcEllipseProfileDef("AREA",None,pt, p.Edges[0].Curve.MajorRadius, p.Edges[0].Curve.MinorRadius) + + else: + curves = False + for e in p.Edges: + if isinstance(e.Curve,Part.Circle): + curves = True + + # extruded polyline + if not curves: + w = Part.Wire(DraftGeomUtils.sortEdges(p.Edges)) + pts = [ifcfile.createIfcCartesianPoint(tuple(v.Point)[:2]) for v in w.Vertexes+[w.Vertexes[0]]] + pol = ifcfile.createIfcPolyline(pts) + + # extruded composite curve else: - verts = [vertex.Point for vertex in e.Vertexes] - if last: - if not DraftVecUtils.equals(last,verts[0]): - verts.reverse() - last = e.Vertexes[0].Point + segments = [] + last = None + edges = DraftGeomUtils.sortEdges(p.Edges) + for e in edges: + if isinstance(e.Curve,Part.Circle): + follow = True + if last: + if not DraftVecUtils.equals(last,e.Vertexes[0].Point): + follow = False + last = e.Vertexes[0].Point + else: + last = e.Vertexes[-1].Point + else: + last = e.Vertexes[-1].Point + p1 = math.degrees(-DraftVecUtils.angle(e.Vertexes[0].Point.sub(e.Curve.Center))) + p2 = math.degrees(-DraftVecUtils.angle(e.Vertexes[-1].Point.sub(e.Curve.Center))) + da = DraftVecUtils.angle(e.valueAt(e.FirstParameter+0.1).sub(e.Curve.Center),e.Vertexes[0].Point.sub(e.Curve.Center)) + if p1 < 0: + p1 = 360 + p1 + if p2 < 0: + p2 = 360 + p2 + if da > 0: + follow = not(follow) + xvc = ifcfile.createIfcDirection((1.0,0.0)) + ovc = ifcfile.createIfcCartesianPoint(tuple(e.Curve.Center)[:2]) + plc = ifcfile.createIfcAxis2Placement2D(ovc,xvc) + cir = ifcfile.createIfcCircle(plc,e.Curve.Radius) + curve = ifcfile.createIfcTrimmedCurve(cir,[ifcfile.create_entity("IfcParameterValue",p1)],[ifcfile.create_entity("IfcParameterValue",p2)],follow,"PARAMETER") + else: - last = e.Vertexes[-1].Point - else: - last = e.Vertexes[-1].Point - ecurves.append(["line",[getTuples(vert,scale) for vert in verts]]) - return "composite", ecurves, getTuples(v,scale), d - else: - # Polyline profile - return "polyline", getTuples(p,scale), getTuples(v,scale), d - return None - -def getIfcBrepFacesData(obj,scale=1,sub=False,tessellation=1): - """getIfcBrepFacesData(obj,[scale,tesselation]): returns a list(0) of lists(1) of lists(2) of lists(3), - list(3) being a list of vertices defining a loop, list(2) describing a face from one or - more loops, list(1) being the whole solid made of several faces, list(0) being the list - of solids inside the object. Scale can indicate a scaling factor. Tesselation is the tesselation - factor to apply on curved faces.""" - shape = None - if sub: - if hasattr(obj,"Proxy"): - if hasattr(obj.Proxy,"getSubVolume"): - shape = obj.Proxy.getSubVolume(obj) - if not shape: - if hasattr(obj,"Shape"): - if obj.Shape: - if not obj.Shape.isNull(): - if obj.Shape.isValid(): - shape = obj.Shape - elif hasattr(obj,"Terrain"): - if obj.Terrain: - if hasattr(obj.Terrain,"Shape"): - if obj.Terrain.Shape: - if not obj.Terrain.Shape.isNull(): - if obj.Terrain.Shape.isValid(): - shape = obj.Terrain.Shape - if shape: - import Part - sols = [] - if shape.Solids: - dataset = shape.Solids - else: - dataset = shape.Shells - print "Warning! object contains no solids" - for sol in shape.Solids: - s = [] - curves = False - for face in sol.Faces: - for e in face.Edges: - if not isinstance(e.Curve,Part.Line): - curves = True - if curves: - tris = sol.tessellate(tessellation) - for tri in tris[1]: - f = [] - for i in tri: - f.append(getTuples(tris[0][i],scale)) - s.append([f]) + verts = [vertex.Point for vertex in e.Vertexes] + if last: + if not DraftVecUtils.equals(last,verts[0]): + verts.reverse() + last = e.Vertexes[0].Point + else: + last = e.Vertexes[-1].Point + else: + last = e.Vertexes[-1].Point + pts = [ifcfile.createIfcCartesianPoint(tuple(v)[:2]) for v in verts] + curve = ifcfile.createIfcPolyline(pts) + segment = ifcfile.createIfcCompositeCurveSegment("CONTINUOUS",True,curve) + segments.append(segment) + + pol = ifcfile.createIfcCompositeCurve(segments,False) + profile = ifcfile.createIfcArbitraryClosedProfileDef("AREA",None,pol) + + if profile: + xvc = ifcfile.createIfcDirection(tuple(r.Rotation.multVec(FreeCAD.Vector(1,0,0)))) + zvc = ifcfile.createIfcDirection(tuple(r.Rotation.multVec(FreeCAD.Vector(0,0,1)))) + ovc = ifcfile.createIfcCartesianPoint(tuple(r.Base)) + lpl = ifcfile.createIfcAxis2Placement3D(ovc,zvc,xvc) + edir = ifcfile.createIfcDirection(tuple(FreeCAD.Vector(extrusionv).normalize())) + shape = ifcfile.createIfcExtrudedAreaSolid(profile,lpl,edir,extrusionv.Length) + shapes.append(shape) + solidType = "SweptSolid" + shapetype = "extrusion" + + if not shapes: + + # brep representation + fcshape = None + solidType = "Brep" + if subtraction: + if hasattr(obj,"Proxy"): + if hasattr(obj.Proxy,"getSubVolume"): + fcshape = obj.Proxy.getSubVolume(obj) + if not fcshape: + if hasattr(obj,"Shape"): + if obj.Shape: + if not obj.Shape.isNull(): + fcshape = obj.Shape + elif hasattr(obj,"Terrain"): + if obj.Terrain: + if hasattr(obj.Terrain,"Shape"): + if obj.Terrain.Shape: + if not obj.Terrain.Shape.isNull(): + fcshape = obj.Terrain.Shape + if fcshape: + solids = [] + if fcshape.Solids: + dataset = fcshape.Solids else: - for face in sol.Faces: - f = [] - f.append(getTuples(face.OuterWire,scale,normal=face.normalAt(0,0),close=False)) - for wire in face.Wires: - if wire.hashCode() != face.OuterWire.hashCode(): - f.append(getTuples(wire,scale,normal=DraftVecUtils.neg(face.normalAt(0,0)),close=False)) - s.append(f) - sols.append(s) - return sols - return None - -def getIfcElevation(obj): - """getIfcElevation(obj): Returns the lowest height (Z coordinate) of this object""" - if obj.isDerivedFrom("Part::Feature"): - b = obj.Shape.BoundBox - return b.ZMin - return 0 - - -def explore(filename=None): - "explore the contents of an ifc file in a Qt dialog" - if not filename: - from PySide import QtGui - filename = QtGui.QFileDialog.getOpenFileName(QtGui.qApp.activeWindow(),'IFC files','*.ifc') - if filename: - filename = filename[0] - if filename: - import ifcReader - getConfig() - schema=getSchema() - ifcReader.DEBUG = DEBUG - d = ifcReader.explorer(filename,schema) - d.show() - return d + dataset = fcshape.Shells + print "Warning! object contains no solids" + for fcsolid in dataset: + faces = [] + curves = False + for fcface in fcsolid.Faces: + for e in fcface.Edges: + if not isinstance(e.Curve,Part.Line): + curves = True + if curves: + tris = fcsolid.tessellate(tessellation) + for tri in tris[1]: + pts = [ifcfile.createIfcCartesianPoint(tuple(tris[0][i])) for i in tri] + loop = ifcfile.createIfcPolyLoop(pts) + bound = ifcfile.createIfcFaceOuterBound(loop,True) + face = ifcfile.createIfcFace([bound]) + faces.append(face) + else: + for fcface in fcsolid.Faces: + loops = [] + verts = [v.Point for v in Part.Wire(DraftGeomUtils.sortEdges(fcface.OuterWire.Edges)).Vertexes] + c = fcface.CenterOfMass + v1 = verts[0].sub(c) + v2 = verts[1].sub(c) + n = fcface.normalAt(0,0) + if DraftVecUtils.angle(v2,v1,n) >= 0: + verts.reverse() # inverting verts order if the direction is couterclockwise + pts = [ifcfile.createIfcCartesianPoint(tuple(v)) for v in verts] + loop = ifcfile.createIfcPolyLoop(pts) + bound = ifcfile.createIfcFaceOuterBound(loop,True) + loops.append(bound) + for wire in fcface.Wires: + if wire.hashCode() != fcface.OuterWire.hashCode(): + verts = [v.Point for v in Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)).Vertexes] + v1 = verts[0].sub(c) + v2 = verts[1].sub(c) + if DraftVecUtils.angle(v2,v1,DraftVecUtils.neg(n)) >= 0: + verts.reverse() + pts = [ifcfile.createIfcCartesianPoint(tuple(v)) for v in verts] + loop = ifcfile.createIfcPolyLoop(pts) + bound = ifcfile.createIfcFaceBound(loop,True) + loops.append(bound) + face = ifcfile.createIfcFace(loops) + faces.append(face) + shell = ifcfile.createIfcClosedShell(faces) + shape = ifcfile.createIfcFacetedBrep(shell) + shapes.append(shape) + shapetype = "brep" + + if shapes: + + if FreeCAD.GuiUp and not subtraction: + rgb = obj.ViewObject.ShapeColor + col = ifcfile.createIfcColourRgb(None,rgb[0],rgb[1],rgb[2]) + ssr = ifcfile.createIfcSurfaceStyleRendering(col,None,None,None,None,None,None,None,"FLAT") + iss = ifcfile.createIfcSurfaceStyle(None,"BOTH",[ssr]) + psa = ifcfile.createIfcPresentationStyleAssignment([iss]) + for shape in shapes: + isi = ifcfile.createIfcStyledItem(shape,[psa],None) + + + xvc = ifcfile.createIfcDirection((1.0,0.0,0.0)) + zvc = ifcfile.createIfcDirection((0.0,0.0,1.0)) + ovc = ifcfile.createIfcCartesianPoint((0.0,0.0,0.0)) + gpl = ifcfile.createIfcAxis2Placement3D(ovc,zvc,xvc) + placement = ifcfile.createIfcLocalPlacement(None,gpl) + representation = ifcfile.createIfcShapeRepresentation(context,'Body',solidType,shapes) + productdef = ifcfile.createIfcProductDefinitionShape(None,None,[representation]) + + return productdef,placement,shapetype diff --git a/src/Mod/Arch/importIFClegacy.py b/src/Mod/Arch/importIFClegacy.py new file mode 100644 index 000000000000..f523d3441e36 --- /dev/null +++ b/src/Mod/Arch/importIFClegacy.py @@ -0,0 +1,2354 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011 * +#* Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# WARNING ################################################################## +# # +# This module is deprecated and will be removed in a future version # +# # +############################################################################ + + +import FreeCAD, Arch, Draft, os, sys, time, Part, DraftVecUtils, uuid, math, re +from DraftTools import translate + +__title__="FreeCAD IFC importer" +__author__ = "Yorik van Havre" +__url__ = "http://www.freecadweb.org" + +# config +subtractiveTypes = ["IfcOpeningElement"] # elements that must be subtracted from their parents +SCHEMA = "http://www.steptools.com/support/stdev_docs/ifcbim/ifc4.exp" # only for internal prser +MAKETEMPFILES = False # if True, shapes are passed from ifcopenshell to freecad through temp files +DEBUG = True # this is only for the python console, this value is overridden when importing through the GUI +SKIP = ["IfcBuildingElementProxy","IfcFlowTerminal","IfcFurnishingElement"] # default. overwritten by the GUI options +IFCLINE_RE = re.compile("#(\d+)[ ]?=[ ]?(.*?)\((.*)\);[\\r]?$") +PRECISION = 4 # rounding value, in number of digits +APPLYFIX = True # if true, the ifcopenshell bug-fixing function is applied when saving files +# end config + +# supported ifc products (export only): +supportedIfcTypes = ["IfcSite", "IfcBuilding", "IfcBuildingStorey", "IfcBeam", "IfcBeamStandardCase", + "IfcChimney", "IfcColumn", "IfcColumnStandardCase", "IfcCovering", "IfcCurtainWall", + "IfcDoor", "IfcDoorStandardCase", "IfcMember", "IfcMemberStandardCase", "IfcPlate", + "IfcPlateStandardCase", "IfcRailing", "IfcRamp", "IfcRampFlight", "IfcRoof", + "IfcSlab", "IfcStair", "IfcStairFlight", "IfcWall","IfcSpace", + "IfcWallStandardCase", "IfcWindow", "IfcWindowStandardCase", "IfcBuildingElementProxy", + "IfcPile", "IfcFooting", "IfcReinforcingBar", "IfcTendon"] +# TODO : shading device not supported? + +if open.__module__ == '__builtin__': + pyopen = open # because we'll redefine open below + +def open(filename,skip=None): + "called when freecad opens a file" + docname = os.path.splitext(os.path.basename(filename))[0] + doc = FreeCAD.newDocument(docname) + doc.Label = decode(docname) + FreeCAD.ActiveDocument = doc + getConfig() + read(filename,skip) + return doc + +def insert(filename,docname,skip=None): + "called when freecad wants to import a file" + try: + doc = FreeCAD.getDocument(docname) + except: + doc = FreeCAD.newDocument(docname) + FreeCAD.ActiveDocument = doc + getConfig() + read(filename,skip) + return doc + +def getConfig(): + "Gets Arch IFC import preferences" + global SKIP, CREATE_IFC_GROUPS, ASMESH, PREFIX_NUMBERS, FORCE_PYTHON_PARSER, SEPARATE_OPENINGS, SEPARATE_PLACEMENTS, JOINSOLIDS, AGGREGATE_WINDOWS + IMPORT_IFC_FURNITURE = False + ASMESH = ["IfcFurnishingElement"] + p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") + CREATE_IFC_GROUPS = p.GetBool("createIfcGroups",False) + FORCE_PYTHON_PARSER = p.GetBool("forceIfcPythonParser",False) + DEBUG = p.GetBool("ifcDebug",False) + SEPARATE_OPENINGS = p.GetBool("ifcSeparateOpenings",False) + SEPARATE_PLACEMENTS = p.GetBool("ifcSeparatePlacements",False) + PREFIX_NUMBERS = p.GetBool("ifcPrefixNumbers",False) + JOINSOLIDS = p.GetBool("ifcJoinSolids",False) + AGGREGATE_WINDOWS = p.GetBool("ifcAggregateWindows",False) + skiplist = p.GetString("ifcSkip","") + if skiplist: + SKIP = skiplist.split(",") + asmeshlist = p.GetString("ifcAsMesh","") + if asmeshlist: + ASMESH = asmeshlist.split(",") + +def getIfcOpenShell(): + "locates and imports ifcopenshell" + global IFCOPENSHELL5 + global IfcImport + IFCOPENSHELL5 = False + try: + import IfcImport + except: + try: + import ifc_wrapper as IfcImport + except: + FreeCAD.Console.PrintMessage(translate("Arch","Couldn't locate IfcOpenShell\n")) + return False + else: + IFCOPENSHELL5 = True + return True + else: + if hasattr(IfcImport,"IfcFile"): + IFCOPENSHELL5 = True + return True + +def read(filename,skip=None): + "Parses an IFC file" + + # parsing the IFC file + t1 = time.time() + + processedIds = [] + skipIds = skip + if not skipIds: + skipIds = [] + elif isinstance(skipIds,int): + skipIds = [skipIds] + + if getIfcOpenShell() and not FORCE_PYTHON_PARSER: + # use the IfcOpenShell parser + + # preparing IfcOpenShell + if DEBUG: global ifcObjects,ifcParents + ifcObjects = {} # a table to relate ifc id with freecad object + ifcParents = {} # a table to relate ifc id with parent id + if SEPARATE_OPENINGS: + if not IFCOPENSHELL5: + if hasattr(IfcImport,"DISABLE_OPENING_SUBTRACTIONS"): + IfcImport.Settings(IfcImport.DISABLE_OPENING_SUBTRACTIONS,True) + else: + SKIP.append("IfcOpeningElement") + useShapes = False + if IFCOPENSHELL5: + useShapes = True + if hasattr(IfcImport,"clean"): + IfcImport.clean() + elif hasattr(IfcImport,"USE_BREP_DATA"): + IfcImport.Settings(IfcImport.USE_BREP_DATA,True) + useShapes = True + else: + if DEBUG: print "Warning: IfcOpenShell version very old, unable to handle Brep data" + + # opening file + if IFCOPENSHELL5: + global ifc + ifc = IfcImport.open(filename) + objects = ifc.by_type("IfcProduct") + num_lines = len(objects) + relations = ifc.by_type("IfcRelAggregates") + ifc.by_type("IfcRelContainedInSpatialStructure") + ifc.by_type("IfcRelVoidsElement") + if not objects: + print "Error opening IFC file" + return + else: + num_lines = sum(1 for line in pyopen(filename)) + if not IfcImport.Init(filename): + print "Error opening IFC file" + return + + # processing geometry + idx = 0 + while True: + objparentid = [] + if IFCOPENSHELL5: + obj = objects[idx] + idx += 1 + objid = int(str(obj).split("=")[0].strip("#")) + objname = obj.get_argument(obj.get_argument_index("Name")) + objtype = str(obj).split("=")[1].split("(")[0] + for r in relations: + if r.is_a("IfcRelAggregates"): + for c in getAttr(r,"RelatedObjects"): + if str(obj) == str(c): + objparentid.append(int(str(getAttr(r,"RelatingObject")).split("=")[0].strip("#"))) + elif r.is_a("IfcRelContainedInSpatialStructure"): + for c in getAttr(r,"RelatedElements"): + if str(obj) == str(c): + objparentid.append(int(str(getAttr(r,"RelatingStructure")).split("=")[0].strip("#"))) + elif r.is_a("IfcRelVoidsElement"): + if str(obj) == str(getAttr(r,"RelatedOpeningElement")): + objparentid.append(int(str(getAttr(r,"RelatingBuildingElement")).split("=")[0].strip("#"))) + + else: + if hasattr(IfcImport, 'GetBrepData'): + obj = IfcImport.GetBrepData() + else: + obj = IfcImport.Get() + objid = obj.id + idx = objid + objname = obj.name + objtype = obj.type + objparentid.append(obj.parent_id) + if DEBUG: print "["+str(int((float(idx)/num_lines)*100))+"%] parsing ",objid,": ",objname," of type ",objtype + + # retrieving name + n = getCleanName(objname,objid,objtype) + + # skip IDs + if objid in skipIds: + if DEBUG: print " skipping because object ID is in skip list" + nobj = None + + # skip types + elif objtype in SKIP: + if DEBUG: print " skipping because type is in skip list" + nobj = None + + # check if object was already processed, to workaround an ifcopenshell bug + elif objid in processedIds: + if DEBUG: print " skipping because this object was already processed" + + else: + # build shape + shape = None + if useShapes: + shape = getShape(obj,objid) + + # walls + if objtype in ["IfcWallStandardCase","IfcWall"]: + nobj = makeWall(objid,shape,n) + + # windows + elif objtype in ["IfcWindow","IfcDoor"]: + nobj = makeWindow(objid,shape,n) + + # structs + elif objtype in ["IfcBeam","IfcColumn","IfcSlab","IfcFooting"]: + nobj = makeStructure(objid,shape,objtype,n) + + # roofs + elif objtype in ["IfcRoof"]: + nobj = makeRoof(objid,shape,n) + + # furniture + elif objtype in ["IfcFurnishingElement"]: + nobj = FreeCAD.ActiveDocument.addObject("Part::Feature",n) + nobj.Shape = shape + + # sites + elif objtype in ["IfcSite"]: + nobj = makeSite(objid,shape,n) + + # floors + elif objtype in ["IfcBuildingStorey"]: + nobj = Arch.makeFloor(name=n) + nobj.Label = n + + # floors + elif objtype in ["IfcBuilding"]: + nobj = Arch.makeBuilding(name=n) + nobj.Label = n + + # spaces + elif objtype in ["IfcSpace"]: + nobj = makeSpace(objid,shape,n) + + elif shape: + # treat as dumb parts + if DEBUG: print "Fixme: Shape-containing object not handled: ",objid, " ", objtype + nobj = FreeCAD.ActiveDocument.addObject("Part::Feature",n) + nobj.Label = n + nobj.Shape = shape + + else: + # treat as meshes + if DEBUG: print "Warning: Object without shape: ",objid, " ", objtype + if hasattr(obj,"mesh"): + if not hasattr(obj.mesh, 'verts'): + obj = IfcImport.Get() # Get triangulated rep of same product + me,pl = getMesh(obj) + nobj = FreeCAD.ActiveDocument.addObject("Mesh::Feature",n) + nobj.Label = n + nobj.Mesh = me + nobj.Placement = pl + else: + if DEBUG: print "Error: Skipping object without mesh: ",objid, " ", objtype + + # registering object number and parent + if objparentid: + ifcParents[objid] = [] + for p in objparentid: + ifcParents[objid].append([p,not (objtype in subtractiveTypes)]) + ifcObjects[objid] = nobj + processedIds.append(objid) + + if IFCOPENSHELL5: + if idx >= len(objects): + break + else: + if not IfcImport.Next(): + break + + + # processing non-geometry and relationships + parents_temp = dict(ifcParents) + import ArchCommands + #print parents_temp + + while parents_temp: + id, comps = parents_temp.popitem() + for c in comps: + parent_id = c[0] + additive = c[1] + + if (id <= 0) or (parent_id <= 0): + # root dummy object + parent = None + + elif parent_id in ifcObjects: + parent = ifcObjects[parent_id] + # check if parent is a subtraction, if yes parent to grandparent + if parent_id in ifcParents: + for p in ifcParents[parent_id]: + if p[1] == False: + grandparent_id = p[0] + if grandparent_id in ifcObjects: + parent = ifcObjects[grandparent_id] + else: + # creating parent if needed + if IFCOPENSHELL5: + obj = ifc.by_id(parent_id) + parentid = int(str(obj).split("=")[0].strip("#")) + parentname = obj.get_argument(obj.get_argument_index("Name")) + parenttype = str(obj).split("=")[1].split("(")[0] + else: + obj = IfcImport.GetObject(parent_id) + parentid = obj.id + parentname = obj.name + parenttype = obj.type + #if DEBUG: print "["+str(int((float(idx)/num_lines)*100))+"%] parsing ",parentid,": ",parentname," of type ",parenttype + n = getCleanName(parentname,parentid,parenttype) + if parentid <= 0: + parent = None + elif parenttype == "IfcBuildingStorey": + parent = Arch.makeFloor(name=n) + parent.Label = n + elif parenttype == "IfcBuilding": + parent = Arch.makeBuilding(name=n) + parent.Label = n + elif parenttype == "IfcSite": + parent = Arch.makeSite(name=n) + parent.Label = n + elif parenttype == "IfcWindow": + parent = Arch.makeWindow(name=n) + parent.Label = n + elif parenttype == "IfcProject": + parent = None + else: + if DEBUG: print "Fixme: skipping unhandled parent: ", parentid, " ", parenttype + parent = None + # registering object number and parent + if not IFCOPENSHELL5: + if parent_ifcobj.parent_id > 0: + ifcParents[parentid] = [parent_ifcobj.parent_id,True] + parents_temp[parentid] = [parent_ifcobj.parent_id,True] + if parent and (not parentid in ifcObjects): + ifcObjects[parentid] = parent + + # attributing parent + if parent and (id in ifcObjects): + if ifcObjects[id] and (ifcObjects[id].Name != parent.Name): + if additive: + if DEBUG: print "adding ",ifcObjects[id].Name, " to ",parent.Name + ArchCommands.addComponents(ifcObjects[id],parent) + else: + if DEBUG: print "removing ",ifcObjects[id].Name, " from ",parent.Name + ArchCommands.removeComponents(ifcObjects[id],parent) + if not IFCOPENSHELL5: + IfcImport.CleanUp() + + else: + # use only the internal python parser + + FreeCAD.Console.PrintWarning(translate("Arch","IfcOpenShell not found or disabled, falling back on internal parser.\n")) + schema=getSchema() + if schema: + if DEBUG: print "opening",filename,"..." + ifc = IfcDocument(filename,schema=schema) + else: + FreeCAD.Console.PrintWarning(translate("Arch","IFC Schema not found, IFC import disabled.\n")) + return None + t2 = time.time() + if DEBUG: print "Successfully loaded",ifc,"in %s s" % ((t2-t1)) + + # getting walls + for w in ifc.getEnt("IfcWallStandardCase"): + nobj = makeWall(w) + + # getting windows and doors + for w in (ifc.getEnt("IfcWindow") + ifc.getEnt("IfcDoor")): + nobj = makeWindow(w) + + # getting structs + for w in (ifc.getEnt("IfcSlab") + ifc.getEnt("IfcBeam") + ifc.getEnt("IfcColumn") \ + + ifc.getEnt("IfcFooting")): + nobj = makeStructure(w) + + # getting floors + for f in ifc.getEnt("IfcBuildingStorey"): + group(f,ifc,"Floor") + + # getting buildings + for b in ifc.getEnt("IfcBuilding"): + group(b,ifc,"Building") + + # getting sites + for s in ifc.getEnt("IfcSite"): + group(s,ifc,"Site") + + if DEBUG: print "done parsing. Recomputing..." + FreeCAD.ActiveDocument.recompute() + t3 = time.time() + if DEBUG: print "done processing IFC file in %s s" % ((t3-t1)) + + return None + + +def getCleanName(name,ifcid,ifctype): + "Get a clean name from an ifc object" + #print "getCleanName called",name,ifcid,ifctype + n = name + if not n: + n = ifctype + if PREFIX_NUMBERS: + n = "ID"+str(ifcid)+" "+n + #for c in ",.!?;:": + # n = n.replace(c,"_") + return n + + +def makeWall(entity,shape=None,name="Wall"): + "makes a wall in the freecad document" + try: + if shape: + # use ifcopenshell + if isinstance(shape,Part.Shape): + body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + body.Shape = shape + else: + body = FreeCAD.ActiveDocument.addObject("Mesh::Feature",name+"_body") + body.Mesh = shape + wall = Arch.makeWall(body,name=name) + wall.Label = name + if DEBUG: print " made wall object ",entity,":",wall + return wall + + # use internal parser + if DEBUG: print "=====> making wall",entity.id + placement = wall = wire = body = width = height = None + placement = getPlacement(entity.ObjectPlacement) + if DEBUG: print " got wall placement",entity.id,":",placement + width = entity.getProperty("Width") + height = entity.getProperty("Height") + if width and height: + if DEBUG: print " got width, height ",entity.id,":",width,"/",height + for r in entity.Representation.Representations: + if r.RepresentationIdentifier == "Axis": + wire = getWire(r.Items,placement) + wall = Arch.makeWall(wire,width,height,align="Center",name="Wall"+str(entity.id)) + else: + if DEBUG: print " no height or width properties found..." + for r in entity.Representation.Representations: + if r.RepresentationIdentifier == "Body": + for b in r.Items: + if b.type == "IFCEXTRUDEDAREASOLID": + norm = getVector(b.ExtrudedDirection) + norm.normalize() + wire = getWire(b.SweptArea,placement) + wall = Arch.makeWall(wire,width=0,height=b.Depth,name="Wall"+str(entity.id)) + wall.Normal = norm + if wall: + if DEBUG: print " made wall object ",entity.id,":",wall + return wall + if DEBUG: print " error: skipping wall",entity.id + return None + except: + if DEBUG: print " error: skipping wall",entity + return None + + +def makeWindow(entity,shape=None,name="Window"): + "makes a window in the freecad document" + try: + if shape: + # use ifcopenshell + if isinstance(shape,Part.Shape): + window = Arch.makeWindow(name=name) + window.Shape = shape + window.Label = name + if DEBUG: print " made window object ",entity,":",window + return window + + # use internal parser + if DEBUG: print "=====> making window",entity.id + placement = window = wire = body = width = height = None + placement = getPlacement(entity.ObjectPlacement) + if DEBUG: print "got window placement",entity.id,":",placement + width = entity.getProperty("Width") + height = entity.getProperty("Height") + for r in entity.Representation.Representations: + if r.RepresentationIdentifier == "Body": + for b in r.Items: + if b.type == "IFCEXTRUDEDAREASOLID": + wire = getWire(b.SweptArea,placement) + window = Arch.makeWindow(wire,width=b.Depth,name=objtype+str(entity.id)) + if window: + if DEBUG: print " made window object ",entity.id,":",window + return window + if DEBUG: print " error: skipping window",entity.id + return None + except: + if DEBUG: print " error: skipping window",entity + return None + + +def makeStructure(entity,shape=None,ifctype=None,name="Structure"): + "makes a structure in the freecad document" + try: + if shape: + # use ifcopenshell + if isinstance(shape,Part.Shape): + body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + body.Shape = shape + else: + body = FreeCAD.ActiveDocument.addObject("Mesh::Feature",name+"_body") + body.Mesh = shape + structure = Arch.makeStructure(body,name=name) + structure.Label = name + if ifctype == "IfcBeam": + structure.Role = "Beam" + elif ifctype == "IfcColumn": + structure.Role = "Column" + elif ifctype == "IfcSlab": + structure.Role = "Slab" + elif ifctype == "IfcFooting": + structure.Role = "Foundation" + if DEBUG: print " made structure object ",entity,":",structure," (type: ",ifctype,")" + return structure + + # use internal parser + if DEBUG: print "=====> making struct",entity.id + placement = structure = wire = body = width = height = None + placement = getPlacement(entity.ObjectPlacement) + if DEBUG: print "got window placement",entity.id,":",placement + width = entity.getProperty("Width") + height = entity.getProperty("Height") + for r in entity.Representation.Representations: + if r.RepresentationIdentifier == "Body": + for b in r.Items: + if b.type == "IFCEXTRUDEDAREASOLID": + wire = getWire(b.SweptArea,placement) + structure = Arch.makeStructure(wire,height=b.Depth,name=objtype+str(entity.id)) + if structure: + if DEBUG: print " made structure object ",entity.id,":",structure + return structure + if DEBUG: print " error: skipping structure",entity.id + return None + except: + if DEBUG: print " error: skipping structure",entity + return None + + +def makeSite(entity,shape=None,name="Site"): + "makes a site in the freecad document" + try: + body = None + if shape: + # use ifcopenshell + if isinstance(shape,Part.Shape): + body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + body.Shape = shape + else: + body = FreeCAD.ActiveDocument.addObject("Mesh::Feature",name+"_body") + body.Mesh = shape + site = Arch.makeSite(name=name) + site.Label = name + if body: + site.Terrain = body + if DEBUG: print " made site object ",entity,":",site + return site + except: + return None + +def makeSpace(entity,shape=None,name="Space"): + "makes a space in the freecad document" + try: + if shape: + # use ifcopenshell + if isinstance(shape,Part.Shape): + space = Arch.makeSpace(name=name) + space.Label = name + body = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + body.Shape = shape + space.Base = body + body.ViewObject.hide() + if DEBUG: print " made space object ",entity,":",space + return space + except: + return None + + +def makeRoof(entity,shape=None,name="Roof"): + "makes a roof in the freecad document" + try: + if shape: + # use ifcopenshell + if isinstance(shape,Part.Shape): + roof = Arch.makeRoof(name=name) + roof.Label = name + roof.Shape = shape + if DEBUG: print " made roof object ",entity,":",roof + return roof + except: + return None + +# geometry helpers ################################################################### + +def getMesh(obj): + "gets mesh and placement from an IfcOpenShell object" + if IFCOPENSHELL5: + return None,None + print "fixme: mesh data not yet supported" # TODO implement this with OCC tessellate + import Mesh + meshdata = [] + print obj.mesh.faces + print obj.mesh.verts + f = obj.mesh.faces + v = obj.mesh.verts + for i in range(0, len(f), 3): + face = [] + for j in range(3): + vi = f[i+j]*3 + face.append([v[vi],v[vi+1],v[vi+2]]) + meshdata.append(face) + print meshdata + me = Mesh.Mesh(meshdata) + # get transformation matrix + m = obj.matrix + mat = FreeCAD.Matrix(m[0], m[3], m[6], m[9], + m[1], m[4], m[7], m[10], + m[2], m[5], m[8], m[11], + 0, 0, 0, 1) + pl = FreeCAD.Placement(mat) + return me,pl + +def getShape(obj,objid): + "gets a shape from an IfcOpenShell object" + #print "retrieving shape from obj ",objid + import Part + sh=Part.Shape() + brep_data = None + if IFCOPENSHELL5: + try: + if hasattr(IfcImport,"SEW_SHELLS"): + ss = IfcImport.SEW_SHELLS + else: + ss = 0 + if SEPARATE_OPENINGS and hasattr(IfcImport,"DISABLE_OPENING_SUBTRACTIONS"): + if SEPARATE_PLACEMENTS and hasattr(IfcImport,"DISABLE_OBJECT_PLACEMENT"): + brep_data = IfcImport.create_shape(obj,IfcImport.DISABLE_OPENING_SUBTRACTIONS | IfcImport.DISABLE_OBJECT_PLACEMENT | ss) + else: + brep_data = IfcImport.create_shape(obj,IfcImport.DISABLE_OPENING_SUBTRACTIONS | ss) + else: + if SEPARATE_PLACEMENTS and hasattr(IfcImport,"DISABLE_OBJECT_PLACEMENT"): + brep_data = IfcImport.create_shape(obj,IfcImport.DISABLE_OBJECT_PLACEMENT | ss) + else: + brep_data = IfcImport.create_shape(obj, ss) + except: + print "Unable to retrieve shape data" + else: + brep_data = obj.mesh.brep_data + if brep_data: + try: + if MAKETEMPFILES: + import tempfile + tf = tempfile.mkstemp(suffix=".brp")[1] + of = pyopen(tf,"wb") + of.write(brep_data) + of.close() + sh = Part.read(tf) + os.remove(tf) + else: + sh.importBrepFromString(brep_data) + except: + print " error: malformed shape" + return None + else: + if IFCOPENSHELL5 and SEPARATE_PLACEMENTS: + p = getPlacement(getAttr(obj,"ObjectPlacement")) + if p: + sh.Placement = p + if not sh.Solids: + # try to extract a solid shape + if sh.Faces: + try: + if DEBUG: print " malformed solid. Attempting to fix..." + shell = Part.makeShell(sh.Faces) + if shell: + solid = Part.makeSolid(shell) + if solid: + sh = solid + except: + if DEBUG: print " failed to retrieve solid from object ",objid + else: + if DEBUG: print " object ", objid, " doesn't contain any geometry" + if not IFCOPENSHELL5: + m = obj.matrix + mat = FreeCAD.Matrix(m[0], m[3], m[6], m[9], + m[1], m[4], m[7], m[10], + m[2], m[5], m[8], m[11], + 0, 0, 0, 1) + sh.Placement = FreeCAD.Placement(mat) + # if DEBUG: print "getting Shape from ",obj + #print "getting shape: ",sh,sh.Solids,sh.Volume,sh.isValid(),sh.isNull() + #for v in sh.Vertexes: print v.Point + if sh: + if not sh.isNull(): + return sh + return None + +def getPlacement(entity): + "returns a placement from the given entity" + if not entity: + return None + if DEBUG: print " getting placement ",entity + if IFCOPENSHELL5: + if isinstance(entity,int): + entity = ifc.by_id(entity) + entitytype = str(entity).split("=")[1].split("(")[0].upper() + entityid = int(str(entity).split("=")[0].strip("#")) + else: + entitytype = entity.type.upper() + entityid = entity.id + pl = None + if entitytype == "IFCAXIS2PLACEMENT3D": + x = getVector(getAttr(entity,"RefDirection")) + z = getVector(getAttr(entity,"Axis")) + if not(x) or not(z): + return None + y = z.cross(x) + loc = getVector(getAttr(entity,"Location")) + m = DraftVecUtils.getPlaneRotation(x,y,z) + pl = FreeCAD.Placement(m) + pl.move(loc) + elif entitytype == "IFCLOCALPLACEMENT": + pl = getPlacement(getAttr(entity,"PlacementRelTo")) + relpl = getPlacement(getAttr(entity,"RelativePlacement")) + if pl and relpl: + pl = relpl.multiply(pl) + elif relpl: + pl = relpl + elif entitytype == "IFCCARTESIANPOINT": + loc = getVector(entity) + pl = FreeCAD.Placement() + pl.move(loc) + if DEBUG: print " made placement for ",entityid,":",pl + return pl + +def getAttr(entity,attr): + "returns the given attribute from the given entity" + if IFCOPENSHELL5: + if isinstance(entity,int): + entity = ifc.by_id(entity) + i = entity.get_argument_index(attr) + return entity.get_argument(i) + else: + return getattr(entity,attr) + +def getVector(entity): + "returns a vector from the given entity" + if not entity: + return None + if DEBUG: print " getting point from ",entity + if IFCOPENSHELL5: + if isinstance(entity,int): + entity = ifc.by_id(entity) + entitytype = str(entity).split("=")[1].split("(")[0].upper() + else: + entitytype = entity.type.upper() + if entitytype == "IFCDIRECTION": + DirectionRatios = getAttr(entity,"DirectionRatios") + if len(DirectionRatios) == 3: + return FreeCAD.Vector(tuple(DirectionRatios)) + else: + return FreeCAD.Vector(tuple(DirectionRatios+[0])) + elif entitytype == "IFCCARTESIANPOINT": + Coordinates = getAttr(entity,"Coordinates") + if len(Coordinates) == 3: + return FreeCAD.Vector(tuple(Coordinates)) + else: + return FreeCAD.Vector(tuple(Coordinates+[0])) + return None + +# below is only used by the internal parser ######################################### + +def decode(name): + "decodes encoded strings" + try: + decodedName = (name.decode("utf8")) + except UnicodeDecodeError: + try: + decodedName = (name.decode("latin1")) + except UnicodeDecodeError: + FreeCAD.Console.PrintError(translate("Arch", "Error: Couldn't determine character encoding\n")) + decodedName = name + return decodedName + +def getSchema(): + "retrieves the express schema" + custom = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetString("CustomIfcSchema","") + if custom: + if os.path.exists(custom): + if DEBUG: print "Using custom schema: ",custom.split(os.sep)[-1] + return custom + p = None + p = os.path.join(FreeCAD.ConfigGet("UserAppData"),SCHEMA.split(os.sep)[-1]) + if os.path.exists(p): + return p + import ArchCommands + p = ArchCommands.download(SCHEMA) + if p: + return p + return None + +def group(entity,ifc,mode=None): + "gathers the children of the given entity" + # only used by the internal parser + + try: + if DEBUG: print "=====> making group",entity.id + placement = None + placement = getPlacement(entity.ObjectPlacement) + if DEBUG: print "got cell placement",entity.id,":",placement + subelements = ifc.find("IFCRELCONTAINEDINSPATIALSTRUCTURE","RelatingStructure",entity) + subelements.extend(ifc.find("IFCRELAGGREGATES","RelatingObject",entity)) + elts = [] + for s in subelements: + if hasattr(s,"RelatedElements"): + s = s.RelatedElements + if not isinstance(s,list): s = [s] + elts.extend(s) + elif hasattr(s,"RelatedObjects"): + s = s.RelatedObjects + if not isinstance(s,list): s = [s] + elts.extend(s) + elif hasattr(s,"RelatedObject"): + s = s.RelatedObject + if not isinstance(s,list): s = [s] + elts.extend(s) + print "found dependent elements: ",elts + + groups = [['Wall',['IfcWallStandardCase'],[]], + ['Window',['IfcWindow','IfcDoor'],[]], + ['Structure',['IfcSlab','IfcFooting','IfcBeam','IfcColumn'],[]], + ['Floor',['IfcBuildingStorey'],[]], + ['Building',['IfcBuilding'],[]], + ['Furniture',['IfcFurnishingElement'],[]]] + + for e in elts: + for g in groups: + for t in g[1]: + if e.type.upper() == t.upper(): + if hasattr(FreeCAD.ActiveDocument,g[0]+str(e.id)): + g[2].append(FreeCAD.ActiveDocument.getObject(g[0]+str(e.id))) + print "groups:",groups + + comps = [] + if CREATE_IFC_GROUPS: + if DEBUG: print "creating subgroups" + for g in groups: + if g[2]: + if g[0] in ['Building','Floor']: + comps.extend(g[2]) + else: + fcg = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup",g[0]+"s") + for o in g[2]: + fcg.addObject(o) + comps.append(fcg) + else: + for g in groups: + comps.extend(g[2]) + + label = entity.Name + name = mode + str(entity.id) + cell = None + if mode == "Site": + cell = Arch.makeSite(comps,name=name) + elif mode == "Floor": + cell = Arch.makeFloor(comps,name=name) + elif mode == "Building": + cell = Arch.makeBuilding(comps,name=name) + if label and cell: + cell.Label = label + except: + if DEBUG: print "error: skipping group ",entity.id + +def getWire(entity,placement=None): + "returns a wire (created in the freecad document) from the given entity" + # only used by the internal parser + if DEBUG: print "making Wire from :",entity + if not entity: return None + if entity.type == "IFCPOLYLINE": + pts = [] + for p in entity.Points: + pts.append(getVector(p)) + return Draft.getWire(pts,placement=placement) + elif entity.type == "IFCARBITRARYCLOSEDPROFILEDEF": + pts = [] + for p in entity.OuterCurve.Points: + pts.append(getVector(p)) + return Draft.getWire(pts,closed=True,placement=placement) + + +# EXPORT ########################################################## + +def export(exportList,filename): + "called when freecad exports a file" + global ifcw + ifcw = None + try: + import IfcImport as ifcw + except: + try: + import ifc_wrapper as ifcw + except: + FreeCAD.Console.PrintError(translate("Arch","Error: IfcOpenShell is not installed\n")) + print """importIFC: ifcOpenShell is not installed. IFC export is unavailable. + Note: IFC export currently requires an experimental version of IfcOpenShell + available from https://github.com/aothms/IfcOpenShell""" + return + + if (not hasattr(ifcw,"IfcFile")) and (not hasattr(ifcw,"file")): + FreeCAD.Console.PrintError(translate("Arch","Error: your IfcOpenShell version is too old\n")) + print """importIFC: The version of ifcOpenShell installed on this system doesn't + have IFC export capabilities. IFC export currently requires an experimental + version of IfcOpenShell available from https://github.com/aothms/IfcOpenShell""" + return + import Arch,Draft + + # creating base IFC project + getConfig() + PRECISION = Draft.precision() + p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") + scaling = p.GetFloat("IfcScalingFactor",1.0) + exporttxt = p.GetBool("IfcExportList",False) + forcebrep = p.GetBool("ifcExportAsBrep",False) + application = "FreeCAD" + ver = FreeCAD.Version() + version = ver[0]+"."+ver[1]+" build"+ver[2] + owner = FreeCAD.ActiveDocument.CreatedBy + company = FreeCAD.ActiveDocument.Company + project = FreeCAD.ActiveDocument.Name + ifc = IfcWriter(filename,project,owner,company,application,version) + txt = [] + + # get all children and reorder list to get buildings and floors processed first + objectslist = Draft.getGroupContents(exportList,walls=True,addgroups=True) + objectslist = Arch.pruneIncluded(objectslist) + + sites = [] + buildings = [] + floors = [] + groups = {} + others = [] + for obj in objectslist: + otype = Draft.getType(obj) + if otype == "Site": + sites.append(obj) + elif otype == "Building": + buildings.append(obj) + elif otype == "Floor": + floors.append(obj) + elif otype == "Group": + groups[obj.Name] = [] + else: + others.append(obj) + objectslist = buildings + floors + others + if DEBUG: print "adding ", len(objectslist), " objects" + + global unprocessed + unprocessed = [] + + # process objects + for obj in objectslist: + + otype = Draft.getType(obj) + name = str(obj.Label) + parent = Arch.getHost(obj) + gdata = None + fdata = None + placement = None + color = None + representation = None + descr = None + extra = None + + # setting the IFC type + if hasattr(obj,"Role"): + ifctype = obj.Role.replace(" ","") + else: + ifctype = otype + if ifctype == "Foundation": + ifctype = "Footing" + elif ifctype == "Rebar": + ifctype = "ReinforcingBar" + elif ifctype in ["Part","Undefined"]: + ifctype = "BuildingElementProxy" + + # getting the "Force BREP" flag + brepflag = False + if hasattr(obj,"IfcAttributes"): + if "FlagForceBrep" in obj.IfcAttributes.keys(): + if obj.IfcAttributes["FlagForceBrep"] == "True": + brepflag = True + + if DEBUG: print "Adding " + obj.Label + " as Ifc" + ifctype + + # writing IFC data + if obj.isDerivedFrom("App::DocumentObjectGroup"): + + # getting parent building + if parent: + parent = ifc.findByName("IfcBuilding",str(parent.Label)) + + if otype == "Site": + print " Skipping (not implemented yet)" # TODO manage sites + elif otype == "Building": + ifc.addBuilding( name=name ) + elif otype == "Floor": + ifc.addStorey( building=parent, name=name ) + + elif obj.isDerivedFrom("Part::Feature"): + + # get color + if FreeCAD.GuiUp: + color = obj.ViewObject.ShapeColor[:3] + + # get parent floor + if parent: + parent = ifc.findByName("IfcBuildingStorey",str(parent.Label)) + + # get representation + if (not forcebrep) and (not brepflag): + gdata = getIfcExtrusionData(obj,scaling,SEPARATE_OPENINGS) + #if DEBUG: print " extrusion data for ",obj.Label," : ",gdata + if not gdata: + fdata = getIfcBrepFacesData(obj,scaling) + #if DEBUG: print " brep data for ",obj.Label," : ",fdata + if not fdata: + if obj.isDerivedFrom("Part::Feature"): + print " Error retrieving the shape of object ", obj.Label + unprocessed.append(obj) + continue + else: + if DEBUG: print " No geometry" + else: + if DEBUG: print " Brep" + else: + if DEBUG: print " Extrusion" + if gdata: + # gdata = [ type, profile data, extrusion data, placement data ] + placement = ifc.addPlacement(origin=gdata[3][0],xaxis=gdata[3][1],zaxis=gdata[3][2]) + if gdata[0] == "polyline": + representation = ifc.addExtrudedPolyline(gdata[1], gdata[2], color=color) + elif gdata[0] == "circle": + representation = ifc.addExtrudedCircle(gdata[1], gdata[2], color=color) + elif gdata[0] == "ellipse": + representation = ifc.addExtrudedEllipse(gdata[1], gdata[2], color=color) + elif gdata[0] == "composite": + representation = ifc.addExtrudedCompositeCurve(gdata[1], gdata[2], color=color) + else: + print "debug: unknow extrusion type" + elif fdata: + representation = [ifc.addFacetedBrep(f, color=color) for f in fdata] + + # create ifc object + ifctype = "Ifc" + ifctype + if hasattr(obj,"Description"): + descr = obj.Description + if otype == "Wall": + if gdata: + if gdata[0] == "polyline": + ifctype = "IfcWallStandardCase" + elif otype == "Structure": + if ifctype in ["IfcSlab","IfcFooting"]: + extra = ["NOTDEFINED"] + elif otype == "Window": + extra = [obj.Width.Value*scaling, obj.Height.Value*scaling] + elif otype == "Space": + extra = ["ELEMENT","INTERNAL",getIfcElevation(obj)] + elif otype == "Part": + extra = ["ELEMENT"] + if not ifctype in supportedIfcTypes: + if DEBUG: print " Type ",ifctype," is not supported yet. Exporting as IfcBuildingElementProxy instead" + ifctype = "IfcBuildingElementProxy" + extra = ["ELEMENT"] + + product = ifc.addProduct( ifctype, representation, storey=parent, placement=placement, name=name, description=descr, extra=extra ) + + if product: + # removing openings + if SEPARATE_OPENINGS and gdata: + for o in obj.Subtractions: + print "Subtracting ",o.Label + fdata = getIfcBrepFacesData(o,scaling,sub=True) + representation = [ifc.addFacetedBrep(f, color=color) for f in fdata] + p2 = ifc.addProduct( "IfcOpeningElement", representation, storey=product, placement=None, name=str(o.Label), description=None) + + # writing text log + spacer = "" + for i in range(36-len(obj.Label)): + spacer += " " + txt.append(obj.Label + spacer + ifctype) + + # adding object to group, if any + for g in groups.keys(): + group = FreeCAD.ActiveDocument.getObject(g) + if group: + for o in group.Group: + if o.Name == obj.Name: + groups[g].append(product) + + else: + unprocessed.append(obj) + else: + if DEBUG: print "Object type ", otype, " is not supported yet." + + # processing groups + for name,entities in groups.iteritems(): + if entities: + o = FreeCAD.ActiveDocument.getObject(name) + if o: + if DEBUG: print "Adding group ", o.Label, " with ",len(entities)," elements" + grp = ifc.addGroup( entities, o.Label ) + + ifc.write() + + if exporttxt: + import time, os + txtstring = "List of objects exported by FreeCAD in file\n" + txtstring += filename + "\n" + txtstring += "On " + time.ctime() + "\n" + txtstring += "\n" + txtstring += str(len(txt)) + " objects exported:\n" + txtstring += "\n" + txtstring += "Nr Name Type\n" + txtstring += "\n" + for i in range(len(txt)): + idx = str(i+1) + sp = "" + for j in range(8-len(idx)): + sp += " " + txtstring += idx + sp + txt[i] + "\n" + txtfile = os.path.splitext(filename)[0]+".txt" + f = pyopen(txtfile,"wb") + f.write(txtstring) + f.close() + + FreeCAD.ActiveDocument.recompute() + + if unprocessed: + print "" + print "WARNING: " + str(len(unprocessed)) + " objects were not exported (stored in importIFC.unprocessed):" + for o in unprocessed: + print " " + o.Label + + +def getTuples(data,scale=1,placement=None,normal=None,close=True): + """getTuples(data,[scale,placement,normal,close]): returns a tuple or a list of tuples from a vector + or from the vertices of a shape. Scale can indicate a scale factor""" + rnd = False + import Part + if isinstance(data,FreeCAD.Vector): + if placement: + data = placement.multVec(data) + if rnd: + data = DraftVecUtils.rounded(data) + return (data.x*scale,data.y*scale,data.z*scale) + elif isinstance(data,Part.Shape): + t = [] + if len(data.Wires) == 1: + import Part,DraftGeomUtils + data = Part.Wire(DraftGeomUtils.sortEdges(data.Wires[0].Edges)) + verts = data.Vertexes + try: + c = data.CenterOfMass + v1 = verts[0].Point.sub(c) + v2 = verts[1].Point.sub(c) + if DraftVecUtils.angle(v2,v1,normal) >= 0: + # inverting verts order if the direction is couterclockwise + verts.reverse() + except: + pass + for v in verts: + pt = v.Point + if placement: + if not placement.isNull(): + pt = placement.multVec(pt) + if rnd: + pt = DraftVecUtils.rounded(pt) + t.append((pt.x*scale,pt.y*scale,pt.z*scale)) + + if close: # faceloops must not be closed, but ifc profiles must. + t.append(t[0]) + else: + print "Arch.getTuples(): Wrong profile data" + return t + +def getIfcExtrusionData(obj,scale=1,nosubs=False): + """getIfcExtrusionData(obj,[scale,nosubs]): returns a closed path (a list of tuples), a tuple expressing an extrusion + vector, and a list of 3 tuples for base position, x axis and z axis. Or returns None, if a base loop and + an extrusion direction cannot be extracted. Scale can indicate a scale factor.""" + + CURVEMODE = "PARAMETER" # For trimmed curves. CARTESIAN or PARAMETER + + if hasattr(obj,"Additions"): + if obj.Additions: + # TODO provisorily treat objs with additions as breps + return None + if hasattr(obj,"Subtractions") and not nosubs: + if obj.Subtractions: + return None + if hasattr(obj,"Proxy"): + if hasattr(obj.Proxy,"getProfiles"): + p = obj.Proxy.getProfiles(obj,noplacement=True) + v = obj.Proxy.getExtrusionVector(obj,noplacement=True) + if (len(p) == 1) and v: + p = p[0] + r = FreeCAD.Placement() + #b = p.CenterOfMass + r = obj.Proxy.getPlacement(obj) + #b = obj.Placement.multVec(FreeCAD.Vector()) + #r.Rotation = DraftVecUtils.getRotation(v,FreeCAD.Vector(0,0,1)) + d = [r.Base,DraftVecUtils.rounded(r.Rotation.multVec(FreeCAD.Vector(1,0,0))),DraftVecUtils.rounded(r.Rotation.multVec(FreeCAD.Vector(0,0,1)))] + #r = r.inverse() + #print "getExtrusionData: computed placement:",r + import Part + if len(p.Edges) == 1: + if isinstance(p.Edges[0].Curve,Part.Circle): + # Circle profile + r1 = p.Edges[0].Curve.Radius*scale + return "circle", [getTuples(p.Edges[0].Curve.Center,scale), r1], getTuples(v,scale), d + elif isinstance(p.Edges[0].Curve,Part.Ellipse): + # Ellipse profile + r1 = p.Edges[0].Curve.MajorRadius*scale + r2 = p.Edges[0].Curve.MinorRadius*scale + return "ellipse", [getTuples(p.Edges[0].Curve.Center,scale), r1, r2], getTuples(v,scale), d + curves = False + for e in p.Edges: + if isinstance(e.Curve,Part.Circle): + curves = True + elif not isinstance(e.Curve,Part.Line): + print "Arch.getIfcExtrusionData: Warning: unsupported edge type in profile" + if curves: + # Composite profile + ecurves = [] + last = None + import DraftGeomUtils + edges = DraftGeomUtils.sortEdges(p.Edges) + for e in edges: + if isinstance(e.Curve,Part.Circle): + import math + follow = True + if last: + if not DraftVecUtils.equals(last,e.Vertexes[0].Point): + follow = False + last = e.Vertexes[0].Point + else: + last = e.Vertexes[-1].Point + else: + last = e.Vertexes[-1].Point + p1 = math.degrees(-DraftVecUtils.angle(e.Vertexes[0].Point.sub(e.Curve.Center))) + p2 = math.degrees(-DraftVecUtils.angle(e.Vertexes[-1].Point.sub(e.Curve.Center))) + da = DraftVecUtils.angle(e.valueAt(e.FirstParameter+0.1).sub(e.Curve.Center),e.Vertexes[0].Point.sub(e.Curve.Center)) + if p1 < 0: + p1 = 360 + p1 + if p2 < 0: + p2 = 360 + p2 + if da > 0: + follow = not(follow) + if CURVEMODE == "CARTESIAN": + # BUGGY + p1 = getTuples(e.Vertexes[0].Point,scale) + p2 = getTuples(e.Vertexes[-1].Point,scale) + ecurves.append(["arc",getTuples(e.Curve.Center,scale),e.Curve.Radius*scale,[p1,p2],follow,CURVEMODE]) + else: + verts = [vertex.Point for vertex in e.Vertexes] + if last: + if not DraftVecUtils.equals(last,verts[0]): + verts.reverse() + last = e.Vertexes[0].Point + else: + last = e.Vertexes[-1].Point + else: + last = e.Vertexes[-1].Point + ecurves.append(["line",[getTuples(vert,scale) for vert in verts]]) + return "composite", ecurves, getTuples(v,scale), d + else: + # Polyline profile + return "polyline", getTuples(p,scale), getTuples(v,scale), d + return None + +def getIfcBrepFacesData(obj,scale=1,sub=False,tessellation=1): + """getIfcBrepFacesData(obj,[scale,tesselation]): returns a list(0) of lists(1) of lists(2) of lists(3), + list(3) being a list of vertices defining a loop, list(2) describing a face from one or + more loops, list(1) being the whole solid made of several faces, list(0) being the list + of solids inside the object. Scale can indicate a scaling factor. Tesselation is the tesselation + factor to apply on curved faces.""" + shape = None + if sub: + if hasattr(obj,"Proxy"): + if hasattr(obj.Proxy,"getSubVolume"): + shape = obj.Proxy.getSubVolume(obj) + if not shape: + if hasattr(obj,"Shape"): + if obj.Shape: + if not obj.Shape.isNull(): + #if obj.Shape.isValid(): + shape = obj.Shape + elif hasattr(obj,"Terrain"): + if obj.Terrain: + if hasattr(obj.Terrain,"Shape"): + if obj.Terrain.Shape: + if not obj.Terrain.Shape.isNull(): + if obj.Terrain.Shape.isValid(): + shape = obj.Terrain.Shape + if shape: + import Part + sols = [] + if shape.Solids: + dataset = shape.Solids + else: + dataset = shape.Shells + print "Warning! object contains no solids" + for sol in shape.Solids: + s = [] + curves = False + for face in sol.Faces: + for e in face.Edges: + if not isinstance(e.Curve,Part.Line): + curves = True + if curves: + tris = sol.tessellate(tessellation) + for tri in tris[1]: + f = [] + for i in tri: + f.append(getTuples(tris[0][i],scale)) + s.append([f]) + else: + for face in sol.Faces: + f = [] + f.append(getTuples(face.OuterWire,scale,normal=face.normalAt(0,0),close=False)) + for wire in face.Wires: + if wire.hashCode() != face.OuterWire.hashCode(): + f.append(getTuples(wire,scale,normal=DraftVecUtils.neg(face.normalAt(0,0)),close=False)) + s.append(f) + sols.append(s) + return sols + return None + +def getIfcElevation(obj): + """getIfcElevation(obj): Returns the lowest height (Z coordinate) of this object""" + if obj.isDerivedFrom("Part::Feature"): + b = obj.Shape.BoundBox + return b.ZMin + return 0 + + +def explore(filename=None): + "explore the contents of an ifc file in a Qt dialog" + if not filename: + from PySide import QtGui + filename = QtGui.QFileDialog.getOpenFileName(QtGui.qApp.activeWindow(),'IFC files','*.ifc') + if filename: + filename = filename[0] + if filename: + getConfig() + schema=getSchema() + d = explorer(filename,schema) + d.show() + return d + +# IfcReader ############################################# + +class IfcSchema: + SIMPLETYPES = ["INTEGER", "REAL", "STRING", "NUMBER", "LOGICAL", "BOOLEAN"] + NO_ATTR = ["WHERE", "INVERSE","WR2","WR3", "WR4", "WR5", "UNIQUE", "DERIVE"] + + def __init__(self, filename): + self.filename = filename + if not os.path.exists(filename): + raise ImportError("no IFCSchema file found!") + else: + self.file = open(self.filename) + self.data = self.file.read() + self.types = self.readTypes() + self.entities = self.readEntities() + if DEBUG: print "Parsed from schema %s: %s entities and %s types" % (self.filename, len(self.entities), len(self.types)) + + def readTypes(self): + """ + Parse all the possible types from the schema, + returns a dictionary Name -> Type + """ + types = {} + for m in re.finditer("TYPE (.*) = (.*);", self.data): + typename, typetype = m.groups() + if typetype in self.SIMPLETYPES: + types[typename] = typetype + else: + types[typename] = "#" + typetype + + return types + + def readEntities(self): + """ + Parse all the possible entities from the schema, + returns a dictionary of the form: + { name: { + "supertype": supertype, + "attributes": [{ key: value }, ..] + }} + """ + entities = {} + + # Regexes must be greedy to prevent matching outer entity and end_entity strings + # Regexes have re.DOTALL to match newlines + for m in re.finditer("ENTITY (.*?)END_ENTITY;", self.data, re.DOTALL): + entity = {} + raw_entity_str = m.groups()[0] + + entity["name"] = re.search("(.*?)[;|\s]", raw_entity_str).groups()[0].upper() + + subtypeofmatch = re.search(".*SUBTYPE OF \((.*?)\);", raw_entity_str) + entity["supertype"] = subtypeofmatch.groups()[0].upper() if subtypeofmatch else None + + # find the shortest string matched from the end of the entity type header to the + # first occurence of a NO_ATTR string (when it occurs on a new line) + inner_str = re.search(";(.*?)$", raw_entity_str, re.DOTALL).groups()[0] + + attrs_str = min([inner_str.partition("\r\n "+a)[0] for a in self.NO_ATTR]) + attrs = [] + for am in re.finditer("(.*?) : (.*?);", attrs_str, re.DOTALL): + name, attr_type = [s.replace("\r\n\t","") for s in am.groups()] + attrs.append((name, attr_type)) + + entity["attributes"] = attrs + entities[entity["name"]] = entity + + + return entities + + def getAttributes(self, name): + """ + Get all attributes af an entity, including supertypes + """ + ent = self.entities[name] + + attrs = [] + while ent != None: + this_ent_attrs = copy.copy(ent["attributes"]) + this_ent_attrs.reverse() + attrs.extend(this_ent_attrs) + ent = self.entities.get(ent["supertype"], None) + + attrs.reverse() + return attrs + + def capitalize(self, name): + "returns a capitalized version of a type" + if name.upper() in self.data.upper(): + i1 = self.data.upper().index(name.upper()) + i2 = i1 + len(name) + name = self.data[i1:i2] + return name + +class IfcFile: + """ + Parses an ifc file given by filename, entities can be retrieved by name and id + The whole file is stored in a dictionary (in memory) + """ + + entsById = {} + entsByName = {} + + def __init__(self, filename,schema): + self.filename = filename + self.schema = IfcSchema(schema) + self.file = open(self.filename) + self.entById, self.entsByName, self.header = self.read() + self.file.close() + if DEBUG: print "Parsed from file %s: %s entities" % (self.filename, len(self.entById)) + + def getEntityById(self, id): + return self.entById.get(id, None) + + def getEntitiesByName(self, name): + return self.entsByName.get(name, None) + + def read(self): + """ + Returns 2 dictionaries, entById and entsByName + """ + entById = {} + entsByName = {} + header = 'HEADER ' + readheader = False + for line in self.file: + e = self.parseLine(line) + if e: + entById[int(e["id"])] = e + ids = e.get(e["name"],[]) + ids.append(e["id"]) + entsByName[e["name"]] = list(set(ids)) + elif 'HEADER' in line: + readheader = True + elif readheader: + if 'ENDSEC' in line: + readheader = False + else: + header += line + + return [entById, entsByName, header] + + def parseLine(self, line): + """ + Parse a line + """ + m = IFCLINE_RE.search(line) # id,name,attrs + if m: + id, name, attrs = m.groups() + id = id.strip() + name = name.strip() + attrs = attrs.strip() + else: + return False + + return {"id": id, "name": name, "attributes": self.parseAttributes(name, attrs)} + + def parseAttributes(self, ent_name, attrs_str): + """ + Parse the attributes of a line + """ + parts = [] + lastpos = 0 + + while lastpos < len(attrs_str): + newpos = self.nextString(attrs_str, lastpos) + parts.extend(self.parseAttribute(attrs_str[lastpos:newpos-1])) + lastpos = newpos + + schema_attributes = self.schema.getAttributes(ent_name) + + assert len(schema_attributes) == len(parts), \ + "Expected %s attributes, got %s (entity: %s" % \ + (len(schema_attributes), len(parts), ent_name) + + attribute_names = [a[0] for a in schema_attributes] + + return dict(zip(attribute_names, parts)) + + def parseAttribute(self, attr_str): + """ + Map a single attribute to a python type (recursively) + """ + parts = [] + lastpos = 0 + while lastpos < len(attr_str): + newpos = self.nextString(attr_str, lastpos) + s = attr_str[lastpos:newpos-1] + if (s[0] == "(" and s[-1] == ")"): # list, recurse + parts.append(self.parseAttribute(s[1:-1])) + else: + try: + parts.append(float(s)) # number, any kind + except ValueError: + if s[0] == "'" and s[-1] == "'": # string + parts.append(s[1:-1]) + elif s == "$": + parts.append(None) + else: + parts.append(s) # ref, enum or other + + lastpos = newpos + + return parts + + + def nextString(self, s, start): + """ + Parse the data part of a line + """ + parens = 0 + quotes = 0 + + for pos in range(start,len(s)): + c = s[pos] + if c == "," and parens == 0 and quotes == 0: + return pos+1 + elif c == "(" and quotes == 0: + parens += 1 + elif c == ")" and quotes == 0: + parens -= 1 + elif c == "\'" and quotes == 0: + quotes = 1 + elif c =="\'" and quotes == 1: + quotes = 0 + + return len(s)+1 + +class IfcEntity: + "a container for an IFC entity" + def __init__(self,ent,doc=None): + self.data = ent + self.id = int(ent['id']) + self.type = ent['name'].upper().strip(",[]()") + self.attributes = ent['attributes'] + self.doc = doc + + def __repr__(self): + return str(self.id) + ' : ' + self.type + ' ' + str(self.attributes) + + def getProperties(self): + return self.doc.find('IFCRELDEFINESBYPROPERTIES','RelatedObjects',self) + + def getProperty(self,propName): + "finds the value of the given property or quantity in this object, if exists" + propsets = self.doc.find('IFCRELDEFINESBYPROPERTIES','RelatedObjects',self) + if not propsets: return None + propset = [] + for p in propsets: + if hasattr(p.RelatingPropertyDefinition,"HasProperties"): + propset.extend(p.RelatingPropertyDefinition.HasProperties) + elif hasattr(p.RelatingPropertyDefinition,"Quantities"): + propset.extend(p.RelatingPropertyDefinition.Quantities) + for prop in propset: + if prop.Name == propName: + print "found valid",prop + if hasattr(prop,"LengthValue"): + return prop.LengthValue + elif hasattr(prop,"AreaValue"): + return prop.AreaValue + elif hasattr(prop,"VolumeValue"): + return prop.VolumeValue + elif hasattr(prop,"NominalValue"): + return prop.NominalValue + return None + + def getAttribute(self,attr): + "returns the value of the given attribute, if exists" + if hasattr(self,attr): + return self.__dict__[attr] + return None + +class IfcDocument: + "an object representing an IFC document" + def __init__(self,filename,schema="IFC2X3_TC1.exp"): + f = IfcFile(filename,schema) + self.filename = filename + self.data = f.entById + self.Entities = {0:f.header} + for k,e in self.data.iteritems(): + eid = int(e['id']) + self.Entities[eid] = IfcEntity(e,self) + if DEBUG: print len(self.Entities),"entities created. Creating attributes..." + for k,ent in self.Entities.iteritems(): + if DEBUG: print "attributing entity ",ent + if hasattr(ent,"attributes"): + for k,v in ent.attributes.iteritems(): + if DEBUG: print "parsing attribute: ",k," value ",v + if isinstance(v,str): + val = self.__clean__(v) + elif isinstance(v,list): + val = [] + for item in v: + if isinstance(item,str): + val.append(self.__clean__(item)) + else: + val.append(item) + else: + val = v + setattr(ent,k.strip(),val) + if DEBUG: print "Document successfully created" + + def __clean__(self,value): + "turns an attribute value into something usable" + try: + val = value.strip(" ()'") + if val[:3].upper() == "IFC": + if "IFCTEXT" in val.upper(): + l = val.split("'") + if len(l) == 3: val = l[1] + elif "IFCBOOLEAN" in value.upper(): + l = val.split(".") + if len(l) == 3: val = l[1] + if val.upper() == "F": val = False + elif val.upper() == "T": val = True + elif "IFCREAL" in val.upper(): + l = val.split("(") + if len(l) == 2: val = float(l[1].strip(")")) + else: + if '#' in val: + if "," in val: + val = val.split(",") + l = [] + for subval in val: + if '#' in subval: + s = subval.strip(" #") + if DEBUG: print "referencing ",s," : ",self.getEnt(int(s)) + l.append(self.getEnt(int(s))) + val = l + else: + val = val.strip() + val = val.replace("#","") + if DEBUG: print "referencing ",val," : ",self.getEnt(int(val)) + val = self.getEnt(int(val)) + if not val: + val = value + except: + if DEBUG: print "error parsing attribute",value + val = value + return val + + def __repr__(self): + return "IFC Document: " + self.filename + ', ' + str(len(self.Entities)) + " entities " + + def getEnt(self,ref): + "gets an entity by id number, or a list of entities by type" + if isinstance(ref,int): + if ref in self.Entities: + return self.Entities[ref] + elif isinstance(ref,str): + l = [] + ref = ref.upper() + for k,ob in self.Entities.iteritems(): + if hasattr(ob,"type"): + if ob.type == ref: + l.append(ob) + return l + return None + + def search(self,pat): + "searches entities types for partial match" + l = [] + pat = pat.upper() + for k,ob in self.Entities.iteritems(): + if hasattr(ob,"type"): + if pat in ob.type: + if not ob.type in l: + l.append(ob.type) + return l + + def find(self,pat1,pat2=None,pat3=None): + '''finds objects in the current IFC document. + arguments can be of the following form: + - (pattern): returns object types matching the given pattern (same as search) + - (type,property,value): finds, in all objects of type "type", those whose + property "property" has the given value + ''' + if pat3: + bobs = self.getEnt(pat1) + obs = [] + for bob in bobs: + if hasattr(bob,pat2): + if bob.getAttribute(pat2) == pat3: + obs.append(bob) + return obs + elif pat1: + ll = self.search(pat1) + obs = [] + for l in ll: + obs.extend(self.getEnt(l)) + return obs + return None + +def explorer(filename,schema="IFC2X3_TC1.exp"): + "returns a PySide dialog showing the contents of an IFC file" + from PySide import QtCore,QtGui + ifc = IfcDocument(filename,schema) + schema = IfcSchema(schema) + tree = QtGui.QTreeWidget() + tree.setColumnCount(3) + tree.setWordWrap(True) + tree.header().setDefaultSectionSize(60) + tree.header().resizeSection(0,60) + tree.header().resizeSection(1,30) + tree.header().setStretchLastSection(True) + tree.headerItem().setText(0, "ID") + tree.headerItem().setText(1, "") + tree.headerItem().setText(2, "Item and Properties") + bold = QtGui.QFont() + bold.setWeight(75) + bold.setBold(True) + + #print ifc.Entities + + for i in ifc.Entities.keys(): + e = ifc.Entities[i] + item = QtGui.QTreeWidgetItem(tree) + if hasattr(e,"id"): + item.setText(0,str(e.id)) + if e.type in ["IFCWALL","IFCWALLSTANDARDCASE"]: + item.setIcon(1,QtGui.QIcon(":icons/Arch_Wall_Tree.svg")) + elif e.type in ["IFCCOLUMN","IFCBEAM","IFCSLAB","IFCFOOTING"]: + item.setIcon(1,QtGui.QIcon(":icons/Arch_Structure_Tree.svg")) + elif e.type in ["IFCSITE"]: + item.setIcon(1,QtGui.QIcon(":icons/Arch_Site_Tree.svg")) + elif e.type in ["IFCBUILDING"]: + item.setIcon(1,QtGui.QIcon(":icons/Arch_Building_Tree.svg")) + elif e.type in ["IFCSTOREY"]: + item.setIcon(1,QtGui.QIcon(":icons/Arch_Floor_Tree.svg")) + elif e.type in ["IFCWINDOW"]: + item.setIcon(1,QtGui.QIcon(":icons/Arch_Window_Tree.svg")) + elif e.type in ["IFCROOF"]: + item.setIcon(1,QtGui.QIcon(":icons/Arch_Roof_Tree.svg")) + elif e.type in ["IFCEXTRUDEDAREASOLID","IFCCLOSEDSHELL"]: + item.setIcon(1,QtGui.QIcon(":icons/Tree_Part.svg")) + elif e.type in ["IFCFACE"]: + item.setIcon(1,QtGui.QIcon(":icons/Draft_SwitchMode.svg")) + elif e.type in ["IFCARBITRARYCLOSEDPROFILEDEF","IFCPOLYLOOP"]: + item.setIcon(1,QtGui.QIcon(":icons/Draft_Draft.svg")) + item.setText(2,str(schema.capitalize(e.type))) + item.setFont(2,bold); + for a in e.attributes.keys(): + if hasattr(e,a): + if not a.upper() in ["ID", "GLOBALID"]: + v = getattr(e,a) + if isinstance(v,IfcEntity): + t = "Entity #" + str(v.id) + ": " + str(v.type) + elif isinstance(v,list): + t = "" + else: + t = str(v) + t = " " + str(a) + " : " + str(t) + item = QtGui.QTreeWidgetItem(tree) + item.setText(2,str(t)) + if isinstance(v,list): + for vi in v: + if isinstance(vi,IfcEntity): + t = "Entity #" + str(vi.id) + ": " + str(vi.type) + else: + t = vi + t = " " + str(t) + item = QtGui.QTreeWidgetItem(tree) + item.setText(2,str(t)) + d = QtGui.QDialog() + d.setObjectName("IfcExplorer") + d.setWindowTitle("Ifc Explorer") + d.resize(640, 480) + layout = QtGui.QVBoxLayout(d) + layout.addWidget(tree) + return d + +# IfcWriter ######################################## + +class _tempEntityHolder: + """a temporary object to store entity references + to be made into something nicer later...""" + def __init__(self): + self.refs = [] + +holder = _tempEntityHolder() + +def uid(): + """returns a suitable GlobalID""" + u = str(uuid.uuid4())[:22] + u = u.replace("-","_") + return u + +def now(string=False): + "returns a suitable Ifc Time" + if string: + return time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()) + else: + return int(time.time()) + +def getPropertyNames(entity): + """getPropertyNames(entity): Returns a dictionary with + the numbers and names of the pythonproperties available for + this entity""" + ents = {} + if hasattr(entity,"get_argument_count"): + l = entity.get_argument_count() + else: + l = len(entity) + for i in range(l): + ents[i] = entity.get_argument_name(i) + return ents + +def getTuple(vec): + """getTuple(vec): returns a tuple from other coordinate + structures: tuple, list, 3d vector, or occ vertex""" + def fmt(t): + t = float(t) + t = round(t,PRECISION) + return t + if isinstance(vec,tuple): + return tuple([fmt(v) for v in vec]) + elif isinstance(vec,list): + return tuple([fmt(v) for v in vec]) + elif hasattr(vec,"x") and hasattr(vec,"y") and hasattr(vec,"z"): + return (fmt(vec.x),fmt(vec.y),fmt(vec.z)) + elif hasattr(vec,"X") and hasattr(vec,"Y") and hasattr(vec,"Z"): + return (fmt(vec.X),fmt(vec.Y),fmt(vec.Z)) + +def getValueAndDirection(vec): + """getValueAndDirection(vec): returns a length and a tuple + representing a normalized vector from a tuple""" + vec = getTuple(vec) + length = round(math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2),PRECISION) + ratio = 1/length + x = round(vec[0]*ratio,PRECISION) + y = round(vec[1]*ratio,PRECISION) + z = round(vec[2]*ratio,PRECISION) + normal = (x,y,z) + return length,normal + +def create(ifcdoc=None,ifcname=None,arguments=[]): + """create(ifcdoc,ifcname,[arguments]):creates an entity + of the given name in the given document and optionally + gives it an ordered list of arguments""" + if hasattr(ifcw,"Entity"): + entity = ifcw.Entity(ifcname) + else: + entity = ifcw.entity_instance(ifcname) + if ifcdoc: + ifcdoc.add(entity) + # this is a temporary hack while ifcopenshell has no ref counting + holder.refs.append(entity) + if not isinstance(arguments,list): + arguments = [arguments] + for i in range(len(arguments)): + arg = arguments[i] + if isinstance(arg,tuple): + if len(arg) in [2,3]: + if hasattr(ifcw,"Doubles"): + arg = ifcw.Doubles(arg) + else: + arg = ifcw.doubles(arg) + entity.set_argument(i,arg) + return entity + + +class IfcWriter(object): + """IfcWriter([filepath,name,owner,organization,application,version]) + Creates an empty IFC document.""" + + def __init__(self,filepath="",name="",owner="",organization="",application="Python IFC exporter",version="0.0"): + if hasattr(ifcw,"IfcFile"): + self._fileobject = ifcw.IfcFile() + else: + self._fileobject = ifcw.file() + self._person = create(self._fileobject,"IfcPerson",[None,None,"",None,None,None,None,None]) + self._org = create(self._fileobject,"IfcOrganization",[None,"",None,None,None]) + pno = create(self._fileobject,"IfcPersonAndOrganization",[self._person,self._org,None]) + app = create(self._fileobject,"IfcApplication",[self._org,version,application,uid()]) + self._owner = create(self._fileobject,"IfcOwnerHistory",[pno,app,None,"ADDED",None,pno,app,now()]) + axp = self.addPlacement(local=False) + dim0 = create(self._fileobject,"IfcDirection",getTuple((0,1,0))) + self._repcontext = create(self._fileobject,"IfcGeometricRepresentationContext",['Plan','Model',3,1.E-05,axp,dim0]) + dim1 = create(self._fileobject,"IfcDimensionalExponents",[0,0,0,0,0,0,0]) + dim2 = create(self._fileobject,"IfcSIUnit",[dim1,"LENGTHUNIT","MILLI","METRE"]) + dim3 = create(self._fileobject,"IfcSIUnit",[dim1,"AREAUNIT",None,"SQUARE_METRE"]) + dim4 = create(self._fileobject,"IfcSIUnit",[dim1,"VOLUMEUNIT",None,"CUBIC_METRE"]) + dim6 = create(self._fileobject,"IfcSIUnit",[dim1,"PLANEANGLEUNIT",None,"RADIAN"]) + dim7 = create(None,"IfcPlaneAngleMeasure",[1.745E-2]) + dim8 = create(self._fileobject,"IfcMeasureWithUnit",[dim7,dim6]) + dim9 = create(self._fileobject,"IfcConversionBasedUnit",[dim1,"PLANEANGLEUNIT","DEGREE",dim8]) + units = create(self._fileobject,"IfcUnitAssignment",[[dim2,dim3,dim4,dim9]]) + self.Project = create(self._fileobject,"IfcProject",[uid(),self._owner,None,None,None,None,None,[self._repcontext],units]) + self.Site = None + self._storeyRelations = {} + self.BuildingProducts = [] + self.Storeys = [] + self.Buildings = [] + self.FilePath = filepath + self.Owner = owner + self.Organization = organization + self.Name = name + + def __repr__(self): + return "IFC document " + self.Name #+ " containing " + str(len(holder)) + " entities" + + def __setattr__(self,key,value): + if value: + if key == "Owner": + self._person.set_argument(2,str(value)) + elif key == "Organization": + self._org.set_argument(1,str(value)) + elif key == "Name": + self.Project.set_argument(2,str(value)) + self.__dict__.__setitem__(key,value) + + def findByName(self,ifctype,name): + "finds an entity of a given ifctype by name" + objs = self._fileobject.by_type(ifctype) + for obj in objs: + if hasattr(obj,"get_argument_count"): + l = obj.get_argument_count() + else: + l = len(obj) + for i in range(l): + if obj.get_argument_name(i) == "Name": + if obj.get_argument(i) == name: + return obj + return None + + def write(self,fp=None): + "writes the document to its file" + if fp: + path = fp + else: + path = self.FilePath + if path: + try: + self._fileobject.write(path) + if APPLYFIX: + print ("IfcWriter: Applying fix...") + self._fix(path) + except: + print ("IfcWriter: Error writing to "+path) + else: + print ("IfcWriter: Successfully written to "+path) + else: + print ("IfcWriter: Error: File path is not defined, unable to save") + + def _fix(self,path): + "hack to fix early bugs in ifcopenshell" + import os + if os.path.exists(path): + f = pyopen(path,"rb") + lines = [] + for l in f.readlines(): + if "(=IFC" in l: + # adding an ifc entity without ID adds an unwanted = sign + l = l.replace("(=IFC","(IFC") + elif "IFCSIUNIT" in l: + # no way to insert * character + l = l.replace("IFCSIUNIT(#12,","IFCSIUNIT(*,") + lines.append(l) + f.close() + f = pyopen(path,"wb") + for l in lines: + f.write(l) + f.close() + + def union(self,solids): + """union(solids): creates a boolean union between all the solids of the list""" + if len(solids) == 1: + return solids[0] + else: + s1 = solids.pop(0) + s2 = solids.pop(0) + base = create(self._fileobject,"IfcBooleanResult",["UNION",s1,s2]) + for s in solids: + base = create(self._fileobject,"IfcBooleanResult",["UNION",base,s]) + return base + + def addPlacement(self,reference=None,origin=(0,0,0),xaxis=(1,0,0),zaxis=(0,0,1),local=True,flat=False): + """addPlacement([reference,origin,xaxis,zaxis,local]): adds a placement. origin, + xaxis and zaxis can be either tuples or 3d vectors. If local is False, a global + placement is returned, otherwise a local one.""" + if flat: + xvc = create(self._fileobject,"IfcDirection",getTuple(xaxis)[:2]) + ovc = create(self._fileobject,"IfcCartesianPoint",getTuple(origin)[:2]) + gpl = create(self._fileobject,"IfcAxis2Placement2D",[ovc,xvc]) + else: + xvc = create(self._fileobject,"IfcDirection",getTuple(xaxis)) + zvc = create(self._fileobject,"IfcDirection",getTuple(zaxis)) + ovc = create(self._fileobject,"IfcCartesianPoint",getTuple(origin)) + gpl = create(self._fileobject,"IfcAxis2Placement3D",[ovc,zvc,xvc]) + if local: + lpl = create(self._fileobject,"IfcLocalPlacement",[reference,gpl]) + return lpl + else: + return gpl + + def addSite(self,placement=None,name="Site",description=None,latitude=None,longitude=None,elevation=None,landtitlenumber=None,address=None): + """makeSite(ifcdoc,project,owner,[placement,name,description]): creates a site + in the given ifc document""" + if self.Site: + return + if not placement: + placement = self.addPlacement() + self.Site = create(self._fileobject,"IfcSite",[uid(),self._owner,str(name),description,None,placement,None,None,"ELEMENT",latitude,longitude,elevation,landtitlenumber,address]) + self._relate(self.Project,self.Site) + + def addBuilding(self,placement=None,name="Default building",description=None): + """addBuilding([placement,name,description]): adds a building""" + if not placement: + placement = self.addPlacement() + if not self.Site: + self.addSite() + bdg = create(self._fileobject,"IfcBuilding",[uid(),self._owner,str(name),description,None,placement,None,None,"ELEMENT",None,None,None]) + self._relate(self.Site,bdg) + self.Buildings.append(bdg) + return bdg + + def addStorey(self,building=None,placement=None,name="Default storey",description=None): + """addStorey([building,placement,name,description]): adds a storey""" + if not placement: + placement = self.addPlacement() + sto = create(self._fileobject,"IfcBuildingStorey",[uid(),self._owner,str(name),description,None,placement,None,None,"ELEMENT",None]) + if not building: + if self.Buildings: + building = self.Buildings[0] + else: + building = self.addBuilding() + self._relate(building,sto) + self.Storeys.append(sto) + return sto + + def addGroup(self,entities,name="Default group",description=None): + """addGroup(entities,[name,description]): adds a group with the given entities""" + if not isinstance(entities,list): + entities = [entities] + gro = create(self._fileobject,"IfcGroup",[uid(),self._owner,str(name),description,None]) + rel = create(self._fileobject,"IfcRelAssignsToGroup",[uid(),self._owner,str(name)+"-relation",None,entities,"PRODUCT",gro]) + return gro + + def _relate(self,container,entities): + """relate(container,entities): relates the given entities to the given + container""" + if not isinstance(entities,list): + entities = [entities] + if container.is_a("IfcBuildingStorey"): + sid = container.get_argument(0) + if sid in self._storeyRelations: + prods = self._storeyRelations[sid].get_argument(4) + self._storeyRelations[sid].set_argument(4,prods+entities) + else: + rel = create(self._fileobject,"IfcRelContainedInSpatialStructure",[uid(),self._owner,'StoreyLink','',entities,container]) + self._storeyRelations[sid] = rel + else: + if entities[0].is_a("IfcOpeningElement"): + create(self._fileobject,"IfcRelVoidsElement",[uid(),self._owner,'Opening','',container,entities[0]]) + else: + create(self._fileobject,"IfcRelAggregates",[uid(),self._owner,'Relationship','',container,entities]) + + def addProduct(self,elttype,shapes,storey=None,placement=None,name="Unnamed element",description=None,extra=None): + """addProduct(elttype,representations,[storey,placement,name,description,extra]): creates an element of the given type + (IfcWall, IfcBeam, etc...) with the given attributes, plus the given extra attributes.""" + elttype = str(elttype) + if not extra: + extra = [] + if not description: + description = None + if not placement: + placement = self.addPlacement() + representations = self.addRepresentations(shapes) + prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,representations]) + try: + elt = create(self._fileobject,elttype,[uid(),self._owner,name,description,None,placement,prd,None]+extra) + except: + print "unable to create an ",elttype, " with attributes: ",[uid(),self._owner,str(name),description,None,placement,prd,None]+extra + try: + if hasattr(ifcw,"Entity"): + o = ifcw.Entity(elttype) + else: + o = ifcw.entity_instance(elttype) + print "supported attributes are: " + print getPropertyNames(o) + except: + print "unable to create an element of type '"+elttype+"'" + print "WARNING: skipping object '"+name+"' of type "+elttype + return None + self.BuildingProducts.append(elt) + if not storey: + if self.Storeys: + storey = self.Storeys[0] + else: + storey = self.addStorey() + self._relate(storey,elt) + return elt + + def addRepresentations(self,shapes): + """addRepresentations(shapes,[solidType]): creates a representation from the given shape""" + solidType = "Brep" + if not isinstance(shapes,list): + if shapes.is_a("IfcExtrudedAreaSolid"): + solidType = "SweptSolid" + shapes = [shapes] + reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body',solidType,[shape for shape in shapes]])] + return reps + + def addColor(self,rgb,rep): + """addColor(rgb,rep): adds a RGB color definition tuple (float,float,float) to a given representation""" + col = create(self._fileobject,"IfcColourRgb",[None]+list(rgb)) + ssr = create(self._fileobject,"IfcSurfaceStyleRendering",[col,None,None,None,None,None,None,None,"FLAT"]) + iss = create(self._fileobject,"IfcSurfaceStyle",[None,"BOTH",[ssr]]) + psa = create(self._fileobject,"IfcPresentationStyleAssignment",[[iss]]) + isi = create(self._fileobject,"IfcStyledItem",[rep,[psa],None]) + return isi + + def addProfile(self,ifctype,data,curvetype="AREA"): + """addProfile(ifctype,data): creates a 2D profile of the given type, with the given + data as arguments, which must be formatted correctly according to the type.""" + + # Expected ifctype and corresponding data formatting: + # IfcPolyLine: [ (0,0,0), (2,1,0), (3,3,0) ] # list of points + # IfcCompositeCurve: [ ["line",[ (0,0,0), (2,1,0) ] ], # list of points + # ["arc", (0,0,0), 15, [0.76, 3.1416], True, "PARAMETER"] # center, radius, [trim1, trim2], SameSense, trimtype + # ... ] + # IfcCircleProfileDef: [ (0,0,0), 15 ] # center, radius + # IfcEllipseProfileDef: [ (0,0,0), 15, 7 ] # center, radiusX, radiusY + + if ifctype == "IfcPolyline": + pts = [create(self._fileobject,"IfcCartesianPoint",getTuple(p)[:2]) for p in data] + pol = create(self._fileobject,"IfcPolyline",[pts]) + profile = create(self._fileobject,"IfcArbitraryClosedProfileDef",[curvetype,None,pol]) + elif ifctype == "IfcCompositeCurve": + curves = [] + for curve in data: + cur = None + if curve[0] == "line": + pts = [create(self._fileobject,"IfcCartesianPoint",getTuple(p)[:2]) for p in curve[1]] + cur = create(self._fileobject,"IfcPolyline",[pts]) + elif curve[0] == "arc": + pla = self.addPlacement(origin=curve[1],local=False,flat=True) + cir = create(self._fileobject,"IfcCircle",[pla,curve[2]]) + if curve[5] == "CARTESIAN": + # BUGGY! Impossible to add cartesian points as "embedded" entity + trim1 = create(None,"IfcCartesianPoint",getTuple(curve[3][0])[:2]) + trim2 = create(None,"IfcCartesianPoint",getTuple(curve[3][1])[:2]) + else: + trim1 = create(None,"IfcParameterValue",[curve[3][0]]) + trim2 = create(None,"IfcParameterValue",[curve[3][1]]) + cur = create(self._fileobject,"IfcTrimmedCurve",[cir,[trim1],[trim2],curve[4],curve[5]]) + if cur: + seg = create(self._fileobject,"IfcCompositeCurveSegment",["CONTINUOUS",True,cur]) + curves.append(seg) + ccu = create(self._fileobject,"IfcCompositeCurve",[curves,False]) + profile = create(self._fileobject,"IfcArbitraryClosedProfileDef",[curvetype,None,ccu]) + else: + if not isinstance(data,list): + data = [data] + p = self.addPlacement(local=False,flat=True) + profile = create(self._fileobject,ifctype,[curvetype,None,p]+data) + return profile + + def addExtrusion(self,profile,extrusion,placement=None): + """addExtrusion(profile,extrusion,[placement]): makes an + extrusion of the given polyline with the given extrusion vector""" + if not placement: + placement = self.addPlacement(local=False) + value,norm = getValueAndDirection(extrusion) + edir = create(self._fileobject,"IfcDirection",[norm]) + solid = create(self._fileobject,"IfcExtrudedAreaSolid",[profile,placement,edir,value]) + return solid + + def addExtrudedPolyline(self,points,extrusion,placement=None,color=None): + """addExtrudedPolyline(points,extrusion,[placement,color]): makes an extruded polyline + from the given points and the given extrusion vector""" + pol = self.addProfile("IfcPolyline",points) + if not placement: + placement = self.addPlacement(local=False) + exp = self.addExtrusion(pol,extrusion,placement) + if color: + self.addColor(color,exp) + return exp + + def addExtrudedCircle(self,data,extrusion,placement=None,color=None): + """addExtrudedCircle(data,extrusion,[placement,color]): makes an extruded circle + from the given data (center,radius) and the given extrusion vector""" + cir = self.addProfile("IfcCircleProfileDef",data[1]) + if not placement: + placement = self.addPlacement(origin=data[0],local=False) + exp = self.addExtrusion(cir,extrusion,placement) + if color: + self.addColor(color,exp) + return exp + + def addExtrudedEllipse(self,data,extrusion,placement=None,color=None): + """addExtrudedEllipse(data,extrusion,[placement,color]): makes an extruded ellipse + from the given data (center,radiusx,radiusy) and the given extrusion vector""" + cir = self.addProfile("IfcEllipseProfileDef",[data[1],data[2]]) + if not placement: + placement = self.addPlacement(origin=data[0],local=False) + exp = self.addExtrusion(cir,extrusion,placement) + if color: + self.addColor(color,exp) + return exp + + def addExtrudedCompositeCurve(self,curves,extrusion,placement=None,color=None): + """addExtrudedCompositeCurve(curves,extrusion,[placement,color]): makes an extruded polyline + from the given curves and the given extrusion vector""" + if not placement: + placement = self.addPlacement(local=False) + ccu = self.addProfile("IfcCompositeCurve",curves) + exp = self.addExtrusion(ccu,extrusion,placement) + if color: + self.addColor(color,exp) + return exp + + def addFace(self,face): + """addFace(face): creates a face from the given face data (a list of lists of points). + The first is the outer wire, the next are optional inner wires. They must be reversed in order""" + ifb = [] + idx = 0 + for f in face: + pts = [] + for p in f: + #print p + if p in self.fpoints: + #print self.fpoints.index(p) + #print self.frefs + pts.append(self.frefs[self.fpoints.index(p)]) + else: + pt = create(self._fileobject,"IfcCartesianPoint",getTuple(p)) + pts.append(pt) + self.fpoints.append(p) + self.frefs.append(pt) + #print pts + loop = create(self._fileobject,"IfcPolyLoop",[pts]) + if idx == 0: + fb = create(self._fileobject,"IfcFaceOuterBound",[loop,True]) + else: + fb = create(self._fileobject,"IfcFaceBound",[loop,True]) + ifb.append(fb) + idx += 1 + iface = create(self._fileobject,"IfcFace",[ifb]) + return iface + + def addFacetedBrep(self,faces,color=None): + """addFacetedBrep(self,faces,[color]): creates a faceted brep object from the given list + of faces (each face is a list of lists of points, inner wires are reversed)""" + self.fpoints = [] + self.frefs = [] + #print "adding ",len(faces)," faces" + #print faces + ifaces = [self.addFace(face) for face in faces] + sh = create(self._fileobject,"IfcClosedShell",[ifaces]) + brp = create(self._fileobject,"IfcFacetedBrep",[sh]) + if color: + self.addColor(color,brp) + return brp