Skip to content

Commit

Permalink
Arch: Export ortho arrays to IFC
Browse files Browse the repository at this point in the history
  • Loading branch information
yorikvanhavre committed Apr 10, 2020
1 parent 4d0c0d2 commit aed8c91
Showing 1 changed file with 176 additions and 66 deletions.
242 changes: 176 additions & 66 deletions src/Mod/Arch/exportIFC.py
Expand Up @@ -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
Expand All @@ -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 = ''
Expand All @@ -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"
Expand All @@ -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"])
Expand All @@ -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
Expand All @@ -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, ... }
Expand Down Expand Up @@ -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"]:
Expand Down Expand Up @@ -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"]):
Expand Down Expand Up @@ -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
Expand All @@ -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]
Expand All @@ -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"):
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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

0 comments on commit aed8c91

Please sign in to comment.