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

Flip Y Axis #237

Merged
merged 20 commits into from
May 5, 2019
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
14 changes: 11 additions & 3 deletions ppb/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,18 @@ def in_frame(self, sprite: BaseSprite) -> bool:
self.frame_bottom >= sprite.top
)

def translate_to_frame(self, point:Vector) -> Vector:
def translate_to_frame(self, point: Vector) -> Vector:
"""
Converts a vector from pixel-based window to in-game coordinate space
"""
offset = (point - self.viewport_offset) * (1/self.pixel_ratio)
return self.position + offset
loc = self.position + offset
return loc.update(y=-loc.y)

def translate_to_viewport(self, point:Vector) -> Vector:
def translate_to_viewport(self, point: Vector) -> Vector:
"""
Converts a vector from in-game to pixel-based window coordinate space
"""
point = point.update(y=-point.y)
offset = (point - self.position) * self.pixel_ratio
return self.viewport_offset + offset
98 changes: 66 additions & 32 deletions ppb/sprites.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
from numbers import Number
from os.path import realpath
from pathlib import Path
from typing import Dict, Iterable, AnyStr, Sequence
from typing import Dict, Iterable, Sequence
from typing import Union

from ppb import Vector
from ppb.events import EventMixin

import ppb_vector.vector2


TOP = "top"
BOTTOM = "bottom"
Expand All @@ -26,7 +28,7 @@ class Side:
BOTTOM: ('y', 1)
}

def __init__(self, parent: 'BaseSprite',side: AnyStr):
def __init__(self, parent: 'BaseSprite', side: str):
self.side = side
self.parent = parent

Expand Down Expand Up @@ -66,11 +68,14 @@ def __gt__(self, other):
def __lt__(self, other):
return self.value < other

def _lookup_side(self, side):
dimension, sign = self.sides[side]
return dimension, sign * self.parent._offset_value

@property
def value(self):
coordinate, multiplier = self.sides[self.side]
offset = self.parent._offset_value
return self.parent.position[coordinate] + (offset * multiplier)
dimension, offset = self._lookup_side(self.side)
return self.parent.position[dimension] + offset

@property
def top(self):
Expand All @@ -80,8 +85,7 @@ def top(self):
@top.setter
def top(self, value):
self._attribute_gate(TOP, [TOP, BOTTOM])
setattr(self.parent, self.side, value[0])
self.parent.top = value[1]
self.parent.position = self._mk_update_vector_side(TOP, value)

@property
def bottom(self):
Expand All @@ -91,8 +95,7 @@ def bottom(self):
@bottom.setter
def bottom(self, value):
self._attribute_gate(BOTTOM, [TOP, BOTTOM])
setattr(self.parent, self.side, value[0])
self.parent.bottom = value[1]
self.parent.position = self._mk_update_vector_side(BOTTOM, value)

@property
def left(self):
Expand All @@ -102,8 +105,7 @@ def left(self):
@left.setter
def left(self, value):
self._attribute_gate(LEFT, [LEFT, RIGHT])
setattr(self.parent, self.side, value[1])
self.parent.left = value[0]
self.parent.position = self._mk_update_vector_side(LEFT, value)

@property
def right(self):
Expand All @@ -113,8 +115,7 @@ def right(self):
@right.setter
def right(self, value):
self._attribute_gate(RIGHT, [LEFT, RIGHT])
setattr(self.parent, self.side, value[1])
self.parent.right = value[0]
self.parent.position = self._mk_update_vector_side(RIGHT, value)

@property
def center(self):
Expand All @@ -125,12 +126,46 @@ def center(self):

@center.setter
def center(self, value):
if self.side in (TOP, BOTTOM):
setattr(self.parent, self.side, value[1])
self.parent.center.x = value[0]
else:
setattr(self.parent, self.side, value[0])
self.parent.position.y = value[1]
self.parent.position = self._mk_update_vector_center(value)

def _mk_update_vector_side(self, attribute, value):
"""
Calculate the updated vector for the given corner
"""
value = Vector(value)
assert attribute != 'center'
# Does a bunch of dynamc resolution:
# Sprite.top.left
# ^ ^ attribute
# self.side
self_dimension, self_offset = self._lookup_side(self.side)

attr_dimension, attr_offset = self._lookup_side(attribute)

assert self_dimension != attr_dimension

fields = {
self_dimension: value[self_dimension] - self_offset,
attr_dimension: value[attr_dimension] - attr_offset,
}
return Vector(fields)

def _mk_update_vector_center(self, value):
"""
Calculate the update vector for the midpoint of this side
"""
value = Vector(value)
# Pretty similar to ._mk_update_vector_side()
self_dimension, self_offset = self._lookup_side(self.side)

attr_dimension = 'y' if self_dimension == 'x' else 'x'

fields = {
self_dimension: value[self_dimension] - self_offset,
attr_dimension: value[attr_dimension]
}

return Vector(fields)

