1 /*
2  * Copyright (c) 2007-2013 Scott Lembcke and Howling Moon Software
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 module dchip.cpSpatialIndex;
23 
24 import dchip.cpBBTree;
25 import dchip.chipmunk;
26 import dchip.chipmunk_types;
27 import dchip.cpBB;
28 import dchip.cpSpaceHash;
29 import dchip.cpSweep1D;
30 import dchip.util;
31 
32 /**
33     Spatial indexes are data structures that are used to accelerate collision detection
34     and spatial queries. Chipmunk provides a number of spatial index algorithms to pick from
35     and they are programmed in a generic way so that you can use them for holding more than
36     just cpShape structs.
37 
38     It works by using void pointers to the objects you add and using a callback to ask your code
39     for bounding boxes when it needs them. Several types of queries can be performed an index as well
40     as reindexing and full collision information. All communication to the spatial indexes is performed
41     through callback functions.
42 
43     Spatial indexes should be treated as opaque structs.
44     This meanns you shouldn't be reading any of the struct fields.
45 */
46 
47 /// Spatial index bounding box callback function type.
48 /// The spatial index calls this function and passes you a pointer to an object you added
49 /// when it needs to get the bounding box associated with that object.
50 alias cpSpatialIndexBBFunc = cpBB function(void* obj);
51 
52 /// Spatial index/object iterator callback function type.
53 alias cpSpatialIndexIteratorFunc = void function(void* obj, void* data);
54 
55 /// Spatial query callback function type.
56 alias cpSpatialIndexQueryFunc = cpCollisionID function(void* obj1, void* obj2, cpCollisionID id, void* data);
57 
58 /// Spatial segment query callback function type.
59 alias cpSpatialIndexSegmentQueryFunc = cpFloat function(void* obj1, void* obj2, void* data);
60 
61 ///
62 package struct cpSpatialIndex
63 {
64     cpSpatialIndexClass* klass;
65     cpSpatialIndexBBFunc bbfunc;
66     cpSpatialIndex* staticIndex;
67     cpSpatialIndex* dynamicIndex;
68 }
69 
70 /// Bounding box tree velocity callback function.
71 /// This function should return an estimate for the object's velocity.
72 alias cpBBTreeVelocityFunc = cpVect function(void* obj);
73 
74 alias cpSpatialIndexDestroyImpl = void function(cpSpatialIndex* index);
75 
76 alias cpSpatialIndexCountImpl = int function(cpSpatialIndex* index);
77 alias cpSpatialIndexEachImpl = void function(cpSpatialIndex* index, cpSpatialIndexIteratorFunc func, void* data);
78 
79 alias cpSpatialIndexContainsImpl = cpBool function(cpSpatialIndex* index, void* obj, cpHashValue hashid);
80 alias cpSpatialIndexInsertImpl = void function(cpSpatialIndex* index, void* obj, cpHashValue hashid);
81 alias cpSpatialIndexRemoveImpl = void function(cpSpatialIndex* index, void* obj, cpHashValue hashid);
82 
83 alias cpSpatialIndexReindexImpl = void function(cpSpatialIndex* index);
84 alias cpSpatialIndexReindexObjectImpl = void function(cpSpatialIndex* index, void* obj, cpHashValue hashid);
85 alias cpSpatialIndexReindexQueryImpl = void function(cpSpatialIndex* index, cpSpatialIndexQueryFunc func, void* data);
86 
87 alias cpSpatialIndexQueryImpl = void function(cpSpatialIndex* index, void* obj, cpBB bb, cpSpatialIndexQueryFunc func, void* data);
88 alias cpSpatialIndexSegmentQueryImpl = void function(cpSpatialIndex* index, void* obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void* data);
89 
90 struct cpSpatialIndexClass
91 {
92     cpSpatialIndexDestroyImpl destroy;
93 
94     cpSpatialIndexCountImpl count;
95     cpSpatialIndexEachImpl each;
96 
97     cpSpatialIndexContainsImpl contains;
98     cpSpatialIndexInsertImpl insert;
99     cpSpatialIndexRemoveImpl remove;
100 
101     cpSpatialIndexReindexImpl reindex;
102     cpSpatialIndexReindexObjectImpl reindexObject;
103     cpSpatialIndexReindexQueryImpl reindexQuery;
104 
105     cpSpatialIndexQueryImpl query;
106     cpSpatialIndexSegmentQueryImpl segmentQuery;
107 }
108 
109 struct dynamicToStaticContext
110 {
111     cpSpatialIndexBBFunc bbfunc;
112     cpSpatialIndex* staticIndex;
113     cpSpatialIndexQueryFunc queryFunc;
114     void* data;
115 }
116 
117 /// Destroy a spatial index.
118 void cpSpatialIndexDestroy(cpSpatialIndex* index)
119 {
120     if (index.klass)
121         index.klass.destroy(index);
122 }
123 
124 /// Get the number of objects in the spatial index.
125 int cpSpatialIndexCount(cpSpatialIndex* index)
126 {
127     return index.klass.count(index);
128 }
129 
130 /// Iterate the objects in the spatial index. @c func will be called once for each object.
131 void cpSpatialIndexEach(cpSpatialIndex* index, cpSpatialIndexIteratorFunc func, void* data)
132 {
133     index.klass.each(index, func, data);
134 }
135 
136 /// Returns true if the spatial index contains the given object.
137 /// Most spatial indexes use hashed storage, so you must provide a hash value too.
138 cpBool cpSpatialIndexContains(cpSpatialIndex* index, void* obj, cpHashValue hashid)
139 {
140     return index.klass.contains(index, obj, hashid);
141 }
142 
143 /// Add an object to a spatial index.
144 /// Most spatial indexes use hashed storage, so you must provide a hash value too.
145 void cpSpatialIndexInsert(cpSpatialIndex* index, void* obj, cpHashValue hashid)
146 {
147     index.klass.insert(index, obj, hashid);
148 }
149 
150 /// Remove an object from a spatial index.
151 /// Most spatial indexes use hashed storage, so you must provide a hash value too.
152 void cpSpatialIndexRemove(cpSpatialIndex* index, void* obj, cpHashValue hashid)
153 {
154     index.klass.remove(index, obj, hashid);
155 }
156 
157 /// Perform a full reindex of a spatial index.
158 void cpSpatialIndexReindex(cpSpatialIndex* index)
159 {
160     index.klass.reindex(index);
161 }
162 
163 /// Reindex a single object in the spatial index.
164 void cpSpatialIndexReindexObject(cpSpatialIndex* index, void* obj, cpHashValue hashid)
165 {
166     index.klass.reindexObject(index, obj, hashid);
167 }
168 
169 /// Perform a rectangle query against the spatial index, calling @c func for each potential match.
170 void cpSpatialIndexQuery(cpSpatialIndex* index, void* obj, cpBB bb, cpSpatialIndexQueryFunc func, void* data)
171 {
172     index.klass.query(index, obj, bb, func, data);
173 }
174 
175 /// Perform a segment query against the spatial index, calling @c func for each potential match.
176 void cpSpatialIndexSegmentQuery(cpSpatialIndex* index, void* obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void* data)
177 {
178     index.klass.segmentQuery(index, obj, a, b, t_exit, func, data);
179 }
180 
181 /// Simultaneously reindex and find all colliding objects.
182 /// @c func will be called once for each potentially overlapping pair of objects found.
183 /// If the spatial index was initialized with a static index, it will collide it's objects against that as well.
184 void cpSpatialIndexReindexQuery(cpSpatialIndex* index, cpSpatialIndexQueryFunc func, void* data)
185 {
186     index.klass.reindexQuery(index, func, data);
187 }
188 
189 /// Destroy and free a spatial index.
190 void cpSpatialIndexFree(cpSpatialIndex* index)
191 {
192     if (index)
193     {
194         cpSpatialIndexDestroy(index);
195         cpfree(index);
196     }
197 }
198 
199 cpSpatialIndex* cpSpatialIndexInit(cpSpatialIndex* index, cpSpatialIndexClass* klass, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex* staticIndex)
200 {
201     index.klass       = klass;
202     index.bbfunc      = bbfunc;
203     index.staticIndex = staticIndex;
204 
205     if (staticIndex)
206     {
207         cpAssertHard(!staticIndex.dynamicIndex, "This static index is already associated with a dynamic index.");
208         staticIndex.dynamicIndex = index;
209     }
210 
211     return index;
212 }
213 
214 void dynamicToStaticIter(void* obj, dynamicToStaticContext* context)
215 {
216     cpSpatialIndexQuery(context.staticIndex, obj, context.bbfunc(obj), context.queryFunc, context.data);
217 }
218 
219 void cpSpatialIndexCollideStatic(cpSpatialIndex* dynamicIndex, cpSpatialIndex* staticIndex, cpSpatialIndexQueryFunc func, void* data)
220 {
221     if (staticIndex && cpSpatialIndexCount(staticIndex) > 0)
222     {
223         dynamicToStaticContext context = { dynamicIndex.bbfunc, staticIndex, func, data };
224         cpSpatialIndexEach(dynamicIndex, safeCast!cpSpatialIndexIteratorFunc(&dynamicToStaticIter), &context);
225     }
226 }