From 6a6aa26a1c3f89eca9d696be376918cf8bef29a5 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 17 May 2024 15:29:59 +0200 Subject: [PATCH] BIM: Allow to export groups as assemblies --- .../Resources/ui/preferences-ifc-export.ui | 78 ++++++++++++------- src/Mod/BIM/importers/exportIFC.py | 30 +++++-- src/Mod/BIM/nativeifc/ifc_tools.py | 8 +- 3 files changed, 81 insertions(+), 35 deletions(-) diff --git a/src/Mod/BIM/Resources/ui/preferences-ifc-export.ui b/src/Mod/BIM/Resources/ui/preferences-ifc-export.ui index 7edc5828e0..3c647dd5c0 100644 --- a/src/Mod/BIM/Resources/ui/preferences-ifc-export.ui +++ b/src/Mod/BIM/Resources/ui/preferences-ifc-export.ui @@ -6,8 +6,8 @@ 0 0 - 630 - 614 + 534 + 691 @@ -274,9 +274,7 @@ If this is your case, you can disable this and then all profiles will be exporte - Some IFC types such as IfcWall or IfcBeam have special standard versions -like IfcWallStandardCase or IfcBeamStandardCase. -If this option is turned on, FreeCAD will automatically export such objects + Some IFC types such as IfcWall or IfcBeam have special standard versions like IfcWallStandardCase or IfcBeamStandardCase. If this option is turned on, FreeCAD will automatically export such objects as standard cases when the necessary conditions are met. @@ -307,27 +305,6 @@ A site is not mandatory but a common practice is to have at least one in the fil - - - - If no building is found in the FreeCAD document, a default one will be added. -Warning: The IFC standard asks for at least one building in each file. By turning this option off, you will produce a non-standard IFC file. -However, at FreeCAD, we believe having a building should not be mandatory, and this option is there to have a chance to demonstrate our point of view. - - - Add default building if one is not found in the document (no standard) - - - true - - - IfcAddDefaultBuilding - - - Mod/Arch - - - @@ -395,6 +372,55 @@ unit to work with when opening the file. + + + + IFC standard compliance + + + + + + If no building is found in the FreeCAD document, a default one will be added. +Warning: The IFC standard asks for at least one building in each file. By turning this option off, you will produce a non-standard IFC file. +However, at FreeCAD, we believe having a building should not be mandatory, and this option is there to have a chance to demonstrate our point of view. + + + Add default building if one is not found in the document + + + true + + + IfcAddDefaultBuilding + + + Mod/Arch + + + + + + + In FreeCAD, it is possible to nest groups inside buildings or storeys. If this option is disabled, FreeCAD groups will be saved as IfcGroups and aggregated to the building structure. Aggregating non-building elements such as IfcGroups is however not recommended by the IFC standards. It is therefore also possible to export these groups as IfcElementAssemblies, which produces an IFC-compliant file. However, at FreeCAD, we believe nesting groups inside structures should be possible, and this option is there to have a chance to demonstrate our point of view. + + + Export nested groups as assemblies + + + true + + + IfcGroupsAsAssemblies + + + Mod/Arch + + + + + + diff --git a/src/Mod/BIM/importers/exportIFC.py b/src/Mod/BIM/importers/exportIFC.py index 9f51c446f6..a03fd1ce08 100644 --- a/src/Mod/BIM/importers/exportIFC.py +++ b/src/Mod/BIM/importers/exportIFC.py @@ -156,7 +156,8 @@ def getPreferences(): 'IFC_UNIT': u, 'SCALE_FACTOR': f, 'GET_STANDARD': params.get_param_arch("getStandardType"), - 'EXPORT_MODEL': ['arch', 'struct', 'hybrid'][params.get_param_arch("ifcExportModel")] + 'EXPORT_MODEL': ['arch', 'struct', 'hybrid'][params.get_param_arch("ifcExportModel")], + 'GROUPS_AS_ASSEMBLIES': params.get_param_arch("IfcGroupsAsAssemblies"), } # get ifcopenshell version @@ -380,13 +381,15 @@ def export(exportList, filename, colors=None, preferences=None): ifctype = getIfcTypeFromObj(obj) # print(ifctype) - if ifctype == "IfcGroup": - groups[obj.Name] = [o.Name for o in obj.Group] - continue - # handle assemblies (arrays, app::parts, references, etc...) assemblyElements = [] + assemblyTypes = ["IfcApp::Part","IfcPart::Compound","IfcElementAssembly"] + is_nested_group = False + if preferences['GROUPS_AS_ASSEMBLIES'] and ifctype == "IfcGroup": + for p in obj.InListRecursive: + if not p.isDerivedFrom("App::DocumentObjectGroup"): + is_nested_group = True if ifctype == "IfcArray": clonedeltas = [] @@ -424,6 +427,7 @@ def export(exportList, filename, colors=None, preferences=None): representation, preferences ) + products[obj.Base.Name] = subproduct assemblyElements.append(subproduct) exportIFCHelper.writeQuantities(ifcfile, obj.Base, @@ -432,7 +436,7 @@ def export(exportList, filename, colors=None, preferences=None): preferences['SCALE_FACTOR'] ) - elif ifctype in ["IfcApp::Part","IfcPart::Compound","IfcElementAssembly"]: + elif ifctype in assemblyTypes or is_nested_group: if hasattr(obj,"Group"): group = obj.Group elif hasattr(obj,"Links"): @@ -462,7 +466,7 @@ def export(exportList, filename, colors=None, preferences=None): placement, representation, preferences) - products[obj.Name] = subproduct + products[subobj.Name] = subproduct assemblyElements.append(subproduct) ifctype = "IfcElementAssembly" @@ -528,6 +532,12 @@ def export(exportList, filename, colors=None, preferences=None): if preferences["DEBUG"]: print("Warning! Axis system object '{}' only contains one set of axis but at least two are needed for a IfcGrid to be added to IFC.".format(obj.Label)) continue + # gather groups + + if ifctype == "IfcGroup": + groups[obj.Name] = [o.Name for o in obj.Group] + continue + if ifctype not in ArchIFCSchema.IfcProducts: ifctype = "IfcBuildingElementProxy" @@ -581,10 +591,14 @@ def export(exportList, filename, colors=None, preferences=None): # gather assembly subelements if assemblyElements: + if is_nested_group: + aname = "FreeCADGroup" + else: + aname = "Assembly" ifcfile.createIfcRelAggregates( ifcopenshell.guid.new(), history, - 'Assembly', + aname, '', products[obj.Name], assemblyElements diff --git a/src/Mod/BIM/nativeifc/ifc_tools.py b/src/Mod/BIM/nativeifc/ifc_tools.py index 9049550c1f..758e577fe5 100644 --- a/src/Mod/BIM/nativeifc/ifc_tools.py +++ b/src/Mod/BIM/nativeifc/ifc_tools.py @@ -331,8 +331,14 @@ def assign_groups(children): for child in children: if child.is_a("IfcGroup"): + mode = "IsGroupedBy" + elif child.is_a("IfcElementAssembly"): + mode = "IsDecomposedBy" + else: + mode = None + if mode: grobj = get_object(child) - for rel in child.IsGroupedBy: + for rel in getattr(child, mode): for elem in rel.RelatedObjects: elobj = get_object(elem) if elobj: