diff --git a/main.py b/main.py index b91fffd..c0610c1 100644 --- a/main.py +++ b/main.py @@ -29,7 +29,13 @@ ] # Configure vectors to draw (list of (start_index, end_index, color, label) using 0-based indices) # Edit this list to add/remove vectors to display -VECTORS = [(9, 8, "g", "v1"), (4, 5, "r", "v2"), (4, 14, "b", "v3"), (0, 4, "m", "v4")] +VECTORS = [ + (9, 8, "g", "v1"), + (4, 5, "r", "v2"), + (4, 14, "b", "v3"), + (0, 4, "m", "v4"), + (9, 15, "c", "v1'"), +] # Line width for vector arrows VECTOR_LW = 4 VECTOR_LEN = 20.0 # Length of vector arrows (can be adjusted as needed) @@ -121,6 +127,7 @@ name = cell.split("New Subject:")[1].strip() point_names.append(name) point_names.append("P15") # Add name for the computed point + point_names.append("P16") # Add name for the computed point if col > 2: frame_number = int(row[0]) @@ -144,6 +151,15 @@ ) points.append(p15) # Add the computed point as the 15th point + # Compute the 16th point based on projection of vec1 to the plane defined by vec2 and vec3 + v1 = vector(points[VECTORS[0][0]], points[VECTORS[0][1]]) + v4 = vector(points[VECTORS[3][0]], points[VECTORS[3][1]]) + proj_l = dot(v1, v4) / dot(v4, v4) if dot(v4, v4) > 1e-9 else 0 + v1_proj_v4 = (proj_l * v4[0], proj_l * v4[1], proj_l * v4[2]) + v5 = (v1[0] - v1_proj_v4[0], v1[1] - v1_proj_v4[1], v1[2] - v1_proj_v4[2]) + p16 = (points[9][0] + v5[0], points[9][1] + v5[1], points[9][2] + v5[2]) + points.append(p16) # Add the computed point as the 16th point + data_list.append(ThumbFrameData(frame_number, points)) return point_names, data_list @@ -154,9 +170,9 @@ pts: List[Tuple[float, float, float]], vec1: int, vec2: int ) -> str: - hstr = f"Angle v{vec1 + 1}-v{vec2 + 1} " + hstr = f"Angle {VECTORS[vec1][3]}-{VECTORS[vec2][3]} " if vec1 >= len(VECTORS) or vec2 >= len(VECTORS): - return hstr + "N/A" + return hstr + ": N/A" (a1, b1, _, _) = VECTORS[vec1] (a2, b2, _, _) = VECTORS[vec2] p1s, p1e, p2s, p2e = pts[a1], pts[b1], pts[a2], pts[b2] @@ -170,7 +186,7 @@ cos_val = max(-1.0, min(1.0, dot / (norm1 * norm2))) angle_rad = math.acos(cos_val) angle_deg = math.degrees(angle_rad) - return hstr + f"{angle_deg:.2f} deg" + return hstr + f"= {angle_deg:.2f} deg" # Function to visualize thumb data with a slider @@ -253,9 +269,11 @@ ) arrow_labels.append(lbl) - # Angle display between v1 and v2 (figure-relative text) + # Angle display angle_text1 = fig.text(0.02, 0.95, "", fontsize=12, weight="bold") angle_text1.set_text(compute_and_format_angle(points, 0, 1)) + angle_text2 = fig.text(0.02, 0.92, "", fontsize=12, weight="bold") + angle_text2.set_text(compute_and_format_angle(points, 4, 1)) ax.set_xlabel("X") ax.set_ylabel("Y") @@ -296,8 +314,9 @@ arrows[i_pair]._verts3d = arrow_pts arrow_labels[i_pair].set_position(mid_pts) - # Update angle text between v1 and v2 + # Update angle text angle_text1.set_text(compute_and_format_angle(points, 0, 1)) + angle_text2.set_text(compute_and_format_angle(points, 4, 1)) ax.set_title(f"Frame {data[frame_idx].frame_number}") fig.canvas.draw_idle() @@ -318,28 +337,28 @@ ) ax.view_init(elev=elev, azim=azim) - # # Calculate roll so that vector from points[4] to points[5] points left - # # Target left direction: from points[4] to points[5] - # target_left = normalize(vector(points[14], points[4])) - # # Up vector (Z-axis) - # up = (0, 0, 1) - # # Current right direction: forward × up - # right = normalize(OuterProduct(forward, up)) - # # Current left direction: up × right - # left = OuterProduct(up, right) - # # Calculate roll angle between current left and target left - # cos_roll = ( - # left[0] * target_left[0] - # + left[1] * target_left[1] - # + left[2] * target_left[2] - # ) - # sin_roll = ( - # right[0] * target_left[0] - # + right[1] * target_left[1] - # + right[2] * target_left[2] - # ) - # roll = math.degrees(math.atan2(sin_roll, cos_roll)) - # ax.view_init(elev=elev, azim=azim, roll=roll) + # Calculate roll so that vector from points[4] to points[5] points left + # Target left direction: from points[4] to points[5] + target_left = normalize(vector(points[14], points[4])) + # Up vector (Z-axis) + up = (0, 0, 1) + # Current right direction: forward × up + right = normalize(outer_product(forward, up)) + # Current left direction: up × right + left = outer_product(up, right) + # Calculate roll angle between current left and target left + cos_roll = ( + left[0] * target_left[0] + + left[1] * target_left[1] + + left[2] * target_left[2] + ) + sin_roll = ( + right[0] * target_left[0] + + right[1] * target_left[1] + + right[2] * target_left[2] + ) + roll = math.degrees(math.atan2(sin_roll, cos_roll)) + ax.view_init(elev=elev, azim=azim, roll=roll) def btn_top_on_clicked(event): set_top_view(data[int(slider.val) - 1].points)