Arch: Adapted IFC importer for new IfcOpenShell6

Squashed following commits:
* Arch: Adapted IFC importer for IfcOpenShell6
* Arch: further fixes for ifcopenshell6 compatibility
* Arch: IFC importer now converts to/from  meters for better compatibility with IfcOpenShell
* Arch: further fixes with ifcopenshell6
* Arch: precision adjustments in IFC exporter
* Arch: small fix in makeRoof
This commit is contained in:
Yorik van Havre 2015-01-10 19:30:36 -02:00
parent 6202476061
commit 40f0927705
5 changed files with 118 additions and 124 deletions

View File

@ -26,10 +26,10 @@ __author__ = "Yorik van Havre"
__url__ = "http://www.freecadweb.org"
# Possible roles for IFC objects
Roles = ['Undefined','Beam','Beam Standard Case','Chimney','Column','Column Standard Case','Covering','Curtain Wall',
'Door','Door Standard Case','Foundation','Furniture','Hydro Equipment','Electric Equipment',
'Member','Plate','Railing','Ramp','Ramp Flight','Rebar','Pile','Roof','Shading Device','Slab','Space'
'Stair','Stair Flight','Tendon','Wall','Wall Standard Case','Wall Layer','Window','Window Standard Case']
Roles = ['Undefined','Beam','Chimney','Column','Covering','Curtain Wall',
'Door','Foundation','Furniture','Hydro Equipment','Electric Equipment',
'Member','Plate','Railing','Ramp','Ramp Flight','Rebar','Pile','Roof','Shading Device','Slab','Space',
'Stair','Stair Flight','Tendon','Wall','Wall Layer','Window']
import FreeCAD,Draft
from FreeCAD import Vector
@ -301,6 +301,7 @@ class Component:
self.Type = "Component"
self.Subvolume = None
self.MoveWithHost = False
obj.Role = Roles
def execute(self,obj):
return

View File

