Commits

Xathos  committed 441a092

Started work on raycasting visualization. Added prototype1 (very rough), and prototype2 (cleaner reimplementation of prototype1). Both use raycasting against a drawn polygon of arbitrary complexity and size, with debugging visualization provided via gl code.

  • Participants
  • Parent commits ee3c556

Comments (0)

Files changed (16)

File colliders.py

 
-import sys
-sys.path.append("..")
-from extensions import XObject
-from vector2 import Vector2
-from collision import detect_line_collision, LineSegment
-from math import pi, sin, cos, sqrt, acos, asin
+class PolyCollider (object):
+    def __init__ (self, parent, verts = []):
+        self.parent = parent
+        self.verts = verts
+        self.bounds = (0,0,0,0)
 
-#
-# Defines three collider types:
-# 
-# BoxCollider - General purpose, relatively well optimized collider
-# SphereCollider - A circular extension of BoxCollider; may have issues, so don't use it.
-# PolyCollider - A polygon collider, that can represent complex shapes of variable complexity.
-#       Use static PolyColliders for backgrounds; BoxColliders for moving objects.
-#       Note: PolyCollider is more trig heavy than BoxCollider, and is expected to perform
-#       slower than its counterpart.
-
-#
-# Finished:
-#   detect_line_collision()  (the core of the physics system) works flawlessly (AFAIK)
-#   BaseCollider, BoxCollider, and PolyCollider classes are (mostly) finished
-#   LineSegment class is done.
-#
-# Todo List:
-#   Finish collider classes
-#   Physics world/simulation core
-#   Add any remaining collision functionality
-#   Rigidbodies?
-#   Visualization
-#   Stress testing
-#
-#   Fine tuning
-#   Rebuild cantankerous classes around physics engine
-#       'offline' simulation if necessary for performance
-#       (separated 'viewer' and 'simulation' cores)
-
-
-
-class BaseCollider (XObject):
-    def __init__ (self, position, rotation = 0.0):
-        self._position = position
-        self._rotation = rotation
-        self._physics_changes = False
-        self._move_vector = Vector2()
-        self.is_solid = True
-        self.is_static = False
-        self.on_collide = None #Callback function
-        self.xbounds = [position.x, position.x]
-        self.ybounds = [position.y, position.y]
-        # eg: collider.on_collide = lambda self, other, point: DoSomething()
-
-    def move (self, move_vector):
-        self._physics_changes = True
-        self._move_vector = move_vector
-
-    def rotate (self, angle):
-        self._physics_changes = True
-        self._rotation += angle
-        self._rotation %= (pi * 2)
-        self._recalc_points()
-
-    def get_collider_segments (self):
-        return None, None, None
-
-    def _on_collision (self, other_collider, point):
-        if self.on_collide is not None:
-            self.on_collide(self, other_collider, point)
-
-class PolyCollider (BaseCollider):
-    def __init__ (self, position, rotation = 0.0, points = []):
-        BaseCollider.__init__(self, position, rotation)
-        self._cache = (None, None, None)
-        self._points = points
         
-    def _recalc_points (self):
-        npoints = []
-
-    def move (self, direction_vector):
-        self._physics_changes = True
-        for point in self._points:
-            point += direction_vector
-
-    def rotate (self, angle):
-        self._physics_changes = True
-        self.xbounds = [self.position.x, self.position.x]
-        self.ybounds = [self.position.x, self.position.x]
-        new_rotation = (self._rotation + angle) % (pi * 2)
-
-        newpoints = []
-        for point in self._points:
-            r = point.x / cos(self._rotation)
-            newpoint = Vector2(r * cos(new_rotation), r * sin(new_rotation))
-            if newpoint.x > self.xbounds[1]:
-                self.xbounds[1] = newpoint.x
-            elif newpoint.x < self.xbounds[0]:
-                self.xbounds[0] = newpoint.x
-            if newpoint.y > self.ybounds[1]:
-                self.ybounds[1] = newpoint.y
-            elif newpoint.y < self.ybounds[0]:
-                self.ybounds[0] = newpoint.y
-            newpoints += [newpoint]
-        self._points = newpoints
-
-    def get_collider_segments (self):
-        if self._physics_changes or self.cache[0] is None:
-            if self.is_static:
-                segments = []
-                j = len(self._points - 1)
-                for i in range(len(self.points)):
-                    segments += [LineSegment(self._points[i], self._points[j])]
-                    j = i
-                self.cache = (segments, None, None)
-                return self.cache
-            else:
-                pass
-        else:
-            return self.cache
-
-    def get_collider_segments (self):
-        if move_vector.x != 0 and move_vector.y != 0:
-            cur_segments = []
-            move_segments = []
-            projected_segments = []
-            j = len(self._points - 1)
-            for i in range(len(self._points)):
-                cur_segments += [LineSegment(self._points[i], self._points[j])]
-                move_segments += [LineSegment(self._points[i], self._points[i] + self.move_vector)]
-                projected_segments += [LineSegment(self._points[i] + self.move_vector, self._points[j] + self.move_vector)]
-                j = i
-            return cur_segments, move_segments, projected_segments
-        else:
-            cur_segments = []
-            j = len(self._points - 1)
-            for i in range(len(self._points)):
-                cur_segments += [LineSegment(self._points[i], self._points[j])]
-                j = i
-            return cur_segments, None, None
-        
-            
-class BoxCollider (BaseCollider):
-    def __init__ (self, position, rotation = 0.0, width = 10.0, height = 10.0):
-        BaseCollider.__init__(self, position, rotation)
-        self._width = width
-        self._height = height
-        self._recalc_points()
-
-    def _recalc_points (self):
-        self._points = [Vector2(cos(self._rotation) * self._width, sin(self._rotation) * self._width),
-                        Vector2(cos(self._rotation + pi / 2) * self._height, sin(self._rotation + pi / 2) * self._height),
-                        Vector2(cos(self._rotation + pi) * self._width, sin(self._rotation + pi) * self._width),
-                        Vector2(cos(self._rotation - pi / 2) * self._height, sin(self._rotation - pi / 2) * self._height)]
-
-    def get_collider_segments (self):
-        if move_vector.x != 0 and move_vector.y != 0:
-            cur_segments = []
-            move_segments = []
-            projected_segments = []
-            j = len(self._points - 1)
-            for i in range(len(self._points)):
-                cur_segments += [LineSegment(self._points[i], self._points[j])]
-                move_segments += [LineSegment(self._points[i], self._points[j])]
-                projected_segments += [LineSegment(self._points[i] + self.move_vector, self._points[j] + self.move_vector)]
-                j = i
-            return cur_segments, move_segments, projected_segments
-        else:
-            cur_segments = []
-            j = len(self._points - 1)
-            for i in range(len(self._points)):
-                cur_segments += [LineSegment(self._points[i], self._points[j])]
-                j = i
-            return cur_segments, None, None
-
-    def get_width (self):
-        return self._width
-
-    def set_width (self, value):
-        self._width = value
-        self._recalc_points()
-
-    def get_height (self):
-        return self._height
-
-    def set_height (self, value):
-        self._height = value
-        self._recalc_points()
-
-    def get_rough_bounds (self):
-        if self._height > self._width:
-            return self._height
-        return self._width
-
-class SphereCollider (BoxCollider):
-    def __init__ (self, position, rotation = 0.0, radius = 10.0):
-        BoxCollider.__init__(position, rotation, radius, radius)
-        self.radius = radius
-
-    def _on_collision (self, other, point):
-        dpos = point - self.position
-        if sqrt(dpos.x * dpos.x + dpos.y * dpos.y) > radius:
-            return #Cancel if collision is outside of circle radius
-        super(SphereCollider, self).on_collide(other, point)
-
-#def p (box):
-#    for point in box._points:
-#        print("%0.2f, %0.2f"%(point.x, point.y))
-#
-#box = BoxCollider(Vector2(), 0.0, 100, 50)
-

File colliders_old.py

+
+import sys
+sys.path.append("..")
+from extensions import XObject
+from vector2 import Vector2
+from collision import detect_line_collision, LineSegment
+from math import pi, sin, cos, sqrt, acos, asin
+
+#
+# Defines three collider types:
+# 
+# BoxCollider - General purpose, relatively well optimized collider
+# SphereCollider - A circular extension of BoxCollider; may have issues, so don't use it.
+# PolyCollider - A polygon collider, that can represent complex shapes of variable complexity.
+#       Use static PolyColliders for backgrounds; BoxColliders for moving objects.
+#       Note: PolyCollider is more trig heavy than BoxCollider, and is expected to perform
+#       slower than its counterpart.
+
+#
+# Finished:
+#   detect_line_collision()  (the core of the physics system) works flawlessly (AFAIK)
+#   BaseCollider, BoxCollider, and PolyCollider classes are (mostly) finished
+#   LineSegment class is done.
+#
+# Todo List:
+#   Finish collider classes
+#   Physics world/simulation core
+#   Add any remaining collision functionality
+#   Rigidbodies?
+#   Visualization
+#   Stress testing
+#
+#   Fine tuning
+#   Rebuild cantankerous classes around physics engine
+#       'offline' simulation if necessary for performance
+#       (separated 'viewer' and 'simulation' cores)
+
+
+class NewPolyCollider (object):
+    def __init__ (self):
+        self.parent #(gameobject)
+        #self.parent.position; self.parent.rotation
+        self.move_vector
+        self.rotation
+        self.verts #local space
+        self.edges #local space
+        #self.edges[i] = LineSegment(self.verts[i], self.verts[i + 1])
+
+    def global_verts (self):
+        pass
+
+    def global_edges (self):
+        pass
+
+    def move_vectors (self):
+        pass
+        
+
+
+class BaseCollider (XObject):
+    def __init__ (self, position, rotation = 0.0):
+        self._position = position
+        self._rotation = rotation
+        self._physics_changes = False
+        self._move_vector = Vector2()
+        self.is_solid = True
+        self.is_static = False
+        self.on_collide = None #Callback function
+        self.xbounds = [position.x, position.x]
+        self.ybounds = [position.y, position.y]
+        # eg: collider.on_collide = lambda self, other, point: DoSomething()
+
+    def move (self, move_vector):
+        self._physics_changes = True
+        self._move_vector = move_vector
+
+    def rotate (self, angle):
+        self._physics_changes = True
+        self._rotation += angle
+        self._rotation %= (pi * 2)
+        self._recalc_points()
+
+    def get_collider_segments (self):
+        return None, None, None
+
+    def _on_collision (self, other_collider, point):
+        if self.on_collide is not None:
+            self.on_collide(self, other_collider, point)
+
+class PolyCollider (BaseCollider):
+    def __init__ (self, position, rotation = 0.0, points = []):
+        BaseCollider.__init__(self, position, rotation)
+        self._cache = (None, None, None)
+        self._points = points
+        
+    def _recalc_points (self):
+        npoints = []
+
+    def move (self, direction_vector):
+        self._physics_changes = True
+        for point in self._points:
+            point += direction_vector
+
+    def rotate (self, angle):
+        self._physics_changes = True
+        self.xbounds = [self.position.x, self.position.x]
+        self.ybounds = [self.position.x, self.position.x]
+        new_rotation = (self._rotation + angle) % (pi * 2)
+
+        newpoints = []
+        for point in self._points:
+            r = point.x / cos(self._rotation)
+            newpoint = Vector2(r * cos(new_rotation), r * sin(new_rotation))
+            if newpoint.x > self.xbounds[1]:
+                self.xbounds[1] = newpoint.x
+            elif newpoint.x < self.xbounds[0]:
+                self.xbounds[0] = newpoint.x
+            if newpoint.y > self.ybounds[1]:
+                self.ybounds[1] = newpoint.y
+            elif newpoint.y < self.ybounds[0]:
+                self.ybounds[0] = newpoint.y
+            newpoints += [newpoint]
+        self._points = newpoints
+
+    def get_static_geometry (self):
+        if self._physics_changes or self.static_geometry_cache is None:
+            segments = []
+            j = len(self._old_points - 1)
+            for i in range(len(self._old_points)):
+                segments += [LineSegment(self._old_points[i], self._old_points[j])]
+                j = i
+            self.static_geometry_cache = segments
+            return segments
+        else:
+            return self.static_geometry_cache
+
+    def get_move_vector_geometry (self):
+        if not self._physics_changes:
+            return None
+        segments = []
+        j = len(self._points -1)
+        for i in range(len(self._points)):
+            pass
+            
+                
+
+ #   def get_collider_segments (self):
+ #       if self._physics_changes or self.cache[0] is None:
+ #           if self.is_static:
+ #               segments = []
+ #               j = len(self._points - 1)
+ #               for i in range(len(self.points)):
+ #                   segments += [LineSegment(self._points[i], self._points[j])]
+ #                   j = i
+ #               self.cache = (segments, None, None)
+ #               return self.cache
+ #           else:
+ #               pass
+ #       else:
+ #           return self.cache
+
+    def get_collider_segments (self):
+        if move_vector.x != 0 and move_vector.y != 0:
+            cur_segments = []
+            move_segments = []
+            projected_segments = []
+            j = len(self._points - 1)
+            for i in range(len(self._points)):
+                cur_segments += [LineSegment(self._points[i], self._points[j])]
+                move_segments += [LineSegment(self._points[i], self._points[i] + self.move_vector)]
+                projected_segments += [LineSegment(self._points[i] + self.move_vector, self._points[j] + self.move_vector)]
+                j = i
+            return cur_segments, move_segments, projected_segments
+        else:
+            cur_segments = []
+            j = len(self._points - 1)
+            for i in range(len(self._points)):
+                cur_segments += [LineSegment(self._points[i], self._points[j])]
+                j = i
+            return cur_segments, None, None
+        
+            
+class BoxCollider (BaseCollider):
+    def __init__ (self, position, rotation = 0.0, width = 10.0, height = 10.0):
+        BaseCollider.__init__(self, position, rotation)
+        self._width = width
+        self._height = height
+        self._recalc_points()
+
+    def _recalc_points (self):
+        self._points = [Vector2(cos(self._rotation) * self._width, sin(self._rotation) * self._width),
+                        Vector2(cos(self._rotation + pi / 2) * self._height, sin(self._rotation + pi / 2) * self._height),
+                        Vector2(cos(self._rotation + pi) * self._width, sin(self._rotation + pi) * self._width),
+                        Vector2(cos(self._rotation - pi / 2) * self._height, sin(self._rotation - pi / 2) * self._height)]
+
+    def get_collider_segments (self):
+        if move_vector.x != 0 and move_vector.y != 0:
+            cur_segments = []
+            move_segments = []
+            projected_segments = []
+            j = len(self._points - 1)
+            for i in range(len(self._points)):
+                cur_segments += [LineSegment(self._points[i], self._points[j])]
+                move_segments += [LineSegment(self._points[i], self._points[j])]
+                projected_segments += [LineSegment(self._points[i] + self.move_vector, self._points[j] + self.move_vector)]
+                j = i
+            return cur_segments, move_segments, projected_segments
+        else:
+            cur_segments = []
+            j = len(self._points - 1)
+            for i in range(len(self._points)):
+                cur_segments += [LineSegment(self._points[i], self._points[j])]
+                j = i
+            return cur_segments, None, None
+
+    def get_width (self):
+        return self._width
+
+    def set_width (self, value):
+        self._width = value
+        self._recalc_points()
+
+    def get_height (self):
+        return self._height
+
+    def set_height (self, value):
+        self._height = value
+        self._recalc_points()
+
+    def get_rough_bounds (self):
+        if self._height > self._width:
+            return self._height
+        return self._width
+
+class SphereCollider (BoxCollider):
+    def __init__ (self, position, rotation = 0.0, radius = 10.0):
+        BoxCollider.__init__(position, rotation, radius, radius)
+        self.radius = radius
+
+    def _on_collision (self, other, point):
+        dpos = point - self.position
+        if sqrt(dpos.x * dpos.x + dpos.y * dpos.y) > radius:
+            return #Cancel if collision is outside of circle radius
+        super(SphereCollider, self).on_collide(other, point)
+
+#def p (box):
+#    for point in box._points:
+#        print("%0.2f, %0.2f"%(point.x, point.y))
+#
+#box = BoxCollider(Vector2(), 0.0, 100, 50)
+
+
+from vector2 import Vector2
+from math import sqrt
+
+class LineSegment (object):
+    def __init__ (self, v1, v2):
+        # Two vector points define the line segment:
+        self.v1 = v1
+        self.v2 = v2
+        # The following is used internally:
+        # cx, cy, and cv represent entries in an augumented matrix
+        # (LineSegment stands in for a matrix row)
+        self.cx = -1 * (v1.y - v2.y)
+        self.cy = v1.x - v2.x
+        self.cv = v1.y * (v1.x - v2.x) - v1.x * (v1.y - v2.y)
+        # xbounds and ybounds define the limit of the line segment.
+        if v1.x < v2.x:
+            self.xbounds = (v1.x, v2.x)
+        else:
+            self.xbounds = (v2.x, v1.x)
+        if v1.y < v2.y:
+            self.ybounds = (v1.y, v2.y)
+        else:
+            self.ybounds = (v2.y, v1.y)
+
+    def point_at_x (self, x):
+        y = (self.cv - self.cx * x) / self.cy
+        if x < self.xbounds[0] or x > self.xbounds[1]:
+            return None, Vector2(x, y)
+        if y < self.ybounds[0] or y > self.ybounds[1]:
+            return None, Vector2(x, y)
+        return Vector2(x, y)
+
+    def point_at_y (self, y):
+        x = (self.cv - self.cy * y) / self.cx
+        if x < self.xbounds[0] or x > self.xbounds[1]:
+            return None, Vector2(x, y)
+        if y < self.ybounds[0] or y > self.ybounds[1]:
+            return None, Vector2(x, y)
+        return Vector2(x, y)
+
+    def __repr__ (self):
+        return "LineSegment(%s, %s)"%(repr(self.v1), repr(self.v2))
+
+    def __str__ (self):
+        return "(%s, %s)\n%0.2f %0.2f | %0.2f\nx bounds: %s\ny bounds: %s"%(self.v1,
+                                self.v2, self.cx, self.cy, self.cv, self.xbounds, self.ybounds)
+
+class Ray (LineSegment):
+    def __init__ (self, start_point, end_point):
+        LineSegment.__init__(self, start_point, end_point)
+ #       self.length = 
+
+    def get_length (self):
+        return sqrt((self.v1.x - self.v2.x) ** 2 + (self.v1.y - self.v2.y) ** 2)
+
+    def set_length (self, new_length):
+        pass
+
+
+
+def detect_line_collision (segment1, segment2):
+    #Uses matrix-based solving algorithm
+    #(line segment objects stand in for matrix rows)
+    # cx, cy, and cv correspond to entries in a 2d augumented matrix's rows.
+    #Swap rows if m1.cx == 0
+    if segment1.cx != 0:
+        m = [segment1, segment2]
+    elif segment2.cx != 0:
+        m = [segment2, segment1]
+    else:
+        return None
+       # raise Exception, "Both cx values are zero!"
+    # Divide row1 by row1.cx (set row1.cx to 1)
+    m[0].cy /= m[0].cx
+    m[0].cv /= m[0].cx
+    m[0].cx /= m[0].cx
+    # Add row1 * -row2.cx to row2 (clear row2.cx)
+    m[1].cy -= m[1].cx * m[0].cy
+    m[1].cv -= m[1].cx * m[0].cv
+    m[1].cx -= m[1].cx
+    # Divide row2 by row2.cy (set row2.cy to 1)
+    m[1].cv /= m[1].cy
+    m[1].cy /= m[1].cy
+    # Add row2 * row1.cy to row1 (clear row1.cy)
+    m[0].cv -= m[0].cy * m[1].cv
+    m[0].cy -= m[0].cy
+    #
+    # Matrix is now solved: point of intersection is (row1.cv, row2.cv)
+    #
+    # Next, check to see if solution is within both line segment's x/y bounds:
+    # if this fails, return None; else return the intersection point as a Vector2   
+    if m[0].cv > m[0].xbounds[1] or m[0].cv < m[0].xbounds[0]:
+        return None
+    if m[0].cv > m[1].xbounds[1] or m[0].cv < m[1].xbounds[0]:
+        return None
+    if m[1].cv > m[0].ybounds[1] or m[1].cv < m[0].ybounds[0]:
+        return None
+    if m[1].cv > m[1].ybounds[1] or m[1].cv < m[1].ybounds[0]:
+        return None
+    return Vector2(m[0].cv, m[1].cv)
+
+def condensed_detect_line_collision (segment1, segment2):
+    # Condensed version of detect_line_collision:
+    # Uses algebraic version of the above matrix operations
+     # m1.cx m1.cy | m1.cv -> a b | x
+     # m2.cx m2.cy | m2.cv -> c d | y
+     # x = x / a - b / a * (y - c * x / a) / (d - c * b / a)
+     # y = (y - c * x / a) / (d - c * b / a)
+    if segment1.cx != 0:
+         a = segment1.cx; c = segment2.cx
+         b = segment1.cy; d = segment2.cy
+         x = segment1.cv; y = segment2.cv
+    elif segment2.cx != 0:
+        a = segment2.cx; c = segment1.cx
+        b = segment2.cy; d = segment1.cy
+        x = segment2.cv; y = segment1.cv
+    else:
+        return None
+
+    fx = x / a - b / a * (y - c * x / a) / (d - c * b / a)
+    fy = (y - c * x / a) / (d - c * b / a)
+    if fx > segment1.xbounds[1] or fx < segment1.xbounds[0]:
+        return None
+    if fx > segment2.xbounds[1] or fx < segment2.xbounds[0]:
+        return None
+    if fy > segment1.ybounds[1] or fy < segment1.ybounds[0]:
+        return None
+    if fy > segment2.ybounds[1] or fy < segment2.ybounds[0]:
+        return None
+    return Vector2(fx, fy)
+
+        
+def alt_detect_line_collision (line1_point1, line1_point2, line2_point1, line2_point2):
+    return detect_line_collision(LineSegment(line1_point1, line1_point2),
+                                 _LineSegment(line2_point1, line2_point2))

