1 /* 2 ******************************************************************************************* 3 * Dgame (a D game framework) - Copyright (c) Randy Schütt 4 * 5 * This software is provided 'as-is', without any express or implied warranty. 6 * In no event will the authors be held liable for any damages arising from 7 * the use of this software. 8 * 9 * Permission is granted to anyone to use this software for any purpose, 10 * including commercial applications, and to alter it and redistribute it 11 * freely, subject to the following restrictions: 12 * 13 * 1. The origin of this software must not be misrepresented; you must not claim 14 * that you wrote the original software. If you use this software in a product, 15 * an acknowledgment in the product documentation would be appreciated but is 16 * not required. 17 * 18 * 2. Altered source versions must be plainly marked as such, and must not be 19 * misrepresented as being the original software. 20 * 21 * 3. This notice may not be removed or altered from any source distribution. 22 ******************************************************************************************* 23 */ 24 module Dgame.Math.Vector2; 25 26 private { 27 debug import std.stdio : writeln; 28 import std.math : pow, sqrt, acos, PI; 29 import std.traits : isNumeric; 30 } 31 32 /** 33 * Vector2 is a structure that defines a two-dimensional point. 34 * 35 * Author: rschuett 36 */ 37 struct Vector2(T) if (isNumeric!T) { 38 /** 39 * x coordinate 40 */ 41 T x = 0; 42 /** 43 * y coordinate 44 */ 45 T y = 0; 46 47 /** 48 * CTor 49 */ 50 this(T x, T y) pure nothrow { 51 this.x = x; 52 this.y = y; 53 } 54 55 /** 56 * CTor 57 */ 58 this(U)(U x, U y) pure nothrow if (isNumeric!U && !is(U : T)) { 59 this(cast(T) x, cast(T) y); 60 } 61 62 /** 63 * CTor 64 */ 65 this(U)(U[2] pos) pure nothrow if (isNumeric!U) { 66 this(pos[0], pos[1]); 67 } 68 69 /** 70 * CTor 71 */ 72 this(U)(ref const Vector2!U vec) pure nothrow { 73 this(vec.x, vec.y); 74 } 75 76 // debug(Dgame) 77 // this(this) { 78 // writeln("Postblit Vector2"); 79 // } 80 81 /** 82 * Supported operation: +=, -=, *=, /= and %= 83 */ 84 ref Vector2 opOpAssign(string op)(ref const Vector2 vec) pure { 85 switch (op) { 86 case "+": 87 this.x += vec.x; 88 this.y += vec.y; 89 break; 90 case "-": 91 this.x -= vec.x; 92 this.y -= vec.y; 93 break; 94 case "*": 95 this.x *= vec.x; 96 this.y *= vec.y; 97 break; 98 case "/": 99 this.x /= vec.x; 100 this.y /= vec.y; 101 break; 102 case "%": 103 this.x %= vec.x; 104 this.y %= vec.y; 105 break; 106 default: throw new Exception("Unsupported operator " ~ op); 107 } 108 109 return this; 110 } 111 112 /** 113 * Supported operation: +=, -=, *=, /= and %= 114 */ 115 ref Vector2 opOpAssign(string op)(T number) pure { 116 switch (op) { 117 case "+": 118 this.x += number; 119 this.y += number; 120 break; 121 case "-": 122 this.x -= number; 123 this.y -= number; 124 break; 125 case "*": 126 this.x *= number; 127 this.y *= number; 128 break; 129 case "/": 130 this.x /= number; 131 this.y /= number; 132 break; 133 case "%": 134 this.x %= number; 135 this.y %= number; 136 break; 137 default: throw new Exception("Unsupported operator " ~ op); 138 } 139 140 return this; 141 } 142 143 /** 144 * Supported operation: +, -, *, / and % 145 */ 146 Vector2 opBinary(string op)(ref const Vector2 vec) const pure { 147 switch (op) { 148 case "+": return Vector2(vec.x + this.x, vec.y + this.y); 149 case "-": return Vector2(vec.x - this.x, vec.y - this.y); 150 case "*": return Vector2(vec.x * this.x, vec.y * this.y); 151 case "/": return Vector2(vec.x / this.x, vec.y / this.y); 152 case "%": return Vector2(vec.x % this.x, vec.y % this.y); 153 default: throw new Exception("Unsupported operator " ~ op); 154 } 155 } 156 157 /** 158 * Supported operation: +, -, *, / and % 159 */ 160 Vector2 opBinary(string op)(T number) const pure { 161 switch (op) { 162 case "+": return Vector2(number + this.x, number + this.y); 163 case "-": return Vector2(number - this.x, number - this.y); 164 case "*": return Vector2(number * this.x, number * this.y); 165 case "/": return Vector2(number / this.x, number / this.y); 166 case "%": return Vector2(number % this.x, number % this.y); 167 default: throw new Exception("Unsupported operator " ~ op); 168 } 169 } 170 171 /** 172 * Returns a negated copy of this Vector. 173 */ 174 Vector2 opNeg() const pure nothrow { 175 return Vector2(-this.x, -this.y); 176 } 177 178 /** 179 * Negate this Vector 180 */ 181 void negate() pure nothrow { 182 this.x = -this.x; 183 this.y = -this.y; 184 } 185 186 /** 187 * Compares two vectors by checking whether the coordinates are equals. 188 */ 189 bool opEquals(ref const Vector2 vec) const pure nothrow { 190 return vec.x == this.x && vec.y == this.y; 191 } 192 193 /** 194 * Checks if this vector is empty. This means that his coordinates are 0. 195 */ 196 bool isEmpty() const pure nothrow { 197 return this.x == 0 && this.y == 0; 198 } 199 200 /** 201 * Calculate the scalar product. 202 */ 203 float scalar(ref const Vector2 vec) const pure nothrow { 204 return this.x * vec.x + this.y * vec.y; 205 } 206 207 /** 208 * alias for scalar 209 */ 210 alias dot = scalar; 211 212 /** 213 * Calculate the length. 214 */ 215 @property 216 float length() const pure nothrow { 217 if (this.isEmpty()) 218 return 0f; 219 220 return sqrt(pow(this.x, 2f) + pow(this.y, 2f)); 221 } 222 223 /** 224 * Calculate the angle between two vectors. 225 * If the second paramter is true, the return value is converted to degrees. 226 * Otherwise, radiant is used. 227 */ 228 float angle(ref const Vector2 vec, bool degrees = true) const pure nothrow { 229 float angle = acos(this.scalar(vec) / (this.length * vec.length)); 230 231 if (degrees) 232 return angle * 180f / PI; 233 234 return angle; 235 } 236 237 /** 238 * Calculate the diff between two vectors. 239 */ 240 float diff(ref const Vector2 vec) const pure nothrow { 241 return sqrt(pow(this.x - vec.x, 2f) + pow(this.y - vec.y, 2f)); 242 } 243 244 /** 245 * Normalize the vector in which the coordinates are divided by the length. 246 */ 247 ref Vector2 normalize() pure nothrow { 248 const float len = this.length; 249 if (len != 0) { 250 this.x /= len; 251 this.y /= len; 252 } 253 254 return this; 255 } 256 257 /** 258 * Set new coordinates. 259 */ 260 void set(T x, T y) pure nothrow { 261 this.x = x; 262 this.y = y; 263 } 264 265 /** 266 * Move the current coordinates. 267 */ 268 void move(T x, T y) pure nothrow { 269 this.x += x; 270 this.y += y; 271 } 272 273 /** 274 * Returns the Vector as static array. 275 */ 276 T[2] asArray() const pure nothrow { 277 return [this.x, this.y]; 278 } 279 } unittest { 280 Vector2f vec; 281 assert(vec.isEmpty()); 282 vec += 1; 283 assert(vec.x == 1f && vec.y == 1f); 284 assert(!vec.isEmpty()); 285 } 286 287 /** 288 * Alias for short Vector 289 */ 290 alias Vector2s = Vector2!(short); 291 /** 292 * Alias for float Vector 293 */ 294 alias Vector2f = Vector2!(float); 295 /** 296 * Alias for byte Vector 297 */ 298 alias Vector2b = Vector2!(byte);