Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge with pgzhelper: add scale, flip_x, flip_y properties, to Actor... #276

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 77 additions & 20 deletions pgzero/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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
)


Expand All @@ -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.

Expand All @@ -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

Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand Down
21 changes: 21 additions & 0 deletions test/test_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down