Measure tool - single and area measurement now persist on screen.

Also, the measure polyline properties now stores the area and the total length that will be used by the decorator in later development.
This commit is contained in:
Bruno Perdigão
2024-10-04 17:10:13 -03:00
parent 1a8c11c715
commit b057f89459
6 changed files with 134 additions and 33 deletions
@@ -573,7 +573,7 @@ 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 = self.input_ui.format_input_ui_units(context, value)
formatted_value = tool.Polyline.format_input_ui_units(context, value)
text = f"{prefix}: {formatted_value}"
text_length = blf.dimensions(self.font_id, text)
self.draw_text_background(context, dim_text_coords, text_length)
+3 -1
View File
@@ -751,8 +751,10 @@ class PolylinePoint(PropertyGroup):
class MeasurePolyline(PropertyGroup):
polyline_point: bpy.props.CollectionProperty(type=PolylinePoint)
polyline_points: bpy.props.CollectionProperty(type=PolylinePoint)
measurement_type: bpy.props.StringProperty(name="Measurement Type")
area: bpy.props.StringProperty(name="Measured Area")
total_length: bpy.props.StringProperty(name="Total Length")
class BIMPolylineProperties(PropertyGroup):
@@ -42,6 +42,11 @@ def toggle_decorations_on_load(*args):
# as queried object is linked from separate .blend file.
def transparent_color(color, alpha=0.1):
color = [i for i in color]
color[3] = alpha
return color
class ProjectDecorator:
installed = None
@@ -239,6 +244,45 @@ class MeasureDecorator:
shader.uniform_float("color", color)
batch.draw(shader)
def calculate_measurement_x_y_and_z(self, context, measurement_prop):
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_text_background(self, context, coords_dim, text_dim):
padding = 5
theme = context.preferences.themes.items()[0][1]
@@ -255,6 +299,7 @@ class MeasureDecorator:
def draw_measurements(self, context):
region = context.region
rv3d = region.data
self.addon_prefs = tool.Blender.get_addon_preferences()
self.font_id = 1
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
@@ -263,36 +308,56 @@ class MeasureDecorator:
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)
blf.color(self.font_id, *color)
measure_data = context.scene.BIMPolylineProperties.measurement_polyline
for data in measure_data:
measurement_prop = data.polyline_point
measurement_prop = data.polyline_points
measure_type = data.measurement_type
for i in range(len(measurement_prop)):
if i == 0:
continue
pos_dim = (Vector(measurement_prop[i].position) + Vector(measurement_prop[i - 1].position)) / 2
coords_dim = view3d_utils.location_3d_to_region_2d(region, rv3d, pos_dim)
dim_text_pos = (Vector(measurement_prop[i].position) + Vector(measurement_prop[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
blf.position(self.font_id, coords_dim[0], coords_dim[1], 0)
blf.position(self.font_id, dim_text_coords[0], dim_text_coords[1], 0)
text = "d: " + formatted_value
text_dim = blf.dimensions(self.font_id, text)
self.draw_text_background(context, coords_dim, text_dim)
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 i == 1:
continue
pos_angle = measurement_prop[i - 1].position
coords_angle = view3d_utils.location_3d_to_region_2d(region, rv3d, pos_angle)
blf.position(self.font_id, coords_angle[0], coords_angle[1], 0)
angle_text_pos = measurement_prop[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_dim = blf.dimensions(self.font_id, text)
self.draw_text_background(context, coords_angle, text_dim)
text_length = blf.dimensions(self.font_id, text)
self.draw_text_background(context, angle_text_coords, text_length)
blf.draw(self.font_id, text)
blf.disable(self.font_id, blf.SHADOW)
if measure_type == "SINGLE":
axis_line, axis_line_center = self.calculate_measurement_x_y_and_z(context, measurement_prop)
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)
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(context, 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)
blf.disable(self.font_id, blf.SHADOW)
def __call__(self, context):
@@ -321,7 +386,7 @@ class MeasureDecorator:
measure_data = context.scene.BIMPolylineProperties.measurement_polyline
for data in measure_data:
polyline_data = data.polyline_point
polyline_data = data.polyline_points
polyline_points = []
polyline_edges = []
@@ -332,6 +397,22 @@ class MeasureDecorator:
for i in range(len(polyline_points) - 1):
polyline_edges.append([i, i + 1])
# Lines for X, Y, Z of single measure
measure_type = data.measurement_type
if measure_type == "SINGLE":
axis, _ = self.calculate_measurement_x_y_and_z(context, polyline_data)
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
if area:
if float(area) > 0:
tris = self.calculate_polygon(polyline_points)
self.draw_batch("TRIS", polyline_points, 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)
@@ -2421,7 +2421,7 @@ class MeasureTool(bpy.types.Operator, PolylineOperator):
) or single_mode:
context.workspace.status_text_set(text=None)
PolylineDecorator.uninstall()
tool.Snap.move_polyline_to_measure()
tool.Snap.move_polyline_to_measure(context, self.input_ui)
tool.Snap.clear_polyline()
MeasureDecorator.install(context)
tool.Blender.update_viewport()
+15 -13
View File
@@ -71,20 +71,8 @@ class Polyline(bonsai.core.tool.Polyline):
value = float(self.get_text_value(attribute_name))
return f"{value:.2f}"
else:
return self.format_input_ui_units(context, value)
return Polyline.format_input_ui_units(context, value)
def format_input_ui_units(cls, context, value):
unit_system = tool.Drawing.get_unit_system()
if unit_system == "IMPERIAL":
precision = context.scene.DocProperties.imperial_precision
factor = 3.28084
else:
precision = None
factor = 1
if context.scene.unit_settings.length_unit == "MILLIMETERS":
factor = 1000
return format_distance(value * factor, precision=precision, suppress_zero_inches=True, in_unit_length=True)
@dataclass
class ToolState:
@@ -500,3 +488,17 @@ class Polyline(bonsai.core.tool.Polyline):
return True, str(result)
except:
return False, "0"
@classmethod
def format_input_ui_units(cls, context, value):
unit_system = tool.Drawing.get_unit_system()
if unit_system == "IMPERIAL":
precision = context.scene.DocProperties.imperial_precision
factor = 3.28084
else:
precision = None
factor = 1
if context.scene.unit_settings.length_unit == "MILLIMETERS":
factor = 1000
return format_distance(value * factor, precision=precision, suppress_zero_inches=True, in_unit_length=True)
+19 -3
View File
@@ -157,13 +157,13 @@ class Snap(bonsai.core.tool.Snap):
polyline_data.remove(len(polyline_data) - 1)
@classmethod
def move_polyline_to_measure(cls):
def move_polyline_to_measure(cls, context, input_ui):
polyline_data = bpy.context.scene.BIMPolylineProperties.polyline_points
measurement_data = bpy.context.scene.BIMPolylineProperties.measurement_polyline.add()
measurement_type = bpy.context.scene.MeasureToolSettings.measure_type
measurement_data.measurement_type = measurement_type
for point in polyline_data:
measurement_point = measurement_data.polyline_point.add()
measurement_point = measurement_data.polyline_points.add()
measurement_point.x = point.x
measurement_point.y = point.y
measurement_point.z = point.z
@@ -171,6 +171,22 @@ class Snap(bonsai.core.tool.Snap):
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
@classmethod
def snap_on_axis(cls, intersection, tool_state, lock_angle=False):
def create_axis_line_data(rot_mat, origin):
@@ -396,7 +412,7 @@ class Snap(bonsai.core.tool.Snap):
# Measure
measure_data = context.scene.BIMPolylineProperties.measurement_polyline
for measure in measure_data:
measure_points = measure.polyline_point
measure_points = measure.polyline_points
snap_points = tool.Raycast.ray_cast_to_measure(context, event, measure_points)
if snap_points:
detected_snaps.append({"Polyline": snap_points})