Skip to content

Commit ae42cf1

Browse files
alejcasAlejandro Casanovas
and
Alejandro Casanovas
authored
flip camera2D init and from_raw_data (#2072)
* flip camera2D init and from_raw_data from_raw_data cameras won't reference the same data objects but create new ones fix examples and tests * from_raw_data changed to from_camera_data reusing the provided objects fix example * fix tests * fix error on from_camera_data method * fix error on from_camera_data method * from_camera_data now uses camera init defaults instead of it's own default values * fix docstring * better docstrings as recommended by @MiCurry * better docstring for "from_camera_data" method as requested by @pushfoo --------- Co-authored-by: Alejandro Casanovas <janscas@users.noreply.github.com>
1 parent 3b98810 commit ae42cf1

File tree

7 files changed

+133
-125
lines changed

7 files changed

+133
-125
lines changed

arcade/camera/camera_2d.py

Lines changed: 109 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
from arcade.gl import Framebuffer
1515

1616
from arcade.window_commands import get_window
17+
1718
if TYPE_CHECKING:
1819
from arcade.application import Window
1920

20-
2121
__all__ = [
2222
'Camera2D'
2323
]
@@ -49,104 +49,40 @@ class Camera2D:
4949
Replacing the camera data and projection data may break controllers. Their
5050
contents are exposed via properties rather than directly to prevent this.
5151
52-
:param window: The Arcade Window to bind the camera to.
53-
Defaults to the currently active window.
54-
:param camera_data: A :py:class:`~arcade.camera.data.CameraData`
55-
describing the position, up, forward and zoom.
56-
:param projection_data: A :py:class:`~arcade.camera.data.OrthographicProjectionData`
57-
which describes the left, right, top, bottom, far, near planes and the viewport
58-
for an orthographic projection.
52+
:param viewport: A 4-int tuple which defines the pixel bounds which the camera will project to.
53+
:param position: The 2D position of the camera in the XY plane.
54+
:param up: A 2D vector which describes which direction is up (defines the +Y-axis of the camera space).
55+
:param zoom: A scalar value which is inversely proportional to the size of the camera projection.
56+
i.e. a zoom of 2.0 halves the size of the projection, doubling the perceived size of objects.
57+
:param projection: A 4-float tuple which defines the world space
58+
bounds which the camera projects to the viewport.
59+
:param near: The near clipping plane of the camera.
60+
:param far: The far clipping plane of the camera.
5961
:param render_target: The FrameBuffer that the camera uses. Defaults to the screen.
6062
If the framebuffer is not the default screen nothing drawn after this camera is used will
6163
show up. The FrameBuffer's internal viewport is ignored.
64+
:param window: The Arcade Window to bind the camera to.
65+
Defaults to the currently active window.
6266
"""
63-
def __init__(self, *,
64-
camera_data: Optional[CameraData] = None,
65-
projection_data: Optional[OrthographicProjectionData] = None,
67+
68+
def __init__(self,
69+
viewport: Optional[Tuple[int, int, int, int]] = None,
70+
position: Optional[Tuple[float, float]] = None,
71+
up: Tuple[float, float] = (0.0, 1.0),
72+
zoom: float = 1.0,
73+
projection: Optional[Tuple[float, float, float, float]] = None,
74+
near: float = -100.0,
75+
far: float = 100.0,
76+
*,
6677
render_target: Optional[Framebuffer] = None,
6778
window: Optional["Window"] = None):
6879

69-
if projection_data:
70-
left, right = projection_data.left, projection_data.right
71-
if projection_data.left == projection_data.right:
72-
raise ZeroProjectionDimension((
73-
f"projection width is 0 due to equal {left=}"
74-
f"and {right=} values"))
75-
bottom, top = projection_data.bottom, projection_data.top
76-
if bottom == top:
77-
raise ZeroProjectionDimension((
78-
f"projection height is 0 due to equal {bottom=}"
79-
f"and {top=}"))
80-
near, far = projection_data.near, projection_data.far
81-
if near == far:
82-
raise ZeroProjectionDimension(
83-
f"projection depth is 0 due to equal {near=}"
84-
f"and {far=} values"
85-
)
86-
8780
self._window: "Window" = window or get_window()
8881
self.render_target: Framebuffer = render_target or self._window.ctx.screen
8982
width, height = self.render_target.size
9083
half_width = width / 2
9184
half_height = height / 2
9285

93-
self._camera_data = camera_data or CameraData(
94-
position=(half_width, half_height, 0.0),
95-
up=(0.0, 1.0, 0.0),
96-
forward=(0.0, 0.0, -1.0),
97-
zoom=1.0
98-
)
99-
self._projection_data: OrthographicProjectionData = projection_data or OrthographicProjectionData(
100-
left=-half_width, right=half_width,
101-
bottom=-half_height, top=half_height,
102-
near=-100.0, far=100.0,
103-
viewport=(0, 0, width, height)
104-
)
105-
106-
self._ortho_projector: OrthographicProjector = OrthographicProjector(
107-
window=self._window,
108-
view=self._camera_data,
109-
projection=self._projection_data
110-
)
111-
112-
@classmethod
113-
def from_raw_data(
114-
cls,
115-
viewport: Optional[Tuple[int, int, int, int]] = None,
116-
position: Optional[Tuple[float, float]] = None,
117-
up: Tuple[float, float] = (0.0, 1.0),
118-
zoom: float = 1.0,
119-
projection: Optional[Tuple[float, float, float, float]] = None,
120-
near: float = -100.0,
121-
far: float = 100.0,
122-
*,
123-
render_target: Optional[Framebuffer] = None,
124-
window: Optional["Window"] = None
125-
) -> Self:
126-
"""
127-
Create a Camera2D without first defining CameraData or an OrthographicProjectionData object.
128-
129-
:param viewport: A 4-int tuple which defines the pixel bounds which the camera with project to.
130-
:param position: The 2D position of the camera in the XY plane.
131-
:param up: The 2D unit vector which defines the +Y-axis of the camera space.
132-
:param zoom: A scalar value which is inversely proportional to the size of the camera projection.
133-
i.e. a zoom of 2.0 halves the size of the projection, doubling the perceived size of objects.
134-
:param projection: A 4-float tuple which defines the world space
135-
bounds which the camera projects to the viewport.
136-
:param near: The near clipping plane of the camera.
137-
:param far: The far clipping plane of the camera.
138-
:param render_target: The FrameBuffer that the camera uses. Defaults to the screen.
139-
If the framebuffer is not the default screen nothing drawn after this camera is used will
140-
show up. The FrameBuffer's internal viewport is ignored.
141-
:param window: The Arcade Window to bind the camera to.
142-
Defaults to the currently active window.
143-
"""
144-
window = window or get_window()
145-
render_target = render_target or window.ctx.screen
146-
width, height = render_target.size
147-
half_width = width / 2
148-
half_height = height / 2
149-
15086
# Unpack projection, but only validate when it's given directly
15187
left, right, bottom, top = projection or (-half_width, half_width, -half_height, half_height)
15288
if projection:
@@ -165,26 +101,97 @@ def from_raw_data(
165101
)
166102

167103
_pos = position or (half_width, half_height)
168-
_data = CameraData(
104+
self._camera_data = CameraData(
169105
position=(_pos[0], _pos[1], 0.0),
170106
up=(up[0], up[1], 0.0),
171107
forward=(0.0, 0.0, -1.0),
172108
zoom=zoom
173109
)
174-
175-
_projection: OrthographicProjectionData = OrthographicProjectionData(
110+
self._projection_data: OrthographicProjectionData = OrthographicProjectionData(
176111
left=left, right=right,
177112
top=top, bottom=bottom,
178113
near=near, far=far,
179114
viewport=viewport or (0, 0, width, height)
180115
)
116+
self._ortho_projector: OrthographicProjector = OrthographicProjector(
117+
window=self._window,
118+
view=self._camera_data,
119+
projection=self._projection_data
120+
)
121+
122+
@classmethod
123+
def from_camera_data(cls, *,
124+
camera_data: Optional[CameraData] = None,
125+
projection_data: Optional[OrthographicProjectionData] = None,
126+
render_target: Optional[Framebuffer] = None,
127+
window: Optional["Window"] = None) -> Self:
128+
"""
129+
Make a ``Camera2D`` directly from data objects.
130+
131+
This :py:class:`classmethod` allows advanced users to:
132+
133+
#. skip or replace the default validation
134+
#. share ``camera_data`` or ``projection_data`` between cameras
135+
136+
.. warning:: Be careful when sharing data objects!
137+
**Any** action on a camera which changes a shared
138+
object changes it for **every** camera which uses
139+
the same object.
140+
141+
.. list-table::
142+
:header-rows: 1
143+
* - Shared Value
144+
- Example Use(s)
145+
* - ``camera_data``
146+
- Mini-maps, reflection, and ghosting effects.
147+
* - ``projection_data``
148+
- Simplified rendering configuration
149+
* - ``render_target``
150+
- Complex rendering setups
151+
152+
:param camera_data: A :py:class:`~arcade.camera.data.CameraData`
153+
describing the position, up, forward and zoom.
154+
:param projection_data: A :py:class:`~arcade.camera.data.OrthographicProjectionData`
155+
which describes the left, right, top, bottom, far, near planes and the viewport
156+
for an orthographic projection.
157+
:param render_target: The FrameBuffer that the camera uses. Defaults to the screen.
158+
If the framebuffer is not the default screen nothing drawn after this camera is used will
159+
show up. The FrameBuffer's internal viewport is ignored.
160+
:param window: The Arcade Window to bind the camera to.
161+
Defaults to the currently active window.
162+
"""
163+
164+
if projection_data:
165+
left, right = projection_data.left, projection_data.right
166+
if projection_data.left == projection_data.right:
167+
raise ZeroProjectionDimension((
168+
f"projection width is 0 due to equal {left=}"
169+
f"and {right=} values"))
170+
bottom, top = projection_data.bottom, projection_data.top
171+
if bottom == top:
172+
raise ZeroProjectionDimension((
173+
f"projection height is 0 due to equal {bottom=}"
174+
f"and {top=}"))
175+
near, far = projection_data.near, projection_data.far
176+
if near == far:
177+
raise ZeroProjectionDimension(
178+
f"projection depth is 0 due to equal {near=}"
179+
f"and {far=} values"
180+
)
181+
182+
# build a new camera with defaults and then apply the provided camera objects.
183+
new_camera = cls(render_target=render_target, window=window)
184+
if camera_data:
185+
new_camera._camera_data = camera_data
186+
if projection_data:
187+
new_camera._projection_data = projection_data
181188

182-
return cls(
183-
camera_data=_data,
184-
projection_data=_projection,
185-
window=window,
186-
render_target=render_target
189+
new_camera._ortho_projector = OrthographicProjector(
190+
window=new_camera._window,
191+
view=new_camera._camera_data,
192+
projection=new_camera._projection_data
187193
)
194+
return new_camera
188195

189196
@property
190197
def view_data(self) -> CameraData:
@@ -235,12 +242,12 @@ def left(self) -> float:
235242
236243
Useful for checking if a :py:class:`~arcade.Sprite` is on screen.
237244
"""
238-
return self._camera_data.position[0] + self._projection_data.left/self._camera_data.zoom
245+
return self._camera_data.position[0] + self._projection_data.left / self._camera_data.zoom
239246

240247
@left.setter
241248
def left(self, _left: float) -> None:
242-
self._camera_data.position =\
243-
(_left - self._projection_data.left / self._camera_data.zoom,)\
249+
self._camera_data.position = \
250+
(_left - self._projection_data.left / self._camera_data.zoom,) \
244251
+ self._camera_data.position[1:]
245252

246253
@property
@@ -249,12 +256,12 @@ def right(self) -> float:
249256
250257
Useful for checking if a :py:class:`~arcade.Sprite` is on screen.
251258
"""
252-
return self._camera_data.position[0] + self._projection_data.right/self._camera_data.zoom
259+
return self._camera_data.position[0] + self._projection_data.right / self._camera_data.zoom
253260

254261
@right.setter
255262
def right(self, _right: float) -> None:
256-
self._camera_data.position =\
257-
(_right - self._projection_data.right / self._camera_data.zoom,)\
263+
self._camera_data.position = \
264+
(_right - self._projection_data.right / self._camera_data.zoom,) \
258265
+ self._camera_data.position[1:]
259266

260267
@property
@@ -263,7 +270,7 @@ def bottom(self) -> float:
263270
264271
Useful for checking if a :py:class:`~arcade.Sprite` is on screen.
265272
"""
266-
return self._camera_data.position[1] + self._projection_data.bottom/self._camera_data.zoom
273+
return self._camera_data.position[1] + self._projection_data.bottom / self._camera_data.zoom
267274

268275
@bottom.setter
269276
def bottom(self, _bottom: float) -> None:
@@ -279,7 +286,7 @@ def top(self) -> float:
279286
280287
Useful for checking if a :py:class:`~arcade.Sprite` is on screen.
281288
"""
282-
return self._camera_data.position[1] + self._projection_data.top/self._camera_data.zoom
289+
return self._camera_data.position[1] + self._projection_data.top / self._camera_data.zoom
283290

284291
@top.setter
285292
def top(self, _top: float) -> None:

arcade/examples/camera_platform.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ def setup(self):
132132
self.scene.add_sprite("Player", self.player_sprite)
133133

134134
viewport = (0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
135-
self.camera = arcade.camera.Camera2D.from_raw_data(viewport=viewport)
136-
self.gui_camera = arcade.camera.Camera2D.from_raw_data(viewport=viewport)
135+
self.camera = arcade.camera.Camera2D(viewport=viewport)
136+
self.gui_camera = arcade.camera.Camera2D(viewport=viewport)
137137

138138
self.camera_shake = arcade.camera.grips.ScreenShake2D(self.camera.view_data,
139139
max_amplitude=12.5,

arcade/examples/full_screen_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def __init__(self):
4343

4444
# The camera used to update the viewport and projection on screen resize.
4545
# The position needs to be set to the bottom left corner.
46-
self.cam = arcade.camera.Camera2D.from_raw_data(position=(0.0, 0.0))
46+
self.cam = arcade.camera.Camera2D(position=(0.0, 0.0))
4747

4848
def on_draw(self):
4949
"""

arcade/examples/minimap.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ def __init__(self, width, height, title):
6262

6363
# Camera for sprites, and one for our GUI
6464
viewport = (0, 0, DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT)
65-
self.camera_sprites = arcade.camera.Camera2D.from_raw_data(viewport=viewport)
66-
self.camera_gui = arcade.camera.Camera2D.from_raw_data(viewport=viewport)
65+
self.camera_sprites = arcade.camera.Camera2D(viewport=viewport)
66+
self.camera_gui = arcade.camera.Camera2D(viewport=viewport)
6767

6868
def setup(self):
6969
""" Set up the game and initialize the variables. """

arcade/examples/minimap_camera.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def __init__(self, width, height, title):
5757
MINIMAP_WIDTH, MINIMAP_HEIGHT)
5858
minimap_projection = (-MAP_PROJECTION_WIDTH/2, MAP_PROJECTION_WIDTH/2,
5959
-MAP_PROJECTION_HEIGHT/2, MAP_PROJECTION_HEIGHT/2)
60-
self.camera_minimap = arcade.camera.Camera2D.from_raw_data(
60+
self.camera_minimap = arcade.camera.Camera2D(
6161
viewport=minimap_viewport, projection=minimap_projection
6262
)
6363

@@ -68,8 +68,8 @@ def __init__(self, width, height, title):
6868

6969
# Camera for sprites, and one for our GUI
7070
viewport = (0, 0, DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT)
71-
self.camera_sprites = arcade.camera.Camera2D.from_raw_data(viewport=viewport)
72-
self.camera_gui = arcade.camera.Camera2D.from_raw_data(viewport=viewport)
71+
self.camera_sprites = arcade.camera.Camera2D(viewport=viewport)
72+
self.camera_gui = arcade.camera.Camera2D(viewport=viewport)
7373

7474
self.selected_camera = self.camera_minimap
7575

arcade/examples/perspective.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def __init__(self):
106106
)
107107
self.time = 0
108108

109-
self.offscreen_cam = arcade.camera.Camera2D.from_raw_data(
109+
self.offscreen_cam = arcade.camera.Camera2D(
110110
position=(0.0, 0.0),
111111
viewport=(0, 0, self.fbo.width, self.fbo.height),
112112
projection=(0, self.fbo.width, 0, self.fbo.height)

0 commit comments

Comments
 (0)