Compare commits
3 Commits
0570256f65
...
b123b12d0d
Author | SHA1 | Date | |
---|---|---|---|
b123b12d0d | |||
1bd935646e | |||
c3e0088a60 |
@@ -510,7 +510,7 @@ class VideoEditor:
|
||||
CROP_SIZE_STEP = 15 # pixels to expand/contract crop
|
||||
|
||||
# Motion tracking settings
|
||||
TRACKING_POINT_THRESHOLD = 20 # pixels for delete/snap radius
|
||||
TRACKING_POINT_THRESHOLD = 10 # pixels for delete/snap radius
|
||||
|
||||
# Seek frame counts
|
||||
SEEK_FRAMES_CTRL = 60 # Ctrl modifier: 60 frames
|
||||
@@ -2033,35 +2033,42 @@ class VideoEditor:
|
||||
prev_result = self._get_previous_tracking_point()
|
||||
next_result = self._get_next_tracking_point()
|
||||
|
||||
# Draw motion path if we have both previous and next points
|
||||
if prev_result and next_result:
|
||||
prev_frame, prev_pts = prev_result
|
||||
next_frame, next_pts = next_result
|
||||
# Draw motion path - either previous→current OR previous→next
|
||||
line_to_draw = None
|
||||
if prev_result and self.current_frame in self.tracking_points:
|
||||
# Draw previous→current line (we're on a frame with tracking points)
|
||||
line_to_draw = ("prev_current", prev_result, (self.current_frame, self.tracking_points[self.current_frame]))
|
||||
elif prev_result and next_result:
|
||||
# Draw previous→next line (we're between frames)
|
||||
line_to_draw = ("prev_next", prev_result, next_result)
|
||||
|
||||
if line_to_draw:
|
||||
line_type, (frame1, pts1), (frame2, pts2) = line_to_draw
|
||||
|
||||
# Draw lines between corresponding tracking points
|
||||
for i, (prev_rx, prev_ry) in enumerate(prev_pts):
|
||||
if i < len(next_pts):
|
||||
next_rx, next_ry = next_pts[i]
|
||||
prev_sx, prev_sy = self._map_rotated_to_screen(prev_rx, prev_ry)
|
||||
next_sx, next_sy = self._map_rotated_to_screen(next_rx, next_ry)
|
||||
for i, (px1, py1) in enumerate(pts1):
|
||||
if i < len(pts2):
|
||||
px2, py2 = pts2[i]
|
||||
sx1, sy1 = self._map_rotated_to_screen(px1, py1)
|
||||
sx2, sy2 = self._map_rotated_to_screen(px2, py2)
|
||||
|
||||
# Draw motion path line with arrow (thin and transparent)
|
||||
overlay = canvas.copy()
|
||||
cv2.line(overlay, (prev_sx, prev_sy), (next_sx, next_sy), (255, 255, 0), 1) # Thin yellow line
|
||||
cv2.line(overlay, (sx1, sy1), (sx2, sy2), (255, 255, 0), 1) # Thin yellow line
|
||||
|
||||
# Draw arrow head pointing from previous to next
|
||||
angle = np.arctan2(next_sy - prev_sy, next_sx - prev_sx)
|
||||
# Draw arrow head pointing from first to second point
|
||||
angle = np.arctan2(sy2 - sy1, sx2 - sx1)
|
||||
arrow_length = 12
|
||||
arrow_angle = np.pi / 6 # 30 degrees
|
||||
|
||||
# Calculate arrow head points
|
||||
arrow_x1 = int(next_sx - arrow_length * np.cos(angle - arrow_angle))
|
||||
arrow_y1 = int(next_sy - arrow_length * np.sin(angle - arrow_angle))
|
||||
arrow_x2 = int(next_sx - arrow_length * np.cos(angle + arrow_angle))
|
||||
arrow_y2 = int(next_sy - arrow_length * np.sin(angle + arrow_angle))
|
||||
arrow_x1 = int(sx2 - arrow_length * np.cos(angle - arrow_angle))
|
||||
arrow_y1 = int(sy2 - arrow_length * np.sin(angle - arrow_angle))
|
||||
arrow_x2 = int(sx2 - arrow_length * np.cos(angle + arrow_angle))
|
||||
arrow_y2 = int(sy2 - arrow_length * np.sin(angle + arrow_angle))
|
||||
|
||||
cv2.line(overlay, (next_sx, next_sy), (arrow_x1, arrow_y1), (255, 255, 0), 1)
|
||||
cv2.line(overlay, (next_sx, next_sy), (arrow_x2, arrow_y2), (255, 255, 0), 1)
|
||||
cv2.line(overlay, (sx2, sy2), (arrow_x1, arrow_y1), (255, 255, 0), 1)
|
||||
cv2.line(overlay, (sx2, sy2), (arrow_x2, arrow_y2), (255, 255, 0), 1)
|
||||
cv2.addWeighted(overlay, 0.3, canvas, 0.7, 0, canvas) # Very transparent
|
||||
|
||||
# Previous tracking point (red) - from the most recent frame with tracking points before current
|
||||
@@ -2196,22 +2203,30 @@ class VideoEditor:
|
||||
best_snap_distance = distance
|
||||
best_snap_point = (int(px), int(py))
|
||||
|
||||
# Check for line snapping between previous and next tracking points (the actual cyan arrows)
|
||||
# Check for line snapping - either previous→next OR previous→current
|
||||
prev_result = self._get_previous_tracking_point()
|
||||
next_result = self._get_next_tracking_point()
|
||||
|
||||
print(f"DEBUG: Line snapping - prev_result: {prev_result}, next_result: {next_result}")
|
||||
|
||||
if prev_result and next_result:
|
||||
prev_frame, prev_pts = prev_result
|
||||
next_frame, next_pts = next_result
|
||||
# Determine which line to check: previous→current OR previous→next
|
||||
line_to_check = None
|
||||
if prev_result and self.current_frame in self.tracking_points:
|
||||
# Check previous→current line (we're on a frame with tracking points)
|
||||
line_to_check = ("prev_current", prev_result, (self.current_frame, self.tracking_points[self.current_frame]))
|
||||
print(f"DEBUG: Checking prev->current line")
|
||||
elif prev_result and next_result:
|
||||
# Check previous→next line (we're between frames)
|
||||
line_to_check = ("prev_next", prev_result, next_result)
|
||||
print(f"DEBUG: Checking prev->next line")
|
||||
|
||||
print(f"DEBUG: Checking line between prev frame {prev_frame} and next frame {next_frame}")
|
||||
if line_to_check:
|
||||
line_type, (frame1, pts1), (frame2, pts2) = line_to_check
|
||||
|
||||
# Check each corresponding pair of points between previous and next
|
||||
for j in range(min(len(prev_pts), len(next_pts))):
|
||||
px1, py1 = prev_pts[j]
|
||||
px2, py2 = next_pts[j]
|
||||
# Check each corresponding pair of points
|
||||
for j in range(min(len(pts1), len(pts2))):
|
||||
px1, py1 = pts1[j]
|
||||
px2, py2 = pts2[j]
|
||||
|
||||
# Convert to screen coordinates
|
||||
sx1, sy1 = self._map_rotated_to_screen(px1, py1)
|
||||
@@ -2220,35 +2235,19 @@ class VideoEditor:
|
||||
# Calculate distance to infinite line and foot of perpendicular
|
||||
line_distance, (foot_x, foot_y) = self._point_to_line_distance_and_foot(x, y, sx1, sy1, sx2, sy2)
|
||||
|
||||
print(f"DEBUG: Line {j}: ({sx1},{sy1}) to ({sx2},{sy2}), distance to click ({x},{y}) = {line_distance:.2f}, foot = ({foot_x:.1f}, {foot_y:.1f})")
|
||||
print(f"DEBUG: {line_type} Line {j}: ({sx1},{sy1}) to ({sx2},{sy2}), distance to click ({x},{y}) = {line_distance:.2f}, foot = ({foot_x:.1f}, {foot_y:.1f})")
|
||||
|
||||
if line_distance <= threshold and line_distance < best_snap_distance:
|
||||
print(f"DEBUG: Line snap found! Distance {line_distance:.2f} <= threshold {threshold}")
|
||||
|
||||
# Clamp the foot to the line segment
|
||||
# Calculate parameter t for the foot point
|
||||
line_dx = sx2 - sx1
|
||||
line_dy = sy2 - sy1
|
||||
line_length_sq = line_dx * line_dx + line_dy * line_dy
|
||||
|
||||
if line_length_sq > 0:
|
||||
t = ((foot_x - sx1) * line_dx + (foot_y - sy1) * line_dy) / line_length_sq
|
||||
t = max(0, min(1, t)) # Clamp to [0, 1]
|
||||
|
||||
# Calculate clamped foot point
|
||||
clamped_foot_x = sx1 + t * line_dx
|
||||
clamped_foot_y = sy1 + t * line_dy
|
||||
|
||||
print(f"DEBUG: Clamped foot from ({foot_x:.1f}, {foot_y:.1f}) to ({clamped_foot_x:.1f}, {clamped_foot_y:.1f})")
|
||||
|
||||
# Convert clamped foot back to rotated coordinates
|
||||
closest_rx, closest_ry = self._map_screen_to_rotated(int(clamped_foot_x), int(clamped_foot_y))
|
||||
# Convert foot of perpendicular back to rotated coordinates (no clamping - infinite line)
|
||||
closest_rx, closest_ry = self._map_screen_to_rotated(int(foot_x), int(foot_y))
|
||||
|
||||
best_snap_distance = line_distance
|
||||
best_snap_point = (int(closest_rx), int(closest_ry))
|
||||
print(f"DEBUG: Best line snap point: ({closest_rx}, {closest_ry})")
|
||||
else:
|
||||
print(f"DEBUG: No prev/next points found for line snapping")
|
||||
print(f"DEBUG: No line found for snapping")
|
||||
|
||||
# Apply the best snap if found
|
||||
if best_snap_point:
|
||||
@@ -2272,6 +2271,9 @@ class VideoEditor:
|
||||
self.clear_transformation_cache()
|
||||
self.save_state()
|
||||
|
||||
# Force immediate display update to recalculate previous/next points and arrows
|
||||
self.display_current_frame()
|
||||
|
||||
# Handle scroll wheel: Ctrl+scroll -> zoom; plain scroll -> seek ±1 frame (independent of multiplier)
|
||||
if event == cv2.EVENT_MOUSEWHEEL:
|
||||
if flags & cv2.EVENT_FLAG_CTRLKEY:
|
||||
|
Reference in New Issue
Block a user