* Allow to export subtractions as separate elements in IFC * Fixed Survey tool * Removed unused Arch preferences
729 lines
34 KiB
Python
729 lines
34 KiB
Python
#***************************************************************************
|
|
#* *
|
|
#* 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
|
|
import IfcImport
|
|
|
|
# checking that we got the right importer, with export capabilities
|
|
if not hasattr(IfcImport,"IfcFile"):
|
|
print "Wrong version of IfcOpenShell"
|
|
sys.exit()
|
|
|
|
PRECISION = 4 # 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 new():
|
|
"""new(): returns a new empty ifc file holder"""
|
|
fil = IfcImport.IfcFile()
|
|
return fil
|
|
|
|
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 = {}
|
|
for i in range(entity.get_argument_count()):
|
|
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"""
|
|
entity = IfcImport.Entity(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]:
|
|
arg = IfcImport.Doubles(arg)
|
|
entity.set_argument(i,arg)
|
|
return entity
|
|
|
|
|
|
# Convenience tools #################################################################
|
|
|
|
|
|
def makeOwner(ifcdoc,person,organization="Undefined",application="Undefined",version=0.0):
|
|
"""makeOwner(ifcdoc,person,[organization,application,version]):
|
|
creates an owner in the given ifc document"""
|
|
per = create(ifcdoc,"IfcPerson",[None,None,person,None,None,None,None,None])
|
|
org = create(ifcdoc,"IfcOrganization",[None,organization,None,None,None])
|
|
pno = create(ifcdoc,"IfcPersonAndOrganization",[per,org,None])
|
|
app = create(ifcdoc,"IfcApplication",[org,str(version),application,uid()])
|
|
own = create(ifcdoc,"IfcOwnerHistory",[pno,app,None,"ADDED",None,pno,app,now()])
|
|
return own
|
|
|
|
def makePlacement(ifcdoc,reference=None,origin=(0,0,0),xaxis=(1,0,0),zaxis=(0,0,1),local=True):
|
|
"""makePlacement(ifcdoc,[reference,origin,xaxis,zaxis]):
|
|
creates a local placement in the given ifc document. origin,
|
|
xaxis and zaxis can be either a tuple or a 3d vector"""
|
|
xvc = create(ifcdoc,"IfcDirection",getTuple(xaxis))
|
|
zvc = create(ifcdoc,"IfcDirection",getTuple(zaxis))
|
|
ovc = create(ifcdoc,"IfcCartesianPoint",getTuple(origin))
|
|
gpl = create(ifcdoc,"IfcAxis2Placement3D",[ovc,zvc,xvc])
|
|
if local:
|
|
lpl = create(ifcdoc,"IfcLocalPlacement",[reference,gpl])
|
|
return lpl
|
|
else:
|
|
return gpl
|
|
|
|
def makeContext(ifcdoc,placement=None):
|
|
"""makeContext(ifcdoc,[placement]): creates a geometric representation context
|
|
in the given ifc document"""
|
|
if not placement:
|
|
placement = makePlacement(ifcdoc)
|
|
rep = create(ifcdoc,"IfcGeometricRepresentationContext",
|
|
[None,'Model',3,1.E-05,placement,None])
|
|
return rep
|
|
|
|
def makeProject(ifcdoc,context,owner,name="Default project",description=None):
|
|
"""makeProject(ifcdoc,context,owner,[name,description]): creates a project
|
|
in the given ifc document"""
|
|
|
|
# 0: 'GlobalId', 1: 'OwnerHistory', 2: 'Name', 3: 'Description',
|
|
# 4: 'ObjectType', 5: 'LongName', 6: 'Phase', 7: 'RepresentationContexts',
|
|
# 8: 'UnitsInContext'
|
|
|
|
dim1 = create(ifcdoc,"IfcDimensionalExponents",[0,0,0,0,0,0,0])
|
|
dim2 = create(ifcdoc,"IfcSIUnit",[dim1,"LENGTHUNIT","MILLI","METRE"])
|
|
dim3 = create(ifcdoc,"IfcSIUnit",[dim1,"AREAUNIT",None,"SQUARE_METRE"])
|
|
dim4 = create(ifcdoc,"IfcSIUnit",[dim1,"VOLUMEUNIT",None,"CUBIC_METRE"])
|
|
dim6 = create(ifcdoc,"IfcSIUnit",[dim1,"PLANEANGLEUNIT",None,"RADIAN"])
|
|
dim7 = create(ifcdoc,"IfcPlaneAngleMeasure",[1.745E-2])
|
|
dim8 = create(ifcdoc,"IfcMeasureWithUnit",[dim7,dim6])
|
|
dim9 = create(ifcdoc,"IfcConversionBasedUnit",[dim1,"PLANEANGLEUNIT","DEGREE",dim8])
|
|
units = create(ifcdoc,"IfcUnitAssignment",[[dim2,dim3,dim4,dim9]])
|
|
pro = create(ifcdoc,"IfcProject",[uid(),owner,name,description,
|
|
None,None,None,[context],units])
|
|
return pro
|
|
|
|
def makeSite(ifcdoc,project,owner,placement=None,name="Default site",description=None):
|
|
"""makeSite(ifcdoc,project,owner,[placement,name,description]): creates a site
|
|
in the given ifc document"""
|
|
|
|
# 0: 'GlobalId', 1: 'OwnerHistory', 2: 'Name', 3: 'Description',
|
|
# 4: 'ObjectType', 5: 'ObjectPlacement', 6: 'Representation', 7: 'LongName',
|
|
# 8: 'CompositionType', 9: 'RefLatitude', 10: 'RefLongitude', 11: 'RefElevation',
|
|
# 12: 'LandTitleNumber', 13: 'SiteAddress'
|
|
|
|
if not placement:
|
|
placement = makePlacement(ifcdoc)
|
|
sit = create(ifcdoc,"IfcSite",[uid(),owner,name,description,None,placement,
|
|
None,None,"ELEMENT",None,None,None,None,None])
|
|
rel = create(ifcdoc,"IfcRelAggregates",[uid(),owner,'ProjectContainer',
|
|
'Project-site relationship',project,[sit]])
|
|
return sit
|
|
|
|
def makeBuilding(ifcdoc,site,owner,placement=None,name="Default building",description=None):
|
|
"""makeBuilding(ifcdoc,site,owner,[placement,name,description]): creates a building
|
|
in the given ifc document"""
|
|
|
|
# 0: 'GlobalId', 1: 'OwnerHistory', 2: 'Name', 3: 'Description',
|
|
# 4: 'ObjectType', 5: 'ObjectPlacement', 6: 'Representation', 7: 'LongName',
|
|
# 8: 'CompositionType', 9: 'ElevationOfRefHeight', 10: 'ElevationOfTerrain', 11: 'BuildingAddress'
|
|
|
|
if not placement:
|
|
placement = makePlacement(ifcdoc)
|
|
bdg = create(ifcdoc,"IfcBuilding",[uid(),owner,name,description,
|
|
None,placement,None,None,"ELEMENT",None,None,None])
|
|
rel = create(ifcdoc,"IfcRelAggregates",[uid(),owner,'SiteContainer',
|
|
'Site-Building relationship',site,[bdg]])
|
|
return bdg
|
|
|
|
def makeStorey(ifcdoc,building,owner,placement=None,name="Default storey",description=None):
|
|
"""makeStorey(ifcdoc,building,owner,[placement,name,description]): creates a storey
|
|
in the given ifc document"""
|
|
|
|
# 0: 'GlobalId', 1: 'OwnerHistory', 2: 'Name', 3: 'Description',
|
|
# 4: 'ObjectType', 5: 'ObjectPlacement', 6: 'Representation', 7: 'LongName',
|
|
# 8: 'CompositionType', 9: 'Elevation
|
|
|
|
if not placement:
|
|
placement = makePlacement(ifcdoc)
|
|
sto = create(ifcdoc,"IfcBuildingStorey",[uid(),owner,name,description,None,placement,
|
|
None,None,"ELEMENT",None])
|
|
rel = create(ifcdoc,"IfcRelAggregates",[uid(),owner,'BuildingContainer',
|
|
'Building-Stories relationship',building,[sto]])
|
|
return sto
|
|
|
|
def makeWall(ifcdoc,storey,owner,context,shape,placement=None,name="Default wall",description=None):
|
|
"""makeWall(ifcdoc,storey,owner,shape,[placement,name,description]): creates a wall
|
|
in the given ifc document"""
|
|
if not placement:
|
|
placement = makePlacement(ifcdoc)
|
|
rep = create(ifcdoc,"IfcShapeRepresentation",[context,'Body','SolidModel',[shape]])
|
|
prd = create(ifcdoc,"IfcProductDefinitionShape",[None,None,[rep]])
|
|
wal = create(ifcdoc,"IfcWallStandardCase",[uid(),owner,name,description,
|
|
None,placement,prd,None])
|
|
return wal
|
|
|
|
def makePolyline(ifcdoc,points):
|
|
"""makePolyline(ifcdoc,points): creates a polyline with the given
|
|
points in the given ifc document"""
|
|
pts = [create(ifcdoc,"IfcCartesianPoint",getTuple(p)) for p in points]
|
|
pol = create(ifcdoc,"IfcPolyline",[pts])
|
|
return pol
|
|
|
|
def makeExtrusion(ifcdoc,polyline,extrusion,placement=None):
|
|
"""makeExtrusion(ifcdoc,polyline,extrusion,[placement]): makes an
|
|
extrusion of the given polyline with the given extrusion vector in
|
|
the given ifc document"""
|
|
if not placement:
|
|
placement = makePlacement(ifcdoc,local=False)
|
|
value,norm = getValueAndDirection(extrusion)
|
|
edir = create(ifcdoc,"IfcDirection",norm)
|
|
area = create(ifcdoc,"IfcArbitraryClosedProfileDef",["AREA",None,polyline])
|
|
solid = create(ifcdoc,"IfcExtrudedAreaSolid",[area,placement,edir,value])
|
|
return solid
|
|
|
|
def relate(ifcdoc,container,owner,entities):
|
|
"""relate(ifcdoc,container,owner,entities): relates the given entities to the given
|
|
container"""
|
|
if container.is_a("IfcBuildingStorey"):
|
|
rel = create(ifcdoc,"IfcRelContainedInSpatialStructure",[uid(),owner,'StoreyLink',
|
|
'Storey-content relationship',entities, container])
|
|
else:
|
|
print "not implemented!"
|
|
|
|
|
|
# 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"):
|
|
self._fileobject = IfcImport.IfcFile()
|
|
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])
|
|
placement = create(self._fileobject,"IfcLocalPlacement",[None,axp])
|
|
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 = create(self._fileobject,"IfcSite",[uid(),self._owner,"Site",None,None,placement,None,None,"ELEMENT",None,None,None,None,None])
|
|
self._relate(self.Project,self._site)
|
|
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:
|
|
for i in range(obj.get_argument_count()):
|
|
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 addBuilding(self,placement=None,name="Default building",description=None):
|
|
"""addBuilding([placement,name,description]): adds a building"""
|
|
if not placement:
|
|
placement = self.addPlacement()
|
|
bdg = create(self._fileobject,"IfcBuilding",[uid(),self._owner,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,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 _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."""
|
|
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,name,description,None,placement,prd,None]+extra
|
|
try:
|
|
o = IfcImport.Entity(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
|
|
|
|
|
|
# EXAMPLES #################################################################
|
|
|
|
def example1():
|
|
|
|
"creation of a new file using manual, step-by-step procedure"
|
|
|
|
f = new()
|
|
own = makeOwner(f,"Yorik van Havre")
|
|
plac = makePlacement(f)
|
|
grep = makeContext(f,plac)
|
|
proj = makeProject(f,grep,own)
|
|
site = makeSite(f,proj,own)
|
|
bldg = makeBuilding(f,site,own)
|
|
stor = makeStorey(f,bldg,own)
|
|
poly = makePolyline(f,[(0,0,0),(0,200,0),(5000,200,0),(5000,0,0),(0,0,0)])
|
|
solid = makeExtrusion(f,poly,(0,0,3500))
|
|
wall = makeWall(f,stor,own,grep,solid)
|
|
poly2 = makePolyline(f,[(0,200,0),(0,2000,0),(200,2000,0),(200,200,0),(0,200,0)])
|
|
solid2 = makeExtrusion(f,poly2,(0,0,3500))
|
|
wall2 = makeWall(f,stor,own,grep,solid2)
|
|
poly3 = makePolyline(f,[(0,2000,0),(0,2200,0),(5000,2200,0),(5000,2000,0),(0,2000,0)])
|
|
solid3 = makeExtrusion(f,poly3,(0,0,3500))
|
|
wall3 = makeWall(f,stor,own,grep,solid3)
|
|
poly4 = makePolyline(f,[(5000,200,0),(5000,2000,0),(4800,2000,0),(4800,200,0),(5000,200,0)])
|
|
solid4 = makeExtrusion(f,poly4,(0,0,3500))
|
|
wall4 = makeWall(f,stor,own,grep,solid4)
|
|
relate(f,stor,own,[wall,wall2,wall3,wall4])
|
|
f.write("/home/yorik/test1.ifc")
|
|
|
|
print dir(f)
|
|
print f.by_type("IfcWallStandardCase")
|
|
w = f.by_type("IfcWallStandardCase")[0]
|
|
print w
|
|
print dir(w)
|
|
print w.is_a("IfcWallStandardCase")
|
|
|
|
def example2():
|
|
|
|
"creation of a new file using advanced IfcDocument object"
|
|
|
|
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)
|
|
|