Arch: Better sticking to standards in IFC import/export

This commit is contained in:
Yorik van Havre 2014-04-21 23:06:44 -03:00
parent 8609337d6f
commit cd7c1f2307
3 changed files with 60 additions and 44 deletions

View File

@ -602,7 +602,7 @@ def addFixture(fixture,baseobject):
else: else:
FreeCAD.Console.PrintMessage(translate("Arch","This object has no support for fixtures")) FreeCAD.Console.PrintMessage(translate("Arch","This object has no support for fixtures"))
def getTuples(data,scale=1,placement=None,normal=None): def getTuples(data,scale=1,placement=None,normal=None,close=True):
"""getTuples(data,[scale,placement,normal]): returns a tuple or a list of tuples from a vector """getTuples(data,[scale,placement,normal]): returns a tuple or a list of tuples from a vector
or from the vertices of a shape. Scale can indicate a scale factor""" or from the vertices of a shape. Scale can indicate a scale factor"""
import Part import Part
@ -630,7 +630,8 @@ def getTuples(data,scale=1,placement=None,normal=None):
if placement: if placement:
pt = placement.multVec(pt) pt = placement.multVec(pt)
t.append((pt.x*scale,pt.y*scale,pt.z*scale)) t.append((pt.x*scale,pt.y*scale,pt.z*scale))
t.append(t[0]) # for IFC verts lists must be closed if close:
t.append(t[0]) # for IFC verts lists must be closed
else: else:
print "Arch.getTuples(): Wrong profile data" print "Arch.getTuples(): Wrong profile data"
return t return t
@ -669,10 +670,10 @@ def getBrepFacesData(obj,scale=1):
s = [] s = []
for face in obj.Shape.Faces: for face in obj.Shape.Faces:
f = [] f = []
f.append(getTuples(face.OuterWire,scale,normal=face.normalAt(0,0))) f.append(getTuples(face.OuterWire,scale,normal=face.normalAt(0,0),close=False))
for wire in face.Wires: for wire in face.Wires:
if wire.hashCode() != face.OuterWire.hashCode(): if wire.hashCode() != face.OuterWire.hashCode():
f.append(getTuples(wire,scale,normal=DraftVecUtils.neg(face.normalAt(0,0)))) f.append(getTuples(wire,scale,normal=DraftVecUtils.neg(face.normalAt(0,0)),close=False))
s.append(f) s.append(f)
sols.append(s) sols.append(s)
return sols return sols

View File

