| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- from __future__ import division
- from math import atan2, pi
- import time
- class Positionable(object):
- """
- Parent class for any object with a position.
- """
- def __init__(self, x=None, y=None):
- self.x = x
- self.y = y
- def __str__(self):
- return '<%s at (%s, %s)>' % (self.__class__.__name__, self.x, self.y)
- def set_position(self, x, y):
- self.x = x
- self.y = y
- def get_position(self):
- return self.x, self.y
- @property
- def xy(self):
- """
- Shortcut getter for (x, y) position.
- """
- return self.x, self.y
- def distance_to(self, positionable):
- """
- Calculate the Pythagorian distance from this positionable to another.
- """
- x, y = positionable.get_position()
- return ((x - self.x) ** 2 + (y - self.y) ** 2) ** .5
- class MovingPositionable(Positionable):
- """
- Parent class for positionable objects that need movement calculations. For
- these calculations, the previous position is also saved.
- """
- def __init__(self, x=None, y=None):
- super(MovingPositionable, self).__init__(x, y)
- self.prev = Positionable(x, y)
- def set_position(self, x, y):
- """
- Set a new position and save the current position as the precious
- position. If no previous position has been set, set is to the new
- position so that the movement is zero.
- """
- if self.x is None or self.y is None:
- self.prev.set_position(x, y)
- else:
- self.prev.set_position(self.x, self.y)
- Positionable.set_position(self, x, y)
- def get_previous_position(self):
- return self.prev
- def rotation_around(self, center):
- """
- Calculate rotation of this positionable relative to a center
- positionable.
- """
- cx, cy = center.get_position()
- px, py = self.prev.get_position()
- prev_angle = atan2(px - cx, py - cy)
- current_angle = atan2(self.x - cx, self.y - cy)
- rotation = current_angle - prev_angle
- if rotation >= pi:
- return 2 * pi - rotation
- if rotation <= -pi:
- return -2 * pi - rotation
- return rotation
- def translation(self):
- """
- Calculate the movement relative to the last position as a vector
- positionable.
- """
- px, py = self.prev.get_position()
- return Positionable(self.x - px, self.y - py)
- def movement_distance(self):
- return self.distance_to(self.prev)
- class AcceleratedPositionable(MovingPositionable):
- """
- Parent class for positionable objects that need acceleration calculations.
- For these calculations, timestamps are saved for both the current and
- previous position.
- """
- def __init__(self, x=None, y=None):
- super(AcceleratedPositionable, self).__init__(x, y)
- self.prev_timestamp = self.current_timestamp = None
- def set_position(self, x, y):
- MovingPositionable.set_position(self, x, y)
- if self.current_timestamp is None:
- self.prev_timestamp = self.current_timestamp = time.time()
- return
- self.prev_timestamp = self.current_timestamp
- self.current_timestamp = time.time()
- def movement_time(self):
- return self.timestamp - self.prev_timestamp
- def acceleration(self):
- """
- Calculate the acceleration in pixels/second.
- """
- return self.movement_distance() / self.movement_time()
- class Surface(Positionable):
- """
- Interface class for surfaces with a position. Defines a function
- 'contains', which calculates whether a position is located in the surface.
- """
- def contains(self, point):
- raise NotImplemented
- class RectangularSurface(Surface):
- """
- Rectangle, represented by a left-top position (x, y) and a size (width,
- height).
- """
- def __init__(self, x, y, width, height):
- super(RectangularSurface, self).__init__(x, y)
- self.set_size(width, height)
- def __str__(self):
- return '<%s at (%s, %s) size=(%s, %s)>' \
- % (self.__class__.__name__, self.x, self.y, self.width,
- self.height)
- def set_size(self, width, height):
- self.width = width
- self.height = height
- def get_size(self):
- return self.width, self.height
- def contains(self, point):
- x, y = point.get_position()
- return self.x <= x <= self.x + self.width \
- and self.y <= y <= self.y + self.height
- class CircularSurface(Surface):
- """
- Circle, represented by a center position (x, y) and a radius.
- """
- def __init__(self, x, y, radius):
- super(CircularSurface, self).__init__(x, y)
- self.set_radius(radius)
- def __str__(self):
- return '<%s at (%s, %s) size=(%s, %s)>' \
- % (self.__class__.__name__, self.x, self.y, self.width,
- self.height)
- def set_radius(self, radius):
- self.radius = radius
- def get_radius(self):
- return self.radius
- def contains(self, point):
- return self.distance_to(point) <= self.radius
|