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 28 import std.traits : isNumeric; 29 static import std.math; 30 31 public: 32 33 /** 34 * Vector2 is a structure that defines a two-dimensional point. 35 * 36 * Author: Randy Schuett (rswhite4@googlemail.com) 37 */ 38 struct Vector2(T) if (isNumeric!(T)) { 39 /** 40 * The x coordinate 41 */ 42 T x = 0; 43 /** 44 * The y coordinate 45 */ 46 T y = 0; 47 48 /** 49 * CTor 50 */ 51 @nogc 52 this(T x, T y) pure nothrow { 53 this.x = x; 54 this.y = y; 55 } 56 57 /** 58 * CTor 59 */ 60 @nogc 61 this(U)(U x, U y) pure nothrow if (isNumeric!(U) && !is(U == T)) { 62 this(cast(T) x, cast(T) y); 63 } 64 65 /** 66 * CTor 67 */ 68 @nogc 69 this(U)(const Vector2!(U) vec) pure nothrow if (!is(U == T)) { 70 this(vec.x, vec.y); 71 } 72 73 /** 74 * Supported operation: +=, -=, *=, /= and %= 75 */ 76 @nogc 77 ref Vector2!(T) opOpAssign(string op)(const Vector2!(T) vec) pure nothrow { 78 switch (op) { 79 case "+": 80 case "-": 81 case "*": 82 case "/": 83 case "%": 84 mixin("this.x " ~ op ~ "= vec.x;"); 85 mixin("this.y " ~ op ~ "= vec.y;"); 86 break; 87 default: 88 assert(0, "Unsupported operator " ~ op); 89 } 90 91 return this; 92 } 93 94 /** 95 * Supported operation: +=, -=, *=, /= and %= 96 */ 97 @nogc 98 ref Vector2!(T) opOpAssign(string op)(float num) pure nothrow { 99 switch (op) { 100 case "+": 101 case "-": 102 case "*": 103 case "/": 104 case "%": 105 mixin("this.x = cast(T)(this.x " ~ op ~ " num);"); 106 mixin("this.y = cast(T)(this.y " ~ op ~ " num);"); 107 break; 108 default: 109 assert(0, "Unsupported operator " ~ op); 110 } 111 112 return this; 113 } 114 115 /** 116 * Supported operation: +, -, *, / and % 117 */ 118 @nogc 119 Vector2!(T) opBinary(string op)(const Vector2!(T) vec) const pure nothrow { 120 switch (op) { 121 case "+": 122 case "-": 123 case "*": 124 case "/": 125 case "%": 126 mixin("return Vector2!(T)(this.x " ~ op ~ " vec.x, this.y " ~ op ~ " vec.y);"); 127 default: 128 assert(0, "Unsupported operator " ~ op); 129 } 130 } 131 132 /** 133 * Supported operation: +, -, *, / and % 134 */ 135 @nogc 136 Vector2!(T) opBinary(string op)(float num) const pure { 137 switch (op) { 138 case "+": 139 case "-": 140 case "*": 141 case "/": 142 case "%": 143 mixin("return Vector2!(T)(cast(T)(this.x " ~ op ~ " num), cast(T)(this.y " ~ op ~ " num));"); 144 default: 145 assert(0, "Unsupported operator " ~ op); 146 } 147 } 148 149 /** 150 * Returns a negated copy of this Vector. 151 */ 152 @nogc 153 Vector2!(T) opNeg() const pure nothrow { 154 return Vector2!(T)(-this.x, -this.y); 155 } 156 157 /** 158 * Compares two vectors by checking whether the coordinates are equals. 159 */ 160 @nogc 161 bool opEquals(const Vector2!(T) vec) const pure nothrow { 162 return vec.x == this.x && vec.y == this.y; 163 } 164 165 /** 166 * Checks if this vector is empty. This means that his coordinates are 0. 167 */ 168 @nogc 169 bool isEmpty() const pure nothrow { 170 return this.x == 0 && this.y == 0; 171 } 172 173 /** 174 * Calculate the scalar product. 175 */ 176 @nogc 177 float scalar(const Vector2!(T) vec) const pure nothrow { 178 return this.x * vec.x + this.y * vec.y; 179 } 180 181 /** 182 * alias for scalar 183 */ 184 alias dot = scalar; 185 186 /** 187 * Calculate the length. 188 */ 189 @nogc 190 @property 191 float length() const pure nothrow { 192 if (this.isEmpty()) 193 return 0f; 194 return std.math.sqrt(std.math.pow(this.x, 2f) + std.math.pow(this.y, 2f)); 195 } 196 197 /** 198 * Calculate the angle between two vectors. 199 * If the second paramter is true, the return value is converted to degrees. 200 * Otherwise, radiant is used. 201 */ 202 @nogc 203 float angle(const Vector2!(T) vec, bool degrees = true) const pure nothrow { 204 immutable float angle = std.math.acos(this.scalar(vec) / (this.length * vec.length)); 205 if (degrees) 206 return angle * 180f / std.math.PI; 207 208 return angle; 209 } 210 211 /** 212 * Calculate the diff between two vectors. 213 */ 214 @nogc 215 float diff(const Vector2!(T) vec) const pure nothrow { 216 return std.math.sqrt(std.math.pow(this.x - vec.x, 2f) + std.math.pow(this.y - vec.y, 2f)); 217 } 218 219 /** 220 * Normalize the vector in which the coordinates are divided by the length. 221 */ 222 @nogc 223 ref Vector2!(T) normalize() pure nothrow { 224 immutable float len = this.length; 225 if (len != 0) { 226 this.x = cast(T)(this.x / len); 227 this.y = cast(T)(this.y / len); 228 } 229 230 return this; 231 } 232 } 233 234 alias Vector2f = Vector2!(float); /// A float representation 235 alias Vector2i = Vector2!(int); /// An int representation 236 237 @nogc 238 unittest { 239 Vector2i vec; 240 241 assert(vec.x == 0); 242 assert(vec.y == 0); 243 assert(vec.isEmpty()); 244 245 vec = Vector2i(20, 30); 246 247 assert(vec.x == 20); 248 assert(vec.y == 30); 249 assert(!vec.isEmpty()); 250 251 vec += 42; 252 253 assert(vec.x == 62); 254 assert(vec.y == 72); 255 assert(!vec.isEmpty()); 256 257 const Vector2i vec2 = vec * 3; 258 259 assert(vec2.x == 3 * vec.x); 260 assert(vec2.y == 3 * vec.y); 261 262 const Vector2i vec3 = vec2 + vec; 263 264 assert(vec3.x == vec.x + vec2.x); 265 assert(vec3.y == vec.y + vec2.y); 266 267 assert(vec == vec); 268 assert(vec2 != vec3); 269 270 const Vector2i vec4 = -vec; 271 272 assert(vec4.x == -62); 273 assert(vec4.y == -72); 274 275 const Vector2f vconv = vec4; 276 277 assert(vec4.x == vconv.x && vec4.y == vconv.y); 278 279 Vector2f v1 = Vector2f(2.3, 4.2); 280 immutable float l1 = v1.length; 281 const Vector2f v1n = v1.normalize(); 282 283 Vector2i v2 = Vector2i(2.3, 4.2); 284 immutable float l2 = v2.length; 285 const Vector2i v2n = v2.normalize(); 286 287 const Vector2f vec5 = Vector2f(80, 64); 288 const Vector2f vec6 = Vector2f(32, 32); 289 const Vector2f vec7 = Vector2f(2.5, 2); 290 291 assert(vec5 / vec6 == vec7); 292 assert(vec5 / vec6.x == vec7); 293 294 const Vector2i vec8 = Vector2i(32, 32); 295 const Vector2f vec9 = (vec8 / 32) * 32; 296 297 assert(vec9.x == vec8.x && vec9.y == vec8.y); 298 }