File collision.py

 
 
 
-def detect_line_collision (segment1, segment2):
+def original_detect_line_collision (segment1, segment2):
     #Uses matrix-based solving algorithm
     #(line segment objects stand in for matrix rows)
     # cx, cy, and cv correspond to entries in a 2d augumented matrix's rows.
     #Swap rows if m1.cx == 0
+    print(segment1)
+    print(segment2)
     if segment1.cx != 0:
         m = [segment1, segment2]
     elif segment2.cx != 0:
         return None
     return Vector2(m[0].cv, m[1].cv)
 
-def condensed_detect_line_collision (segment1, segment2):
+def detect_line_collision (segment1, segment2):
     # Condensed version of detect_line_collision:
     # Uses algebraic version of the above matrix operations
      # m1.cx m1.cy | m1.cv -> a b | x
     elif segment2.cx != 0:
         a = segment2.cx; c = segment1.cx
         b = segment2.cy; d = segment1.cy
-        x = segment2.cv; d = segment1.cv
+        x = segment2.cv; y = segment1.cv
     else:
         return None
-
-    fx = x / a - b / a * (y - c * x / a) / (d - c * b / a)
-    fy = (y - c * x / a) / (d - c * b / a)
+    #The following cases are to prevent divide by zero errors or other potential problems
+    #Note: a is already taken care of by the first if block
+    if b == 0 and d == 0:
+        return None
+    elif c == 0:
+        fx = x / a - b / a * y / d
+        fy = y / d
+    elif b == 0:
+        fx = x / a
+        fy = (y - c * x) / d
+    elif d == 0:
+        fx = y / c
+        fy = (x - a * x) / b
+    elif d == c * b / a:
+        return None
+        #if d == c * b / a, then d - c * b / a == 0
+        # => No solution is possible (divide by zero error)
+    else: #Default solution
+        fx = x / a - b / a * (y - c * x / a) / (d - c * b / a)
+        fy = (y - c * x / a) / (d - c * b / a)
     if fx > segment1.xbounds[1] or fx < segment1.xbounds[0]:
         return None
     if fx > segment2.xbounds[1] or fx < segment2.xbounds[0]:
         
 def alt_detect_line_collision (line1_point1, line1_point2, line2_point1, line2_point2):
     return detect_line_collision(LineSegment(line1_point1, line1_point2),
-                                 _LineSegment(line2_point1, line2_point2))
+                                 LineSegment(line2_point1, line2_point2))

