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