def _attribute_gate(self, attribute, bad_sides):
if self.side in bad_sides:
Expand All @@ -154,6 +189,10 @@ class Rotatable:
def facing(self):
return Vector(*self.basis).rotate(self.rotation).normalize()

@facing.setter
def facing(self, value):
self.rotation = self.basis.angle(value)

@property
def rotation(self):
return self._rotation
Expand Down Expand Up @@ -187,9 +226,7 @@ class BaseSprite(EventMixin, Rotatable):
def __init__(self, **kwargs):
super().__init__()

# Make these instance properties with fresh instances
# Don't use Vector.convert() because we need copying
self.position = Vector(*self.position)
self.position = Vector(self.position)

# Initialize things
for k, v in kwargs.items():
Expand All @@ -198,7 +235,7 @@ def __init__(self, **kwargs):
k = 'position'
# Castings
if k == 'position':
v = Vector(*v) # Vector.convert() when that ships.
v = Vector(v)
setattr(self, k, v)

# Trigger some calculations
Expand All @@ -209,43 +246,40 @@ def center(self) -> Vector:
return self.position

@center.setter
def center(self, value: Sequence[float]):
x = value[0]
y = value[1]
self.position.x = x
self.position.y = y
def center(self, value: ppb_vector.vector2.VectorLike):
self.position = Vector(value)

@property
def left(self) -> Side:
return Side(self, LEFT)

@left.setter
def left(self, value: float):
self.position.x = value + self._offset_value
self.position = Vector(value + self._offset_value, self.position.y)

@property
def right(self) -> Side:
return Side(self, RIGHT)

@right.setter
def right(self, value):
self.position.x = value - self._offset_value
self.position = Vector(value - self._offset_value, self.position.y)

@property
def top(self):
return Side(self, TOP)

@top.setter
def top(self, value):
self.position.y = value + self._offset_value
self.position = Vector(self.position.x, value + self._offset_value)

@property
def bottom(self):
return Side(self, BOTTOM)

@bottom.setter
def bottom(self, value):
self.position.y = value - self._offset_value
self.position = Vector(self.position.x, value - self._offset_value)

@property
def _offset_value(self):
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pygame
ppb-vector
ppb-vector >= 1.0a1, < 2
dataclasses; python_version < "3.7"
14 changes: 7 additions & 7 deletions tests/test_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ def test_camera_translate_to_frame():
cam = Camera(viewport=(0, 0, 800, 600), pixel_ratio=80)
assert cam.position == Vector(0, 0)
assert cam.translate_to_frame(Vector(400, 300)) == Vector(0, 0)
assert cam.translate_to_frame(Vector(560, 220)) == Vector(2, -1)
assert cam.translate_to_frame(Vector(560, 220)) == Vector(2, 1)
cam.position = Vector(5, 5)
assert cam.translate_to_frame(Vector(400, 300)) == Vector(5, 5)
assert cam.translate_to_frame(Vector(560, 220)) == Vector(7, 4)
assert cam.translate_to_frame(Vector(400, 300)) == Vector(5, -5)
assert cam.translate_to_frame(Vector(560, 220)) == Vector(7, -4)


def test_camera_translate_to_viewport():
cam = Camera(viewport=(0, 0, 800, 600), pixel_ratio=80)
assert cam.position == Vector(0, 0)
assert cam.translate_to_viewport(Vector(0, 0)) == Vector(400, 300)
assert cam.translate_to_viewport(Vector(2, -1)) == Vector(560, 220)
assert cam.translate_to_viewport(Vector(2, 1)) == Vector(560, 220)
cam.position = Vector(5, 5)
assert cam.translate_to_viewport(Vector(5, 5)) == Vector(400, 300)
assert cam.translate_to_viewport(Vector(7, 4)) == Vector(560, 220)
assert cam.translate_to_viewport(Vector(5, -5)) == Vector(400, 300)
assert cam.translate_to_viewport(Vector(7, -4)) == Vector(560, 220)


def test_sprite_in_viewport():
Expand All @@ -70,4 +70,4 @@ def test_viewport_change_affects_frame_height():
cam = Camera(viewport=(0, 0, 800, 600), pixel_ratio=80)
assert cam.frame_left == -5
cam.viewport_width = 400
assert cam.frame_left == -2.5
assert cam.frame_left == -2.5
9 changes: 0 additions & 9 deletions tests/test_sprites.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,6 @@ def test_bottom(self):
self.assertEqual(self.sprite.position.x, 0)
self.assertEqual(self.sprite.position.y, 1.5)

def test_center_accessors(self):
self.sprite.center.x = 20
self.assertEqual(self.sprite.position.x, 20)
self.assertEqual(self.sprite.position.y, 0)

self.sprite.center.y = 15
self.assertEqual(self.sprite.position.x, 20)
self.assertEqual(self.sprite.position.y, 15)

def test_left_top(self):
self.assertEqual(self.sprite.left.top, Vector(-0.5, -0.5))

Expand Down