Arch: Export ortho arrays to IFC

This commit is contained in:
Yorik van Havre
2020-04-10 14:31:14 +02:00
parent 9574c5698b
commit 19744a9f83

View File

@@ -137,7 +137,8 @@ def getPreferences():
'ADD_DEFAULT_STOREY': p.GetBool("IfcAddDefaultStorey",False),
'ADD_DEFAULT_BUILDING': p.GetBool("IfcAddDefaultBuilding",True),
'IFC_UNIT': u,
'SCALE_FACTOR': f
'SCALE_FACTOR': f,
'GET_STANDARD': p.GetBool("getStandardType",False)
}
return preferences
@@ -162,6 +163,8 @@ def export(exportList,filename,colors=None,preferences=None):
FreeCAD.Console.PrintMessage("Visit https://www.freecadweb.org/wiki/Arch_IFC to learn how to install it\n")
return
# process template
version = FreeCAD.Version()
owner = FreeCAD.ActiveDocument.CreatedBy
email = ''
@@ -171,11 +174,10 @@ def export(exportList,filename,colors=None,preferences=None):
email = s[1].strip(">")
global template
template = ifctemplate.replace("$version",version[0]+"."+version[1]+" build "+version[2])
getstd = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("getStandardType",False)
if hasattr(ifcopenshell,"schema_identifier"):
schema = ifcopenshell.schema_identifier
elif hasattr(ifcopenshell,"version") and (float(ifcopenshell.version[:3]) >= 0.6):
# v0.6 allows to set our own schema
# v0.6 onwards allows to set our own schema
schema = ["IFC4", "IFC2X3"][FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetInt("IfcVersion",0)]
else:
schema = "IFC2X3"
@@ -196,6 +198,9 @@ def export(exportList,filename,colors=None,preferences=None):
of.write(template)
of.close()
os.close(templatefilehandle)
# create IFC file
global ifcfile, surfstyles, clones, sharedobjects, profiledefs, shapedefs
ifcfile = ifcopenshell.open(templatefile)
ifcfile = exportIFCHelper.writeUnits(ifcfile,preferences["IFC_UNIT"])
@@ -211,13 +216,17 @@ def export(exportList,filename,colors=None,preferences=None):
if obj.Shape:
if obj.Shape.Edges and (not obj.Shape.Faces):
annotations.append(obj)
# clean objects list of unwanted types
objectslist = [obj for obj in objectslist if obj not in annotations]
objectslist = Arch.pruneIncluded(objectslist,strict=True)
objectslist = [obj for obj in objectslist if Draft.getType(obj) not in ["Dimension","Material","MaterialContainer","WorkingPlaneProxy"]]
if preferences['FULL_PARAMETRIC']:
objectslist = Arch.getAllChildren(objectslist)
# create project and context
contextCreator = exportIFCHelper.ContextCreator(ifcfile, objectslist)
context = contextCreator.model_view_subcontext
project = contextCreator.project
@@ -227,6 +236,8 @@ def export(exportList,filename,colors=None,preferences=None):
decl = Draft.getObjectsOfType(objectslist, "Site")[0].Declination.getValueAs(FreeCAD.Units.Radian)
contextCreator.model_context.TrueNorth.DirectionRatios = (math.cos(decl+math.pi/2), math.sin(decl+math.pi/2))
# define holders for the different types we create
products = {} # { Name: IfcEntity, ... }
subproducts = {} # { Name: IfcEntity, ... } for storing additions/subtractions and other types of subcomponents of a product
surfstyles = {} # { (r,g,b): IfcEntity, ... }
@@ -267,33 +278,57 @@ def export(exportList,filename,colors=None,preferences=None):
# getting generic data
name = obj.Label
if six.PY2:
name = name.encode("utf8")
description = obj.Description if hasattr(obj,"Description") else ""
if six.PY2:
description = description.encode("utf8")
# getting uid
uid = None
if hasattr(obj,"IfcData"):
if "IfcUID" in obj.IfcData.keys():
uid = str(obj.IfcData["IfcUID"])
if not uid:
uid = ifcopenshell.guid.new()
# storing the uid for further use
if preferences['STORE_UID'] and hasattr(obj,"IfcData"):
d = obj.IfcData
d["IfcUID"] = uid
obj.IfcData = d
name = getText("Name",obj)
description = getText("Description",obj)
uid = getUID(obj,preferences)
ifctype = getIfcTypeFromObj(obj)
if ifctype == "IfcGroup":
groups[obj.Name] = [o.Name for o in obj.Group]
continue
# handle assemblies (arrays, app::parts, references, etc...)
assemblyElements = []
if ifctype == "IfcArray":
if obj.ArrayType == "ortho":
clonedeltas = []
for i in range(obj.NumberX):
clonedeltas.append(obj.Placement.Base+(i*obj.IntervalX))
for j in range(obj.NumberY):
if j > 0:
clonedeltas.append(obj.Placement.Base+(i*obj.IntervalX)+(j*obj.IntervalY))
for k in range(obj.NumberZ):
if k > 0:
clonedeltas.append(obj.Placement.Base+(i*obj.IntervalX)+(j*obj.IntervalY)+(k*obj.IntervalZ))
#print("clonedeltas:",clonedeltas)
for delta in clonedeltas:
representation,placement,shapetype = getRepresentation(
ifcfile,
context,
obj.Base,
forcebrep=(getBrepFlag(obj.Base,preferences)),
colors=colors,
preferences=preferences,
forceclone=delta
)
subproduct = createProduct(
ifcfile,
obj.Base,
getIfcTypeFromObj(obj.Base),
getUID(obj.Base,preferences),
history,
getText("Name",obj.Base),
getText("Description",obj.Base),
placement,
representation,
preferences,
schema)
assemblyElements.append(subproduct)
ifctype = "IfcElementAssembly"
# export grids
if ifctype in ["IfcAxis","IfcAxisSystem","IfcGrid"]:
@@ -356,61 +391,55 @@ def export(exportList,filename,colors=None,preferences=None):
if ifctype not in ArchIFCSchema.IfcProducts.keys():
ifctype = "IfcBuildingElementProxy"
# getting the "Force BREP" flag
brepflag = False
if hasattr(obj,"IfcData"):
if "FlagForceBrep" in obj.IfcData.keys():
if obj.IfcData["FlagForceBrep"] == "True":
brepflag = True
# getting the representation
representation,placement,shapetype = getRepresentation(
ifcfile,
context,
obj,
forcebrep=(brepflag or preferences['FORCE_BREP']),
forcebrep=(getBrepFlag(obj,preferences)),
colors=colors,
preferences=preferences
)
if getstd:
if preferences['GET_STANDARD']:
if isStandardCase(obj,ifctype):
ifctype += "StandardCase"
if preferences['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 == "IfcSite":
kwargs.update({
"RefLatitude":dd2dms(obj.Latitude),
"RefLongitude":dd2dms(obj.Longitude),
"RefElevation":obj.Elevation.Value*preferences['SCALE_FACTOR'],
"SiteAddress":buildAddress(obj,ifcfile),
"CompositionType": "ELEMENT"
})
if schema == "IFC2X3":
kwargs = exportIFC2X3Attributes(obj, kwargs, preferences['SCALE_FACTOR'])
else:
kwargs = exportIfcAttributes(obj, kwargs, preferences['SCALE_FACTOR'])
if preferences['DEBUG']:
print(str(count).ljust(3)," : ", ifctype, " (",shapetype,") : ",name)
# creating the product
#print(obj.Label," : ",ifctype," : ",kwargs)
product = getattr(ifcfile,"create"+ifctype)(**kwargs)
product = createProduct(
ifcfile,
obj,
ifctype,
uid,
history,
name,
description,
placement,
representation,
preferences,
schema)
products[obj.Name] = product
if ifctype in ["IfcBuilding","IfcBuildingStorey","IfcSite","IfcSpace"]:
spatialelements[obj.Name] = product
# gather assembly subelements
if assemblyElements:
ifcfile.createIfcRelAggregates(
ifcopenshell.guid.new(),
history,
'Assembly',
'',
products[obj.Name],
assemblyElements
)
if preferences['DEBUG']: print(" aggregating",len(assemblyElements),"object(s)")
# additions
if hasattr(obj,"Additions") and (shapetype in ["extrusion","no shape"]):
@@ -1743,9 +1772,10 @@ def getProfile(ifcfile,p):
return profile
def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tessellation=1,colors=None,preferences=None):
def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tessellation=1,colors=None,preferences=None,forceclone=False):
"""returns an IfcShapeRepresentation object or None"""
"""returns an IfcShapeRepresentation object or None. forceclone can be False (does nothing),
"store" or True (stores the object as clone base) or a Vector (creates a clone)"""
import Part
import DraftGeomUtils
@@ -1756,20 +1786,27 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
shapetype = "no shape"
tostore = False
subplacement = None
skipshape = False
# check for clones
if (not subtraction) and (not forcebrep):
if ((not subtraction) and (not forcebrep)) or forceclone:
if forceclone:
if not obj.Name in clones:
clones[obj.Name] = []
for k,v in clones.items():
if (obj.Name == k) or (obj.Name in v):
if k in sharedobjects:
# base shape already exists
repmap = sharedobjects[k]
pla = obj.getGlobalPlacement()
pos = FreeCAD.Vector(pla.Base)
if isinstance(forceclone,FreeCAD.Vector):
pos += forceclone
axis1 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(1,0,0))))
axis2 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,1,0))))
axis3 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,0,1))))
origin = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(pla.Base).multiply(preferences['SCALE_FACTOR'])))
origin = ifcbin.createIfcCartesianPoint(tuple(pos.multiply(preferences['SCALE_FACTOR'])))
transf = ifcbin.createIfcCartesianTransformationOperator3D(axis1,axis2,origin,1.0,axis3)
mapitem = ifcfile.createIfcMappedItem(repmap,transf)
shapes = [mapitem]
@@ -1783,7 +1820,11 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
if obj.isDerivedFrom("Part::Feature") and (len(obj.Shape.Solids) > 1) and hasattr(obj,"Axis") and obj.Axis:
forcebrep = True
if (not shapes) and (not forcebrep):
# specific cases that must ignore their own shape
if Draft.getType(obj) in ["Array"]:
skipshape = True
if (not shapes) and (not forcebrep) and (not skipshape):
profile = None
ev = FreeCAD.Vector()
if hasattr(obj,"Proxy"):
@@ -1858,7 +1899,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
solidType = "SweptSolid"
shapetype = "extrusion"
if not shapes:
if (not shapes) and (not skipshape):
# check if we keep a null shape (additions-only object)
@@ -2106,3 +2147,72 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
productdef = ifcfile.createIfcProductDefinitionShape(None,None,[representation])
return productdef,placement,shapetype
def getBrepFlag(obj,preferences):
"""returns True if the object must be exported as BREP"""
brepflag = False
if preferences['FORCE_BREP']:
return True
if hasattr(obj,"IfcData"):
if "FlagForceBrep" in obj.IfcData.keys():
if obj.IfcData["FlagForceBrep"] == "True":
brepflag = True
return brepflag
def createProduct(ifcfile,obj,ifctype,uid,history,name,description,placement,representation,preferences,schema):
"""creates a product in the given IFC file"""
kwargs = {
"GlobalId": uid,
"OwnerHistory": history,
"Name": name,
"Description": description,
"ObjectPlacement": placement,
"Representation": representation
}
if ifctype == "IfcSite":
kwargs.update({
"RefLatitude":dd2dms(obj.Latitude),
"RefLongitude":dd2dms(obj.Longitude),
"RefElevation":obj.Elevation.Value*preferences['SCALE_FACTOR'],
"SiteAddress":buildAddress(obj,ifcfile),
"CompositionType": "ELEMENT"
})
if schema == "IFC2X3":
kwargs = exportIFC2X3Attributes(obj, kwargs, preferences['SCALE_FACTOR'])
else:
kwargs = exportIfcAttributes(obj, kwargs, preferences['SCALE_FACTOR'])
product = getattr(ifcfile,"create"+ifctype)(**kwargs)
return product
def getUID(obj,preferences):
"""gets or creates an UUID for an object"""
uid = None
if hasattr(obj,"IfcData"):
if "IfcUID" in obj.IfcData.keys():
uid = str(obj.IfcData["IfcUID"])
if not uid:
uid = ifcopenshell.guid.new()
# storing the uid for further use
if preferences['STORE_UID'] and hasattr(obj,"IfcData"):
d = obj.IfcData
d["IfcUID"] = uid
obj.IfcData = d
return uid
def getText(field,obj):
"""Returns the value of a text property of an object"""
result = ""
if field == "Name":
field = "Label"
if hasattr(obj,field):
result = getattr(obj,field)
if six.PY2:
result = result.encode("utf8")
return result