From 82f148edd311b6bb1449db169d2d92b34509159c Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 07:08:15 -0400 Subject: [PATCH 01/12] Fix Camera2D.__init__ ignoring render target size * Use final render_target.size instead of window size * Clean up . accesses for shorter code + tiny speed boost --- arcade/camera/camera_2d.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arcade/camera/camera_2d.py b/arcade/camera/camera_2d.py index 3b59283ce9..48054d16f3 100644 --- a/arcade/camera/camera_2d.py +++ b/arcade/camera/camera_2d.py @@ -59,9 +59,9 @@ 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 @@ -73,7 +73,7 @@ def __init__(self, *, -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 + (0, 0, width, height) # Viewport ) self._ortho_projector: OrthographicProjector = OrthographicProjector( From af0321ab99c4fa6cf3341d88b063c02af3068c1d Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 07:19:06 -0400 Subject: [PATCH 02/12] Fix Camera2D.from_raw_data ignoring target size * Get render_target earlier * Use render_target.size to define width and height * Make code cleaner +_ a tiny bit faster with less . access --- arcade/camera/camera_2d.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arcade/camera/camera_2d.py b/arcade/camera/camera_2d.py index 48054d16f3..6a904d06dc 100644 --- a/arcade/camera/camera_2d.py +++ b/arcade/camera/camera_2d.py @@ -114,9 +114,10 @@ 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( @@ -131,14 +132,14 @@ def from_raw_data( 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 + viewport or (0, 0, width, height) # Viewport ) return Camera2D( camera_data=_data, projection_data=_projection, window=window, - render_target=(render_target or window.ctx.screen) + render_target=render_target ) @property From 4a597671c3bde2c74867324ff875ee157215f670 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 07:43:01 -0400 Subject: [PATCH 03/12] Add tests for using render_target size --- tests/unit/camera/test_orthographic_camera.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/unit/camera/test_orthographic_camera.py b/tests/unit/camera/test_orthographic_camera.py index 3ccf8e9684..b236777180 100644 --- a/tests/unit/camera/test_orthographic_camera.py +++ b/tests/unit/camera/test_orthographic_camera.py @@ -1,6 +1,7 @@ import pytest as pytest from arcade import camera, Window +from arcade.camera import Camera2D def test_orthographic_projector_use(window: Window): @@ -44,6 +45,44 @@ def test_orthographic_projector_activate(window: Window): window.default_camera.use() +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 + + # These may be bugged? + #assert ortho_camera.viewport == (0, width, 0, 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 + + @pytest.mark.parametrize("width, height", [(800, 600), (1280, 720), (500, 500)]) def test_orthographic_projector_map_coordinates(window: Window, width, height): # Given From 6f07dce593efc98cf40d24f146bca0613f26d975 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 07:45:08 -0400 Subject: [PATCH 04/12] Use durable keyword args in Camera2D.__init__ --- arcade/camera/camera_2d.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arcade/camera/camera_2d.py b/arcade/camera/camera_2d.py index 6a904d06dc..f85515e1dd 100644 --- a/arcade/camera/camera_2d.py +++ b/arcade/camera/camera_2d.py @@ -64,16 +64,16 @@ def __init__(self, *, 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, width, 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( From 71ca620ae1b63abba55d84c72203c7724e4161bc Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 07:54:43 -0400 Subject: [PATCH 05/12] Use more durable keyword arguments in Camera2D.from_raw_data --- arcade/camera/camera_2d.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arcade/camera/camera_2d.py b/arcade/camera/camera_2d.py index f85515e1dd..336c16dfa1 100644 --- a/arcade/camera/camera_2d.py +++ b/arcade/camera/camera_2d.py @@ -121,18 +121,18 @@ def from_raw_data( _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 + 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, width, 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( From 615ee412bd4a2656c64cb7555182f9602617d918 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 07:57:03 -0400 Subject: [PATCH 06/12] Make Camera2D.from_raw_data a class method * Make it a class method with a typing_extensions.Self return type * Add a test for subclassing safety --- arcade/camera/camera_2d.py | 11 +++++++---- tests/unit/camera/test_orthographic_camera.py | 8 ++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/arcade/camera/camera_2d.py b/arcade/camera/camera_2d.py index 336c16dfa1..708908e474 100644 --- a/arcade/camera/camera_2d.py +++ b/arcade/camera/camera_2d.py @@ -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 @@ -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), @@ -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. @@ -120,7 +123,7 @@ def from_raw_data( half_height = height / 2 _pos = position or (half_width, half_height) - _data: CameraData = CameraData( + _data: Self = CameraData( position=(_pos[0], _pos[1], 0.0), up=(up[0], up[1], 0.0), forward=(0.0, 0.0, -1.0), @@ -135,7 +138,7 @@ def from_raw_data( viewport=viewport or (0, 0, width, height) ) - return Camera2D( + return cls( camera_data=_data, projection_data=_projection, window=window, diff --git a/tests/unit/camera/test_orthographic_camera.py b/tests/unit/camera/test_orthographic_camera.py index b236777180..f36d9a7b94 100644 --- a/tests/unit/camera/test_orthographic_camera.py +++ b/tests/unit/camera/test_orthographic_camera.py @@ -45,6 +45,14 @@ def test_orthographic_projector_activate(window: Window): window.default_camera.use() +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 From b97d8759c76a22c621b35a681868e48e09a00454 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 08:08:01 -0400 Subject: [PATCH 07/12] Fix type issue --- arcade/camera/camera_2d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/camera/camera_2d.py b/arcade/camera/camera_2d.py index 708908e474..6da5a8c1a9 100644 --- a/arcade/camera/camera_2d.py +++ b/arcade/camera/camera_2d.py @@ -123,7 +123,7 @@ def from_raw_data( half_height = height / 2 _pos = position or (half_width, half_height) - _data: Self = CameraData( + _data = CameraData( position=(_pos[0], _pos[1], 0.0), up=(up[0], up[1], 0.0), forward=(0.0, 0.0, -1.0), From b32f1b2b622d941104f876cb00bc892eaeeb8297 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 08:21:59 -0400 Subject: [PATCH 08/12] Correct Camera2D.viewport docstring per Discord discussion with Dragon --- arcade/camera/camera_2d.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arcade/camera/camera_2d.py b/arcade/camera/camera_2d.py index 6da5a8c1a9..30a37374ff 100644 --- a/arcade/camera/camera_2d.py +++ b/arcade/camera/camera_2d.py @@ -501,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 From c5e05866124ec232d93df3dce1fd95ab8cf69e52 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 08:22:13 -0400 Subject: [PATCH 09/12] Correct the Camera2D tests to check viewport property --- tests/unit/camera/test_orthographic_camera.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/unit/camera/test_orthographic_camera.py b/tests/unit/camera/test_orthographic_camera.py index f36d9a7b94..af00d07a05 100644 --- a/tests/unit/camera/test_orthographic_camera.py +++ b/tests/unit/camera/test_orthographic_camera.py @@ -71,12 +71,11 @@ def test_camera2d_init_uses_render_target_size(window: Window, width, height): assert ortho_camera.viewport_width == width assert ortho_camera.viewport_height == height - # These may be bugged? - #assert ortho_camera.viewport == (0, width, 0, 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 + 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) @@ -90,6 +89,12 @@ def test_camera2d_from_raw_data_uses_render_target_size(window: Window, width, h 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", [(800, 600), (1280, 720), (500, 500)]) def test_orthographic_projector_map_coordinates(window: Window, width, height): From f5fe528e4deb5ce396fca29f9d3be25d6023f289 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 08:25:21 -0400 Subject: [PATCH 10/12] Move Camera2D tests to their own file --- tests/unit/camera/test_camera2d.py | 55 +++++++++++++++++++ tests/unit/camera/test_orthographic_camera.py | 53 ------------------ 2 files changed, 55 insertions(+), 53 deletions(-) create mode 100644 tests/unit/camera/test_camera2d.py diff --git a/tests/unit/camera/test_camera2d.py b/tests/unit/camera/test_camera2d.py new file mode 100644 index 0000000000..404c98055a --- /dev/null +++ b/tests/unit/camera/test_camera2d.py @@ -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 diff --git a/tests/unit/camera/test_orthographic_camera.py b/tests/unit/camera/test_orthographic_camera.py index af00d07a05..ac9cc11c22 100644 --- a/tests/unit/camera/test_orthographic_camera.py +++ b/tests/unit/camera/test_orthographic_camera.py @@ -1,12 +1,10 @@ import pytest as pytest from arcade import camera, Window -from arcade.camera import Camera2D def test_orthographic_projector_use(window: Window): # Given - from pyglet.math import Mat4 ortho_camera = camera.OrthographicProjector() view_matrix = ortho_camera._generate_view_matrix() @@ -45,57 +43,6 @@ def test_orthographic_projector_activate(window: Window): window.default_camera.use() -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 - - @pytest.mark.parametrize("width, height", [(800, 600), (1280, 720), (500, 500)]) def test_orthographic_projector_map_coordinates(window: Window, width, height): # Given From 270d316cb7d564164e8aac5b0235c9bb8feaf5d4 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 08:26:00 -0400 Subject: [PATCH 11/12] Move orthographic projector tests to better named file --- ...test_orthographic_camera.py => test_orthographic_projector.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/unit/camera/{test_orthographic_camera.py => test_orthographic_projector.py} (100%) diff --git a/tests/unit/camera/test_orthographic_camera.py b/tests/unit/camera/test_orthographic_projector.py similarity index 100% rename from tests/unit/camera/test_orthographic_camera.py rename to tests/unit/camera/test_orthographic_projector.py From e49065bb592a73f844978c447d21c75cc3b4d8bd Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 6 Apr 2024 08:26:16 -0400 Subject: [PATCH 12/12] Add missing newline at end of file --- tests/unit/camera/test_orthographic_projector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/camera/test_orthographic_projector.py b/tests/unit/camera/test_orthographic_projector.py index ac9cc11c22..de38b3643d 100644 --- a/tests/unit/camera/test_orthographic_projector.py +++ b/tests/unit/camera/test_orthographic_projector.py @@ -183,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)) - ) \ No newline at end of file + )