Skip to content

Commit

Permalink
Arch: Better sticking to standards in IFC import/export
Browse files Browse the repository at this point in the history
  • Loading branch information
yorikvanhavre committed Apr 22, 2014
1 parent 8609337 commit cd7c1f2
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 44 deletions.
9 changes: 5 additions & 4 deletions src/Mod/Arch/ArchCommands.py
Expand Up @@ -602,7 +602,7 @@ def addFixture(fixture,baseobject):
else:
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
or from the vertices of a shape. Scale can indicate a scale factor"""
import Part
Expand Down Expand Up @@ -630,7 +630,8 @@ def getTuples(data,scale=1,placement=None,normal=None):
if placement:
pt = placement.multVec(pt)
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:
print "Arch.getTuples(): Wrong profile data"
return t
Expand Down Expand Up @@ -669,10 +670,10 @@ def getBrepFacesData(obj,scale=1):
s = []
for face in obj.Shape.Faces:
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:
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)
sols.append(s)
return sols
Expand Down
21 changes: 16 additions & 5 deletions src/Mod/Arch/ifcWriter.py
Expand Up @@ -240,7 +240,7 @@ def makeWall(ifcdoc,storey,owner,context,shape,placement=None,name="Default wall
in the given ifc document"""
if not placement:
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]])
wal = create(ifcdoc,"IfcWallStandardCase",[uid(),owner,name,description,
None,placement,prd,None])
Expand Down Expand Up @@ -375,6 +375,9 @@ def _fix(self,path):
elif "FILE_NAME" in l:
# 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")
Expand Down Expand Up @@ -441,7 +444,11 @@ def addWall(self,shapes,storey=None,placement=None,name="Default wall",descripti
placement = self.addPlacement()
if not isinstance(shapes,list):
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])
if standard:
wal = create(self._fileobject,"IfcWallStandardCase",[uid(),self._owner,name,description,None,placement,prd,None])
Expand All @@ -456,14 +463,18 @@ def addWall(self,shapes,storey=None,placement=None,name="Default wall",descripti
self._relate(storey,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
from the given representation shape(s). Ifctype is the type of structural object (IfcBeam, IfcColumn, etc)"""
if not placement:
placement = self.addPlacement()
if not isinstance(shapes,list):
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])
if ifctype in ["IfcSlab","IfcFooting"]:
stt = create(self._fileobject,ifctype,[uid(),self._owner,name,description,None,placement,prd,None,"NOTDEFINED"])
Expand All @@ -485,7 +496,7 @@ def addWindow(self,ifctype,width,height,shapes,host=None,placement=None,name="De
placement = self.addPlacement()
if not isinstance(shapes,list):
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])
win = create(self._fileobject,ifctype,[uid(),self._owner,name,description,None,placement,prd,None,float(height),float(width)])
self.BuildingProducts.append(win)
Expand Down
74 changes: 39 additions & 35 deletions src/Mod/Arch/importIFC.py
Expand Up @@ -30,8 +30,10 @@

# config
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
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

if open.__module__ == '__builtin__':
Expand Down Expand Up @@ -60,11 +62,9 @@ def insert(filename,docname,skip=None):

def getConfig():
"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
IMPORT_IFC_FURNITURE = False
DEBUG = False
SKIP = ["IfcBuildingElementProxy","IfcFlowTerminal","IfcFurnishingElement"]
ASMESH = ["IfcFurnishingElement"]
PREFIX_NUMBERS = False
FORCE_PYTHON_PARSER = False
Expand Down Expand Up @@ -197,17 +197,17 @@ def read(filename,skip=None):

# skip IDs
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

# skip types
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

# check if object was already processed, to workaround an ifcopenshell bug
elif objid in processedIds:
if DEBUG: print "skipping because this object was already processed"
if DEBUG: print " skipping because this object was already processed"

else:
# build shape
Expand Down Expand Up @@ -318,12 +318,12 @@ def read(filename,skip=None):
else:
# creating parent if needed
if IFCOPENSHELL5:
parent_ifcobj = ifc.by_id(parent_id)
obj = ifc.by_id(parent_id)
parentid = int(str(obj).split("=")[0].strip("#"))
parentname = obj.get_argument(obj.get_argument_index("Name"))
parenttype = str(obj).split("=")[1].split("(")[0]
else:
parent_ifcobj = IfcImport.GetObject(parent_id)
obj = IfcImport.GetObject(parent_id)
parentid = obj.id
parentname = obj.name
parenttype = obj.type
Expand All @@ -343,6 +343,8 @@ def read(filename,skip=None):
elif parenttype == "IfcWindow":
parent = Arch.makeWindow(name=n)
parent.Label = n
elif parenttype == "IfcProject":
parent = None
else:
if DEBUG: print "Fixme: skipping unhandled parent: ", parentid, " ", parenttype
parent = None
Expand Down Expand Up @@ -440,24 +442,24 @@ def makeWall(entity,shape=None,name="Wall"):
body.Mesh = shape
wall = Arch.makeWall(body,name=name)
wall.Label = name
if DEBUG: print "made wall object ",entity,":",wall
if DEBUG: print " made wall object ",entity,":",wall
return wall

# use internal parser
if DEBUG: print "=====> making wall",entity.id
placement = wall = wire = body = width = height = None
placement = getPlacement(entity.ObjectPlacement)
if DEBUG: print "got wall placement",entity.id,":",placement
if DEBUG: print " got wall placement",entity.id,":",placement
width = entity.getProperty("Width")
height = entity.getProperty("Height")
if width and height:
if DEBUG: print "got width, height ",entity.id,":",width,"/",height
if DEBUG: print " got width, height ",entity.id,":",width,"/",height
for r in entity.Representation.Representations:
if r.RepresentationIdentifier == "Axis":
wire = getWire(r.Items,placement)
wall = Arch.makeWall(wire,width,height,align="Center",name="Wall"+str(entity.id))
else:
if DEBUG: print "no height or width properties found..."
if DEBUG: print " no height or width properties found..."
for r in entity.Representation.Representations:
if r.RepresentationIdentifier == "Body":
for b in r.Items:
Expand All @@ -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.Normal = norm
if wall:
if DEBUG: print "made wall object ",entity.id,":",wall
if DEBUG: print " made wall object ",entity.id,":",wall
return wall
if DEBUG: print "error: skipping wall",entity.id
if DEBUG: print " error: skipping wall",entity.id
return None
except:
if DEBUG: print "error: skipping wall",entity
if DEBUG: print " error: skipping wall",entity
return None


Expand All @@ -486,7 +488,7 @@ def makeWindow(entity,shape=None,name="Window"):
window = Arch.makeWindow(name=name)
window.Shape = shape
window.Label = name
if DEBUG: print "made window object ",entity,":",window
if DEBUG: print " made window object ",entity,":",window
return window

# use internal parser
Expand All @@ -503,12 +505,12 @@ def makeWindow(entity,shape=None,name="Window"):
wire = getWire(b.SweptArea,placement)
window = Arch.makeWindow(wire,width=b.Depth,name=objtype+str(entity.id))
if window:
if DEBUG: print "made window object ",entity.id,":",window
if DEBUG: print " made window object ",entity.id,":",window
return window
if DEBUG: print "error: skipping window",entity.id
if DEBUG: print " error: skipping window",entity.id
return None
except:
if DEBUG: print "error: skipping window",entity
if DEBUG: print " error: skipping window",entity
return None


Expand All @@ -533,7 +535,7 @@ def makeStructure(entity,shape=None,ifctype=None,name="Structure"):
structure.Role = "Slab"
elif ifctype == "IfcFooting":
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

# use internal parser
Expand All @@ -550,12 +552,12 @@ def makeStructure(entity,shape=None,ifctype=None,name="Structure"):
wire = getWire(b.SweptArea,placement)
structure = Arch.makeStructure(wire,height=b.Depth,name=objtype+str(entity.id))
if structure:
if DEBUG: print "made structure object ",entity.id,":",structure
if DEBUG: print " made structure object ",entity.id,":",structure
return structure
if DEBUG: print "error: skipping structure",entity.id
if DEBUG: print " error: skipping structure",entity.id
return None
except:
if DEBUG: print "error: skipping structure",entity
if DEBUG: print " error: skipping structure",entity
return None


Expand All @@ -575,7 +577,7 @@ def makeSite(entity,shape=None,name="Site"):
site.Label = name
if body:
site.Terrain = body
if DEBUG: print "made site object ",entity,":",site
if DEBUG: print " made site object ",entity,":",site
return site
except:
return None
Expand All @@ -592,7 +594,7 @@ def makeSpace(entity,shape=None,name="Space"):
body.Shape = shape
space.Base = body
body.ViewObject.hide()
if DEBUG: print "made space object ",entity,":",space
if DEBUG: print " made space object ",entity,":",space
return space
except:
return None
Expand All @@ -607,7 +609,7 @@ def makeRoof(entity,shape=None,name="Roof"):
roof = Arch.makeRoof(name=name)
roof.Label = name
roof.Shape = shape
if DEBUG: print "made roof object ",entity,":",roof
if DEBUG: print " made roof object ",entity,":",roof
return roof
except:
return None
Expand Down Expand Up @@ -677,7 +679,7 @@ def getShape(obj,objid):
else:
sh.importBrepFromString(brep_data)
except:
print "Error: malformed shape"
print " error: malformed shape"
return None
else:
if IFCOPENSHELL5 and SEPARATE_PLACEMENTS:
Expand All @@ -688,16 +690,16 @@ def getShape(obj,objid):
# try to extract a solid shape
if sh.Faces:
try:
if DEBUG: print "Malformed solid. Attempting to fix..."
if DEBUG: print " malformed solid. Attempting to fix..."
shell = Part.makeShell(sh.Faces)
if shell:
solid = Part.makeSolid(shell)
if solid:
sh = solid
except:
if DEBUG: print "failed to retrieve solid from object ",objid
if DEBUG: print " failed to retrieve solid from object ",objid
else:
if DEBUG: print "object ", objid, " doesn't contain any geometry"
if DEBUG: print " object ", objid, " doesn't contain any geometry"
if not IFCOPENSHELL5:
m = obj.matrix
mat = FreeCAD.Matrix(m[0], m[3], m[6], m[9],
Expand Down Expand Up @@ -1026,7 +1028,7 @@ def export(exportList,filename):
#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:
# 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:
fdata = Arch.getBrepFacesData(obj,scaling)
ifc.addStructure( role, [ifc.addFacetedBrep(f) for f in fdata], storey=parent, name=name )
Expand All @@ -1037,11 +1039,13 @@ def export(exportList,filename):
if parent:
p = ifc.findByName("IfcWallStandardCase",str(parent.Label))
if not p:
p = ifc.findByName("IfcColumn",str(parent.Label))
p = ifc.findByName("IfcWall",str(parent.Label))
if not p:
p = ifc.findByName("IfcBeam",str(parent.Label))
p = ifc.findByName("IfcColumn",str(parent.Label))
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
role = "IfcWindow"
if hasattr(obj,"Role"):
Expand Down

0 comments on commit cd7c1f2

Please sign in to comment.