diff --git a/pgzero/actor.py b/pgzero/actor.py index e8697e9..8a79a36 100644 --- a/pgzero/actor.py +++ b/pgzero/actor.py @@ -48,7 +48,7 @@ def calculate_anchor(value, dim, total): MAX_ALPHA = 255 # Based on pygame's max alpha. -def transform_anchor(ax, ay, w, h, angle): +def transform_anchor(ax, ay, w, h, angle, scale=1.0): """Transform anchor based upon a rotation of a surface of size w x h.""" theta = -radians(angle) @@ -68,8 +68,8 @@ def transform_anchor(ax, ay, w, h, angle): ray = cax * sintheta + cay * costheta return ( - tw * 0.5 + rax, - th * 0.5 + ray + (tw * 0.5 + rax)*scale, + (th * 0.5 + ray)*scale ) @@ -80,6 +80,22 @@ def _set_angle(actor, current_surface): return pygame.transform.rotate(current_surface, actor._angle) +def _set_scale(actor, current_surface): + if actor._scale == 1.0: + # No changes required for default scale. + return current_surface + new_width = current_surface.get_width() * actor._scale + new_height = current_surface.get_height() * actor._scale + return pygame.transform.scale(current_surface, (new_width, new_height)) + + +def _set_flip(actor, current_surface): + if (not actor._flip_x) and (not actor._flip_y): + # No changes required for default flip. + return current_surface + return pygame.transform.flip(current_surface, actor._flip_x, actor._flip_y) + + def _set_opacity(actor, current_surface): alpha = int(actor.opacity * MAX_ALPHA + 0.5) # +0.5 for rounding up. @@ -104,8 +120,11 @@ class Actor: a for a in dir(rect.ZRect) if not a.startswith("_") ] - function_order = [_set_opacity, _set_angle] + function_order = [_set_opacity, _set_scale, _set_flip, _set_angle] _anchor = _anchor_value = (0, 0) + _scale = 1.0 + _flip_x = False + _flip_y = False _angle = 0.0 _opacity = 1.0 @@ -236,29 +255,65 @@ def _calc_anchor(self): ay = calculate_anchor(ay, 'y', oh) self._untransformed_anchor = ax, ay if self._angle == 0.0: - self._anchor = self._untransformed_anchor + anchor = self._untransformed_anchor + self._anchor = (anchor[0] * self._scale, anchor[1] * self._scale) else: - self._anchor = transform_anchor(ax, ay, ow, oh, self._angle) - - @property - def angle(self): - return self._angle + self._anchor = transform_anchor(ax, ay, ow, oh, self._angle, self._scale) - @angle.setter - def angle(self, angle): - self._angle = angle + def _transform(self): w, h = self._orig_surf.get_size() - ra = radians(angle) + ra = radians(self._angle) sin_a = sin(ra) cos_a = cos(ra) - self.height = abs(w * sin_a) + abs(h * cos_a) - self.width = abs(w * cos_a) + abs(h * sin_a) + self.height = (abs(w * sin_a) + abs(h * cos_a))*self._scale + self.width = (abs(w * cos_a) + abs(h * sin_a))*self._scale ax, ay = self._untransformed_anchor p = self.pos - self._anchor = transform_anchor(ax, ay, w, h, angle) + self._anchor = transform_anchor(ax, ay, w, h, self._angle, self._scale) self.pos = p - self._update_transform(_set_angle) + + @property + def angle(self): + return self._angle + + @angle.setter + def angle(self, angle): + if self._angle != angle: + self._angle = angle + self._transform() + self._update_transform(_set_angle) + + @property + def scale(self): + return self._scale + + @scale.setter + def scale(self, scale): + if self._scale != scale: + self._scale = scale + self._transform() + self._update_transform(_set_scale) + + @property + def flip_x(self): + return self._flip_x + + @flip_x.setter + def flip_x(self, flip_x): + if self._flip_x != flip_x: + self._flip_x = flip_x + self._update_transform(_set_flip) + + @property + def flip_y(self): + return self._flip_y + + @flip_y.setter + def flip_y(self, flip_y): + if self._flip_y != flip_y: + self._flip_y = flip_y + self._update_transform(_set_flip) @property def opacity(self): @@ -277,8 +332,10 @@ def opacity(self): @opacity.setter def opacity(self, opacity): # Clamp the opacity to the allowable range. - self._opacity = min(1.0, max(0.0, opacity)) - self._update_transform(_set_opacity) + new_opacity = min(1.0, max(0.0, opacity)) + if self._opacity != new_opacity: + self._opacity = new_opacity + self._update_transform(_set_opacity) @property def pos(self): diff --git a/test/test_actor.py b/test/test_actor.py index b74410c..425d2f7 100644 --- a/test/test_actor.py +++ b/test/test_actor.py @@ -110,6 +110,27 @@ def test_rotation(self): a.angle += 1.0 self.assertEqual(a.pos, (100.0, 100.0)) + def test_no_scaling(self): + a = Actor('alien', pos=(100.0, 100.0)) + originial_size = (a.width, a.height) + + a.scale = 1 + self.assertEqual((a.width, a.height) + a.pos, originial_size + a.pos) + + def test_scale_down(self): + a = Actor('alien', pos=(100.0, 100.0)) + scale = 0.25 + exp_size = (a.width*scale, a.height*scale) + a.scale = scale + self.assertEqual((a.width, a.height) + a.pos, exp_size + (100.0, 100.0)) + + def test_scale_up(self): + a = Actor('alien', pos=(100.0, 100.0)) + scale = 2.5 + exp_size = (a.width*scale, a.height*scale) + a.scale = scale + self.assertEqual((a.width, a.height) + a.pos, exp_size + (100.0, 100.0)) + def test_opacity_default(self): """Ensure opacity is initially set to its default value.""" a = Actor('alien')