@ -240,7 +240,7 @@ def makeWall(ifcdoc,storey,owner,context,shape,placement=None,name="Default wall
in the given ifc document""" in the given ifc document"""
if not placement: if not placement:
placement = makePlacement(ifcdoc) placement = makePlacement(ifcdoc)
rep = create(ifcdoc,"IfcShapeRepresentation",[context,'Body','SweptSolid',[shape]]) rep = create(ifcdoc,"IfcShapeRepresentation",[context,'Body','SolidModel',[shape]])
prd = create(ifcdoc,"IfcProductDefinitionShape",[None,None,[rep]]) prd = create(ifcdoc,"IfcProductDefinitionShape",[None,None,[rep]])
wal = create(ifcdoc,"IfcWallStandardCase",[uid(),owner,name,description, wal = create(ifcdoc,"IfcWallStandardCase",[uid(),owner,name,description,
None,placement,prd,None]) None,placement,prd,None])
@ -375,6 +375,9 @@ class IfcDocument(object):
elif "FILE_NAME" in l: elif "FILE_NAME" in l:
# bug 4: incomplete file name entry # bug 4: incomplete file name entry
l = l.replace("FILE_NAME('','',(''),('',''),'IfcOpenShell','IfcOpenShell','');","FILE_NAME('"+path+"','"+now(string=True)+"',('"+self.Owner+"'),('',''),'IfcOpenShell','IfcOpenShell','');") 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) lines.append(l)
f.close() f.close()
f = open(path,"wb") f = open(path,"wb")
@ -441,7 +444,11 @@ class IfcDocument(object):
placement = self.addPlacement() placement = self.addPlacement()
if not isinstance(shapes,list): if not isinstance(shapes,list):
shapes = [shapes] shapes = [shapes]
reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body','SweptSolid',[shape]]) for shape in shapes] if standard:
solidType = "SweptSolid"
else:
solidType = "Brep"
reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body',solidType,[shape]]) for shape in shapes]
prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,reps]) prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,reps])
if standard: if standard:
wal = create(self._fileobject,"IfcWallStandardCase",[uid(),self._owner,name,description,None,placement,prd,None]) wal = create(self._fileobject,"IfcWallStandardCase",[uid(),self._owner,name,description,None,placement,prd,None])
@ -456,14 +463,18 @@ class IfcDocument(object):
self._relate(storey,wal) self._relate(storey,wal)
return wal return wal
def addStructure(self,ifctype,shapes,storey=None,placement=None,name="Default Structure",description=None): def addStructure(self,ifctype,shapes,storey=None,placement=None,name="Default Structure",description=None,standard=False):
"""addStructure(ifctype,shapes,[storey,placement,name,description]): creates a structure """addStructure(ifctype,shapes,[storey,placement,name,description]): creates a structure
from the given representation shape(s). Ifctype is the type of structural object (IfcBeam, IfcColumn, etc)""" from the given representation shape(s). Ifctype is the type of structural object (IfcBeam, IfcColumn, etc)"""
if not placement: if not placement:
placement = self.addPlacement() placement = self.addPlacement()
if not isinstance(shapes,list): if not isinstance(shapes,list):
shapes = [shapes] shapes = [shapes]
reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body','SweptSolid',[shape]]) for shape in shapes] if standard:
solidType = "SweptSolid"
else:
solidType = "Brep"
reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body',solidType,[shape]]) for shape in shapes]
prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,reps]) prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,reps])
if ifctype in ["IfcSlab","IfcFooting"]: if ifctype in ["IfcSlab","IfcFooting"]:
stt = create(self._fileobject,ifctype,[uid(),self._owner,name,description,None,placement,prd,None,"NOTDEFINED"]) stt = create(self._fileobject,ifctype,[uid(),self._owner,name,description,None,placement,prd,None,"NOTDEFINED"])
@ -485,7 +496,7 @@ class IfcDocument(object):
placement = self.addPlacement() placement = self.addPlacement()
if not isinstance(shapes,list): if not isinstance(shapes,list):
shapes = [shapes] shapes = [shapes]
reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body','SweptSolid',[shape]]) for shape in shapes] reps = [create(self._fileobject,"IfcShapeRepresentation",[self._repcontext,'Body','SolidModel',[shape]]) for shape in shapes]
prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,reps]) prd = create(self._fileobject,"IfcProductDefinitionShape",[None,None,reps])
win = create(self._fileobject,ifctype,[uid(),self._owner,name,description,None,placement,prd,None,float(height),float(width)]) win = create(self._fileobject,ifctype,[uid(),self._owner,name,description,None,placement,prd,None,float(height),float(width)])
self.BuildingProducts.append(win) self.BuildingProducts.append(win)

View File

