Refactor coordinate mapping in VideoEditor for improved zoom and rotation handling

This commit enhances the _map_original_to_screen and _map_screen_to_original methods by clarifying the calculations for zoom and rotation. It introduces new variables for better readability and ensures accurate mapping of coordinates, including adjustments for display offsets. The changes streamline the processing of frame dimensions and improve the overall functionality of the video editing experience.
This commit is contained in:
2025-09-17 01:19:12 +02:00
parent fdf7d98850
commit b440da3094

View File

@@ -1201,14 +1201,18 @@ class VideoEditor:
def _map_original_to_screen(self, ox, oy):
"""Map a point in original frame coords to canvas screen coords."""
cx, cy, cw, ch = self._get_effective_crop_rect_for_frame(getattr(self, 'current_frame', 0))
frame_number = getattr(self, 'current_frame', 0)
cx, cy, cw, ch = self._get_effective_crop_rect_for_frame(frame_number)
# Relative to effective crop
px = ox - cx
py = oy - cy
angle = self.rotation_angle
# Dimensions after rotation
if angle in (90, 270):
rotated_w, rotated_h = ch, cw
else:
rotated_w, rotated_h = cw, ch
# Forward rotation mapping
if angle == 90:
rx, ry = py, rotated_w - px
elif angle == 180:
@@ -1217,51 +1221,68 @@ class VideoEditor:
rx, ry = rotated_h - py, px
else:
rx, ry = px, py
# Zoom
zx = rx * self.zoom_factor
zy = ry * self.zoom_factor
base_w, base_h = rotated_w, rotated_h
disp_w = int(base_w * self.zoom_factor)
disp_h = int(base_h * self.zoom_factor)
# Apply display offset cropping in zoomed space
new_w = int(rotated_w * self.zoom_factor)
new_h = int(rotated_h * self.zoom_factor)
offx_max = max(0, new_w - self.window_width)
offy_max = max(0, new_h - self.window_height)
offx = max(0, min(int(self.display_offset[0]), offx_max))
offy = max(0, min(int(self.display_offset[1]), offy_max))
inframe_x = zx - offx
inframe_y = zy - offy
# Size of processed_frame from apply_crop_zoom_and_rotation
base_w = new_w if new_w <= self.window_width else self.window_width
base_h = new_h if new_h <= self.window_height else self.window_height
# Final scale and canvas placement
available_height = self.window_height - (0 if self.is_image_mode else self.TIMELINE_HEIGHT)
scale = min(self.window_width / max(1, disp_w), available_height / max(1, disp_h))
if scale < 1.0:
final_w = int(disp_w * scale)
final_h = int(disp_h * scale)
else:
final_w = disp_w
final_h = disp_h
scale = 1.0
start_x = (self.window_width - final_w) // 2
start_y = (available_height - final_h) // 2
sx = int(round(start_x + zx * scale))
sy = int(round(start_y + zy * scale))
scale = min(self.window_width / max(1, base_w), available_height / max(1, base_h))
final_w = int(base_w * scale)
final_h = int(base_h * scale)
start_x_canvas = (self.window_width - final_w) // 2
start_y_canvas = (available_height - final_h) // 2
sx = int(round(start_x_canvas + inframe_x * scale))
sy = int(round(start_y_canvas + inframe_y * scale))
return sx, sy
def _map_screen_to_original(self, sx, sy):
"""Map a point on canvas screen coords back to original frame coords."""
cx, cy, cw, ch = self._get_effective_crop_rect_for_frame(getattr(self, 'current_frame', 0))
frame_number = getattr(self, 'current_frame', 0)
cx, cy, cw, ch = self._get_effective_crop_rect_for_frame(frame_number)
angle = self.rotation_angle
# Dimensions after rotation
if angle in (90, 270):
rotated_w, rotated_h = ch, cw
else:
rotated_w, rotated_h = cw, ch
disp_w = int(rotated_w * self.zoom_factor)
disp_h = int(rotated_h * self.zoom_factor)
# Zoomed dimensions and base processed dimensions (after window cropping)
new_w = int(rotated_w * self.zoom_factor)
new_h = int(rotated_h * self.zoom_factor)
base_w = new_w if new_w <= self.window_width else self.window_width
base_h = new_h if new_h <= self.window_height else self.window_height
# Final scaling used in display
available_height = self.window_height - (0 if self.is_image_mode else self.TIMELINE_HEIGHT)
scale = min(self.window_width / max(1, disp_w), available_height / max(1, disp_h))
if scale < 1.0:
final_w = int(disp_w * scale)
final_h = int(disp_h * scale)
else:
final_w = disp_w
final_h = disp_h
scale = 1.0
start_x = (self.window_width - final_w) // 2
start_y = (available_height - final_h) // 2
zx = (sx - start_x) / max(1e-6, scale)
zy = (sy - start_y) / max(1e-6, scale)
scale = min(self.window_width / max(1, base_w), available_height / max(1, base_h))
final_w = int(base_w * scale)
final_h = int(base_h * scale)
start_x_canvas = (self.window_width - final_w) // 2
start_y_canvas = (available_height - final_h) // 2
# Back to processed (zoomed+cropped) space
zx = (sx - start_x_canvas) / max(1e-6, scale)
zy = (sy - start_y_canvas) / max(1e-6, scale)
# Add display offset in zoomed space
offx_max = max(0, new_w - self.window_width)
offy_max = max(0, new_h - self.window_height)
offx = max(0, min(int(self.display_offset[0]), offx_max))
offy = max(0, min(int(self.display_offset[1]), offy_max))
zx += offx
zy += offy
# Reverse zoom
rx = zx / max(1e-6, self.zoom_factor)
ry = zy / max(1e-6, self.zoom_factor)
# Reverse rotation
if angle == 90:
px, py = rotated_w - ry, rx
elif angle == 180:
@@ -1270,6 +1291,7 @@ class VideoEditor:
px, py = ry, rotated_h - rx
else:
px, py = rx, ry
# Back to original frame
ox = px + cx
oy = py + cy
ox = max(0, min(int(round(ox)), self.frame_width - 1))