mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +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()
|
||||
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"]:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user