Arch: Introduced new IFC importer/exporter
* The new IFC importer uses excusively the newest ifcopenshell.py module * Renamed old importer to importIFClegacy.py * Included the now deprecated ifcReader and ifcWriter modules into importIFClegacy * The new IFC importer falls back on the older one if the ifcopenshell module is not found
This commit is contained in:
parent
7a3ddd5bae
commit
fec636b835
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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__
|
||||
|
|
@ -1,587 +0,0 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2013 *
|
||||
#* Yorik van Havre <yorik@uncreated.net> *
|
||||
#* *
|
||||
#* 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)
|
||||
|
File diff suppressed because it is too large
Load Diff
2354
src/Mod/Arch/importIFClegacy.py
Normal file
2354
src/Mod/Arch/importIFClegacy.py
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user