diff --git a/src/bonsai/bonsai/bim/module/drawing/__init__.py b/src/bonsai/bonsai/bim/module/drawing/__init__.py index 7ca0e4bd93..187020fc77 100644 --- a/src/bonsai/bonsai/bim/module/drawing/__init__.py +++ b/src/bonsai/bonsai/bim/module/drawing/__init__.py @@ -61,6 +61,7 @@ classes = ( operator.EnableEditingAssignedProduct, operator.EnableEditingElementFilter, operator.EnableEditingText, + operator.ExcludeAnnotation, operator.ExpandSheet, operator.LoadDrawings, operator.LoadReferences, diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index 4bc3067de8..6057741168 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -3757,3 +3757,19 @@ class OpenDocumentationWebUi(bpy.types.Operator): else: bpy.ops.bim.open_web_browser(page="documentation") return {"FINISHED"} + + +class ExcludeAnnotation(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.exclude_annotation" + bl_label = "Exclude Annotation" + bl_description = "Excludes the automatic annotation reference from the drawing" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + if not (obj := bpy.context.scene.camera) or not (drawing := tool.Ifc.get_entity(obj)): + return + for obj in tool.Blender.get_selected_objects(include_active=False): + if (element := tool.Ifc.get_entity(obj)) and tool.Drawing.is_auto_annotation(element): + if (referenced_element := tool.Drawing.get_annotation_element(element)): + tool.Drawing.exclude_annotation_from_drawing(referenced_element, drawing) + core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing) diff --git a/src/bonsai/bonsai/bim/module/drawing/ui.py b/src/bonsai/bonsai/bim/module/drawing/ui.py index 45e7acec97..82ddfef55a 100644 --- a/src/bonsai/bonsai/bim/module/drawing/ui.py +++ b/src/bonsai/bonsai/bim/module/drawing/ui.py @@ -194,6 +194,7 @@ class BIM_PT_element_filters(Panel): text = "Exclude Filter" if ElementFiltersData.data["has_exclude_filter"] else "No Exclude Filter Found" icon = "GREASEPENCIL" if ElementFiltersData.data["has_exclude_filter"] else "ADD" row.label(text=text, icon="FILTER") + row.operator("bim.exclude_annotation", icon="REMOVE", text="") row.operator("bim.enable_editing_element_filter", icon=icon, text="").filter_mode = "EXCLUDE" diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 578dc8b4e9..248fc9c948 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -787,6 +787,7 @@ def calc_delete_is_batch(ifc_file: ifcopenshell.file, context: bpy.types.Context is_batch = total_elements > 500000 and total_polygons > 2000 return is_batch + class OverrideDelete(bpy.types.Operator): bl_idname = "bim.override_object_delete" bl_label = "IFC Delete" @@ -857,10 +858,10 @@ class OverrideDelete(bpy.types.Operator): objects_to_remove = context.selected_objects self.process_arrays(context) - + # Track aggregates before deleting their parts aggregates_to_check = self.track_aggregates(objects_to_remove) - + clear_active_object = True for i, obj in enumerate(objects_to_remove, 1): @@ -895,6 +896,9 @@ class OverrideDelete(bpy.types.Operator): {"INFO"}, "The last grid axis of a grid cannot be deleted. Delete the grid instead." ) continue + if tool.Drawing.is_auto_annotation(element): + self.report({"INFO"}, "References cannot be deleted. Exclude the referenced element instead.") + continue tool.Geometry.delete_ifc_object(obj) elif tool.Geometry.is_representation_item(obj): tool.Geometry.delete_ifc_item(obj) @@ -959,23 +963,25 @@ class OverrideDelete(bpy.types.Operator): aggregate.id() except: continue - + related_objects = ifcopenshell.util.element.get_parts(aggregate) if len(related_objects) == 0: aggregate_name = aggregate.Name or f"{aggregate.is_a()} #{aggregate.id()}" deleted_aggregates.append(aggregate_name) - + aggregate_obj = tool.Ifc.get_object(aggregate) if aggregate_obj and tool.Blender.is_valid_data_block(aggregate_obj): tool.Geometry.delete_ifc_object(aggregate_obj) - + # Show info message if aggregates were deleted if deleted_aggregates: if len(deleted_aggregates) == 1: - self.report({'INFO'}, f"Aggregate '{deleted_aggregates[0]}' was deleted because it had no remaining parts") + self.report( + {"INFO"}, f"Aggregate '{deleted_aggregates[0]}' was deleted because it had no remaining parts" + ) else: aggregate_list = ", ".join(f"'{name}'" for name in deleted_aggregates) - self.report({'INFO'}, f"Aggregates {aggregate_list} were deleted because they had no remaining parts") + self.report({"INFO"}, f"Aggregates {aggregate_list} were deleted because they had no remaining parts") def process_arrays(self, context: bpy.types.Context) -> None: ifc_file = tool.Ifc.get() @@ -1002,6 +1008,7 @@ class OverrideDelete(bpy.types.Operator): else: break # allows to remove only n last layers of an array + class SelectedIdsData(NamedTuple): objects: set[bpy.types.Object] collections: set[bpy.types.Collection] diff --git a/src/bonsai/bonsai/core/drawing.py b/src/bonsai/bonsai/core/drawing.py index d69147b13e..c10c88b461 100644 --- a/src/bonsai/bonsai/core/drawing.py +++ b/src/bonsai/bonsai/core/drawing.py @@ -476,7 +476,17 @@ def sync_references( return group = drawing_tool.get_drawing_group(drawing) - for reference_element in drawing_tool.get_potential_reference_elements(drawing): + potential_reference_elements = drawing_tool.get_potential_reference_elements(drawing) + for element in drawing_tool.get_group_elements(group): + if ( + drawing_tool.is_auto_annotation(element) + and drawing_tool.get_assigned_product(element) not in potential_reference_elements + ): + if obj := ifc.get_object(element): + drawing_tool.delete_object(obj) + ifc.run("root.remove_product", product=element) + + for reference_element in potential_reference_elements: reference_obj = ifc.get_object(reference_element) annotation = drawing_tool.get_drawing_reference_annotation(drawing, reference_element) diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index 86712d19c6..9acdc617a2 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -1488,13 +1488,19 @@ class Drawing(bonsai.core.tool.Drawing): for axis in element.UAxes + element.VAxes + (element.WAxes or tuple()): if axis in existing_references: continue - elements.append(element) + elements.append(axis) target_view = tool.Drawing.get_drawing_target_view(drawing) if target_view in ("SECTION_VIEW", "ELEVATION_VIEW"): for element in tool.Ifc.get().by_type("IfcBuildingStorey"): + if element in existing_references: + continue elements.append(element) return elements + @classmethod + def is_auto_annotation(cls, element: ifcopenshell.entity_instance): + return element.is_a("IfcAnnotation") and element.ObjectType in ("GRID", "SECTION", "ELEVATION", "SECTION_LEVEL") + @classmethod def get_drawing_reference_annotation( cls, drawing: ifcopenshell.entity_instance, reference_element: ifcopenshell.entity_instance @@ -2649,5 +2655,4 @@ class Drawing(bonsai.core.tool.Drawing): def hide_all_drawing_collections(cls) -> None: for element in tool.Ifc.get().by_type("IfcAnnotation"): if element.ObjectType == "DRAWING" and (obj := tool.Ifc.get_object(element)): - print('found element', element) tool.Blender.get_layer_collection(obj.users_collection[0]).hide_viewport = True diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index c31d1e0fbb..6df540ef13 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -236,8 +236,8 @@ class Geometry(bonsai.core.tool.Geometry): elif element.is_a("IfcAnnotation"): if element.ObjectType == "DRAWING": return bonsai.core.drawing.remove_drawing(tool.Ifc, tool.Drawing, drawing=element) - elif (referenced_element := tool.Drawing.get_annotation_element(element)) and (drawing := tool.Drawing.get_annotation_drawing(element)): - tool.Drawing.exclude_annotation_from_drawing(referenced_element, drawing) + elif tool.Drawing.is_auto_annotation(element): + return # For now, these are special referenced objects and cannot be deleted. Exclude instead. elif element.is_a("IfcRelSpaceBoundary"): ifcopenshell.api.boundary.remove_boundary(ifc_file, boundary=element) tool.Boundary.undecorate_boundary(obj)