1 /** 2 * Copyright: Enalye 3 * License: Zlib 4 * Authors: Enalye 5 */ 6 module atelier.core.vec3; 7 8 import std.math; 9 import atelier.core.vec2; 10 11 /// Represent a mathematical 3-dimensional vector. 12 struct Vec3(T) { 13 static assert(__traits(isArithmetic, T)); 14 15 static if (__traits(isUnsigned, T)) { 16 /// {1, 1, 1} vector. Its length is not one ! 17 enum one = Vec3!T(1u, 1u, 1u); 18 /// Null vector. 19 enum zero = Vec3!T(0u, 0u, 0u); 20 } 21 else { 22 static if (__traits(isFloating, T)) { 23 /// {1, 1, 1} vector. Its length is not one ! 24 enum one = Vec3!T(1f, 1f, 1f); 25 /// {0.5, 0.5, 0.5} vector. Its length is not 0.5 ! 26 enum half = Vec3!T(.5f, .5f, .5f); 27 /// Null vector. 28 enum zero = Vec3!T(0f, 0f, 0f); 29 /// {-1, 0, 0} vector. 30 enum left = Vec3!T(-1f, 0, 0); 31 /// {1, 0, 0} vector. 32 enum right = Vec3!T(1f, 0, 0); 33 /// {0, -1, 0} vector. 34 enum down = Vec3!T(0, -1f, 0); 35 /// {0, 1, 0} vector. 36 enum up = Vec3!T(0, 1f, 0); 37 /// {0, 0, -1} vector. 38 enum bottom = Vec3!T(0, 0, -1f); 39 /// {0, 0, 1} vector. 40 enum top = Vec3!T(0, 0, 1f); 41 } 42 else { 43 /// {1, 1, 1} vector. Its length is not one ! 44 enum one = Vec3!T(1, 1, 1); 45 /// Null vector. 46 enum zero = Vec3!T(0, 0, 0); 47 /// {-1, 0, 0} vector. 48 enum left = Vec3!T(-1, 0, 0); 49 /// {1, 0, 0} vector. 50 enum right = Vec3!T(1, 0, 0); 51 /// {0, -1, 0} vector. 52 enum down = Vec3!T(0, -1, 0); 53 /// {0, 1, 0} vector. 54 enum up = Vec3!T(0, 1, 0); 55 /// {0, 0, -1} vector. 56 enum bottom = Vec3!T(0, 0, -1); 57 /// {0, 0, 1} vector. 58 enum top = Vec3!T(0, 0, 1); 59 } 60 } 61 62 /// x-axis coordinate. 63 T x; 64 /// y-axis coordinate. 65 T y; 66 /// z-axis coordinate. 67 T z; 68 69 @property { 70 /// Return a 2-dimensional vector with {x, y} 71 Vec2!T xy() const { 72 return Vec2!T(x, y); 73 } 74 /// Ditto 75 Vec2!T xy(Vec2!T v) { 76 x = v.x; 77 y = v.y; 78 return v; 79 } 80 81 /// Return a 2-dimensional vector with {y, z} 82 Vec2!T yz() const { 83 return Vec2!T(y, z); 84 } 85 /// Ditto 86 Vec2!T yz(Vec2!T v) { 87 y = v.x; 88 z = v.y; 89 return v; 90 } 91 92 /// Return a 2-dimensional vector with {x, z} 93 Vec2!T xz() const { 94 return Vec2!T(x, z); 95 } 96 /// Ditto 97 Vec2!T xz(Vec2!T v) { 98 x = v.x; 99 z = v.y; 100 return v; 101 } 102 } 103 104 /// Build a new 3-dimensional vector. 105 this(T x_, T y_, T z_) { 106 x = x_; 107 y = y_; 108 z = z_; 109 } 110 111 /// Ditto 112 this(Vec2!T xy_, T z_) { 113 x = xy_.x; 114 y = xy_.y; 115 z = z_; 116 } 117 118 /// Ditto 119 this(T x_, Vec2!T yz_) { 120 x = x_; 121 y = yz_.x; 122 z = yz_.y; 123 } 124 125 /// Changes {x, y, z} 126 void set(T x_, T y_, T z_) { 127 x = x_; 128 y = y_; 129 z = z_; 130 } 131 132 /// Ditto 133 void set(Vec2!T xy_, T z_) { 134 x = xy_.x; 135 y = xy_.y; 136 z = z_; 137 } 138 139 /// Ditto 140 void set(T x_, Vec2!T yz_) { 141 x = x_; 142 y = yz_.x; 143 z = yz_.y; 144 } 145 146 /// The distance between this point and the other one. 147 T distance(Vec3!T v) const { 148 static if (__traits(isUnsigned, T)) 149 alias V = int; 150 else 151 alias V = T; 152 153 V px = x - v.x, py = y - v.y, pz = z - v.z; 154 155 static if (__traits(isFloating, T)) 156 return std.math.sqrt(px * px + py * py + pz * pz); 157 else 158 return cast(T) std.math.sqrt(cast(float)(px * px + py * py + pz * pz)); 159 } 160 161 /// The distance between this point and the other one squared. 162 /// Not square rooted, so no crash and more efficient. 163 T distanceSquared(Vec3!T v) const { 164 static if (__traits(isUnsigned, T)) 165 alias V = int; 166 else 167 alias V = T; 168 169 V px = x - v.x, py = y - v.y, pz = z - v.z; 170 171 return px * px + py * py + pz * pz; 172 } 173 174 /// The smaller vector possible between the two. 175 Vec3!T min(const Vec3!T v) const { 176 return Vec3!T(x < v.x ? x : v.x, y < v.y ? y : v.y, z < v.z ? z : v.z); 177 } 178 179 /// The larger vector possible between the two. 180 Vec3!T max(const Vec3!T v) const { 181 return Vec3!T(x > v.x ? x : v.x, y > v.y ? y : v.y, z > v.z ? z : v.z); 182 } 183 184 /// Remove negative components. 185 Vec3!T abs() const { 186 static if (__traits(isFloating, T)) 187 return Vec3!T(x < .0 ? -x : x, y < .0 ? -y : y, z < .0 ? -z : z); 188 else static if (__traits(isUnsigned, T)) 189 return Vec3!T(x < 0U ? -x : x, y < 0U ? -y : y, z < 0U ? -z : z); 190 else 191 return Vec3!T(x < 0 ? -x : x, y < 0 ? -y : y, z < 0 ? -z : z); 192 } 193 194 /// Truncate the values. 195 Vec3!T floor() const { 196 static if (__traits(isFloating, T)) 197 return Vec3!T(std.math.floor(x), std.math.floor(y), std.math.floor(z)); 198 else 199 return this; 200 } 201 202 /// Round up the values. 203 Vec3!T ceil() const { 204 static if (__traits(isFloating, T)) 205 return Vec3!T(std.math.ceil(x), std.math.ceil(y), std.math.ceil(z)); 206 else 207 return this; 208 } 209 210 /// Round the values to the nearest integer. 211 Vec3!T round() const { 212 static if (__traits(isFloating, T)) 213 return Vec3!T(std.math.round(x), std.math.round(y), std.math.round(z)); 214 else 215 return this; 216 } 217 218 /// Adds x, y and z. 219 T sum() const { 220 return x + y + z; 221 } 222 223 /// The total length of this vector. 224 T length() const { 225 static if (__traits(isFloating, T)) 226 return std.math.sqrt(x * x + y * y + z * z); 227 else 228 return cast(T) std.math.sqrt(cast(float)(x * x + y * y + z * z)); 229 } 230 231 /// The squared length of this vector. 232 /// Can be null, and more efficiant than length. 233 T lengthSquared() const { 234 return x * x + y * y + z * z; 235 } 236 237 /// Transform this vector in a unit vector. 238 void normalize() { 239 if (this == Vec3!T.zero) 240 return; 241 static if (__traits(isFloating, T)) 242 const T len = std.math.sqrt(x * x + y * y + z * z); 243 else 244 const T len = cast(T) std.math.sqrt(cast(float)(x * x + y * y + z * z)); 245 246 x /= len; 247 y /= len; 248 z /= len; 249 } 250 251 /// Returns a unit vector from this one without modifying this one. 252 Vec3!T normalized() const { 253 if (this == Vec3!T.zero) 254 return this; 255 static if (__traits(isFloating, T)) 256 const T len = std.math.sqrt(x * x + y * y + z * z); 257 else 258 const T len = cast(T) std.math.sqrt(cast(float)(x * x + y * y + z * z)); 259 260 return Vec3!T(x / len, y / len, z / len); 261 } 262 263 /// Bounds the vector between those two. 264 Vec3!T clamp(const Vec3!T min, const Vec3!T max) const { 265 Vec3!T v = this; 266 if (v.x < min.x) 267 v.x = min.x; 268 else if (v.x > max.x) 269 v.x = max.x; 270 if (v.y < min.y) 271 v.y = min.y; 272 else if (v.y > max.y) 273 v.y = max.y; 274 if (v.z < min.z) 275 v.z = min.z; 276 else if (v.z > max.z) 277 v.z = max.z; 278 return v; 279 } 280 281 /// Is this vector between those boundaries ? 282 bool isBetween(const Vec3!T min, const Vec3!T max) const { 283 if (x < min.x) 284 return false; 285 else if (x > max.x) 286 return false; 287 if (y < min.y) 288 return false; 289 else if (y > max.y) 290 return false; 291 if (z < min.z) 292 return false; 293 else if (z > max.z) 294 return false; 295 return true; 296 } 297 298 static if (__traits(isFloating, T)) { 299 /// Returns an interpolated vector from this vector to the end vector by a factor. \ 300 /// Does not modify this vector. 301 Vec3!T lerp(Vec3!T end, float t) const { 302 return (this * (1.0 - t)) + (end * t); 303 } 304 } 305 306 /// Operators. 307 bool opEquals(const Vec3!T v) const { 308 return (x == v.x) && (y == v.y) && (z == v.z); 309 } 310 311 /// Ditto 312 Vec3!T opUnary(string op)() const { 313 return mixin("Vec3!T(" ~ op ~ " x, " ~ op ~ " y, " ~ op ~ " z)"); 314 } 315 316 /// Ditto 317 Vec3!T opBinary(string op)(const Vec3!T v) const { 318 return mixin("Vec3!T(x " ~ op ~ " v.x, y " ~ op ~ " v.y, z " ~ op ~ " v.z)"); 319 } 320 321 /// Ditto 322 Vec3!T opBinary(string op)(T s) const { 323 return mixin("Vec3!T(x " ~ op ~ " s, y " ~ op ~ " s, z " ~ op ~ " s)"); 324 } 325 326 /// Ditto 327 Vec3!T opBinaryRight(string op)(T s) const { 328 return mixin("Vec3!T(s " ~ op ~ " x, s " ~ op ~ " y, s " ~ op ~ " z)"); 329 } 330 331 /// Ditto 332 Vec3!T opOpAssign(string op)(Vec3!T v) { 333 mixin("x = x" ~ op ~ "v.x;y = y" ~ op ~ "v.y;z = z" ~ op ~ "v.z;"); 334 return this; 335 } 336 337 /// Ditto 338 Vec3!T opOpAssign(string op)(T s) { 339 mixin("x = x" ~ op ~ "s;y = y" ~ op ~ "s;z = z" ~ op ~ "s;"); 340 return this; 341 } 342 343 /// Ditto 344 Vec3!U opCast(V : Vec3!U, U)() const { 345 return V(cast(U) x, cast(U) y, cast(U) z); 346 } 347 } 348 349 alias Vec3f = Vec3!(float); 350 alias Vec3i = Vec3!(int); 351 alias Vec3u = Vec3!(uint);