Skip to content

Commit 1fe7b70

Browse files
committed
add flip_x, flip_y, scale properties to Actor
1 parent 46a889f commit 1fe7b70

File tree

2 files changed

+96
-19
lines changed

2 files changed

+96
-19
lines changed

pgzero/actor.py

Lines changed: 75 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def calculate_anchor(value, dim, total):
4848
MAX_ALPHA = 255 # Based on pygame's max alpha.
4949

5050

51-
def transform_anchor(ax, ay, w, h, angle):
51+
def transform_anchor(ax, ay, w, h, angle, scale=1.0):
5252
"""Transform anchor based upon a rotation of a surface of size w x h."""
5353
theta = -radians(angle)
5454

@@ -68,8 +68,8 @@ def transform_anchor(ax, ay, w, h, angle):
6868
ray = cax * sintheta + cay * costheta
6969

7070
return (
71-
tw * 0.5 + rax,
72-
th * 0.5 + ray
71+
(tw * 0.5 + rax)*scale,
72+
(th * 0.5 + ray)*scale
7373
)
7474

7575

@@ -80,6 +80,22 @@ def _set_angle(actor, current_surface):
8080
return pygame.transform.rotate(current_surface, actor._angle)
8181

8282

83+
def _set_scale(actor, current_surface):
84+
if actor._scale == 1.0:
85+
# No changes required for default scale.
86+
return current_surface
87+
new_width = int(current_surface.get_width() * actor._scale)
88+
new_height = int(current_surface.get_height() * actor._scale)
89+
return pygame.transform.scale(current_surface, (new_width, new_height))
90+
91+
92+
def _set_flip(actor, current_surface):
93+
if (not actor._flip_x) and (not actor._flip_y):
94+
# No changes required for default flip.
95+
return current_surface
96+
return pygame.transform.flip(current_surface, actor._flip_x, actor._flip_y)
97+
98+
8399
def _set_opacity(actor, current_surface):
84100
alpha = int(actor.opacity * MAX_ALPHA + 0.5) # +0.5 for rounding up.
85101

@@ -104,8 +120,11 @@ class Actor:
104120
a for a in dir(rect.ZRect) if not a.startswith("_")
105121
]
106122

107-
function_order = [_set_opacity, _set_angle]
123+
function_order = [_set_opacity, _set_scale, _set_flip, _set_angle]
108124
_anchor = _anchor_value = (0, 0)
125+
_scale = 1.0
126+
_flip_x = False
127+
_flip_y = False
109128
_angle = 0.0
110129
_opacity = 1.0
111130

@@ -238,27 +257,62 @@ def _calc_anchor(self):
238257
if self._angle == 0.0:
239258
self._anchor = self._untransformed_anchor
240259
else:
241-
self._anchor = transform_anchor(ax, ay, ow, oh, self._angle)
242-
243-
@property
244-
def angle(self):
245-
return self._angle
260+
self._anchor = transform_anchor(ax, ay, ow, oh, self._angle, self._scale)
246261

247-
@angle.setter
248-
def angle(self, angle):
249-
self._angle = angle
262+
def _transform(self):
250263
w, h = self._orig_surf.get_size()
251264

252-
ra = radians(angle)
265+
ra = radians(self._angle)
253266
sin_a = sin(ra)
254267
cos_a = cos(ra)
255-
self.height = abs(w * sin_a) + abs(h * cos_a)
256-
self.width = abs(w * cos_a) + abs(h * sin_a)
268+
self.height = int((abs(w * sin_a) + abs(h * cos_a))*self._scale)
269+
self.width = int((abs(w * cos_a) + abs(h * sin_a))*self._scale)
257270
ax, ay = self._untransformed_anchor
258271
p = self.pos
259-
self._anchor = transform_anchor(ax, ay, w, h, angle)
272+
self._anchor = transform_anchor(ax, ay, w, h, self._angle, self._scale)
260273
self.pos = p
261-
self._update_transform(_set_angle)
274+
275+
@property
276+
def angle(self):
277+
return self._angle
278+
279+
@angle.setter
280+
def angle(self, angle):
281+
if self._angle != angle:
282+
self._angle = angle
283+
self._transform()
284+
self._update_transform(_set_angle)
285+
286+
@property
287+
def scale(self):
288+
return self._scale
289+
290+
@scale.setter
291+
def scale(self, scale):
292+
if self._scale != scale:
293+
self._scale = scale
294+
self._transform()
295+
self._update_transform(_set_scale)
296+
297+
@property
298+
def flip_x(self):
299+
return self._flip_x
300+
301+
@flip_x.setter
302+
def flip_x(self, flip_x):
303+
if self._flip_x != flip_x:
304+
self._flip_x = flip_x
305+
self._update_transform(_set_flip)
306+
307+
@property
308+
def flip_y(self):
309+
return self._flip_y
310+
311+
@flip_y.setter
312+
def flip_y(self, flip_y):
313+
if self._flip_y != flip_y:
314+
self._flip_y = flip_y
315+
self._update_transform(_set_flip)
262316

263317
@property
264318
def opacity(self):
@@ -277,8 +331,10 @@ def opacity(self):
277331
@opacity.setter
278332
def opacity(self, opacity):
279333
# Clamp the opacity to the allowable range.
280-
self._opacity = min(1.0, max(0.0, opacity))
281-
self._update_transform(_set_opacity)
334+
new_opacity = min(1.0, max(0.0, opacity))
335+
if self._opacity != new_opacity:
336+
self._opacity = new_opacity
337+
self._update_transform(_set_opacity)
282338

283339
@property
284340
def pos(self):

test/test_actor.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,27 @@ def test_rotation(self):
110110
a.angle += 1.0
111111
self.assertEqual(a.pos, (100.0, 100.0))
112112

113+
def test_no_scaling(self):
114+
a = Actor('alien', pos=(100.0, 100.0))
115+
originial_size = (a.width, a.height)
116+
117+
a.scale = 1
118+
self.assertEqual((a.width, a.height) + a.pos, originial_size + a.pos)
119+
120+
def test_scale_down(self):
121+
a = Actor('alien', pos=(100.0, 100.0))
122+
scale = 0.25
123+
exp_size = (int(a.width*scale), int(a.height*scale))
124+
a.scale = scale
125+
self.assertEqual((a.width, a.height) + a.pos, exp_size + (100.0, 100.0))
126+
127+
def test_scale_up(self):
128+
a = Actor('alien', pos=(100.0, 100.0))
129+
scale = 2.5
130+
exp_size = (int(a.width*scale), int(a.height*scale))
131+
a.scale = scale
132+
self.assertEqual((a.width, a.height) + a.pos, exp_size + (100.0, 100.0))
133+
113134
def test_opacity_default(self):
114135
"""Ensure opacity is initially set to its default value."""
115136
a = Actor('alien')

0 commit comments

Comments
 (0)