WIP - Polyline tool refactor

This commit is contained in:
Bruno Perdigão
2024-09-07 15:23:43 -03:00
committed by Bruno Perdigão
parent 2e192034f8
commit be8b325a83
4 changed files with 116 additions and 110 deletions
+24 -24
View File
@@ -301,7 +301,8 @@ class ProfileDecorator:
class PolylineDecorator:
is_installed = False
handlers = []
mouse_pos = None
event = None
input_type = None
input_ui = None
angle_snap_mat = None
@@ -333,30 +334,27 @@ class PolylineDecorator:
cls.is_installed = False
@classmethod
def set_mouse_position(cls, event):
cls.mouse_pos = event.mouse_region_x, event.mouse_region_y
def update(cls, event, tool_state, input_ui):
cls.event = event
cls.tool_state = tool_state
cls.input_ui = input_ui
@classmethod
def set_input_ui(cls, input_ui, input_type):
def set_input_ui(cls, input_ui):
cls.input_ui = input_ui
cls.input_type = input_type
@classmethod
def set_angle_axis_line(cls, start, end):
cls.axis_start = start
cls.axis_end = end
@classmethod
def set_axis_rectangle(cls, corners):
cls.axis_rectangle = [*corners]
# @classmethod
# def set_axis_rectangle(cls, corners):
# cls.axis_rectangle = [*corners]
@classmethod
def set_use_default_container(cls, value=False):
cls.use_default_container = value
@classmethod
def set_instructions(cls, instructions):
cls.instructions = instructions
def set_tool_state(cls, tool_state):
cls.tool_state = tool_state
@classmethod
def set_snap_info(cls, snap_info):
@@ -379,6 +377,8 @@ class PolylineDecorator:
"Z": "Z coord:",
"AREA": "Area: ",
}
mouse_pos = self.event.mouse_region_x, self.event.mouse_region_y
self.addon_prefs = tool.Blender.get_addon_preferences()
self.font_id = 0
@@ -392,18 +392,18 @@ class PolylineDecorator:
new_line = 20
for i, (key, field_name) in enumerate(texts.items()):
if key != self.input_type:
if key != self.tool_state.input_type:
formatted_value = self.input_ui.get_formatted_value(key)
else:
formatted_value = self.input_ui.get_text_value(key)
if formatted_value is None:
continue
if key == self.input_type:
if key == self.tool_state.input_type:
blf.color(self.font_id, *color_highlight)
else:
blf.color(self.font_id, *color)
blf.position(self.font_id, self.mouse_pos[0] + offset, self.mouse_pos[1] - (new_line * i), 0)
blf.position(self.font_id, mouse_pos[0] + offset, mouse_pos[1] - (new_line * i), 0)
blf.draw(self.font_id, field_name + formatted_value)
@@ -452,10 +452,10 @@ class PolylineDecorator:
color = self.addon_prefs.decorations_colour
blf.color(self.font_id, *color)
text_w, text_h = blf.dimensions(0, self.instructions)
text_w, text_h = blf.dimensions(0, self.tool_state.instructions)
position = (region.width / 2) - (text_w / 2)
blf.position(self.font_id, position, 10, 0)
blf.draw(self.font_id, self.instructions)
blf.draw(self.font_id, self.tool_state.instructions)
text_w, text_h = blf.dimensions(0, self.snap_info)
position = (region.width / 2) - (text_w / 2)
@@ -494,7 +494,7 @@ class PolylineDecorator:
default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
projection_point = []
if self.use_default_container:
if self.tool_state.use_default_container:
# When a point is above the plane it projects the point
# to the plane and creates a line
if snap_prop.snap_type != "Plane" and snap_prop.z != 0:
@@ -520,10 +520,10 @@ class PolylineDecorator:
self.line_shader.uniform_float("lineWidth", 0.75)
self.draw_batch("LINES", [self.axis_start, self.axis_end], decorator_color_unselected, [(0, 1)])
try:
self.draw_batch("TRIS", self.axis_rectangle, (1, 1, 1, 0.1), [(0, 1, 3), (0, 2, 3)])
except:
pass
# 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
+40 -26
View File
@@ -325,6 +325,7 @@ class DrawPolylineWall(bpy.types.Operator):
self.is_input_on = False
self.input_options = ["D", "A", "X", "Y"]
self.input_type = None
self.input_type = None
self.input_value_xy = [None, None]
self.input_ui = tool.Polyline.create_input_ui()
self.is_typing = False
@@ -337,6 +338,7 @@ class DrawPolylineWall(bpy.types.Operator):
X Y: Axis
Shift: Lock axis
"""
self.tool_state = tool.Polyline.create_tool_state()
def recalculate_inputs(self, context):
if self.number_input:
@@ -347,10 +349,10 @@ class DrawPolylineWall(bpy.types.Operator):
return is_valid
else:
if self.input_type in {"X", "Y"}:
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
elif self.input_type in {"D", "A"}:
tool.Polyline.calculate_x_y_and_z(context, self.input_ui)
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
tool.Polyline.calculate_x_y_and_z(context, self.input_ui, self.tool_state)
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
else:
self.input_ui.set_value(self.input_type, self.number_output)
tool.Blender.update_viewport()
@@ -388,8 +390,10 @@ class DrawPolylineWall(bpy.types.Operator):
if event.type == "MOUSEMOVE" or event.type == "INBETWEEN_MOUSEMOVE":
self.mousemove_count += 1
self.is_input_on = False
self.tool_state.is_input_on = False
self.input_type = None
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
self.tool_state.input_type = None
PolylineDecorator.update(event, self.tool_state, self.input_ui)
tool.Snap.clear_snapping_ref()
tool.Blender.update_viewport()
else:
@@ -401,10 +405,9 @@ class DrawPolylineWall(bpy.types.Operator):
self.objs_2d_bbox.append(tool.Raycast.get_on_screen_2d_bounding_boxes(context, obj))
if self.mousemove_count > 3:
detected_snaps = tool.Snap.detect_snapping_points(context, event, self.objs_2d_bbox)
detected_snaps = tool.Snap.detect_snapping_points(context, event, self.objs_2d_bbox, self.tool_state)
self.snapping_points = tool.Snap.select_snapping_points(context, event, detected_snaps)
PolylineDecorator.set_mouse_position(event)
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
tool.Blender.update_viewport()
return {"RUNNING_MODAL"}
@@ -426,7 +429,7 @@ class DrawPolylineWall(bpy.types.Operator):
if event.value == "PRESS" and event.type == "C":
tool.Snap.close_polyline()
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
PolylineDecorator.update(event, self.tool_state, self.input_ui)
tool.Blender.update_viewport()
if self.is_input_on and event.value == "PRESS" and event.type == "TAB":
@@ -434,39 +437,48 @@ class DrawPolylineWall(bpy.types.Operator):
index = self.input_options.index(self.input_type)
size = len(self.input_options)
self.input_type = self.input_options[((index + 1) % size)]
self.tool_state.input_type = self.input_options[((index + 1) % size)]
self.is_typing = False
self.number_input = self.input_ui.get_formatted_value(self.input_type)
self.number_input = list(self.number_input)
self.number_output = "".join(self.number_input)
self.input_ui.set_value(self.input_type, self.number_output)
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
PolylineDecorator.update(event, self.tool_state, self.input_ui)
tool.Blender.update_viewport()
if not self.is_input_on and event.value == "RELEASE" and event.type == "TAB":
self.recalculate_inputs(context)
self.is_input_on = True
self.tool_state.is_input_on = True
self.input_type = "D"
self.tool_state.input_type = "D"
self.is_typing = False
self.number_input = self.input_ui.get_formatted_value(self.input_type)
self.number_input = list(self.number_input)
self.number_output = "".join(self.number_input)
self.input_ui.set_value(self.input_type, self.number_output)
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
PolylineDecorator.update(event, self.tool_state, self.input_ui)
tool.Blender.update_viewport()
if not self.is_input_on and event.ascii in self.number_options:
self.recalculate_inputs(context)
self.is_input_on = True
self.tool_state.is_input_on = True
self.input_type = "D"
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
self.tool_state.input_type = "D"
# PolylineDecorator.set_input_ui(self.input_ui)
# PolylineDecorator.set_tool_state(self.tool_state)
PolylineDecorator.update(event, self.tool_state, self.input_ui)
tool.Blender.update_viewport()
if event.value == "RELEASE" and event.type in {"D", "A"}:
self.recalculate_inputs(context)
self.is_input_on = True
self.tool_state.is_input_on = True
self.input_type = event.type
self.tool_state.input_type = event.type
self.input_ui.set_value(self.input_type, "")
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
PolylineDecorator.update(event, self.tool_state, self.input_ui)
tool.Blender.update_viewport()
if self.input_type in self.input_options:
@@ -493,7 +505,7 @@ class DrawPolylineWall(bpy.types.Operator):
self.is_typing = True
self.number_output = "".join(self.number_input)
self.input_ui.set_value(self.input_type, self.number_output)
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
PolylineDecorator.update(event, self.tool_state, self.input_ui)
tool.Blender.update_viewport()
if not self.is_input_on and event.value == "RELEASE" and event.type in {"RET", "NUMPAD_ENTER", "RIGHTMOUSE"}:
@@ -508,16 +520,18 @@ class DrawPolylineWall(bpy.types.Operator):
if is_valid:
tool.Snap.insert_polyline_point(self.input_ui)
self.is_input_on = False
self.tool_state.is_input_on = False
self.input_type = None
self.tool_state.input_type = None
self.number_input = []
self.number_output = ""
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
PolylineDecorator.update(event, self.tool_state, self.input_ui)
tool.Blender.update_viewport()
if event.value == "PRESS" and event.type == "M":
self.snapping_points = tool.Snap.modify_snapping_point_selection(self.snapping_points)
PolylineDecorator.set_mouse_position(event)
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
PolylineDecorator.update(event, self.tool_state, self.input_ui)
tool.Blender.update_viewport()
if event.type in {"MIDDLEMOUSE", "WHEELUPMOUSE", "WHEELDOWNMOUSE"}:
@@ -527,8 +541,10 @@ class DrawPolylineWall(bpy.types.Operator):
if event.value == "RELEASE" and event.type in {"ESC"}:
self.recalculate_inputs(context)
self.is_input_on = False
self.tool_state.is_input_on = False
self.input_type = None
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
self.tool_state.input_type = None
PolylineDecorator.update(event, self.tool_state, self.input_ui)
tool.Blender.update_viewport()
else:
if event.value == "RELEASE" and event.type in {"ESC"}:
@@ -544,21 +560,19 @@ class DrawPolylineWall(bpy.types.Operator):
if context.space_data.type == "VIEW_3D":
PolylineDecorator.install(context)
tool.Snap.clear_snapping_point()
tool.Snap.set_use_default_container(True)
tool.Snap.clear_snap_angle()
PolylineDecorator.set_use_default_container(True)
tool.Polyline.set_use_default_container(True)
self.tool_state.use_default_container = True
tool.Snap.set_tool_state(self.tool_state)
tool.Snap.set_snap_axis_method(None)
tool.Snap.set_snap_plane_method("XY")
PolylineDecorator.set_instructions(self.instructions)
PolylineDecorator.set_input_ui(self.input_ui, self.input_type)
PolylineDecorator.update(event, self.tool_state, self.input_ui)
self.visible_objs = tool.Raycast.get_visible_objects(context)
for obj in self.visible_objs:
self.objs_2d_bbox.append(tool.Raycast.get_on_screen_2d_bounding_boxes(context, obj))
detected_snaps = tool.Snap.detect_snapping_points(context, event, self.objs_2d_bbox)
detected_snaps = tool.Snap.detect_snapping_points(context, event, self.objs_2d_bbox, self.tool_state)
self.snapping_points = tool.Snap.select_snapping_points(context, event, detected_snaps)
PolylineDecorator.set_mouse_position(event)
tool.Polyline.calculate_distance_and_angle(context, self.is_input_on, self.input_ui)
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
tool.Blender.update_viewport()
context.window_manager.modal_handler_add(self)
+28 -34
View File
@@ -83,6 +83,26 @@ class PolylineUI:
return format_distance(value * factor, precision=precision, suppress_zero_inches=True, in_unit_length=True)
@dataclass
class ToolState:
use_default_container: bool = None
snap_angle: float = None
is_input_on: bool = None
# angle_axis_start: Vector
# angle_axis_end: Vector
axis_method: str = None
plane_method: str = None
instructions: str = """TAB: Cycle Input
M: Modify Snap Point
C: Close
Backspace: Remove
X Y: Axis
Shift: Lock axis
"""
snap_info: str = None
# input_state: str = None | "Select" | "Edit"
input_type: str = None
class Polyline(bonsai.core.tool.Polyline):
@@ -91,37 +111,11 @@ class Polyline(bonsai.core.tool.Polyline):
return PolylineUI(init_z=init_z, init_area=init_area)
@classmethod
def set_mouse_position(cls, event):
cls.mouse_pos = event.mouse_region_x, event.mouse_region_y
def create_tool_state(cls):
return ToolState()
@classmethod
def set_angle_axis_line(cls, start, end):
cls.axis_start = start
cls.axis_end = end
@classmethod
def set_axis_rectangle(cls, corners):
cls.axis_rectangle = [*corners]
@classmethod
def set_use_default_container(cls, value=False):
cls.use_default_container = value
@classmethod
def set_plane(cls, plane_origin, plane_normal):
cls.plane_origin = plane_origin
cls.plane_normal = plane_normal
@classmethod
def set_instructions(cls, instructions):
cls.instructions = instructions
@classmethod
def set_snap_info(cls, snap_info):
cls.snap_info = snap_info
@classmethod
def calculate_distance_and_angle(cls, context, is_input_on, input_ui):
def calculate_distance_and_angle(cls, context, input_ui, tool_state):
try:
polyline_data = context.scene.BIMModelProperties.polyline_point
@@ -138,8 +132,8 @@ class Polyline(bonsai.core.tool.Polyline):
else:
last_point = Vector((0, 0, 0))
if is_input_on:
if cls.use_default_container:
if tool_state.is_input_on:
if tool_state.use_default_container:
snap_vector = Vector(
(input_ui.get_number_value("X"), input_ui.get_number_value("Y"), default_container_elevation)
)
@@ -148,7 +142,7 @@ class Polyline(bonsai.core.tool.Polyline):
(input_ui.get_number_value("X"), input_ui.get_number_value("Y"), input_ui.get_number_value("Z"))
)
else:
if cls.use_default_container:
if tool_state.use_default_container:
snap_vector = Vector((snap_prop.x, snap_prop.y, default_container_elevation))
else:
snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
@@ -229,7 +223,7 @@ class Polyline(bonsai.core.tool.Polyline):
return
@classmethod
def calculate_x_y_and_z(cls, context, input_ui):
def calculate_x_y_and_z(cls, context, input_ui, tool_state):
try:
polyline_data = context.scene.BIMModelProperties.polyline_point
default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
@@ -242,7 +236,7 @@ class Polyline(bonsai.core.tool.Polyline):
snap_prop = context.scene.BIMModelProperties.snap_mouse_point[0]
snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
if cls.use_default_container:
if tool_state.use_default_container:
snap_vector = Vector((snap_prop.x, snap_prop.y, default_container_elevation))
else:
snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
+24 -26
View File
@@ -27,15 +27,14 @@ from lark import Lark, Transformer
class Snap(bonsai.core.tool.Snap):
mouse_pos = None
snap_angle = None
use_default_container = False
tool_state = None
snap_plane_method = None
snap_axis_method = None
@classmethod
def set_use_default_container(cls, value=True):
cls.use_default_container = value
def set_tool_state(cls, tool_state):
cls.tool_state = tool_state
@classmethod
def set_snap_plane_method(cls, value=True):
@@ -145,7 +144,7 @@ class Snap(bonsai.core.tool.Snap):
a = input_ui.get_formatted_value("A")
snap_vertex = bpy.context.scene.BIMModelProperties.snap_mouse_point[0]
if cls.use_default_container:
if cls.tool_state.use_default_container:
z = tool.Ifc.get_object(tool.Root.get_default_container()).location.z
if x is None and y is None:
@@ -289,11 +288,10 @@ class Snap(bonsai.core.tool.Snap):
return sorted_intersections[0], "Mix"
@classmethod
def detect_snapping_points(cls, context, event, objs_2d_bbox):
region = context.region
def detect_snapping_points(cls, context, event, objs_2d_bbox, tool_state):
rv3d = context.region_data
space = context.space_data
cls.mouse_pos = event.mouse_region_x, event.mouse_region_y
mouse_pos = event.mouse_region_x, event.mouse_region_y
detected_snaps = []
snap_threshold = 0.3
@@ -322,7 +320,7 @@ class Snap(bonsai.core.tool.Snap):
plane_normal = view_direction.normalized()
if cls.snap_plane_method == "XY" or (not cls.snap_plane_method and cls.snap_axis_method in {"X", "Y"}):
if cls.use_default_container:
if cls.tool_state.use_default_container:
plane_origin = Vector((0, 0, elevation))
elif not last_polyline_point:
plane_origin = Vector((0, 0, 0))
@@ -342,7 +340,7 @@ class Snap(bonsai.core.tool.Snap):
return plane_origin, plane_normal
def cast_rays_and_get_best_object(objs_to_raycast):
def cast_rays_and_get_best_object(objs_to_raycast, mouse_pos):
best_length_squared = 1.0
best_obj = None
best_hit = None
@@ -352,13 +350,13 @@ class Snap(bonsai.core.tool.Snap):
hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj)
if hit is None:
# Tried original mouse position. Now it will try the offsets.
original_mouse_pos = cls.mouse_pos
original_mouse_pos = mouse_pos
for value in mouse_offset:
cls.mouse_pos = tuple(x + y for x, y in zip(original_mouse_pos, value))
hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj, cls.mouse_pos)
mouse_pos = tuple(x + y for x, y in zip(original_mouse_pos, value))
hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj, mouse_pos)
if hit:
break
cls.mouse_pos = original_mouse_pos
mouse_pos = original_mouse_pos
if hit is not None:
hit_world = obj.original.matrix_world @ hit
@@ -380,14 +378,14 @@ class Snap(bonsai.core.tool.Snap):
objs_to_raycast = []
for obj, bbox_2d in objs_2d_bbox:
if obj.type == "MESH" and bbox_2d:
if tool.Raycast.intersect_mouse_2d_bounding_box(cls.mouse_pos, bbox_2d, offset):
if tool.Raycast.intersect_mouse_2d_bounding_box(mouse_pos, bbox_2d, offset):
if space.local_view:
if obj.local_view_get(context.space_data):
objs_to_raycast.append(obj)
else:
objs_to_raycast.append(obj)
# Obj
snap_obj, hit, face_index = cast_rays_and_get_best_object(objs_to_raycast)
snap_obj, hit, face_index = cast_rays_and_get_best_object(objs_to_raycast, mouse_pos)
if hit is not None:
detected_snaps.append({"Object": (snap_obj, hit, face_index)})
@@ -423,29 +421,29 @@ class Snap(bonsai.core.tool.Snap):
rot_intersection = None
if not cls.snap_plane_method:
if cls.snap_axis_method == "X":
cls.snap_angle = 180
tool_state.snap_angle = 180
if cls.snap_axis_method == "Y":
cls.snap_angle = 90
tool_state.snap_angle = 90
if cls.snap_axis_method == "Z":
cls.snap_angle = 90
tool_state.snap_angle = 90
if cls.snap_axis_method:
# Doesn't update snap_angle so that it keeps in the same axis
rot_intersection, _, axis_start, axis_end = cls.snap_on_axis(intersection, cls.snap_angle)
rot_intersection, _, axis_start, axis_end = cls.snap_on_axis(intersection, tool_state.snap_angle)
if cls.snap_plane_method:
if cls.snap_plane_method in {"XY", "XZ"} and cls.snap_axis_method == "X":
cls.snap_angle = 180
tool_state.snap_angle = 180
if cls.snap_plane_method in {"XY", "YZ"} and cls.snap_axis_method == "Y":
cls.snap_angle = 90
tool_state.snap_angle = 90
if cls.snap_plane_method in {"YZ"} and cls.snap_axis_method == "Z":
cls.snap_angle = 180
tool_state.snap_angle = 180
if cls.snap_plane_method in {"XZ"} and cls.snap_axis_method == "Z":
cls.snap_angle = 90
tool_state.snap_angle = 90
if event.shift or cls.snap_axis_method:
# Doesn't update snap_angle so that it keeps in the same axis
rot_intersection, _, axis_start, axis_end = cls.snap_on_axis(intersection, cls.snap_angle)
rot_intersection, _, axis_start, axis_end = cls.snap_on_axis(intersection, tool_state.snap_angle)
else:
rot_intersection, cls.snap_angle, axis_start, axis_end = cls.snap_on_axis(intersection, None)
rot_intersection, tool_state.snap_angle, axis_start, axis_end = cls.snap_on_axis(intersection, None)
if rot_intersection:
detected_snaps.append({"Axis": (rot_intersection, axis_start, axis_end)})