Skip to content

Commit aed8c91

Browse files
committed
Arch: Export ortho arrays to IFC
1 parent 4d0c0d2 commit aed8c91

File tree

1 file changed

+176
-66
lines changed

1 file changed

+176
-66
lines changed

src/Mod/Arch/exportIFC.py

Lines changed: 176 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ def getPreferences():
137137
'ADD_DEFAULT_STOREY': p.GetBool("IfcAddDefaultStorey",False),
138138
'ADD_DEFAULT_BUILDING': p.GetBool("IfcAddDefaultBuilding",True),
139139
'IFC_UNIT': u,
140-
'SCALE_FACTOR': f
140+
'SCALE_FACTOR': f,
141+
'GET_STANDARD': p.GetBool("getStandardType",False)
141142
}
142143

143144
return preferences
@@ -162,6 +163,8 @@ def export(exportList,filename,colors=None,preferences=None):
162163
FreeCAD.Console.PrintMessage("Visit https://www.freecadweb.org/wiki/Arch_IFC to learn how to install it\n")
163164
return
164165

166+
# process template
167+
165168
version = FreeCAD.Version()
166169
owner = FreeCAD.ActiveDocument.CreatedBy
167170
email = ''
@@ -171,11 +174,10 @@ def export(exportList,filename,colors=None,preferences=None):
171174
email = s[1].strip(">")
172175
global template
173176
template = ifctemplate.replace("$version",version[0]+"."+version[1]+" build "+version[2])
174-
getstd = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("getStandardType",False)
175177
if hasattr(ifcopenshell,"schema_identifier"):
176178
schema = ifcopenshell.schema_identifier
177179
elif hasattr(ifcopenshell,"version") and (float(ifcopenshell.version[:3]) >= 0.6):
178-
# v0.6 allows to set our own schema
180+
# v0.6 onwards allows to set our own schema
179181
schema = ["IFC4", "IFC2X3"][FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetInt("IfcVersion",0)]
180182
else:
181183
schema = "IFC2X3"
@@ -196,6 +198,9 @@ def export(exportList,filename,colors=None,preferences=None):
196198
of.write(template)
197199
of.close()
198200
os.close(templatefilehandle)
201+
202+
# create IFC file
203+
199204
global ifcfile, surfstyles, clones, sharedobjects, profiledefs, shapedefs
200205
ifcfile = ifcopenshell.open(templatefile)
201206
ifcfile = exportIFCHelper.writeUnits(ifcfile,preferences["IFC_UNIT"])
@@ -211,13 +216,17 @@ def export(exportList,filename,colors=None,preferences=None):
211216
if obj.Shape:
212217
if obj.Shape.Edges and (not obj.Shape.Faces):
213218
annotations.append(obj)
219+
214220
# clean objects list of unwanted types
221+
215222
objectslist = [obj for obj in objectslist if obj not in annotations]
216223
objectslist = Arch.pruneIncluded(objectslist,strict=True)
217224
objectslist = [obj for obj in objectslist if Draft.getType(obj) not in ["Dimension","Material","MaterialContainer","WorkingPlaneProxy"]]
218225
if preferences['FULL_PARAMETRIC']:
219226
objectslist = Arch.getAllChildren(objectslist)
220227

228+
# create project and context
229+
221230
contextCreator = exportIFCHelper.ContextCreator(ifcfile, objectslist)
222231
context = contextCreator.model_view_subcontext
223232
project = contextCreator.project
@@ -227,6 +236,8 @@ def export(exportList,filename,colors=None,preferences=None):
227236
decl = Draft.getObjectsOfType(objectslist, "Site")[0].Declination.getValueAs(FreeCAD.Units.Radian)
228237
contextCreator.model_context.TrueNorth.DirectionRatios = (math.cos(decl+math.pi/2), math.sin(decl+math.pi/2))
229238

239+
# define holders for the different types we create
240+
230241
products = {} # { Name: IfcEntity, ... }
231242
subproducts = {} # { Name: IfcEntity, ... } for storing additions/subtractions and other types of subcomponents of a product
232243
surfstyles = {} # { (r,g,b): IfcEntity, ... }
@@ -267,33 +278,57 @@ def export(exportList,filename,colors=None,preferences=None):
267278

