From 93457051daf8c204850d9a954d1af1fc9fd9a6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Sat, 5 Oct 2024 14:51:52 -0300 Subject: [PATCH] Measure tool - show text decorator for polyline area and length. --- .../bonsai/bim/module/model/decorator.py | 58 +++++++++++--- .../bonsai/bim/module/project/decorator.py | 80 +++++++++++++------ .../bonsai/bim/module/project/operator.py | 12 +++ src/bonsai/bonsai/bim/module/project/prop.py | 4 +- .../bonsai/bim/module/project/workspace.py | 4 +- src/bonsai/bonsai/tool/polyline.py | 47 +++++------ 6 files changed, 140 insertions(+), 65 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/decorator.py b/src/bonsai/bonsai/bim/module/model/decorator.py index bee578af3e..1cccff49c9 100644 --- a/src/bonsai/bonsai/bim/module/model/decorator.py +++ b/src/bonsai/bonsai/bim/module/model/decorator.py @@ -524,9 +524,13 @@ class PolylineDecorator: def draw_measurements(self, context): region = context.region rv3d = region.data - measure_type = context.scene.MeasureToolSettings.measure_type + measure_type = context.scene.MeasureToolSettings.measurement_type polyline_data = context.scene.BIMPolylineProperties.insertion_polyline - polyline_points = polyline_data[0].polyline_points if polyline_data else [] + if not polyline_data: + return + else: + polyline_data = context.scene.BIMPolylineProperties.insertion_polyline[0] + polyline_points = polyline_data.polyline_points self.addon_prefs = tool.Blender.get_addon_preferences() self.font_id = 1 @@ -554,7 +558,7 @@ class PolylineDecorator: if i == 1: continue - angle_text_pos = polyline_points[i - 1].position + angle_text_pos = Vector(polyline_points[i - 1].position) angle_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, angle_text_pos) blf.position(self.font_id, angle_text_coords[0], angle_text_coords[1], 0) text = "a: " + polyline_points[i].angle @@ -572,12 +576,40 @@ class PolylineDecorator: 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] - formatted_value = tool.Polyline.format_input_ui_units(context, value) + formatted_value = tool.Polyline.format_input_ui_units(value) text = f"{prefix}: {formatted_value}" text_length = blf.dimensions(self.font_id, text) self.draw_text_background(context, dim_text_coords, text_length) blf.draw(self.font_id, text) + # Area and Length text + polyline_verts = [Vector((p.x, p.y, p.z)) for p in polyline_points] + center = sum(polyline_verts, Vector()) / len(polyline_verts) # center between all polyline points + area_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, center) + total_length_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, polyline_verts[-1]) + + # Area + if measure_type == "AREA": + if len(polyline_verts) < 3: + return + blf.position(self.font_id, area_text_coords[0], area_text_coords[1], 0) + value = polyline_data.area + text = f"area: {value}" + text_length = blf.dimensions(self.font_id, text) + self.draw_text_background(context, area_text_coords, text_length) + blf.draw(self.font_id, text) + + # Length + if measure_type in {"POLYLINE","AREA"}: + if len(polyline_verts) < 2: + return + blf.position(self.font_id, total_length_text_coords[0], total_length_text_coords[1], 0) + value = polyline_data.total_length + text = f"length: {value}" + text_length = blf.dimensions(self.font_id, text) + self.draw_text_background(context, total_length_text_coords, text_length) + blf.draw(self.font_id, text) + blf.disable(self.font_id, blf.SHADOW) @@ -673,7 +705,11 @@ class PolylineDecorator: # Create polyline with selected points polyline_data = context.scene.BIMPolylineProperties.insertion_polyline - polyline_points = polyline_data[0].polyline_points if polyline_data else [] + if not polyline_data: + return + else: + polyline_data = context.scene.BIMPolylineProperties.insertion_polyline[0] + polyline_points = polyline_data.polyline_points polyline_verts = [] polyline_edges = [] for point_prop in polyline_points: @@ -703,8 +739,7 @@ class PolylineDecorator: self.draw_batch("LINES", [self.axis_start, self.axis_end], axis_color, [(0, 1)]) # Lines for X, Y, Z of single measure - measure_type = context.scene.MeasureToolSettings.measure_type - if measure_type == "SINGLE": + if polyline_data.measurement_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)]) @@ -712,12 +747,9 @@ class PolylineDecorator: self.draw_batch("LINES", [*z_axis], decorator_color_z_axis, [(0, 1)]) # Area highlight - try: - has_area = self.input_ui.init_area - except: - has_area = False - if has_area: - if self.input_ui.get_number_value("AREA") > 0: + area = polyline_data.area + if area: + if float(area) > 0: tris = self.calculate_polygon(polyline_verts) self.draw_batch("TRIS", polyline_verts, transparent_color(decorator_color_special), tris) diff --git a/src/bonsai/bonsai/bim/module/project/decorator.py b/src/bonsai/bonsai/bim/module/project/decorator.py index 674d859978..6bd6d55bbc 100644 --- a/src/bonsai/bonsai/bim/module/project/decorator.py +++ b/src/bonsai/bonsai/bim/module/project/decorator.py @@ -274,6 +274,7 @@ class MeasureDecorator: bm.edges.index_update() new_faces = bmesh.ops.contextual_create(bm, geom=bm.edges) + center = bm.faces[0] bm.verts.index_update() bm.edges.index_update() @@ -281,7 +282,7 @@ class MeasureDecorator: bm.free() - return tris + return tris, center def draw_text_background(self, context, coords_dim, text_dim): padding = 5 @@ -312,17 +313,18 @@ class MeasureDecorator: blf.color(self.font_id, *color) measure_data = context.scene.BIMPolylineProperties.measurement_polyline - for data in measure_data: - measurement_prop = data.polyline_points - measure_type = data.measurement_type + for polyline_data in measure_data: + polyline_points = polyline_data.polyline_points + measure_type = polyline_data.measurement_type - for i in range(len(measurement_prop)): + all_positions = [] + for i in range(len(polyline_points)): if i == 0: continue - dim_text_pos = (Vector(measurement_prop[i].position) + Vector(measurement_prop[i - 1].position)) / 2 + dim_text_pos = (Vector(polyline_points[i].position) + Vector(polyline_points[i - 1].position)) / 2 dim_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, dim_text_pos) - formatted_value = measurement_prop[i].dim + formatted_value = polyline_points[i].dim blf.position(self.font_id, dim_text_coords[0], dim_text_coords[1], 0) text = "d: " + formatted_value @@ -332,16 +334,16 @@ class MeasureDecorator: if i == 1: continue - angle_text_pos = measurement_prop[i - 1].position + angle_text_pos = Vector(polyline_points[i - 1].position) angle_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, angle_text_pos) blf.position(self.font_id, angle_text_coords[0], angle_text_coords[1], 0) - text = "a: " + measurement_prop[i].angle + text = "a: " + polyline_points[i].angle text_length = blf.dimensions(self.font_id, text) self.draw_text_background(context, angle_text_coords, text_length) blf.draw(self.font_id, text) if measure_type == "SINGLE": - axis_line, axis_line_center = self.calculate_measurement_x_y_and_z(context, measurement_prop) + axis_line, axis_line_center = self.calculate_measurement_x_y_and_z(context, polyline_points) for i, dim_text_pos in enumerate(axis_line_center): dim_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, dim_text_pos) blf.position(self.font_id, dim_text_coords[0], dim_text_coords[1], 0) @@ -350,12 +352,40 @@ class MeasureDecorator: 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] - formatted_value = tool.Polyline.format_input_ui_units(context, value) + formatted_value = tool.Polyline.format_input_ui_units(value) text = f"{prefix}: {formatted_value}" text_length = blf.dimensions(self.font_id, text) self.draw_text_background(context, dim_text_coords, text_length) blf.draw(self.font_id, text) + # Area and Length text + polyline_verts = [Vector((p.x, p.y, p.z)) for p in polyline_points] + center = sum(polyline_verts, Vector()) / len(polyline_verts) # center between all polyline points + area_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, center) + total_length_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, polyline_verts[-1]) + + # Area + if measure_type == "AREA": + if len(polyline_verts) < 3: + return + blf.position(self.font_id, area_text_coords[0], area_text_coords[1], 0) + value = polyline_data.area + text = f"area: {value}" + text_length = blf.dimensions(self.font_id, text) + self.draw_text_background(context, area_text_coords, text_length) + blf.draw(self.font_id, text) + + # Length + if measure_type in {"POLYLINE","AREA"}: + if len(polyline_verts) < 2: + return + blf.position(self.font_id, total_length_text_coords[0], total_length_text_coords[1], 0) + value = polyline_data.total_length + text = f"length: {value}" + text_length = blf.dimensions(self.font_id, text) + self.draw_text_background(context, total_length_text_coords, text_length) + blf.draw(self.font_id, text) + blf.disable(self.font_id, blf.SHADOW) @@ -385,36 +415,36 @@ class MeasureDecorator: gpu.state.point_size_set(6) measure_data = context.scene.BIMPolylineProperties.measurement_polyline - for data in measure_data: - polyline_data = data.polyline_points + for polyline_data in measure_data: + polyline_points = polyline_data.polyline_points - polyline_points = [] + polyline_verts = [] polyline_edges = [] - for point_prop in polyline_data: + for point_prop in polyline_points: point = Vector((point_prop.x, point_prop.y, point_prop.z)) - polyline_points.append(point) + polyline_verts.append(point) - for i in range(len(polyline_points) - 1): + for i in range(len(polyline_verts) - 1): polyline_edges.append([i, i + 1]) # Lines for X, Y, Z of single measure - measure_type = data.measurement_type + measure_type = polyline_data.measurement_type if measure_type == "SINGLE": - axis, _ = self.calculate_measurement_x_y_and_z(context, polyline_data) + axis, _ = self.calculate_measurement_x_y_and_z(context, polyline_points) 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)]) # Area highlight - area = data.area + area = polyline_data.area if area: if float(area) > 0: - tris = self.calculate_polygon(polyline_points) - self.draw_batch("TRIS", polyline_points, transparent_color(decorator_color_special), tris) + tris, _ = self.calculate_polygon(polyline_verts) + self.draw_batch("TRIS", polyline_verts, transparent_color(decorator_color_special), tris) # Draw polyline with selected points self.line_shader.uniform_float("lineWidth", 2.0) - self.draw_batch("POINTS", polyline_points, decorator_color_special) - if len(polyline_points) > 1: - self.draw_batch("LINES", polyline_points, decorator_color_special, polyline_edges) + self.draw_batch("POINTS", polyline_verts, decorator_color_special) + if len(polyline_verts) > 1: + self.draw_batch("LINES", polyline_verts, decorator_color_special, polyline_edges) diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index db344a86fa..9acf5c9214 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -2435,6 +2435,18 @@ class MeasureTool(bpy.types.Operator, PolylineOperator): self.handle_keyboard_input(context, event) self.handle_inserting_polyline(context, event) + + # Add measurement type to the insertion polyline + polyline_data = context.scene.BIMPolylineProperties.insertion_polyline + if not polyline_data: + pass + else: + polyline_data = context.scene.BIMPolylineProperties.insertion_polyline[0] + measurement_type = bpy.context.scene.MeasureToolSettings.measurement_type + if not polyline_data.measurement_type: + polyline_data.measurement_type = measurement_type + + tool.Polyline.calculate_area(context, self.input_ui) if event.type == "E": diff --git a/src/bonsai/bonsai/bim/module/project/prop.py b/src/bonsai/bonsai/bim/module/project/prop.py index 23f7258ed6..43a014bd5a 100644 --- a/src/bonsai/bonsai/bim/module/project/prop.py +++ b/src/bonsai/bonsai/bim/module/project/prop.py @@ -232,10 +232,10 @@ class BIMProjectProperties(PropertyGroup): class MeasureToolSettings(PropertyGroup): - measure_type_items = [ + measurement_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") + measurement_type: bpy.props.EnumProperty(items=measurement_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 6cfe2f86fc..f7125c494d 100644 --- a/src/bonsai/bonsai/bim/module/project/workspace.py +++ b/src/bonsai/bonsai/bim/module/project/workspace.py @@ -67,7 +67,7 @@ class ExploreTool(bpy.types.WorkSpaceTool): 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) + row.prop(prop, "measurement_type", text="Measure Type", expand=True, icon_only=True, emboss=True) class ExploreHotkey(bpy.types.Operator): @@ -103,5 +103,5 @@ class ExploreHotkey(bpy.types.Operator): def hotkey_S_M(self): for obj in tool.Blender.get_selected_objects(): obj.select_set(False) - measure_type = bpy.context.scene.MeasureToolSettings.measure_type + measure_type = bpy.context.scene.MeasureToolSettings.measurement_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 15fb93cf7d..33abb8bb72 100644 --- a/src/bonsai/bonsai/tool/polyline.py +++ b/src/bonsai/bonsai/tool/polyline.py @@ -71,8 +71,7 @@ class Polyline(bonsai.core.tool.Polyline): value = float(self.get_text_value(attribute_name)) return f"{value:.2f}" else: - return Polyline.format_input_ui_units(context, value) - + return Polyline.format_input_ui_units(value) @dataclass class ToolState: @@ -215,6 +214,11 @@ class Polyline(bonsai.core.tool.Polyline): if input_ui.get_text_value("AREA") is not None: input_ui.set_value("AREA", area) + + area = input_ui.get_number_value("AREA") + if area: + area = tool.Polyline.format_input_ui_units(area) + polyline_data.area = area return @classmethod @@ -226,6 +230,7 @@ class Polyline(bonsai.core.tool.Polyline): last_point_data = polyline_points[len(polyline_points) - 1] last_point = Vector((last_point_data.x, last_point_data.y, last_point_data.z)) except: + polyline_points = [] default_container_elevation = 0 last_point = Vector((0, 0, 0)) @@ -495,20 +500,19 @@ class Polyline(bonsai.core.tool.Polyline): return False, "0" @classmethod - def format_input_ui_units(cls, context, value): + def format_input_ui_units(cls, value): unit_system = tool.Drawing.get_unit_system() if unit_system == "IMPERIAL": - precision = context.scene.DocProperties.imperial_precision + precision = bpy.context.scene.DocProperties.imperial_precision factor = 3.28084 else: precision = None factor = 1 - if context.scene.unit_settings.length_unit == "MILLIMETERS": + if bpy.context.scene.unit_settings.length_unit == "MILLIMETERS": factor = 1000 return format_distance(value * factor, precision=precision, suppress_zero_inches=True, in_unit_length=True) - @classmethod def insert_polyline_point(cls, input_ui, tool_state): x = input_ui.get_number_value("X") @@ -529,7 +533,6 @@ class Polyline(bonsai.core.tool.Polyline): y = snap_vertex.y z = snap_vertex.z - polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline if not polyline_data: polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline.add() @@ -558,6 +561,17 @@ class Polyline(bonsai.core.tool.Polyline): polyline_point.angle = a polyline_point.position = Vector((x, y, z)) + # Add total length + total_length = 0 + for i, point in enumerate(polyline_points): + if i == 0: + continue + dim = float(tool.Polyline.validate_input(point.dim, "D")[1]) + total_length += dim + total_length = tool.Polyline.format_input_ui_units(total_length) + polyline_data.total_length = total_length + + @classmethod def close_polyline(cls): polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline @@ -586,7 +600,7 @@ class Polyline(bonsai.core.tool.Polyline): polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_points = polyline_data[0].polyline_points if polyline_data else [] measurement_data = bpy.context.scene.BIMPolylineProperties.measurement_polyline.add() - measurement_type = bpy.context.scene.MeasureToolSettings.measure_type + measurement_type = bpy.context.scene.MeasureToolSettings.measurement_type measurement_data.measurement_type = measurement_type for point in polyline_points: measurement_point = measurement_data.polyline_points.add() @@ -597,18 +611,5 @@ class Polyline(bonsai.core.tool.Polyline): measurement_point.angle = point.angle measurement_point.position = point.position - # Add total length - total_length = 0 - for i, point in enumerate(measurement_data.polyline_points): - if i == 0: - continue - dim = float(tool.Polyline.validate_input(point.dim, "D")[1]) - total_length += dim - total_length = tool.Polyline.format_input_ui_units(context, total_length) - measurement_data.total_length = total_length - - # Add area - area = input_ui.get_number_value("AREA") - if area: - area = tool.Polyline.format_input_ui_units(context, area) - measurement_data.area = area + measurement_data.total_length = polyline_data[0].total_length + measurement_data.area = polyline_data[0].area