File collision.pyc

Binary file modified.

File featureset (rough).txt

+Featureset:
+
+Modular, extensible design:
+    Physics/Graphics "Core"
+    Socketable AIs
+    Flexible World behaviour
+        The "Ruleset" can be extended, modified, or be otherwise replaced (like the AIs)
+    Presets are stored in modular files:
+        ruleset
+        tank skins
+        tank controllers (ais)
+        scene geometry
+        'map'/scene (scene geometry + scripted objects)

File new_collision.py

+from vector2 import Vector2
+from math import sqrt, sin, cos
+
+
+def overlap (bounds1, bounds2):
+   # if bounds1.x.max < bounds2.x.min or bounds1.x.min > bounds2.max:
+   if bounds1[1] < bounds2[0] or bounds1[0] > bounds2[1]:
+        return False
+   # if bounds1.y.max < bounds2.y.min or bounds1.y.min > bounds2.y.max:
+   if bounds1[3] < bounds2[2] or bounds1[2] > bounds2[3]:
+        return False
+    return True
+
+def collision_check_sub (move_vector, static_collider, active_collider):
+
+
+
+def check_collision (poly1, poly2):
+    delta_mv1 = poly1.move_vector - poly2.move_vector
+    delta_mv2 = poly2.move_vector - poly1.move_vector
+
+    j = len(verts) - 1
+    lines = []
+    for i in range(len(verts)):
+        lines += [Line(verts[i], verts[j])]
+        j = i
+
+
+def check_collision (spoly, mpoly, direction):
+
+    for i in range(len(verts)):
+        line = Line(verts[i], verts[j])
+        if line.normal.dot(direction) > 0:
+            lines += [line]
+            
+        
+
+    #incomplete
+
+
+class Line (object):
+    def __init__ (self, v1, v2):
+        self.v1 = v1
+        self.v2 = v3
+
+        self.cx = v2.y - v1.y
+        self.cy = v1.x - v2.x
+        self.cv = v1.y * (v1.x - v2.x) - v1.x * (v1.y - v2.y)
+
+        if v1.x < v2.x:
+            xbounds = (v1.x, v2.x)
+        else:
+            xbounds = (v2.x, v1.x)
+        if v1.y < v2.y:
+            ybounds = (v1.y - v2.y)
+        else:
+            ybounds = (v2.y, v1.y)
+        self.bounds = (xbounds[0], xbounds[1], ybounds[0], ybounds[1])
+
+        #Will this work?
+        self._angle = v1.angle(v2)
+        self.normal = Vector2(cos(self._angle), sin(self._angle))
+        
 
     def step (self, dt):
         for agent in self.agents:
