Arch: Support of IfcBuildingElement and IfcPropertySets in ifc import/export
This commit is contained in:
@@ -370,7 +370,8 @@ class ViewProviderBuildingPart:
|
||||
c = o.ViewObject.ShapeColor[:3]+(obj.ViewObject.Transparency/100.0,)
|
||||
for i in range(len(o.Shape.Faces)):
|
||||
cols.append(c)
|
||||
obj.ViewObject.DiffuseColor = cols
|
||||
if hasattr(obj.ViewObject,"DiffuseColor"):
|
||||
obj.ViewObject.DiffuseColor = cols
|
||||
|
||||
def onChanged(self,vobj,prop):
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ def addComponents(objectsList,host):
|
||||
if not isinstance(objectsList,list):
|
||||
objectsList = [objectsList]
|
||||
hostType = Draft.getType(host)
|
||||
if hostType in ["Floor","Building","Site"]:
|
||||
if hostType in ["Floor","Building","Site","BuildingPart"]:
|
||||
for o in objectsList:
|
||||
host.addObject(o)
|
||||
elif hostType in ["Wall","Structure","Window","Roof","Stairs","StructuralSystem","Panel"]:
|
||||
@@ -194,11 +194,11 @@ def removeComponents(objectsList,host=None):
|
||||
if o.InList:
|
||||
h = o.InList[0]
|
||||
tp = Draft.getType(h)
|
||||
if tp in ["Floor","Building","Site"]:
|
||||
c = h.Components
|
||||
if tp in ["Floor","Building","Site","BuildingPart"]:
|
||||
c = h.Group
|
||||
if o in c:
|
||||
c.remove(o)
|
||||
h.Components = c
|
||||
h.Group = c
|
||||
o.ViewObject.show()
|
||||
elif tp in ["Wall","Structure"]:
|
||||
a = h.Additions
|
||||
|
||||
@@ -434,7 +434,7 @@ class Component:
|
||||
if not "MoveWithHost" in pl:
|
||||
obj.addProperty("App::PropertyBool","MoveWithHost","Component",QT_TRANSLATE_NOOP("App::Property","Specifies if this object must move together when its host is moved"))
|
||||
if not "IfcProperties" in pl:
|
||||
obj.addProperty("App::PropertyLink","IfcProperties","Component",QT_TRANSLATE_NOOP("App::Property","Custom IFC properties and attributes"))
|
||||
obj.addProperty("App::PropertyMap","IfcProperties","Component",QT_TRANSLATE_NOOP("App::Property","Stores IFC properties"))
|
||||
if not "VerticalArea" in pl:
|
||||
obj.addProperty("App::PropertyArea","VerticalArea","Component",QT_TRANSLATE_NOOP("App::Property","The area of all vertical faces of this object"))
|
||||
obj.setEditorMode("VerticalArea",1)
|
||||
|
||||
@@ -551,7 +551,7 @@ class _Structure(ArchComponent.Component):
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("Arch","This mesh is an invalid solid")+"\n")
|
||||
obj.Base.ViewObject.show()
|
||||
if not base:
|
||||
if (not base) and (not obj.Additions):
|
||||
#FreeCAD.Console.PrintError(translate("Arch","Error: Invalid base object")+"\n")
|
||||
return
|
||||
|
||||
|
||||
@@ -389,12 +389,13 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if DEBUG: print("Building relationships table...",end="")
|
||||
|
||||
# building relations tables
|
||||
|
||||
objects = {} # { id:object, ... }
|
||||
prodrepr = {} # product/representations table
|
||||
additions = {} # { host:[child,...], ... }
|
||||
groups = {} # { host:[child,...], ... } # used in structural IFC
|
||||
subtractions = [] # [ [opening,host], ... ]
|
||||
properties = {} # { obj : { cat : [property, ... ], ... }, ... }
|
||||
properties = {} # { objid : { psetid : [propertyid, ... ], ... }, ... }
|
||||
colors = {} # { id:(r,g,b) }
|
||||
shapes = {} # { id:shaoe } only used for merge mode
|
||||
structshapes = {} # { id:shaoe } only used for merge mode
|
||||
@@ -413,12 +414,12 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
for obj in r.RelatedObjects:
|
||||
if not obj.id() in properties:
|
||||
properties[obj.id()] = {}
|
||||
prop_by_category = {}
|
||||
prop = []
|
||||
psets = {}
|
||||
props = []
|
||||
if r.RelatingPropertyDefinition.is_a("IfcPropertySet"):
|
||||
prop.extend([e.id() for e in r.RelatingPropertyDefinition.HasProperties])
|
||||
prop_by_category[r.RelatingPropertyDefinition.id()] = prop
|
||||
properties[obj.id()].update(prop_by_category)
|
||||
props.extend([prop.id() for prop in r.RelatingPropertyDefinition.HasProperties])
|
||||
psets[r.RelatingPropertyDefinition.id()] = props
|
||||
properties[obj.id()].update(psets)
|
||||
for r in ifcfile.by_type("IfcRelAssociatesMaterial"):
|
||||
for o in r.RelatedObjects:
|
||||
if r.RelatingMaterial.is_a("IfcMaterial"):
|
||||
@@ -482,7 +483,8 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
import FreeCADGui
|
||||
FreeCADGui.ActiveDocument.activeView().viewAxonometric()
|
||||
|
||||
# products
|
||||
# handle IFC products
|
||||
|
||||
for product in products:
|
||||
|
||||
count += 1
|
||||
@@ -493,6 +495,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if DEBUG: print(count,"/",len(products)," creating object #",pid," : ",ptype,end="")
|
||||
|
||||
# checking for full FreeCAD parametric definition, overriding everything else
|
||||
|
||||
if pid in properties.keys():
|
||||
if "FreeCADPropertySet" in [ifcfile[pset].Name for pset in properties[pid].keys()]:
|
||||
if DEBUG: print(" restoring from parametric definition...",end="")
|
||||
@@ -535,6 +538,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
continue
|
||||
|
||||
# detect if this object is sharing its shape
|
||||
|
||||
clone = None
|
||||
store = None
|
||||
prepr = None
|
||||
@@ -639,6 +643,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if MERGE_MODE_ARCH == 0 and archobj:
|
||||
|
||||
# full Arch objects
|
||||
|
||||
for freecadtype,ifctypes in typesmap.items():
|
||||
if ptype in ifctypes:
|
||||
if clone:
|
||||
@@ -666,6 +671,11 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
print("failed to compute placement ",)
|
||||
else:
|
||||
obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name)
|
||||
if (freecadtype in ["Structure","Wall"]) and not baseobj:
|
||||
# remove sizes to prevent auto shape creation
|
||||
obj.Height = 0
|
||||
obj.Width = 0
|
||||
obj.Length = 0
|
||||
if store:
|
||||
sharedobjects[store] = obj
|
||||
obj.Label = name
|
||||
@@ -675,7 +685,9 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if FreeCAD.GuiUp and baseobj:
|
||||
if hasattr(baseobj,"ViewObject"):
|
||||
baseobj.ViewObject.hide()
|
||||
|
||||
# setting role
|
||||
|
||||
try:
|
||||
if hasattr(obj,"IfcRole"):
|
||||
obj.IfcRole = ptype[3:]
|
||||
@@ -691,14 +703,18 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
obj.Role = r
|
||||
except:
|
||||
print("Unable to give IFC role ",ptype," to object ",obj.Label)
|
||||
|
||||
# setting uid
|
||||
|
||||
if hasattr(obj,"IfcAttributes"):
|
||||
a = obj.IfcAttributes
|
||||
a["IfcUID"] = str(guid)
|
||||
obj.IfcAttributes = a
|
||||
break
|
||||
|
||||
if not obj:
|
||||
obj = Arch.makeComponent(baseobj,name=name)
|
||||
|
||||
if obj:
|
||||
sols = str(obj.Shape.Solids) if hasattr(obj,"Shape") else ""
|
||||
if DEBUG: print(sols,end="")
|
||||
@@ -707,6 +723,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
elif (MERGE_MODE_ARCH == 1 and archobj) or (MERGE_MODE_STRUCT == 0 and not archobj):
|
||||
|
||||
# non-parametric Arch objects
|
||||
|
||||
if ptype in ["IfcSite","IfcBuilding","IfcBuildingStorey"]:
|
||||
for freecadtype,ifctypes in typesmap.items():
|
||||
if ptype in ifctypes:
|
||||
@@ -717,6 +734,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
elif (MERGE_MODE_ARCH == 2 and archobj) or (MERGE_MODE_STRUCT == 1 and not archobj):
|
||||
|
||||
# Part shapes
|
||||
|
||||
if ptype in ["IfcSite","IfcBuilding","IfcBuildingStorey"]:
|
||||
for freecadtype,ifctypes in typesmap.items():
|
||||
if ptype in ifctypes:
|
||||
@@ -732,9 +750,20 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
obj.Label = name
|
||||
objects[pid] = obj
|
||||
|
||||
# properties
|
||||
# handle properties
|
||||
|
||||
if pid in properties:
|
||||
if IMPORT_PROPERTIES and hasattr(obj,"IfcProperties"):
|
||||
|
||||
# treat as spreadsheet (pref option)
|
||||
|
||||
if isinstance(obj.IfcProperties,dict):
|
||||
|
||||
# fix property type if needed
|
||||
|
||||
obj.removeProperty("IfcProperties")
|
||||
obj.addProperty("App::PropertyLink","IfcProperties","Component","Stores IFC properties as a spreadsheet")
|
||||
|
||||
ifc_spreadsheet = Arch.makeIfcSpreadsheet()
|
||||
n=2
|
||||
for c in properties[pid].keys():
|
||||
@@ -762,7 +791,34 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
ifc_spreadsheet.set(str('E'+str(n)), str(l.NominalValue.Unit))
|
||||
n += 1
|
||||
obj.IfcProperties = ifc_spreadsheet
|
||||
|
||||
elif hasattr(obj,"IfcProperties") and isinstance(obj.IfcProperties,dict):
|
||||
|
||||
# 0.18 behaviour: properties are saved as pset;;type;;value in IfcProperties
|
||||
|
||||
d = obj.IfcProperties
|
||||
for pset in properties[pid].keys():
|
||||
psetname = ifcfile[pset].Name.encode("utf8")
|
||||
for prop in properties[pid][pset]:
|
||||
e = ifcfile[prop]
|
||||
pname = e.Name.encode("utf8")
|
||||
if e.is_a("IfcPropertySingleValue"):
|
||||
ptype = e.NominalValue.is_a()
|
||||
if ptype in ['IfcLabel','IfcText','IfcIdentifier','IfcDescriptiveMeasure']:
|
||||
pvalue = e.NominalValue.wrappedValue.encode("utf8")
|
||||
else:
|
||||
pvalue = str(e.NominalValue.wrappedValue)
|
||||
if hasattr(e.NominalValue,'Unit'):
|
||||
if e.NominalValue.Unit:
|
||||
pvalue += e.NominalValue.Unit
|
||||
d[pname] = psetname+";;"+ptype+";;"+pvalue
|
||||
#print ("adding property: ",pname,ptype,pvalue," pset ",psetname)
|
||||
obj.IfcProperties = d
|
||||
|
||||
elif hasattr(obj,"IfcAttributes"):
|
||||
|
||||
# 0.17: properties are saved as type(value) in IfcAttributes
|
||||
|
||||
a = obj.IfcAttributes
|
||||
for c in properties[pid].keys():
|
||||
for p in properties[pid][c]:
|
||||
@@ -772,6 +828,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
obj.IfcAttributes = a
|
||||
|
||||
# color
|
||||
|
||||
if FreeCAD.GuiUp and (pid in colors) and hasattr(obj.ViewObject,"ShapeColor"):
|
||||
#if DEBUG: print(" setting color: ",int(colors[pid][0]*255),"/",int(colors[pid][1]*255),"/",int(colors[pid][2]*255))
|
||||
obj.ViewObject.ShapeColor = colors[pid]
|
||||
@@ -780,6 +837,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if DEBUG: FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
# attached 2D elements
|
||||
|
||||
if product.Representation:
|
||||
for r in product.Representation.Representations:
|
||||
if r.RepresentationIdentifier == "FootPrint":
|
||||
@@ -819,6 +877,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if DEBUG: print("Processing Struct relationships...",end="")
|
||||
|
||||
# groups
|
||||
|
||||
for host,children in groups.items():
|
||||
if ifcfile[host].is_a("IfcStructuralAnalysisModel"):
|
||||
# print(host, ' --> ', children)
|
||||
@@ -850,6 +909,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
Arch.rebuildArchShape(obj)
|
||||
|
||||
# processing remaining (normal) groups
|
||||
|
||||
for host,children in groups.items():
|
||||
if ifcfile[host].is_a("IfcGroup"):
|
||||
if ifcfile[host].Name:
|
||||
@@ -898,6 +958,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if DEBUG: print("Processing Arch relationships...",end="")
|
||||
|
||||
# subtractions
|
||||
|
||||
if SEPARATE_OPENINGS:
|
||||
for subtraction in subtractions:
|
||||
if (subtraction[0] in objects.keys()) and (subtraction[1] in objects.keys()):
|
||||
@@ -906,11 +967,12 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if DEBUG: FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
# additions
|
||||
|
||||
for host,children in additions.items():
|
||||
if host in objects.keys():
|
||||
cobs = [objects[child] for child in children if child in objects.keys()]
|
||||
if cobs:
|
||||
if DEBUG and (len(cobs) > 10) and (not(Draft.getType(objects[host]) in ["Site","Building","Floor"])):
|
||||
if DEBUG and (len(cobs) > 10) and (not(Draft.getType(objects[host]) in ["Site","Building","Floor","BuildingPart"])):
|
||||
# avoid huge fusions
|
||||
print("more than 10 shapes to add: skipping.")
|
||||
else:
|
||||
@@ -923,6 +985,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
# cleaning bad shapes
|
||||
|
||||
for obj in objects.values():
|
||||
if obj.isDerivedFrom("Part::Feature"):
|
||||
if obj.Shape.isNull() and not(Draft.getType(obj) in ["Site"]):
|
||||
@@ -1015,6 +1078,8 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
|
||||
|
||||
def export(exportList,filename):
|
||||
|
||||
|
||||
"exports FreeCAD contents to an IFC file"
|
||||
|
||||
getPreferences()
|
||||
@@ -1076,6 +1141,7 @@ def export(exportList,filename):
|
||||
if FULL_PARAMETRIC:
|
||||
objectslist = Arch.getAllChildren(objectslist)
|
||||
products = {} # { Name: IfcEntity, ... }
|
||||
subproducts = {} # { Name: IfcEntity, ... } for storing additions/subtractions and other types of subcomponents of a product
|
||||
surfstyles = {} # { (r,g,b): IfcEntity, ... }
|
||||
clones = {} # { Basename:[Clonename1,Clonename2,...] }
|
||||
sharedobjects = {} # { BaseName: IfcRepresentationMap }
|
||||
@@ -1085,6 +1151,7 @@ def export(exportList,filename):
|
||||
shapedefs = {} # { ShapeDefString:[shapes],... }
|
||||
|
||||
# build clones table
|
||||
|
||||
if CREATE_CLONES:
|
||||
for o in objectslist:
|
||||
b = Draft.getCloneBase(o,strict=True)
|
||||
@@ -1095,18 +1162,22 @@ def export(exportList,filename):
|
||||
#print(objectslist)
|
||||
|
||||
# testing if more than one site selected (forbidden in IFC)
|
||||
|
||||
if len(Draft.getObjectsOfType(objectslist,"Site")) > 1:
|
||||
FreeCAD.Console.PrintError("More than one site is selected, which is forbidden by IFC standards. Please export only one site by IFC file.\n")
|
||||
return
|
||||
|
||||
# products
|
||||
|
||||
for obj in objectslist:
|
||||
|
||||
# getting generic data
|
||||
|
||||
name = str(obj.Label.encode("utf8"))
|
||||
description = str(obj.Description.encode("utf8")) if hasattr(obj,"Description") else ""
|
||||
|
||||
# getting uid
|
||||
|
||||
uid = None
|
||||
if hasattr(obj,"IfcAttributes"):
|
||||
if "IfcUID" in obj.IfcAttributes.keys():
|
||||
@@ -1120,6 +1191,7 @@ def export(exportList,filename):
|
||||
obj.IfcAttributes = d
|
||||
|
||||
# setting the IFC type + name conversions
|
||||
|
||||
if hasattr(obj,"IfcRole"):
|
||||
ifctype = obj.IfcRole.replace(" ","")
|
||||
elif hasattr(obj,"Role"):
|
||||
@@ -1139,6 +1211,7 @@ def export(exportList,filename):
|
||||
ifctype = "IfcBuildingElementProxy"
|
||||
|
||||
# getting the "Force BREP" flag
|
||||
|
||||
brepflag = False
|
||||
if hasattr(obj,"IfcAttributes"):
|
||||
if "FlagForceBrep" in obj.IfcAttributes.keys():
|
||||
@@ -1146,11 +1219,13 @@ def export(exportList,filename):
|
||||
brepflag = True
|
||||
|
||||
# getting the representation
|
||||
|
||||
representation,placement,shapetype = getRepresentation(ifcfile,context,obj,forcebrep=(brepflag or FORCE_BREP))
|
||||
|
||||
if DEBUG: print(str(count).ljust(3)," : ", ifctype, " (",shapetype,") : ",name)
|
||||
|
||||
# setting the arguments
|
||||
|
||||
kwargs = {"GlobalId": uid, "OwnerHistory": history, "Name": name,
|
||||
"Description": description, "ObjectPlacement": placement, "Representation": representation}
|
||||
if ifctype in ["IfcSlab","IfcFooting","IfcRoof"]:
|
||||
@@ -1187,19 +1262,23 @@ def export(exportList,filename):
|
||||
"BarLength": obj.Length.Value})
|
||||
|
||||
# creating the product
|
||||
|
||||
#print(obj.Label," : ",ifctype," : ",kwargs)
|
||||
product = getattr(ifcfile,"create"+ifctype)(**kwargs)
|
||||
products[obj.Name] = product
|
||||
|
||||
# additions
|
||||
if hasattr(obj,"Additions") and (shapetype == "extrusion"):
|
||||
|
||||
if hasattr(obj,"Additions") and (shapetype in ["extrusion","no shape"]):
|
||||
for o in obj.Additions:
|
||||
r2,p2,c2 = getRepresentation(ifcfile,context,o,forcebrep=True)
|
||||
r2,p2,c2 = getRepresentation(ifcfile,context,o)
|
||||
if DEBUG: print(" adding ",c2," : ",o.Label)
|
||||
prod2 = ifcfile.createIfcBuildingElementProxy(ifcopenshell.guid.compress(uuid.uuid1().hex),history,o.Label.encode("utf8"),None,None,p2,r2,None,"ELEMENT")
|
||||
subproducts[o.Name] = prod2
|
||||
ifcfile.createIfcRelAggregates(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'Addition','',product,[prod2])
|
||||
|
||||
# subtractions
|
||||
|
||||
guests = []
|
||||
for o in obj.InList:
|
||||
if hasattr(o,"Hosts"):
|
||||
@@ -1207,20 +1286,67 @@ def export(exportList,filename):
|
||||
if co == obj:
|
||||
if not o in guests:
|
||||
guests.append(o)
|
||||
if hasattr(obj,"Subtractions") and (shapetype == "extrusion"):
|
||||
if hasattr(obj,"Subtractions") and (shapetype in ["extrusion","no shape"]):
|
||||
for o in obj.Subtractions + guests:
|
||||
r2,p2,c2 = getRepresentation(ifcfile,context,o,forcebrep=True,subtraction=True)
|
||||
r2,p2,c2 = getRepresentation(ifcfile,context,o,subtraction=True)
|
||||
if DEBUG: print(" subtracting ",c2," : ",o.Label)
|
||||
prod2 = ifcfile.createIfcOpeningElement(ifcopenshell.guid.compress(uuid.uuid1().hex),history,o.Label.encode("utf8"),None,None,p2,r2,None)
|
||||
subproducts[o.Name] = prod2
|
||||
ifcfile.createIfcRelVoidsElement(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'Subtraction','',product,prod2)
|
||||
|
||||
# properties
|
||||
|
||||
ifcprop = False
|
||||
if hasattr(obj,"IfcProperties"):
|
||||
|
||||
if obj.IfcProperties:
|
||||
|
||||
ifcprop = True
|
||||
if DEBUG: print(" adding ifc properties")
|
||||
if obj.IfcProperties.TypeId == 'Spreadsheet::Sheet':
|
||||
|
||||
if isinstance(obj.IfcProperties,dict):
|
||||
|
||||
# IfcProperties is a dictionary
|
||||
|
||||
psets = {}
|
||||
for key,value in obj.IfcProperties.items():
|
||||
|
||||
# properties in IfcProperties dict are stored as "key":"pset;;type;;value" or "key":"type;;value"
|
||||
|
||||
value = value.split(";;")
|
||||
if len(value) == 3:
|
||||
pset = value[0]
|
||||
ptype = value[1]
|
||||
pvalue = value[2]
|
||||
elif len(value) == 2:
|
||||
pset = "Default property set"
|
||||
ptype = value[0]
|
||||
pvalue = value[1]
|
||||
else:
|
||||
if DEBUG:print(" unable to export property:",key,value)
|
||||
continue
|
||||
|
||||
#if DEBUG: print(" property ",key," : ",pvalue.encode("utf8"), " (", str(ptype), ") in ",pset)
|
||||
if ptype in ["IfcLabel","IfcText","IfcIdentifier",'IfcDescriptiveMeasure']:
|
||||
pvalue = pvalue.encode("utf8")
|
||||
elif ptype == "IfcBoolean":
|
||||
if pvalue == ".T.":
|
||||
pvalue = True
|
||||
else:
|
||||
pvalue = False
|
||||
elif ptype == "IfcInteger":
|
||||
pvalue = int(pvalue)
|
||||
else:
|
||||
pvalue = float(pvalue)
|
||||
p = ifcfile.createIfcPropertySingleValue(str(key),None,ifcfile.create_entity(str(ptype),pvalue),None)
|
||||
psets.setdefault(pset,[]).append(p)
|
||||
for pname,props in psets.items():
|
||||
pset = ifcfile.createIfcPropertySet(ifcopenshell.guid.compress(uuid.uuid1().hex),history,pname,None,props)
|
||||
ifcfile.createIfcRelDefinesByProperties(ifcopenshell.guid.compress(uuid.uuid1().hex),history,None,None,[product],pset)
|
||||
|
||||
elif obj.IfcProperties.TypeId == 'Spreadsheet::Sheet':
|
||||
|
||||
# IfcProperties is a spreadsheet (deprecated)
|
||||
|
||||
sheet = obj.IfcProperties
|
||||
propertiesDic = {}
|
||||
categories = []
|
||||
@@ -1271,13 +1397,18 @@ def export(exportList,filename):
|
||||
props.append(ifcfile.createIfcPropertySingleValue(prop["key"],None,ifcfile.create_entity(prop["tp"],prop["val"]),None))
|
||||
pset = ifcfile.createIfcPropertySet(ifcopenshell.guid.compress(uuid.uuid1().hex),history,cat,None,props)
|
||||
ifcfile.createIfcRelDefinesByProperties(ifcopenshell.guid.compress(uuid.uuid1().hex),history,None,None,[product],pset)
|
||||
if (not ifcprop) and hasattr(obj,"IfcAttributes"):
|
||||
|
||||
if hasattr(obj,"IfcAttributes"):
|
||||
|
||||
if obj.IfcAttributes:
|
||||
ifcprop = True
|
||||
#if DEBUG : print(" adding ifc attributes")
|
||||
props = []
|
||||
for key in obj.IfcAttributes:
|
||||
if not (key in ["IfcUID","FlagForceBrep"]):
|
||||
|
||||
# (deprecated) properties in IfcAttributes dict are stored as "key":"type(value)"
|
||||
|
||||
r = obj.IfcAttributes[key].strip(")").split("(")
|
||||
if len(r) == 1:
|
||||
tp = "IfcText"
|
||||
@@ -1287,7 +1418,7 @@ def export(exportList,filename):
|
||||
val = "(".join(r[1:])
|
||||
val = val.strip("'")
|
||||
val = val.strip('"')
|
||||
if DEBUG: print(" property ",key," : ",val.encode("utf8"), " (", str(tp), ")")
|
||||
#if DEBUG: print(" property ",key," : ",val.encode("utf8"), " (", str(tp), ")")
|
||||
if tp in ["IfcLabel","IfcText","IfcIdentifier",'IfcDescriptiveMeasure']:
|
||||
val = val.encode("utf8")
|
||||
elif tp == "IfcBoolean":
|
||||
@@ -1303,9 +1434,11 @@ def export(exportList,filename):
|
||||
if props:
|
||||
pset = ifcfile.createIfcPropertySet(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'PropertySet',None,props)
|
||||
ifcfile.createIfcRelDefinesByProperties(ifcopenshell.guid.compress(uuid.uuid1().hex),history,None,None,[product],pset)
|
||||
|
||||
if not ifcprop:
|
||||
#if DEBUG : print("no ifc properties to export")
|
||||
pass
|
||||
|
||||
if FULL_PARAMETRIC:
|
||||
# exporting all the object properties
|
||||
FreeCADProps = []
|
||||
@@ -1376,12 +1509,13 @@ def export(exportList,filename):
|
||||
count += 1
|
||||
|
||||
# relationships
|
||||
|
||||
sites = []
|
||||
buildings = []
|
||||
floors = []
|
||||
treated = []
|
||||
defaulthost = []
|
||||
for floor in Draft.getObjectsOfType(objectslist,"Floor"):
|
||||
for floor in Draft.getObjectsOfType(objectslist,"Floor")+Draft.getObjectsOfType(objectslist,"BuildingPart"):
|
||||
objs = Draft.getGroupContents(floor,walls=True)
|
||||
objs = Arch.pruneIncluded(objs)
|
||||
children = []
|
||||
@@ -1453,6 +1587,7 @@ def export(exportList,filename):
|
||||
ifcfile.createIfcRelContainedInSpatialStructure(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'UnassignedObjectsLink','',untreated,defaulthost)
|
||||
|
||||
# materials
|
||||
|
||||
materials = {}
|
||||
for m in Arch.getDocumentMaterials():
|
||||
relobjs = []
|
||||
@@ -1464,6 +1599,8 @@ def export(exportList,filename):
|
||||
if o.Material.Name == m.Name:
|
||||
if o.Name in products:
|
||||
relobjs.append(products[o.Name])
|
||||
elif o.Name in subproducts:
|
||||
relobjs.append(subproducts[o.Name])
|
||||
if relobjs:
|
||||
mat = ifcfile.createIfcMaterial(m.Label.encode("utf8"))
|
||||
materials[m.Label] = mat
|
||||
@@ -1485,6 +1622,7 @@ def export(exportList,filename):
|
||||
ifcfile.createIfcRelAssociatesMaterial(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'MaterialLink','',relobjs,mat)
|
||||
|
||||
# groups
|
||||
|
||||
sortedgroups = []
|
||||
while groups:
|
||||
for g in groups.keys():
|
||||
@@ -1583,7 +1721,9 @@ def export(exportList,filename):
|
||||
|
||||
|
||||
def createFromProperties(propsets,ifcfile):
|
||||
|
||||
"creates a FreeCAD parametric object from a set of properties"
|
||||
|
||||
obj = None
|
||||
sets = []
|
||||
global parametrics
|
||||
@@ -1660,6 +1800,7 @@ def createFromProperties(propsets,ifcfile):
|
||||
|
||||
|
||||
def createCurve(ifcfile,wire):
|
||||
|
||||
"creates an IfcCompositeCurve from a shape"
|
||||
|
||||
segments = []
|
||||
@@ -1714,6 +1855,7 @@ def createCurve(ifcfile,wire):
|
||||
|
||||
|
||||
def getProfile(ifcfile,p):
|
||||
|
||||
"""returns an IFC profile definition from a shape"""
|
||||
|
||||
import Part,DraftGeomUtils
|
||||
@@ -1761,6 +1903,7 @@ def getProfile(ifcfile,p):
|
||||
|
||||
|
||||
def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tessellation=1):
|
||||
|
||||
"""returns an IfcShapeRepresentation object or None"""
|
||||
|
||||
import Part,math,DraftGeomUtils,DraftVecUtils
|
||||
@@ -1772,6 +1915,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
||||
subplacement = None
|
||||
|
||||
# check for clones
|
||||
|
||||
if (not subtraction) and (not forcebrep):
|
||||
for k,v in clones.items():
|
||||
if (obj.Name == k) or (obj.Name in v):
|
||||
@@ -1862,145 +2006,159 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
||||
shapetype = "extrusion"
|
||||
|
||||
if not shapes:
|
||||
# brep representation
|
||||
fcshape = None
|
||||
solidType = "Brep"
|
||||
if subtraction:
|
||||
if hasattr(obj,"Proxy"):
|
||||
if hasattr(obj.Proxy,"getSubVolume"):
|
||||
fcshape = obj.Proxy.getSubVolume(obj)
|
||||
if not fcshape:
|
||||
if obj.isDerivedFrom("Part::Feature"):
|
||||
if False: # below is buggy. No way to duplicate shapes that way?
|
||||
#if hasattr(obj,"Base") and hasattr(obj,"Additions")and hasattr(obj,"Subtractions"):
|
||||
if obj.Base and (not obj.Additions) and not(obj.Subtractions):
|
||||
if obj.Base.isDerivedFrom("Part::Feature"):
|
||||
if obj.Base.Shape:
|
||||
if obj.Base.Shape.Solids:
|
||||
fcshape = obj.Base.Shape
|
||||
subplacement = FreeCAD.Placement(obj.Placement)
|
||||
if not fcshape:
|
||||
if obj.Shape:
|
||||
if not obj.Shape.isNull():
|
||||
fcshape = obj.Shape
|
||||
if fcshape:
|
||||
shapedef = str([v.Point for v in fcshape.Vertexes])
|
||||
if shapedef in shapedefs:
|
||||
shapes = shapedefs[shapedef]
|
||||
shapetype = "reusing brep"
|
||||
else:
|
||||
# new ifcopenshell serializer
|
||||
from ifcopenshell import geom
|
||||
serialized = False
|
||||
if hasattr(geom,"serialise") and obj.isDerivedFrom("Part::Feature") and SERIALIZE:
|
||||
if obj.Shape.Faces:
|
||||
sh = obj.Shape.copy()
|
||||
sh.scale(0.001) # to meters
|
||||
p = geom.serialise(sh.exportBrepToString())
|
||||
if p:
|
||||
productdef = ifcfile.add(p)
|
||||
for rep in productdef.Representations:
|
||||
rep.ContextOfItems = context
|
||||
xvc = ifcfile.createIfcDirection((1.0,0.0,0.0))
|
||||
zvc = ifcfile.createIfcDirection((0.0,0.0,1.0))
|
||||
ovc = ifcfile.createIfcCartesianPoint((0.0,0.0,0.0))
|
||||
gpl = ifcfile.createIfcAxis2Placement3D(ovc,zvc,xvc)
|
||||
placement = ifcfile.createIfcLocalPlacement(None,gpl)
|
||||
shapetype = "advancedbrep"
|
||||
shapes = None
|
||||
serialized = True
|
||||
if not serialized:
|
||||
# old method
|
||||
solids = []
|
||||
if fcshape.Solids:
|
||||
dataset = fcshape.Solids
|
||||
else:
|
||||
dataset = fcshape.Shells
|
||||
#if DEBUG: print "Warning! object contains no solids"
|
||||
|
||||
# if this is a clone, place back the shapes in null position
|
||||
if tostore:
|
||||
for shape in dataset:
|
||||
shape.Placement = FreeCAD.Placement()
|
||||
# check if we keep a null shape (additions-only object)
|
||||
|
||||
for fcsolid in dataset:
|
||||
fcsolid.scale(0.001) # to meters
|
||||
faces = []
|
||||
curves = False
|
||||
shapetype = "brep"
|
||||
for fcface in fcsolid.Faces:
|
||||
for e in fcface.Edges:
|
||||
if DraftGeomUtils.geomType(e) != "Line":
|
||||
from FreeCAD import Base
|
||||
try:
|
||||
if e.curvatureAt(e.FirstParameter+(e.LastParameter-e.FirstParameter)/2) > 0.0001:
|
||||
curves = True
|
||||
break
|
||||
except Part.OCCError:
|
||||
pass
|
||||
except Base.FreeCADError:
|
||||
pass
|
||||
if curves:
|
||||
joinfacets = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ifcJoinCoplanarFacets",False)
|
||||
usedae = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ifcUseDaeOptions",False)
|
||||
if joinfacets:
|
||||
result = Arch.removeCurves(fcsolid,dae=usedae)
|
||||
if result:
|
||||
fcsolid = result
|
||||
else:
|
||||
# fall back to standard triangulation
|
||||
joinfacets = False
|
||||
if not joinfacets:
|
||||
shapetype = "triangulated"
|
||||
if usedae:
|
||||
import importDAE
|
||||
tris = importDAE.triangulate(fcsolid)
|
||||
else:
|
||||
tris = fcsolid.tessellate(tessellation)
|
||||
for tri in tris[1]:
|
||||
pts = [ifcfile.createIfcCartesianPoint(tuple(tris[0][i])) for i in tri]
|
||||
loop = ifcfile.createIfcPolyLoop(pts)
|
||||
bound = ifcfile.createIfcFaceOuterBound(loop,True)
|
||||
face = ifcfile.createIfcFace([bound])
|
||||
faces.append(face)
|
||||
fcsolid = Part.Shape() # empty shape so below code is not executed
|
||||
if (hasattr(obj,"Base") and hasattr(obj,"Width") and hasattr(obj,"Height")) and (not obj.Base) and obj.Additions and (not obj.Width.Value) and (not obj.Height.Value):
|
||||
shapes = None
|
||||
|
||||
for fcface in fcsolid.Faces:
|
||||
loops = []
|
||||
verts = [v.Point for v in fcface.OuterWire.OrderedVertexes]
|
||||
c = fcface.CenterOfMass
|
||||
v1 = verts[0].sub(c)
|
||||
v2 = verts[1].sub(c)
|
||||
n = fcface.normalAt(0,0)
|
||||
if DraftVecUtils.angle(v2,v1,n) >= 0:
|
||||
verts.reverse() # inverting verts order if the direction is couterclockwise
|
||||
pts = [ifcfile.createIfcCartesianPoint(tuple(v)) for v in verts]
|
||||
loop = ifcfile.createIfcPolyLoop(pts)
|
||||
bound = ifcfile.createIfcFaceOuterBound(loop,True)
|
||||
loops.append(bound)
|
||||
for wire in fcface.Wires:
|
||||
if wire.hashCode() != fcface.OuterWire.hashCode():
|
||||
verts = [v.Point for v in wire.OrderedVertexes]
|
||||
if len(verts) > 1:
|
||||
v1 = verts[0].sub(c)
|
||||
v2 = verts[1].sub(c)
|
||||
if DraftVecUtils.angle(v2,v1,DraftVecUtils.neg(n)) >= 0:
|
||||
verts.reverse()
|
||||
pts = [ifcfile.createIfcCartesianPoint(tuple(v)) for v in verts]
|
||||
loop = ifcfile.createIfcPolyLoop(pts)
|
||||
bound = ifcfile.createIfcFaceBound(loop,True)
|
||||
loops.append(bound)
|
||||
else:
|
||||
|
||||
# brep representation
|
||||
|
||||
fcshape = None
|
||||
solidType = "Brep"
|
||||
if subtraction:
|
||||
if hasattr(obj,"Proxy"):
|
||||
if hasattr(obj.Proxy,"getSubVolume"):
|
||||
fcshape = obj.Proxy.getSubVolume(obj)
|
||||
if not fcshape:
|
||||
if obj.isDerivedFrom("Part::Feature"):
|
||||
if False: # below is buggy. No way to duplicate shapes that way?
|
||||
#if hasattr(obj,"Base") and hasattr(obj,"Additions")and hasattr(obj,"Subtractions"):
|
||||
if obj.Base and (not obj.Additions) and not(obj.Subtractions):
|
||||
if obj.Base.isDerivedFrom("Part::Feature"):
|
||||
if obj.Base.Shape:
|
||||
if obj.Base.Shape.Solids:
|
||||
fcshape = obj.Base.Shape
|
||||
subplacement = FreeCAD.Placement(obj.Placement)
|
||||
if not fcshape:
|
||||
if obj.Shape:
|
||||
if not obj.Shape.isNull():
|
||||
fcshape = obj.Shape
|
||||
if fcshape:
|
||||
shapedef = str([v.Point for v in fcshape.Vertexes])
|
||||
if shapedef in shapedefs:
|
||||
shapes = shapedefs[shapedef]
|
||||
shapetype = "reusing brep"
|
||||
else:
|
||||
|
||||
# new ifcopenshell serializer
|
||||
|
||||
from ifcopenshell import geom
|
||||
serialized = False
|
||||
if hasattr(geom,"serialise") and obj.isDerivedFrom("Part::Feature") and SERIALIZE:
|
||||
if obj.Shape.Faces:
|
||||
sh = obj.Shape.copy()
|
||||
sh.scale(0.001) # to meters
|
||||
p = geom.serialise(sh.exportBrepToString())
|
||||
if p:
|
||||
productdef = ifcfile.add(p)
|
||||
for rep in productdef.Representations:
|
||||
rep.ContextOfItems = context
|
||||
xvc = ifcfile.createIfcDirection((1.0,0.0,0.0))
|
||||
zvc = ifcfile.createIfcDirection((0.0,0.0,1.0))
|
||||
ovc = ifcfile.createIfcCartesianPoint((0.0,0.0,0.0))
|
||||
gpl = ifcfile.createIfcAxis2Placement3D(ovc,zvc,xvc)
|
||||
placement = ifcfile.createIfcLocalPlacement(None,gpl)
|
||||
shapetype = "advancedbrep"
|
||||
shapes = None
|
||||
serialized = True
|
||||
|
||||
if not serialized:
|
||||
|
||||
# old method
|
||||
|
||||
solids = []
|
||||
if fcshape.Solids:
|
||||
dataset = fcshape.Solids
|
||||
else:
|
||||
dataset = fcshape.Shells
|
||||
#if DEBUG: print "Warning! object contains no solids"
|
||||
|
||||
# if this is a clone, place back the shapes in null position
|
||||
if tostore:
|
||||
for shape in dataset:
|
||||
shape.Placement = FreeCAD.Placement()
|
||||
|
||||
for fcsolid in dataset:
|
||||
fcsolid.scale(0.001) # to meters
|
||||
faces = []
|
||||
curves = False
|
||||
shapetype = "brep"
|
||||
for fcface in fcsolid.Faces:
|
||||
for e in fcface.Edges:
|
||||
if DraftGeomUtils.geomType(e) != "Line":
|
||||
from FreeCAD import Base
|
||||
try:
|
||||
if e.curvatureAt(e.FirstParameter+(e.LastParameter-e.FirstParameter)/2) > 0.0001:
|
||||
curves = True
|
||||
break
|
||||
except Part.OCCError:
|
||||
pass
|
||||
except Base.FreeCADError:
|
||||
pass
|
||||
if curves:
|
||||
joinfacets = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ifcJoinCoplanarFacets",False)
|
||||
usedae = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ifcUseDaeOptions",False)
|
||||
if joinfacets:
|
||||
result = Arch.removeCurves(fcsolid,dae=usedae)
|
||||
if result:
|
||||
fcsolid = result
|
||||
else:
|
||||
print ("Warning: wire with one/no vertex in ",obj.Label)
|
||||
face = ifcfile.createIfcFace(loops)
|
||||
faces.append(face)
|
||||
# fall back to standard triangulation
|
||||
joinfacets = False
|
||||
if not joinfacets:
|
||||
shapetype = "triangulated"
|
||||
if usedae:
|
||||
import importDAE
|
||||
tris = importDAE.triangulate(fcsolid)
|
||||
else:
|
||||
tris = fcsolid.tessellate(tessellation)
|
||||
for tri in tris[1]:
|
||||
pts = [ifcfile.createIfcCartesianPoint(tuple(tris[0][i])) for i in tri]
|
||||
loop = ifcfile.createIfcPolyLoop(pts)
|
||||
bound = ifcfile.createIfcFaceOuterBound(loop,True)
|
||||
face = ifcfile.createIfcFace([bound])
|
||||
faces.append(face)
|
||||
fcsolid = Part.Shape() # empty shape so below code is not executed
|
||||
|
||||
if faces:
|
||||
shell = ifcfile.createIfcClosedShell(faces)
|
||||
shape = ifcfile.createIfcFacetedBrep(shell)
|
||||
shapes.append(shape)
|
||||
for fcface in fcsolid.Faces:
|
||||
loops = []
|
||||
verts = [v.Point for v in fcface.OuterWire.OrderedVertexes]
|
||||
c = fcface.CenterOfMass
|
||||
v1 = verts[0].sub(c)
|
||||
v2 = verts[1].sub(c)
|
||||
n = fcface.normalAt(0,0)
|
||||
if DraftVecUtils.angle(v2,v1,n) >= 0:
|
||||
verts.reverse() # inverting verts order if the direction is couterclockwise
|
||||
pts = [ifcfile.createIfcCartesianPoint(tuple(v)) for v in verts]
|
||||
loop = ifcfile.createIfcPolyLoop(pts)
|
||||
bound = ifcfile.createIfcFaceOuterBound(loop,True)
|
||||
loops.append(bound)
|
||||
for wire in fcface.Wires:
|
||||
if wire.hashCode() != fcface.OuterWire.hashCode():
|
||||
verts = [v.Point for v in wire.OrderedVertexes]
|
||||
if len(verts) > 1:
|
||||
v1 = verts[0].sub(c)
|
||||
v2 = verts[1].sub(c)
|
||||
if DraftVecUtils.angle(v2,v1,DraftVecUtils.neg(n)) >= 0:
|
||||
verts.reverse()
|
||||
pts = [ifcfile.createIfcCartesianPoint(tuple(v)) for v in verts]
|
||||
loop = ifcfile.createIfcPolyLoop(pts)
|
||||
bound = ifcfile.createIfcFaceBound(loop,True)
|
||||
loops.append(bound)
|
||||
else:
|
||||
print ("Warning: wire with one/no vertex in ",obj.Label)
|
||||
face = ifcfile.createIfcFace(loops)
|
||||
faces.append(face)
|
||||
|
||||
shapedefs[shapedef] = shapes
|
||||
if faces:
|
||||
shell = ifcfile.createIfcClosedShell(faces)
|
||||
shape = ifcfile.createIfcFacetedBrep(shell)
|
||||
shapes.append(shape)
|
||||
|
||||
shapedefs[shapedef] = shapes
|
||||
|
||||
if shapes:
|
||||
|
||||
|
||||
@@ -387,7 +387,7 @@ def getGroupContents(objectslist,walls=False,addgroups=False,spaces=False):
|
||||
objectslist = [objectslist]
|
||||
for obj in objectslist:
|
||||
if obj:
|
||||
if obj.isDerivedFrom("App::DocumentObjectGroup") or ((getType(obj) in ["Space","Site"]) and hasattr(obj,"Group")):
|
||||
if obj.isDerivedFrom("App::DocumentObjectGroup") or ((getType(obj) in ["BuildingPart","Space","Site"]) and hasattr(obj,"Group")):
|
||||
if getType(obj) == "Site":
|
||||
if obj.Shape:
|
||||
newlist.append(obj)
|
||||
|
||||
Reference in New Issue
Block a user