1 """ May it be a light for you in dark places, when all other lights go out.
5 import pymunk.pygame_util
8 from .constants import (
9 SCREEN_SIZE, LIGHT_CATEGORY, FITTINGS_CATEGORY)
10 from .utils import debug_timer
12 LIGHT_FILTER = pymunk.ShapeFilter(
13 mask=pymunk.ShapeFilter.ALL_MASKS ^ (
14 LIGHT_CATEGORY | FITTINGS_CATEGORY),
15 categories=LIGHT_CATEGORY)
17 FITTINGS_FILTER = pymunk.ShapeFilter(
18 mask=pymunk.ShapeFilter.ALL_MASKS ^ (
19 LIGHT_CATEGORY | FITTINGS_CATEGORY),
20 categories=FITTINGS_CATEGORY)
24 """ An iterable that returns ordered rays from pos to the edge of the
25 screen, starting with the edge point (0, 0) and continuing clockwise
26 in pymunk coordinates.
29 left, right, bottom, top = 0, w, 0, h
31 for y in range(0, h, step):
32 yield pymunk.Vec2d(left, y)
33 for x in range(0, w, step):
34 yield pymunk.Vec2d(x, top)
35 for y in range(top, -1, -step):
36 yield pymunk.Vec2d(right, y)
37 for x in range(right, -1, -step):
38 yield pymunk.Vec2d(x, bottom)
41 @debug_timer("lights.calculate_ray_polys")
42 def calculate_ray_polys(space, body, position):
43 position = pymunk.Vec2d(position)
46 for ray in screen_rays(position):
47 info = space.segment_query_first(position, ray, 1, LIGHT_FILTER)
48 point = ray if info is None else info.point
49 vertices.append(point)
51 trial_poly = pymunk.Poly(None, vertices)
52 trial_poly.update(pymunk.Transform.identity())
53 query_prev = trial_poly.point_query(vertices[-2])
54 query_pos = trial_poly.point_query(position)
55 if query_prev.distance < -0.01 or query_pos.distance < -0.01:
56 new_poly = pymunk.Poly(body, vertices[:-1])
57 vertices = [position, vertices[-1]]
58 ray_polys.append(new_poly)
60 vertices = trial_poly.get_vertices() + [point]
62 ray_polys.append(pymunk.Poly(body, vertices))
66 class BaseLight(object):
67 """ Common light functionality. """
72 "blue": (0, 255, 255),
73 "yellow": (255, 255, 0),
74 "white": (255, 255, 255),
77 def __init__(self, colour, position):
79 self.body = pymunk.Body(0, 0, pymunk.body.Body.STATIC)
80 self.fitting = pymunk.Circle(self.body, 5.0)
82 self.position = pymunk.Vec2d(position)
85 def load(cls, config):
87 light_type = kw.pop("type")
89 c for c in cls.__subclasses__()
90 if c.__name__.lower() == light_type]
91 return light_class(**kw)
94 if self.body.space is not None:
95 space.remove(self.body, *self.body.shapes)
96 shapes = self.shapes_for_ray_polys(
97 calculate_ray_polys(space, self.body, self.position))
99 shape.filter = LIGHT_FILTER
100 self.fitting.filter = FITTINGS_FILTER
101 space.add(self.body, self.fitting, *shapes)
103 def shapes_for_ray_polys(self, ray_polys):
107 self.on = not self.on
109 def render_light(self, surface):
112 subsurface = surface.copy()
113 light_colour = self.COLOURS[self.colour]
114 for shape in self.body.shapes:
115 if shape is self.fitting:
118 pymunk.pygame_util.to_pygame(v, surface) for v in
119 shape.get_vertices()]
121 subsurface, light_colour, pygame_poly, 0)
123 subsurface, light_colour, True, pygame_poly, 1)
124 subsurface.set_alpha(50)
125 surface.blit(subsurface, (0, 0), None)
127 def render_fittings(self, surface):
128 centre = self.position + self.fitting.offset
130 surface, (255, 255, 0),
131 pymunk.pygame_util.to_pygame(centre, surface),
132 int(self.fitting.radius))
135 class SpotLight(BaseLight):
137 self, colour="white", position=None, direction=90.0, spread=45.0):
138 super(SpotLight, self).__init__(colour, position)
139 self.direction = direction
144 class Lamp(BaseLight):
145 def __init__(self, colour="white", position=None, radius=100.0):
146 super(Lamp, self).__init__(colour, position)