Fetch pygame polys from ray manager.
[tabakrolletjie.git] / tabakrolletjie / rays.py
1 """ Light ray manipulation. Pew. Pew. Pew. Wommmm. """
2
3 import pymunk
4 import pymunk.autogeometry
5 import pymunk.pygame_util
6
7 from .constants import SCREEN_SIZE
8 from .utils import debug_timer
9
10
11 def screen_rays(pos):
12     """ An iterable that returns ordered rays from pos to the edge of the
13         screen, starting with the edge point (0, 0) and continuing clockwise
14         in pymunk coordinates.
15     """
16     w, h = SCREEN_SIZE
17     left, right, bottom, top = 0, w, 0, h
18     step = 1
19     for y in range(0, h, step):
20         yield pymunk.Vec2d(left, y)
21     for x in range(0, w, step):
22         yield pymunk.Vec2d(x, top)
23     for y in range(top, -1, -step):
24         yield pymunk.Vec2d(right, y)
25     for x in range(right, -1, -step):
26         yield pymunk.Vec2d(x, bottom)
27
28
29 @debug_timer("rays.calculate_ray_polys", True)
30 def calculate_ray_polys(space, position, light_filter):
31     position = pymunk.Vec2d(position)
32     vertices = [position]
33     start, end = None, None
34     ray_polys = []
35     for ray in screen_rays(position):
36         info = space.segment_query_first(position, ray, 1, light_filter)
37         point = ray if info is None else info.point
38         vertices.append(point)
39         if len(vertices) == 2:
40             start = vertices[1]
41         elif len(vertices) > 3:
42             trial_poly = pymunk.Poly(None, vertices)
43             trial_poly.update(pymunk.Transform.identity())
44             query_prev = trial_poly.point_query(end)
45             query_pos = trial_poly.point_query(position)
46             if query_prev.distance < -0.01 or query_pos.distance < -0.01:
47                 ray_polys.append(RayPoly(start, end, vertices[:-1]))
48                 start = vertices[-1]
49                 vertices = [position, start]
50             else:
51                 vertices = trial_poly.get_vertices()
52         end = point
53     if len(vertices) > 2:
54         ray_polys.append(RayPoly(start, end, vertices))
55     return ray_polys
56
57
58 class RayPolyManager(object):
59     def __init__(self, body, ray_filter):
60         self._body = body
61         self._ray_filter = ray_filter
62         self._rays = []
63
64     def generate_rays(self, space, position):
65         self._rays = calculate_ray_polys(space, position, self._ray_filter)
66
67     def polys(self):
68         return [rp.poly(self._body, self._ray_filter) for rp in self._rays]
69
70     def pygame_polys(self, surface):
71         return [rp.pygame_poly(surface) for rp in self._rays]
72
73
74 class RayPoly(object):
75     def __init__(self, start, end, vertices):
76         self.start = start
77         self.end = end
78         self.vertices = vertices
79
80     def poly(self, body, filter):
81         shape = pymunk.Poly(body, self.vertices)
82         shape.filter = filter
83         return shape
84
85     def pygame_poly(self, surface):
86         return [
87             pymunk.pygame_util.to_pygame(v, surface)
88             for v in self.vertices
89         ]