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.position = pymunk.Vec2d(position)
81 self.body = pymunk.Body(0, 0, pymunk.body.Body.STATIC)
82 self.fitting = pymunk.Circle(self.body, 10.0, self.position)
83 self.body.light = self
86 def load(cls, config):
88 light_type = kw.pop("type")
90 c for c in cls.__subclasses__()
91 if c.__name__.lower() == light_type]
92 return light_class(**kw)
95 if self.body.space is not None:
96 space.remove(self.body, *self.body.shapes)
97 shapes = self.shapes_for_ray_polys(
98 calculate_ray_polys(space, self.body, self.position))
100 shape.filter = LIGHT_FILTER
101 self.fitting.filter = FITTINGS_FILTER
102 space.add(self.body, self.fitting, *shapes)
104 def shapes_for_ray_polys(self, ray_polys):
108 self.on = not self.on
110 def render_light(self, surface):
113 subsurface = surface.copy()
114 light_colour = self.COLOURS[self.colour]
115 for shape in self.body.shapes:
116 if shape is self.fitting:
119 pymunk.pygame_util.to_pygame(v, surface) for v in
120 shape.get_vertices()]
122 subsurface, light_colour, pygame_poly, 0)
124 subsurface, light_colour, True, pygame_poly, 1)
125 subsurface.set_alpha(50)
126 surface.blit(subsurface, (0, 0), None)
128 def render_fittings(self, surface):
130 surface, (255, 255, 0),
131 pymunk.pygame_util.to_pygame(self.fitting.offset, 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)