1 """ May it be a light for you in dark places, when all other lights go out.
5 import pymunk.pygame_util
9 from .constants import (
10 SCREEN_SIZE, LIGHT_CATEGORY, FITTINGS_CATEGORY)
11 from .utils import debug_timer
13 LIGHT_FILTER = pymunk.ShapeFilter(
14 mask=pymunk.ShapeFilter.ALL_MASKS ^ (
15 LIGHT_CATEGORY | FITTINGS_CATEGORY),
16 categories=LIGHT_CATEGORY)
18 FITTINGS_FILTER = pymunk.ShapeFilter(
19 mask=pymunk.ShapeFilter.ALL_MASKS ^ (
20 LIGHT_CATEGORY | FITTINGS_CATEGORY),
21 categories=FITTINGS_CATEGORY)
25 """ An iterable that returns ordered rays from pos to the edge of the
26 screen, starting with the edge point (0, 0) and continuing clockwise
27 in pymunk coordinates.
30 left, right, bottom, top = 0, w, 0, h
32 for y in range(0, h, step):
33 yield pymunk.Vec2d(left, y)
34 for x in range(0, w, step):
35 yield pymunk.Vec2d(x, top)
36 for y in range(top, -1, -step):
37 yield pymunk.Vec2d(right, y)
38 for x in range(right, -1, -step):
39 yield pymunk.Vec2d(x, bottom)
42 @debug_timer("lights.calculate_ray_polys")
43 def calculate_ray_polys(space, body, position):
44 position = pymunk.Vec2d(position)
47 for ray in screen_rays(position):
48 info = space.segment_query_first(position, ray, 1, LIGHT_FILTER)
49 point = ray if info is None else info.point
50 vertices.append(point)
52 trial_poly = pymunk.Poly(None, vertices)
53 trial_poly.update(pymunk.Transform.identity())
54 query_prev = trial_poly.point_query(vertices[-2])
55 query_pos = trial_poly.point_query(position)
56 if query_prev.distance < -0.01 or query_pos.distance < -0.01:
57 new_poly = pymunk.Poly(body, vertices[:-1])
58 vertices = [position, vertices[-1]]
59 ray_polys.append(new_poly)
61 vertices = trial_poly.get_vertices() + [point]
63 ray_polys.append(pymunk.Poly(body, vertices))
67 class LightManager(object):
68 """ Manages a set of lights. """
70 def __init__(self, space, gamestate):
73 BaseLight.load(cfg) for cfg in gamestate.station["lights"]]
74 for light in self._lights:
75 light.add(self._space)
77 def toggle_nearest(self, *args, **kw):
78 light = self.nearest(*args, **kw)
82 def nearest(self, pos, surfpos=True, max_distance=1.0):
84 surface = pygame.display.get_surface()
85 pos = pymunk.pygame_util.from_pygame(pos, surface)
86 point_info = self._space.point_query_nearest(
87 pos, 1.0, pymunk.ShapeFilter(mask=FITTINGS_CATEGORY))
88 if point_info is not None:
89 return point_info.shape.body.light
92 def render_light(self, surface):
93 for light in self._lights:
94 light.render_light(surface)
96 def render_fittings(self, surface):
97 for light in self._lights:
98 light.render_fitting(surface)
101 class BaseLight(object):
102 """ Common light functionality. """
106 "green": (0, 255, 0),
107 "blue": (0, 255, 255),
108 "yellow": (255, 255, 0),
109 "white": (255, 255, 255),
112 def __init__(self, colour, position):
114 self.position = pymunk.Vec2d(position)
116 self.body = pymunk.Body(0, 0, pymunk.body.Body.STATIC)
117 self.fitting = pymunk.Circle(self.body, 10.0, self.position)
118 self.body.light = self
121 def load(cls, config):
123 light_type = kw.pop("type")
125 c for c in cls.__subclasses__()
126 if c.__name__.lower() == light_type]
127 return light_class(**kw)
129 def add(self, space):
130 if self.body.space is not None:
131 space.remove(self.body, *self.body.shapes)
132 shapes = self.shapes_for_ray_polys(
133 calculate_ray_polys(space, self.body, self.position))
135 shape.filter = LIGHT_FILTER
136 self.fitting.filter = FITTINGS_FILTER
137 space.add(self.body, self.fitting, *shapes)
139 def shapes_for_ray_polys(self, ray_polys):
143 self.on = not self.on
145 def render_light(self, surface):
148 subsurface = surface.copy()
149 light_colour = self.COLOURS[self.colour]
150 for shape in self.body.shapes:
151 if shape is self.fitting:
154 pymunk.pygame_util.to_pygame(v, surface) for v in
155 shape.get_vertices()]
157 subsurface, light_colour, pygame_poly, 0)
159 subsurface, light_colour, True, pygame_poly, 1)
160 subsurface.set_alpha(50)
161 surface.blit(subsurface, (0, 0), None)
163 def render_fitting(self, surface):
165 surface, (255, 255, 0),
166 pymunk.pygame_util.to_pygame(self.fitting.offset, surface),
167 int(self.fitting.radius))
170 class SpotLight(BaseLight):
172 self, colour="white", position=None, direction=90.0, spread=45.0):
173 super(SpotLight, self).__init__(colour, position)
174 self.direction = direction
179 class Lamp(BaseLight):
180 def __init__(self, colour="white", position=None, radius=100.0):
181 super(Lamp, self).__init__(colour, position)