"""Camera angle data structures for Fibo Edit.""" from dataclasses import dataclass from enum import Enum class View(Enum): """Camera view angles""" BACK_VIEW = "back view" BACK_LEFT_QUARTER = "back-left quarter view" BACK_RIGHT_QUARTER = "back-right quarter view" FRONT_VIEW = "front view" FRONT_LEFT_QUARTER = "front-left quarter view" FRONT_RIGHT_QUARTER = "front-right quarter view" LEFT_SIDE = "left side view" RIGHT_SIDE = "right side view" class Shot(Enum): """ Camera shot angles (measured from horizontal/eye-level as 0 degrees) - ELEVATED: 45-60 degrees above subject (moderately elevated) - EYE_LEVEL: 0 degrees (horizontal with subject) - HIGH_ANGLE: 60-90 degrees above subject (steep overhead, bird's eye) - LOW_ANGLE: Below eye level (looking up at subject) """ ELEVATED = "elevated shot" EYE_LEVEL = "eye-level shot" HIGH_ANGLE = "high-angle shot" LOW_ANGLE = "low-angle shot" class Zoom(Enum): """Camera zoom levels""" CLOSE_UP = "close-up" MEDIUM = "medium shot" WIDE = "wide shot" @dataclass class AngleInstruction: view: View shot: Shot zoom: Zoom def __str__(self): return f" {self.view.value} {self.shot.value} {self.zoom.value}" @classmethod def from_camera_params(cls, rotation: float, tilt: float, zoom: float) -> "AngleInstruction": """ Create an AngleInstruction from camera parameters. Args: rotation: Horizontal rotation in degrees (-180 to 180) -180/180: back view, -90: left view, 0: front view, 90: right view tilt: Vertical tilt (-1 to 1) -1 to -0.33: low-angle shot -0.33 to 0.33: eye-level shot 0.33 to 0.66: elevated shot 0.66 to 1: high-angle shot zoom: Zoom level (0 to 10) 0-3.33: wide shot 3.33-6.66: medium shot 6.66-10: close-up Returns: AngleInstruction instance """ # Map rotation to View # Normalize rotation to -180 to 180 range rotation = rotation % 360 if rotation > 180: rotation -= 360 # Determine view based on rotation if -157.5 <= rotation < -112.5: view = View.BACK_LEFT_QUARTER elif -112.5 <= rotation < -67.5: view = View.LEFT_SIDE elif -67.5 <= rotation < -22.5: view = View.FRONT_LEFT_QUARTER elif -22.5 <= rotation < 22.5: view = View.FRONT_VIEW elif 22.5 <= rotation < 67.5: view = View.FRONT_RIGHT_QUARTER elif 67.5 <= rotation < 112.5: view = View.RIGHT_SIDE elif 112.5 <= rotation < 157.5: view = View.BACK_RIGHT_QUARTER else: # 157.5 to 180 or -180 to -157.5 view = View.BACK_VIEW # Map tilt to Shot if tilt < -0.33: shot = Shot.LOW_ANGLE elif tilt < 0.33: shot = Shot.EYE_LEVEL elif tilt < 0.66: shot = Shot.ELEVATED else: shot = Shot.HIGH_ANGLE # Map zoom to Zoom if zoom < 3.33: zoom_level = Zoom.WIDE elif zoom < 6.66: zoom_level = Zoom.MEDIUM else: zoom_level = Zoom.CLOSE_UP return cls(view=view, shot=shot, zoom=zoom_level)