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