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.cpSpaceQuery; 23 24 import dchip.chipmunk_private; 25 import dchip.chipmunk_types; 26 import dchip.cpArbiter; 27 import dchip.cpBB; 28 import dchip.cpCollision; 29 import dchip.cpBody; 30 import dchip.cpSpace; 31 import dchip.cpSpaceStep; 32 import dchip.cpSpatialIndex; 33 import dchip.cpShape; 34 import dchip.cpVect; 35 import dchip.util; 36 37 struct PointQueryContext 38 { 39 cpVect point; 40 cpLayers layers; 41 cpGroup group; 42 cpSpacePointQueryFunc func; 43 void* data; 44 } 45 46 cpCollisionID PointQuery(PointQueryContext* context, cpShape* shape, cpCollisionID id, void* data) 47 { 48 if ( 49 !(shape.group && context.group == shape.group) && (context.layers & shape.layers) && 50 cpShapePointQuery(shape, context.point) 51 ) 52 { 53 context.func(shape, context.data); 54 } 55 56 return id; 57 } 58 59 void cpSpacePointQuery(cpSpace* space, cpVect point, cpLayers layers, cpGroup group, cpSpacePointQueryFunc func, void* data) 60 { 61 PointQueryContext context = { point, layers, group, func, data }; 62 cpBB bb = cpBBNewForCircle(point, 0.0f); 63 64 cpSpaceLock(space); 65 { 66 cpSpatialIndexQuery(space.activeShapes, &context, bb, safeCast!cpSpatialIndexQueryFunc(&PointQuery), data); 67 cpSpatialIndexQuery(space.staticShapes, &context, bb, safeCast!cpSpatialIndexQueryFunc(&PointQuery), data); 68 } 69 cpSpaceUnlock(space, cpTrue); 70 } 71 72 void PointQueryFirst(cpShape* shape, cpShape** outShape) 73 { 74 if (!shape.sensor) 75 *outShape = shape; 76 } 77 78 cpShape* cpSpacePointQueryFirst(cpSpace* space, cpVect point, cpLayers layers, cpGroup group) 79 { 80 cpShape* shape = null; 81 cpSpacePointQuery(space, point, layers, group, safeCast!cpSpacePointQueryFunc(&PointQueryFirst), &shape); 82 83 return shape; 84 } 85 86 //MARK: Nearest Point Query Functions 87 88 struct NearestPointQueryContext 89 { 90 cpVect point; 91 cpFloat maxDistance = 0; 92 cpLayers layers; 93 cpGroup group; 94 cpSpaceNearestPointQueryFunc func; 95 } 96 97 cpCollisionID NearestPointQuery(NearestPointQueryContext* context, cpShape* shape, cpCollisionID id, void* data) 98 { 99 if ( 100 !(shape.group && context.group == shape.group) && (context.layers & shape.layers) 101 ) 102 { 103 cpNearestPointQueryInfo info; 104 cpShapeNearestPointQuery(shape, context.point, &info); 105 106 if (info.shape && info.d < context.maxDistance) 107 context.func(shape, info.d, info.p, data); 108 } 109 110 return id; 111 } 112 113 void cpSpaceNearestPointQuery(cpSpace* space, cpVect point, cpFloat maxDistance, cpLayers layers, cpGroup group, cpSpaceNearestPointQueryFunc func, void* data) 114 { 115 NearestPointQueryContext context = { point, maxDistance, layers, group, func }; 116 cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f)); 117 118 cpSpaceLock(space); 119 { 120 cpSpatialIndexQuery(space.activeShapes, &context, bb, cast(cpSpatialIndexQueryFunc)&NearestPointQuery, data); 121 cpSpatialIndexQuery(space.staticShapes, &context, bb, cast(cpSpatialIndexQueryFunc)&NearestPointQuery, data); 122 } 123 cpSpaceUnlock(space, cpTrue); 124 } 125 126 cpCollisionID NearestPointQueryNearest(NearestPointQueryContext* context, cpShape* shape, cpCollisionID id, cpNearestPointQueryInfo* out_) 127 { 128 if ( 129 !(shape.group && context.group == shape.group) && (context.layers & shape.layers) && !shape.sensor 130 ) 131 { 132 cpNearestPointQueryInfo info; 133 cpShapeNearestPointQuery(shape, context.point, &info); 134 135 if (info.d < out_.d) 136 (*out_) = info; 137 } 138 139 return id; 140 } 141 142 cpShape* cpSpaceNearestPointQueryNearest(cpSpace* space, cpVect point, cpFloat maxDistance, cpLayers layers, cpGroup group, cpNearestPointQueryInfo* out_) 143 { 144 cpNearestPointQueryInfo info = { null, cpvzero, maxDistance, cpvzero }; 145 146 if (out_) 147 { 148 (*out_) = info; 149 } 150 else 151 { 152 out_ = &info; 153 } 154 155 NearestPointQueryContext context = { 156 point, maxDistance, 157 layers, group, 158 null 159 }; 160 161 cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f)); 162 cpSpatialIndexQuery(space.activeShapes, &context, bb, safeCast!cpSpatialIndexQueryFunc(&NearestPointQueryNearest), out_); 163 cpSpatialIndexQuery(space.staticShapes, &context, bb, safeCast!cpSpatialIndexQueryFunc(&NearestPointQueryNearest), out_); 164 165 return out_.shape; 166 } 167 168 //MARK: Segment Query Functions 169 170 struct SegmentQueryContext 171 { 172 cpVect start, end; 173 cpLayers layers; 174 cpGroup group; 175 cpSpaceSegmentQueryFunc func; 176 } 177 178 cpFloat SegmentQuery(SegmentQueryContext* context, cpShape* shape, void* data) 179 { 180 cpSegmentQueryInfo info; 181 182 if ( 183 !(shape.group && context.group == shape.group) && (context.layers & shape.layers) && 184 cpShapeSegmentQuery(shape, context.start, context.end, &info) 185 ) 186 { 187 context.func(shape, info.t, info.n, data); 188 } 189 190 return 1.0f; 191 } 192 193 void cpSpaceSegmentQuery(cpSpace* space, cpVect start, cpVect end, cpLayers layers, cpGroup group, cpSpaceSegmentQueryFunc func, void* data) 194 { 195 SegmentQueryContext context = { 196 start, end, 197 layers, group, 198 func, 199 }; 200 201 cpSpaceLock(space); 202 { 203 cpSpatialIndexSegmentQuery(space.staticShapes, &context, start, end, 1.0f, safeCast!cpSpatialIndexSegmentQueryFunc(&SegmentQuery), data); 204 cpSpatialIndexSegmentQuery(space.activeShapes, &context, start, end, 1.0f, safeCast!cpSpatialIndexSegmentQueryFunc(&SegmentQuery), data); 205 } 206 cpSpaceUnlock(space, cpTrue); 207 } 208 209 cpFloat SegmentQueryFirst(SegmentQueryContext* context, cpShape* shape, cpSegmentQueryInfo* out_) 210 { 211 cpSegmentQueryInfo info; 212 213 if ( 214 !(shape.group && context.group == shape.group) && (context.layers & shape.layers) && 215 !shape.sensor && 216 cpShapeSegmentQuery(shape, context.start, context.end, &info) && 217 info.t < out_.t 218 ) 219 { 220 (*out_) = info; 221 } 222 223 return out_.t; 224 } 225 226 cpShape* cpSpaceSegmentQueryFirst(cpSpace* space, cpVect start, cpVect end, cpLayers layers, cpGroup group, cpSegmentQueryInfo* out_) 227 { 228 cpSegmentQueryInfo info = { null, 1.0f, cpvzero }; 229 230 if (out_) 231 { 232 (*out_) = info; 233 } 234 else 235 { 236 out_ = &info; 237 } 238 239 SegmentQueryContext context = { 240 start, end, 241 layers, group, 242 null 243 }; 244 245 cpSpatialIndexSegmentQuery(space.staticShapes, &context, start, end, 1.0f, safeCast!cpSpatialIndexSegmentQueryFunc(&SegmentQueryFirst), out_); 246 cpSpatialIndexSegmentQuery(space.activeShapes, &context, start, end, out_.t, safeCast!cpSpatialIndexSegmentQueryFunc(&SegmentQueryFirst), out_); 247 248 return out_.shape; 249 } 250 251 //MARK: BB Query Functions 252 253 struct BBQueryContext 254 { 255 cpBB bb; 256 cpLayers layers; 257 cpGroup group; 258 cpSpaceBBQueryFunc func; 259 } 260 261 cpCollisionID BBQuery(BBQueryContext* context, cpShape* shape, cpCollisionID id, void* data) 262 { 263 if ( 264 !(shape.group && context.group == shape.group) && (context.layers & shape.layers) && 265 cpBBIntersects(context.bb, shape.bb) 266 ) 267 { 268 context.func(shape, data); 269 } 270 271 return id; 272 } 273 274 void cpSpaceBBQuery(cpSpace* space, cpBB bb, cpLayers layers, cpGroup group, cpSpaceBBQueryFunc func, void* data) 275 { 276 BBQueryContext context = { bb, layers, group, func }; 277 278 cpSpaceLock(space); 279 { 280 cpSpatialIndexQuery(space.activeShapes, &context, bb, safeCast!cpSpatialIndexQueryFunc(&BBQuery), data); 281 cpSpatialIndexQuery(space.staticShapes, &context, bb, safeCast!cpSpatialIndexQueryFunc(&BBQuery), data); 282 } 283 cpSpaceUnlock(space, cpTrue); 284 } 285 286 //MARK: Shape Query Functions 287 288 struct ShapeQueryContext 289 { 290 cpSpaceShapeQueryFunc func; 291 void* data; 292 cpBool anyCollision; 293 } 294 295 // Callback from the spatial hash. 296 cpCollisionID ShapeQuery(cpShape* a, cpShape* b, cpCollisionID id, ShapeQueryContext* context) 297 { 298 // Reject any of the simple cases 299 if ( 300 (a.group && a.group == b.group) || 301 !(a.layers & b.layers) || 302 a == b 303 ) 304 return id; 305 306 cpContact[CP_MAX_CONTACTS_PER_ARBITER] contacts; 307 int numContacts = 0; 308 309 // Shape 'a' should have the lower shape type. (required by cpCollideShapes() ) 310 if (a.klass.type <= b.klass.type) 311 { 312 numContacts = cpCollideShapes(a, b, &id, contacts.ptr); 313 } 314 else 315 { 316 numContacts = cpCollideShapes(b, a, &id, contacts.ptr); 317 318 for (int i = 0; i < numContacts; i++) 319 contacts[i].n = cpvneg(contacts[i].n); 320 } 321 322 if (numContacts) 323 { 324 context.anyCollision = !(a.sensor || b.sensor); 325 326 if (context.func) 327 { 328 cpContactPointSet set; 329 set.count = numContacts; 330 331 for (int i = 0; i < set.count; i++) 332 { 333 set.points[i].point = contacts[i].p; 334 set.points[i].normal = contacts[i].n; 335 set.points[i].dist = contacts[i].dist; 336 } 337 338 context.func(b, &set, context.data); 339 } 340 } 341 342 return id; 343 } 344 345 cpBool cpSpaceShapeQuery(cpSpace* space, cpShape* shape, cpSpaceShapeQueryFunc func, void* data) 346 { 347 cpBody* body_ = shape.body_; 348 cpBB bb = (body_ ? cpShapeUpdate(shape, body_.p, body_.rot) : shape.bb); 349 ShapeQueryContext context = { func, data, cpFalse }; 350 351 cpSpaceLock(space); 352 { 353 cpSpatialIndexQuery(space.activeShapes, shape, bb, safeCast!cpSpatialIndexQueryFunc(&ShapeQuery), &context); 354 cpSpatialIndexQuery(space.staticShapes, shape, bb, safeCast!cpSpatialIndexQueryFunc(&ShapeQuery), &context); 355 } 356 cpSpaceUnlock(space, cpTrue); 357 358 return context.anyCollision; 359 }