@@ -137,7 +137,8 @@ def getPreferences():
137
137
'ADD_DEFAULT_STOREY' : p .GetBool ("IfcAddDefaultStorey" ,False ),
138
138
'ADD_DEFAULT_BUILDING' : p .GetBool ("IfcAddDefaultBuilding" ,True ),
139
139
'IFC_UNIT' : u ,
140
- 'SCALE_FACTOR' : f
140
+ 'SCALE_FACTOR' : f ,
141
+ 'GET_STANDARD' : p .GetBool ("getStandardType" ,False )
141
142
}
142
143
143
144
return preferences
@@ -162,6 +163,8 @@ def export(exportList,filename,colors=None,preferences=None):
162
163
FreeCAD .Console .PrintMessage ("Visit https://www.freecadweb.org/wiki/Arch_IFC to learn how to install it\n " )
163
164
return
164
165
166
+ # process template
167
+
165
168
version = FreeCAD .Version ()
166
169
owner = FreeCAD .ActiveDocument .CreatedBy
167
170
email = ''
@@ -171,11 +174,10 @@ def export(exportList,filename,colors=None,preferences=None):
171
174
email = s [1 ].strip (">" )
172
175
global template
173
176
template = ifctemplate .replace ("$version" ,version [0 ]+ "." + version [1 ]+ " build " + version [2 ])
174
- getstd = FreeCAD .ParamGet ("User parameter:BaseApp/Preferences/Mod/Arch" ).GetBool ("getStandardType" ,False )
175
177
if hasattr (ifcopenshell ,"schema_identifier" ):
176
178
schema = ifcopenshell .schema_identifier
177
179
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
179
181
schema = ["IFC4" , "IFC2X3" ][FreeCAD .ParamGet ("User parameter:BaseApp/Preferences/Mod/Arch" ).GetInt ("IfcVersion" ,0 )]
180
182
else :
181
183
schema = "IFC2X3"
@@ -196,6 +198,9 @@ def export(exportList,filename,colors=None,preferences=None):
196
198
of .write (template )
197
199
of .close ()
198
200
os .close (templatefilehandle )
201
+
202
+ # create IFC file
203
+
199
204
global ifcfile , surfstyles , clones , sharedobjects , profiledefs , shapedefs
200
205
ifcfile = ifcopenshell .open (templatefile )
201
206
ifcfile = exportIFCHelper .writeUnits (ifcfile ,preferences ["IFC_UNIT" ])
@@ -211,13 +216,17 @@ def export(exportList,filename,colors=None,preferences=None):
211
216
if obj .Shape :
212
217
if obj .Shape .Edges and (not obj .Shape .Faces ):
213
218
annotations .append (obj )
219
+
214
220
# clean objects list of unwanted types
221
+
215
222
objectslist = [obj for obj in objectslist if obj not in annotations ]
216
223
objectslist = Arch .pruneIncluded (objectslist ,strict = True )
217
224
objectslist = [obj for obj in objectslist if Draft .getType (obj ) not in ["Dimension" ,"Material" ,"MaterialContainer" ,"WorkingPlaneProxy" ]]
218
225
if preferences ['FULL_PARAMETRIC' ]:
219
226
objectslist = Arch .getAllChildren (objectslist )
220
227
228
+ # create project and context
229
+
221
230
contextCreator = exportIFCHelper .ContextCreator (ifcfile , objectslist )
222
231
context = contextCreator .model_view_subcontext
223
232
project = contextCreator .project
@@ -227,6 +236,8 @@ def export(exportList,filename,colors=None,preferences=None):
227
236
decl = Draft .getObjectsOfType (objectslist , "Site" )[0 ].Declination .getValueAs (FreeCAD .Units .Radian )
228
237
contextCreator .model_context .TrueNorth .DirectionRatios = (math .cos (decl + math .pi / 2 ), math .sin (decl + math .pi / 2 ))
229
238
239
+ # define holders for the different types we create
240
+
230
241
products = {} # { Name: IfcEntity, ... }
231
242
subproducts = {} # { Name: IfcEntity, ... } for storing additions/subtractions and other types of subcomponents of a product
232
243
surfstyles = {} # { (r,g,b): IfcEntity, ... }
@@ -267,33 +278,57 @@ def export(exportList,filename,colors=None,preferences=None):
267
278
268
279
# getting generic data
269
280
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 )
291
284
ifctype = getIfcTypeFromObj (obj )
292
285
293
286
if ifctype == "IfcGroup" :
294
287
groups [obj .Name ] = [o .Name for o in obj .Group ]
295
288
continue
296
289
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
+
297
332
# export grids
298
333
299
334
if ifctype in ["IfcAxis" ,"IfcAxisSystem" ,"IfcGrid" ]:
@@ -356,61 +391,55 @@ def export(exportList,filename,colors=None,preferences=None):
356
391
if ifctype not in ArchIFCSchema .IfcProducts .keys ():
357
392
ifctype = "IfcBuildingElementProxy"
358
393
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
-
367
394
# getting the representation
368
395
369
396
representation ,placement ,shapetype = getRepresentation (
370
397
ifcfile ,
371
398
context ,
372
399
obj ,
373
- forcebrep = (brepflag or preferences [ 'FORCE_BREP' ] ),
400
+ forcebrep = (getBrepFlag ( obj , preferences ) ),
374
401
colors = colors ,
375
402
preferences = preferences
376
403
)
377
- if getstd :
404
+ if preferences [ 'GET_STANDARD' ] :
378
405
if isStandardCase (obj ,ifctype ):
379
406
ifctype += "StandardCase"
380
407
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 )
405
410
406
411
# creating the product
407
412
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
+
410
426
products [obj .Name ] = product
411
427
if ifctype in ["IfcBuilding" ,"IfcBuildingStorey" ,"IfcSite" ,"IfcSpace" ]:
412
428
spatialelements [obj .Name ] = product
413
429
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
+
414
443
# additions
415
444
416
445
if hasattr (obj ,"Additions" ) and (shapetype in ["extrusion" ,"no shape" ]):
@@ -1743,9 +1772,10 @@ def getProfile(ifcfile,p):
1743
1772
return profile
1744
1773
1745
1774
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 ):
1747
1776
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)"""
1749
1779
1750
1780
import Part
1751
1781
import DraftGeomUtils
@@ -1756,20 +1786,27 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
1756
1786
shapetype = "no shape"
1757
1787
tostore = False
1758
1788
subplacement = None
1789
+ skipshape = False
1759
1790
1760
1791
# check for clones
1761
1792
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 ] = []
1763
1797
for k ,v in clones .items ():
1764
1798
if (obj .Name == k ) or (obj .Name in v ):
1765
1799
if k in sharedobjects :
1766
1800
# base shape already exists
1767
1801
repmap = sharedobjects [k ]
1768
1802
pla = obj .getGlobalPlacement ()
1803
+ pos = FreeCAD .Vector (pla .Base )
1804
+ if isinstance (forceclone ,FreeCAD .Vector ):
1805
+ pos += forceclone
1769
1806
axis1 = ifcbin .createIfcDirection (tuple (pla .Rotation .multVec (FreeCAD .Vector (1 ,0 ,0 ))))
1770
1807
axis2 = ifcbin .createIfcDirection (tuple (pla .Rotation .multVec (FreeCAD .Vector (0 ,1 ,0 ))))
1771
1808
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' ])))
1773
1810
transf = ifcbin .createIfcCartesianTransformationOperator3D (axis1 ,axis2 ,origin ,1.0 ,axis3 )
1774
1811
mapitem = ifcfile .createIfcMappedItem (repmap ,transf )
1775
1812
shapes = [mapitem ]
@@ -1783,7 +1820,11 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
1783
1820
if obj .isDerivedFrom ("Part::Feature" ) and (len (obj .Shape .Solids ) > 1 ) and hasattr (obj ,"Axis" ) and obj .Axis :
1784
1821
forcebrep = True
1785
1822
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 ):
1787
1828
profile = None
1788
1829
ev = FreeCAD .Vector ()
1789
1830
if hasattr (obj ,"Proxy" ):
@@ -1858,7 +1899,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
1858
1899
solidType = "SweptSolid"
1859
1900
shapetype = "extrusion"
1860
1901
1861
- if not shapes :
1902
+ if ( not shapes ) and ( not skipshape ) :
1862
1903
1863
1904
# check if we keep a null shape (additions-only object)
1864
1905
@@ -2106,3 +2147,72 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
2106
2147
productdef = ifcfile .createIfcProductDefinitionShape (None ,None ,[representation ])
2107
2148
2108
2149
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