diff --git a/src/bonsai/bonsai/bim/module/geometry/decorator.py b/src/bonsai/bonsai/bim/module/geometry/decorator.py index 866a53ec92..d9e8505437 100644 --- a/src/bonsai/bonsai/bim/module/geometry/decorator.py +++ b/src/bonsai/bonsai/bim/module/geometry/decorator.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with Bonsai. If not, see . +import bpy import blf import gpu import bmesh @@ -34,11 +35,51 @@ class ItemDecorator: def install(cls, context): if cls.is_installed: cls.uninstall() + obj_is_selected = {} + objs = {} + for item_obj in context.scene.BIMGeometryProperties.item_objs: + if obj := item_obj.obj: + objs[obj.name] = cls.get_obj_data(obj) + obj_is_selected[obj.name] = obj.select_get() + handler = cls() + handler.objs = objs + handler.obj_is_selected = obj_is_selected cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_text, (context,), "WINDOW", "POST_PIXEL")) cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw, (context,), "WINDOW", "POST_VIEW")) cls.is_installed = True + @classmethod + def get_obj_data(cls, obj): + verts = [] + edges = [] + tris = [] + + if len(obj.data.loop_triangles) > 0: + verts = [tuple(obj.matrix_world @ v.co) for v in obj.data.vertices] + tris = [tuple(t.vertices) for t in obj.data.loop_triangles] + + i = len(verts) + matrix_world = obj.matrix_world + bbox_verts = [matrix_world @ Vector(co) for co in obj.bound_box] + bbox_edges = [ + (0 + i, 3 + i), + (3 + i, 7 + i), + (7 + i, 4 + i), + (4 + i, 0 + i), + (0 + i, 1 + i), + (3 + i, 2 + i), + (7 + i, 6 + i), + (4 + i, 5 + i), + (1 + i, 2 + i), + (2 + i, 6 + i), + (6 + i, 5 + i), + (5 + i, 1 + i), + ] + edges = bbox_edges + verts.extend(bbox_verts) + return {"verts": verts, "edges": edges, "tris": tris} + @classmethod def uninstall(cls): for handler in cls.handlers: @@ -80,7 +121,7 @@ class ItemDecorator: blf.disable(font_id, blf.SHADOW) def draw(self, context): - def transparent_color(color, alpha=0.3): + def transparent_color(color, alpha=0.05): color = [i for i in color] color[3] = alpha return color @@ -89,72 +130,30 @@ class ItemDecorator: selected_elements_color = self.addon_prefs.decorator_color_selected unselected_elements_color = self.addon_prefs.decorator_color_unselected special_elements_color = self.addon_prefs.decorator_color_special + decorator_color_background = self.addon_prefs.decorator_color_background - gpu.state.point_size_set(6) gpu.state.blend_set("ALPHA") self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR") self.line_shader.bind() # required to be able to change uniforms of the shader # POLYLINE_UNIFORM_COLOR specific uniforms self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height)) - self.line_shader.uniform_float("lineWidth", 2.0) + self.line_shader.uniform_float("lineWidth", 1.0) # general shader self.shader = gpu.shader.from_builtin("UNIFORM_COLOR") - selected_verts = [] - selected_edges = [] - selected_tris = [] - unselected_verts = [] - unselected_edges = [] - unselected_tris = [] - for item in context.scene.BIMGeometryProperties.item_objs: - if (obj := item.obj) and obj.hide_get() == False: + for obj_name, data in self.objs.items(): + if (obj := bpy.data.objects.get(obj_name)) and obj.hide_get() == False: + if self.obj_is_selected[obj_name] != obj.select_get(): + self.obj_is_selected[obj_name] = obj.select_get() + data = ItemDecorator.get_obj_data(obj) + self.objs[obj_name] = data if obj.select_get(): if context.mode != "OBJECT": continue - edges = selected_edges - verts = selected_verts - tris = selected_tris + self.draw_batch("LINES", data["verts"], selected_elements_color, data["edges"]) + self.draw_batch("TRIS", data["verts"], transparent_color(selected_elements_color), data["tris"]) else: - edges = unselected_edges - verts = unselected_verts - tris = unselected_tris - - i = len(verts) - matrix_world = obj.matrix_world - bbox_verts = [matrix_world @ Vector(co) for co in obj.bound_box] - bbox_edges = [ - (0 + i, 3 + i), - (3 + i, 7 + i), - (7 + i, 4 + i), - (4 + i, 0 + i), - (0 + i, 1 + i), - (3 + i, 2 + i), - (7 + i, 6 + i), - (4 + i, 5 + i), - (1 + i, 2 + i), - (2 + i, 6 + i), - (6 + i, 5 + i), - (5 + i, 1 + i), - ] - edges.extend(bbox_edges) - verts.extend(bbox_verts) - - if (total_tris := len(obj.data.loop_triangles)) == 0: - continue - - if total_tris > 1000: # For performance - continue - - offset = len(verts) - verts.extend([tuple(obj.matrix_world @ v.co) for v in obj.data.vertices]) - tris.extend([tuple([i + offset for i in t.vertices]) for t in obj.data.loop_triangles]) - - if unselected_verts: - self.draw_batch("LINES", unselected_verts, transparent_color(unselected_elements_color), unselected_edges) - self.draw_batch("TRIS", unselected_verts, transparent_color(special_elements_color), unselected_tris) - - if selected_verts: - self.draw_batch("LINES", selected_verts, selected_elements_color, selected_edges) - self.draw_batch("TRIS", selected_verts, transparent_color(selected_elements_color), selected_tris) + self.draw_batch("LINES", data["verts"], decorator_color_background, data["edges"]) + self.draw_batch("TRIS", data["verts"], transparent_color(special_elements_color), data["tris"]) diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 17cf72cedd..79fa98ebd7 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -1745,6 +1745,7 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator): return bpy.ops.bim.edit_boundary_geometry() elif tool.Geometry.is_representation_item(context.active_object): self.edit_representation_item(context.active_object) + tool.Root.reload_item_decorator() return {"FINISHED"} objs = context.selected_objects or [context.active_object] @@ -2358,6 +2359,8 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator): if tool.Geometry.is_meshlike_item(item): tool.Geometry.lock_object(item_obj) + tool.Root.reload_item_decorator() + class UpdateItemAttributes(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.update_item_attributes" diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index aae95903ad..621d7e0d91 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -131,7 +131,6 @@ class CreateProject(bpy.types.Operator): ) bonsai.bim.schema.reload(tool.Ifc.get().schema_identifier) tool.Blender.register_toolbar() - tool.Root.reload_item_decorator() def rollback(self, data): IfcStore.file = None @@ -843,7 +842,6 @@ class LoadProjectElements(bpy.types.Operator): tool.Project.set_default_context() tool.Project.set_default_modeling_dimensions() tool.Root.reload_grid_decorator() - tool.Root.reload_item_decorator() return {"FINISHED"} def get_decomposition_elements(self): diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index d319435335..cd17d025a5 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -1434,7 +1434,8 @@ class Geometry(bonsai.core.tool.Geometry): has_changed = False for item_obj in props.item_objs: - obj = item_obj.obj + if not (obj := item_obj.obj): + continue item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) if item.is_a("IfcSweptAreaSolid"): if not tool.Ifc.is_moved(obj): @@ -1455,6 +1456,7 @@ class Geometry(bonsai.core.tool.Geometry): ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position) if has_changed: cls.reload_representation(rep_obj) + tool.Root.reload_item_decorator() @classmethod def import_item_attributes(cls, obj: bpy.types.Object) -> None: diff --git a/src/bonsai/bonsai/tool/root.py b/src/bonsai/bonsai/tool/root.py index 0522ab6ea0..e5e689a0da 100644 --- a/src/bonsai/bonsai/tool/root.py +++ b/src/bonsai/bonsai/tool/root.py @@ -217,8 +217,7 @@ class Root(bonsai.core.tool.Root): @classmethod def reload_item_decorator(cls) -> None: - item_objs = bpy.context.scene.BIMGeometryProperties.item_objs - item_objs.clear() + bpy.context.view_layer.update() ItemDecorator.install(bpy.context) @classmethod