Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 36 additions & 30 deletions arcade/camera/camera_2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from math import degrees, radians, atan2, cos, sin
from contextlib import contextmanager

from typing_extensions import Self

from arcade.camera.orthographic import OrthographicProjector
from arcade.camera.data_types import CameraData, OrthographicProjectionData, Projector
from arcade.gl import Framebuffer
Expand Down Expand Up @@ -59,21 +61,21 @@ def __init__(self, *,
window: Optional["Window"] = None):
self._window: "Window" = window or get_window()
self.render_target: Framebuffer = render_target or self._window.ctx.screen

half_width = self._window.width / 2
half_height = self._window.height / 2
width, height = self.render_target.size
half_width = width / 2
half_height = height / 2

self._data = camera_data or CameraData(
(half_width, half_height, 0.0), # position
(0.0, 1.0, 0.0), # up vector
(0.0, 0.0, -1.0), # forward vector
1.0 # zoom
position=(half_width, half_height, 0.0),
up=(0.0, 1.0, 0.0),
forward=(0.0, 0.0, -1.0),
zoom=1.0
)
self._projection: OrthographicProjectionData = projection_data or OrthographicProjectionData(
-half_width, half_width, # Left and Right.
-half_height, half_height, # Bottom and Top.
0.0, 100.0, # Near and Far.
(0, 0, self._window.width, self._window.height) # Viewport
left=-half_width, right=half_width,
bottom=-half_height, top=half_height,
near=0.0, far=100.0,
viewport=(0, 0, width, height)
)

self._ortho_projector: OrthographicProjector = OrthographicProjector(
Expand All @@ -82,8 +84,9 @@ def __init__(self, *,
projection=self._projection
)

@staticmethod
@classmethod
def from_raw_data(
cls,
viewport: Optional[Tuple[int, int, int, int]] = None,
position: Optional[Tuple[float, float]] = None,
up: Tuple[float, float] = (0.0, 1.0),
Expand All @@ -94,7 +97,7 @@ def from_raw_data(
*,
render_target: Optional[Framebuffer] = None,
window: Optional["Window"] = None
):
) -> Self:
"""
Create a Camera2D without first defining CameraData or an OrthographicProjectionData object.

Expand All @@ -114,31 +117,32 @@ def from_raw_data(
Defaults to the currently active window.
"""
window = window or get_window()

half_width = window.width / 2
half_height = window.height / 2
render_target = render_target or window.ctx.screen
width, height = render_target.size
half_width = width / 2
half_height = height / 2

_pos = position or (half_width, half_height)
_data: CameraData = CameraData(
(_pos[0], _pos[1], 0.0), # position
(up[0], up[1], 0.0), # up vector
(0.0, 0.0, -1.0), # forward vector
zoom # zoom
_data = CameraData(
position=(_pos[0], _pos[1], 0.0),
up=(up[0], up[1], 0.0),
forward=(0.0, 0.0, -1.0),
zoom=zoom
)

left, right, bottom, top = projection or (-half_width, half_width, -half_height, half_height)
_projection: OrthographicProjectionData = OrthographicProjectionData(
left, right, # Left and Right.
top, bottom, # Bottom and Top.
near or 0.0, far or 100.0, # Near and Far.
viewport or (0, 0, window.width, window.height) # Viewport
left=left, right=right,
top=top, bottom=bottom,
near=near or 0.0, far=far or 100.0,
viewport=viewport or (0, 0, width, height)
)

return Camera2D(
return cls(
camera_data=_data,
projection_data=_projection,
window=window,
render_target=(render_target or window.ctx.screen)
render_target=render_target
)

@property
Expand Down Expand Up @@ -497,9 +501,11 @@ def projection_far(self, _far: float) -> None:

@property
def viewport(self) -> Tuple[int, int, int, int]:
"""
The pixel area that will be drawn to while the camera is active.
(left, right, bottom, top)
"""Get/set pixels of the ``render_target`` drawn to when active.

The pixel area is defined as integer pixel coordinates starting
from the bottom left of ``self.render_target``. They are ordered
as ``(left, bottom, width, height)``.
"""
return self._projection.viewport

Expand Down
55 changes: 55 additions & 0 deletions tests/unit/camera/test_camera2d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pytest as pytest

from arcade import Window
from arcade.camera import Camera2D


def test_camera2d_from_raw_data_inheritance_safety(window: Window):
class MyCamera2D(Camera2D):
...

subclassed = MyCamera2D.from_raw_data(zoom=10.0)
assert isinstance(subclassed, MyCamera2D)


RENDER_TARGET_SIZES = [
(800, 600), # Normal window size
(1280, 720), # Bigger
(16, 16) # Tiny
]


@pytest.mark.parametrize("width, height", RENDER_TARGET_SIZES)
def test_camera2d_init_uses_render_target_size(window: Window, width, height):

size = (width, height)
texture = window.ctx.texture(size, components=4)
framebuffer = window.ctx.framebuffer(color_attachments=[texture])

ortho_camera = Camera2D(render_target=framebuffer)
assert ortho_camera.viewport_width == width
assert ortho_camera.viewport_height == height

assert ortho_camera.viewport == (0, 0, width, height)
assert ortho_camera.viewport_left == 0
assert ortho_camera.viewport_right == width
assert ortho_camera.viewport_bottom == 0
assert ortho_camera.viewport_top == height


@pytest.mark.parametrize("width, height", RENDER_TARGET_SIZES)
def test_camera2d_from_raw_data_uses_render_target_size(window: Window, width, height):

size = (width, height)
texture = window.ctx.texture(size, components=4)
framebuffer = window.ctx.framebuffer(color_attachments=[texture])

ortho_camera = Camera2D.from_raw_data(render_target=framebuffer)
assert ortho_camera.viewport_width == width
assert ortho_camera.viewport_height == height

assert ortho_camera.viewport == (0, 0, width, height)
assert ortho_camera.viewport_left == 0
assert ortho_camera.viewport_right == width
assert ortho_camera.viewport_bottom == 0
assert ortho_camera.viewport_top == height
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

def test_orthographic_projector_use(window: Window):
# Given
from pyglet.math import Mat4
ortho_camera = camera.OrthographicProjector()

view_matrix = ortho_camera._generate_view_matrix()
Expand Down Expand Up @@ -184,4 +183,4 @@ def test_orthographic_projector_map_coordinates_zoom(window: Window, width, heig
ortho_camera.map_screen_to_world_coordinate(mouse_pos_b)
==
pytest.approx(((100 - half_width)*4.0, (100 - half_height)*4.0, 0.0))
)
)