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(scope const void*, scope 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 }