pyxora.camera
1from .display import Display 2from .wrapper.functions import vector 3from .wrapper import Text,Image,Shape 4 5from math import log2 6from typing import Tuple 7 8import pygame 9 10class Camera: 11 """ 12 Camera class for the pyxora display environment. 13 Handles position, zoom, and rendering of shapes, text, and images with camera-relative coordinates. 14 15 Note: Every Scene has a camera instance by default. You can create multiple instances but it's not recommended. 16 """ 17 18 def __init__(self) -> None: 19 """Initialize the camera position, offset, zoom, and set initial zoom.""" 20 self._pos = vector(0, 0) 21 self._offset = vector(0, 0) 22 self._zoom_scale = 1 23 self._zoom_factor = 0 24 self._zoom_direction = 0 25 self.__max_zoom_factor = 3 26 self.zoom(1) 27 28 @property 29 def surface(self) -> pygame.Surface: 30 """Property to the current drawing surface.""" 31 return Display.surface 32 33 @property 34 def position(self) -> pygame.math.Vector2: 35 """Property to get a copy of the position of the camera.""" 36 return self._pos.copy() 37 38 @property 39 def rect(self) -> pygame.Rect: 40 """Property to get the camera's rectangle area as a pygame.Rect.""" 41 rect = pygame.Rect(self._pos + self._offset, Display._res) 42 rect.width = int(rect.width / self.zoom_scale) 43 rect.height = int(rect.height / self.zoom_scale) 44 return rect 45 46 @property 47 def zoom_scale(self) -> float: 48 """Property to get the current zoom scale factor""" 49 return self._zoom_scale 50 51 @property 52 def zoom_factor(self) -> float: 53 """Property to get the current log2 zoom factor""" 54 return self._zoom_factor 55 56 @property 57 def zoom_direction(self) -> float: 58 """Property to get the current zoom direction.""" 59 return self._zoom_direction 60 61 @property 62 def zoom_level(self) -> float: 63 """Property to get the current zoom level.""" 64 if self.zoom_direction == 0 and self.zoom_factor == 0: 65 return 1 66 elif self.zoom_factor == 0: 67 return 1 68 elif self.zoom_factor >= 1: 69 return self.zoom_factor + 1 70 else: 71 return self.zoom_factor - 1 72 73 def set_max_zoom(self, factor: float) -> None: 74 """ 75 Set the maximum zoom factor for the camera. 76 77 Args: 78 factor: The maximum zoom factor to set. 79 """ 80 self.__max_zoom_factor = factor 81 82 def get_max_zoom(self) -> float: 83 """ 84 Get the maximum zoom factor for the camera. 85 86 Returns: 87 The maximum zoom factor. 88 """ 89 return self.__max_zoom_factor 90 91 def is_visible(self, obj) -> bool: 92 """ 93 Check if an object is at least partially visible on the screen (Requires object to have a rect attribute). 94 95 Args: 96 obj: The object to check visibility for. 97 98 Returns: 99 True if the object is visible, False otherwise. 100 """ 101 return self.rect.colliderect(obj.rect) 102 103 def move(self, offset: Tuple[int | float, int | float] | pygame.math.Vector2) -> None: 104 """ 105 Move the camera by a given offset (tuple or vector). 106 107 Args: 108 offset: The offset to move the camera by. 109 """ 110 self._pos.x += offset[0] 111 self._pos.y += offset[1] 112 113 def move_at(self, new_pos: Tuple[int | float, int | float] | pygame.math.Vector2) -> None: 114 """ 115 Move the camera to a specific position (centered). 116 117 Args: 118 new_pos: The new position to move the camera to. 119 """ 120 self._pos.x = new_pos[0] + Display._res[0] / 2 121 self._pos.y = new_pos[1] + Display._res[1] / 2 122 123 def zoom(self, factor: int) -> None: 124 """ 125 Zoom the camera by a given step, adjusting its scale. 126 127 Args: 128 factor: The zoom factor to apply. 129 """ 130 if factor == 0: 131 return 132 factor = (factor + 1) if factor < 0 else (factor - 1) 133 scale = 2 ** (self.zoom_factor + factor) 134 self.zoom_at(scale) 135 136 def zoom_at(self, scale: float) -> None: 137 """ 138 Set the camera zoom to a specific scale, centering the view. 139 140 Args: 141 scale: The scale to set the camera zoom to. 142 """ 143 if not scale: 144 return 145 min_scale = 2 ** -self.__max_zoom_factor 146 max_scale = 2 ** self.__max_zoom_factor 147 scale = max(min_scale, min(max_scale, scale)) 148 factor = log2(scale) 149 direction = factor - self._zoom_factor 150 151 # Center the camera based on the new zoom scale 152 sign = 1 if scale < 0 else -1 153 self._offset.x = 1 / scale * Display._res[0] / 2 * sign + Display._res[0] / 2 154 self._offset.y = 1 / scale * Display._res[1] / 2 * sign + Display._res[1] / 2 155 156 self._zoom_scale = scale 157 self._zoom_factor = factor 158 self._zoom_direction = direction 159 160 def draw_shape(self, Shape: Shape, fill: int = 0) -> None: 161 """Draw a shape on the camera's surface if it is visible.""" 162 if not self.is_visible(Shape): 163 return 164 165 Shape._pos -= self._pos + self._offset 166 Shape._pos *= self._zoom_scale 167 Shape.draw(self.surface, fill, self._zoom_scale) 168 Shape._pos /= self._zoom_scale 169 Shape._pos += self._offset + self._pos 170 171 def draw_text(self, Txt: Text) -> None: 172 """Draw text on the camera's surface if it is visible.""" 173 if not self.is_visible(Txt): 174 return 175 176 Txt._pos -= self._pos + self._offset 177 Txt._pos *= self._zoom_scale 178 Txt.draw(self.surface, self._zoom_scale) 179 Txt._pos /= self._zoom_scale 180 Txt._pos += self._offset + self._pos 181 182 def draw_image(self, Image: Image) -> None: 183 """Draw an image on the camera's surface if it is visible.""" 184 if not self.is_visible(Image): 185 return 186 187 Image._pos -= self._pos + self._offset 188 Image._pos *= self._zoom_scale 189 Image.draw(self.surface, self._zoom_scale) 190 Image._pos /= self._zoom_scale 191 Image._pos += self._offset + self._pos 192 193 def _dynamic_zoom(self) -> None: 194 """Dynamically adjust the zoom based on the display's resolution.""" 195 scale = min((Display._res[0] / Display._new_res[0]),(Display._res[1] / Display._new_res[1])) 196 self.zoom_at(scale)
11class Camera: 12 """ 13 Camera class for the pyxora display environment. 14 Handles position, zoom, and rendering of shapes, text, and images with camera-relative coordinates. 15 16 Note: Every Scene has a camera instance by default. You can create multiple instances but it's not recommended. 17 """ 18 19 def __init__(self) -> None: 20 """Initialize the camera position, offset, zoom, and set initial zoom.""" 21 self._pos = vector(0, 0) 22 self._offset = vector(0, 0) 23 self._zoom_scale = 1 24 self._zoom_factor = 0 25 self._zoom_direction = 0 26 self.__max_zoom_factor = 3 27 self.zoom(1) 28 29 @property 30 def surface(self) -> pygame.Surface: 31 """Property to the current drawing surface.""" 32 return Display.surface 33 34 @property 35 def position(self) -> pygame.math.Vector2: 36 """Property to get a copy of the position of the camera.""" 37 return self._pos.copy() 38 39 @property 40 def rect(self) -> pygame.Rect: 41 """Property to get the camera's rectangle area as a pygame.Rect.""" 42 rect = pygame.Rect(self._pos + self._offset, Display._res) 43 rect.width = int(rect.width / self.zoom_scale) 44 rect.height = int(rect.height / self.zoom_scale) 45 return rect 46 47 @property 48 def zoom_scale(self) -> float: 49 """Property to get the current zoom scale factor""" 50 return self._zoom_scale 51 52 @property 53 def zoom_factor(self) -> float: 54 """Property to get the current log2 zoom factor""" 55 return self._zoom_factor 56 57 @property 58 def zoom_direction(self) -> float: 59 """Property to get the current zoom direction.""" 60 return self._zoom_direction 61 62 @property 63 def zoom_level(self) -> float: 64 """Property to get the current zoom level.""" 65 if self.zoom_direction == 0 and self.zoom_factor == 0: 66 return 1 67 elif self.zoom_factor == 0: 68 return 1 69 elif self.zoom_factor >= 1: 70 return self.zoom_factor + 1 71 else: 72 return self.zoom_factor - 1 73 74 def set_max_zoom(self, factor: float) -> None: 75 """ 76 Set the maximum zoom factor for the camera. 77 78 Args: 79 factor: The maximum zoom factor to set. 80 """ 81 self.__max_zoom_factor = factor 82 83 def get_max_zoom(self) -> float: 84 """ 85 Get the maximum zoom factor for the camera. 86 87 Returns: 88 The maximum zoom factor. 89 """ 90 return self.__max_zoom_factor 91 92 def is_visible(self, obj) -> bool: 93 """ 94 Check if an object is at least partially visible on the screen (Requires object to have a rect attribute). 95 96 Args: 97 obj: The object to check visibility for. 98 99 Returns: 100 True if the object is visible, False otherwise. 101 """ 102 return self.rect.colliderect(obj.rect) 103 104 def move(self, offset: Tuple[int | float, int | float] | pygame.math.Vector2) -> None: 105 """ 106 Move the camera by a given offset (tuple or vector). 107 108 Args: 109 offset: The offset to move the camera by. 110 """ 111 self._pos.x += offset[0] 112 self._pos.y += offset[1] 113 114 def move_at(self, new_pos: Tuple[int | float, int | float] | pygame.math.Vector2) -> None: 115 """ 116 Move the camera to a specific position (centered). 117 118 Args: 119 new_pos: The new position to move the camera to. 120 """ 121 self._pos.x = new_pos[0] + Display._res[0] / 2 122 self._pos.y = new_pos[1] + Display._res[1] / 2 123 124 def zoom(self, factor: int) -> None: 125 """ 126 Zoom the camera by a given step, adjusting its scale. 127 128 Args: 129 factor: The zoom factor to apply. 130 """ 131 if factor == 0: 132 return 133 factor = (factor + 1) if factor < 0 else (factor - 1) 134 scale = 2 ** (self.zoom_factor + factor) 135 self.zoom_at(scale) 136 137 def zoom_at(self, scale: float) -> None: 138 """ 139 Set the camera zoom to a specific scale, centering the view. 140 141 Args: 142 scale: The scale to set the camera zoom to. 143 """ 144 if not scale: 145 return 146 min_scale = 2 ** -self.__max_zoom_factor 147 max_scale = 2 ** self.__max_zoom_factor 148 scale = max(min_scale, min(max_scale, scale)) 149 factor = log2(scale) 150 direction = factor - self._zoom_factor 151 152 # Center the camera based on the new zoom scale 153 sign = 1 if scale < 0 else -1 154 self._offset.x = 1 / scale * Display._res[0] / 2 * sign + Display._res[0] / 2 155 self._offset.y = 1 / scale * Display._res[1] / 2 * sign + Display._res[1] / 2 156 157 self._zoom_scale = scale 158 self._zoom_factor = factor 159 self._zoom_direction = direction 160 161 def draw_shape(self, Shape: Shape, fill: int = 0) -> None: 162 """Draw a shape on the camera's surface if it is visible.""" 163 if not self.is_visible(Shape): 164 return 165 166 Shape._pos -= self._pos + self._offset 167 Shape._pos *= self._zoom_scale 168 Shape.draw(self.surface, fill, self._zoom_scale) 169 Shape._pos /= self._zoom_scale 170 Shape._pos += self._offset + self._pos 171 172 def draw_text(self, Txt: Text) -> None: 173 """Draw text on the camera's surface if it is visible.""" 174 if not self.is_visible(Txt): 175 return 176 177 Txt._pos -= self._pos + self._offset 178 Txt._pos *= self._zoom_scale 179 Txt.draw(self.surface, self._zoom_scale) 180 Txt._pos /= self._zoom_scale 181 Txt._pos += self._offset + self._pos 182 183 def draw_image(self, Image: Image) -> None: 184 """Draw an image on the camera's surface if it is visible.""" 185 if not self.is_visible(Image): 186 return 187 188 Image._pos -= self._pos + self._offset 189 Image._pos *= self._zoom_scale 190 Image.draw(self.surface, self._zoom_scale) 191 Image._pos /= self._zoom_scale 192 Image._pos += self._offset + self._pos 193 194 def _dynamic_zoom(self) -> None: 195 """Dynamically adjust the zoom based on the display's resolution.""" 196 scale = min((Display._res[0] / Display._new_res[0]),(Display._res[1] / Display._new_res[1])) 197 self.zoom_at(scale)
Camera class for the pyxora display environment. Handles position, zoom, and rendering of shapes, text, and images with camera-relative coordinates.
Note: Every Scene has a camera instance by default. You can create multiple instances but it's not recommended.
19 def __init__(self) -> None: 20 """Initialize the camera position, offset, zoom, and set initial zoom.""" 21 self._pos = vector(0, 0) 22 self._offset = vector(0, 0) 23 self._zoom_scale = 1 24 self._zoom_factor = 0 25 self._zoom_direction = 0 26 self.__max_zoom_factor = 3 27 self.zoom(1)
Initialize the camera position, offset, zoom, and set initial zoom.
29 @property 30 def surface(self) -> pygame.Surface: 31 """Property to the current drawing surface.""" 32 return Display.surface
Property to the current drawing surface.
34 @property 35 def position(self) -> pygame.math.Vector2: 36 """Property to get a copy of the position of the camera.""" 37 return self._pos.copy()
Property to get a copy of the position of the camera.
39 @property 40 def rect(self) -> pygame.Rect: 41 """Property to get the camera's rectangle area as a pygame.Rect.""" 42 rect = pygame.Rect(self._pos + self._offset, Display._res) 43 rect.width = int(rect.width / self.zoom_scale) 44 rect.height = int(rect.height / self.zoom_scale) 45 return rect
Property to get the camera's rectangle area as a pygame.Rect.
47 @property 48 def zoom_scale(self) -> float: 49 """Property to get the current zoom scale factor""" 50 return self._zoom_scale
Property to get the current zoom scale factor
52 @property 53 def zoom_factor(self) -> float: 54 """Property to get the current log2 zoom factor""" 55 return self._zoom_factor
Property to get the current log2 zoom factor
57 @property 58 def zoom_direction(self) -> float: 59 """Property to get the current zoom direction.""" 60 return self._zoom_direction
Property to get the current zoom direction.
62 @property 63 def zoom_level(self) -> float: 64 """Property to get the current zoom level.""" 65 if self.zoom_direction == 0 and self.zoom_factor == 0: 66 return 1 67 elif self.zoom_factor == 0: 68 return 1 69 elif self.zoom_factor >= 1: 70 return self.zoom_factor + 1 71 else: 72 return self.zoom_factor - 1
Property to get the current zoom level.
74 def set_max_zoom(self, factor: float) -> None: 75 """ 76 Set the maximum zoom factor for the camera. 77 78 Args: 79 factor: The maximum zoom factor to set. 80 """ 81 self.__max_zoom_factor = factor
Set the maximum zoom factor for the camera.
Arguments:
- factor: The maximum zoom factor to set.
83 def get_max_zoom(self) -> float: 84 """ 85 Get the maximum zoom factor for the camera. 86 87 Returns: 88 The maximum zoom factor. 89 """ 90 return self.__max_zoom_factor
Get the maximum zoom factor for the camera.
Returns:
The maximum zoom factor.
92 def is_visible(self, obj) -> bool: 93 """ 94 Check if an object is at least partially visible on the screen (Requires object to have a rect attribute). 95 96 Args: 97 obj: The object to check visibility for. 98 99 Returns: 100 True if the object is visible, False otherwise. 101 """ 102 return self.rect.colliderect(obj.rect)
Check if an object is at least partially visible on the screen (Requires object to have a rect attribute).
Arguments:
- obj: The object to check visibility for.
Returns:
True if the object is visible, False otherwise.
104 def move(self, offset: Tuple[int | float, int | float] | pygame.math.Vector2) -> None: 105 """ 106 Move the camera by a given offset (tuple or vector). 107 108 Args: 109 offset: The offset to move the camera by. 110 """ 111 self._pos.x += offset[0] 112 self._pos.y += offset[1]
Move the camera by a given offset (tuple or vector).
Arguments:
- offset: The offset to move the camera by.
114 def move_at(self, new_pos: Tuple[int | float, int | float] | pygame.math.Vector2) -> None: 115 """ 116 Move the camera to a specific position (centered). 117 118 Args: 119 new_pos: The new position to move the camera to. 120 """ 121 self._pos.x = new_pos[0] + Display._res[0] / 2 122 self._pos.y = new_pos[1] + Display._res[1] / 2
Move the camera to a specific position (centered).
Arguments:
- new_pos: The new position to move the camera to.
124 def zoom(self, factor: int) -> None: 125 """ 126 Zoom the camera by a given step, adjusting its scale. 127 128 Args: 129 factor: The zoom factor to apply. 130 """ 131 if factor == 0: 132 return 133 factor = (factor + 1) if factor < 0 else (factor - 1) 134 scale = 2 ** (self.zoom_factor + factor) 135 self.zoom_at(scale)
Zoom the camera by a given step, adjusting its scale.
Arguments:
- factor: The zoom factor to apply.
137 def zoom_at(self, scale: float) -> None: 138 """ 139 Set the camera zoom to a specific scale, centering the view. 140 141 Args: 142 scale: The scale to set the camera zoom to. 143 """ 144 if not scale: 145 return 146 min_scale = 2 ** -self.__max_zoom_factor 147 max_scale = 2 ** self.__max_zoom_factor 148 scale = max(min_scale, min(max_scale, scale)) 149 factor = log2(scale) 150 direction = factor - self._zoom_factor 151 152 # Center the camera based on the new zoom scale 153 sign = 1 if scale < 0 else -1 154 self._offset.x = 1 / scale * Display._res[0] / 2 * sign + Display._res[0] / 2 155 self._offset.y = 1 / scale * Display._res[1] / 2 * sign + Display._res[1] / 2 156 157 self._zoom_scale = scale 158 self._zoom_factor = factor 159 self._zoom_direction = direction
Set the camera zoom to a specific scale, centering the view.
Arguments:
- scale: The scale to set the camera zoom to.
161 def draw_shape(self, Shape: Shape, fill: int = 0) -> None: 162 """Draw a shape on the camera's surface if it is visible.""" 163 if not self.is_visible(Shape): 164 return 165 166 Shape._pos -= self._pos + self._offset 167 Shape._pos *= self._zoom_scale 168 Shape.draw(self.surface, fill, self._zoom_scale) 169 Shape._pos /= self._zoom_scale 170 Shape._pos += self._offset + self._pos
Draw a shape on the camera's surface if it is visible.
172 def draw_text(self, Txt: Text) -> None: 173 """Draw text on the camera's surface if it is visible.""" 174 if not self.is_visible(Txt): 175 return 176 177 Txt._pos -= self._pos + self._offset 178 Txt._pos *= self._zoom_scale 179 Txt.draw(self.surface, self._zoom_scale) 180 Txt._pos /= self._zoom_scale 181 Txt._pos += self._offset + self._pos
Draw text on the camera's surface if it is visible.
183 def draw_image(self, Image: Image) -> None: 184 """Draw an image on the camera's surface if it is visible.""" 185 if not self.is_visible(Image): 186 return 187 188 Image._pos -= self._pos + self._offset 189 Image._pos *= self._zoom_scale 190 Image.draw(self.surface, self._zoom_scale) 191 Image._pos /= self._zoom_scale 192 Image._pos += self._offset + self._pos
Draw an image on the camera's surface if it is visible.