annotate grease/collision.py @ 14:a9019d0ac5f8

Added system for containers
author KarstenBock@gmx.net
date Sun, 31 Jul 2011 23:47:28 +0200
parents bc88f7d5ca8b
children
rev   line source
5
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
1 #############################################################################
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
2 #
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
3 # Copyright (c) 2010 by Casey Duncan and contributors
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
4 # All Rights Reserved.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
5 #
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
6 # This software is subject to the provisions of the MIT License
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
7 # A copy of the license should accompany this distribution.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
8 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
9 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
10 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
11 #
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
12 #############################################################################
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
13 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
14 **Grease collision detection systems**
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
15
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
16 Grease uses two-phase broad and narrow collision detection. *Broad-phase*
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
17 collision systems are used to efficiently identify pairs that may be colliding
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
18 without resorting to a brute-force check of all possible pairs. *Narrow-phase*
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
19 collision systems use the pairs generated by the broad-phase and perform more
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
20 precise collision tests to determine if a collision has actually occurred. The
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
21 narrow-phase system also calculates more details about each collision,
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
22 including collision point and normal vector for use in collision response.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
23
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
24 A typical collision detection system consists of a narrow-phase system that
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
25 contains a broad-phased system. The narrow-phase system is usually the only
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
26
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
27 one that the application directly interacts with, though the application is
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
28 free to use the broad-phased system directly if desired. This could be
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
29 useful in cases where speed, rather than precision is paramount.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
30
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
31 The narrow-phase system can be assigned handler objects to run after
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
32 collision detection. These can perform tasks like handling collision response
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
33 or dispatching collision events to application handlers.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
34
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
35 Note that broad-phase systems can return false positives, though they should
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
36 never return false negatives. Do not assume that all pairs returned by a
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
37 broad-phase system are actually in collision.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
38 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
39
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
40 __version__ = '$Id$'
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
41
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
42 from grease.geometry import Vec2d
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
43 from bisect import bisect_right
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
44
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
45
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
46 class Pair(tuple):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
47 """Pair of entities in collision. This is an ordered sequence of two
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
48 entities, that compares and hashes unordered.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
49
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
50 Also stores additional collision point and normal vectors
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
51 for each entity.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
52
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
53 Sets of ``Pair`` objects are exposed in the ``collision_pairs``
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
54 attribute of collision systems to indicate the entity pairs in
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
55 collision.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
56 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
57 info = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
58 """A sequence of (entity, collision point, collision normal)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
59 for each entity in the pair
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
60 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
61
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
62 def __new__(cls, entity1, entity2, point=None, normal=None):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
63 pair = tuple.__new__(cls, (entity1, entity2))
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
64 return pair
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
65
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
66 def __hash__(self):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
67 return hash(self[0]) ^ hash(self[1])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
68
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
69 def __eq__(self, other):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
70 other = tuple(other)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
71 return tuple(self) == other or (self[1], self[0]) == other
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
72
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
73 def __repr__(self):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
74 return '%s%r' % (self.__class__.__name__, tuple(self))
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
75
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
76 def set_point_normal(self, point0, normal0, point1, normal1):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
77 """Set the collision point and normal for both entities"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
78 self.info = (
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
79 (self[0], point0, normal0),
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
80 (self[1], point1, normal1),
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
81 )
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
82
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
83
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
84 class BroadSweepAndPrune(object):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
85 """2D Broad-phase sweep and prune bounding box collision detector
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
86
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
87 This algorithm is efficient for collision detection between many
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
88 moving bodies. It has linear algorithmic complexity and takes
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
89 advantage of temporal coherence between frames. It also does
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
90 not suffer from bad worst-case performance (like RDC can).
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
91 Unlike spacial hashing, it does not need to be optimized for
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
92 specific space and body sizes.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
93
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
94 Other algorithms may be more efficient for collision detection with
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
95 stationary bodies, bodies that are always evenly distributed, or ad-hoc
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
96 queries.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
97
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
98 :param collision_component: Name of the collision component used by this
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
99 system, defaults to 'collision'. This component supplies each
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
100 entities' aabb and collision masks.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
101 :type collision_component: str
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
102 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
103 world = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
104 """|World| object this system belongs to"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
105
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
106 collision_component = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
107 """Name of world's collision component used by this system"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
108
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
109 LEFT_ATTR = "left"
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
110 RIGHT_ATTR = "right"
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
111 TOP_ATTR = "top"
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
112 BOTTOM_ATTR = "bottom"
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
113
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
114 def __init__(self, collision_component='collision'):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
115 self.collision_component = collision_component
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
116 self._by_x = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
117 self._by_y = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
118 self._collision_pairs = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
119
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
120 def set_world(self, world):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
121 """Bind the system to a world"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
122 self.world = world
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
123
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
124 def step(self, dt):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
125 """Update the system for this time step, updates and sorts the
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
126 axis arrays.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
127 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
128 component = getattr(self.world.components, self.collision_component)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
129 LEFT = self.LEFT_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
130 RIGHT = self.RIGHT_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
131 TOP = self.TOP_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
132 BOTTOM = self.BOTTOM_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
133 if self._by_x is None:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
134 # Build axis lists from scratch
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
135 # Note we cache the box positions here
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
136 # so that we can perform hit tests efficiently
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
137 # it also isolates us from changes made to the
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
138 # box positions after we run
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
139 by_x = self._by_x = []
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
140 append_x = by_x.append
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
141 by_y = self._by_y = []
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
142 append_y = by_y.append
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
143 for data in component.itervalues():
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
144 append_x([data.aabb.left, LEFT, data])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
145 append_x([data.aabb.right, RIGHT, data])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
146 append_y([data.aabb.bottom, BOTTOM, data])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
147 append_y([data.aabb.top, TOP, data])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
148 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
149 by_x = self._by_x
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
150 by_y = self._by_y
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
151 removed = []
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
152 for entry in by_x:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
153 entry[0] = getattr(entry[2].aabb, entry[1])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
154 for entry in by_y:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
155 entry[0] = getattr(entry[2].aabb, entry[1])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
156 # Removing entities is inefficient, but expected to be rare
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
157 if component.deleted_entities:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
158 deleted_entities = component.deleted_entities
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
159 deleted_x = []
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
160 deleted_y = []
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
161 for i, (_, _, data) in enumerate(by_x):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
162 if data.entity in deleted_entities:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
163 deleted_x.append(i)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
164 deleted_x.reverse()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
165 for i in deleted_x:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
166 del by_x[i]
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
167 for i, (_, _, data) in enumerate(by_y):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
168 if data.entity in deleted_entities:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
169 deleted_y.append(i)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
170 deleted_y.reverse()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
171 for i in deleted_y:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
172 del by_y[i]
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
173 # Tack on new entities
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
174 for entity in component.new_entities:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
175 data = component[entity]
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
176 by_x.append([data.aabb.left, LEFT, data])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
177 by_x.append([data.aabb.right, RIGHT, data])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
178 by_y.append([data.aabb.bottom, BOTTOM, data])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
179 by_y.append([data.aabb.top, TOP, data])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
180
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
181 # Tim-sort is highly efficient with mostly sorted lists.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
182 # Because positions tend to change little each frame
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
183 # we take advantage of this here. Obviously things are
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
184 # less efficient with very fast moving, or teleporting entities
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
185 by_x.sort()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
186 by_y.sort()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
187 self._collision_pairs = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
188
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
189 @property
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
190 def collision_pairs(self):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
191 """Set of candidate collision pairs for this timestep"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
192 if self._collision_pairs is None:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
193 if self._by_x is None:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
194 # Axis arrays not ready
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
195 return set()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
196
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
197 LEFT = self.LEFT_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
198 RIGHT = self.RIGHT_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
199 TOP = self.TOP_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
200 BOTTOM = self.BOTTOM_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
201 # Build candidates overlapping along the x-axis
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
202 component = getattr(self.world.components, self.collision_component)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
203 xoverlaps = set()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
204 add_xoverlap = xoverlaps.add
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
205 discard_xoverlap = xoverlaps.discard
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
206 open = {}
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
207 for _, side, data in self._by_x:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
208 if side is LEFT:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
209 for open_entity, (from_mask, into_mask) in open.iteritems():
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
210 if data.from_mask & into_mask or from_mask & data.into_mask:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
211 add_xoverlap(Pair(data.entity, open_entity))
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
212 open[data.entity] = (data.from_mask, data.into_mask)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
213 elif side is RIGHT:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
214 del open[data.entity]
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
215
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
216 if len(xoverlaps) <= 10 and len(xoverlaps)*4 < len(self._by_y):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
217 # few candidates were found, so just scan the x overlap candidates
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
218 # along y. This requires an additional sort, but it should
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
219 # be cheaper than scanning everyone and its simpler
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
220 # than a separate brute-force check
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
221 entities = set([entity for entity, _ in xoverlaps]
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
222 + [entity for _, entity in xoverlaps])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
223 by_y = []
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
224 for entity in entities:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
225 data = component[entity]
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
226 # We can use tuples here, which are cheaper to create
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
227 by_y.append((data.aabb.bottom, BOTTOM, data))
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
228 by_y.append((data.aabb.top, TOP, data))
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
229 by_y.sort()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
230 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
231 by_y = self._by_y
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
232
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
233 # Now check the candidates along the y-axis
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
234 open = set()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
235 add_open = open.add
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
236 discard_open = open.discard
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
237 self._collision_pairs = set()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
238 add_pair = self._collision_pairs.add
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
239 for _, side, data in by_y:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
240 if side is BOTTOM:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
241 for open_entity in open:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
242 pair = Pair(data.entity, open_entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
243 if pair in xoverlaps:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
244 discard_xoverlap(pair)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
245 add_pair(pair)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
246 if not xoverlaps:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
247 # No more candidates, bail
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
248 return self._collision_pairs
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
249 add_open(data.entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
250 elif side is TOP:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
251 discard_open(data.entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
252 return self._collision_pairs
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
253
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
254 def query_point(self, x_or_point, y=None, from_mask=0xffffffff):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
255 """Hit test at the point specified.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
256
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
257 :param x_or_point: x coordinate (float) or sequence of (x, y) floats.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
258
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
259 :param y: y coordinate (float) if x is not a sequence
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
260
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
261 :param from_mask: Bit mask used to filter query results. This value
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
262 is bit ANDed with candidate entities' ``collision.into_mask``.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
263 If the result is non-zero, then it is considered a hit. By
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
264 default all entities colliding with the input point are
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
265 returned.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
266
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
267 :return: A set of entities where the point is inside their bounding
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
268 boxes as of the last time step.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
269 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
270 if self._by_x is None:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
271 # Axis arrays not ready
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
272 return set()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
273 if y is None:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
274 x, y = x_or_point
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
275 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
276 x = x_or_point
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
277 LEFT = self.LEFT_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
278 RIGHT = self.RIGHT_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
279 TOP = self.TOP_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
280 BOTTOM = self.BOTTOM_ATTR
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
281 x_index = bisect_right(self._by_x, [x])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
282 x_hits = set()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
283 add_x_hit = x_hits.add
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
284 discard_x_hit = x_hits.discard
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
285 if x_index <= len(self._by_x) // 2:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
286 # closer to the left, scan from left to right
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
287 while (x == self._by_x[x_index][0]
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
288 and self._by_x[x_index][1] is LEFT
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
289 and x_index < len(self._by_x)):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
290 # Ensure we hit on exact left edge matches
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
291 x_index += 1
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
292 for _, side, data in self._by_x[:x_index]:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
293 if side is LEFT and from_mask & data.into_mask:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
294 add_x_hit(data.entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
295 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
296 discard_x_hit(data.entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
297 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
298 # closer to the right
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
299 for _, side, data in reversed(self._by_x[x_index:]):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
300 if side is RIGHT and from_mask & data.into_mask:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
301 add_x_hit(data.entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
302 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
303 discard_x_hit(data.entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
304 if not x_hits:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
305 return x_hits
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
306
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
307 y_index = bisect_right(self._by_y, [y])
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
308 y_hits = set()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
309 add_y_hit = y_hits.add
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
310 discard_y_hit = y_hits.discard
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
311 if y_index <= len(self._by_y) // 2:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
312 # closer to the bottom
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
313 while (y == self._by_y[y_index][0]
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
314 and self._by_y[y_index][1] is BOTTOM
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
315 and y_index < len(self._by_y)):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
316 # Ensure we hit on exact bottom edge matches
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
317 y_index += 1
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
318 for _, side, data in self._by_y[:y_index]:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
319 if side is BOTTOM:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
320 add_y_hit(data.entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
321 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
322 discard_y_hit(data.entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
323 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
324 # closer to the top
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
325 for _, side, data in reversed(self._by_y[y_index:]):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
326 if side is TOP:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
327 add_y_hit(data.entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
328 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
329 discard_y_hit(data.entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
330 if y_hits:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
331 return x_hits & y_hits
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
332 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
333 return y_hits
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
334
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
335
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
336 class Circular(object):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
337 """Basic narrow-phase collision detector which treats all entities as
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
338 circles with their radius defined in the collision component.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
339
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
340 :param handlers: A sequence of collision handler functions that are invoked
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
341 after collision detection.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
342 :type handlers: sequence of functions
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
343
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
344 :param collision_component: Name of collision component for this system,
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
345 defaults to 'collision'. This supplies each entity's collision
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
346 radius and masks.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
347 :type collision_component: str
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
348
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
349 :param position_component: Name of position component for this system,
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
350 defaults to 'position'. This supplies each entity's position.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
351 :type position_component: str
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
352
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
353 :param update_aabbs: If True (the default), then the entities'
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
354 `collision.aabb` fields will be updated using their position
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
355 and collision radius before invoking the broad phase system.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
356 Set this False if another system updates the aabbs.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
357 :type update_aabbs: bool
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
358
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
359 :param broad_phase: A broad-phase collision system to use as a source
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
360 for collision pairs. If not specified, a :class:`BroadSweepAndPrune`
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
361 system will be created automatically.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
362 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
363 world = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
364 """|World| object this system belongs to"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
365
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
366 position_component = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
367 """Name of world's position component used by this system"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
368
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
369 collision_component = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
370 """Name of world's collision component used by this system"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
371
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
372 update_aabbs = True
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
373 """Flag to indicate whether the system updates the entities' `collision.aabb`
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
374 field before invoking the broad phase collision system
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
375 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
376
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
377 handlers = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
378 """A sequence of collision handler functions invoke after collision
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
379 detection
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
380 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
381
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
382 broad_phase = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
383 """Broad phase collision system used as a source for collision pairs"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
384
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
385 def __init__(self, handlers=(), position_component='position',
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
386 collision_component='collision', update_aabbs=True, broad_phase=None):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
387 self.handlers = tuple(handlers)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
388 if broad_phase is None:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
389 broad_phase = BroadSweepAndPrune(collision_component)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
390 self.collision_component = collision_component
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
391 self.position_component = position_component
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
392 self.update_aabbs = bool(update_aabbs)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
393 self.broad_phase = broad_phase
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
394 self._collision_pairs = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
395
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
396 def set_world(self, world):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
397 """Bind the system to a world"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
398 self.world = world
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
399 self.broad_phase.set_world(world)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
400 for handler in self.handlers:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
401 if hasattr(handler, 'set_world'):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
402 handler.set_world(world)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
403
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
404 def step(self, dt):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
405 """Update the collision system for this time step and invoke
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
406 the handlers
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
407 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
408 if self.update_aabbs:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
409 for position, collision in self.world.components.join(
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
410 self.position_component, self.collision_component):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
411 aabb = collision.aabb
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
412 x, y = position.position
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
413 radius = collision.radius
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
414 aabb.left = x - radius
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
415 aabb.right = x + radius
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
416 aabb.bottom = y - radius
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
417 aabb.top = y + radius
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
418 self.broad_phase.step(dt)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
419 self._collision_pairs = None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
420 for handler in self.handlers:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
421 handler(self)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
422
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
423 @property
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
424 def collision_pairs(self):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
425 """The set of entity pairs in collision in this timestep"""
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
426 if self._collision_pairs is None:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
427 position = getattr(self.world.components, self.position_component)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
428 collision = getattr(self.world.components, self.collision_component)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
429 pairs = self._collision_pairs = set()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
430 for pair in self.broad_phase.collision_pairs:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
431 entity1, entity2 = pair
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
432 position1 = position[entity1].position
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
433 position2 = position[entity2].position
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
434 radius1 = collision[entity1].radius
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
435 radius2 = collision[entity2].radius
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
436 separation = position2 - position1
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
437 if separation.get_length_sqrd() <= (radius1 + radius2)**2:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
438 normal = separation.normalized()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
439 pair.set_point_normal(
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
440 normal * radius1 + position1, normal,
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
441 normal * -radius2 + position2, -normal)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
442 pairs.add(pair)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
443 return self._collision_pairs
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
444
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
445 def query_point(self, x_or_point, y=None, from_mask=0xffffffff):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
446 """Hit test at the point specified.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
447
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
448 :param x_or_point: x coordinate (float) or sequence of (x, y) floats.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
449
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
450 :param y: y coordinate (float) if x is not a sequence
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
451
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
452 :param from_mask: Bit mask used to filter query results. This value
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
453 is bit ANDed with candidate entities' ``collision.into_mask``.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
454 If the result is non-zero, then it is considered a hit. By
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
455 default all entities colliding with the input point are
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
456 returned.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
457
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
458 :return: A set of entities where the point is inside their collision
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
459 radii as of the last time step.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
460
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
461 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
462 if y is None:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
463 point = Vec2d(x_or_point)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
464 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
465 point = Vec2d(x_or_point, y)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
466 hits = set()
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
467 position = getattr(self.world.components, self.position_component)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
468 collision = getattr(self.world.components, self.collision_component)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
469 for entity in self.broad_phase.query_point(x_or_point, y, from_mask):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
470 separation = point - position[entity].position
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
471 if separation.get_length_sqrd() <= collision[entity].radius**2:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
472 hits.add(entity)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
473 return hits
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
474
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
475
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
476 def dispatch_events(collision_system):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
477 """Collision handler that dispatches `on_collide()` events to entities
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
478 marked for collision by the specified collision system. The `on_collide()`
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
479 event handler methods are defined by the application on the desired entity
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
480 classes. These methods should have the following signature::
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
481
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
482 def on_collide(self, other_entity, collision_point, collision_normal):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
483 '''Handle A collision between this entity and `other_entity`
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
484
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
485 - other_entity (Entity): The other entity in collision with
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
486 `self`
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
487
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
488 - collision_point (Vec2d): The point on this entity (`self`)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
489 where the collision occurred. Note this may be `None` for
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
490 some collision systems that do not report it.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
491
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
492 - collision_normal (Vec2d): The normal vector at the point of
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
493 collision. As with `collision_point`, this may be None for
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
494 some collision systems.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
495 '''
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
496
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
497 Note the arguments to `on_collide()` are always passed positionally, so you
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
498 can use different argument names than above if desired.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
499
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
500 If a pair of entities are in collision, then the event will be dispatched
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
501 to both objects in arbitrary order if all of their collision masks align.
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
502 """
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
503 collision = getattr(collision_system.world.components,
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
504 collision_system.collision_component)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
505 for pair in collision_system.collision_pairs:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
506 entity1, entity2 = pair
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
507 if pair.info is not None:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
508 args1, args2 = pair.info
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
509 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
510 args1 = entity1, None, None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
511 args2 = entity2, None, None
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
512 try:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
513 on_collide = entity1.on_collide
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
514 masks_align = collision[entity2].from_mask & collision[entity1].into_mask
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
515 except (AttributeError, KeyError):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
516 pass
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
517 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
518 if masks_align:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
519 on_collide(*args2)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
520 try:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
521 on_collide = entity2.on_collide
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
522 masks_align = collision[entity1].from_mask & collision[entity2].into_mask
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
523 except (AttributeError, KeyError):
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
524 pass
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
525 else:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
526 if masks_align:
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
527 on_collide(*args1)
bc88f7d5ca8b Added base files for grease
KarstenBock@gmx.net
parents:
diff changeset
528