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 }