Source code for arcade.camera.default

from __future__ import annotations

from collections.abc import Generator
from contextlib import contextmanager
from typing import TYPE_CHECKING

from pyglet.math import Mat4, Vec2, Vec3
from typing_extensions import Self

from arcade.camera.data_types import DEFAULT_FAR, DEFAULT_NEAR_ORTHO
from arcade.types import Point
from arcade.window_commands import get_window

if TYPE_CHECKING:
    from arcade.context import ArcadeContext

__all__ = ()


[docs] class DefaultProjector: """ An extremely limited projector which lacks any kind of control. This is only here to act as the default camera used internally by Arcade. There should be no instance where a developer would want to use this class. The job of the default projector is to ensure that when no other Projector (Camera2D, OthrographicProjector, PerspectiveProjector, etc) is in use the projection and view matrices are correct such at (0.0, 0.0) is in the bottom left corner of the viewport and that one pixel equals one 'unit'. Args: context: The window context to bind the camera to. Defaults to the currently active context. """ def __init__(self, *, context: ArcadeContext | None = None): self._ctx: ArcadeContext = context or get_window().ctx self._viewport: tuple[int, int, int, int] | None = None self._scissor: tuple[int, int, int, int] | None = None self._matrix: Mat4 | None = None
[docs] def update_viewport(self): """ Called when the ArcadeContext's viewport or active framebuffer has been set. It only actually updates the viewport if no other camera is active. Also setting the viewport to match the size of the active framebuffer sets the viewport to None. """ # If another camera is active then the viewport was probably set # by camera.use() if self._ctx.current_camera != self: return if ( self._ctx.viewport[2] != self._ctx.fbo.width or self._ctx.viewport[3] != self._ctx.fbo.height ): self.viewport = self._ctx.viewport else: self.viewport = None self.use()
@property def viewport(self) -> tuple[int, int, int, int] | None: return self._viewport @viewport.setter def viewport(self, viewport: tuple[int, int, int, int] | None) -> None: self._viewport = viewport self._matrix = Mat4.orthogonal_projection( 0, self.width, 0, self.height, DEFAULT_NEAR_ORTHO, DEFAULT_FAR ) @viewport.deleter def viewport(self): self.viewport = None @property def scissor(self) -> tuple[int, int, int, int] | None: return self._scissor @scissor.setter def scissor(self, scissor: tuple[int, int, int, int] | None) -> None: self._scissor = scissor @scissor.deleter def scissor(self) -> None: self._scissor = None @property def width(self) -> int: if self._viewport is not None: return int(self._viewport[2]) return self._ctx.fbo.width @property def height(self) -> int: if self._viewport is not None: return int(self._viewport[3]) return self._ctx.fbo.height
[docs] def get_current_viewport(self) -> tuple[int, int, int, int]: if self._viewport is not None: return self._viewport return (0, 0, self._ctx.fbo.width, self._ctx.fbo.height)
[docs] def use(self) -> None: """ Set the window's Projection and View matrices. """ viewport = self.get_current_viewport() self._ctx.current_camera = self if self._ctx.viewport != viewport: self._ctx.active_framebuffer.viewport = viewport self._ctx.scissor = None if self._scissor is None else self._scissor self._ctx.view_matrix = Mat4() if self._matrix is None: self._matrix = Mat4.orthogonal_projection( 0, viewport[2], 0, viewport[3], DEFAULT_NEAR_ORTHO, DEFAULT_FAR ) self._ctx.projection_matrix = self._matrix
[docs] @contextmanager def activate(self) -> Generator[Self, None, None]: """ The context manager version of the use method. usable with the 'with' block. e.g. 'with ViewportProjector.activate() as cam: ...' """ previous_projector = self._ctx.current_camera previous_view = self._ctx.view_matrix previous_projection = self._ctx.projection_matrix previous_scissor = self._ctx.scissor previous_viewport = self._ctx.viewport try: self.use() yield self finally: self._ctx.viewport = previous_viewport self._ctx.scissor = previous_scissor self._ctx.projection_matrix = previous_projection self._ctx.view_matrix = previous_view self._ctx.current_camera = previous_projector
[docs] def project(self, world_coordinate: Point) -> Vec2: """ Take a Vec2 or Vec3 of coordinates and return the related screen coordinate """ x, y, *z = world_coordinate return Vec2(x, y)
[docs] def unproject(self, screen_coordinate: Point) -> Vec3: """ Map the screen pos to screen_coordinates. Due to the nature of viewport projector this does not do anything. """ x, y, *_z = screen_coordinate z = 0.0 if not _z else _z[0] return Vec3(x, y, z)