Fix bug where aggregates break with filtered elements during import.

This commit is contained in:
Dion Moult
2021-09-12 17:29:11 +10:00
parent 0453db7745
commit 92adeca837
6 changed files with 84 additions and 30 deletions
+3
View File
@@ -18,8 +18,11 @@
import bpy import bpy
import uuid import uuid
import zipfile
import tempfile
import ifcopenshell import ifcopenshell
import blenderbim.bim.handler import blenderbim.bim.handler
from pathlib import Path
class IfcStore: class IfcStore:
+17 -15
View File
@@ -211,10 +211,6 @@ class IfcImporter:
self.profile_code("Process element filter") self.profile_code("Process element filter")
self.create_collections() self.create_collections()
self.profile_code("Create collections") self.profile_code("Create collections")
self.create_aggregates()
self.profile_code("Create aggregates")
self.create_aggregate_tree()
self.profile_code("Create aggregate tree")
self.create_openings_collection() self.create_openings_collection()
self.profile_code("Create opening collection") self.profile_code("Create opening collection")
self.create_materials() self.create_materials()
@@ -978,15 +974,16 @@ class IfcImporter:
self.project["blender"].objects.link(obj) self.project["blender"].objects.link(obj)
def create_collections(self): def create_collections(self):
if self.ifc_import_settings.collection_mode == "DECOMPOSITION" and len(self.file.by_type("IfcRelAggregates")) > 10000:
# More than 10,000 collections makes Blender unhappy
print("Falling back to SPATIAL_DECOMPOSITION collection mode")
self.ifc_import_settings.collection_mode = "SPATIAL_DECOMPOSITION"
if self.ifc_import_settings.collection_mode == "DECOMPOSITION": if self.ifc_import_settings.collection_mode == "DECOMPOSITION":
self.create_decomposition_collections() self.create_decomposition_collections()
elif self.ifc_import_settings.collection_mode == "SPATIAL_DECOMPOSITION":
self.create_spatial_decomposition_collections()
def create_decomposition_collections(self): def create_decomposition_collections(self):
self.create_spatial_decomposition_collections()
self.create_aggregate_collections()
def create_spatial_decomposition_collections(self):
containers = set([ifcopenshell.util.element.get_container(e) for e in self.elements]) containers = set([ifcopenshell.util.element.get_container(e) for e in self.elements])
self.decomposition_containers = set() self.decomposition_containers = set()
for container in containers: for container in containers:
@@ -1014,18 +1011,23 @@ class IfcImporter:
for rel_aggregate in element.IsDecomposedBy: for rel_aggregate in element.IsDecomposedBy:
self.add_related_objects(collection, rel_aggregate.RelatedObjects) self.add_related_objects(collection, rel_aggregate.RelatedObjects)
def create_aggregate_collections(self):
self.create_aggregates()
self.create_aggregate_tree()
def create_aggregates(self): def create_aggregates(self):
if self.ifc_import_settings.has_filter: if self.ifc_import_settings.has_filter:
rel_aggregates = [e.IsDecomposedBy[0].RelatingObject for e in self.elements if e.IsDecomposedBy] rel_aggregates = [e.IsDecomposedBy[0] for e in self.elements if e.IsDecomposedBy]
else: else:
rel_aggregates = [a for a in self.file.by_type("IfcRelAggregates") if a.RelatingObject.is_a("IfcElement")] rel_aggregates = [a for a in self.file.by_type("IfcRelAggregates") if a.RelatingObject.is_a("IfcElement")]
if len(rel_aggregates) > 10000: if len(rel_aggregates) > 10000:
# More than 10,000 collections makes Blender unhappy # More than 10,000 collections makes Blender unhappy
print("Falling back to SPATIAL_DECOMPOSITION collection mode") print("Skipping aggregate collections for performance.")
self.ifc_import_settings.collection_mode = "SPATIAL_DECOMPOSITION" return
else:
for rel_aggregate in rel_aggregates: for rel_aggregate in rel_aggregates:
self.create_aggregate(rel_aggregate) self.create_aggregate(rel_aggregate)
def create_aggregate_tree(self): def create_aggregate_tree(self):
for aggregate in self.aggregates.values(): for aggregate in self.aggregates.values():
@@ -514,8 +514,9 @@ class LoadProject(bpy.types.Operator):
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"}) filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
def execute(self, context): def execute(self, context):
if os.path.exists(self.filepath) and "ifc" in os.path.splitext(self.filepath)[1]: if not os.path.exists(self.filepath) or "ifc" not in os.path.splitext(self.filepath)[1].lower():
context.scene.BIMProperties.ifc_file = self.filepath return {"FINISHED"}
context.scene.BIMProperties.ifc_file = self.filepath
context.scene.BIMProjectProperties.is_loading = True context.scene.BIMProjectProperties.is_loading = True
return {"FINISHED"} return {"FINISHED"}
@@ -583,8 +584,19 @@ class LoadProjectElements(bpy.types.Operator):
for container in containers: for container in containers:
for rel in container.ContainsElements: for rel in container.ContainsElements:
elements.update(rel.RelatedElements) elements.update(rel.RelatedElements)
self.append_decomposed_elements(elements)
return elements return elements
def append_decomposed_elements(self, elements):
decomposed_elements = set()
for element in elements:
if element.IsDecomposedBy:
for subelement in element.IsDecomposedBy[0].RelatedObjects:
decomposed_elements.add(subelement)
if decomposed_elements:
self.append_decomposed_elements(decomposed_elements)
elements.update(decomposed_elements)
def get_ifc_class_elements(self): def get_ifc_class_elements(self):
elements = set() elements = set()
for filter_category in self.props.filter_categories: for filter_category in self.props.filter_categories:
+2 -2
View File
@@ -64,7 +64,7 @@ def updateDataDir(self, context):
blenderbim.bim.schema.ifc.data_dir = context.scene.BIMProperties.data_dir blenderbim.bim.schema.ifc.data_dir = context.scene.BIMProperties.data_dir
def updateIfcFile(self, context): def update_ifc_file(self, context):
if context.scene.BIMProperties.ifc_file: if context.scene.BIMProperties.ifc_file:
blenderbim.bim.handler.loadIfcStore(context.scene) blenderbim.bim.handler.loadIfcStore(context.scene)
@@ -221,7 +221,7 @@ class BIMProperties(PropertyGroup):
data_dir: StringProperty( data_dir: StringProperty(
default=os.path.join(cwd, "data") + os.path.sep, name="Data Directory", update=updateDataDir default=os.path.join(cwd, "data") + os.path.sep, name="Data Directory", update=updateDataDir
) )
ifc_file: StringProperty(name="IFC File", update=updateIfcFile) ifc_file: StringProperty(name="IFC File", update=update_ifc_file)
export_schema: EnumProperty(items=[("IFC4", "IFC4", ""), ("IFC2X3", "IFC2X3", "")], name="IFC Schema") export_schema: EnumProperty(items=[("IFC4", "IFC4", ""), ("IFC2X3", "IFC2X3", "")], name="IFC Schema")
last_transaction: StringProperty(name="Last Transaction") last_transaction: StringProperty(name="Last Transaction")
contexts: EnumProperty(items=getContexts, name="Contexts") contexts: EnumProperty(items=getContexts, name="Contexts")
@@ -61,10 +61,14 @@ class TestLoadProjectElements(test.bim.bootstrap.NewFile):
And the object "IfcBuildingStorey/Level 1" is an "IfcBuildingStorey" And the object "IfcBuildingStorey/Level 1" is an "IfcBuildingStorey"
And the object "IfcSlab/Slab" is an "IfcSlab" And the object "IfcSlab/Slab" is an "IfcSlab"
And the object "IfcWall/Wall" is an "IfcWall" And the object "IfcWall/Wall" is an "IfcWall"
And the object "IfcElementAssembly/Empty" is an "IfcElementAssembly"
And the object "IfcBeam/Beam" is an "IfcBeam"
And the object "IfcSite/My Site" is in the collection "IfcSite/My Site" And the object "IfcSite/My Site" is in the collection "IfcSite/My Site"
And the object "IfcBuilding/My Building" is in the collection "IfcBuilding/My Building" And the object "IfcBuilding/My Building" is in the collection "IfcBuilding/My Building"
And the object "IfcBuildingStorey/Ground Floor" is in the collection "IfcBuildingStorey/Ground Floor" And the object "IfcBuildingStorey/Ground Floor" is in the collection "IfcBuildingStorey/Ground Floor"
And the object "IfcBuildingStorey/Level 1" is in the collection "IfcBuildingStorey/Level 1" And the object "IfcBuildingStorey/Level 1" is in the collection "IfcBuildingStorey/Level 1"
And the object "IfcElementAssembly/Empty" is in the collection "IfcElementAssembly/Empty"
And the object "IfcBeam/Beam" is in the collection "IfcElementAssembly/Empty"
And the object "IfcSlab/Slab" is in the collection "IfcBuildingStorey/Ground Floor" And the object "IfcSlab/Slab" is in the collection "IfcBuildingStorey/Ground Floor"
And the object "IfcWall/Wall" is in the collection "IfcBuildingStorey/Level 1" And the object "IfcWall/Wall" is in the collection "IfcBuildingStorey/Level 1"
And "scene.BIMProjectProperties.is_loading" is "False" And "scene.BIMProjectProperties.is_loading" is "False"
@@ -79,20 +83,24 @@ class TestLoadProjectElements(test.bim.bootstrap.NewFile):
Then "scene.BIMProjectProperties.filter_categories['IfcSite/My Site'].total_elements" is "0" Then "scene.BIMProjectProperties.filter_categories['IfcSite/My Site'].total_elements" is "0"
Then "scene.BIMProjectProperties.filter_categories['IfcBuilding/My Building'].total_elements" is "0" Then "scene.BIMProjectProperties.filter_categories['IfcBuilding/My Building'].total_elements" is "0"
Then "scene.BIMProjectProperties.filter_categories['IfcBuildingStorey/Ground Floor'].total_elements" is "1" Then "scene.BIMProjectProperties.filter_categories['IfcBuildingStorey/Ground Floor'].total_elements" is "1"
Then "scene.BIMProjectProperties.filter_categories['IfcBuildingStorey/Level 1'].total_elements" is "1" Then "scene.BIMProjectProperties.filter_categories['IfcBuildingStorey/Level 1'].total_elements" is "2"
When I set "scene.BIMProjectProperties.filter_categories['IfcBuildingStorey/Ground Floor'].is_selected" to "True" When I set "scene.BIMProjectProperties.filter_categories['IfcBuildingStorey/Level 1'].is_selected" to "True"
And I press "bim.load_project_elements" And I press "bim.load_project_elements"
Then the object "IfcProject/My Project" is an "IfcProject" Then the object "IfcProject/My Project" is an "IfcProject"
And the object "IfcSite/My Site" is an "IfcSite" And the object "IfcSite/My Site" is an "IfcSite"
And the object "IfcBuilding/My Building" is an "IfcBuilding" And the object "IfcBuilding/My Building" is an "IfcBuilding"
And the object "IfcBuildingStorey/Ground Floor" is an "IfcBuildingStorey" And the object "IfcBuildingStorey/Level 1" is an "IfcBuildingStorey"
And the object "IfcSlab/Slab" is an "IfcSlab" And the object "IfcWall/Wall" is an "IfcWall"
And the object "IfcElementAssembly/Empty" is an "IfcElementAssembly"
And the object "IfcBeam/Beam" is an "IfcBeam"
And the object "IfcSite/My Site" is in the collection "IfcSite/My Site" And the object "IfcSite/My Site" is in the collection "IfcSite/My Site"
And the object "IfcBuilding/My Building" is in the collection "IfcBuilding/My Building" And the object "IfcBuilding/My Building" is in the collection "IfcBuilding/My Building"
And the object "IfcBuildingStorey/Ground Floor" is in the collection "IfcBuildingStorey/Ground Floor" And the object "IfcBuildingStorey/Level 1" is in the collection "IfcBuildingStorey/Level 1"
And the object "IfcSlab/Slab" is in the collection "IfcBuildingStorey/Ground Floor" And the object "IfcWall/Wall" is in the collection "IfcBuildingStorey/Level 1"
And the object "IfcBuildingStorey/Level 1" does not exist And the object "IfcElementAssembly/Empty" is in the collection "IfcElementAssembly/Empty"
And the object "IfcWall/Wall" does not exist And the object "IfcBeam/Beam" is in the collection "IfcElementAssembly/Empty"
And the object "IfcBuildingStorey/Ground Floor" does not exist
And the object "IfcSlab/Slab" does not exist
""" """
@test.bim.bootstrap.scenario @test.bim.bootstrap.scenario
+32 -3
View File
@@ -1,7 +1,7 @@
ISO-10303-21; ISO-10303-21;
HEADER; HEADER;
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1'); FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
FILE_NAME('basic.ifc','2021-09-11T19:44:13+10:00',(),(),'IfcOpenShell 0.6.0b0','BlenderBIM 0.0.999999','Nobody'); FILE_NAME('basic.ifc','2021-09-12T16:48:33+10:00',(),(),'IfcOpenShell 0.6.0b0','BlenderBIM 0.0.999999','Nobody');
FILE_SCHEMA(('IFC4')); FILE_SCHEMA(('IFC4'));
ENDSEC; ENDSEC;
DATA; DATA;
@@ -110,8 +110,8 @@ DATA;
#133=IFCSHAPEREPRESENTATION(#22,'Box','BoundingBox',(#132)); #133=IFCSHAPEREPRESENTATION(#22,'Box','BoundingBox',(#132));
#134=IFCPRODUCTDEFINITIONSHAPE($,$,(#133,#130)); #134=IFCPRODUCTDEFINITIONSHAPE($,$,(#133,#130));
#135=IFCSTYLEDITEM(#129,(#101),'Material'); #135=IFCSTYLEDITEM(#129,(#101),'Material');
#136=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1631353443,#9,#8,1631353443); #136=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1631429228,#9,#8,1631353443);
#137=IFCRELCONTAINEDINSPATIALSTRUCTURE('1CgivZt6z1l8IgKaTkZMcT',#136,$,$,(#116),#71); #137=IFCRELCONTAINEDINSPATIALSTRUCTURE('1CgivZt6z1l8IgKaTkZMcT',#136,$,$,(#116,#159),#71);
#148=IFCCARTESIANPOINT((0.,0.,3.)); #148=IFCCARTESIANPOINT((0.,0.,3.));
#149=IFCDIRECTION((0.,0.,1.)); #149=IFCDIRECTION((0.,0.,1.));
#150=IFCDIRECTION((1.,0.,0.)); #150=IFCDIRECTION((1.,0.,0.));
@@ -122,5 +122,34 @@ DATA;
#155=IFCDIRECTION((1.,0.,0.)); #155=IFCDIRECTION((1.,0.,0.));
#156=IFCAXIS2PLACEMENT3D(#153,#154,#155); #156=IFCAXIS2PLACEMENT3D(#153,#154,#155);
#157=IFCLOCALPLACEMENT(#152,#156); #157=IFCLOCALPLACEMENT(#152,#156);
#158=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1631429175,#9,#8,1631429175);
#159=IFCELEMENTASSEMBLY('122brjtWrFAPe7NkNdFNlG',#158,'Empty',$,$,#174,$,$,$,.ACCESSORY_ASSEMBLY.);
#170=IFCCARTESIANPOINT((0.,0.,0.));
#171=IFCDIRECTION((0.,0.,1.));
#172=IFCDIRECTION((1.,0.,0.));
#173=IFCAXIS2PLACEMENT3D(#170,#171,#172);
#174=IFCLOCALPLACEMENT(#152,#173);
#175=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1631429290,#9,#8,1631429215);
#176=IFCBEAM('2f_ancUNT1$Btb3bI4H7gM',#175,'Beam',$,$,#216,#194,$,.BEAM.);
#182=IFCINDEXEDPOLYGONALFACE((1,2,4,3));
#183=IFCINDEXEDPOLYGONALFACE((3,4,8,7));
#184=IFCINDEXEDPOLYGONALFACE((7,8,6,5));
#185=IFCINDEXEDPOLYGONALFACE((5,6,2,1));
#186=IFCINDEXEDPOLYGONALFACE((3,7,5,1));
#187=IFCINDEXEDPOLYGONALFACE((8,4,2,6));
#188=IFCCARTESIANPOINTLIST3D(((-1.,-1.,-1.),(-1.,-1.,1.),(-1.,1.,-1.),(-1.,1.,1.),(1.,-1.,-1.),(1.,-1.,1.),(1.,1.,-1.),(1.,1.,1.)));
#189=IFCPOLYGONALFACESET(#188,$,(#182,#183,#184,#185,#186,#187),$);
#190=IFCSHAPEREPRESENTATION(#21,'Body','Tessellation',(#189));
#191=IFCCARTESIANPOINT((-1.,-1.,-1.));
#192=IFCBOUNDINGBOX(#191,2.,2.,2.);
#193=IFCSHAPEREPRESENTATION(#22,'Box','BoundingBox',(#192));
#194=IFCPRODUCTDEFINITIONSHAPE($,$,(#193,#190));
#205=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1631429238,#9,#8,1631429238);
#206=IFCRELAGGREGATES('1SGr7ReC13EhuetdWMr2G9',#205,$,$,#159,(#176));
#212=IFCCARTESIANPOINT((0.,0.,-3.));
#213=IFCDIRECTION((0.,0.,1.));
#214=IFCDIRECTION((1.,0.,0.));
#215=IFCAXIS2PLACEMENT3D(#212,#213,#214);
#216=IFCLOCALPLACEMENT(#174,#215);
ENDSEC; ENDSEC;
END-ISO-10303-21; END-ISO-10303-21;