Source code for physics_viz.camera

"""
Camera system for 2D physics visualization

Provides viewport transformation from world space to screen space.
"""

import pygame
from .physics_engine_core import Vector2


[docs] class Camera: """2D camera with pan and zoom support The camera transforms world coordinates (meters) to screen coordinates (pixels). Supports panning, zooming, and following objects. """ def __init__(self, screen_width, screen_height, pixels_per_meter=20.0): """Initialize camera Args: screen_width: Screen width in pixels screen_height: Screen height in pixels pixels_per_meter: Zoom level (pixels per meter) """ self.screen_width = screen_width self.screen_height = screen_height self.pixels_per_meter = pixels_per_meter # Camera position in world space (meters) self.position = Vector2(0, 0) # Zoom limits self.min_zoom = 5.0 self.max_zoom = 200.0
[docs] def world_to_screen(self, world_pos): """Convert world position (meters) to screen position (pixels) Args: world_pos: Vector2 in world space Returns: tuple: (x, y) in screen space """ # Translate relative to camera rel_x = world_pos.x - self.position.x rel_y = world_pos.y - self.position.y # Apply zoom and convert to pixels screen_x = rel_x * self.pixels_per_meter + self.screen_width / 2 screen_y = -rel_y * self.pixels_per_meter + self.screen_height / 2 # Flip Y return (int(screen_x), int(screen_y))
[docs] def screen_to_world(self, screen_pos): """Convert screen position (pixels) to world position (meters) Args: screen_pos: tuple (x, y) in screen space Returns: Vector2 in world space """ screen_x, screen_y = screen_pos # Remove screen center offset and convert to meters rel_x = (screen_x - self.screen_width / 2) / self.pixels_per_meter rel_y = -(screen_y - self.screen_height / 2) / self.pixels_per_meter # Flip Y # Add camera position world_x = rel_x + self.position.x world_y = rel_y + self.position.y return Vector2(world_x, world_y)
[docs] def world_to_screen_scalar(self, world_scalar): """Convert world distance (meters) to screen distance (pixels) Args: world_scalar: Distance in meters Returns: int: Distance in pixels """ return int(world_scalar * self.pixels_per_meter)
[docs] def screen_to_world_scalar(self, screen_scalar): """Convert screen distance (pixels) to world distance (meters) Args: screen_scalar: Distance in pixels Returns: float: Distance in meters """ return screen_scalar / self.pixels_per_meter
[docs] def pan(self, dx, dy): """Pan camera by screen offset Args: dx: Horizontal pan in pixels dy: Vertical pan in pixels """ world_dx = dx / self.pixels_per_meter world_dy = -dy / self.pixels_per_meter # Flip Y self.position.x += world_dx self.position.y += world_dy
[docs] def zoom(self, factor, center=None): """Zoom camera by factor Args: factor: Zoom multiplier (> 1 = zoom in, < 1 = zoom out) center: Optional screen position to zoom toward (tuple) """ old_ppm = self.pixels_per_meter self.pixels_per_meter *= factor # Clamp zoom self.pixels_per_meter = max(self.min_zoom, min(self.max_zoom, self.pixels_per_meter)) # If zooming toward a point, adjust position to keep that point fixed if center: # Convert center to world space using old zoom old_world = self.screen_to_world(center) # Update zoom # Convert center to world space using new zoom new_world = self.screen_to_world(center) # Offset camera to keep point fixed self.position.x += (old_world.x - new_world.x) self.position.y += (old_world.y - new_world.y)
[docs] def focus_on(self, world_pos): """Center camera on world position Args: world_pos: Vector2 in world space """ self.position.x = world_pos.x self.position.y = world_pos.y
[docs] def get_view_bounds(self): """Get visible world bounds Returns: tuple: (min_x, max_x, min_y, max_y) in world space """ # Get corners in world space top_left = self.screen_to_world((0, 0)) bottom_right = self.screen_to_world((self.screen_width, self.screen_height)) return (top_left.x, bottom_right.x, bottom_right.y, top_left.y)