+
+
+def collide (poly1, poly2):
+    #Simple: pure translation; no rotation
+    if not overlap(poly1.bounds, poly2.bounds):
+        return None, poly1, poly2
+    mv2 = poly1.move_vector - poly2.move_vector
+    mv1 = poly2.move_vector - poly1.move_vector
+
+    geometry1 = []
+    for edge in poly1.edges:
+        if edge.dot(mv1) > 0:
+            geometry1 += [edge]
+    move_vectors1 = []
+    for i in range(len(poly2.edges)):
+        if poly2.edges[i].dot(mv1) < 0:
+            if len(mvs1) == 0:
+                mvs1 += [LineSegment(poly2.verts[i], poly2.pastverts[i]),
+                                 LineSegment(poly2.verts[i+1], poly2.pastverts[i + 1])]
+            else:
+                mvs1 += [LineSegment(poly2.verts[i +1], poly2.pastverts[i+1])]
+    limit = 0.0
+    collision_point = None
+    for edge in geometry1:
+        for vector in move_vectors1:
+            if not overlap(limit.bounds, edge.bounds):
+                continue
+            cpoint = line_collide(edge, vector)
+            if cpoint is None:
+                continue
+            new_limit = (vector - cpoint) / (vector - poly2.position)
+            if new_limit < limit:
+                limit = new_limit
+                collision_point = cpoint
+    #repeat all above w/ poly1 and poly2's roles flipped
+    #don't re-zero 'limit' though
+
+    if limit == 0:
+        return None, poly1, poly2
+
+    return collision_report(limit, collision_point), poly1, poly2
+    
+    
+            
+
+    
+    
+
+
+
+
+
             
                 

File prototype1/prototype1.py

