1 /** 2 Vector struct, which performs basic 3D vector math operations. 3 4 Copyright: 5 Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017-2018 Godot-D contributors 8 Copyright (c) 2022-2022 Godot-DLang contributors 9 10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 11 12 13 */ 14 module godot.vector3; 15 16 import godot.abi.core; 17 import godot.abi.types; 18 import godot.basis; 19 import godot.string; 20 21 import std.math; 22 23 private bool isValidSwizzle(dstring s) { 24 import std.algorithm : canFind; 25 26 if (s.length != 2 && s.length != 3) 27 return false; 28 foreach (dchar c; s) { 29 if (!"xyzn".canFind(c)) 30 return false; 31 } 32 return true; 33 } 34 35 /** 36 Vector3 is one of the core classes of the engine, and includes several built-in helper functions to perform basic vector math operations. 37 */ 38 struct Vector3 { 39 @nogc nothrow: 40 41 enum Axis { 42 x, 43 y, 44 z 45 } 46 47 union { 48 struct { 49 real_t x = 0; /// 50 real_t y = 0; /// 51 real_t z = 0; /// 52 } 53 54 real_t[3] coord; 55 } 56 57 import std.algorithm : count; 58 59 /++ 60 Swizzle the vector with x, y, z, or n. Pass floats as args for any n's; if 61 there are more n's than args, the last arg is used for the rest. If no args 62 are passed at all, 0.0 is used for each n. 63 64 The swizzle must be 2 or 3 characters, as Godot only has Vector2/3. 65 +/ 66 auto opDispatch(string swizzle, size_t nArgCount)(float[nArgCount] nArgs...) const 67 if (swizzle.isValidSwizzle && nArgCount <= swizzle.count('n')) { 68 import godot.vector3; 69 import std.algorithm : min, count; 70 71 static if (swizzle.length == 2) 72 Vector2 ret = void; 73 else 74 Vector3 ret = void; 75 /// how many n's already appeared before ci, which equals the index into nArgs for the n at ci 76 enum ni(size_t ci) = min(nArgCount - 1, swizzle[0 .. ci].count('n')); 77 static foreach (ci, c; swizzle) { 78 static if (c == 'n') { 79 static if (nArgCount == 0) 80 ret.coord[ci] = 0f; 81 else static if (ni!ci >= nArgCount) 82 ret.coord[ci] = nArgs[nArgCount - 1]; 83 else 84 ret.coord[ci] = nArgs[ni!ci]; 85 } else 86 ret.coord[ci] = mixin([c]); 87 } 88 return ret; 89 } 90 91 this(real_t x, real_t y, real_t z) { 92 this.x = x; 93 this.y = y; 94 this.z = z; 95 } 96 97 this(real_t[3] coord) { 98 this.coord = coord; 99 } 100 101 this(in Vector3 b) { 102 this.x = b.x; 103 this.y = b.y; 104 this.z = b.z; 105 } 106 107 void opAssign(in Vector3 b) { 108 this.x = b.x; 109 this.y = b.y; 110 this.z = b.z; 111 } 112 113 const(real_t) opIndex(int p_axis) const { 114 return coord[p_axis]; 115 } 116 117 ref real_t opIndex(int p_axis) return { 118 return coord[p_axis]; 119 } 120 121 Vector3 opBinary(string op)(in Vector3 other) const 122 if (op == "+" || op == "-" || op == "*" || op == "/") { 123 Vector3 ret; 124 ret.x = mixin("x " ~ op ~ "other.x"); 125 ret.y = mixin("y " ~ op ~ "other.y"); 126 ret.z = mixin("z " ~ op ~ "other.z"); 127 return ret; 128 } 129 130 void opOpAssign(string op)(in Vector3 other) 131 if (op == "+" || op == "-" || op == "*" || op == "/") { 132 x = mixin("x " ~ op ~ "other.x"); 133 y = mixin("y " ~ op ~ "other.y"); 134 z = mixin("z " ~ op ~ "other.z"); 135 } 136 137 Vector3 opUnary(string op : "-")() { 138 return Vector3(-x, -y, -z); 139 } 140 141 Vector3 opBinary(string op)(in real_t scalar) const 142 if (op == "*" || op == "/") { 143 Vector3 ret; 144 ret.x = mixin("x " ~ op ~ " scalar"); 145 ret.y = mixin("y " ~ op ~ " scalar"); 146 ret.z = mixin("z " ~ op ~ " scalar"); 147 return ret; 148 } 149 150 Vector3 opBinaryRight(string op)(in real_t scalar) const 151 if (op == "*") { 152 Vector3 ret; 153 ret.x = mixin("x " ~ op ~ " scalar"); 154 ret.y = mixin("y " ~ op ~ " scalar"); 155 ret.z = mixin("z " ~ op ~ " scalar"); 156 return ret; 157 } 158 159 void opOpAssign(string op)(in real_t scalar) if (op == "*" || op == "/") { 160 x = mixin("x " ~ op ~ " scalar"); 161 y = mixin("y " ~ op ~ " scalar"); 162 z = mixin("z " ~ op ~ " scalar"); 163 } 164 165 int opCmp(in Vector3 other) const { 166 import std.algorithm.comparison; 167 168 return cmp(this.coord[], other.coord[]); 169 } 170 171 Vector3 abs() const { 172 return Vector3(fabs(x), fabs(y), fabs(z)); 173 } 174 175 Vector3 ceil() const { 176 return Vector3(.ceil(x), .ceil(y), .ceil(z)); 177 } 178 179 Vector3 cross(in Vector3 b) const { 180 return Vector3( 181 (y * b.z) - (z * b.y), 182 (z * b.x) - (x * b.z), 183 (x * b.y) - (y * b.x) 184 ); 185 } 186 187 Vector3 linearInterpolate(in Vector3 p_b, real_t p_t) const { 188 return Vector3( 189 x + (p_t * (p_b.x - x)), 190 y + (p_t * (p_b.y - y)), 191 z + (p_t * (p_b.z - z)) 192 ); 193 } 194 195 alias lerp = linearInterpolate; 196 197 Vector3 cubicInterpolate(in Vector3 b, in Vector3 pre_a, in Vector3 post_b, in real_t t) const { 198 Vector3 p0 = pre_a; 199 Vector3 p1 = this; 200 Vector3 p2 = b; 201 Vector3 p3 = post_b; 202 203 real_t t2 = t * t; 204 real_t t3 = t2 * t; 205 206 Vector3 ret; 207 ret = ((p1 * 2.0) + 208 (-p0 + p2) * t + 209 (p0 * 2.0 - p1 * 5.0 + p2 * 4 - p3) * t2 + 210 (-p0 + p1 * 3.0 - p2 * 3.0 + p3) * t3) * 0.5; 211 return ret; 212 } 213 214 real_t length() const { 215 real_t x2 = x * x; 216 real_t y2 = y * y; 217 real_t z2 = z * z; 218 219 return sqrt(x2 + y2 + z2); 220 } 221 222 real_t lengthSquared() const { 223 real_t x2 = x * x; 224 real_t y2 = y * y; 225 real_t z2 = z * z; 226 227 return x2 + y2 + z2; 228 } 229 230 real_t distanceSquaredTo(in Vector3 b) const { 231 return (b - this).length(); 232 } 233 234 real_t distanceTo(in Vector3 b) const { 235 return (b - this).lengthSquared(); 236 } 237 238 real_t dot(in Vector3 b) const { 239 return x * b.x + y * b.y + z * b.z; 240 } 241 242 Vector3 floor() const { 243 return Vector3(.floor(x), .floor(y), .floor(z)); 244 } 245 246 Vector3 inverse() const { 247 return Vector3(1.0 / x, 1.0 / y, 1.0 / z); 248 } 249 250 int maxAxis() const { 251 return (x < y) ? (y < z ? 2 : 1) : (x < z ? 2 : 0); 252 } 253 254 int minAxis() const { 255 return (x < y) ? (x < z ? 0 : 2) : (y < z ? 1 : 2); 256 } 257 258 void normalize() { 259 real_t l = length(); 260 if (l == 0) { 261 x = y = z = 0; 262 } else { 263 x /= l; 264 y /= l; 265 z /= l; 266 } 267 } 268 269 Vector3 normalized() const { 270 Vector3 v = this; 271 v.normalize(); 272 return v; 273 } 274 275 Vector3 reflect(in Vector3 by) const { 276 return by - this * this.dot(by) * 2.0; 277 } 278 279 Vector3 rotated(in Vector3 axis, in real_t phi) const { 280 Vector3 v = this; 281 v.rotate(axis, phi); 282 return v; 283 } 284 285 void rotate(in Vector3 axis, in real_t phi) { 286 this = Basis(axis, phi).xform(this); 287 } 288 289 Vector3 slide(in Vector3 by) const { 290 return by - this * this.dot(by); 291 } 292 293 void snap(real_t step) { 294 foreach (ref v; coord) 295 v = (step != 0) ? (.floor(v / step + 0.5) * step) : v; 296 } 297 298 Vector3 snapped(in real_t step) const { 299 Vector3 v = this; 300 v.snap(step); 301 return v; 302 } 303 } 304 305 struct Vector3i { 306 @nogc nothrow: 307 308 enum Axis { 309 x, 310 y, 311 z 312 } 313 314 union { 315 struct { 316 int x = 0; /// 317 int y = 0; /// 318 int z = 0; /// 319 } 320 321 int[3] coord; 322 } 323 324 this(int x, int y, int z) { 325 this.x = x; 326 this.y = y; 327 this.z = z; 328 } 329 330 this(int[3] coord) { 331 this.coord = coord; 332 } 333 334 this(in Vector3i b) { 335 this.x = b.x; 336 this.y = b.y; 337 this.z = b.z; 338 } 339 340 void opAssign(in Vector3i b) { 341 this.x = b.x; 342 this.y = b.y; 343 this.z = b.z; 344 } 345 346 void opAssign(in godot_vector3i b) { 347 this.x = b._opaque[0]; 348 this.y = b._opaque[1]; 349 this.z = b._opaque[2]; 350 } 351 352 const(godot_int) opIndex(int p_axis) const { 353 return coord[p_axis]; 354 } 355 356 ref godot_int opIndex(int p_axis) return { 357 return coord[p_axis]; 358 } 359 360 Vector3i opBinary(string op)(in Vector3i other) const 361 if (op == "+" || op == "-" || op == "*" || op == "/") { 362 Vector3i ret; 363 ret.x = mixin("x " ~ op ~ "other.x"); 364 ret.y = mixin("y " ~ op ~ "other.y"); 365 ret.z = mixin("z " ~ op ~ "other.z"); 366 return ret; 367 } 368 369 void opOpAssign(string op)(in Vector3i other) 370 if (op == "+" || op == "-" || op == "*" || op == "/") { 371 x = mixin("x " ~ op ~ "other.x"); 372 y = mixin("y " ~ op ~ "other.y"); 373 z = mixin("z " ~ op ~ "other.z"); 374 } 375 376 Vector3i opUnary(string op : "-")() { 377 return Vector3i(-x, -y, -z); 378 } 379 380 Vector3i opBinary(string op)(in godot_int scalar) const 381 if (op == "*" || op == "/") { 382 Vector3i ret; 383 ret.x = mixin("x " ~ op ~ " scalar"); 384 ret.y = mixin("y " ~ op ~ " scalar"); 385 ret.z = mixin("z " ~ op ~ " scalar"); 386 return ret; 387 } 388 389 Vector3i opBinaryRight(string op)(in godot_int scalar) const 390 if (op == "*") { 391 Vector3i ret; 392 ret.x = mixin("x " ~ op ~ " scalar"); 393 ret.y = mixin("y " ~ op ~ " scalar"); 394 ret.z = mixin("z " ~ op ~ " scalar"); 395 return ret; 396 } 397 398 void opOpAssign(string op)(in godot_int scalar) if (op == "*" || op == "/") { 399 x = mixin("x " ~ op ~ " scalar"); 400 y = mixin("y " ~ op ~ " scalar"); 401 z = mixin("z " ~ op ~ " scalar"); 402 } 403 404 int opCmp(in Vector3i other) const { 405 import std.algorithm.comparison; 406 407 return cmp(this.coord[], other.coord[]); 408 } 409 410 int maxAxis() const { 411 return (x < y) ? (y < z ? 2 : 1) : (x < z ? 2 : 0); 412 } 413 414 int minAxis() const { 415 return (x < y) ? (x < z ? 0 : 2) : (y < z ? 1 : 2); 416 } 417 418 Vector3i abs() const { 419 return Vector3i(.abs(x), .abs(y), .abs(z)); 420 } 421 422 void zero() { 423 coord[] = 0; 424 } 425 426 int64_t length_squared() const { 427 return (x * cast(int64_t) x) + (y * cast(int64_t) y) + (z * cast(int64_t) z); 428 } 429 430 double length() const { 431 return sqrt(cast(double) length_squared()); 432 } 433 434 Vector3i sign() const { 435 //static int isign(int i) { return i == 0 ? 0 : (i < 0 ? -1 : 1); } 436 return Vector3i(sgn(x), sgn(y), sgn(z)); 437 } 438 439 Vector3i clamp(in Vector3i p_min, in Vector3i p_max) const { 440 import std.algorithm.comparison : _clamp = clamp; // template looses priority to local symbol 441 return Vector3i(_clamp(x, p_min.x, p_max.x), _clamp(y, p_min.y, p_max.y), _clamp(z, p_min.z, p_max 442 .z)); 443 } 444 445 Vector3 opCast(Vector3)() const { 446 return Vector3(x, y, z); 447 } 448 }