@ -30,8 +30,10 @@ __url__ = "http://www.freecadweb.org"
# config # config
subtractiveTypes = ["IfcOpeningElement"] # elements that must be subtracted from their parents subtractiveTypes = ["IfcOpeningElement"] # elements that must be subtracted from their parents
SCHEMA = "http://www.steptools.com/support/stdev_docs/express/ifc2x3/ifc2x3_tc1.exp" SCHEMA = "http://www.steptools.com/support/stdev_docs/express/ifc2x3/ifc2x3_tc1.exp" # only for internal prser
MAKETEMPFILES = False # if True, shapes are passed from ifcopenshell to freecad through temp files 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 # end config
if open.__module__ == '__builtin__': if open.__module__ == '__builtin__':
@ -60,11 +62,9 @@ def insert(filename,docname,skip=None):
def getConfig(): def getConfig():
"Gets Arch IFC import preferences" "Gets Arch IFC import preferences"
global CREATE_IFC_GROUPS, ASMESH, DEBUG, SKIP, PREFIX_NUMBERS, FORCE_PYTHON_PARSER, SEPARATE_OPENINGS, SEPARATE_PLACEMENTS global CREATE_IFC_GROUPS, ASMESH, PREFIX_NUMBERS, FORCE_PYTHON_PARSER, SEPARATE_OPENINGS, SEPARATE_PLACEMENTS
CREATE_IFC_GROUPS = False CREATE_IFC_GROUPS = False
IMPORT_IFC_FURNITURE = False IMPORT_IFC_FURNITURE = False
DEBUG = False
SKIP = ["IfcBuildingElementProxy","IfcFlowTerminal","IfcFurnishingElement"]
ASMESH = ["IfcFurnishingElement"] ASMESH = ["IfcFurnishingElement"]
PREFIX_NUMBERS = False PREFIX_NUMBERS = False
FORCE_PYTHON_PARSER = False FORCE_PYTHON_PARSER = False
@ -197,17 +197,17 @@ def read(filename,skip=None):
# skip IDs # skip IDs
if objid in skipIds: if objid in skipIds:
if DEBUG: print "skipping because object ID is in skip list" if DEBUG: print " skipping because object ID is in skip list"
nobj = None nobj = None
# skip types # skip types
elif objtype in SKIP: elif objtype in SKIP:
if DEBUG: print "skipping because type is in skip list" if DEBUG: print " skipping because type is in skip list"
nobj = None nobj = None
# check if object was already processed, to workaround an ifcopenshell bug # check if object was already processed, to workaround an ifcopenshell bug
elif objid in processedIds: elif objid in processedIds:
if DEBUG: print "skipping because this object was already processed" if DEBUG: print " skipping because this object was already processed"
else: else:
# build shape # build shape
@ -318,12 +318,12 @@ def read(filename,skip=None):
else: else:
# creating parent if needed # creating parent if needed
if IFCOPENSHELL5: if IFCOPENSHELL5:
parent_ifcobj = ifc.by_id(parent_id) obj = ifc.by_id(parent_id)
parentid = int(str(obj).split("=")[0].strip("#")) parentid = int(str(obj).split("=")[0].strip("#"))
parentname = obj.get_argument(obj.get_argument_index("Name")) parentname = obj.get_argument(obj.get_argument_index("Name"))
parenttype = str(obj).split("=")[1].split("(")[0] parenttype = str(obj).split("=")[1].split("(")[0]
else: else:
parent_ifcobj = IfcImport.GetObject(parent_id) obj = IfcImport.GetObject(parent_id)
parentid = obj.id parentid = obj.id
parentname = obj.name parentname = obj.name
parenttype = obj.type parenttype = obj.type
@ -343,6 +343,8 @@ def read(filename,skip=None):
elif parenttype == "IfcWindow": elif parenttype == "IfcWindow":
parent = Arch.makeWindow(name=n) parent = Arch.makeWindow(name=n)
parent.Label = n parent.Label = n
elif parenttype == "IfcProject":
parent = None
else: else:
if DEBUG: print "Fixme: skipping unhandled parent: ", parentid, " ", parenttype if DEBUG: print "Fixme: skipping unhandled parent: ", parentid, " ", parenttype
parent = None parent = None
@ -440,24 +442,24 @@ def makeWall(entity,shape=None,name="Wall"):
body.Mesh = shape body.Mesh = shape
wall = Arch.makeWall(body,name=name) wall = Arch.makeWall(body,name=name)
wall.Label = name wall.Label = name
if DEBUG: print "made wall object ",entity,":",wall if DEBUG: print " made wall object ",entity,":",wall
return wall return wall
# use internal parser # use internal parser
if DEBUG: print "=====> making wall",entity.id if DEBUG: print "=====> making wall",entity.id
placement = wall = wire = body = width = height = None placement = wall = wire = body = width = height = None
placement = getPlacement(entity.ObjectPlacement) placement = getPlacement(entity.ObjectPlacement)
if DEBUG: print "got wall placement",entity.id,":",placement if DEBUG: print " got wall placement",entity.id,":",placement
width = entity.getProperty("Width") width = entity.getProperty("Width")
height = entity.getProperty("Height") height = entity.getProperty("Height")
if width and height: if width and height:
if DEBUG: print "got width, height ",entity.id,":",width,"/",height if DEBUG: print " got width, height ",entity.id,":",width,"/",height
for r in entity.Representation.Representations: for r in entity.Representation.Representations:
if r.RepresentationIdentifier == "Axis": if r.RepresentationIdentifier == "Axis":
wire = getWire(r.Items,placement) wire = getWire(r.Items,placement)
wall = Arch.makeWall(wire,width,height,align="Center",name="Wall"+str(entity.id)) wall = Arch.makeWall(wire,width,height,align="Center",name="Wall"+str(entity.id))
else: else:
if DEBUG: print "no height or width properties found..." if DEBUG: print " no height or width properties found..."
for r in entity.Representation.Representations: for r in entity.Representation.Representations:
if r.RepresentationIdentifier == "Body": if r.RepresentationIdentifier == "Body":
for b in r.Items: for b in r.Items:
@ -468,12 +470,12 @@ def makeWall(entity,shape=None,name="Wall"):
wall = Arch.makeWall(wire,width=0,height=b.Depth,name="Wall"+str(entity.id)) wall = Arch.makeWall(wire,width=0,height=b.Depth,name="Wall"+str(entity.id))
wall.Normal = norm wall.Normal = norm
if wall: if wall:
if DEBUG: print "made wall object ",entity.id,":",wall if DEBUG: print " made wall object ",entity.id,":",wall
return wall return wall
if DEBUG: print "error: skipping wall",entity.id if DEBUG: print " error: skipping wall",entity.id
return None return None
except: except:
if DEBUG: print "error: skipping wall",entity if DEBUG: print " error: skipping wall",entity
return None return None
@ -486,7 +488,7 @@ def makeWindow(entity,shape=None,name="Window"):
window = Arch.makeWindow(name=name) window = Arch.makeWindow(name=name)
window.Shape = shape window.Shape = shape
window.Label = name window.Label = name
if DEBUG: print "made window object ",entity,":",window if DEBUG: print " made window object ",entity,":",window
return window return window
# use internal parser # use internal parser
@ -503,12 +505,12 @@ def makeWindow(entity,shape=None,name="Window"):
wire = getWire(b.SweptArea,placement) wire = getWire(b.SweptArea,placement)
window = Arch.makeWindow(wire,width=b.Depth,name=objtype+str(entity.id)) window = Arch.makeWindow(wire,width=b.Depth,name=objtype+str(entity.id))
if window: if window:
if DEBUG: print "made window object ",entity.id,":",window if DEBUG: print " made window object ",entity.id,":",window
return window return window
if DEBUG: print "error: skipping window",entity.id if DEBUG: print " error: skipping window",entity.id
return None return None
except: except:
if DEBUG: print "error: skipping window",entity if DEBUG: print " error: skipping window",entity
return None return None
@ -533,7 +535,7 @@ def makeStructure(entity,shape=None,ifctype=None,name="Structure"):
structure.Role = "Slab" structure.Role = "Slab"
elif ifctype == "IfcFooting": elif ifctype == "IfcFooting":
structure.Role = "Foundation" structure.Role = "Foundation"
if DEBUG: print "made structure object ",entity,":",structure," (type: ",ifctype,")" if DEBUG: print " made structure object ",entity,":",structure," (type: ",ifctype,")"
return structure return structure
# use internal parser # use internal parser
@ -550,12 +552,12 @@ def makeStructure(entity,shape=None,ifctype=None,name="Structure"):
wire = getWire(b.SweptArea,placement) wire = getWire(b.SweptArea,placement)
structure = Arch.makeStructure(wire,height=b.Depth,name=objtype+str(entity.id)) structure = Arch.makeStructure(wire,height=b.Depth,name=objtype+str(entity.id))
if structure: if structure:
if DEBUG: print "made structure object ",entity.id,":",structure if DEBUG: print " made structure object ",entity.id,":",structure
return structure return structure
if DEBUG: print "error: skipping structure",entity.id if DEBUG: print " error: skipping structure",entity.id
return None return None
except: except:
if DEBUG: print "error: skipping structure",entity if DEBUG: print " error: skipping structure",entity
return None return None
@ -575,7 +577,7 @@ def makeSite(entity,shape=None,name="Site"):
site.Label = name site.Label = name
if body: if body:
site.Terrain = body site.Terrain = body
if DEBUG: print "made site object ",entity,":",site if DEBUG: print " made site object ",entity,":",site
return site return site
except: except:
return None return None
@ -592,7 +594,7 @@ def makeSpace(entity,shape=None,name="Space"):
body.Shape = shape body.Shape = shape
space.Base = body space.Base = body
body.ViewObject.hide() body.ViewObject.hide()
if DEBUG: print "made space object ",entity,":",space if DEBUG: print " made space object ",entity,":",space
return space return space
except: except:
return None return None
@ -607,7 +609,7 @@ def makeRoof(entity,shape=None,name="Roof"):
roof = Arch.makeRoof(name=name) roof = Arch.makeRoof(name=name)
roof.Label = name roof.Label = name
roof.Shape = shape roof.Shape = shape
if DEBUG: print "made roof object ",entity,":",roof if DEBUG: print " made roof object ",entity,":",roof
return roof return roof
except: except:
return None return None
@ -677,7 +679,7 @@ def getShape(obj,objid):
else: else:
sh.importBrepFromString(brep_data) sh.importBrepFromString(brep_data)
except: except:
print "Error: malformed shape" print " error: malformed shape"
return None return None
else: else:
if IFCOPENSHELL5 and SEPARATE_PLACEMENTS: if IFCOPENSHELL5 and SEPARATE_PLACEMENTS:
@ -688,16 +690,16 @@ def getShape(obj,objid):
# try to extract a solid shape # try to extract a solid shape
if sh.Faces: if sh.Faces:
try: try:
if DEBUG: print "Malformed solid. Attempting to fix..." if DEBUG: print " malformed solid. Attempting to fix..."
shell = Part.makeShell(sh.Faces) shell = Part.makeShell(sh.Faces)
if shell: if shell:
solid = Part.makeSolid(shell) solid = Part.makeSolid(shell)
if solid: if solid:
sh = solid sh = solid
except: except:
if DEBUG: print "failed to retrieve solid from object ",objid if DEBUG: print " failed to retrieve solid from object ",objid
else: else:
if DEBUG: print "object ", objid, " doesn't contain any geometry" if DEBUG: print " object ", objid, " doesn't contain any geometry"
if not IFCOPENSHELL5: if not IFCOPENSHELL5:
m = obj.matrix m = obj.matrix
mat = FreeCAD.Matrix(m[0], m[3], m[6], m[9], mat = FreeCAD.Matrix(m[0], m[3], m[6], m[9],
@ -1026,7 +1028,7 @@ def export(exportList,filename):
#ifc.addStructure( role, ifc.addExtrudedPolyline(gdata[0],gdata[1]), storey=parent, name=name ) #ifc.addStructure( role, ifc.addExtrudedPolyline(gdata[0],gdata[1]), storey=parent, name=name )
if FreeCAD.Vector(gdata[1]).getAngle(FreeCAD.Vector(0,0,1)) < .01: if FreeCAD.Vector(gdata[1]).getAngle(FreeCAD.Vector(0,0,1)) < .01:
# Workaround for non-Z extrusions, apparently not supported by ifc++ TODO: fix this # Workaround for non-Z extrusions, apparently not supported by ifc++ TODO: fix this
ifc.addStructure( role, ifc.addExtrudedPolyline(gdata[0],gdata[1]), storey=parent, name=name ) ifc.addStructure( role, ifc.addExtrudedPolyline(gdata[0],gdata[1]), storey=parent, name=name, standard=True )
else: else:
fdata = Arch.getBrepFacesData(obj,scaling) fdata = Arch.getBrepFacesData(obj,scaling)
ifc.addStructure( role, [ifc.addFacetedBrep(f) for f in fdata], storey=parent, name=name ) ifc.addStructure( role, [ifc.addFacetedBrep(f) for f in fdata], storey=parent, name=name )
@ -1037,11 +1039,13 @@ def export(exportList,filename):
if parent: if parent:
p = ifc.findByName("IfcWallStandardCase",str(parent.Label)) p = ifc.findByName("IfcWallStandardCase",str(parent.Label))
if not p: if not p:
p = ifc.findByName("IfcColumn",str(parent.Label)) p = ifc.findByName("IfcWall",str(parent.Label))
if not p: if not p:
p = ifc.findByName("IfcBeam",str(parent.Label)) p = ifc.findByName("IfcColumn",str(parent.Label))
if not p: if not p:
p = ifc.findByName("IfcSlab",str(parent.Label)) p = ifc.findByName("IfcBeam",str(parent.Label))
if not p:
p = ifc.findByName("IfcSlab",str(parent.Label))
parent = p parent = p
role = "IfcWindow" role = "IfcWindow"
if hasattr(obj,"Role"): if hasattr(obj,"Role"):