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