+import sys
+sys.path.append("..")
+from vector2 import Vector2
+from temp import Polygon, GameObject
+from collision import LineSegment, detect_line_collision
+from math import sqrt
+import pyglet
+from pyglet.window import key
+from pyglet.gl import *
+#
+# Temp. Key Setup:
+# d: Add a new line to the polygon, from the last added point to the cursor position
+# c: Clear all points and lines in the polygon
+# s: Set the ray_origin to cursor position
+# r: Raycast from ray_origin to cursor position
+# b: Toggle line bounds visualization on
+# n: Toggle line bounds visualization off
+
+window = pyglet.window.Window()
+keys = key.KeyStateHandler()
+window.push_handlers(keys)
+
+obj = GameObject(Vector2(100, 100))
+
+cursor = Vector2()
+ray_origin = Vector2()
+ray_hit = Vector2()
+ray = LineSegment(Vector2(), Vector2())
+draw_bounds = True
+
+@window.event
+def on_mouse_motion (x, y, dx, dy):
+    global cursor
+    cursor = Vector2(x, y)
+
+def draw_line (v1, v2):
+    pyglet.graphics.draw(2, GL_LINES, ('v2f',
+            (v1.x, v1.y, v2.x, v2.y)))
+
+def prim_draw_line (x1, y1, x2, y2):
+    pyglet.graphics.draw(2, GL_LINES,
+            ('v2f', (x1, y1, x2, y2)))
+
+def draw_crosshair (point, radius = 5):
+    pyglet.graphics.draw(2, GL_LINES,
+            ('v2f', (point.x - radius, point.y, point.x + radius, point.y)))
+    pyglet.graphics.draw(2, GL_LINES,
+            ('v2f', (point.x, point.y - radius, point.x, point.y + radius)))
+
+@window.event
+def on_draw ():
+    pass
+
+def draw_bound_box (xbounds, ybounds):
+    prim_draw_line(xbounds[0], ybounds[0], xbounds[1], ybounds[0])
+    prim_draw_line(xbounds[1], ybounds[0], xbounds[1], ybounds[1])
+    prim_draw_line(xbounds[1], ybounds[1], xbounds[0], ybounds[1])
+    prim_draw_line(xbounds[0], ybounds[1], xbounds[0], ybounds[0])
+
+def redraw ():
+    global draw_bounds
+    #print("Redrawing")
+    window.clear()
+    #Draw polygon
+    for line in obj.geometry.lines:
+        if draw_bounds:
+            glColor4f(0.2, 0.2, 0.0, 1.0)
+            draw_bound_box(line.xbounds, line.ybounds)
+        glColor4f(1.0, 0.0, 1.0, 1.0)
+        draw_line(line.v1, line.v2)
+    #Draw ray
+    if draw_bounds:
+        glColor4f(1.0, 0.0, 0.0, 1.0)
+        draw_bound_box(ray.xbounds, ray.ybounds)
+    glColor4f(1.0, 1.0, 0.0, 1.0)
+    draw_line(ray.v1, ray.v2)
+    #Draw ray origin
+    glColor4f(0.0, 1.0, 0.0, 1.0)
+    draw_crosshair(ray_origin)
+    #Draw ray hit
+    glColor4f(1.0, 0.0, 0.0, 1.0)
+    draw_crosshair(ray_hit)
+
+def overlap (line1, line2):
+    if line1.xbounds[0] > line2.xbounds[1] or line1.xbounds[1] < line2.xbounds[0]:
+        return False
+    if line1.ybounds[0] > line2.ybounds[1] or line1.ybounds[1] < line2.ybounds[0]:
+        return False
+    return True
+
+def update (dt):
+    global ray_origin, ray_hit, draw_bounds
+    if keys[key.D]:
+        if len(obj.geometry.points) > 0:
+            lastpoint = obj.geometry.points[- 1]
+            d = sqrt((lastpoint.x - cursor.x) ** 2 + (lastpoint.y - cursor.y) ** 2)
+            #print("Distance: %0.2f"%d)
+        else:
+            d = 10.0
+        if d > 5.0:
+            obj.geometry.points += [cursor]
+            ln = len(obj.geometry.points)
+            if ln > 1:
+                obj.geometry.lines += [LineSegment(obj.geometry.points[ln -2],
+                        obj.geometry.points[ln -1])]
+            print(ln)
+            redraw()
+    if keys[key.C]:
+        obj.geometry.points = []
+        obj.geometry.lines = []
+        print("Clear")
+        redraw()
+    if keys[key.S]:
+        ray_origin = cursor
+        redraw()
+    if keys[key.B]:
+        draw_bounds = True
+        redraw()
+    if keys[key.N]:
+        draw_bounds = False
+        redraw()
+    if keys[key.R]:
+        raycast()
+
+def raycast ():
+    global ray_origin, ray_hit, ray
+    hit_points = []
+    ray = LineSegment(ray_origin, cursor)
+    for seg in obj.geometry.lines:
+        if not overlap(seg, ray):
+            continue
+        hit = detect_line_collision(ray, seg)
+        if hit is not None:
+            hit_points += [hit]
+    print(hit_points)
+    hit = ray_origin
+    dist = 0.0
+    for point in hit_points:
+        d = sqrt((point.x - ray_origin.x) ** 2 + (point.y - ray_origin.y) ** 2)
+        if d > dist:
+            hit = point
+            dist = d
+    if hit.x != ray_origin.x and hit.y != ray_origin.y:
+        ray_hit = hit
+    redraw()
+    for point in hit_points:
+        glColor4f(1.0, 0.5, 0.0, 1.0)
+        draw_crosshair(point)
+            
+            
+framerate = 24.0
+pyglet.clock.schedule_interval(update, 1.0 / framerate)
+
+pyglet.app.run()
+    

File prototype1/prototype1_old.py

+import sys
+sys.path.append("..")
+from vector2 import Vector2
+from temp import Polygon, GameObject
+from collision import LineSegment, detect_line_collision
+from math import sqrt
+import pyglet
+from pyglet.window import key
+from pyglet.gl import *
+
+
+window = pyglet.window.Window()
+keys = key.KeyStateHandler()
+window.push_handlers(keys)
+
+obj = GameObject(Vector2(100, 100))
+
+cursor = Vector2()
+ray_origin = Vector2()
+ray_hit = Vector2()
+
+@window.event
+def on_mouse_motion (x, y, dx, dy):
+    global cursor
+    cursor = Vector2(x, y)
+
+def draw_line (v1, v2):
+    pyglet.graphics.draw(2, GL_LINES, ('v2f',
+            (v1.x, v1.y, v2.x, v2.y))
+    )
+
+def draw_crosshair (point, radius = 5):
+    pyglet.graphics.draw(2, GL_LINES,
+            ('v2f', (point.x - radius, point.y, point.x + radius, point.y)))
+    pyglet.graphics.draw(2, GL_LINES,
+            ('v2f', (point.x, point.y - radius, point.x, point.y - radius)))
+
+@window.event
+def on_draw ():
+    window.clear()
+    #Draw polygon
+    glColor4f(1.0, 0.0, 1.0, 1.0)
+    j = len(obj.geometry.points) - 1
+    for i in range(len(obj.geometry.points)):
+        draw_line(obj.geometry.points[j] + obj.position,
+                  obj.geometry.points[i] + obj.position)
+        j = i
+    #Draw ray origin
+    glColor4f(0.0, 1.0, 0.0, 1.0)
+    draw_crosshair(ray_origin)
+    #Draw ray hit
+    glColor4f(1.0, 0.0, 0.0, 1.0)
+    draw_crosshair(ray_hit)
+    #Draw ray
+    glColor4f(1.0, 1.0, 0.0, 1.0)
+    draw_line(ray_origin, ray_hit)
+
+def overlap (line1, line2):
+    if line1.xbounds[0] > line2.xbounds[1] or line1.xbounds[1] < line2.xbounds[0]:
+        return False
+    if line1.ybounds[0] > line2.ybounds[1] or line1.ybounds[1] < line2.ybounds[0]:
+        return False
+    return True
+
+def update (dt):
+    global ray_origin, ray_hit
+    if keys[key.D]:
+        if len(obj.geometry.points) > 0:
+            lastpoint = obj.geometry.points[- 1] + obj.position
+            d = sqrt((lastpoint.x - cursor.x) ** 2 + (lastpoint.y - cursor.y) ** 2)
+            #print("Distance: %0.2f"%d)
+        else:
+            d = 10.0
+        if d > 5.0:
+            obj.geometry.points += [cursor - obj.position]
+            print(len(obj.geometry.points))
+    if keys[key.C]:
+        obj.geometry.points = []
+        print("Clear")
+    if keys[key.S]:
+        ray_origin = cursor
+    if keys[key.R]:
+        hit_points = []
+        ray = LineSegment(ray_origin, cursor)
+        j = len(obj.geometry.points) - 1
+        for i in range(len(obj.geometry.points)):
+            seg = LineSegment(obj.geometry.points[j], obj.geometry.points[i])
+            if not overlap(seg, ray):
+                continue
+            hit = detect_line_collision(ray, seg)
+            if hit is not None:
+                hit_points += [hit]
+        print(hit_points)
+        ray_hit = ray_origin
+        dist = 100000.0
+        for point in hit_points:
+            d = sqrt((point.x - ray_origin.x) ** 2 + (point.y - ray_origin.y) ** 2)
+            if d > dist:
+                ray_hit = point
+                dist = d
+            
+            
+framerate = 24.0
+pyglet.clock.schedule_interval(update, 1.0 / framerate)
+
+pyglet.app.run()
+    

