diff --git a/src/bonsai/bonsai/bim/module/model/decorator.py b/src/bonsai/bonsai/bim/module/model/decorator.py index 5f53effcd6..b059d9a6bd 100644 --- a/src/bonsai/bonsai/bim/module/model/decorator.py +++ b/src/bonsai/bonsai/bim/module/model/decorator.py @@ -319,6 +319,9 @@ class PolylineDecorator: handler = cls() cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_snap_point, (context,), "WINDOW", "POST_PIXEL")) cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_measurements, (context,), "WINDOW", "POST_PIXEL")) + cls.handlers.append( + SpaceView3D.draw_handler_add(handler.draw_measurement_axis, (context,), "WINDOW", "POST_VIEW") + ) cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_input_ui, (context,), "WINDOW", "POST_PIXEL")) cls.handlers.append( SpaceView3D.draw_handler_add(handler.draw_product_preview, (context,), "WINDOW", "POST_VIEW") @@ -350,14 +353,50 @@ class PolylineDecorator: cls.axis_start = start cls.axis_end = end - # @classmethod - # def set_axis_rectangle(cls, corners): - # cls.axis_rectangle = [*corners] - @classmethod def set_tool_state(cls, tool_state): cls.tool_state = tool_state + def calculate_measurement_x_y_and_z(self, context): + measurement_prop = context.scene.BIMPolylineProperties.polyline_point + + if len(measurement_prop) == 0 or len(measurement_prop) > 2: + return None, None + + start = measurement_prop[0] + if len(measurement_prop) == 1: + end = context.scene.BIMPolylineProperties.snap_mouse_point[0] + else: + end = measurement_prop[1] + + x_axis = (Vector((start.x, start.y, start.z)), Vector((end.x, start.y, start.z))) + y_axis = (Vector((end.x, start.y, start.z)), Vector((end.x, end.y, start.z))) + z_axis = (Vector((end.x, end.y, start.z)), Vector((end.x, end.y, end.z))) + x_middle = (x_axis[1] + x_axis[0]) / 2 + y_middle = (y_axis[1] + y_axis[0]) / 2 + z_middle = (z_axis[1] + z_axis[0]) / 2 + + return (x_axis, y_axis, z_axis), (x_middle, y_middle, z_middle) + + def calculate_polygon(self, points): + bm = bmesh.new() + + new_verts = [bm.verts.new(v) for v in points] + new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(points) - 1)] + + bm.verts.index_update() + bm.edges.index_update() + + new_faces = bmesh.ops.contextual_create(bm, geom=bm.edges) + + bm.verts.index_update() + bm.edges.index_update() + tris = [[loop.vert.index for loop in triangles] for triangles in bm.calc_loop_triangles()] + + bm.free() + + return tris + def draw_batch(self, shader_type, content_pos, color, indices=None): shader = self.line_shader if shader_type == "LINES" else self.shader batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices) @@ -424,7 +463,6 @@ class PolylineDecorator: bm.verts.index_update() bm.edges.index_update() edges = [[v.index for v in e.verts] for e in bm.edges] - print(edges) tris = [[l.vert.index for l in loop] for loop in bm.calc_loop_triangles()] all_edges.extend(edges) all_tris.extend(tris) @@ -491,6 +529,7 @@ class PolylineDecorator: def draw_measurements(self, context): region = context.region rv3d = region.data + measure_type = context.scene.MeasureToolSettings.measure_type measurement_prop = context.scene.BIMPolylineProperties.polyline_point self.addon_prefs = tool.Blender.get_addon_preferences() @@ -526,8 +565,65 @@ class PolylineDecorator: text_dim = blf.dimensions(self.font_id, text) self.draw_text_background(context, coords_angle, text_dim) blf.draw(self.font_id, text) + + if measure_type == "SINGLE": + axis, axis_middle = self.calculate_measurement_x_y_and_z(context) + for i, measurement in enumerate(axis_middle): + coords_measurement = view3d_utils.location_3d_to_region_2d(region, rv3d, measurement) + blf.position(self.font_id, coords_measurement[0], coords_measurement[1], 0) + value = round((axis[i][1] - axis[i][0]).length, 4) + direction = axis[i][1] - axis[i][0] + if (i == 0 and direction.x < 0) or (i == 1 and direction.y < 0) or (i == 2 and direction.z < 0): + value = -value + prefix = "xyz"[i] + text = f"{prefix}: {str(value)}" + text_dim = blf.dimensions(self.font_id, text) + self.draw_text_background(context, coords_measurement, text_dim) + blf.draw(self.font_id, text) + blf.disable(self.font_id, blf.SHADOW) + def draw_measurement_axis(self, context): + self.addon_prefs = tool.Blender.get_addon_preferences() + decorator_color = self.addon_prefs.decorations_colour + decorator_color_special = self.addon_prefs.decorator_color_special + decorator_color_selected = self.addon_prefs.decorator_color_selected + decorator_color_error = self.addon_prefs.decorator_color_error + decorator_color_unselected = self.addon_prefs.decorator_color_unselected + decorator_color_background = self.addon_prefs.decorator_color_background + theme = context.preferences.themes.items()[0][1] + decorator_color_object_active = (*theme.view_3d.object_active, 1) # unwrap color values and adds alpha=1 + decorator_color_x_axis = (*theme.user_interface.axis_x, 1) + decorator_color_y_axis = (*theme.user_interface.axis_y, 1) + decorator_color_z_axis = (*theme.user_interface.axis_z, 1) + + 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)) + + # general shader + self.shader = gpu.shader.from_builtin("UNIFORM_COLOR") + gpu.state.point_size_set(6) + + self.font_id = 1 + font_size = tool.Blender.scale_font_size(12) + blf.size(self.font_id, font_size) + blf.enable(self.font_id, blf.SHADOW) + blf.shadow(self.font_id, 6, 0, 0, 0, 1) + color = self.addon_prefs.decorations_colour + + blf.color(self.font_id, *color) + + measure_type = context.scene.MeasureToolSettings.measure_type + if measure_type == "SINGLE": + axis, _ = self.calculate_measurement_x_y_and_z(context) + x_axis, y_axis, z_axis = axis + self.draw_batch("LINES", [*x_axis], decorator_color_x_axis, [(0, 1)]) + self.draw_batch("LINES", [*y_axis], decorator_color_y_axis, [(0, 1)]) + self.draw_batch("LINES", [*z_axis], decorator_color_z_axis, [(0, 1)]) + def draw_snap_point(self, context): self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR") self.line_shader.bind() # required to be able to change uniforms of the shader @@ -648,18 +744,15 @@ class PolylineDecorator: self.line_shader.uniform_float("lineWidth", 0.75) self.draw_batch("LINES", [self.axis_start, self.axis_end], axis_color, [(0, 1)]) - # try: - # self.draw_batch("TRIS", self.axis_rectangle, (1, 1, 1, 0.1), [(0, 1, 3), (0, 2, 3)]) - # except: - # pass - # Area highlight - # if "AREA" in list(self.input_panel.keys()): # TODO Change to input_ui - # if self.input_panel["AREA"] and float(self.input_panel["AREA"]) > 0: # TODO Change to input_ui - # edges = [] - # for i in range(1, len(polyline_points) - 1): - # edges.append((0, i, i + 1)) - # self.draw_batch("TRIS", polyline_points, (0, 1, 0, 0.1), edges) + try: + has_area = self.input_ui.init_area + except: + has_area = False + if has_area: + if self.input_ui.get_number_value("AREA") > 0: + tris = self.calculate_polygon(polyline_points) + self.draw_batch("TRIS", polyline_points, transparent_color(decorator_color_special), tris) # Mouse points if snap_prop.snap_type in ["Plane", "Axis", "Mix"]: diff --git a/src/bonsai/bonsai/bim/module/project/__init__.py b/src/bonsai/bonsai/bim/module/project/__init__.py index bb15b2513d..5c7e27d7cd 100644 --- a/src/bonsai/bonsai/bim/module/project/__init__.py +++ b/src/bonsai/bonsai/bim/module/project/__init__.py @@ -65,6 +65,7 @@ classes = ( prop.FilterCategory, prop.Link, prop.BIMProjectProperties, + prop.MeasureToolSettings, ui.BIM_MT_new_project, ui.BIM_MT_project, ui.BIM_PT_new_project_wizard, @@ -92,6 +93,7 @@ def register(): if not bpy.app.background: bpy.utils.register_tool(workspace.ExploreTool, after={"builtin.transform"}, separator=True, group=False) bpy.types.Scene.BIMProjectProperties = bpy.props.PointerProperty(type=prop.BIMProjectProperties) + bpy.types.Scene.MeasureToolSettings = bpy.props.PointerProperty(type=prop.MeasureToolSettings) bpy.app.handlers.load_post.append(decorator.toggle_decorations_on_load) bpy.types.TOPBAR_MT_file_import.append(ui.file_import_menu) bpy.types.TOPBAR_MT_file.prepend(ui.file_menu) @@ -115,6 +117,7 @@ def unregister(): if not bpy.app.background: bpy.utils.unregister_tool(workspace.ExploreTool) del bpy.types.Scene.BIMProjectProperties + del bpy.types.Scene.MeasureToolSettings bpy.app.handlers.load_post.remove(decorator.toggle_decorations_on_load) bpy.types.TOPBAR_MT_file.remove(ui.file_menu) bpy.types.TOPBAR_MT_file_context_menu.remove(ui.file_menu) diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 7ce93f02e8..570fe0bd69 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -2372,13 +2372,18 @@ class MeasureTool(bpy.types.Operator, PolylineOperator): bl_label = "Measure Tool" bl_options = {"REGISTER", "UNDO"} + measure_type: bpy.props.StringProperty() + @classmethod def poll(cls, context): return context.space_data.type == "VIEW_3D" def __init__(self): super().__init__() - self.input_ui = tool.Polyline.create_input_ui(init_z=True) + if self.measure_type == 'AREA': + self.input_ui = tool.Polyline.create_input_ui(init_z=True, init_area=True) + else: + self.input_ui = tool.Polyline.create_input_ui(init_z=True) self.input_options = ["D", "A", "X", "Y", "Z"] self.instructions = """TAB: Cycle Input D: Distance Input @@ -2405,11 +2410,15 @@ class MeasureTool(bpy.types.Operator, PolylineOperator): self.handle_snap_selection(context, event) + single_mode = False + if self.measure_type == "SINGLE" and len(context.scene.BIMPolylineProperties.polyline_point) >= 2: + single_mode = True + if ( not self.tool_state.is_input_on and event.value == "RELEASE" and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"} - ): + ) or single_mode: context.workspace.status_text_set(text=None) PolylineDecorator.uninstall() tool.Snap.move_polyline_to_measure() @@ -2421,6 +2430,7 @@ class MeasureTool(bpy.types.Operator, PolylineOperator): self.handle_keyboard_input(context, event) self.handle_inserting_polyline(context, event) + tool.Polyline.calculate_area(context, self.input_ui) if event.type == "E": context.scene.BIMPolylineProperties.measure_polyline.clear() diff --git a/src/bonsai/bonsai/bim/module/project/prop.py b/src/bonsai/bonsai/bim/module/project/prop.py index 7b1a7c8fac..23f7258ed6 100644 --- a/src/bonsai/bonsai/bim/module/project/prop.py +++ b/src/bonsai/bonsai/bim/module/project/prop.py @@ -229,3 +229,13 @@ class BIMProjectProperties(PropertyGroup): def get_library_element_index(self, lib_element): return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element)) + + +class MeasureToolSettings(PropertyGroup): + measure_type_items = [ + ("SINGLE", "SINGLE", "Single", "FIXED_SIZE",1), + ("POLYLINE", "POLYLINE", "Polyline", "DRIVER_ROTATIONAL_DIFFERENCE", 2), + ("AREA", "AREA", "Area", "OUTLINER_DATA_LIGHTPROBE", 3), + ] + + measure_type: bpy.props.EnumProperty(items=measure_type_items, default="POLYLINE") diff --git a/src/bonsai/bonsai/bim/module/project/workspace.py b/src/bonsai/bonsai/bim/module/project/workspace.py index 7ebbd3b164..6cfe2f86fc 100644 --- a/src/bonsai/bonsai/bim/module/project/workspace.py +++ b/src/bonsai/bonsai/bim/module/project/workspace.py @@ -58,9 +58,16 @@ class ExploreTool(bpy.types.WorkSpaceTool): row = layout.row(align=True) row.label(text="", icon="EVENT_ALT") row.label(text="Disable Culling" if LinksData.enable_culling else "Enable Culling", icon="EVENT_C") + + prop = context.scene.MeasureToolSettings row = layout.row(align=True) row.label(text="", icon="EVENT_SHIFT") - row.label(text="Measure Tool", icon="EVENT_M") + row.label(text="", icon="EVENT_M") + row = layout.row(align=True) + op = row.operator("bim.explore_hotkey", text="Measure Tool", icon="CON_DISTLIMIT") + op.hotkey = "S_M" + row = layout.row(align=True) + row.prop(prop, "measure_type", text="Measure Type", expand=True, icon_only=True, emboss=True) class ExploreHotkey(bpy.types.Operator): @@ -94,4 +101,7 @@ class ExploreHotkey(bpy.types.Operator): bpy.ops.bim.enable_culling("INVOKE_DEFAULT") def hotkey_S_M(self): - bpy.ops.bim.measure_tool("INVOKE_DEFAULT") + for obj in tool.Blender.get_selected_objects(): + obj.select_set(False) + measure_type = bpy.context.scene.MeasureToolSettings.measure_type + bpy.ops.bim.measure_tool("INVOKE_DEFAULT", measure_type=measure_type) diff --git a/src/bonsai/bonsai/tool/polyline.py b/src/bonsai/bonsai/tool/polyline.py index 38d6651f8b..532d795d1b 100644 --- a/src/bonsai/bonsai/tool/polyline.py +++ b/src/bonsai/bonsai/tool/polyline.py @@ -45,7 +45,7 @@ class Polyline(bonsai.core.tool.Polyline): if self.init_z: self._Z = "" if self.init_area: - self._AREA = "" + self._AREA = "0" def set_value(self, attribute_name, value): value = str(value) @@ -222,8 +222,8 @@ class Polyline(bonsai.core.tool.Polyline): else: area = 0 - if input_ui.get_text_value("A") is not None: - input_ui.set_value("A", area) + if input_ui.get_text_value("AREA") is not None: + input_ui.set_value("AREA", area) return @classmethod