mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-24 15:07:38 +00:00
Initial implementation of measure tool modes: single, polyline and area.
- Single: draws a single measurement that shows the lines and dimensions for x, y and z. - Polyline: it's how it was already working - Area: show the area value in the input panel and creates the polygon shape that represents the area. Only works for coplanar points. The mode can be chosen by clicking the option icon in the workspace menu.
This commit is contained in:
@@ -319,6 +319,9 @@ class PolylineDecorator:
|
|||||||
handler = cls()
|
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_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_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_input_ui, (context,), "WINDOW", "POST_PIXEL"))
|
||||||
cls.handlers.append(
|
cls.handlers.append(
|
||||||
SpaceView3D.draw_handler_add(handler.draw_product_preview, (context,), "WINDOW", "POST_VIEW")
|
SpaceView3D.draw_handler_add(handler.draw_product_preview, (context,), "WINDOW", "POST_VIEW")
|
||||||
@@ -350,14 +353,50 @@ class PolylineDecorator:
|
|||||||
cls.axis_start = start
|
cls.axis_start = start
|
||||||
cls.axis_end = end
|
cls.axis_end = end
|
||||||
|
|
||||||
# @classmethod
|
|
||||||
# def set_axis_rectangle(cls, corners):
|
|
||||||
# cls.axis_rectangle = [*corners]
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_tool_state(cls, tool_state):
|
def set_tool_state(cls, tool_state):
|
||||||
cls.tool_state = 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):
|
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||||
shader = self.line_shader if shader_type == "LINES" else self.shader
|
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||||
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||||
@@ -424,7 +463,6 @@ class PolylineDecorator:
|
|||||||
bm.verts.index_update()
|
bm.verts.index_update()
|
||||||
bm.edges.index_update()
|
bm.edges.index_update()
|
||||||
edges = [[v.index for v in e.verts] for e in bm.edges]
|
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()]
|
tris = [[l.vert.index for l in loop] for loop in bm.calc_loop_triangles()]
|
||||||
all_edges.extend(edges)
|
all_edges.extend(edges)
|
||||||
all_tris.extend(tris)
|
all_tris.extend(tris)
|
||||||
@@ -491,6 +529,7 @@ class PolylineDecorator:
|
|||||||
def draw_measurements(self, context):
|
def draw_measurements(self, context):
|
||||||
region = context.region
|
region = context.region
|
||||||
rv3d = region.data
|
rv3d = region.data
|
||||||
|
measure_type = context.scene.MeasureToolSettings.measure_type
|
||||||
measurement_prop = context.scene.BIMPolylineProperties.polyline_point
|
measurement_prop = context.scene.BIMPolylineProperties.polyline_point
|
||||||
|
|
||||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||||
@@ -526,8 +565,65 @@ class PolylineDecorator:
|
|||||||
text_dim = blf.dimensions(self.font_id, text)
|
text_dim = blf.dimensions(self.font_id, text)
|
||||||
self.draw_text_background(context, coords_angle, text_dim)
|
self.draw_text_background(context, coords_angle, text_dim)
|
||||||
blf.draw(self.font_id, text)
|
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)
|
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):
|
def draw_snap_point(self, context):
|
||||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||||
self.line_shader.bind() # required to be able to change uniforms of the shader
|
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.line_shader.uniform_float("lineWidth", 0.75)
|
||||||
self.draw_batch("LINES", [self.axis_start, self.axis_end], axis_color, [(0, 1)])
|
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
|
# Area highlight
|
||||||
# if "AREA" in list(self.input_panel.keys()): # TODO Change to input_ui
|
try:
|
||||||
# if self.input_panel["AREA"] and float(self.input_panel["AREA"]) > 0: # TODO Change to input_ui
|
has_area = self.input_ui.init_area
|
||||||
# edges = []
|
except:
|
||||||
# for i in range(1, len(polyline_points) - 1):
|
has_area = False
|
||||||
# edges.append((0, i, i + 1))
|
if has_area:
|
||||||
# self.draw_batch("TRIS", polyline_points, (0, 1, 0, 0.1), edges)
|
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
|
# Mouse points
|
||||||
if snap_prop.snap_type in ["Plane", "Axis", "Mix"]:
|
if snap_prop.snap_type in ["Plane", "Axis", "Mix"]:
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ classes = (
|
|||||||
prop.FilterCategory,
|
prop.FilterCategory,
|
||||||
prop.Link,
|
prop.Link,
|
||||||
prop.BIMProjectProperties,
|
prop.BIMProjectProperties,
|
||||||
|
prop.MeasureToolSettings,
|
||||||
ui.BIM_MT_new_project,
|
ui.BIM_MT_new_project,
|
||||||
ui.BIM_MT_project,
|
ui.BIM_MT_project,
|
||||||
ui.BIM_PT_new_project_wizard,
|
ui.BIM_PT_new_project_wizard,
|
||||||
@@ -92,6 +93,7 @@ def register():
|
|||||||
if not bpy.app.background:
|
if not bpy.app.background:
|
||||||
bpy.utils.register_tool(workspace.ExploreTool, after={"builtin.transform"}, separator=True, group=False)
|
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.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.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_import.append(ui.file_import_menu)
|
||||||
bpy.types.TOPBAR_MT_file.prepend(ui.file_menu)
|
bpy.types.TOPBAR_MT_file.prepend(ui.file_menu)
|
||||||
@@ -115,6 +117,7 @@ def unregister():
|
|||||||
if not bpy.app.background:
|
if not bpy.app.background:
|
||||||
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
|
||||||
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)
|
||||||
|
|||||||
@@ -2372,13 +2372,18 @@ class MeasureTool(bpy.types.Operator, PolylineOperator):
|
|||||||
bl_label = "Measure Tool"
|
bl_label = "Measure Tool"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
measure_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.space_data.type == "VIEW_3D"
|
return context.space_data.type == "VIEW_3D"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
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.input_options = ["D", "A", "X", "Y", "Z"]
|
||||||
self.instructions = """TAB: Cycle Input
|
self.instructions = """TAB: Cycle Input
|
||||||
D: Distance Input
|
D: Distance Input
|
||||||
@@ -2405,11 +2410,15 @@ class MeasureTool(bpy.types.Operator, PolylineOperator):
|
|||||||
|
|
||||||
self.handle_snap_selection(context, event)
|
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 (
|
if (
|
||||||
not self.tool_state.is_input_on
|
not self.tool_state.is_input_on
|
||||||
and event.value == "RELEASE"
|
and event.value == "RELEASE"
|
||||||
and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"}
|
and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"}
|
||||||
):
|
) or single_mode:
|
||||||
context.workspace.status_text_set(text=None)
|
context.workspace.status_text_set(text=None)
|
||||||
PolylineDecorator.uninstall()
|
PolylineDecorator.uninstall()
|
||||||
tool.Snap.move_polyline_to_measure()
|
tool.Snap.move_polyline_to_measure()
|
||||||
@@ -2421,6 +2430,7 @@ class MeasureTool(bpy.types.Operator, PolylineOperator):
|
|||||||
self.handle_keyboard_input(context, event)
|
self.handle_keyboard_input(context, event)
|
||||||
|
|
||||||
self.handle_inserting_polyline(context, event)
|
self.handle_inserting_polyline(context, event)
|
||||||
|
tool.Polyline.calculate_area(context, self.input_ui)
|
||||||
|
|
||||||
if event.type == "E":
|
if event.type == "E":
|
||||||
context.scene.BIMPolylineProperties.measure_polyline.clear()
|
context.scene.BIMPolylineProperties.measure_polyline.clear()
|
||||||
|
|||||||
@@ -229,3 +229,13 @@ class BIMProjectProperties(PropertyGroup):
|
|||||||
|
|
||||||
def get_library_element_index(self, lib_element):
|
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))
|
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")
|
||||||
|
|||||||
@@ -58,9 +58,16 @@ class ExploreTool(bpy.types.WorkSpaceTool):
|
|||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.label(text="", icon="EVENT_ALT")
|
row.label(text="", icon="EVENT_ALT")
|
||||||
row.label(text="Disable Culling" if LinksData.enable_culling else "Enable Culling", icon="EVENT_C")
|
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 = layout.row(align=True)
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
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):
|
class ExploreHotkey(bpy.types.Operator):
|
||||||
@@ -94,4 +101,7 @@ class ExploreHotkey(bpy.types.Operator):
|
|||||||
bpy.ops.bim.enable_culling("INVOKE_DEFAULT")
|
bpy.ops.bim.enable_culling("INVOKE_DEFAULT")
|
||||||
|
|
||||||
def hotkey_S_M(self):
|
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)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class Polyline(bonsai.core.tool.Polyline):
|
|||||||
if self.init_z:
|
if self.init_z:
|
||||||
self._Z = ""
|
self._Z = ""
|
||||||
if self.init_area:
|
if self.init_area:
|
||||||
self._AREA = ""
|
self._AREA = "0"
|
||||||
|
|
||||||
def set_value(self, attribute_name, value):
|
def set_value(self, attribute_name, value):
|
||||||
value = str(value)
|
value = str(value)
|
||||||
@@ -222,8 +222,8 @@ class Polyline(bonsai.core.tool.Polyline):
|
|||||||
else:
|
else:
|
||||||
area = 0
|
area = 0
|
||||||
|
|
||||||
if input_ui.get_text_value("A") is not None:
|
if input_ui.get_text_value("AREA") is not None:
|
||||||
input_ui.set_value("A", area)
|
input_ui.set_value("AREA", area)
|
||||||
return
|
return
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
Reference in New Issue
Block a user