File prototype1/temp.py

+import sys
+sys.path.append("..")
+from vector2 import Vector2
+
+
+class GameObject (object):
+    def __init__ (self, position = None, rotation = 0.0):
+        if position is None:
+            self.position = Vector2(0, 0)
+        else:
+            self.position = position
+        self.rotation = rotation
+        self.geometry = Polygon(self)
+            
+class Polygon (object):
+    def __init__ (self, parent, points = []):
+        self.parent = parent
+        self.points = points
+        self.lines = []
+
+        
+        

File prototype2/prototype2

+import sys
+sys.path.append("..")
+from vector2 import Vector2
+from math import sqrt
+from temp import Polygon, GameObject, PhysicsWorld
+import pyglet
+from pyglet.window import key

File prototype2/prototype2.py

+import sys
+sys.path.append("..")
+from vector2 import Vector2
+from math import sqrt
+from temp import Polygon, GameObject, PhysicsWorld
+from collision import LineSegment, detect_line_collision
+import pyglet
+from pyglet.gl import *
+from pyglet.window import key
+
+# INPUT:
+# d: Add a point to the polygon at cursor position (mouse position)
+# c: Clear all vertex data from the polygon
+# s: Reset the ray's origin to cursor position
+# r: Raycast from the ray's origin to cursor position
+# b: Display bounding boxes
+# n: Disable display of bounding boxes
+#
+#
+
+window = pyglet.window.Window()
+keys = key.KeyStateHandler()
+window.push_handlers(keys)
+world = PhysicsWorld()
+
+obj = GameObject(Vector2(200, 200))
+world.add(obj)
+
+cursor_pos = Vector2()
+ray = LineSegment(Vector2(), Vector2())
+ray_hit = None
+
+#Options:
+draw_bounds = True #Show bounding boxes? (toggleable: b/n)
+debug_raycasting = True #Print debug info from raycasting?
+
+@window.event
+def on_mouse_motion (x, y, dx, dy):
+    global cursor_pos
+    cursor_pos = Vector2(x, y)
+
+@window.event
+def on_draw ():
+    pass
+
+def redraw ():
+    global draw_bounds
+    window.clear()
+    world.debug_draw(draw_bounds)
+    #Manually debug draw the ray (not included in world)
+    if draw_bounds:
+        #Draw bounds
+        glColor4f(0.3, 0.0, 0.3, 1.0)
+        pyglet.graphics.draw(2, GL_LINES,
+                ('v2f', (ray.xbounds[0], ray.ybounds[0], ray.xbounds[1], ray.ybounds[0])))
+        pyglet.graphics.draw(2, GL_LINES,
+                ('v2f', (ray.xbounds[1], ray.ybounds[0], ray.xbounds[1], ray.ybounds[1])))
+        pyglet.graphics.draw(2, GL_LINES,
+                ('v2f', (ray.xbounds[1], ray.ybounds[1], ray.xbounds[0], ray.ybounds[1])))
+        pyglet.graphics.draw(2, GL_LINES,
+                ('v2f', (ray.xbounds[0], ray.ybounds[1], ray.xbounds[0], ray.ybounds[0])))
+    #Draw ray
+    glColor4f(1.0, 0.0, 0.0, 1.0)
+    pyglet.graphics.draw(2, GL_LINES,
+            ('v2f', (ray.v1.x, ray.v1.y, ray.v2.x, ray.v2.y)))
+    #Draw hit point
+    if ray_hit is not None:
+        glColor4f(0.0, 1.0, 0.0, 1.0)
+        pyglet.graphics.draw(2, GL_LINES,
+                ('v2f', (ray_hit.x - 5, ray_hit.y, ray_hit.x + 5, ray_hit.y)))
+        pyglet.graphics.draw(2, GL_LINES,
+                ('v2f', (ray_hit.x, ray_hit.y - 5, ray_hit.x, ray_hit.y + 5)))
+            
+def update (dt):
+    global ray, draw_bounds, ray_hit
+
+    if keys[key.D]:
+        if len(obj.geometry.points) > 0:
+            lastpoint = obj.geometry.points[-1]
+            d = sqrt((lastpoint.x - cursor_pos.x) ** 2 + (lastpoint.y - cursor_pos.y) ** 2)
+        else:
+            d = 10.0
+        if d > 5.0:
+            obj.geometry.points += [cursor_pos]
+            obj.geometry.rebuild()
+        redraw()
+    if keys[key.C]:
+        obj.geometry.points = []
+        obj.geometry.rebuild()
+        redraw()
+    if keys[key.S]:
+        ray = LineSegment(cursor_pos, cursor_pos)
+        ray_hit = cursor_pos
+        redraw()
+    if keys[key.R]:
+        ray = LineSegment(ray.v1, cursor_pos)
+        ray_hit = world.raycast(ray, debug_raycasting)
+        #print(ray_hit)
+        redraw()
+    if keys[key.B]:
+        draw_bounds = True
+        redraw()
+    if keys[key.N]:
+        draw_bounds = False
+        redraw()
+
+window.clear()
+pyglet.clock.schedule_interval(update, 1.0 / 24.0)
+pyglet.app.run()

File prototype2/temp.py

