import pymunk.autogeometry
import pymunk.pygame_util
-from .constants import SCREEN_SIZE
from .utils import debug_timer
-def screen_rays(pos):
+def screen_rays(pos, bounding_radius):
""" An iterable that returns ordered rays from pos to the edge of the
screen, starting with the edge point (0, 0) and continuing clockwise
in pymunk coordinates.
"""
- w, h = SCREEN_SIZE
- left, right, bottom, top = 0, w, 0, h
+ r = int(bounding_radius)
+ left, right = int(pos.x) - r, int(pos.x) + r
+ bottom, top = int(pos.y) - r, int(pos.y) + r
step = 1
- for y in range(0, h, step):
+ for y in range(bottom, top + 1, step):
yield pymunk.Vec2d(left, y)
- for x in range(0, w, step):
+ for x in range(left, right + 1, step):
yield pymunk.Vec2d(x, top)
- for y in range(top, -1, -step):
+ for y in range(top, bottom - 1, -step):
yield pymunk.Vec2d(right, y)
- for x in range(right, -1, -step):
+ for x in range(right, left - 1, -step):
yield pymunk.Vec2d(x, bottom)
@debug_timer("rays.calculate_ray_polys")
-def calculate_ray_polys(space, position, light_filter):
+def calculate_ray_polys(space, position, bounding_radius, light_filter):
""" Calculate a set of convex RayPolys that cover all the areas that light
can reach from the given position, taking into account the obstacles
present in the space.
vertices = [position]
start, end = None, None
ray_polys = []
- for ray in screen_rays(position):
+ for ray in screen_rays(position, bounding_radius):
info = space.segment_query_first(position, ray, 1, light_filter)
point = ray if info is None else info.point
vertices.append(point)
class RayPolyManager(object):
def __init__(
self, body, position, ray_filter, radius_limits, direction,
- spread):
+ spread, bounding_radius):
self._body = body # light's body
self._position = pymunk.Vec2d(position) # light's position
self._ray_filter = ray_filter # light filter
self._rays = [] # list of RayPolys
+ self._direction = None # normal vector for direction
self._start = None # normal vector in direction of start angle limit
self._end = None # normal vector in direction of end angle limit
self._set_angle_limits(direction, spread)
+ self._bounding_radius = None # absolute maximum radius
+ if direction:
+ self.direction = direction # Update direction
self._max_radius = None # maximum radius in pixels
self._min_radius = None # minimum radius in pixels
self._set_radius_limits(radius_limits)
+ self._set_bounding_radius(bounding_radius)
self._old_poly_cache = None # last polys added to the space
self._poly_cache = None # list of pymunk.Polys for rays
self._space = None # space the rays form part of
def set_space(self, space):
self._space = space
self._rays = calculate_ray_polys(
- self._space, self._position, self._ray_filter)
+ self._space, self._position, self._bounding_radius,
+ self._ray_filter)
self._poly_cache = None
def update_shapes(self):
def min_radius(self, value):
self._min_radius = value or 0.0
+ def serialize(self):
+ """ Return the required information from the ray_manager """
+ if self._direction is None:
+ direction = None
+ spread = None
+ else:
+ direction = self._direction.angle_degrees
+ spread = math.degrees(self.spread)
+ return {
+ "radius_limits": (self._min_radius, self._max_radius),
+ "direction": direction,
+ "spread": spread,
+ }
+
def reaches(self, position):
- distance = self.position.get_distance(self.position)
+ distance = self.position.get_distance(position)
return (self._min_radius <= distance <= self._max_radius)
def _set_radius_limits(self, radius_limits):
else:
self._max_radius = radius_limits[1]
+ def _set_bounding_radius(self, bounding_radius):
+ if bounding_radius is None:
+ bounding_radius = self._max_radius
+ self._bounding_radius = bounding_radius
+
def rotatable(self):
- return self._start is not None
+ return self._direction is not None
- def rotate_degrees(self, degrees):
- self._start.rotate_degrees(degrees)
- self._end.rotate_degrees(degrees)
+ @property
+ def direction(self):
+ if self._direction is None:
+ return 0
+ return self._direction.angle_degrees
+
+ @direction.setter
+ def direction(self, degrees):
+ spread = self._direction.get_angle_between(self._start)
+ self._direction.angle_degrees = degrees
+ self._start = self._direction.rotated(spread)
+ self._end = self._direction.rotated(-spread)
self._poly_cache = None
+ @property
+ def spread(self):
+ if not self._direction:
+ return 2 * math.pi
+ return math.fabs(self._start.get_angle_between(self._end))
+
def _set_angle_limits(self, direction, spread):
if direction is None or spread is None:
+ self._direction = None
self._start = None
self._end = None
else:
- n = pymunk.Vec2d(1, 0)
- self._start = n.rotated_degrees(-spread/2.)
- self._end = n.rotated_degrees(spread/2.)
+ self._direction = pymunk.Vec2d(1, 0)
+ self._start = self._direction.rotated_degrees(-spread/2.)
+ self._end = self._direction.rotated_degrees(spread/2.)
self._poly_cache = None
def polys(self):