Remove unused pos arg.
[tabakrolletjie.git] / tabakrolletjie / rays.py
index f893828112a8084157fe353ea2e2a37b124c19d8..a475d8fb27bb6c680a90555cad7dc2a4f6043954 100644 (file)
@@ -2,6 +2,8 @@
 
 import math
 
+import pygame.rect
+
 import pymunk
 import pymunk.autogeometry
 import pymunk.pygame_util
@@ -10,7 +12,7 @@ from .constants import SCREEN_SIZE
 from .utils import debug_timer
 
 
-def screen_rays(pos):
+def screen_rays():
     """ An iterable that returns ordered rays from pos to the edge of the
         screen, starting with the edge point (0, 0) and continuing clockwise
         in pymunk coordinates.
@@ -38,7 +40,7 @@ def calculate_ray_polys(space, position, light_filter):
     vertices = [position]
     start, end = None, None
     ray_polys = []
-    for ray in screen_rays(position):
+    for ray in screen_rays():
         info = space.segment_query_first(position, ray, 1, light_filter)
         point = ray if info is None else info.point
         vertices.append(point)
@@ -50,15 +52,14 @@ def calculate_ray_polys(space, position, light_filter):
             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:
-                ray_polys.append(RayPoly(
-                    start - position, end - position, vertices[:-1]))
+                ray_polys.append(RayPoly(position, vertices[:-1]))
                 start = vertices[-1]
                 vertices = [position, start]
             else:
                 vertices = trial_poly.get_vertices()
         end = point
     if len(vertices) > 2:
-        ray_polys.append(RayPoly(start, end, vertices))
+        ray_polys.append(RayPoly(position, vertices))
     return ray_polys
 
 
@@ -76,77 +77,170 @@ def to_pymunk_radians(deg):
 
 
 class RayPolyManager(object):
-    def __init__(self, body, ray_filter):
-        self._body = body
-        self._ray_filter = ray_filter
-        self._rays = []
-        self._angle_limits = (None, None)
+    def __init__(
+            self, body, position, ray_filter, radius_limits, direction,
+            spread):
+        self._body = body  # light's body
+        self._position = pymunk.Vec2d(position)  # light's position
+        self._ray_filter = ray_filter  # light filter
+        self._rays = []  # list of RayPolys
+        self._direction = None  # normal vector for direction
+        self._start = None  # normal vector in direction of start angle limit
+        self._end = None  # normal vector in direction of end angle limit
+        self._set_angle_limits(direction, spread)
+        if direction:
+            self.direction = direction  # Update direction
+        self._max_radius = None  # maximum radius in pixels
+        self._min_radius = None  # minimum radius in pixels
+        self._set_radius_limits(radius_limits)
+        self._old_poly_cache = None  # last polys added to the space
+        self._poly_cache = None  # list of pymunk.Polys for rays
+        self._space = None  # space the rays form part of
+
+    def set_space(self, space):
+        self._space = space
+        self._rays = calculate_ray_polys(
+            self._space, self._position, self._ray_filter)
         self._poly_cache = None
-        self._pygame_poly_cache = None
 
-    def generate_rays(self, space, position):
-        self._rays = calculate_ray_polys(space, position, self._ray_filter)
+    def update_shapes(self):
+        if self._old_poly_cache:
+            self._space.remove(*self._old_poly_cache)
+        new_polys = self._old_poly_cache = self.polys()
+        self._space.add(*new_polys)
+
+    @property
+    def position(self):
+        return self._position
+
+    @property
+    def max_radius(self):
+        return self._max_radius
+
+    @max_radius.setter
+    def max_radius(self, value):
+        self._max_radius = value or 0.0
+
+    @property
+    def min_radius(self):
+        return self._min_radius
+
+    @min_radius.setter
+    def min_radius(self, value):
+        self._min_radius = value or 0.0
+
+    def reaches(self, position):
+        distance = self.position.get_distance(position)
+        return (self._min_radius <= distance <= self._max_radius)
+
+    def _set_radius_limits(self, radius_limits):
+        if radius_limits is None or not radius_limits[0]:
+            self._min_radius = 0
+        else:
+            self._min_radius = radius_limits[0]
+        if radius_limits is None or not radius_limits[1]:
+            self._max_radius = 50.0
+        else:
+            self._max_radius = radius_limits[1]
+
+    def rotatable(self):
+        return self._direction is not None
+
+    @property
+    def direction(self):
+        if self._direction is None:
+            return 0
+        return self._direction.angle_degrees
+
+    @direction.setter
+    def direction(self, degrees):
+        spread = self._direction.get_angle_between(self._start)
+        self._direction.angle_degrees = degrees
+        self._start = self._direction.rotated(spread)
+        self._end = self._direction.rotated(-spread)
         self._poly_cache = None
-        self._pygame_poly_cache = None
 
-    def set_angle_limits(self, angle_limits):
-        start, end = angle_limits
-        self._angle_limits = (
-            to_pymunk_radians(start), to_pymunk_radians(end))
+    @property
+    def spread(self):
+        if not self._direction:
+            return 2 * math.pi
+        return math.fabs(self._start.get_angle_between(self._end))
+
+    def _set_angle_limits(self, direction, spread):
+        if direction is None or spread is None:
+            self._direction = None
+            self._start = None
+            self._end = None
+        else:
+            self._direction = pymunk.Vec2d(1, 0)
+            self._start = self._direction.rotated_degrees(-spread/2.)
+            self._end = self._direction.rotated_degrees(spread/2.)
         self._poly_cache = None
-        self._pygame_poly_cache = None
 
     def polys(self):
         if self._poly_cache is None:
-            print "===="
-            self._poly_cache = [
-                rp.poly(self._body, self._ray_filter) for rp in self._rays
-                if rp.within_limits(*self._angle_limits)
-            ]
-            print "===="
-
+            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_position(self, surface):
+        return pymunk.pygame_util.to_pygame(self._position, surface)
+
+    def pygame_rect(self, surface):
+        half_width = self.max_radius
+        rect_width = half_width * 2
+        rect_x, rect_y = pymunk.pygame_util.to_pygame(self._position, surface)
+        dest_rect = pygame.rect.Rect(rect_x, rect_y, rect_width, rect_width)
+        dest_rect.move_ip(-half_width, -half_width)
+        return dest_rect
+
     def pygame_polys(self, surface):
-        if self._pygame_poly_cache is None:
-            print "REGEN ...", self._angle_limits
-            self._pygame_poly_cache = [
-                [
-                    pymunk.pygame_util.to_pygame(v, surface)
-                    for v in poly.get_vertices()
-                ]
-                for poly in self.polys()
-            ]
-        return self._pygame_poly_cache
+        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, start_vec, end_vec, vertices):
-        self.start = start_vec  # vector from position to first point
-        self.end = end_vec  # vector from position to last point
-        self.vertices = vertices
-
-    def poly(self, body, filter):
-        shape = pymunk.Poly(body, self.vertices)
-        shape.filter = filter
-        return shape
-
-    def within_limits(self, start_limit, end_limit):
-        if start_limit is None or end_limit is None:
-            return True
-        print "----"
-        print "LIM: ", start_limit, end_limit
-        print "ANG: ", self.start.angle, self.end.angle
-        n1 = self.start.normalized()
-        n2 = self.end.normalized()
-        s = pymunk.Vec2d(1, 0).rotated(start_limit)
-        e = pymunk.Vec2d(1, 0).rotated(end_limit)
-        n1bet = s.dot(e) < s.dot(n1) and s.dot(e) < s.dot(n1)
-        n2bet = s.dot(e) < e.dot(n2) and s.dot(e) < e.dot(n2)
-        print "DOTS n1: ", n1.dot(n2), n1.dot(s), n2.dot(s)
-        print "DOTS n2: ", n1.dot(n2), n1.dot(e), n2.dot(e)
-        print "BET: ", n1bet, n2bet
-        if n1bet:
-            print "TRUE\n----"
-            return True
-        return False
+    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