mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
New support for specifying collection modes. Spatial elements now support 2D representations as well.
This commit is contained in:
@@ -163,9 +163,8 @@ class IfcImporter:
|
||||
self.settings_native.set(self.settings_native.INCLUDE_CURVES, True)
|
||||
self.settings_2d = ifcopenshell.geom.settings()
|
||||
self.settings_2d.set(self.settings_2d.INCLUDE_CURVES, True)
|
||||
self.iterator_elements = set()
|
||||
self.project = None
|
||||
self.spatial_structure_elements = {}
|
||||
self.collections = {}
|
||||
self.elements = set()
|
||||
self.type_collection = None
|
||||
self.type_products = {}
|
||||
@@ -177,7 +176,6 @@ class IfcImporter:
|
||||
self.added_data = {}
|
||||
self.native_elements = set()
|
||||
self.native_data = {}
|
||||
self.aggregates = {}
|
||||
|
||||
self.material_creator = MaterialCreator(ifc_import_settings, self)
|
||||
|
||||
@@ -217,24 +215,24 @@ class IfcImporter:
|
||||
self.profile_code("Create materials")
|
||||
self.create_styles()
|
||||
self.profile_code("Create styles")
|
||||
self.parse_native_elements()
|
||||
self.profile_code("Parsing native elements")
|
||||
self.create_grids()
|
||||
self.profile_code("Create grids")
|
||||
self.create_native_products()
|
||||
self.profile_code("Create native products")
|
||||
self.create_products()
|
||||
self.profile_code("Create products")
|
||||
self.create_empty_and_2d_elements()
|
||||
self.profile_code("Create empty products")
|
||||
self.create_type_products()
|
||||
self.profile_code("Create type products")
|
||||
self.create_annotation()
|
||||
self.profile_code("Create annotation")
|
||||
self.create_structural_elements()
|
||||
self.profile_code("Create structural elements")
|
||||
self.place_objects_in_spatial_tree()
|
||||
self.profile_code("Placing objects in spatial tree")
|
||||
self.parse_native_elements()
|
||||
self.profile_code("Parsing native elements")
|
||||
self.create_native_elements()
|
||||
self.profile_code("Create native elements")
|
||||
self.create_elements()
|
||||
self.profile_code("Create elements")
|
||||
self.create_grids()
|
||||
self.profile_code("Create grids")
|
||||
self.create_spatial_elements()
|
||||
self.profile_code("Create spatial elements")
|
||||
self.create_structural_items()
|
||||
self.profile_code("Create structural items")
|
||||
self.create_type_products()
|
||||
self.profile_code("Create type products")
|
||||
self.place_objects_in_collections()
|
||||
self.profile_code("Place objects in collections")
|
||||
if self.ifc_import_settings.should_merge_by_class:
|
||||
self.merge_by_class()
|
||||
self.profile_code("Merging by class")
|
||||
@@ -271,15 +269,30 @@ class IfcImporter:
|
||||
def process_element_filter(self):
|
||||
if self.ifc_import_settings.has_filter:
|
||||
self.elements = set(self.ifc_import_settings.elements)
|
||||
self.spatial_elements = self.get_spatial_elements_filtered_by_elements(self.elements)
|
||||
else:
|
||||
self.elements = set(self.file.by_type("IfcElement"))
|
||||
self.iterator_elements = set(self.elements)
|
||||
if self.file.schema == "IFC2X3":
|
||||
self.spatial_elements = set(self.file.by_type("IfcSpatialStructureElement"))
|
||||
else:
|
||||
self.spatial_elements = set(self.file.by_type("IfcSpatialElement"))
|
||||
|
||||
def get_spatial_elements_filtered_by_elements(self, elements):
|
||||
leaf_spatial_elements = set([ifcopenshell.util.element.get_container(e) for e in elements])
|
||||
results = set()
|
||||
for spatial_element in leaf_spatial_elements:
|
||||
while True:
|
||||
results.add(spatial_element)
|
||||
spatial_element = ifcopenshell.util.element.get_aggregate(spatial_element)
|
||||
if not spatial_element or spatial_element.is_a("IfcContext"):
|
||||
break
|
||||
return results
|
||||
|
||||
def parse_native_elements(self):
|
||||
for element in self.elements:
|
||||
if self.is_native(element):
|
||||
self.native_elements.add(element)
|
||||
self.iterator_elements -= self.native_elements
|
||||
self.elements -= self.native_elements
|
||||
|
||||
def is_native(self, element):
|
||||
if (
|
||||
@@ -468,14 +481,7 @@ class IfcImporter:
|
||||
grid_collection.objects.link(obj)
|
||||
|
||||
def create_type_products(self):
|
||||
for collection in self.project["blender"].children:
|
||||
if collection.name == "Types":
|
||||
self.type_collection = collection
|
||||
break
|
||||
if not self.type_collection:
|
||||
self.type_collection = bpy.data.collections.new("Types")
|
||||
self.project["blender"].children.link(self.type_collection)
|
||||
|
||||
# TODO allow filtering of spatial elements too
|
||||
if self.ifc_import_settings.has_filter:
|
||||
type_products = set([ifcopenshell.util.element.get_type(e) for e in self.elements])
|
||||
else:
|
||||
@@ -518,7 +524,7 @@ class IfcImporter:
|
||||
):
|
||||
return representation_map
|
||||
|
||||
def create_native_products(self):
|
||||
def create_native_elements(self):
|
||||
total = 0
|
||||
checkpoint = time.time()
|
||||
bm = bmesh.new()
|
||||
@@ -551,39 +557,51 @@ class IfcImporter:
|
||||
bm.to_mesh(mesh)
|
||||
bm.clear()
|
||||
bm.free()
|
||||
|
||||
print("Done creating geometry")
|
||||
|
||||
def create_products(self):
|
||||
if self.ifc_import_settings.has_filter and not self.iterator_elements:
|
||||
return
|
||||
def create_spatial_elements(self):
|
||||
products = self.create_products(self.spatial_elements)
|
||||
self.spatial_elements -= products
|
||||
products = self.create_curve_products(self.spatial_elements)
|
||||
self.spatial_elements -= products
|
||||
for element in self.spatial_elements:
|
||||
self.create_product(element)
|
||||
|
||||
def create_elements(self):
|
||||
products = self.create_products(self.elements)
|
||||
self.elements -= products
|
||||
products = self.create_curve_products(self.elements)
|
||||
self.elements -= products
|
||||
for element in self.elements:
|
||||
self.create_product(element)
|
||||
|
||||
def create_products(self, products):
|
||||
results = set()
|
||||
if not products:
|
||||
return results
|
||||
if self.ifc_import_settings.should_use_cpu_multiprocessing:
|
||||
iterator = ifcopenshell.geom.iterator(
|
||||
self.settings,
|
||||
self.file,
|
||||
multiprocessing.cpu_count(),
|
||||
include=self.iterator_elements or None,
|
||||
self.settings, self.file, multiprocessing.cpu_count(), include=products
|
||||
)
|
||||
else:
|
||||
iterator = ifcopenshell.geom.iterator(self.settings, self.file, include=self.iterator_elements or None)
|
||||
iterator = ifcopenshell.geom.iterator(self.settings, self.file, include=products)
|
||||
valid_file = iterator.initialize()
|
||||
if not valid_file:
|
||||
return False
|
||||
return results
|
||||
checkpoint = time.time()
|
||||
total_created = 0
|
||||
approx_total_products = len(self.iterator_elements) or len(self.file.by_type("IfcElement"))
|
||||
total = 0
|
||||
start_progress = self.progress
|
||||
progress_range = 85 - start_progress
|
||||
while True:
|
||||
if total_created % 250 == 0:
|
||||
total += 1
|
||||
if total % 250 == 0:
|
||||
print(
|
||||
"{} / ~{} elements processed in {:.2f}s ...".format(
|
||||
total_created, approx_total_products, time.time() - checkpoint
|
||||
"{} ({}%) elements processed in {:.2f}s ...".format(
|
||||
total, iterator.progress(), time.time() - checkpoint
|
||||
)
|
||||
)
|
||||
checkpoint = time.time()
|
||||
if approx_total_products:
|
||||
self.update_progress(((total_created / approx_total_products) * progress_range) + start_progress)
|
||||
#self.update_progress(((total / approx_total_products) * progress_range) + start_progress)
|
||||
shape = iterator.get()
|
||||
if shape:
|
||||
product = self.file.by_id(shape.guid)
|
||||
@@ -592,35 +610,18 @@ class IfcImporter:
|
||||
if shape.context not in ["Body", "Facetation"] and IfcStore.get_element(shape.guid):
|
||||
# We only load a single context, and we prioritise the Body context. See #1290.
|
||||
pass
|
||||
elif product.is_a("IfcAnnotation") and product.ObjectType == "DRAWING":
|
||||
# We have already processed this during the create_annotation step
|
||||
pass
|
||||
else:
|
||||
self.create_product(product, shape)
|
||||
total_created += 1
|
||||
results.add(product)
|
||||
if not iterator.next():
|
||||
break
|
||||
print("Done creating geometry")
|
||||
|
||||
def create_empty_and_2d_elements(self):
|
||||
curve_products = []
|
||||
|
||||
unadded_element_ids = set([e.id() for e in self.elements]) - set(self.added_data.keys())
|
||||
for element_id in unadded_element_ids:
|
||||
element = self.file.by_id(element_id)
|
||||
if element.is_a("IfcPort"):
|
||||
continue
|
||||
if not element.Representation:
|
||||
self.create_product(element)
|
||||
else:
|
||||
curve_products.append(element)
|
||||
if curve_products:
|
||||
self.create_curve_products(curve_products)
|
||||
return results
|
||||
|
||||
def create_annotation(self):
|
||||
self.create_curve_products(self.file.by_type("IfcAnnotation"))
|
||||
|
||||
def create_structural_elements(self):
|
||||
def create_structural_items(self):
|
||||
# Create structural collections
|
||||
self.structural_member_collection = bpy.data.collections.new("Members")
|
||||
self.structural_connection_collection = bpy.data.collections.new("Connections")
|
||||
@@ -659,6 +660,9 @@ class IfcImporter:
|
||||
self.link_element(product, obj)
|
||||
|
||||
def create_curve_products(self, products):
|
||||
results = set()
|
||||
if not products:
|
||||
return results
|
||||
if self.ifc_import_settings.should_use_cpu_multiprocessing:
|
||||
iterator = ifcopenshell.geom.iterator(
|
||||
self.settings_2d, self.file, multiprocessing.cpu_count(), include=products
|
||||
@@ -667,7 +671,7 @@ class IfcImporter:
|
||||
iterator = ifcopenshell.geom.iterator(self.settings_2d, self.file, include=products)
|
||||
valid_file = iterator.initialize()
|
||||
if not valid_file:
|
||||
return False
|
||||
return results
|
||||
checkpoint = time.time()
|
||||
total = 0
|
||||
while True:
|
||||
@@ -677,10 +681,13 @@ class IfcImporter:
|
||||
checkpoint = time.time()
|
||||
shape = iterator.get()
|
||||
if shape:
|
||||
product = self.file.by_id(shape.guid)
|
||||
self.create_product(self.file.by_id(shape.guid), shape)
|
||||
results.add(product)
|
||||
if not iterator.next():
|
||||
break
|
||||
print("Done creating geometry")
|
||||
return results
|
||||
|
||||
def create_product(self, element, shape=None, mesh=None):
|
||||
if element is None:
|
||||
@@ -731,7 +738,7 @@ class IfcImporter:
|
||||
def get_representation_item_material_name(self, item):
|
||||
if not item.StyledByItem:
|
||||
return
|
||||
style_ids = [e.id() for e in self.ifc_importer.file.traverse(item.StyledByItem[0]) if e.is_a("IfcSurfaceStyle")]
|
||||
style_ids = [e.id() for e in self.file.traverse(item.StyledByItem[0]) if e.is_a("IfcSurfaceStyle")]
|
||||
return style_ids[0] if style_ids else None
|
||||
|
||||
def create_native_faceted_brep(self, element, mesh_name):
|
||||
@@ -982,38 +989,32 @@ class IfcImporter:
|
||||
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:
|
||||
while container:
|
||||
self.decomposition_containers.add(container)
|
||||
container = ifcopenshell.util.element.get_aggregate(container)
|
||||
if container and container.is_a("IfcContext"):
|
||||
container = None
|
||||
for rel_aggregate in self.project["ifc"].IsDecomposedBy or []:
|
||||
self.add_related_objects(self.project["blender"], rel_aggregate.RelatedObjects)
|
||||
self.create_spatial_decomposition_collection(self.project["blender"], rel_aggregate.RelatedObjects)
|
||||
self.create_type_collection()
|
||||
|
||||
def add_related_objects(self, parent, related_objects):
|
||||
def create_type_collection(self):
|
||||
for collection in self.project["blender"].children:
|
||||
if collection.name == "Types":
|
||||
self.type_collection = collection
|
||||
break
|
||||
if not self.type_collection:
|
||||
self.type_collection = bpy.data.collections.new("Types")
|
||||
self.project["blender"].children.link(self.type_collection)
|
||||
|
||||
def create_spatial_decomposition_collection(self, parent, related_objects):
|
||||
for element in related_objects:
|
||||
if element not in self.decomposition_containers:
|
||||
if element not in self.spatial_elements:
|
||||
continue
|
||||
global_id = element.GlobalId
|
||||
collection = bpy.data.collections.new(self.get_name(element))
|
||||
self.spatial_structure_elements[global_id] = {"blender": collection}
|
||||
self.collections[global_id] = collection
|
||||
parent.children.link(collection)
|
||||
obj = self.create_product(element)
|
||||
if obj:
|
||||
self.spatial_structure_elements[global_id]["blender_obj"] = obj
|
||||
collection.objects.link(obj)
|
||||
if element.IsDecomposedBy:
|
||||
for rel_aggregate in element.IsDecomposedBy:
|
||||
self.add_related_objects(collection, rel_aggregate.RelatedObjects)
|
||||
self.create_spatial_decomposition_collection(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] for e in self.elements if e.IsDecomposedBy]
|
||||
else:
|
||||
@@ -1022,39 +1023,24 @@ class IfcImporter:
|
||||
if len(rel_aggregates) > 10000:
|
||||
# More than 10,000 collections makes Blender unhappy
|
||||
print("Skipping aggregate collections for performance.")
|
||||
self.ifc_import_settings.collection_mode = "SPATIAL_DECOMPOSITION"
|
||||
return
|
||||
|
||||
aggregates = {}
|
||||
for rel_aggregate in rel_aggregates:
|
||||
self.create_aggregate(rel_aggregate)
|
||||
element = rel_aggregate.RelatingObject
|
||||
collection = bpy.data.collections.new(self.get_name(element))
|
||||
aggregates[element.GlobalId] = {"element": element, "collection": collection}
|
||||
self.collections[element.GlobalId] = collection
|
||||
|
||||
def create_aggregate_tree(self):
|
||||
for aggregate in self.aggregates.values():
|
||||
if aggregate["container"].is_a("IfcSpatialStructureElement"):
|
||||
self.spatial_structure_elements[aggregate["container"].GlobalId]["blender"].children.link(
|
||||
aggregate["blender"]
|
||||
)
|
||||
else:
|
||||
self.aggregates[aggregate["container"].GlobalId]["blender"].children.link(aggregate["blender"])
|
||||
|
||||
def create_aggregate(self, rel_aggregate):
|
||||
element = rel_aggregate.RelatingObject
|
||||
obj = bpy.data.objects.new("{}/{}".format(element.is_a(), element.Name), None)
|
||||
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element))
|
||||
self.link_element(element, obj)
|
||||
collection = bpy.data.collections.new(obj.name)
|
||||
collection.objects.link(obj)
|
||||
self.aggregates[element.GlobalId] = {
|
||||
"blender": collection,
|
||||
"blender_obj": obj,
|
||||
"container": self.get_aggregate_container(element),
|
||||
}
|
||||
|
||||
def get_aggregate_container(self, element):
|
||||
if hasattr(element, "ContainedInStructure") and element.ContainedInStructure:
|
||||
container = element.ContainedInStructure[0].RelatingStructure
|
||||
elif hasattr(element, "Decomposes") and element.Decomposes:
|
||||
container = element.Decomposes[0].RelatingObject
|
||||
return container
|
||||
for global_id, aggregate in aggregates.items():
|
||||
parent = ifcopenshell.util.element.get_aggregate(aggregate["element"])
|
||||
if parent:
|
||||
self.collections[parent.GlobalId].children.link(aggregate["collection"])
|
||||
continue
|
||||
parent = ifcopenshell.util.element.get_container(aggregate["element"])
|
||||
if parent:
|
||||
self.collections[parent.GlobalId].children.link(aggregate["collection"])
|
||||
|
||||
def create_openings_collection(self):
|
||||
self.opening_collection = bpy.data.collections.new("IfcOpeningElements")
|
||||
@@ -1115,74 +1101,41 @@ class IfcImporter:
|
||||
def get_name(self, element):
|
||||
return "{}/{}".format(element.is_a(), element.Name)
|
||||
|
||||
def place_objects_in_spatial_tree(self):
|
||||
def place_objects_in_collections(self):
|
||||
for ifc_definition_id, obj in self.added_data.items():
|
||||
if isinstance(obj, bpy.types.Object):
|
||||
self.place_object_in_spatial_tree(self.file.by_id(ifc_definition_id), obj)
|
||||
self.place_object_in_collection(self.file.by_id(ifc_definition_id), obj)
|
||||
|
||||
def place_object_in_spatial_tree(self, element, obj):
|
||||
if element.is_a() in ["IfcProject", "IfcProjectLibrary"]:
|
||||
def place_object_in_collection(self, element, obj):
|
||||
if self.ifc_import_settings.collection_mode == "DECOMPOSITION":
|
||||
self.place_object_in_decomposition_collection(element, obj)
|
||||
elif self.ifc_import_settings.collection_mode == "SPATIAL_DECOMPOSITION":
|
||||
self.place_object_in_spatial_decomposition_collection(element, obj)
|
||||
|
||||
def place_object_in_decomposition_collection(self, element, obj):
|
||||
if element.is_a("IfcProject"):
|
||||
return
|
||||
elif element.GlobalId in self.collections:
|
||||
return self.collections[element.GlobalId].objects.link(obj)
|
||||
elif getattr(element, "Decomposes", None):
|
||||
aggregate = ifcopenshell.util.element.get_aggregate(element)
|
||||
return self.collections[aggregate.GlobalId].objects.link(obj)
|
||||
else:
|
||||
return self.place_object_in_spatial_decomposition_collection(element, obj)
|
||||
|
||||
def place_object_in_spatial_decomposition_collection(self, element, obj):
|
||||
if element.is_a("IfcProject"):
|
||||
return
|
||||
elif element.GlobalId in self.collections:
|
||||
return self.collections[element.GlobalId].objects.link(obj)
|
||||
elif element.is_a("IfcTypeObject"):
|
||||
self.type_collection.objects.link(obj)
|
||||
elif element.GlobalId in self.aggregates:
|
||||
return
|
||||
elif element.GlobalId in self.spatial_structure_elements:
|
||||
if not obj.data:
|
||||
return
|
||||
# Since spatial structure elements are generated as empties, we'll replace it with the representation
|
||||
spatial_obj = self.spatial_structure_elements[element.GlobalId]["blender_obj"]
|
||||
spatial_collection = self.spatial_structure_elements[element.GlobalId]["blender"]
|
||||
spatial_name = spatial_obj.name
|
||||
spatial_collection.objects.link(obj)
|
||||
bpy.data.objects.remove(spatial_obj)
|
||||
obj.name = spatial_name
|
||||
elif (
|
||||
hasattr(element, "ContainedInStructure")
|
||||
and element.ContainedInStructure
|
||||
and element.ContainedInStructure[0].RelatingStructure
|
||||
):
|
||||
container = element.ContainedInStructure[0].RelatingStructure
|
||||
if element.is_a("IfcGrid"):
|
||||
grid_collection = bpy.data.collections.get(obj.name)
|
||||
if grid_collection: # Just in case we ran into invalid grids from Revit
|
||||
self.spatial_structure_elements[container.GlobalId]["blender"].children.link(grid_collection)
|
||||
grid_collection.objects.link(obj)
|
||||
else:
|
||||
self.spatial_structure_elements[container.GlobalId]["blender"].objects.link(obj)
|
||||
elif hasattr(element, "Decomposes") and element.Decomposes:
|
||||
collection = None
|
||||
if element.Decomposes[0].RelatingObject.is_a("IfcProject"):
|
||||
collection = self.project["blender"]
|
||||
elif element.Decomposes[0].RelatingObject.is_a("IfcSpatialStructureElement"):
|
||||
if element.is_a("IfcSpatialStructureElement"):
|
||||
global_id = element.GlobalId
|
||||
if global_id in self.spatial_structure_elements:
|
||||
if (
|
||||
element.is_a("IfcSpatialStructureElement")
|
||||
and "blender_obj" in self.spatial_structure_elements[global_id]
|
||||
):
|
||||
bpy.data.objects.remove(self.spatial_structure_elements[global_id]["blender_obj"])
|
||||
collection = self.spatial_structure_elements[global_id]["blender"]
|
||||
elif self.ifc_import_settings.collection_mode == "SPATIAL_DECOMPOSITION":
|
||||
# TODO: refactor this to a more holistic collection mode feature
|
||||
return self.place_object_in_spatial_tree(element.Decomposes[0].RelatingObject, obj)
|
||||
else:
|
||||
aggregate = element.Decomposes[0].RelatingObject
|
||||
aggregate_data = self.aggregates.get(aggregate.GlobalId)
|
||||
if not aggregate_data:
|
||||
return self.place_object_in_spatial_tree(aggregate, obj)
|
||||
collection = aggregate_data["blender"]
|
||||
if collection:
|
||||
collection.objects.link(obj)
|
||||
else:
|
||||
self.ifc_import_settings.logger.error("An element could not be placed in the spatial tree %s", element)
|
||||
return self.type_collection.objects.link(obj)
|
||||
elif element.is_a("IfcOpeningElement"):
|
||||
self.opening_collection.objects.link(obj)
|
||||
return self.opening_collection.objects.link(obj)
|
||||
elif element.is_a("IfcStructuralMember"):
|
||||
self.structural_member_collection.objects.link(obj)
|
||||
return self.structural_member_collection.objects.link(obj)
|
||||
elif element.is_a("IfcStructuralConnection"):
|
||||
self.structural_connection_collection.objects.link(obj)
|
||||
return self.structural_connection_collection.objects.link(obj)
|
||||
elif element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING":
|
||||
view_collection = bpy.data.collections.get("Views")
|
||||
if not view_collection:
|
||||
@@ -1192,6 +1145,18 @@ class IfcImporter:
|
||||
drawing_collection = bpy.data.collections.new("IfcGroup/" + group.Name)
|
||||
view_collection.children.link(drawing_collection)
|
||||
drawing_collection.objects.link(obj)
|
||||
return
|
||||
|
||||
container = ifcopenshell.util.element.get_container(element)
|
||||
if container:
|
||||
if element.is_a("IfcGrid"): # TODO: refactor into a more holistic collection mode feature
|
||||
grid_collection = bpy.data.collections.get(obj.name)
|
||||
if grid_collection: # Just in case we run into invalid grids from Revit
|
||||
self.collections[container.GlobalId].children.link(grid_collection)
|
||||
grid_collection.objects.link(obj)
|
||||
else:
|
||||
self.collections[container.GlobalId].objects.link(obj)
|
||||
|
||||
else:
|
||||
self.ifc_import_settings.logger.warning("Warning: this object is outside the spatial hierarchy %s", element)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
|
||||
@@ -377,7 +377,7 @@ class AppendLibraryElement(bpy.types.Operator):
|
||||
self.import_type_materials(element, ifc_importer)
|
||||
self.import_type_styles(element, ifc_importer)
|
||||
ifc_importer.create_type_product(element)
|
||||
ifc_importer.place_objects_in_spatial_tree()
|
||||
ifc_importer.place_objects_in_collections()
|
||||
|
||||
def import_type_materials(self, element, ifc_importer):
|
||||
for rel in element.HasAssociations:
|
||||
@@ -569,6 +569,7 @@ class LoadProjectElements(bpy.types.Operator):
|
||||
settings.elements = self.get_whitelist_elements()
|
||||
elif self.props.filter_mode == "BLACKLIST":
|
||||
settings.elements = self.get_blacklist_elements()
|
||||
settings.collection_mode = self.props.collection_mode
|
||||
settings.should_use_cpu_multiprocessing = self.props.should_use_cpu_multiprocessing
|
||||
settings.should_merge_by_class = self.props.should_merge_by_class
|
||||
settings.should_merge_by_material = self.props.should_merge_by_material
|
||||
|
||||
@@ -216,6 +216,61 @@ class TestLoadProjectElements(test.bim.bootstrap.NewFile):
|
||||
And the object "IfcWall/Wall" does not exist
|
||||
"""
|
||||
|
||||
@test.bim.bootstrap.scenario
|
||||
def test_loading_with_the_decomposition_collection_mode(self):
|
||||
return """
|
||||
Given I press "bim.load_project(filepath='{cwd}/test/files/decomposition.ifc', is_advanced=True)"
|
||||
When I set "scene.BIMProjectProperties.collection_mode" to "DECOMPOSITION"
|
||||
And I set "scene.BIMProjectProperties.filter_mode" to "NONE"
|
||||
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/My Storey" is an "IfcBuildingStorey"
|
||||
And the object "IfcSpace/Space" is an "IfcSpace"
|
||||
And the object "IfcElementAssembly/Assembly" 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/My Storey" is in the collection "IfcBuildingStorey/My Storey"
|
||||
And the object "IfcSpace/Space" is in the collection "IfcSpace/Space"
|
||||
And the object "IfcElementAssembly/Assembly" is in the collection "IfcElementAssembly/Assembly"
|
||||
And the object "IfcBeam/Beam" is in the collection "IfcElementAssembly/Assembly"
|
||||
And the collection "IfcSite/My Site" is in the collection "IfcProject/My Project"
|
||||
And the collection "IfcBuilding/My Building" is in the collection "IfcSite/My Site"
|
||||
And the collection "IfcBuildingStorey/My Storey" is in the collection "IfcBuilding/My Building"
|
||||
And the collection "IfcSpace/Space" is in the collection "IfcBuildingStorey/My Storey"
|
||||
And the collection "IfcElementAssembly/Assembly" is in the collection "IfcSpace/Space"
|
||||
And "scene.BIMProjectProperties.is_loading" is "False"
|
||||
"""
|
||||
|
||||
@test.bim.bootstrap.scenario
|
||||
def test_loading_with_the_spatial_decomposition_collection_mode(self):
|
||||
return """
|
||||
Given I press "bim.load_project(filepath='{cwd}/test/files/decomposition.ifc', is_advanced=True)"
|
||||
When I set "scene.BIMProjectProperties.collection_mode" to "SPATIAL_DECOMPOSITION"
|
||||
And I set "scene.BIMProjectProperties.filter_mode" to "NONE"
|
||||
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/My Storey" is an "IfcBuildingStorey"
|
||||
And the object "IfcSpace/Space" is an "IfcSpace"
|
||||
And the object "IfcElementAssembly/Assembly" 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/My Storey" is in the collection "IfcBuildingStorey/My Storey"
|
||||
And the object "IfcSpace/Space" is in the collection "IfcSpace/Space"
|
||||
And the object "IfcElementAssembly/Assembly" is in the collection "IfcSpace/Space"
|
||||
And the object "IfcBeam/Beam" is in the collection "IfcSpace/Space"
|
||||
And the collection "IfcSite/My Site" is in the collection "IfcProject/My Project"
|
||||
And the collection "IfcBuilding/My Building" is in the collection "IfcSite/My Site"
|
||||
And the collection "IfcBuildingStorey/My Storey" is in the collection "IfcBuilding/My Building"
|
||||
And the collection "IfcSpace/Space" is in the collection "IfcBuildingStorey/My Storey"
|
||||
And "scene.BIMProjectProperties.is_loading" is "False"
|
||||
"""
|
||||
|
||||
@test.bim.bootstrap.scenario
|
||||
def test_manual_offset_of_object_placements(self):
|
||||
return """
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
||||
FILE_NAME('decomposition.ifc','2021-09-21T09:50:31+10:00',(),(),'IfcOpenShell 0.6.0b0','BlenderBIM 0.0.999999','Nobody');
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPERSON('HSeldon','Seldon','Hari',$,$,$,$,$);
|
||||
#2=IFCORGANIZATION('APTR','Aperture Science',$,$,$);
|
||||
#3=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$);
|
||||
#4=IFCTELECOMADDRESS(.USERDEFINED.,'The main webpage of the software collection.','WEBPAGE',$,$,$,$,'https://ifcopenshell.org',$);
|
||||
#5=IFCTELECOMADDRESS(.USERDEFINED.,'The BlenderBIM Add-on webpage of the software collection.','WEBPAGE',$,$,$,$,'https://blenderbim.org',$);
|
||||
#6=IFCTELECOMADDRESS(.USERDEFINED.,'The source code repository of the software collection.','REPOSITORY',$,$,$,$,'https://github.com/IfcOpenShell/IfcOpenShell.git',$);
|
||||
#7=IFCORGANIZATION($,'IfcOpenShell','IfcOpenShell is an open source (LGPL) software library that helps users and software developers to work with the IFC file format.',(#3),(#4,#5,#6));
|
||||
#8=IFCAPPLICATION(#7,'0.0.999999','BlenderBIM Add-on','BlenderBIM');
|
||||
#9=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||
#10=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1632181704,#9,#8,1632181704);
|
||||
#11=IFCPROJECT('25mhbqrQzBIhdUUHrZK_q3',#10,'My Project',$,$,$,$,(#20,#27),#15);
|
||||
#12=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#13=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
#14=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||
#15=IFCUNITASSIGNMENT((#12,#13,#14));
|
||||
#16=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#17=IFCDIRECTION((0.,0.,1.));
|
||||
#18=IFCDIRECTION((1.,0.,0.));
|
||||
#19=IFCAXIS2PLACEMENT3D(#16,#17,#18);
|
||||
#20=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#19,$);
|
||||
#21=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#20,$,.MODEL_VIEW.,$);
|
||||
#22=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Box','Model',*,*,*,*,#20,$,.MODEL_VIEW.,$);
|
||||
#23=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#24=IFCDIRECTION((0.,0.,1.));
|
||||
#25=IFCDIRECTION((1.,0.,0.));
|
||||
#26=IFCAXIS2PLACEMENT3D(#23,#24,#25);
|
||||
#27=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#26,$);
|
||||
#28=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#27,$,.PLAN_VIEW.,$);
|
||||
#29=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1632181704,#9,#8,1632181704);
|
||||
#30=IFCSITE('2nSisauITAw99Ipgpxnuc1',#29,'My Site',$,$,#56,$,$,$,$,$,$,$,$);
|
||||
#36=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1632181704,#9,#8,1632181704);
|
||||
#37=IFCBUILDING('3VLkNpYOP6c92$32FgVhz1',#36,'My Building',$,$,#63,$,$,$,$,$,$);
|
||||
#43=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1632181704,#9,#8,1632181704);
|
||||
#44=IFCBUILDINGSTOREY('2EXifXJjf7IvZ04BvDaAfA',#43,'My Storey',$,$,#70,$,$,$,$);
|
||||
#50=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1632181704,#9,#8,1632181704);
|
||||
#51=IFCRELAGGREGATES('0riie8ARHD2P_3P8pg2s0e',#50,$,$,#11,(#30));
|
||||
#52=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#53=IFCDIRECTION((0.,0.,1.));
|
||||
#54=IFCDIRECTION((1.,0.,0.));
|
||||
#55=IFCAXIS2PLACEMENT3D(#52,#53,#54);
|
||||
#56=IFCLOCALPLACEMENT($,#55);
|
||||
#57=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1632181704,#9,#8,1632181704);
|
||||
#58=IFCRELAGGREGATES('3HMkYYf2j01up2RzoG1K$S',#57,$,$,#30,(#37));
|
||||
#59=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#60=IFCDIRECTION((0.,0.,1.));
|
||||
#61=IFCDIRECTION((1.,0.,0.));
|
||||
#62=IFCAXIS2PLACEMENT3D(#59,#60,#61);
|
||||
#63=IFCLOCALPLACEMENT(#56,#62);
|
||||
#64=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1632181704,#9,#8,1632181704);
|
||||
#65=IFCRELAGGREGATES('1r5yHIAoX7ExW8Fb$KK6Rv',#64,$,$,#37,(#44));
|
||||
#66=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#67=IFCDIRECTION((0.,0.,1.));
|
||||
#68=IFCDIRECTION((1.,0.,0.));
|
||||
#69=IFCAXIS2PLACEMENT3D(#66,#67,#68);
|
||||
#70=IFCLOCALPLACEMENT(#63,#69);
|
||||
#71=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1632181716,#9,#8,1632181716);
|
||||
#72=IFCSPACE('13wXihTNXCyASBfO8n3YgZ',#71,'Space',$,$,#102,#90,$,$,.EXTERNAL.,$);
|
||||
#78=IFCINDEXEDPOLYGONALFACE((1,5,7,3));
|
||||
#79=IFCINDEXEDPOLYGONALFACE((4,3,7,8));
|
||||
#80=IFCINDEXEDPOLYGONALFACE((8,7,5,6));
|
||||
#81=IFCINDEXEDPOLYGONALFACE((6,2,4,8));
|
||||
#82=IFCINDEXEDPOLYGONALFACE((2,1,3,4));
|
||||
#83=IFCINDEXEDPOLYGONALFACE((6,5,1,2));
|
||||
#84=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.)));
|
||||
#85=IFCPOLYGONALFACESET(#84,$,(#78,#79,#80,#81,#82,#83),$);
|
||||
#86=IFCSHAPEREPRESENTATION(#21,'Body','Tessellation',(#85));
|
||||
#87=IFCCARTESIANPOINT((-1.,-1.,-1.));
|
||||
#88=IFCBOUNDINGBOX(#87,2.,2.,2.);
|
||||
#89=IFCSHAPEREPRESENTATION(#22,'Box','BoundingBox',(#88));
|
||||
#90=IFCPRODUCTDEFINITIONSHAPE($,$,(#89,#86));
|
||||
#91=IFCCOLOURRGB($,0.800000011920929,0.800000011920929,0.800000011920929);
|
||||
#92=IFCCOLOURRGB($,0.800000011920929,0.800000011920929,0.800000011920929);
|
||||
#93=IFCSURFACESTYLERENDERING(#91,0.,#92,$,$,$,$,$,.NOTDEFINED.);
|
||||
#94=IFCSURFACESTYLE('Material',.BOTH.,(#93));
|
||||
#95=IFCSTYLEDITEM(#85,(#94),'Material');
|
||||
#96=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1632181716,#9,#8,1632181716);
|
||||
#97=IFCRELAGGREGATES('2NuKgx$4L8OBX9R$MwP3Bf',#96,$,$,#44,(#72));
|
||||
#98=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#99=IFCDIRECTION((0.,0.,1.));
|
||||
#100=IFCDIRECTION((1.,0.,0.));
|
||||
#101=IFCAXIS2PLACEMENT3D(#98,#99,#100);
|
||||
#102=IFCLOCALPLACEMENT(#70,#101);
|
||||
#103=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1632181774,#9,#8,1632181774);
|
||||
#104=IFCELEMENTASSEMBLY('0hCUjYJgv6FwS2fAvN21mm',#103,'Assembly',$,$,#120,$,$,$,.ACCESSORY_ASSEMBLY.);
|
||||
#110=IFCRELCONTAINEDINSPATIALSTRUCTURE('2zdJYp1Tr8qvLmsWlFRgz0',#151,$,$,(#104),#72);
|
||||
#116=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#117=IFCDIRECTION((0.,0.,1.));
|
||||
#118=IFCDIRECTION((1.,0.,0.));
|
||||
#119=IFCAXIS2PLACEMENT3D(#116,#117,#118);
|
||||
#120=IFCLOCALPLACEMENT(#102,#119);
|
||||
#121=IFCOWNERHISTORY(#9,#8,.READWRITE.,.MODIFIED.,1632181807,#9,#8,1632181786);
|
||||
#122=IFCBEAM('1KiE3gs9z1sfj9c0spBIGv',#121,'Beam',$,$,#158,#140,$,.BEAM.);
|
||||
#128=IFCINDEXEDPOLYGONALFACE((1,2,4,3));
|
||||
#129=IFCINDEXEDPOLYGONALFACE((3,4,8,7));
|
||||
#130=IFCINDEXEDPOLYGONALFACE((7,8,6,5));
|
||||
#131=IFCINDEXEDPOLYGONALFACE((5,6,2,1));
|
||||
#132=IFCINDEXEDPOLYGONALFACE((3,7,5,1));
|
||||
#133=IFCINDEXEDPOLYGONALFACE((8,4,2,6));
|
||||
#134=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.)));
|
||||
#135=IFCPOLYGONALFACESET(#134,$,(#128,#129,#130,#131,#132,#133),$);
|
||||
#136=IFCSHAPEREPRESENTATION(#21,'Body','Tessellation',(#135));
|
||||
#137=IFCCARTESIANPOINT((-1.,-1.,-1.));
|
||||
#138=IFCBOUNDINGBOX(#137,2.,2.,2.);
|
||||
#139=IFCSHAPEREPRESENTATION(#22,'Box','BoundingBox',(#138));
|
||||
#140=IFCPRODUCTDEFINITIONSHAPE($,$,(#139,#136));
|
||||
#151=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1632181798,#9,#8,1632181798);
|
||||
#152=IFCOWNERHISTORY(#9,#8,.READWRITE.,.ADDED.,1632181807,#9,#8,1632181807);
|
||||
#153=IFCRELAGGREGATES('1N$QRzghfDgA1Hw$swtu4S',#152,$,$,#104,(#122));
|
||||
#154=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#155=IFCDIRECTION((0.,0.,1.));
|
||||
#156=IFCDIRECTION((1.,0.,0.));
|
||||
#157=IFCAXIS2PLACEMENT3D(#154,#155,#156);
|
||||
#158=IFCLOCALPLACEMENT(#120,#157);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
Reference in New Issue
Block a user