Moved everything to explore tool

This commit is contained in:
falken10
2025-05-16 11:06:10 +02:00
committed by Bruno Perdigão
parent 8b4305360e
commit d3f2711fe4
7 changed files with 290 additions and 292 deletions
@@ -77,7 +77,6 @@ classes = (
operator.UpdateItemAttributes, operator.UpdateItemAttributes,
operator.UpdateParametricRepresentation, operator.UpdateParametricRepresentation,
operator.UpdateRepresentation, operator.UpdateRepresentation,
operator.BIM_OT_local_coordinates_gizmo,
operator.CreateInstance, operator.CreateInstance,
prop.RepresentationItem, prop.RepresentationItem,
prop.RepresentationItemObject, prop.RepresentationItemObject,
@@ -135,15 +134,6 @@ def register():
bpy.types.Object.BIMGeometryProperties = bpy.props.PointerProperty(type=prop.BIMObjectGeometryProperties) bpy.types.Object.BIMGeometryProperties = bpy.props.PointerProperty(type=prop.BIMObjectGeometryProperties)
bpy.types.Scene.BIMGeometryProperties = bpy.props.PointerProperty(type=prop.BIMGeometryProperties) bpy.types.Scene.BIMGeometryProperties = bpy.props.PointerProperty(type=prop.BIMGeometryProperties)
bpy.types.Scene.show_colored_dimensions = bpy.props.BoolProperty(
name="Show Colored Dimensions",
description="Show XYZ dimensions in color. Local coordinates for single selection. Global coordinates for multiple selection. Toggles gizmo",
default=False,
update=ui.BIM_PT_derived_coordinates.update_show_colored_dimensions,
)
bpy.types.Scene.bonsai_prev_orientation_type = bpy.props.StringProperty(name="Prev Orientation Type")
bpy.types.Scene.bonsai_prev_gizmo_translate = bpy.props.BoolProperty(name="Prev Gizmo Translate")
bpy.types.VIEW3D_MT_object.append(ui.object_menu) bpy.types.VIEW3D_MT_object.append(ui.object_menu)
bpy.types.OUTLINER_MT_object.append(ui.outliner_menu) bpy.types.OUTLINER_MT_object.append(ui.outliner_menu)
bpy.types.VIEW3D_MT_object_context_menu.append(ui.object_menu) bpy.types.VIEW3D_MT_object_context_menu.append(ui.object_menu)
@@ -225,6 +215,3 @@ def unregister():
km.keymap_items.remove(kmi) km.keymap_items.remove(kmi)
addon_keymaps.clear() addon_keymaps.clear()
del bpy.types.Scene.show_colored_dimensions
del bpy.types.Scene._bonsai_prev_orientation_type
del bpy.types.Scene._bonsai_prev_gizmo_translate
@@ -3470,79 +3470,3 @@ class CreateInstance(bpy.types.Operator, tool.Ifc.Operator):
return {"FINISHED"} return {"FINISHED"}
previous_selection = set()
def selection_monitor_handler(scene, depsgraph):
global previous_selection
# Get the current selection
current_selection = {obj.name for obj in scene.objects if obj.select_get()}
# If selection changed, print and update
if current_selection != previous_selection:
if len(bpy.context.selected_objects) == 1:
scene.transform_orientation_slots[1].type = "LOCAL"
else:
scene.transform_orientation_slots[1].type = "GLOBAL"
area = next((a for a in bpy.context.screen.areas if a.type == "VIEW_3D"), None)
space = next((s for s in area.spaces if s.type == "VIEW_3D"), None)
space.show_gizmo_object_translate = True
previous_selection = current_selection
def register_handler():
unregister_handler()
bpy.app.handlers.depsgraph_update_post.append(selection_monitor_handler)
def unregister_handler():
handlers = bpy.app.handlers.depsgraph_update_post
handlers[:] = [h for h in handlers if h.__name__ != "selection_monitor_handler"]
class BIM_OT_local_coordinates_gizmo(bpy.types.Operator):
bl_idname = "bim.toggle_local_gizmo"
bl_label = "Show Local Gizmo"
def execute(self, context):
scene = bpy.context.scene
area_3d = next((area for area in bpy.context.screen.areas if area.type == "VIEW_3D"), None)
space_3d = next((space for space in area_3d.spaces if space.type == "VIEW_3D"), None) if area_3d else None
try:
if context.scene.show_colored_dimensions:
register_handler()
bpy.app.handlers.depsgraph_update_post.append(
selection_monitor_handler
) # Save initial state only if not already saved
if not scene.get("bonsai_prev_orientation_type", None):
scene["bonsai_prev_orientation_type"] = scene.transform_orientation_slots[1].type
if not scene.get("bonsai_prev_gizmo_translate", None):
scene["bonsai_prev_gizmo_translate"] = space_3d.show_gizmo_object_translate if space_3d else False
# Set orientation and gizmo based on selection count
if space_3d:
if len(context.selected_objects) == 1:
scene.transform_orientation_slots[1].type = "LOCAL"
else:
scene.transform_orientation_slots[1].type = "GLOBAL"
space_3d.show_gizmo_object_translate = True
else:
unregister_handler()
# Restore previous state
if space_3d:
prev_orientation = scene.get("bonsai_prev_orientation_type", None)
if prev_orientation:
scene.transform_orientation_slots[1].type = prev_orientation
scene["bonsai_prev_orientation_type"] = ""
prev_gizmo = scene.get("bonsai_prev_gizmo_translate", None)
if prev_gizmo is not None:
space_3d.show_gizmo_object_translate = prev_gizmo
scene["bonsai_prev_gizmo_translate"] = None
except Exception:
self.report({"WARNING"}, "Could not set transform orientation.")
return {"FINISHED"}
+1 -202
View File
@@ -19,10 +19,6 @@
import bpy import bpy
import bonsai.bim import bonsai.bim
import bonsai.tool as tool import bonsai.tool as tool
import gpu
import mathutils
import blf
from gpu_extras.batch import batch_for_shader
from bpy.types import Panel, Menu, UIList from bpy.types import Panel, Menu, UIList
from bonsai.bim.helper import prop_with_search from bonsai.bim.helper import prop_with_search
from bonsai.bim.module.geometry.data import ( from bonsai.bim.module.geometry.data import (
@@ -35,196 +31,6 @@ from bonsai.bim.module.geometry.data import (
from bonsai.bim.module.layer.data import LayersData from bonsai.bim.module.layer.data import LayersData
_draw_handler_box = None
_draw_handler_text = None
def get_combined_bounding_box_corners(objects):
import mathutils
if not objects:
return None, None, None
if len(objects) == 1:
obj = bpy.context.active_object
corners = [obj.matrix_world @ mathutils.Vector(corner) for corner in obj.bound_box]
else:
all_corners = []
for obj in objects:
if hasattr(obj, "bound_box"):
all_corners.extend([obj.matrix_world @ mathutils.Vector(corner) for corner in obj.bound_box])
min_corner = mathutils.Vector(
(min(v.x for v in all_corners), min(v.y for v in all_corners), min(v.z for v in all_corners))
)
max_corner = mathutils.Vector(
(max(v.x for v in all_corners), max(v.y for v in all_corners), max(v.z for v in all_corners))
)
corners = [
mathutils.Vector((min_corner.x, min_corner.y, min_corner.z)),
mathutils.Vector((min_corner.x, min_corner.y, max_corner.z)),
mathutils.Vector((min_corner.x, max_corner.y, max_corner.z)),
mathutils.Vector((min_corner.x, max_corner.y, min_corner.z)),
mathutils.Vector((max_corner.x, min_corner.y, min_corner.z)),
mathutils.Vector((max_corner.x, min_corner.y, max_corner.z)),
mathutils.Vector((max_corner.x, max_corner.y, max_corner.z)),
mathutils.Vector((max_corner.x, max_corner.y, min_corner.z)),
]
edges = [
(0, 1, "Z"),
(1, 2, "Y"),
(2, 3, "Z"),
(3, 0, "Y"),
(4, 5, "Z"),
(5, 6, "Y"),
(6, 7, "Z"),
(7, 4, "Y"),
(0, 4, "X"),
(1, 5, "X"),
(2, 6, "X"),
(3, 7, "X"),
]
axis_colors = {
"X": (0.956, 0.282, 0.322, 1),
"Y": (0.565, 0.812, 0.125, 1),
"Z": (0.196, 0.529, 0.929, 1),
}
return corners, edges, axis_colors
def find_closest_trihedron(corners, edges, region, rv3d):
from bpy_extras.view3d_utils import location_3d_to_region_2d
# Project all corners to 2D and find the one with the lowest Y value
min_y = float("inf")
best_origin = None
for idx, corner in enumerate(corners):
screen_co = location_3d_to_region_2d(region, rv3d, corner)
if screen_co is not None and screen_co.y < min_y:
min_y = screen_co.y
best_origin = idx
trihedron = [
{"X": (0, 4), "Y": (0, 3), "Z": (0, 1)},
{"X": (1, 5), "Y": (1, 2), "Z": (1, 0)},
{"X": (2, 6), "Y": (2, 1), "Z": (2, 3)},
{"X": (3, 7), "Y": (3, 0), "Z": (3, 2)},
{"X": (4, 0), "Y": (4, 7), "Z": (4, 5)},
{"X": (5, 1), "Y": (5, 6), "Z": (5, 4)},
{"X": (6, 2), "Y": (6, 5), "Z": (6, 7)},
{"X": (7, 3), "Y": (7, 4), "Z": (7, 6)},
]
return trihedron[best_origin]
def draw_bounding_box_wire_cube():
selected_objects = [obj for obj in bpy.context.selected_objects if hasattr(obj, "bound_box")]
if not selected_objects:
return
corners, edges, axis_colors = get_combined_bounding_box_corners(selected_objects)
region = bpy.context.region
rv3d = bpy.context.region_data
shader = gpu.shader.from_builtin("UNIFORM_COLOR")
gpu.state.line_width_set(2.0)
# Draw the bounding box edges
for i1, i2, axis in edges:
color = (0.5, 0.5, 0.5, 0.75)
shader.bind()
shader.uniform_float("color", color)
batch = batch_for_shader(shader, "LINES", {"pos": [corners[i1], corners[i2]]})
batch.draw(shader)
# Draw the trihedron edges
closest_indices = find_closest_trihedron(corners, edges, region, rv3d)
for axis in "XYZ":
pair = closest_indices[axis]
if pair is not None:
i1, i2 = pair
color = axis_colors[axis]
shader.bind()
shader.uniform_float("color", color)
batch = batch_for_shader(shader, "LINES", {"pos": [corners[i1], corners[i2]]})
batch.draw(shader)
def draw_dimension_text():
from bpy_extras.view3d_utils import location_3d_to_region_2d
from bonsai.bim.module.drawing.helper import format_distance
selected_objects = [obj for obj in bpy.context.selected_objects if hasattr(obj, "bound_box")]
if not selected_objects:
return
corners, edges, axis_colors = get_combined_bounding_box_corners(selected_objects)
if not corners:
return
dims = mathutils.Vector(
(
(corners[4] - corners[0]).length,
(corners[3] - corners[0]).length,
(corners[1] - corners[0]).length,
)
)
region = bpy.context.region
rv3d = bpy.context.region_data
closest_indices = find_closest_trihedron(corners, edges, region, rv3d)
font_id = 0
blf.size(font_id, 20)
for axis in "XYZ":
pair = closest_indices[axis]
if pair is not None:
i1, i2 = pair
center = (corners[i1] + corners[i2]) / 2
value = getattr(dims, axis.lower())
screen_co = location_3d_to_region_2d(region, rv3d, center)
if screen_co is not None:
# Draw axis label in color
blf.position(font_id, screen_co.x, screen_co.y, 0)
blf.color(font_id, *axis_colors[axis])
blf.draw(font_id, f"{axis}: ")
axis_width, _ = blf.dimensions(font_id, f"{axis}: ")
# Draw value+unit in white
blf.position(font_id, screen_co.x + axis_width, screen_co.y, 0)
blf.color(font_id, 1, 1, 1, 1)
value_str = format_distance(value, hide_units=False)
blf.draw(font_id, value_str)
# To show the corners indexes, uncomment the following lines
# for idx, corner in enumerate(corners):
# screen_co = location_3d_to_region_2d(region, rv3d, corner)
# if screen_co is not None:
# blf.position(font_id, screen_co.x, screen_co.y, 0)
# blf.color(font_id, 1, 1, 0, 1)
# blf.draw(font_id, str(idx))
def enable_bounding_box_wire_cube():
global _draw_handler_box, _draw_handler_text
if _draw_handler_box is None:
_draw_handler_box = bpy.types.SpaceView3D.draw_handler_add(
draw_bounding_box_wire_cube, (), "WINDOW", "POST_VIEW"
)
if _draw_handler_text is None:
_draw_handler_text = bpy.types.SpaceView3D.draw_handler_add(draw_dimension_text, (), "WINDOW", "POST_PIXEL")
def disable_bounding_box_wire_cube():
global _draw_handler_box, _draw_handler_text
if _draw_handler_box is not None:
bpy.types.SpaceView3D.draw_handler_remove(_draw_handler_box, "WINDOW")
_draw_handler_box = None
if _draw_handler_text is not None:
bpy.types.SpaceView3D.draw_handler_remove(_draw_handler_text, "WINDOW")
_draw_handler_text = None
class UIData: class UIData:
data = {} data = {}
is_loaded = False is_loaded = False
@@ -709,9 +515,6 @@ class BIM_PT_derived_coordinates(Panel):
def poll(cls, context): def poll(cls, context):
return context.active_object is not None return context.active_object is not None
def update_show_colored_dimensions(self, context):
bpy.ops.bim.toggle_local_gizmo()
def draw(self, context): def draw(self, context):
if not DerivedCoordinatesData.is_loaded: if not DerivedCoordinatesData.is_loaded:
DerivedCoordinatesData.load() DerivedCoordinatesData.load()
@@ -722,23 +525,19 @@ class BIM_PT_derived_coordinates(Panel):
text += "*" text += "*"
row.operator("bim.edit_object_placement", text=text, icon="EXPORT") row.operator("bim.edit_object_placement", text=text, icon="EXPORT")
# --- XYZ Dimensions with checkbox ---
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="XYZ Dimensions") row.label(text="XYZ Dimensions")
row.prop(context.scene, "show_colored_dimensions", text="Show colored dimensions")
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.enabled = False row.enabled = False
area_3d = next((area for area in context.screen.areas if area.type == "VIEW_3D"), None) area_3d = next((area for area in context.screen.areas if area.type == "VIEW_3D"), None)
space_3d = next((space for space in area_3d.spaces if space.type == "VIEW_3D"), None) space_3d = next((space for space in area_3d.spaces if space.type == "VIEW_3D"), None)
if context.scene.show_colored_dimensions: if bpy.context.scene.MeasureToolSettings.measurement_type == "XYZ_DIMENSIONS":
enable_bounding_box_wire_cube()
for axis, icon, idx in [("X", "STRIP_COLOR_01", 0), ("Y", "STRIP_COLOR_04", 1), ("Z", "STRIP_COLOR_05", 2)]: for axis, icon, idx in [("X", "STRIP_COLOR_01", 0), ("Y", "STRIP_COLOR_04", 1), ("Z", "STRIP_COLOR_05", 2)]:
row.label(text="", icon=icon) row.label(text="", icon=icon)
row.prop(context.active_object, "dimensions", text=axis, index=idx) row.prop(context.active_object, "dimensions", text=axis, index=idx)
else: else:
disable_bounding_box_wire_cube()
row.prop(context.active_object, "dimensions", text="X", index=0) row.prop(context.active_object, "dimensions", text="X", index=0)
row.prop(context.active_object, "dimensions", text="Y", index=1) row.prop(context.active_object, "dimensions", text="Y", index=1)
row.prop(context.active_object, "dimensions", text="Z", index=2) row.prop(context.active_object, "dimensions", text="Z", index=2)
@@ -49,6 +49,7 @@ classes = (
operator.LoadProjectElements, operator.LoadProjectElements,
operator.MeasureTool, operator.MeasureTool,
operator.MeasureFaceAreaTool, operator.MeasureFaceAreaTool,
operator.MeasureXYZDimensionsTool,
operator.ClearMeasurement, operator.ClearMeasurement,
operator.NewProject, operator.NewProject,
operator.QueryLinkedElement, operator.QueryLinkedElement,
@@ -100,6 +101,8 @@ def register():
bpy.types.TOPBAR_MT_file_import.append(ui.file_import_menu) bpy.types.TOPBAR_MT_file_import.append(ui.file_import_menu)
bpy.types.TOPBAR_MT_file.prepend(ui.file_menu) bpy.types.TOPBAR_MT_file.prepend(ui.file_menu)
bpy.types.TOPBAR_MT_file_context_menu.prepend(ui.file_menu) bpy.types.TOPBAR_MT_file_context_menu.prepend(ui.file_menu)
bpy.types.Scene.prev_transform_orientation_slot_type = bpy.props.StringProperty(name="Previous Gizmo Orientation Type")
bpy.types.Scene.prev_show_gizmo_object_translate = bpy.props.BoolProperty(name="Previous Gizmo Translate")
wm = bpy.context.window_manager wm = bpy.context.window_manager
if wm.keyconfigs.addon: if wm.keyconfigs.addon:
km = wm.keyconfigs.addon.keymaps.get("Window") km = wm.keyconfigs.addon.keymaps.get("Window")
@@ -120,6 +123,8 @@ def unregister():
bpy.utils.unregister_tool(workspace.ExploreTool) bpy.utils.unregister_tool(workspace.ExploreTool)
del bpy.types.Scene.BIMProjectProperties del bpy.types.Scene.BIMProjectProperties
del bpy.types.Scene.MeasureToolSettings del bpy.types.Scene.MeasureToolSettings
del bpy.types.Scene.prev_transform_orientation_slot_type
del bpy.types.Scene.prev_show_gizmo_object_translate
bpy.app.handlers.load_post.remove(decorator.toggle_decorations_on_load) 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.remove(ui.file_menu)
bpy.types.TOPBAR_MT_file_context_menu.remove(ui.file_menu) bpy.types.TOPBAR_MT_file_context_menu.remove(ui.file_menu)
@@ -25,6 +25,9 @@ import tempfile
import traceback import traceback
import subprocess import subprocess
import datetime import datetime
import blf
import mathutils
import gpu
import ifcopenshell.api.attribute import ifcopenshell.api.attribute
import numpy as np import numpy as np
import ifcopenshell import ifcopenshell
@@ -64,6 +67,7 @@ from bonsai.bim.module.project.prop import BreadcrumbType
from bonsai.bim.module.model.decorator import PolylineDecorator, FaceAreaDecorator from bonsai.bim.module.model.decorator import PolylineDecorator, FaceAreaDecorator
from bonsai.bim.module.model.polyline import PolylineOperator from bonsai.bim.module.model.polyline import PolylineOperator
from typing import Union, TYPE_CHECKING, get_args, Literal from typing import Union, TYPE_CHECKING, get_args, Literal
from gpu_extras.batch import batch_for_shader
if TYPE_CHECKING: if TYPE_CHECKING:
import bpy.stub_internal.rna_enums as rna_enums import bpy.stub_internal.rna_enums as rna_enums
@@ -2787,6 +2791,278 @@ class MeasureFaceAreaTool(bpy.types.Operator, PolylineOperator):
FaceAreaDecorator.install(context) FaceAreaDecorator.install(context)
return {"RUNNING_MODAL"} return {"RUNNING_MODAL"}
_draw_handler_box = None
_draw_handler_text = None
def get_combined_bounding_box_corners(objects):
import mathutils
if not objects:
return None, None, None
if len(objects) == 1:
obj = bpy.context.active_object
corners = [obj.matrix_world @ mathutils.Vector(corner) for corner in obj.bound_box]
else:
all_corners = []
for obj in objects:
if hasattr(obj, "bound_box"):
all_corners.extend([obj.matrix_world @ mathutils.Vector(corner) for corner in obj.bound_box])
min_corner = mathutils.Vector(
(min(v.x for v in all_corners), min(v.y for v in all_corners), min(v.z for v in all_corners))
)
max_corner = mathutils.Vector(
(max(v.x for v in all_corners), max(v.y for v in all_corners), max(v.z for v in all_corners))
)
corners = [
mathutils.Vector((min_corner.x, min_corner.y, min_corner.z)),
mathutils.Vector((min_corner.x, min_corner.y, max_corner.z)),
mathutils.Vector((min_corner.x, max_corner.y, max_corner.z)),
mathutils.Vector((min_corner.x, max_corner.y, min_corner.z)),
mathutils.Vector((max_corner.x, min_corner.y, min_corner.z)),
mathutils.Vector((max_corner.x, min_corner.y, max_corner.z)),
mathutils.Vector((max_corner.x, max_corner.y, max_corner.z)),
mathutils.Vector((max_corner.x, max_corner.y, min_corner.z)),
]
edges = [
(0, 1, "Z"),
(1, 2, "Y"),
(2, 3, "Z"),
(3, 0, "Y"),
(4, 5, "Z"),
(5, 6, "Y"),
(6, 7, "Z"),
(7, 4, "Y"),
(0, 4, "X"),
(1, 5, "X"),
(2, 6, "X"),
(3, 7, "X"),
]
axis_colors = {
"X": (0.956, 0.282, 0.322, 1),
"Y": (0.565, 0.812, 0.125, 1),
"Z": (0.196, 0.529, 0.929, 1),
}
return corners, edges, axis_colors
def find_closest_trihedron(corners, edges, region, rv3d):
from bpy_extras.view3d_utils import location_3d_to_region_2d
# Project all corners to 2D and find the one with the lowest Y value
min_y = float("inf")
best_origin = None
for idx, corner in enumerate(corners):
screen_co = location_3d_to_region_2d(region, rv3d, corner)
if screen_co is not None and screen_co.y < min_y:
min_y = screen_co.y
best_origin = idx
trihedron = [
{"X": (0, 4), "Y": (0, 3), "Z": (0, 1)},
{"X": (1, 5), "Y": (1, 2), "Z": (1, 0)},
{"X": (2, 6), "Y": (2, 1), "Z": (2, 3)},
{"X": (3, 7), "Y": (3, 0), "Z": (3, 2)},
{"X": (4, 0), "Y": (4, 7), "Z": (4, 5)},
{"X": (5, 1), "Y": (5, 6), "Z": (5, 4)},
{"X": (6, 2), "Y": (6, 5), "Z": (6, 7)},
{"X": (7, 3), "Y": (7, 4), "Z": (7, 6)},
]
return trihedron[best_origin]
def draw_bounding_box_wire_cube():
selected_objects = [obj for obj in bpy.context.selected_objects if hasattr(obj, "bound_box")]
if not selected_objects:
return
corners, edges, axis_colors = get_combined_bounding_box_corners(selected_objects)
region = bpy.context.region
rv3d = bpy.context.region_data
shader = gpu.shader.from_builtin("UNIFORM_COLOR")
gpu.state.line_width_set(2.0)
# Draw the bounding box edges
for i1, i2, axis in edges:
color = (0.5, 0.5, 0.5, 0.75)
shader.bind()
shader.uniform_float("color", color)
batch = batch_for_shader(shader, "LINES", {"pos": [corners[i1], corners[i2]]})
batch.draw(shader)
# Draw the trihedron edges
closest_indices = find_closest_trihedron(corners, edges, region, rv3d)
for axis in "XYZ":
pair = closest_indices[axis]
if pair is not None:
i1, i2 = pair
color = axis_colors[axis]
shader.bind()
shader.uniform_float("color", color)
batch = batch_for_shader(shader, "LINES", {"pos": [corners[i1], corners[i2]]})
batch.draw(shader)
def draw_dimension_text():
from bpy_extras.view3d_utils import location_3d_to_region_2d
from bonsai.bim.module.drawing.helper import format_distance
selected_objects = [obj for obj in bpy.context.selected_objects if hasattr(obj, "bound_box")]
if not selected_objects:
return
corners, edges, axis_colors = get_combined_bounding_box_corners(selected_objects)
if not corners:
return
dims = mathutils.Vector(
(
(corners[4] - corners[0]).length,
(corners[3] - corners[0]).length,
(corners[1] - corners[0]).length,
)
)
region = bpy.context.region
rv3d = bpy.context.region_data
closest_indices = find_closest_trihedron(corners, edges, region, rv3d)
font_id = 0
blf.size(font_id, 20)
for axis in "XYZ":
pair = closest_indices[axis]
if pair is not None:
i1, i2 = pair
center = (corners[i1] + corners[i2]) / 2
value = getattr(dims, axis.lower())
screen_co = location_3d_to_region_2d(region, rv3d, center)
if screen_co is not None:
# Draw axis label in color
blf.position(font_id, screen_co.x, screen_co.y, 0)
blf.color(font_id, *axis_colors[axis])
blf.draw(font_id, f"{axis}: ")
axis_width, _ = blf.dimensions(font_id, f"{axis}: ")
# Draw value+unit in white
blf.position(font_id, screen_co.x + axis_width, screen_co.y, 0)
blf.color(font_id, 1, 1, 1, 1)
value_str = format_distance(value, hide_units=False)
blf.draw(font_id, value_str)
# To show the corners indexes, uncomment the following lines
# for idx, corner in enumerate(corners):
# screen_co = location_3d_to_region_2d(region, rv3d, corner)
# if screen_co is not None:
# blf.position(font_id, screen_co.x, screen_co.y, 0)
# blf.color(font_id, 1, 1, 0, 1)
# blf.draw(font_id, str(idx))
def enable_bounding_box_wire_cube():
global _draw_handler_box, _draw_handler_text
if _draw_handler_box is None:
_draw_handler_box = bpy.types.SpaceView3D.draw_handler_add(
draw_bounding_box_wire_cube, (), "WINDOW", "POST_VIEW"
)
if _draw_handler_text is None:
_draw_handler_text = bpy.types.SpaceView3D.draw_handler_add(draw_dimension_text, (), "WINDOW", "POST_PIXEL")
def disable_bounding_box_wire_cube():
global _draw_handler_box, _draw_handler_text
if _draw_handler_box is not None:
bpy.types.SpaceView3D.draw_handler_remove(_draw_handler_box, "WINDOW")
_draw_handler_box = None
if _draw_handler_text is not None:
bpy.types.SpaceView3D.draw_handler_remove(_draw_handler_text, "WINDOW")
_draw_handler_text = None
previous_selection = set()
def selection_monitor_handler(scene, depsgraph):
global previous_selection
# Get the current selection
current_selection = {obj.name for obj in scene.objects if obj.select_get()}
# If selection changed, print and update
print("Selection changed:", current_selection)
if current_selection != previous_selection:
if len(bpy.context.selected_objects) == 1:
scene.transform_orientation_slots[1].type = "LOCAL"
else:
scene.transform_orientation_slots[1].type = "GLOBAL"
area = next((a for a in bpy.context.screen.areas if a.type == "VIEW_3D"), None)
space = next((s for s in area.spaces if s.type == "VIEW_3D"), None)
space.show_gizmo_object_translate = True
previous_selection = current_selection
def register_handler():
unregister_handler()
bpy.app.handlers.depsgraph_update_post.append(selection_monitor_handler)
def unregister_handler():
handlers = bpy.app.handlers.depsgraph_update_post
handlers[:] = [h for h in handlers if h.__name__ != "selection_monitor_handler"]
class MeasureXYZDimensionsTool(bpy.types.Operator):
bl_idname = "bim.measure_xyz_dimensions_tool"
bl_label = "Show bonding box dimensions"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
scene = bpy.context.scene
area_3d = next((area for area in bpy.context.screen.areas if area.type == "VIEW_3D"), None)
space_3d = next((space for space in area_3d.spaces if space.type == "VIEW_3D"), None) if area_3d else None
try:
if bpy.context.scene.MeasureToolSettings.measurement_type == "XYZ_DIMENSIONS":
enable_bounding_box_wire_cube()
register_handler()
if not scene.get("prev_transform_orientation_slot_type", None):
scene["prev_transform_orientation_slot_type"] = scene.transform_orientation_slots[1].type
if not scene.get("prev_show_gizmo_object_translate", None):
scene["prev_show_gizmo_object_translate"] = space_3d.show_gizmo_object_translate if space_3d else False
# Set orientation and gizmo based on selection count
if space_3d:
if len(context.selected_objects) == 1:
scene.transform_orientation_slots[1].type = "LOCAL"
else:
scene.transform_orientation_slots[1].type = "GLOBAL"
space_3d.show_gizmo_object_translate = True
else:
disable_bounding_box_wire_cube()
unregister_handler()
# Restore previous state
if space_3d:
prev_orientation = scene.get("prev_transform_orientation_slot_type", None)
if prev_orientation:
scene.transform_orientation_slots[1].type = prev_orientation
scene["prev_transform_orientation_slot_type"] = ""
prev_gizmo = scene.get("prev_show_gizmo_object_translate", None)
if prev_gizmo is not None:
space_3d.show_gizmo_object_translate = prev_gizmo
scene["prev_show_gizmo_object_translate"] = None
except Exception:
self.report({"WARNING"}, "Could not set transform orientation.")
return {"FINISHED"}
class ClearMeasurement(bpy.types.Operator): class ClearMeasurement(bpy.types.Operator):
bl_idname = "bim.clear_measurement" bl_idname = "bim.clear_measurement"
+6 -1
View File
@@ -507,15 +507,20 @@ class BIMProjectProperties(PropertyGroup):
yield link yield link
def measurement_type_update(self, context):
print(f"Measurement type changed to: {self.measurement_type}")
bpy.ops.bim.measure_xyz_dimensions_tool()
class MeasureToolSettings(PropertyGroup): class MeasureToolSettings(PropertyGroup):
measurement_type_items = [ measurement_type_items = [
("SINGLE", "SINGLE", "Single", "FIXED_SIZE", 1), ("SINGLE", "SINGLE", "Single", "FIXED_SIZE", 1),
("POLYLINE", "POLYLINE", "Polyline", "DRIVER_ROTATIONAL_DIFFERENCE", 2), ("POLYLINE", "POLYLINE", "Polyline", "DRIVER_ROTATIONAL_DIFFERENCE", 2),
("POLY_AREA", "POLY_AREA", "Poyline Area", "OUTLINER_DATA_LIGHTPROBE", 3), ("POLY_AREA", "POLY_AREA", "Poyline Area", "OUTLINER_DATA_LIGHTPROBE", 3),
("FACE_AREA", "FACE_AREA", "Face Area", "FACESEL", 4), ("FACE_AREA", "FACE_AREA", "Face Area", "FACESEL", 4),
("XYZ_DIMENSIONS", "XYZ_DIMENSIONS", "XYZ Dimensions", "ORIENTATION_LOCAL", 5),
] ]
measurement_type: bpy.props.EnumProperty(items=measurement_type_items, default="POLYLINE") measurement_type: bpy.props.EnumProperty(items=measurement_type_items, default="POLYLINE", update= measurement_type_update)
if TYPE_CHECKING: if TYPE_CHECKING:
measurement_type: Literal["SINGLE", "POLYLINE", "AREA"] measurement_type: Literal["SINGLE", "POLYLINE", "AREA"]
@@ -108,5 +108,7 @@ class ExploreHotkey(bpy.types.Operator):
measure_type = bpy.context.scene.MeasureToolSettings.measurement_type measure_type = bpy.context.scene.MeasureToolSettings.measurement_type
if measure_type == "FACE_AREA": if measure_type == "FACE_AREA":
bpy.ops.bim.measure_face_area_tool("INVOKE_DEFAULT") bpy.ops.bim.measure_face_area_tool("INVOKE_DEFAULT")
elif measure_type == "XYZ_DIMENSIONS":
pass
else: else:
bpy.ops.bim.measure_tool("INVOKE_DEFAULT", measure_type=measure_type) bpy.ops.bim.measure_tool("INVOKE_DEFAULT", measure_type=measure_type)