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 uuid
import zipfile
import tempfile
import ifcopenshell
import blenderbim.bim.handler
from pathlib import Path
class IfcStore:
+17 -15
View File
@@ -211,10 +211,6 @@ class IfcImporter:
self.profile_code("Process element filter")
self.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.profile_code("Create opening collection")
self.create_materials()
@@ -978,15 +974,16 @@ class IfcImporter:
self.project["blender"].objects.link(obj)
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":
self.create_decomposition_collections()
elif self.ifc_import_settings.collection_mode == "SPATIAL_DECOMPOSITION":
self.create_spatial_decomposition_collections()
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])
self.decomposition_containers = set()
for container in containers:
@@ -1014,18 +1011,23 @@ class IfcImporter:
for rel_aggregate in element.IsDecomposedBy:
self.add_related_objects(collection, rel_aggregate.RelatedObjects)
def create_aggregate_collections(self):
self.create_aggregates()
self.create_aggregate_tree()
def create_aggregates(self):
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:
rel_aggregates = [a for a in self.file.by_type("IfcRelAggregates") if a.RelatingObject.is_a("IfcElement")]
if len(rel_aggregates) > 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"
else:
for rel_aggregate in rel_aggregates:
self.create_aggregate(rel_aggregate)
print("Skipping aggregate collections for performance.")
return
for rel_aggregate in rel_aggregates:
self.create_aggregate(rel_aggregate)
def create_aggregate_tree(self):
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"})
def execute(self, context):
if os.path.exists(self.filepath) and "ifc" in os.path.splitext(self.filepath)[1]:
context.scene.BIMProperties.ifc_file = self.filepath
if not os.path.exists(self.filepath) or "ifc" not in os.path.splitext(self.filepath)[1].lower():
return {"FINISHED"}
context.scene.BIMProperties.ifc_file = self.filepath
context.scene.BIMProjectProperties.is_loading = True
return {"FINISHED"}
@@ -583,8 +584,19 @@ class LoadProjectElements(bpy.types.Operator):
for container in containers:
for rel in container.ContainsElements:
elements.update(rel.RelatedElements)
self.append_decomposed_elements(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):
elements = set()
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
def updateIfcFile(self, context):
def update_ifc_file(self, context):
if context.scene.BIMProperties.ifc_file:
blenderbim.bim.handler.loadIfcStore(context.scene)
@@ -221,7 +221,7 @@ class BIMProperties(PropertyGroup):
data_dir: StringProperty(
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")
last_transaction: StringProperty(name="Last Transaction")
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 "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 "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 "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 "IfcWall/Wall" is in the collection "IfcBuildingStorey/Level 1"
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['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/Level 1'].total_elements" is "1"
When I set "scene.BIMProjectProperties.filter_categories['IfcBuildingStorey/Ground Floor'].is_selected" to "True"
Then "scene.BIMProjectProperties.filter_categories['IfcBuildingStorey/Level 1'].total_elements" is "2"
When I set "scene.BIMProjectProperties.filter_categories['IfcBuildingStorey/Level 1'].is_selected" to "True"
And I press "bim.load_project_elements"
Then the object "IfcProject/My Project" is an "IfcProject"
And the object "IfcSite/My Site" is an "IfcSite"
And the object "IfcBuilding/My Building" is an "IfcBuilding"
And the object "IfcBuildingStorey/Ground Floor" is an "IfcBuildingStorey"
And the object "IfcSlab/Slab" is an "IfcSlab"
And the object "IfcBuildingStorey/Level 1" is an "IfcBuildingStorey"
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 "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 "IfcSlab/Slab" is in the collection "IfcBuildingStorey/Ground Floor"
And the object "IfcBuildingStorey/Level 1" does not exist
And the object "IfcWall/Wall" does not exist
And the object "IfcBuildingStorey/Level 1" is in the collection "IfcBuildingStorey/Level 1"
And the object "IfcWall/Wall" 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 "IfcBuildingStorey/Ground Floor" does not exist
And the object "IfcSlab/Slab" does not exist
"""
@test.bim.bootstrap.scenario
+32 -3
View File
@@ -1,7 +1,7 @@
ISO-10303-21;
HEADER;
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'));
ENDSEC;
DATA;
@@ -110,8 +110,8 @@ DATA;
#133=IFCSHAPEREPRESENTATION(#22,'Box','BoundingBox',(#132));
#134=IFCPRODUCTDEFINITIONSHAPE($,$,(#133,#130));
#135=IFCSTYLEDITEM(#129,(#101),'Material');
#136=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1631353443,#9,#8,1631353443);
#137=IFCRELCONTAINEDINSPATIALSTRUCTURE('1CgivZt6z1l8IgKaTkZMcT',#136,$,$,(#116),#71);
#136=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1631429228,#9,#8,1631353443);
#137=IFCRELCONTAINEDINSPATIALSTRUCTURE('1CgivZt6z1l8IgKaTkZMcT',#136,$,$,(#116,#159),#71);
#148=IFCCARTESIANPOINT((0.,0.,3.));
#149=IFCDIRECTION((0.,0.,1.));
#150=IFCDIRECTION((1.,0.,0.));
@@ -122,5 +122,34 @@ DATA;
#155=IFCDIRECTION((1.,0.,0.));
#156=IFCAXIS2PLACEMENT3D(#153,#154,#155);
#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;
END-ISO-10303-21;