+import sys
+sys.path.append("..")
+from vector2 import Vector2
+from math import sqrt
+from collision import detect_line_collision, LineSegment
+import pyglet
+from pyglet.gl import *
+
+
+class GameObject (object):
+    def __init__ (self, position = None, rotation = 0.0):
+        if position is None:
+            self.position = Vector2(0, 0)
+        else:
+            self.position = position
+        self.rotation = rotation
+        self.geometry = Polygon(self)
+            
+class Polygon (object):
+    def __init__ (self, parent, points = []):
+        self.parent = parent
+        self.points = points
+        self.rebuild()
+
+    def rebuild (self):
+        self.lines = []
+        if len(self.points) == 0:
+            self.xbounds = [self.parent.position.x, self.parent.position.x]
+            self.ybounds = [self.parent.position.y, self.parent.position.y]
+            return
+        self.xbounds = [self.points[0].x, self.points[0].x]
+        self.ybounds = [self.points[0].y, self.points[0].y]
+        j = len(self.points) - 1
+        for i in range(len(self.points)):
+            self.lines += [LineSegment(self.points[j], self.points[i])]
+            if self.points[i].x > self.xbounds[1]:
+                self.xbounds[1] = self.points[i].x
+            elif self.points[i].x < self.xbounds[0]:
+                self.xbounds[0] = self.points[i].x
+            if self.points[i].y > self.ybounds[1]:
+                self.ybounds[1] = self.points[i].y
+            elif self.points[i].y < self.ybounds[0]:
+                self.ybounds[0] = self.points[i].y
+            j = i
+
+    def debug_draw (self, draw_bounds = False):
+        #Draw polygon bounds
+        if draw_bounds:
+            glColor4f(0.0, 0.5, 0.5, 1.0)
+            pyglet.graphics.draw(2, GL_LINES,
+                    ('v2f', (self.xbounds[0], self.ybounds[0], self.xbounds[1], self.ybounds[0])))
+            pyglet.graphics.draw(2, GL_LINES,
+                    ('v2f', (self.xbounds[1], self.ybounds[0], self.xbounds[1], self.ybounds[1])))
+            pyglet.graphics.draw(2, GL_LINES,
+                    ('v2f', (self.xbounds[1], self.ybounds[1], self.xbounds[0], self.ybounds[1])))
+            pyglet.graphics.draw(2, GL_LINES,
+                    ('v2f', (self.xbounds[0], self.ybounds[1], self.xbounds[0], self.ybounds[0])))
+        glColor4f(1.0, 1.0, 0.0, 1.0)
+        for line in self.lines:
+            #Draw Line bounds
+            if draw_bounds:
+                glColor4f(0.2, 0.2, 0.0, 1.0)
+                pyglet.graphics.draw(2, GL_LINES,
+                        ('v2f', (line.xbounds[0], line.ybounds[0], line.xbounds[1], line.ybounds[0])))
+                pyglet.graphics.draw(2, GL_LINES,
+                        ('v2f', (line.xbounds[1], line.ybounds[0], line.xbounds[1], line.ybounds[1])))
+                pyglet.graphics.draw(2, GL_LINES,
+                        ('v2f', (line.xbounds[1], line.ybounds[1], line.xbounds[0], line.ybounds[1])))
+                pyglet.graphics.draw(2, GL_LINES,
+                        ('v2f', (line.xbounds[0], line.ybounds[1], line.xbounds[0], line.ybounds[0])))
+                glColor4f(1.0, 1.0, 0.0, 1.0)
+            #Draw Line
+            pyglet.graphics.draw(2, GL_LINES,
+                    ('v2f', (line.v1.x, line.v1.y, line.v2.x, line.v2.y)))
+            
+
+def line_overlap (line1, line2):
+    if line1.xbounds[0] > line2.xbounds[1] or line1.xbounds[1] < line2.xbounds[0]:
+        return False
+    if line1.ybounds[0] > line2.ybounds[1] or line1.ybounds[1] < line2.ybounds[0]:
+        return False
+    return True
+
+class PhysicsWorld (object):
+    def __init__ (self):
+        self.objs = []
+
+    def add (self, obj):
+        if type(obj) != GameObject:
+            raise TypeError, "Only GameObjects can be added to a PhysicsWorld"
+        self.objs += [obj]
+
+    def debug_draw (self, draw_bounds = False):
+        for obj in self.objs:
+            obj.geometry.debug_draw(draw_bounds)
+
+    def raycast_return_all (self, ray, debug = False):
+        hits = []
+        if debug:
+            print("Raycasting:\n%s"%ray)
+        calcs = 0
+        for obj in self.objs:
+           # if not obj.geometry.overlap_with_line(ray):
+           #     continue
+            for line in obj.geometry.lines:
+                if not line_overlap(line, ray):
+                    continue
+                calcs += 1
+                hit = detect_line_collision(ray, line)
+                if hit is not None:
+                    hits += [hit]
+        if debug:
+            print("Found %i points of intersection after %i calculations:\n%s"%(len(hits), calcs, hits))
+        return hits
+    
+    def raycast (self, ray, debug = False):
+        """Return the """
+        hits = self.raycast_return_all(ray, debug)
+        closest = None
+        dist = 100000.0
+        for point in hits:
+            d = sqrt((point.x - ray.v1.x) ** 2 + (point.y - ray.v1.y) ** 2)
+            if d < dist:
+                closest = point
+                dist = d
+        return closest
+
+
+
+        
+        

File structure.txt

+
+classes:
+    Vector2
+    Line
+    PhysicsWorld
+    GameWorld (extension, or wrapper of PhysicsWorld)
+    GameObject
+    Polygon (vertex/edge data)
+
+    Ray (Line factory)
+    Tank (extension of GameObject)
+
+methods:
+    public:
+    raycast (world, ray)
+
+    internal:
+    object_collision(obj1, obj2) (wrapper for poly_collision)
+    poly_collision(poly1, poly2)
+    ray_collision(line, poly)
+    line_collision(line1, line2)
+    
+Physics behaviour:
+    World update w/ deltatime:
+        1. check all moving objs vs all static objs for collision
+        2. check all moving objs vs all other moving objs for collision
+        3. Limit movement
+        If objects bounce:
+            4. Calculate new movement vectors for all applicable objects that have collided
+            5. Resolve these collisions using subset of time frame
+            6. Repeat as long as bouncing objects have collisions that need to be resolved.
+            
+Resolve collision between two objects:
+    If bounds don't overlap, exit early
+    1. Calculate delta_movement, and inverse_delta_movement
+    2. Generate lists of verts and edges using these vectors
+    3. Run subroutine: reg. collision, and inverse:
+        Check move_vectors vs geometry for collisions
+    4. Handle results; determine what occured, and return collision info (if any)

File vector2.pyc

Binary file modified.