X-Git-Url: https://git.ctpug.org.za/?a=blobdiff_plain;f=tabakrolletjie%2Frays.py;h=32ae1a749bbb86c539e8c46e95709eb0292bd0d0;hb=4db6751987e27976328db096f934cec83d4dd4c0;hp=1892edf4febe9e97dd9e26806afed426780c4e9c;hpb=b3a98c5532a9aef2c48f2bc894ada824b2456675;p=tabakrolletjie.git diff --git a/tabakrolletjie/rays.py b/tabakrolletjie/rays.py index 1892edf..32ae1a7 100644 --- a/tabakrolletjie/rays.py +++ b/tabakrolletjie/rays.py @@ -1,6 +1,9 @@ """ Light ray manipulation. Pew. Pew. Pew. Wommmm. """ +import math + import pymunk +import pymunk.autogeometry import pymunk.pygame_util from .constants import SCREEN_SIZE @@ -26,25 +29,128 @@ def screen_rays(pos): @debug_timer("rays.calculate_ray_polys") -def calculate_ray_polys(space, body, position, light_filter): +def calculate_ray_polys(space, position, light_filter): + """ Calculate a set of convex RayPolys that cover all the areas that light + can reach from the given position, taking into account the obstacles + present in the space. + """ position = pymunk.Vec2d(position) vertices = [position] + start, end = None, None ray_polys = [] for ray in screen_rays(position): info = space.segment_query_first(position, ray, 1, light_filter) point = ray if info is None else info.point vertices.append(point) - if len(vertices) > 3: + if len(vertices) == 2: + start = vertices[1] + elif len(vertices) > 3: trial_poly = pymunk.Poly(None, vertices) trial_poly.update(pymunk.Transform.identity()) - query_prev = trial_poly.point_query(vertices[-2]) + query_prev = trial_poly.point_query(end) query_pos = trial_poly.point_query(position) if query_prev.distance < -0.01 or query_pos.distance < -0.01: - new_poly = pymunk.Poly(body, vertices[:-1]) - vertices = [position, vertices[-1]] - ray_polys.append(new_poly) + ray_polys.append(RayPoly(position, vertices[:-1])) + start = vertices[-1] + vertices = [position, start] else: - vertices = trial_poly.get_vertices() + [point] + vertices = trial_poly.get_vertices() + end = point if len(vertices) > 2: - ray_polys.append(pymunk.Poly(body, vertices)) + ray_polys.append(RayPoly(position, vertices)) return ray_polys + + +def to_pymunk_radians(deg): + """ Convert degrees in [0, 360] to radians in (-pi, pi]. + + Return None if degrees is None. + """ + if deg is None: + return None + deg = deg * math.pi / 180.0 + if deg > math.pi: + deg -= 2 * math.pi + return deg + + +class RayPolyManager(object): + def __init__(self, body, ray_filter): + self._body = body # light's body + self._ray_filter = ray_filter # light filter + self._rays = [] # list of RayPolys + self._start = None # normal vector in direction of start angle limit + self._end = None # normal vector in direction of end angle limit + self._poly_cache = None # list of pymunk.Polys for rays + + def generate_rays(self, space, position): + self._rays = calculate_ray_polys(space, position, self._ray_filter) + self._poly_cache = None + + def set_angle_limits(self, angle_limits): + if angle_limits is None: + self._start = None + self._end = None + else: + self._start = pymunk.Vec2d(1, 0).rotated( + to_pymunk_radians(angle_limits[0])) + self._end = pymunk.Vec2d(1, 0).rotated( + to_pymunk_radians(angle_limits[1])) + self._poly_cache = None + + def polys(self): + if self._poly_cache is None: + self._poly_cache = poly_cache = [] + for rp in self._rays: + poly = rp.poly(self._start, self._end) + if poly: + poly.body = self._body + poly.filter = self._ray_filter + poly_cache.append(poly) + return self._poly_cache + + def pygame_polys(self, surface): + return [ + [pymunk.pygame_util.to_pygame(v, surface) + for v in poly.get_vertices()] + for poly in self.polys() + ] + + +class RayPoly(object): + def __init__(self, position, vertices): + self.position = position # pointy end of the conical polygon + self.vertices = vertices # all vertices in the polygon + + def _between(self, v, start, end): + if start < end: + return start <= v <= end + return (start <= v) or (v <= end) + + def poly(self, start, end): + trial = pymunk.Poly(None, self.vertices) + trial.update(pymunk.Transform.identity()) + + if start is None or end is None: + return trial # no limits + + start_info = trial.segment_query( + self.position + 1250 * start, self.position + 0.1 * start, 0) + end_info = trial.segment_query( + self.position + 1250 * end, self.position + 0.1 * end, 0) + + vertices = self.vertices[:] + vertices = [ + v for v in vertices + if self._between((v - self.position).angle, start.angle, end.angle) + ] + if start_info.shape is not None: + vertices.append(start_info.point) + if end_info.shape is not None: + vertices.append(end_info.point) + vertices.append(self.position) + + poly = pymunk.Poly(None, vertices) + if len(poly.get_vertices()) < 3: + return None + return poly