1414from arcade .gl import Framebuffer
1515
1616from arcade .window_commands import get_window
17+
1718if 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 :
0 commit comments