268279
# getting generic data
269280

270-
name = obj.Label
271-
if six.PY2:
272-
name = name.encode("utf8")
273-
description = obj.Description if hasattr(obj,"Description") else ""
274-
if six.PY2:
275-
description = description.encode("utf8")
276-
277-
# getting uid
278-
279-
uid = None
280-
if hasattr(obj,"IfcData"):
281-
if "IfcUID" in obj.IfcData.keys():
282-
uid = str(obj.IfcData["IfcUID"])
283-
if not uid:
284-
uid = ifcopenshell.guid.new()
285-
# storing the uid for further use
286-
if preferences['STORE_UID'] and hasattr(obj,"IfcData"):
287-
d = obj.IfcData
288-
d["IfcUID"] = uid
289-
obj.IfcData = d
290-
281+
name = getText("Name",obj)
282+
description = getText("Description",obj)
283+
uid = getUID(obj,preferences)
291284
ifctype = getIfcTypeFromObj(obj)
292285

293286
if ifctype == "IfcGroup":
294287
groups[obj.Name] = [o.Name for o in obj.Group]
295288
continue
296289

290+
# handle assemblies (arrays, app::parts, references, etc...)
291+
292+
assemblyElements = []
293+
294+
if ifctype == "IfcArray":
295+
if obj.ArrayType == "ortho":
296+
clonedeltas = []
297+
for i in range(obj.NumberX):
298+
clonedeltas.append(obj.Placement.Base+(i*obj.IntervalX))
299+
for j in range(obj.NumberY):
300+
if j > 0:
301+
clonedeltas.append(obj.Placement.Base+(i*obj.IntervalX)+(j*obj.IntervalY))
302+
for k in range(obj.NumberZ):
303+
if k > 0:
304+
clonedeltas.append(obj.Placement.Base+(i*obj.IntervalX)+(j*obj.IntervalY)+(k*obj.IntervalZ))
305+
#print("clonedeltas:",clonedeltas)
306+
for delta in clonedeltas:
307+
representation,placement,shapetype = getRepresentation(
308+
ifcfile,
309+
context,
310+
obj.Base,
311+
forcebrep=(getBrepFlag(obj.Base,preferences)),
312+
colors=colors,
313+
preferences=preferences,
314+
forceclone=delta
315+
)
316+
subproduct = createProduct(
317+
ifcfile,
318+
obj.Base,
319+
getIfcTypeFromObj(obj.Base),
320+
getUID(obj.Base,preferences),
321+
history,
322+
getText("Name",obj.Base),
323+
getText("Description",obj.Base),
324+
placement,
325+
representation,
326+
preferences,
327+
schema)
328+
329+
assemblyElements.append(subproduct)
330+
ifctype = "IfcElementAssembly"
331+
297332
# export grids
298333

299334
if ifctype in ["IfcAxis","IfcAxisSystem","IfcGrid"]:
@@ -356,61 +391,55 @@ def export(exportList,filename,colors=None,preferences=None):
356391
if ifctype not in ArchIFCSchema.IfcProducts.keys():
357392
ifctype = "IfcBuildingElementProxy"
358393

359-
# getting the "Force BREP" flag
360-
361-
brepflag = False
362-
if hasattr(obj,"IfcData"):
363-
if "FlagForceBrep" in obj.IfcData.keys():
364-
if obj.IfcData["FlagForceBrep"] == "True":
365-
brepflag = True
366-
367394
# getting the representation
368395

369396
representation,placement,shapetype = getRepresentation(
370397
ifcfile,
371398
context,
372399
obj,
373-
forcebrep=(brepflag or preferences['FORCE_BREP']),
400+
forcebrep=(getBrepFlag(obj,preferences)),
374401
colors=colors,
375402
preferences=preferences
376403
)
377-
if getstd:
404+
if preferences['GET_STANDARD']:
378405
if isStandardCase(obj,ifctype):
379406
ifctype += "StandardCase"
380407

