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.cpBB; 23 24 import dchip.chipmunk_types; 25 import dchip.cpVect; 26 27 /// Chipmunk's axis-aligned 2D bounding box type along with a few handy routines. 28 29 /// Chipmunk's axis-aligned 2D bounding box type. (left, bottom, right, top) 30 struct cpBB 31 { 32 cpFloat l = 0, b = 0, r = 0, t = 0; 33 } 34 35 /// Convenience constructor for cpBB structs. 36 alias cpBBNew = cpBB; 37 38 /// Constructs a cpBB for a circle with the given position and radius. 39 cpBB cpBBNewForCircle(const cpVect p, const cpFloat r) 40 { 41 return cpBBNew(p.x - r, p.y - r, p.x + r, p.y + r); 42 } 43 44 /// Returns true if @c a and @c b intersect. 45 cpBool cpBBIntersects(const cpBB a, const cpBB b) 46 { 47 return (a.l <= b.r && b.l <= a.r && a.b <= b.t && b.b <= a.t); 48 } 49 50 /// Returns true if @c other lies completely within @c bb. 51 cpBool cpBBContainsBB(const cpBB bb, const cpBB other) 52 { 53 return (bb.l <= other.l && bb.r >= other.r && bb.b <= other.b && bb.t >= other.t); 54 } 55 56 /// Returns true if @c bb contains @c v. 57 cpBool cpBBContainsVect(const cpBB bb, const cpVect v) 58 { 59 return (bb.l <= v.x && bb.r >= v.x && bb.b <= v.y && bb.t >= v.y); 60 } 61 62 /// Returns a bounding box that holds both bounding boxes. 63 cpBB cpBBMerge(const cpBB a, const cpBB b) 64 { 65 return cpBBNew( 66 cpfmin(a.l, b.l), 67 cpfmin(a.b, b.b), 68 cpfmax(a.r, b.r), 69 cpfmax(a.t, b.t) 70 ); 71 } 72 73 /// Returns a bounding box that holds both @c bb and @c v. 74 cpBB cpBBExpand(const cpBB bb, const cpVect v) 75 { 76 return cpBBNew( 77 cpfmin(bb.l, v.x), 78 cpfmin(bb.b, v.y), 79 cpfmax(bb.r, v.x), 80 cpfmax(bb.t, v.y) 81 ); 82 } 83 84 /// Returns the center of a bounding box. 85 cpVect cpBBCenter(cpBB bb) 86 { 87 return cpvlerp(cpv(bb.l, bb.b), cpv(bb.r, bb.t), 0.5f); 88 } 89 90 /// Returns the area of the bounding box. 91 cpFloat cpBBArea(cpBB bb) 92 { 93 return (bb.r - bb.l) * (bb.t - bb.b); 94 } 95 96 /// Merges @c a and @c b and returns the area of the merged bounding box. 97 cpFloat cpBBMergedArea(cpBB a, cpBB b) 98 { 99 return (cpfmax(a.r, b.r) - cpfmin(a.l, b.l)) * (cpfmax(a.t, b.t) - cpfmin(a.b, b.b)); 100 } 101 102 /// Returns the fraction along the segment query the cpBB is hit. Returns INFINITY if it doesn't hit. 103 cpFloat cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b) 104 { 105 cpFloat idx = 1.0f / (b.x - a.x); 106 cpFloat tx1 = (bb.l == a.x ? -INFINITY : (bb.l - a.x) * idx); 107 cpFloat tx2 = (bb.r == a.x ? INFINITY : (bb.r - a.x) * idx); 108 cpFloat txmin = cpfmin(tx1, tx2); 109 cpFloat txmax = cpfmax(tx1, tx2); 110 111 cpFloat idy = 1.0f / (b.y - a.y); 112 cpFloat ty1 = (bb.b == a.y ? -INFINITY : (bb.b - a.y) * idy); 113 cpFloat ty2 = (bb.t == a.y ? INFINITY : (bb.t - a.y) * idy); 114 cpFloat tymin = cpfmin(ty1, ty2); 115 cpFloat tymax = cpfmax(ty1, ty2); 116 117 if (tymin <= txmax && txmin <= tymax) 118 { 119 cpFloat min = cpfmax(txmin, tymin); 120 cpFloat max = cpfmin(txmax, tymax); 121 122 if (0.0 <= max && min <= 1.0) 123 return cpfmax(min, 0.0); 124 } 125 126 return INFINITY; 127 } 128 129 /// Return true if the bounding box intersects the line segment with ends @c a and @c b. 130 cpBool cpBBIntersectsSegment(cpBB bb, cpVect a, cpVect b) 131 { 132 return (cpBBSegmentQuery(bb, a, b) != INFINITY); 133 } 134 135 /// Clamp a vector to a bounding box. 136 cpVect cpBBClampVect(const cpBB bb, const cpVect v) 137 { 138 return cpv(cpfclamp(v.x, bb.l, bb.r), cpfclamp(v.y, bb.b, bb.t)); 139 } 140 141 // TODO edge case issue 142 /// Wrap a vector to a bounding box. 143 cpVect cpBBWrapVect(const cpBB bb, const cpVect v) 144 { 145 cpFloat ix = cpfabs(bb.r - bb.l); 146 cpFloat modx = cpfmod(v.x - bb.l, ix); 147 cpFloat x = (modx > 0.0f) ? modx : modx + ix; 148 149 cpFloat iy = cpfabs(bb.t - bb.b); 150 cpFloat mody = cpfmod(v.y - bb.b, iy); 151 cpFloat y = (mody > 0.0f) ? mody : mody + iy; 152 153 return cpv(x + bb.l, y + bb.b); 154 }