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.cpVect; 23 24 import dchip.chipmunk_types; 25 26 /// Chipmunk's 2D vector type along with a handy 2D vector math lib. 27 28 /// Constant for the zero vector. 29 immutable cpVect cpvzero = { 0.0f, 0.0f }; 30 31 /// Convenience constructor for cpVect structs. 32 alias cpv = cpVect; 33 34 /// Spherical linearly interpolate between v1 and v2. 35 cpVect cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t) 36 { 37 cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2)); 38 cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f)); 39 40 if (omega < 1e-3) 41 { 42 // If the angle between two vectors is very small, lerp instead to avoid precision issues. 43 return cpvlerp(v1, v2, t); 44 } 45 else 46 { 47 cpFloat denom = 1.0f / cpfsin(omega); 48 return cpvadd(cpvmult(v1, cpfsin((1.0f - t) * omega) * denom), cpvmult(v2, cpfsin(t * omega) * denom)); 49 } 50 } 51 52 /// Spherical linearly interpolate between v1 towards v2 by no more than angle a radians 53 cpVect cpvslerpconst(const cpVect v1, const cpVect v2, const cpFloat a) 54 { 55 cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2)); 56 cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f)); 57 58 return cpvslerp(v1, v2, cpfmin(a, omega) / omega); 59 } 60 61 /// Returns a string representation of v. Intended mostly for debugging purposes and not production use. 62 /// $(B Note:) The string points to a static local and is reset every time the function is called. 63 /// If you want to print more than one vector you will have to split up your printing onto separate lines. 64 string cpvstr(const cpVect v) 65 { 66 import std.exception : assumeUnique; 67 import std..string : sformat; 68 69 static char[256] str; 70 sformat(str, "(% .3s, % .3s)", v.x, v.y); 71 return assumeUnique(str); 72 } 73 74 /// Check if two vectors are equal. (Be careful when comparing floating point numbers!) 75 cpBool cpveql(const cpVect v1, const cpVect v2) 76 { 77 return (v1.x == v2.x && v1.y == v2.y); 78 } 79 80 /// Add two vectors 81 cpVect cpvadd(const cpVect v1, const cpVect v2) 82 { 83 return cpv(v1.x + v2.x, v1.y + v2.y); 84 } 85 86 /// Subtract two vectors. 87 cpVect cpvsub(const cpVect v1, const cpVect v2) 88 { 89 return cpv(v1.x - v2.x, v1.y - v2.y); 90 } 91 92 /// Negate a vector. 93 cpVect cpvneg(const cpVect v) 94 { 95 return cpv(-v.x, -v.y); 96 } 97 98 /// Scalar multiplication. 99 cpVect cpvmult(const cpVect v, const cpFloat s) 100 { 101 return cpv(v.x * s, v.y * s); 102 } 103 104 /// Vector dot product. 105 cpFloat cpvdot(const cpVect v1, const cpVect v2) 106 { 107 return v1.x * v2.x + v1.y * v2.y; 108 } 109 110 /// 2D vector cross product analog. 111 /// The cross product of 2D vectors results in a 3D vector with only a z component. 112 /// This function returns the magnitude of the z value. 113 cpFloat cpvcross(const cpVect v1, const cpVect v2) 114 { 115 return v1.x * v2.y - v1.y * v2.x; 116 } 117 118 /// Returns a perpendicular vector. (90 degree rotation) 119 cpVect cpvperp(const cpVect v) 120 { 121 return cpv(-v.y, v.x); 122 } 123 124 /// Returns a perpendicular vector. (-90 degree rotation) 125 cpVect cpvrperp(const cpVect v) 126 { 127 return cpv(v.y, -v.x); 128 } 129 130 /// Returns the vector projection of v1 onto v2. 131 cpVect cpvproject(const cpVect v1, const cpVect v2) 132 { 133 return cpvmult(v2, cpvdot(v1, v2) / cpvdot(v2, v2)); 134 } 135 136 /// Returns the unit length vector for the given angle (in radians). 137 cpVect cpvforangle(const cpFloat a) 138 { 139 return cpv(cpfcos(a), cpfsin(a)); 140 } 141 142 /// Returns the angular direction v is pointing in (in radians). 143 cpFloat cpvtoangle(const cpVect v) 144 { 145 return cpfatan2(v.y, v.x); 146 } 147 148 /// Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector. 149 cpVect cpvrotate(const cpVect v1, const cpVect v2) 150 { 151 return cpv(v1.x * v2.x - v1.y * v2.y, v1.x * v2.y + v1.y * v2.x); 152 } 153 154 /// Inverse of cpvrotate(). 155 cpVect cpvunrotate(const cpVect v1, const cpVect v2) 156 { 157 return cpv(v1.x * v2.x + v1.y * v2.y, v1.y * v2.x - v1.x * v2.y); 158 } 159 160 /// Returns the squared length of v. Faster than cpvlength() when you only need to compare lengths. 161 cpFloat cpvlengthsq(const cpVect v) 162 { 163 return cpvdot(v, v); 164 } 165 166 /// Returns the length of v. 167 cpFloat cpvlength(const cpVect v) 168 { 169 return cpfsqrt(cpvdot(v, v)); 170 } 171 172 /// Linearly interpolate between v1 and v2. 173 cpVect cpvlerp(const cpVect v1, const cpVect v2, const cpFloat t) 174 { 175 return cpvadd(cpvmult(v1, 1.0f - t), cpvmult(v2, t)); 176 } 177 178 /// Returns a normalized copy of v. 179 cpVect cpvnormalize(const cpVect v) 180 { 181 // Neat trick I saw somewhere to avoid div/0. 182 return cpvmult(v, 1.0f / (cpvlength(v) + CPFLOAT_MIN)); 183 } 184 185 /// @deprecated Just an alias for cpvnormalize() now. 186 cpVect cpvnormalize_safe(const cpVect v) 187 { 188 return cpvnormalize(v); 189 } 190 191 /// Clamp v to length len. 192 cpVect cpvclamp(const cpVect v, const cpFloat len) 193 { 194 return (cpvdot(v, v) > len * len) ? cpvmult(cpvnormalize(v), len) : v; 195 } 196 197 /// Linearly interpolate between v1 towards v2 by distance d. 198 cpVect cpvlerpconst(cpVect v1, cpVect v2, cpFloat d) 199 { 200 return cpvadd(v1, cpvclamp(cpvsub(v2, v1), d)); 201 } 202 203 /// Returns the distance between v1 and v2. 204 cpFloat cpvdist(const cpVect v1, const cpVect v2) 205 { 206 return cpvlength(cpvsub(v1, v2)); 207 } 208 209 /// Returns the squared distance between v1 and v2. Faster than cpvdist() when you only need to compare distances. 210 cpFloat cpvdistsq(const cpVect v1, const cpVect v2) 211 { 212 return cpvlengthsq(cpvsub(v1, v2)); 213 } 214 215 /// Returns true if the distance between v1 and v2 is less than dist. 216 cpBool cpvnear(const cpVect v1, const cpVect v2, const cpFloat dist) 217 { 218 return cpvdistsq(v1, v2) < dist * dist; 219 } 220 221 /// 2x2 matrix type used for tensors and such. 222 223 /// Create a 2x2 matrix. 224 cpMat2x2 cpMat2x2New(cpFloat a, cpFloat b, cpFloat c, cpFloat d) 225 { 226 cpMat2x2 m = { a, b, c, d }; 227 return m; 228 } 229 230 /// Transform a 2x2 matrix. 231 cpVect cpMat2x2Transform(cpMat2x2 m, cpVect v) 232 { 233 return cpv(v.x * m.a + v.y * m.b, v.x * m.c + v.y * m.d); 234 }