381-
if preferences['DEBUG']: print(str(count).ljust(3)," : ", ifctype, " (",shapetype,") : ",name)
382-
383-
# setting the arguments
384-
385-
kwargs = {
386-
"GlobalId": uid,
387-
"OwnerHistory": history,
388-
"Name": name,
389-
"Description": description,
390-
"ObjectPlacement": placement,
391-
"Representation": representation
392-
}
393-
if ifctype == "IfcSite":
394-
kwargs.update({
395-
"RefLatitude":dd2dms(obj.Latitude),
396-
"RefLongitude":dd2dms(obj.Longitude),
397-
"RefElevation":obj.Elevation.Value*preferences['SCALE_FACTOR'],
398-
"SiteAddress":buildAddress(obj,ifcfile),
399-
"CompositionType": "ELEMENT"
400-
})
401-
if schema == "IFC2X3":
402-
kwargs = exportIFC2X3Attributes(obj, kwargs, preferences['SCALE_FACTOR'])
403-
else:
404-
kwargs = exportIfcAttributes(obj, kwargs, preferences['SCALE_FACTOR'])
408+
if preferences['DEBUG']:
409+
print(str(count).ljust(3)," : ", ifctype, " (",shapetype,") : ",name)
405410

406411
# creating the product
407412

408-
#print(obj.Label," : ",ifctype," : ",kwargs)
409-
product = getattr(ifcfile,"create"+ifctype)(**kwargs)
413+
product = createProduct(
414+
ifcfile,
415+
obj,
416+
ifctype,
417+
uid,
418+
history,
419+
name,
420+
description,
421+
placement,
422+
representation,
423+
preferences,
424+
schema)
425+
410426
products[obj.Name] = product
411427
if ifctype in ["IfcBuilding","IfcBuildingStorey","IfcSite","IfcSpace"]:
412428
spatialelements[obj.Name] = product
413429

430+
# gather assembly subelements
431+
432+
if assemblyElements:
433+
ifcfile.createIfcRelAggregates(
434+
ifcopenshell.guid.new(),
435+
history,
436+
'Assembly',
437+
'',
438+
products[obj.Name],
439+
assemblyElements
440+
)
441+
if preferences['DEBUG']: print(" aggregating",len(assemblyElements),"object(s)")
442+
414443
# additions
415444

416445
if hasattr(obj,"Additions") and (shapetype in ["extrusion","no shape"]):
@@ -1743,9 +1772,10 @@ def getProfile(ifcfile,p):
17431772
return profile
17441773

17451774

1746-
def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tessellation=1,colors=None,preferences=None):
1775+
def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tessellation=1,colors=None,preferences=None,forceclone=False):
17471776

1748-
"""returns an IfcShapeRepresentation object or None"""
1777+
"""returns an IfcShapeRepresentation object or None. forceclone can be False (does nothing),
1778+
"store" or True (stores the object as clone base) or a Vector (creates a clone)"""
17491779

17501780
import Part
17511781
import DraftGeomUtils
@@ -1756,20 +1786,27 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
17561786
shapetype = "no shape"
17571787
tostore = False
17581788
subplacement = None
1789+
skipshape = False
17591790

17601791
# check for clones
17611792

1762-
if (not subtraction) and (not forcebrep):
1793+
if ((not subtraction) and (not forcebrep)) or forceclone:
1794+
if forceclone:
1795+
if not obj.Name in clones:
1796+
clones[obj.Name] = []
17631797
for k,v in clones.items():
17641798
if (obj.Name == k) or (obj.Name in v):
17651799
if k in sharedobjects:
17661800
# base shape already exists
17671801
repmap = sharedobjects[k]
17681802
pla = obj.getGlobalPlacement()
1803+
pos = FreeCAD.Vector(pla.Base)
1804+
if isinstance(forceclone,FreeCAD.Vector):
1805+
pos += forceclone
17691806
axis1 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(1,0,0))))
17701807
axis2 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,1,0))))
17711808
axis3 = ifcbin.createIfcDirection(tuple(pla.Rotation.multVec(FreeCAD.Vector(0,0,1))))
1772-
origin = ifcbin.createIfcCartesianPoint(tuple(FreeCAD.Vector(pla.Base).multiply(preferences['SCALE_FACTOR'])))
1809+
origin = ifcbin.createIfcCartesianPoint(tuple(pos.multiply(preferences['SCALE_FACTOR'])))
17731810
transf = ifcbin.createIfcCartesianTransformationOperator3D(axis1,axis2,origin,1.0,axis3)
17741811
mapitem = ifcfile.createIfcMappedItem(repmap,transf)
17751812
shapes = [mapitem]
@@ -1783,7 +1820,11 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
17831820
if obj.isDerivedFrom("Part::Feature") and (len(obj.Shape.Solids) > 1) and hasattr(obj,"Axis") and obj.Axis:
17841821
forcebrep = True
17851822

