1 """ Light ray manipulation. Pew. Pew. Pew. Wommmm. """
6 import pymunk.autogeometry
7 import pymunk.pygame_util
9 from .constants import SCREEN_SIZE
10 from .utils import debug_timer
14 """ An iterable that returns ordered rays from pos to the edge of the
15 screen, starting with the edge point (0, 0) and continuing clockwise
16 in pymunk coordinates.
19 left, right, bottom, top = 0, w, 0, h
21 for y in range(0, h, step):
22 yield pymunk.Vec2d(left, y)
23 for x in range(0, w, step):
24 yield pymunk.Vec2d(x, top)
25 for y in range(top, -1, -step):
26 yield pymunk.Vec2d(right, y)
27 for x in range(right, -1, -step):
28 yield pymunk.Vec2d(x, bottom)
31 @debug_timer("rays.calculate_ray_polys")
32 def calculate_ray_polys(space, position, light_filter):
33 """ Calculate a set of convex RayPolys that cover all the areas that light
34 can reach from the given position, taking into account the obstacles
37 position = pymunk.Vec2d(position)
39 start, end = None, None
41 for ray in screen_rays(position):
42 info = space.segment_query_first(position, ray, 1, light_filter)
43 point = ray if info is None else info.point
44 vertices.append(point)
45 if len(vertices) == 2:
47 elif len(vertices) > 3:
48 trial_poly = pymunk.Poly(None, vertices)
49 trial_poly.update(pymunk.Transform.identity())
50 query_prev = trial_poly.point_query(end)
51 query_pos = trial_poly.point_query(position)
52 if query_prev.distance < -0.01 or query_pos.distance < -0.01:
53 ray_polys.append(RayPoly(
54 start - position, end - position, vertices[:-1]))
56 vertices = [position, start]
58 vertices = trial_poly.get_vertices()
61 ray_polys.append(RayPoly(start, end, vertices))
65 def to_pymunk_radians(deg):
66 """ Convert degrees in [0, 360] to radians in (-pi, pi].
68 Return None if degrees is None.
72 deg = deg * math.pi / 180.0
78 class RayPolyManager(object):
79 def __init__(self, body, ray_filter):
81 self._ray_filter = ray_filter
83 self._angle_limits = (None, None)
84 self._poly_cache = None
85 self._pygame_poly_cache = None
87 def generate_rays(self, space, position):
88 self._rays = calculate_ray_polys(space, position, self._ray_filter)
89 self._poly_cache = None
90 self._pygame_poly_cache = None
92 def set_angle_limits(self, angle_limits):
93 start, end = angle_limits
94 self._angle_limits = (
95 to_pymunk_radians(start), to_pymunk_radians(end))
96 self._poly_cache = None
97 self._pygame_poly_cache = None
100 if self._poly_cache is None:
103 rp.poly(self._body, self._ray_filter) for rp in self._rays
104 if rp.within_limits(*self._angle_limits)
108 return self._poly_cache
110 def pygame_polys(self, surface):
111 if self._pygame_poly_cache is None:
112 print "REGEN ...", self._angle_limits
113 self._pygame_poly_cache = [
115 pymunk.pygame_util.to_pygame(v, surface)
116 for v in poly.get_vertices()
118 for poly in self.polys()
120 return self._pygame_poly_cache
123 class RayPoly(object):
124 def __init__(self, start_vec, end_vec, vertices):
125 self.start = start_vec # vector from position to first point
126 self.end = end_vec # vector from position to last point
127 self.vertices = vertices
129 def poly(self, body, filter):
130 shape = pymunk.Poly(body, self.vertices)
131 shape.filter = filter
134 def within_limits(self, start_limit, end_limit):
135 if start_limit is None or end_limit is None:
138 print "LIM: ", start_limit, end_limit
139 print "ANG: ", self.start.angle, self.end.angle
140 n1 = self.start.normalized()
141 n2 = self.end.normalized()
142 s = pymunk.Vec2d(1, 0).rotated(start_limit)
143 e = pymunk.Vec2d(1, 0).rotated(end_limit)
144 n1bet = s.dot(e) < s.dot(n1) and s.dot(e) < s.dot(n1)
145 n2bet = s.dot(e) < e.dot(n2) and s.dot(e) < e.dot(n2)
146 print "DOTS n1: ", n1.dot(n2), n1.dot(s), n2.dot(s)
147 print "DOTS n2: ", n1.dot(n2), n1.dot(e), n2.dot(e)
148 print "BET: ", n1bet, n2bet