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.cpSweep1D;
23
24 import core.stdc.stdlib : qsort;
25
26 import dchip.chipmunk;
27 import dchip.chipmunk_types;
28 import dchip.cpBB;
29 import dchip.cpSpatialIndex;
30 import dchip.util;
31
32 struct Bounds
33 {
34 cpFloat min = 0, max = 0;
35 }
36
37 struct TableCell
38 {
39 void* obj;
40 Bounds bounds;
41 }
42
43 struct cpSweep1D
44 {
45 cpSpatialIndex spatialIndex;
46
47 int num;
48 int max;
49 TableCell* table;
50 }
51
52 cpBool BoundsOverlap(Bounds a, Bounds b)
53 {
54 return (a.min <= b.max && b.min <= a.max);
55 }
56
57 Bounds BBToBounds(cpSweep1D* sweep, cpBB bb)
58 {
59 Bounds bounds = { bb.l, bb.r };
60 return bounds;
61 }
62
63 TableCell MakeTableCell(cpSweep1D* sweep, void* obj)
64 {
65 TableCell cell = { obj, BBToBounds(sweep, sweep.spatialIndex.bbfunc(obj)) };
66 return cell;
67 }
68
69 cpSweep1D* cpSweep1DAlloc()
70 {
71 return cast(cpSweep1D*)cpcalloc(1, cpSweep1D.sizeof);
72 }
73
74 void ResizeTable(cpSweep1D* sweep, int size)
75 {
76 sweep.max = size;
77 sweep.table = cast(TableCell*)cprealloc(sweep.table, size * TableCell.sizeof);
78 }
79
80 cpSpatialIndex* cpSweep1DInit(cpSweep1D* sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex* staticIndex)
81 {
82 cpSpatialIndexInit(cast(cpSpatialIndex*)sweep, Klass(), bbfunc, staticIndex);
83
84 sweep.num = 0;
85 ResizeTable(sweep, 32);
86
87 return cast(cpSpatialIndex*)sweep;
88 }
89
90 cpSpatialIndex* cpSweep1DNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex* staticIndex)
91 {
92 return cpSweep1DInit(cpSweep1DAlloc(), bbfunc, staticIndex);
93 }
94
95 void cpSweep1DDestroy(cpSweep1D* sweep)
96 {
97 cpfree(sweep.table);
98 sweep.table = null;
99 }
100
101 //MARK: Misc
102
103 int cpSweep1DCount(cpSweep1D* sweep)
104 {
105 return sweep.num;
106 }
107
108 void cpSweep1DEach(cpSweep1D* sweep, cpSpatialIndexIteratorFunc func, void* data)
109 {
110 TableCell* table = sweep.table;
111
112 for (int i = 0, count = sweep.num; i < count; i++)
113 func(table[i].obj, data);
114 }
115
116 int cpSweep1DContains(cpSweep1D* sweep, void* obj, cpHashValue hashid)
117 {
118 TableCell* table = sweep.table;
119
120 for (int i = 0, count = sweep.num; i < count; i++)
121 {
122 if (table[i].obj == obj)
123 return cpTrue;
124 }
125
126 return cpFalse;
127 }
128
129 //MARK: Basic Operations
130
131 void cpSweep1DInsert(cpSweep1D* sweep, void* obj, cpHashValue hashid)
132 {
133 if (sweep.num == sweep.max)
134 ResizeTable(sweep, sweep.max * 2);
135
136 sweep.table[sweep.num] = MakeTableCell(sweep, obj);
137 sweep.num++;
138 }
139
140 void cpSweep1DRemove(cpSweep1D* sweep, void* obj, cpHashValue hashid)
141 {
142 TableCell* table = sweep.table;
143
144 for (int i = 0, count = sweep.num; i < count; i++)
145 {
146 if (table[i].obj == obj)
147 {
148 int num = --sweep.num;
149
150 table[i] = table[num];
151 table[num].obj = null;
152
153 return;
154 }
155 }
156 }
157
158 //MARK: Reindexing Functions
159
160 void cpSweep1DReindexObject(cpSweep1D* sweep, void* obj, cpHashValue hashid)
161 {
162 // Nothing to do here
163 }
164
165 void cpSweep1DReindex(cpSweep1D* sweep)
166 {
167 // Nothing to do here
168 // Could perform a sort, but queries are not accelerated anyway.
169 }
170
171 //MARK: Query Functions
172
173 void cpSweep1DQuery(cpSweep1D* sweep, void* obj, cpBB bb, cpSpatialIndexQueryFunc func, void* data)
174 {
175 // Implementing binary search here would allow you to find an upper limit
176 // but not a lower limit. Probably not worth the hassle.
177
178 Bounds bounds = BBToBounds(sweep, bb);
179
180 TableCell* table = sweep.table;
181
182 for (int i = 0, count = sweep.num; i < count; i++)
183 {
184 TableCell cell = table[i];
185
186 if (BoundsOverlap(bounds, cell.bounds) && obj != cell.obj)
187 func(obj, cell.obj, 0, data);
188 }
189 }
190
191 void cpSweep1DSegmentQuery(cpSweep1D* sweep, void* obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void* data)
192 {
193 cpBB bb = cpBBExpand(cpBBNew(a.x, a.y, a.x, a.y), b);
194 Bounds bounds = BBToBounds(sweep, bb);
195
196 TableCell* table = sweep.table;
197
198 for (int i = 0, count = sweep.num; i < count; i++)
199 {
200 TableCell cell = table[i];
201
202 if (BoundsOverlap(bounds, cell.bounds))
203 func(obj, cell.obj, data);
204 }
205 }
206
207 //MARK: Reindex/Query
208
209 int TableSort(TableCell* a, TableCell* b)
210 {
211 return (a.bounds.min < b.bounds.min ? -1 : (a.bounds.min > b.bounds.min ? 1 : 0));
212 }
213
214 void cpSweep1DReindexQuery(cpSweep1D* sweep, cpSpatialIndexQueryFunc func, void* data)
215 {
216 TableCell* table = sweep.table;
217 int count = sweep.num;
218
219 // Update bounds and sort
220 for (int i = 0; i < count; i++)
221 table[i] = MakeTableCell(sweep, table[i].obj);
222
223 alias extern(C) int function(const void*, const void*) TableSortFunc;
224
225 qsort(table, count, TableCell.sizeof, safeCast!TableSortFunc(&TableSort)); // TODO use insertion sort instead
226
227 for (int i = 0; i < count; i++)
228 {
229 TableCell cell = table[i];
230 cpFloat max = cell.bounds.max;
231
232 for (int j = i + 1; table[j].bounds.min < max && j < count; j++)
233 {
234 func(cell.obj, table[j].obj, 0, data);
235 }
236 }
237
238 // Reindex query is also responsible for colliding against the static index.
239 // Fortunately there is a helper function for that.
240 cpSpatialIndexCollideStatic(cast(cpSpatialIndex*)sweep, sweep.spatialIndex.staticIndex, func, data);
241 }
242
243 __gshared cpSpatialIndexClass klass;
244
245 void _initModuleCtor_cpSweep1D()
246 {
247 klass = cpSpatialIndexClass(
248 cast(cpSpatialIndexDestroyImpl)&cpSweep1DDestroy,
249
250 cast(cpSpatialIndexCountImpl)&cpSweep1DCount,
251 cast(cpSpatialIndexEachImpl)&cpSweep1DEach,
252 cast(cpSpatialIndexContainsImpl)&cpSweep1DContains,
253
254 cast(cpSpatialIndexInsertImpl)&cpSweep1DInsert,
255 cast(cpSpatialIndexRemoveImpl)&cpSweep1DRemove,
256
257 cast(cpSpatialIndexReindexImpl)&cpSweep1DReindex,
258 cast(cpSpatialIndexReindexObjectImpl)&cpSweep1DReindexObject,
259 cast(cpSpatialIndexReindexQueryImpl)&cpSweep1DReindexQuery,
260
261 cast(cpSpatialIndexQueryImpl)&cpSweep1DQuery,
262 cast(cpSpatialIndexSegmentQueryImpl)&cpSweep1DSegmentQuery,
263 );
264 }
265
266 cpSpatialIndexClass* Klass()
267 {
268 return &klass;
269 }