1786-
if (not shapes) and (not forcebrep):
1823+
# specific cases that must ignore their own shape
1824+
if Draft.getType(obj) in ["Array"]:
1825+
skipshape = True
1826+
1827+
if (not shapes) and (not forcebrep) and (not skipshape):
17871828
profile = None
17881829
ev = FreeCAD.Vector()
17891830
if hasattr(obj,"Proxy"):
@@ -1858,7 +1899,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
18581899
solidType = "SweptSolid"
18591900
shapetype = "extrusion"
18601901

1861-
if not shapes:
1902+
if (not shapes) and (not skipshape):
18621903

18631904
# check if we keep a null shape (additions-only object)
18641905

@@ -2106,3 +2147,72 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
21062147
productdef = ifcfile.createIfcProductDefinitionShape(None,None,[representation])
21072148

21082149
return productdef,placement,shapetype
2150+
2151+
2152+
def getBrepFlag(obj,preferences):
2153+
"""returns True if the object must be exported as BREP"""
2154+
brepflag = False
2155+
if preferences['FORCE_BREP']:
2156+
return True
2157+
if hasattr(obj,"IfcData"):
2158+
if "FlagForceBrep" in obj.IfcData.keys():
2159+
if obj.IfcData["FlagForceBrep"] == "True":
2160+
brepflag = True
2161+
return brepflag
2162+
2163+
2164+
def createProduct(ifcfile,obj,ifctype,uid,history,name,description,placement,representation,preferences,schema):
2165+
"""creates a product in the given IFC file"""
2166+
2167+
kwargs = {
2168+
"GlobalId": uid,
2169+
"OwnerHistory": history,
2170+
"Name": name,
2171+
"Description": description,
2172+
"ObjectPlacement": placement,
2173+
"Representation": representation
2174+
}
2175+
if ifctype == "IfcSite":
2176+
kwargs.update({
2177+
"RefLatitude":dd2dms(obj.Latitude),
2178+
"RefLongitude":dd2dms(obj.Longitude),
2179+
"RefElevation":obj.Elevation.Value*preferences['SCALE_FACTOR'],
2180+
"SiteAddress":buildAddress(obj,ifcfile),
2181+
"CompositionType": "ELEMENT"
2182+
})
2183+
if schema == "IFC2X3":
2184+
kwargs = exportIFC2X3Attributes(obj, kwargs, preferences['SCALE_FACTOR'])
2185+
else:
2186+
kwargs = exportIfcAttributes(obj, kwargs, preferences['SCALE_FACTOR'])
2187+
product = getattr(ifcfile,"create"+ifctype)(**kwargs)
2188+
return product
2189+
2190+
2191+
def getUID(obj,preferences):
2192+
"""gets or creates an UUID for an object"""
2193+
2194+
uid = None
2195+
if hasattr(obj,"IfcData"):
2196+
if "IfcUID" in obj.IfcData.keys():
2197+
uid = str(obj.IfcData["IfcUID"])
2198+
if not uid:
2199+
uid = ifcopenshell.guid.new()
2200+
# storing the uid for further use
2201+
if preferences['STORE_UID'] and hasattr(obj,"IfcData"):
2202+
d = obj.IfcData
2203+
d["IfcUID"] = uid
2204+
obj.IfcData = d
2205+
return uid
2206+
2207+
2208+
def getText(field,obj):
2209+
"""Returns the value of a text property of an object"""
2210+
2211+
result = ""
2212+
if field == "Name":
2213+
field = "Label"
2214+
if hasattr(obj,field):
2215+
result = getattr(obj,field)
2216+
if six.PY2:
2217+
result = result.encode("utf8")
2218+
return result

0 commit comments

Comments
 (0)