1 /** 2 * Vector 3 */ 4 module d2d.math.Vector; 5 6 import std.algorithm; 7 import std.array; 8 import std.conv; 9 import std.math; 10 import std.parallelism; 11 import std.traits; 12 13 /** 14 * A vector is an object representing distance in vertical and horizontal directions in multidimensional space 15 * Components are the first template parameter with the second template parameter being vector dimensionality 16 * Most vector operations take advantage of parallelism to do simple arithmetic on each component in parallel 17 * TODO: slices returning vectors, swizzling, and dispatch forwarding 18 */ 19 class Vector(T, uint dimensions) { 20 21 alias components this; //Allows for slicing/indexing the vector to work on its components 22 23 ///The components of the vector 24 union { 25 T[dimensions] components; 26 struct { 27 static if (dimensions > 0) { 28 T x; 29 } 30 static if (dimensions > 1) { 31 T y; 32 } 33 static if (dimensions > 2) { 34 T z; 35 } 36 static if (dimensions > 3) { 37 T w; 38 } 39 } 40 } 41 42 /** 43 * Sets the angles of the vector where the angles are given in radians 44 * Angles are direction angles (eg. first angle is direction from x, second is direction from y, etc...) 45 * 0 goes along the positive axis 46 */ 47 @property void directionAngles(Vector!(double, dimensions) angles) { 48 immutable mag = this.magnitude; 49 foreach (i, angle; (cast(double[]) angles.components).parallel) { 50 this.components[i] = cast(T)(mag * cos(angle)); 51 } 52 } 53 54 /** 55 * Gets the angles of the vector where the angles are given in radians 56 * Angles are direction angles (eg. first angle is direction from x, second is direction from y, etc...) 57 * 0 goes along the positive axis 58 */ 59 @property Vector!(double, dimensions) directionAngles() { 60 Vector!(double, dimensions) angles = new Vector!(double, dimensions)(cast(T) 0); 61 immutable mag = this.magnitude; 62 foreach (i, component; (cast(T[]) this.components).parallel) { 63 angles.components[i] = acos(component / mag); 64 } 65 return angles; 66 } 67 68 /** 69 * Sets the length of the vector 70 * Maintains component ratios of the vector 71 */ 72 @property void magnitude(double mag) { 73 immutable scale = mag / this.magnitude; 74 foreach (i, ref component; (cast(T[]) this.components).parallel) { 75 component = cast(T)(component * scale); 76 } 77 } 78 79 /** 80 * Gets the length of the vector 81 */ 82 @property double magnitude() { 83 return sqrt(cast(double) reduce!((squareMag, 84 component) => squareMag + component.pow(2))(cast(T) 0, this.components)); 85 } 86 87 /** 88 * A vector constructor; takes in the number of args given and assigns them as components 89 */ 90 this(T[] components...) { 91 assert(components.length == dimensions); 92 this.components = components; 93 } 94 95 /** 96 * A vector constructor; takes in a value that acts as both vector components 97 */ 98 this(T allComponents) { 99 this.components[] = allComponents; 100 } 101 102 /** 103 * A constructor that sets all elements to 0 104 */ 105 this() { 106 this.components[] = 0; 107 } 108 109 /** 110 * A copy constructor for a vector; makes the same vector, but as a different instance 111 */ 112 this(Vector!(T, dimensions) toCopy) { 113 this.components[] = toCopy.components[]; 114 } 115 116 /** 117 * Allows assigning the vector to a static array to set all components of the vector 118 */ 119 void opAssign(T[] rhs) { 120 this.components = rhs; 121 } 122 123 /** 124 * Allows assigning the vector to a single value to set all elements of the vector to such a value 125 */ 126 void opAssign(T rhs) { 127 this.components[] = rhs; 128 } 129 130 /** 131 * Allows the vector to have the joint operator assign syntax 132 * Works component-wise (eg. (3, 2, 1) += (1, 2, 3) makes (3, 2, 1) into (4, 4, 4)) 133 */ 134 void opOpAssign(string op)(Vector!(T, dimensions) otherVector) { 135 mixin("this.components[] " ~ op ~ "= otherVector.components[];"); 136 } 137 138 /** 139 * Allows the vector to have the joint operator assign syntax 140 * Works component-wise, so each operation of the constant is applied to each component 141 */ 142 void opOpAssign(string op)(T[] otherComponents) { 143 mixin("this.components[] " ~ op ~ "= otherComponents[];"); 144 } 145 146 /** 147 * Allows the vector to have the joint operator assign syntax 148 * Works component-wise, so each operation of the constant is applied to each component 149 */ 150 void opOpAssign(string op)(T constant) { 151 mixin("this.components[] " ~ op ~ "= constant;"); 152 } 153 154 /** 155 * Casts the vector to a vector of another type 156 */ 157 U opCast(U)() if (is(U : Vector!V, V...)) { 158 alias type = TemplateArgsOf!U[0]; 159 U newVec = new U(type.init); 160 foreach (i, component; (cast(T[]) this.components).parallel) { 161 newVec.components[i] = cast(type) component; 162 } 163 return newVec; 164 } 165 166 /** 167 * Allows unary functions to be applied to the vector; aplies the same operator to all components 168 */ 169 Vector!(T, dimensions) opUnary(string op)() { 170 Vector!(T, dimensions) newVec = new Vector(this.components); 171 mixin("newVec.components[] = " ~ op ~ "newVec.components[];"); 172 return newVec; 173 } 174 175 /** 176 * Allows the vector to be used with normal operators 177 * Works component-wise (eg. (3, 2, 1) + (1, 2, 3) = (4, 4, 4)) 178 */ 179 Vector!(T, dimensions) opBinary(string op)(Vector!(T, dimensions) otherVector) { 180 Vector!(T, dimensions) newVec = new Vector(this.components); 181 mixin("newVec.components[] " ~ op ~ "= otherVector.components[];"); 182 return newVec; 183 } 184 185 /** 186 * Allows the vector to be used with normal operators 187 * Works component-wise (eg. (3, 2, 1) + (1, 2, 3) = (4, 4, 4)) 188 */ 189 Vector!(T, dimensions) opBinary(string op)(T[] otherComponents) { 190 Vector!(T, dimensions) newVec = new Vector(this.components); 191 mixin("newVec.components[] " ~ op ~ "= otherComponents[];"); 192 return newVec; 193 } 194 195 /** 196 * Allows the vector to be used with normal operators 197 * Works component-wise, so each operation of the constant is applied to each component 198 */ 199 Vector!(T, dimensions) opBinary(string op)(T constant) { 200 Vector!(T, dimensions) newVec = new Vector(this.components); 201 mixin("newVec.components[] " ~ op ~ "= constant;"); 202 return newVec; 203 } 204 205 /** 206 * Gives the vector a pretty string format 207 * (eg. (1, 2, 3) => <1, 2, 3>) 208 */ 209 override string toString() { 210 string representation = "<"; 211 foreach (i; 0 .. this.components.length) { 212 representation ~= this.components[i].to!string; 213 representation ~= (i + 1 != this.components.length) ? ", " : ">"; 214 } 215 return representation; 216 } 217 218 /** 219 * Returns whether the vector is equal to another vector or constant 220 * Uses approxEquals to do easy equality for vectors of doubles 221 */ 222 override bool opEquals(Object o) { 223 Vector!(T, dimensions) cmp = cast(Vector!(T, dimensions)) o; 224 foreach (i, component; this.components) { 225 if (!approxEqual(component, cmp.components[i])) { 226 return false; 227 } 228 } 229 return true; 230 } 231 232 /** 233 * Applies the given function to each element of the vector 234 */ 235 void apply(void delegate(T) application) { 236 foreach(component; (cast(T[]) this.components).parallel) { 237 application(component); 238 } 239 } 240 241 /** 242 * Applies the given function to each element of the vector and returns the results in a new vector 243 */ 244 Vector!(U, dimensions) apply(U)(U delegate(T) application) { 245 Vector!(U, dimensions) applied = new Vector!(U, dimensions)(U.init); 246 foreach(i, component; (cast(T[]) this.components).parallel) { 247 applied.components[i] = application(component); 248 } 249 return applied; 250 } 251 252 } 253 254 /** 255 * Calculates the dot product or the similarity of two vectors 256 */ 257 T dot(T, uint dim)(Vector!(T, dim) first, Vector!(T, dim) second) { 258 immutable pairWiseMultiple = first * second; 259 return pairWiseMultiple.components.sum; 260 } 261 262 /** 263 * Calculates the cross product or the perpendicular vector to two vectors 264 * Currently only works on 2 or 3 dimensional vectors 265 */ 266 Vector!(T, 3) cross(T, uint size)(Vector!(T, size) first, Vector!(T, size) second) 267 if (size == 2 || size == 3) { 268 static if (size == 2) { 269 return new Vector!(T, 3)(0, 0, first.x * second.y - first.y * second.x); 270 } 271 return new Vector!(T, 3)(first.y * second.z - first.z * second.y, 272 first.z * second.x - first.x * second.z, first.x * second.y - first.y * second.x); 273 }