This commit is contained in:
Andrej730
2025-04-28 13:27:30 +05:00
parent f9a0081b33
commit 10f84537de
6 changed files with 38 additions and 29 deletions
+11 -12
View File
@@ -38,7 +38,7 @@ import ifcopenshell.util.shape
import bonsai.tool as tool import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore, IFC_CONNECTED_TYPE from bonsai.bim.ifc import IfcStore, IFC_CONNECTED_TYPE
from bonsai.tool.loader import OBJECT_DATA_TYPE from bonsai.tool.loader import OBJECT_DATA_TYPE
from typing import Dict, Union, Optional, Any, Literal from typing import Dict, Union, Optional, Any, Literal, Iterable
from ifcopenshell.util.shape import MatrixType from ifcopenshell.util.shape import MatrixType
@@ -319,16 +319,15 @@ class IfcImporter:
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True) tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
def process_element_filter(self) -> None: def process_element_filter(self) -> None:
elements: list[ifcopenshell.entity_instance]
if self.ifc_import_settings.has_filter: if self.ifc_import_settings.has_filter:
self.elements = self.ifc_import_settings.elements elements = list(self.ifc_import_settings.elements)
if isinstance(self.elements, set):
self.elements = list(self.elements)
# TODO: enable filtering for annotations # TODO: enable filtering for annotations
else: else:
if self.file.schema in ("IFC2X3", "IFC4"): if self.file.schema in ("IFC2X3", "IFC4"):
self.elements = self.file.by_type("IfcElement") + self.file.by_type("IfcProxy") elements = self.file.by_type("IfcElement") + self.file.by_type("IfcProxy")
else: else:
self.elements = self.file.by_type("IfcElement") elements = self.file.by_type("IfcElement")
drawing_groups = [g for g in self.file.by_type("IfcGroup") if g.ObjectType == "DRAWING"] drawing_groups = [g for g in self.file.by_type("IfcGroup") if g.ObjectType == "DRAWING"]
drawing_annotations = set() drawing_annotations = set()
@@ -338,16 +337,16 @@ class IfcImporter:
self.annotations = set([a for a in self.file.by_type("IfcAnnotation")]) self.annotations = set([a for a in self.file.by_type("IfcAnnotation")])
self.annotations -= drawing_annotations self.annotations -= drawing_annotations
self.elements = [e for e in self.elements if not e.is_a("IfcFeatureElement") or e.is_a("IfcSurfaceFeature")] elements = [e for e in elements if not e.is_a("IfcFeatureElement") or e.is_a("IfcSurfaceFeature")]
if self.ifc_import_settings.element_limit_mode == "UNLIMITED": if self.ifc_import_settings.element_limit_mode == "UNLIMITED":
self.elements = set(self.elements) self.elements = set(elements)
else: else:
offset = self.ifc_import_settings.element_offset offset = self.ifc_import_settings.element_offset
offset_limit = offset + self.ifc_import_settings.element_limit offset_limit = offset + self.ifc_import_settings.element_limit
self.elements = set(self.elements[offset:offset_limit]) self.elements = set(elements[offset:offset_limit])
if self.ifc_import_settings.has_filter or self.ifc_import_settings.element_limit_mode != "UNLIMITED": if self.ifc_import_settings.has_filter or self.ifc_import_settings.element_limit_mode != "UNLIMITED":
self.element_types = set([ifcopenshell.util.element.get_type(e) for e in self.elements]) self.element_types = {t for e in self.elements if (t := ifcopenshell.util.element.get_type(e))}
else: else:
self.element_types = set(self.file.by_type("IfcTypeProduct")) self.element_types = set(self.file.by_type("IfcTypeProduct"))
@@ -1168,13 +1167,13 @@ class IfcImportSettings:
self.element_limit_mode = "UNLIMITED" self.element_limit_mode = "UNLIMITED"
self.element_offset = 0 self.element_offset = 0
self.element_limit = 30000 self.element_limit = 30000
self.has_filter = None self.has_filter = False
self.should_filter_spatial_elements = True self.should_filter_spatial_elements = True
self.should_setup_viewport_camera = True self.should_setup_viewport_camera = True
self.contexts: list[ifcopenshell.entity_instance] = [] self.contexts: list[ifcopenshell.entity_instance] = []
self.context_settings: list[ifcopenshell.geom.main.settings] = [] self.context_settings: list[ifcopenshell.geom.main.settings] = []
self.gross_context_settings: list[ifcopenshell.geom.main.settings] = [] self.gross_context_settings: list[ifcopenshell.geom.main.settings] = []
self.elements: set[ifcopenshell.entity_instance] = set() self.elements: Iterable[ifcopenshell.entity_instance] = set()
self.load_indexed_maps = False self.load_indexed_maps = False
@staticmethod @staticmethod
@@ -1736,13 +1736,14 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
) )
def join_blender_obj(self) -> None: def join_blender_obj(self) -> None:
ifc_file = tool.Ifc.get()
for obj in bpy.context.selected_objects: for obj in bpy.context.selected_objects:
if obj == self.target: if obj == self.target:
continue continue
# TODO Properly handle element types, grid axes, and representation items # TODO Properly handle element types, grid axes, and representation items
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
if element: if element:
ifcopenshell.api.run("root.remove_product", tool.Ifc.get(), product=element) ifcopenshell.api.root.remove_product(ifc_file, product=element)
bpy.ops.object.join() bpy.ops.object.join()
@@ -162,6 +162,11 @@ class RepresentationItemObject(PropertyGroup):
obj: PointerProperty(type=bpy.types.Object) obj: PointerProperty(type=bpy.types.Object)
ifc_definition_id: IntProperty() ifc_definition_id: IntProperty()
if TYPE_CHECKING:
name: str
obj: Union[bpy.types.Object, None]
ifc_definition_id: int
class ShapeAspect(PropertyGroup): class ShapeAspect(PropertyGroup):
name: StringProperty( name: StringProperty(
@@ -1103,8 +1103,8 @@ class LoadProjectElements(bpy.types.Operator):
bonsai.bim.handler.refresh_ui_data() bonsai.bim.handler.refresh_ui_data()
return {"FINISHED"} return {"FINISHED"}
def get_decomposition_elements(self): def get_decomposition_elements(self) -> set[ifcopenshell.entity_instance]:
containers = set() containers: set[ifcopenshell.entity_instance] = set()
for filter_category in self.props.filter_categories: for filter_category in self.props.filter_categories:
if not filter_category.is_selected: if not filter_category.is_selected:
continue continue
@@ -1116,15 +1116,15 @@ class LoadProjectElements(bpy.types.Operator):
container = None container = None
elif self.file.schema != "IFC2X3" and container.is_a("IfcContext"): elif self.file.schema != "IFC2X3" and container.is_a("IfcContext"):
container = None container = None
elements = set() elements: set[ifcopenshell.entity_instance] = set()
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) self.append_decomposed_elements(elements)
return elements return elements
def append_decomposed_elements(self, elements): def append_decomposed_elements(self, elements: set[ifcopenshell.entity_instance]) -> None:
decomposed_elements = set() decomposed_elements: set[ifcopenshell.entity_instance] = set()
for element in elements: for element in elements:
if element.IsDecomposedBy: if element.IsDecomposedBy:
for subelement in element.IsDecomposedBy[0].RelatedObjects: for subelement in element.IsDecomposedBy[0].RelatedObjects:
@@ -1133,26 +1133,26 @@ class LoadProjectElements(bpy.types.Operator):
self.append_decomposed_elements(decomposed_elements) self.append_decomposed_elements(decomposed_elements)
elements.update(decomposed_elements) elements.update(decomposed_elements)
def get_ifc_class_elements(self): def get_ifc_class_elements(self) -> set[ifcopenshell.entity_instance]:
elements = set() elements: set[ifcopenshell.entity_instance] = set()
for filter_category in self.props.filter_categories: for filter_category in self.props.filter_categories:
if not filter_category.is_selected: if not filter_category.is_selected:
continue continue
elements.update(self.file.by_type(filter_category.name, include_subtypes=False)) elements.update(self.file.by_type(filter_category.name, include_subtypes=False))
return elements return elements
def get_ifc_type_elements(self): def get_ifc_type_elements(self) -> set[ifcopenshell.entity_instance]:
elements = set() elements: set[ifcopenshell.entity_instance] = set()
for filter_category in self.props.filter_categories: for filter_category in self.props.filter_categories:
if not filter_category.is_selected: if not filter_category.is_selected:
continue continue
elements.update(ifcopenshell.util.element.get_types(self.file.by_id(filter_category.ifc_definition_id))) elements.update(ifcopenshell.util.element.get_types(self.file.by_id(filter_category.ifc_definition_id)))
return elements return elements
def get_whitelist_elements(self): def get_whitelist_elements(self) -> set[ifcopenshell.entity_instance]:
return set(ifcopenshell.util.selector.filter_elements(self.file, self.props.filter_query)) return set(ifcopenshell.util.selector.filter_elements(self.file, self.props.filter_query))
def get_blacklist_elements(self): def get_blacklist_elements(self) -> set[ifcopenshell.entity_instance]:
return set(self.file.by_type("IfcElement")) - set( return set(self.file.by_type("IfcElement")) - set(
ifcopenshell.util.selector.filter_elements(self.file, self.props.filter_query) ifcopenshell.util.selector.filter_elements(self.file, self.props.filter_query)
) )
+8 -4
View File
@@ -67,6 +67,7 @@ from typing import (
Generator, Generator,
cast, cast,
TypeGuard, TypeGuard,
Any,
) )
from typing_extensions import TypeIs from typing_extensions import TypeIs
@@ -1861,6 +1862,7 @@ class Geometry(bonsai.core.tool.Geometry):
tool.Blender.set_active_object(props.representation_obj) tool.Blender.set_active_object(props.representation_obj)
cls.sync_item_positions() cls.sync_item_positions()
representation = cls.get_active_representation(props.representation_obj) representation = cls.get_active_representation(props.representation_obj)
assert representation
ifcopenshell.api.geometry.validate_type(tool.Ifc.get(), representation) ifcopenshell.api.geometry.validate_type(tool.Ifc.get(), representation)
props.is_changing_mode = True props.is_changing_mode = True
if props.mode != "OBJECT": if props.mode != "OBJECT":
@@ -1873,6 +1875,7 @@ class Geometry(bonsai.core.tool.Geometry):
def edit_meshlike_item(cls, obj: bpy.types.Object) -> None: def edit_meshlike_item(cls, obj: bpy.types.Object) -> None:
item = tool.Geometry.get_active_representation(obj) item = tool.Geometry.get_active_representation(obj)
assert item assert item
assert isinstance(obj.data, (bpy.types.Curve, bpy.types.Mesh))
mprops = tool.Geometry.get_mesh_props(obj.data) mprops = tool.Geometry.get_mesh_props(obj.data)
if mprops.mesh_checksum == cls.get_mesh_checksum(obj.data): if mprops.mesh_checksum == cls.get_mesh_checksum(obj.data):
return return
@@ -1898,7 +1901,7 @@ class Geometry(bonsai.core.tool.Geometry):
ifcopenshell.util.element.replace_attribute(inverse, item, new_item) ifcopenshell.util.element.replace_attribute(inverse, item, new_item)
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), item) ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), item)
tool.Ifc.link(new_item, obj.data) tool.Ifc.link(new_item, obj.data)
cls.reload_representation(props.representation_obj) cls.reload_representation(rep_obj)
@classmethod @classmethod
def split_by_loose_parts(cls, obj: bpy.types.Object) -> List[bpy.types.Mesh]: def split_by_loose_parts(cls, obj: bpy.types.Object) -> List[bpy.types.Mesh]:
@@ -2130,7 +2133,6 @@ class Geometry(bonsai.core.tool.Geometry):
tool.Root.reload_grid_decorator() tool.Root.reload_grid_decorator()
return old_to_new, new_active_obj or active_object return old_to_new, new_active_obj or active_object
@classmethod @classmethod
def duplicate_ifc_item(cls, obj: bpy.types.Object) -> None: def duplicate_ifc_item(cls, obj: bpy.types.Object) -> None:
props = tool.Geometry.get_geometry_props() props = tool.Geometry.get_geometry_props()
@@ -2148,11 +2150,13 @@ class Geometry(bonsai.core.tool.Geometry):
for collection in obj.users_collection: for collection in obj.users_collection:
collection.objects.link(new_obj) collection.objects.link(new_obj)
representation = tool.Geometry.get_active_representation(props.representation_obj) assert (rep_obj := props.representation_obj)
representation = tool.Geometry.get_active_representation(rep_obj)
assert representation
representation = ifcopenshell.util.representation.resolve_representation(representation) representation = ifcopenshell.util.representation.resolve_representation(representation)
representation.Items = list(representation.Items) + [new_item] representation.Items = list(representation.Items) + [new_item]
tool.Geometry.reload_representation(props.representation_obj) tool.Geometry.reload_representation(rep_obj)
obj.select_set(False) obj.select_set(False)
tool.Root.reload_item_decorator() tool.Root.reload_item_decorator()
@@ -47,7 +47,7 @@ def validate_type(
combination, or False otherwise. combination, or False otherwise.
""" """
def is_operand(item): def is_operand(item: ifcopenshell.entity_instance) -> bool:
return ( return (
item.is_a("IfcBooleanResult") item.is_a("IfcBooleanResult")
or item.is_a("IfcCsgPrimitive3D") or item.is_a("IfcCsgPrimitive3D")