1 /** 2 Vector used for 2D Math. 3 4 Copyright: 5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017-2018 Godot-D contributors 8 9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 10 11 12 */ 13 module godot.core.vector2; 14 15 import godot.core.defs; 16 17 import std.math; 18 19 private bool isValidSwizzle(dstring s) 20 { 21 import std.algorithm : canFind; 22 if(s.length != 2 && s.length != 3) return false; 23 foreach(dchar c; s) 24 { 25 if(!"xyn".canFind(c)) return false; 26 } 27 return true; 28 } 29 30 /** 31 2-element structure that can be used to represent positions in 2d-space, or any other pair of numeric values. 32 */ 33 struct Vector2 34 { 35 @nogc nothrow: 36 37 union 38 { 39 struct 40 { 41 union 42 { 43 real_t x = 0.0; /// 44 real_t width; /// 45 } 46 union 47 { 48 real_t y = 0.0; /// 49 real_t height; /// 50 } 51 } 52 53 real_t[2] coord; 54 } 55 56 import std.algorithm : count; 57 /++ 58 Swizzle the vector with x, y, or n. Pass floats as args for any n's; if 59 there are more n's than args, the last arg is used for the rest. If no args 60 are passed at all, 0.0 is used for each n. 61 62 The swizzle must be 2 or 3 characters, as Godot only has Vector2/3. 63 +/ 64 auto opDispatch(string swizzle, size_t nArgCount)(float[nArgCount] nArgs...) const 65 if(swizzle.isValidSwizzle && nArgCount <= swizzle.count('n')) 66 { 67 import godot.core.vector3; 68 import std.algorithm : min, count; 69 static if(swizzle.length == 2) Vector2 ret = void; 70 else Vector3 ret = void; 71 /// how many n's already appeared before ci, which equals the index into nArgs for the n at ci 72 enum ni(size_t ci) = min(nArgCount-1, swizzle[0..ci].count('n')); 73 static foreach(ci, c; swizzle) 74 { 75 static if(c == 'n') 76 { 77 static if(nArgCount == 0) ret.coord[ci] = 0f; 78 else static if(ni!ci >= nArgCount) ret.coord[ci] = nArgs[nArgCount-1]; 79 else ret.coord[ci] = nArgs[ni!ci]; 80 } 81 else ret.coord[ci] = mixin([c]); 82 } 83 return ret; 84 } 85 86 this(real_t x, real_t y) 87 { 88 this.x = x; 89 this.y = y; 90 } 91 92 this(real_t[2] coord) 93 { 94 this.coord = coord; 95 } 96 97 this(in Vector2 b) 98 { 99 this.x = b.x; 100 this.y = b.y; 101 } 102 103 void opAssign(in Vector2 b) 104 { 105 this.x = b.x; 106 this.y = b.y; 107 } 108 109 ref real_t opIndex(int axis) return 110 { 111 return axis?y:x; 112 } 113 const(real_t) opIndex(int axis) const 114 { 115 return axis?y:x; 116 } 117 118 Vector2 opBinary(string op)(in Vector2 other) const 119 if(op=="+" || op=="-" || op=="*" || op=="/") 120 { 121 Vector2 ret; 122 ret.x = mixin("x "~op~"other.x"); 123 ret.y = mixin("y "~op~"other.y"); 124 return ret; 125 } 126 void opOpAssign(string op)(in Vector2 other) 127 if(op=="+" || op=="-" || op=="*" || op=="/") 128 { 129 x = mixin("x "~op~"other.x"); 130 y = mixin("y "~op~"other.y"); 131 } 132 133 Vector2 opUnary(string op : "-")() 134 { 135 return Vector2(-x, -y); 136 } 137 138 Vector2 opBinary(string op)(in real_t scalar) const 139 if(op=="*" || op=="/") 140 { 141 Vector2 ret; 142 ret.x = mixin("x "~op~" scalar"); 143 ret.y = mixin("y "~op~" scalar"); 144 return ret; 145 } 146 Vector2 opBinaryRight(string op)(in real_t scalar) const 147 if(op=="*") 148 { 149 Vector2 ret; 150 ret.x = mixin("x "~op~" scalar"); 151 ret.y = mixin("y "~op~" scalar"); 152 return ret; 153 } 154 void opOpAssign(string op)(in real_t scalar) 155 if(op=="*" || op=="/") 156 { 157 x = mixin("x "~op~" scalar"); 158 y = mixin("y "~op~" scalar"); 159 } 160 161 int opCmp(in Vector2 other) const 162 { 163 import std.algorithm.comparison; 164 import std.range; 165 return cmp(only(x,y), only(other.x, other.y)); 166 } 167 168 real_t aspect() const 169 { 170 return width/height; 171 } 172 173 void normalize() 174 { 175 real_t l = x*x + y*y; 176 if (l != 0) 177 { 178 l = sqrt(l); 179 x /= l; 180 y /= l; 181 } 182 } 183 184 Vector2 normalized() const 185 { 186 Vector2 v = this; 187 v.normalize(); 188 return v; 189 } 190 191 real_t length() const 192 { 193 return sqrt(x*x + y*y); 194 } 195 real_t lengthSquared() const 196 { 197 return x*x + y*y; 198 } 199 200 real_t distanceTo(in Vector2 p_vector2) const 201 { 202 return sqrt((x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y)); 203 } 204 205 real_t distanceSquaredTo(in Vector2 p_vector2) const 206 { 207 return (x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y); 208 } 209 210 real_t angleTo(in Vector2 p_vector2) const 211 { 212 return atan2(cross(p_vector2), dot(p_vector2)); 213 } 214 215 real_t angleToPoint(in Vector2 p_vector2) const 216 { 217 return atan2(y - p_vector2.y, x-p_vector2.x); 218 } 219 220 real_t dot(in Vector2 p_other) const 221 { 222 return x * p_other.x + y * p_other.y; 223 } 224 225 real_t cross(in Vector2 p_other) const 226 { 227 return x * p_other.y - y * p_other.x; 228 } 229 230 Vector2 cross(real_t p_other) const 231 { 232 return Vector2(p_other * y, -p_other * x); 233 } 234 235 Vector2 project(in Vector2 p_vec) const 236 { 237 Vector2 v1 = p_vec; 238 Vector2 v2 = this; 239 return v2 * (v1.dot(v2) / v2.dot(v2)); 240 } 241 242 Vector2 planeProject(real_t p_d, in Vector2 p_vec) const 243 { 244 return p_vec - this * (dot(p_vec) -p_d); 245 } 246 247 Vector2 clamped(real_t p_len) const 248 { 249 real_t l = length(); 250 Vector2 v = this; 251 if (l > 0 && p_len < l) 252 { 253 v /= l; 254 v *= p_len; 255 } 256 return v; 257 } 258 259 static Vector2 linearInterpolate(in Vector2 p_a, in Vector2 p_b, real_t p_t) 260 { 261 Vector2 res=p_a; 262 res.x+= (p_t * (p_b.x-p_a.x)); 263 res.y+= (p_t * (p_b.y-p_a.y)); 264 return res; 265 } 266 267 Vector2 linearInterpolate(in Vector2 p_b,real_t p_t) const 268 { 269 Vector2 res=this; 270 res.x+= (p_t * (p_b.x-x)); 271 res.y+= (p_t * (p_b.y-y)); 272 return res; 273 274 } 275 alias lerp = linearInterpolate; 276 277 Vector2 cubicInterpolate(in Vector2 p_b,in Vector2 p_pre_a, in Vector2 p_post_b, real_t p_t) const 278 { 279 Vector2 p0=p_pre_a; 280 Vector2 p1=this; 281 Vector2 p2=p_b; 282 Vector2 p3=p_post_b; 283 284 real_t t = p_t; 285 real_t t2 = t * t; 286 real_t t3 = t2 * t; 287 288 Vector2 ret; 289 ret = ( ( p1 * 2.0) + 290 ( -p0 + p2 ) * t + 291 ( p0 * 2.0 - p1 * 5.0 + p2 * 4 - p3 ) * t2 + 292 ( -p0 + p1 * 3.0 - p2 * 3.0 + p3 ) * t3 ) * 0.5; 293 294 return ret; 295 } 296 297 298 Vector2 slide(in Vector2 p_vec) const 299 { 300 return p_vec - this * this.dot(p_vec); 301 } 302 303 Vector2 reflect(in Vector2 p_vec) const 304 { 305 return p_vec - this * this.dot(p_vec) * 2.0; 306 } 307 308 real_t angle() const 309 { 310 return atan2(y, x); 311 } 312 313 void setRotation(real_t p_radians) 314 { 315 x = cos(p_radians); 316 y = sin(p_radians); 317 } 318 319 Vector2 abs() const 320 { 321 return Vector2( fabs(x), fabs(y) ); 322 } 323 324 Vector2 rotated(real_t p_by) const 325 { 326 Vector2 v = void; 327 v.setRotation(angle() + p_by); 328 v *= length(); 329 return v; 330 } 331 332 Vector2 tangent() const 333 { 334 return Vector2(y,-x); 335 } 336 337 Vector2 floor() const 338 { 339 return Vector2(.floor(x), .floor(y)); 340 } 341 342 Vector2 snapped(in Vector2 p_by) const 343 { 344 return Vector2( 345 p_by.x != 0 ? .floor(x / p_by.x + 0.5) * p_by.x : x, 346 p_by.y != 0 ? .floor(y / p_by.y + 0.5) * p_by.y : y 347 ); 348 } 349 } 350 351 352 353