From 4da0b04162aa98784ca768184148b0ee1bea15e8 Mon Sep 17 00:00:00 2001 From: falken10vdl Date: Mon, 28 Jul 2025 23:48:36 +0200 Subject: [PATCH] add adjunt_overlapping_labels --- .../bonsai/bim/module/model/decorator.py | 147 +++++++++++++----- 1 file changed, 108 insertions(+), 39 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/decorator.py b/src/bonsai/bonsai/bim/module/model/decorator.py index e9435eea99..b45c50b623 100644 --- a/src/bonsai/bonsai/bim/module/model/decorator.py +++ b/src/bonsai/bonsai/bim/module/model/decorator.py @@ -521,6 +521,10 @@ class PolylineDecorator: color = self.addon_prefs.decorations_colour blf.color(self.font_id, *color) + + screen_coords = {} + label_index = 0 + for i in range(len(self.polyline_points)): if i < 1 and self.measure_type == "POLY_AREA": continue @@ -529,39 +533,39 @@ class PolylineDecorator: dim_text_pos = (Vector(self.polyline_points[i].position) + Vector(self.polyline_points[i - 1].position)) / 2 dim_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, dim_text_pos) - formatted_value = self.polyline_points[i].dim + if dim_text_coords: + formatted_value = self.polyline_points[i].dim + text = "d: " + formatted_value + screen_coords[f"distance_{label_index}"] = (dim_text_coords, text) + label_index += 1 - blf.position(self.font_id, dim_text_coords[0], dim_text_coords[1], 0) - text = "d: " + 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) + if self.measure_type != "SINGLE": + for i in range(len(self.polyline_points)): + if i ==1: + continue - if i == 1: - continue - angle_text_pos = Vector(self.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: " + self.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) + angle_text_pos = Vector(self.polyline_points[i - 1].position) + angle_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, angle_text_pos) + + if angle_text_coords: + text = "a: " + self.polyline_points[i].angle + screen_coords[f"angle_{label_index}"] = (angle_text_coords, text) + label_index += 1 if self.measure_type == "SINGLE": axis_line, axis_line_center = self.calculate_measurement_x_y_and_z(context) - 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) - pos = blf.position(self.font_id, dim_text_coords[0], dim_text_coords[1], 0) - value = round((axis_line[i][1] - axis_line[i][0]).length, 4) - direction = axis_line[i][1] - axis_line[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] - 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) + if axis_line and axis_line_center: + 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) + if dim_text_coords: + value = round((axis_line[i][1] - axis_line[i][0]).length, 4) + direction = axis_line[i][1] - axis_line[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] + formatted_value = tool.Polyline.format_input_ui_units(value) + text = f"{prefix}: {formatted_value}" + screen_coords[f"xyz_{i}"] = (dim_text_coords, text) # Area and Length text polyline_verts = [Vector((p.x, p.y, p.z)) for p in self.polyline_points] @@ -576,28 +580,93 @@ class PolylineDecorator: polyline_verts[:-1] ) # Doesn't use the last point if is a closed polyline area_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, center) - value = self.polyline_data.area - text = f"area: {value}" - text_length = blf.dimensions(self.font_id, text) - area_text_coords[0] -= text_length[0] / 2 # Center text horizontally - blf.position(self.font_id, area_text_coords[0], area_text_coords[1], 0) - self.draw_text_background(context, area_text_coords, text_length) - blf.draw(self.font_id, text) + if area_text_coords: + value = self.polyline_data.area + text = f"area: {value}" + screen_coords["area"] = (area_text_coords, text) # Length if self.measure_type in {"POLYLINE", "POLY_AREA"}: if len(polyline_verts) < 3: return total_length_text_coords = view3d_utils.location_3d_to_region_2d(region, rv3d, polyline_verts[-1]) - blf.position(self.font_id, total_length_text_coords[0], total_length_text_coords[1], 0) - value = self.polyline_data.total_length - text = f"length: {value}" + if total_length_text_coords: + value = self.polyline_data.total_length + text = f"length: {value}" + screen_coords["length"] = (total_length_text_coords, text) + + self.adjust_overlapping_labels(screen_coords) + + for label_key, (screen_co, text) in screen_coords.items(): + blf.position(self.font_id, screen_co.x, screen_co.y, 0) + blf.color(self.font_id, 1, 1, 1, 1) text_length = blf.dimensions(self.font_id, text) - self.draw_text_background(context, total_length_text_coords, text_length) + self.draw_text_background(context, screen_co, text_length) blf.draw(self.font_id, text) blf.disable(self.font_id, blf.SHADOW) + def adjust_overlapping_labels(self, screen_coords): + font_id = self.font_id + text_dimensions = {} + + for label_key, (screen_co, text) in screen_coords.items(): + text_dimensions[label_key] = blf.dimensions(font_id, text) + + min_spacing = 12 + + label_keys = list(screen_coords.keys()) + for pass_num in range(3): # 3 passes to try to optimize complex overlaps + for i in range(len(label_keys)): + for j in range(i + 1, len(label_keys)): + key1, key2 = label_keys[i], label_keys[j] + co1, _ = screen_coords[key1] + co2, _ = screen_coords[key2] + dim1 = text_dimensions[key1] + dim2 = text_dimensions[key2] + + bounds1 = { + "left": co1.x - min_spacing, + "right": co1.x + dim1[0] + min_spacing, + "top": co1.y + dim1[1] + min_spacing, + "bottom": co1.y - min_spacing, + } + bounds2 = { + "left": co2.x - min_spacing, + "right": co2.x + dim2[0] + min_spacing, + "top": co2.y + dim2[1] + min_spacing, + "bottom": co2.y - min_spacing, + } + + if ( + bounds1["left"] < bounds2["right"] + and bounds1["right"] > bounds2["left"] + and bounds1["bottom"] < bounds2["top"] + and bounds1["top"] > bounds2["bottom"] + ): + x_overlap = min(bounds1["right"], bounds2["right"]) - max(bounds1["left"], bounds2["left"]) + y_overlap = min(bounds1["top"], bounds2["top"]) - max(bounds1["bottom"], bounds2["bottom"]) + + separation_multiplier = 1.25 + + # Move labels in the direction requiring less movement + if x_overlap < y_overlap: + separation_distance = (x_overlap / 2 + min_spacing) * separation_multiplier + if co1.x < co2.x: + co1.x -= separation_distance + co2.x += separation_distance + else: + co1.x += separation_distance + co2.x -= separation_distance + else: + separation_distance = (y_overlap / 2 + min_spacing) * separation_multiplier + if co1.y < co2.y: + co1.y -= separation_distance + co2.y += separation_distance + else: + co1.y += separation_distance + co2.y -= separation_distance + def draw_measurements_poly(self, context): self.shader_config(context) polyline_verts: list[Vector] = []