@ -47,45 +47,45 @@ def makeRoof(baseobj=None,facenr=1, angles=[45.,], run = [], idrel = [0,],thickn
_ViewProviderRoof(obj.ViewObject)
if baseobj:
obj.Base = baseobj
if obj.Base.isDerivedFrom("Part::Feature"):
if (obj.Base.Shape.Faces and obj.Face):
w = obj.Base.Shape.Faces[obj.Face-1].Wires[0]
elif obj.Base.Shape.Wires:
w = obj.Base.Shape.Wires[0]
if w:
if w.isClosed():
edges = DraftGeomUtils.sortEdges(w.Edges)
l = len(edges)
la = len(angles)
alist = angles
for i in range(l-la):
alist.append(angles[0])
obj.Angles=alist
lr = len(run)
rlist = run
for i in range(l-lr):
rlist.append(w.Edges[i].Length/2.)
obj.Runs = rlist
lidrel = len(idrel)
rellist = idrel
for i in range(l-lidrel):
rellist.append(0)
obj.IdRel = rellist
lthick = len(thickness)
tlist = thickness
for i in range(l-lthick):
tlist.append(thickness[0])
obj.Thickness = tlist
lover = len(overhang)
olist = overhang
for i in range(l-lover):
olist.append(overhang[0])
obj.Overhang = olist
if obj.Base.isDerivedFrom("Part::Feature"):
if (obj.Base.Shape.Faces and obj.Face):
w = obj.Base.Shape.Faces[obj.Face-1].Wires[0]
elif obj.Base.Shape.Wires:
w = obj.Base.Shape.Wires[0]
if w:
if w.isClosed():
edges = DraftGeomUtils.sortEdges(w.Edges)
l = len(edges)
la = len(angles)
alist = angles
for i in range(l-la):
alist.append(angles[0])
obj.Angles=alist
lr = len(run)
rlist = run
for i in range(l-lr):
rlist.append(w.Edges[i].Length/2.)
obj.Runs = rlist
lidrel = len(idrel)
rellist = idrel
for i in range(l-lidrel):
rellist.append(0)
obj.IdRel = rellist
lthick = len(thickness)
tlist = thickness
for i in range(l-lthick):
tlist.append(thickness[0])
obj.Thickness = tlist
lover = len(overhang)
olist = overhang
for i in range(l-lover):
olist.append(overhang[0])
obj.Overhang = olist
obj.Face = facenr
return obj

View File

@ -214,6 +214,8 @@ class _Space(ArchComponent.Component):
def getArea(self,obj):
"returns the horizontal area at the center of the space"
import Part,DraftGeomUtils
if not hasattr(obj.Shape,"CenterOfMass"):
return 0
try:
pl = Part.makePlane(1,1)
pl.translate(obj.Shape.CenterOfMass)

View File

@ -116,42 +116,6 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="toolTip">
<string>A scaling factor to apply to imported IFC objects</string>
</property>
<property name="text">
<string>Scaling factor</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefDoubleSpinBox" name="gui::prefdoublespinbox">
<property name="toolTip">
<string>IFC files are typically written in millimeters. If you are working in a different unit, this allow you to scale all your units to be expressed in millimeters.</string>
</property>
<property name="decimals">
<number>4</number>
</property>
<property name="maximum">
<double>99999.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
<property name="prefEntry" stdset="0">
<cstring>IfcScalingFactor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Arch</cstring>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>

View File

@ -30,6 +30,7 @@ import os,time,tempfile,uuid,FreeCAD,Part,Draft,Arch
if open.__module__ == '__builtin__':
pyopen = open # because we'll redefine open below
# which IFC type must create which FreeCAD type
typesmap = { "Site": ["IfcSite"],
"Building": ["IfcBuilding"],
"Floor": ["IfcBuildingStorey"],
@ -42,13 +43,16 @@ typesmap = { "Site": ["IfcSite"],
"Rebar": ["IfcReinforcingBar"],
"Equipment": ["IfcFurnishingElement","IfcSanitaryTerminal","IfcFlowTerminal","IfcElectricAppliance"]
}
# specific name translations
translationtable = { "Foundation":"Footing",
"Floor":"BuildingStorey",
"Rebar":"ReinforcingBar",
"HydroEquipment":"SanitaryTerminal",
"ElectricEquipment":"ElectricAppliance",
"Furniture":"FurnishingElement"
"Furniture":"FurnishingElement",
"Stair Flight":"StairFlight",
"Curtain Wall":"CurtainWall"
}
ifctemplate = """ISO-10303-21;
@ -70,11 +74,11 @@ DATA;
#10=IFCDIRECTION((0.,1.,0.));
#11=IFCGEOMETRICREPRESENTATIONCONTEXT('Plan','Model',3,1.E-05,#9,#10);
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
#13=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.);
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.01745),#16);
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
#20=IFCPROJECT('$projectid',#5,'$project',$,$,$,$,(#11),#19);
@ -83,11 +87,10 @@ END-ISO-10303-21;
"""
def explore(filename=None,toplevel="IfcRoot"):
"""explore([filename],[toplevel]): opens a dialog showing
def explore(filename=None):
"""explore([filename]): opens a dialog showing
the contents of an IFC file. If no filename is given, a dialog will
pop up to choose a file. toplevel (default IFcRoot) can be used to
limit the display to a certain category of objects and its derivates."""
pop up to choose a file."""
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
DEBUG = p.GetBool("ifcDebug",False)
@ -95,9 +98,8 @@ def explore(filename=None,toplevel="IfcRoot"):
try:
import ifcopenshell
except:
if DEBUG: print "using legacy importer"
import importIFClegacy
return importIFClegacy.explore(filename)
FreeCAD.Console.PrintError("IfcOpenShell was not found on this system. IFC support is disabled\n")
return
if not filename:
from PySide import QtGui
@ -115,7 +117,6 @@ def explore(filename=None,toplevel="IfcRoot"):
print "File not found"
return
ifcopenshell.clean()
ifc = ifcopenshell.open(filename)
tree = QtGui.QTreeWidget()
tree.setColumnCount(3)
@ -130,8 +131,14 @@ def explore(filename=None,toplevel="IfcRoot"):
bold = QtGui.QFont()
bold.setWeight(75)
bold.setBold(True)
entities = ifc.by_type("IfcRoot")
entities += ifc.by_type("IfcRepresentation")
entities += ifc.by_type("IfcRepresentationItem")
entities += ifc.by_type("IfcPlacement")
entities = sorted(entities, key=lambda eid: eid.id())
for entity in ifc.by_type(toplevel):
for entity in entities:
item = QtGui.QTreeWidgetItem(tree)
if hasattr(entity,"id"):
item.setText(0,str(entity.id()))
@ -161,18 +168,18 @@ def explore(filename=None,toplevel="IfcRoot"):
i = 0
while True:
try:
argname = entity.wrapped_data.get_argument_name(i)
argname = entity.attribute_name(i)
except:
break
else:
try:
argvalue = entity.wrapped_data.get_argument(i)
argvalue = getattr(entity,argname)
except:
print "Error in entity ",entity
break
else:
if not argname in ["Id", "GlobalId"]:
if isinstance(argvalue,ifcopenshell.ifc_wrapper.entity_instance):
if isinstance(argvalue,ifcopenshell.entity_instance):
t = "Entity #" + str(argvalue.id()) + ": " + str(argvalue.is_a())
elif isinstance(argvalue,list):
t = ""
@ -183,7 +190,7 @@ def explore(filename=None,toplevel="IfcRoot"):
item.setText(2,str(t))
if isinstance(argvalue,list):
for argitem in argvalue:
if isinstance(argitem,ifcopenshell.ifc_wrapper.entity_instance):
if isinstance(argitem,ifcopenshell.entity_instance):
t = "Entity #" + str(argitem.id()) + ": " + str(argitem.is_a())
else:
t = argitem
@ -221,14 +228,12 @@ def insert(filename,docname,skip=[]):
PREFIX_NUMBERS = p.GetBool("ifcPrefixNumbers",False)
SKIP = p.GetString("ifcSkip","")
SEPARATE_OPENINGS = p.GetBool("ifcSeparateOpenings",False)
SCALE = p.GetFloat("IfcScalingFactor",1.0)
try:
import ifcopenshell
except:
if DEBUG: print "using legacy importer"
import importIFClegacy
return importIFClegacy.insert(filename,docname,skip)
FreeCAD.Console.PrintError("IfcOpenShell was not found on this system. IFC support is disabled\n")
return
if DEBUG: print "opening ",filename,"..."
try:
@ -238,13 +243,17 @@ def insert(filename,docname,skip=[]):
FreeCAD.ActiveDocument = doc
global ifcfile # keeping global for debugging purposes
ifcopenshell.clean()
if isinstance(filename,unicode):
import sys #workaround since ifcopenshell currently can't handle unicode filenames
filename = filename.encode(sys.getfilesystemencoding())
ifcfile = ifcopenshell.open(filename)
shape_attributes = ifcopenshell.SEW_SHELLS
if SEPARATE_OPENINGS: shape_attributes += ifcopenshell.DISABLE_OPENING_SUBTRACTIONS
from ifcopenshell import geom
settings = ifcopenshell.geom.settings()
settings.set(settings.USE_BREP_DATA,True)
settings.set(settings.SEW_SHELLS,True)
settings.set(settings.USE_WORLD_COORDS,True)
if SEPARATE_OPENINGS:
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS,True)
sites = ifcfile.by_type("IfcSite")
buildings = ifcfile.by_type("IfcBuilding")
floors = ifcfile.by_type("IfcBuildingStorey")
@ -264,7 +273,9 @@ def insert(filename,docname,skip=[]):
subtractions.append([r.RelatedOpeningElement.id(), r.RelatingBuildingElement.id()])
for r in ifcfile.by_type("IfcRelDefinesByProperties"):
for obj in r.RelatedObjects:
properties.setdefault(obj.id(),[]).extend([e.id() for e in r.RelatingPropertyDefinition.HasProperties])
if r.RelatingPropertyDefinition.is_a("IfcPropertySet"):
properties.setdefault(obj.id(),[]).extend([e.id() for e in r.RelatingPropertyDefinition.HasProperties])
count = 0
# products
for product in products:
@ -275,17 +286,20 @@ def insert(filename,docname,skip=[]):
if PREFIX_NUMBERS: name = "ID" + str(pid) + " " + name
obj = None
baseobj = None
brep = None
if (ptype == "IfcOpeningElement") and (not SEPARATE_OPENINGS): continue
if pid in skip: continue
if ptype in SKIP: continue
brep = ifcopenshell.create_shape(product,shape_attributes)
if pid in skip: continue # user given id skip list
if ptype in SKIP: continue # preferences-set type skip list
try:
cr = ifcopenshell.geom.create_shape(settings,product)
brep = cr.geometry.brep_data
except:
pass
if brep:
shape = Part.Shape()
shape.importBrepFromString(brep)
if SCALE != 1:
shape.scale(SCALE)
shape.scale(1000.0) # IfcOpenShell always outputs in meters
if not shape.isNull():
baseobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body")
baseobj.Shape = shape
@ -299,6 +313,9 @@ def insert(filename,docname,skip=[]):
tr = dict((v,k) for k, v in translationtable.iteritems())
if r in tr.keys():
r = tr[r]
# remove the "StandardCase"
if "StandardCase" in r:
r = r[:-12]
obj.Role = r
except:
pass
@ -313,7 +330,8 @@ def insert(filename,docname,skip=[]):
if obj:
sh = baseobj.Shape.ShapeType if hasattr(baseobj,"Shape") else "None"
sols = str(baseobj.Shape.Solids) if hasattr(baseobj,"Shape") else ""
if DEBUG: print "creating object ",pid," : ",ptype, " with shape: ",sh," ",sols
pc = str(int((float(count)/len(products)*100)))+"% "
if DEBUG: print pc,"creating object ",pid," : ",ptype, " with shape: ",sh," ",sols
objects[pid] = obj
# properties
@ -322,25 +340,31 @@ def insert(filename,docname,skip=[]):
a = obj.IfcAttributes
for p in properties[pid]:
o = ifcfile[p]
a[o.Name] = str(o.NominalValue)
if o.is_a("IfcPropertySingleValue"):
a[o.Name] = str(o.NominalValue)
obj.IfcAttributes = a
count += 1
FreeCAD.ActiveDocument.recompute()
if DEBUG: print "Processing relationships..."
# subtractions
if SEPARATE_OPENINGS:
for subtraction in subtractions:
if (subtraction[0] in objects.keys()) and (subtraction[1] in objects.keys()):
Arch.removeComponents(objects[subtraction[0]],objects[subtraction[1]])
#print objects[subtraction[0]].Name, objects[subtraction[1]].Name
Arch.removeComponents(objects[subtraction[0]],objects[subtraction[1]])
# additions
for host,children in additions.iteritems():
if host in objects.keys():
cobs = [objects[child] for child in children if child in objects.keys()]
if cobs:
Arch.addComponents(cobs,objects[host])
FreeCAD.ActiveDocument.recompute()
# cleaning bad shapes
for obj in objects.values():
if obj.isDerivedFrom("Part::Feature"):
@ -365,9 +389,8 @@ def export(exportList,filename):
global ifcopenshell
import ifcopenshell
except:
if DEBUG: print "using legacy exporter"
import importIFClegacy
return importIFClegacy.export(exportList,filename)
FreeCAD.Console.PrintError("IfcOpenShell was not found on this system. IFC support is disabled\n")
return
if isinstance(filename,unicode):
import sys #workaround since ifcopenshell currently can't handle unicode filenames
@ -452,9 +475,9 @@ def export(exportList,filename):
if ifctype in ["IfcSlab","IfcFooting","IfcRoof"]:
args = args + ["NOTDEFINED"]
elif ifctype in ["IfcWindow","IfcDoor"]:
args = args + [obj.Width.Value, obj.Height.Value]
args = args + [obj.Width.Value/1000.0, obj.Height.Value/1000.0]
elif ifctype == "IfcSpace":
args = args + ["ELEMENT","INTERNAL",obj.Shape.BoundBox.ZMin]
args = args + ["ELEMENT","INTERNAL",obj.Shape.BoundBox.ZMin/1000.0]
elif ifctype == "IfcBuildingElementProxy":
args = args + ["ELEMENT"]
elif ifctype == "IfcSite":
@ -565,9 +588,12 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
if hasattr(obj.Proxy,"getProfiles"):
p = obj.Proxy.getProfiles(obj,noplacement=True)
extrusionv = obj.Proxy.getExtrusionVector(obj,noplacement=True)
extrusionv.multiply(0.001) # to meters
if (len(p) == 1) and extrusionv:
p = p[0]
p.scale(0.001) # to meters
r = obj.Proxy.getPlacement(obj)
r.Base = r.Base.multiply(0.001) # to meters
if len(p.Edges) == 1:
@ -624,7 +650,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
ovc = ifcfile.createIfcCartesianPoint(tuple(e.Curve.Center)[:2])
plc = ifcfile.createIfcAxis2Placement2D(ovc,xvc)
cir = ifcfile.createIfcCircle(plc,e.Curve.Radius)
curve = ifcfile.createIfcTrimmedCurve(cir,[ifcfile.create_entity("IfcParameterValue",p1)],[ifcfile.create_entity("IfcParameterValue",p2)],follow,"PARAMETER")
curve = ifcfile.createIfcTrimmedCurve(cir,[ifcfile.createIfcParameterValue(p1)],[ifcfile.createIfcParameterValue(p2)],follow,"PARAMETER")
else:
verts = [vertex.Point for vertex in e.Vertexes]
@ -683,6 +709,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
dataset = fcshape.Shells
print "Warning! object contains no solids"
for fcsolid in dataset:
fcsolid.scale(0.001) # to meters
faces = []
curves = False
for fcface in fcsolid.Faces: