refactor(main.py): simplify and optimize coordinate conversion logic in VideoEditor class
This commit is contained in:
		
							
								
								
									
										198
									
								
								croppa/main.py
									
									
									
									
									
								
							
							
						
						
									
										198
									
								
								croppa/main.py
									
									
									
									
									
								
							@@ -1103,114 +1103,120 @@ class VideoEditor:
 | 
			
		||||
        """Convert screen coordinates to video frame coordinates and set crop"""
 | 
			
		||||
        x, y, w, h = screen_rect
 | 
			
		||||
 | 
			
		||||
        if self.current_display_frame is not None:
 | 
			
		||||
            # Get the original frame dimensions
 | 
			
		||||
            original_height, original_width = self.current_display_frame.shape[:2]
 | 
			
		||||
            available_height = self.window_height - self.TIMELINE_HEIGHT
 | 
			
		||||
        if self.current_display_frame is None:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
            # Calculate how the original frame is displayed (after crop/zoom/rotation)
 | 
			
		||||
            display_frame = self.apply_crop_zoom_and_rotation(
 | 
			
		||||
                self.current_display_frame.copy()
 | 
			
		||||
            )
 | 
			
		||||
            if display_frame is None:
 | 
			
		||||
                return
 | 
			
		||||
        # Get the original frame dimensions
 | 
			
		||||
        original_height, original_width = self.current_display_frame.shape[:2]
 | 
			
		||||
        available_height = self.window_height - (0 if self.is_image_mode else self.TIMELINE_HEIGHT)
 | 
			
		||||
 | 
			
		||||
            display_height, display_width = display_frame.shape[:2]
 | 
			
		||||
        # Calculate how the original frame is displayed (after crop/zoom/rotation)
 | 
			
		||||
        display_frame = self.apply_crop_zoom_and_rotation(
 | 
			
		||||
            self.current_display_frame.copy()
 | 
			
		||||
        )
 | 
			
		||||
        if display_frame is None:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
            # Calculate scale for the display frame
 | 
			
		||||
            scale = min(
 | 
			
		||||
                self.window_width / display_width, available_height / display_height
 | 
			
		||||
            )
 | 
			
		||||
            if scale < 1.0:
 | 
			
		||||
                final_display_width = int(display_width * scale)
 | 
			
		||||
                final_display_height = int(display_height * scale)
 | 
			
		||||
            else:
 | 
			
		||||
                final_display_width = display_width
 | 
			
		||||
                final_display_height = display_height
 | 
			
		||||
                scale = 1.0
 | 
			
		||||
        display_height, display_width = display_frame.shape[:2]
 | 
			
		||||
 | 
			
		||||
            start_x = (self.window_width - final_display_width) // 2
 | 
			
		||||
            start_y = (available_height - final_display_height) // 2
 | 
			
		||||
        # Calculate scale for the display frame
 | 
			
		||||
        scale = min(
 | 
			
		||||
            self.window_width / display_width, available_height / display_height
 | 
			
		||||
        )
 | 
			
		||||
        if scale < 1.0:
 | 
			
		||||
            final_display_width = int(display_width * scale)
 | 
			
		||||
            final_display_height = int(display_height * scale)
 | 
			
		||||
        else:
 | 
			
		||||
            final_display_width = display_width
 | 
			
		||||
            final_display_height = display_height
 | 
			
		||||
            scale = 1.0
 | 
			
		||||
 | 
			
		||||
            # Convert screen coordinates to display frame coordinates
 | 
			
		||||
            display_x = (x - start_x) / scale
 | 
			
		||||
            display_y = (y - start_y) / scale
 | 
			
		||||
            display_w = w / scale
 | 
			
		||||
            display_h = h / scale
 | 
			
		||||
        start_x = (self.window_width - final_display_width) // 2
 | 
			
		||||
        start_y = (available_height - final_display_height) // 2
 | 
			
		||||
 | 
			
		||||
            # Clamp to display frame bounds
 | 
			
		||||
            display_x = max(0, min(display_x, display_width))
 | 
			
		||||
            display_y = max(0, min(display_y, display_height))
 | 
			
		||||
            display_w = min(display_w, display_width - display_x)
 | 
			
		||||
            display_h = min(display_h, display_height - display_y)
 | 
			
		||||
        # Convert screen coordinates to display frame coordinates
 | 
			
		||||
        display_x = (x - start_x) / scale
 | 
			
		||||
        display_y = (y - start_y) / scale
 | 
			
		||||
        display_w = w / scale
 | 
			
		||||
        display_h = h / scale
 | 
			
		||||
 | 
			
		||||
            # Convert display frame coordinates back to original frame coordinates
 | 
			
		||||
            # This is the inverse of apply_crop_zoom_and_rotation
 | 
			
		||||
            # The order is: crop -> rotation -> zoom
 | 
			
		||||
            # So we need to reverse: zoom -> rotation -> crop
 | 
			
		||||
        # Clamp to display frame bounds
 | 
			
		||||
        display_x = max(0, min(display_x, display_width))
 | 
			
		||||
        display_y = max(0, min(display_y, display_height))
 | 
			
		||||
        display_w = min(display_w, display_width - display_x)
 | 
			
		||||
        display_h = min(display_h, display_height - display_y)
 | 
			
		||||
 | 
			
		||||
            # Step 1: Reverse zoom (zoom is applied to the rotated frame)
 | 
			
		||||
            if self.zoom_factor != 1.0:
 | 
			
		||||
                display_x = display_x / self.zoom_factor
 | 
			
		||||
                display_y = display_y / self.zoom_factor
 | 
			
		||||
                display_w = display_w / self.zoom_factor
 | 
			
		||||
                display_h = display_h / self.zoom_factor
 | 
			
		||||
        # Now we need to convert from the display frame coordinates back to original frame coordinates
 | 
			
		||||
        # The display frame is the result of: original -> crop -> rotation -> zoom
 | 
			
		||||
        
 | 
			
		||||
        # Step 1: Reverse zoom
 | 
			
		||||
        if self.zoom_factor != 1.0:
 | 
			
		||||
            display_x = display_x / self.zoom_factor
 | 
			
		||||
            display_y = display_y / self.zoom_factor
 | 
			
		||||
            display_w = display_w / self.zoom_factor
 | 
			
		||||
            display_h = display_h / self.zoom_factor
 | 
			
		||||
 | 
			
		||||
            # Step 2: Reverse rotation (rotation is applied to the cropped frame)
 | 
			
		||||
            if self.rotation_angle != 0:
 | 
			
		||||
                # Get the dimensions after crop but before rotation
 | 
			
		||||
                if self.crop_rect:
 | 
			
		||||
                    crop_w, crop_h = int(self.crop_rect[2]), int(self.crop_rect[3])
 | 
			
		||||
                else:
 | 
			
		||||
                    crop_w, crop_h = original_width, original_height
 | 
			
		||||
                
 | 
			
		||||
                # Apply inverse rotation to coordinates
 | 
			
		||||
                if self.rotation_angle == 90:
 | 
			
		||||
                    # 90° clockwise -> 270° counter-clockwise
 | 
			
		||||
                    new_x = display_y
 | 
			
		||||
                    new_y = crop_w - display_x - display_w
 | 
			
		||||
                    new_w = display_h
 | 
			
		||||
                    new_h = display_w
 | 
			
		||||
                elif self.rotation_angle == 180:
 | 
			
		||||
                    # 180° -> 180° (same)
 | 
			
		||||
                    new_x = crop_w - display_x - display_w
 | 
			
		||||
                    new_y = crop_h - display_y - display_h
 | 
			
		||||
                    new_w = display_w
 | 
			
		||||
                    new_h = display_h
 | 
			
		||||
                elif self.rotation_angle == 270:
 | 
			
		||||
                    # 270° clockwise -> 90° counter-clockwise
 | 
			
		||||
                    new_x = crop_h - display_y - display_h
 | 
			
		||||
                    new_y = display_x
 | 
			
		||||
                    new_w = display_h
 | 
			
		||||
                    new_h = display_w
 | 
			
		||||
                else:
 | 
			
		||||
                    new_x, new_y, new_w, new_h = display_x, display_y, display_w, display_h
 | 
			
		||||
                
 | 
			
		||||
                display_x, display_y, display_w, display_h = new_x, new_y, new_w, new_h
 | 
			
		||||
 | 
			
		||||
            # Step 3: Reverse crop (crop is applied to the original frame)
 | 
			
		||||
            original_x = display_x
 | 
			
		||||
            original_y = display_y
 | 
			
		||||
            original_w = display_w
 | 
			
		||||
            original_h = display_h
 | 
			
		||||
 | 
			
		||||
            # Add the crop offset to get back to original frame coordinates
 | 
			
		||||
        # Step 2: Reverse rotation
 | 
			
		||||
        if self.rotation_angle != 0:
 | 
			
		||||
            # Get the dimensions of the frame after crop but before rotation
 | 
			
		||||
            if self.crop_rect:
 | 
			
		||||
                crop_x, crop_y, crop_w, crop_h = self.crop_rect
 | 
			
		||||
                original_x += crop_x
 | 
			
		||||
                original_y += crop_y
 | 
			
		||||
                crop_w, crop_h = int(self.crop_rect[2]), int(self.crop_rect[3])
 | 
			
		||||
            else:
 | 
			
		||||
                crop_w, crop_h = original_width, original_height
 | 
			
		||||
            
 | 
			
		||||
            # Apply inverse rotation to coordinates
 | 
			
		||||
            # The key insight: we need to use the dimensions of the ROTATED frame for the coordinate transformation
 | 
			
		||||
            # because the coordinates we have are in the rotated coordinate system
 | 
			
		||||
            if self.rotation_angle == 90:
 | 
			
		||||
                # 90° clockwise rotation: (x,y) -> (y, rotated_width-x-w)
 | 
			
		||||
                # The rotated frame has dimensions: height x width (swapped)
 | 
			
		||||
                rotated_w, rotated_h = crop_h, crop_w
 | 
			
		||||
                new_x = display_y
 | 
			
		||||
                new_y = rotated_w - display_x - display_w
 | 
			
		||||
                new_w = display_h
 | 
			
		||||
                new_h = display_w
 | 
			
		||||
            elif self.rotation_angle == 180:
 | 
			
		||||
                # 180° rotation: (x,y) -> (width-x-w, height-y-h)
 | 
			
		||||
                new_x = crop_w - display_x - display_w
 | 
			
		||||
                new_y = crop_h - display_y - display_h
 | 
			
		||||
                new_w = display_w
 | 
			
		||||
                new_h = display_h
 | 
			
		||||
            elif self.rotation_angle == 270:
 | 
			
		||||
                # 270° clockwise rotation: (x,y) -> (rotated_height-y-h, x)
 | 
			
		||||
                # The rotated frame has dimensions: height x width (swapped)
 | 
			
		||||
                rotated_w, rotated_h = crop_h, crop_w
 | 
			
		||||
                new_x = rotated_h - display_y - display_h
 | 
			
		||||
                new_y = display_x
 | 
			
		||||
                new_w = display_h
 | 
			
		||||
                new_h = display_w
 | 
			
		||||
            else:
 | 
			
		||||
                new_x, new_y, new_w, new_h = display_x, display_y, display_w, display_h
 | 
			
		||||
            
 | 
			
		||||
            display_x, display_y, display_w, display_h = new_x, new_y, new_w, new_h
 | 
			
		||||
 | 
			
		||||
            # Clamp to original frame bounds
 | 
			
		||||
            original_x = max(0, min(original_x, original_width))
 | 
			
		||||
            original_y = max(0, min(original_y, original_height))
 | 
			
		||||
            original_w = min(original_w, original_width - original_x)
 | 
			
		||||
            original_h = min(original_h, original_height - original_y)
 | 
			
		||||
        # Step 3: Convert from cropped frame coordinates to original frame coordinates
 | 
			
		||||
        original_x = display_x
 | 
			
		||||
        original_y = display_y
 | 
			
		||||
        original_w = display_w
 | 
			
		||||
        original_h = display_h
 | 
			
		||||
 | 
			
		||||
            if original_w > 10 and original_h > 10:  # Minimum size check
 | 
			
		||||
                # Save current crop for undo
 | 
			
		||||
                if self.crop_rect:
 | 
			
		||||
                    self.crop_history.append(self.crop_rect)
 | 
			
		||||
                self.crop_rect = (original_x, original_y, original_w, original_h)
 | 
			
		||||
        # Add the crop offset to get back to original frame coordinates
 | 
			
		||||
        if self.crop_rect:
 | 
			
		||||
            crop_x, crop_y, crop_w, crop_h = self.crop_rect
 | 
			
		||||
            original_x += crop_x
 | 
			
		||||
            original_y += crop_y
 | 
			
		||||
 | 
			
		||||
        # Clamp to original frame bounds
 | 
			
		||||
        original_x = max(0, min(original_x, original_width))
 | 
			
		||||
        original_y = max(0, min(original_y, original_height))
 | 
			
		||||
        original_w = min(original_w, original_width - original_x)
 | 
			
		||||
        original_h = min(original_h, original_height - original_y)
 | 
			
		||||
 | 
			
		||||
        if original_w > 10 and original_h > 10:  # Minimum size check
 | 
			
		||||
            # Save current crop for undo
 | 
			
		||||
            if self.crop_rect:
 | 
			
		||||
                self.crop_history.append(self.crop_rect)
 | 
			
		||||
            self.crop_rect = (original_x, original_y, original_w, original_h)
 | 
			
		||||
 | 
			
		||||
    def seek_to_timeline_position(self, mouse_x, bar_x_start, bar_width):
 | 
			
		||||
        """Seek to position based on mouse click on timeline"""
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user