Merge branch 'master' of ctpug.org.za:tabakrolletjie
[tabakrolletjie.git] / tabakrolletjie / lights.py
1 """ May it be a light for you in dark places, when all other lights go out.
2 """
3
4 import pymunk
5 import pymunk.pygame_util
6 import pygame.draw
7
8 from .constants import SCREEN_SIZE, LIGHT_CATEGORY
9
10 LIGHT_FILTER = pymunk.ShapeFilter(
11     mask=pymunk.ShapeFilter.ALL_MASKS ^ LIGHT_CATEGORY,
12     categories=LIGHT_CATEGORY)
13
14
15 def screen_rays(pos):
16     """ An iterable that returns ordered rays from pos to the edge of the
17         screen, starting with the edge point (0, 0) and continuing clockwise
18         in pymunk coordinates.
19     """
20     w, h = SCREEN_SIZE
21     left, right, bottom, top = 0, w, 0, h
22     for y in range(h):
23         yield pymunk.Vec2d(left, y)
24     for x in range(w):
25         yield pymunk.Vec2d(x, top)
26     for y in range(top, -1, -1):
27         yield pymunk.Vec2d(right, y)
28     for x in range(right, -1, -1):
29         yield pymunk.Vec2d(x, bottom)
30
31
32 def calculate_ray_polys(space, body, position):
33     position = pymunk.Vec2d(position)
34     vertices = [position]
35     ray_polys = []
36     for ray in screen_rays(position):
37         info = space.segment_query_first(position, ray, 1, LIGHT_FILTER)
38         point = ray if info is None else info.point
39         vertices.append(point)
40         if len(vertices) > 3:
41             trial_poly = pymunk.Poly(None, vertices)
42             trial_poly.update(pymunk.Transform.identity())
43             query_prev = trial_poly.point_query(vertices[-2])
44             query_pos = trial_poly.point_query(position)
45             if query_prev.distance < -0.01 or query_pos.distance < -0.01:
46                 new_poly = pymunk.Poly(body, vertices[:-1])
47                 vertices = [position, vertices[-1]]
48                 ray_polys.append(new_poly)
49     if len(vertices) > 2:
50         ray_polys.append(pymunk.Poly(body, vertices))
51     print "NUM POLYS: ", len(ray_polys)
52     return ray_polys
53
54
55 class BaseLight(object):
56     """ Common light functionality. """
57
58     def __init__(self, colour, position):
59         self.body = pymunk.Body(0, 0, pymunk.body.Body.STATIC)
60         self.colour = colour
61         self.position = position
62
63     def add(self, space):
64         if self.body.space is not None:
65             space.remove(self.body, *self.body.shapes)
66         shapes = self.shapes_for_ray_polys(
67             calculate_ray_polys(space, self.body, self.position))
68         for shape in shapes:
69             shape.filter = LIGHT_FILTER
70         space.add(self.body, *shapes)
71
72     def shapes_for_ray_polys(self, space):
73         raise NotImplementedError(
74             "Lights should implement .determine_ray_polys.")
75
76     @classmethod
77     def load(cls, config):
78         kw = config.copy()
79         light_type = kw.pop("type")
80         [light_class] = [
81             c for c in cls.__subclasses__()
82             if c.__name__.lower() == light_type]
83         return light_class(**kw)
84
85
86 class SpotLight(BaseLight):
87     def __init__(
88             self, colour="white", position=None, direction=90.0, spread=45.0):
89         super(SpotLight, self).__init__(colour, position)
90         self.direction = direction
91         self.spread = spread
92         self.i = 0
93
94     def shapes_for_ray_polys(self, ray_polys):
95         return ray_polys
96
97     def render(self, surface):
98         subsurface = surface.copy()
99         pygame.draw.circle(
100             surface, (255, 255, 0),
101             pymunk.pygame_util.to_pygame(self.position, surface), 5)
102         for shape in self.body.shapes:
103             pygame_poly = [
104                 pymunk.pygame_util.to_pygame(v, surface) for v in
105                 shape.get_vertices()]
106             pygame.draw.polygon(
107                 subsurface, (200, 200, 200), pygame_poly, 0)
108             pygame.draw.aalines(
109                 subsurface, (200, 200, 200), True, pygame_poly, 1)
110         subsurface.set_alpha(200)
111         surface.blit(subsurface, (0, 0), None)
112
113
114 class Lamp(BaseLight):
115     def __init__(self, colour="white", position=None, radius=100.0):
116         super(Lamp, self).__init__(colour, position)
117         self.radius = radius