1 /** 2 Grimoire 3 Copyright (c) 2017 Enalye 4 5 This software is provided 'as-is', without any express or implied warranty. 6 In no event will the authors be held liable for any damages arising 7 from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute 11 it freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; 14 you must not claim that you wrote the original software. 15 If you use this software in a product, an acknowledgment 16 in the product documentation would be appreciated but 17 is not required. 18 19 2. Altered source versions must be plainly marked as such, 20 and must not be misrepresented as being the original software. 21 22 3. This notice may not be removed or altered from any source distribution. 23 */ 24 25 module core.vec2; 26 27 import std.math; 28 29 import core.vec3; 30 import core.vec4; 31 32 enum double degToRad = std.math.PI / 180.0; 33 enum double radToDeg = 180.0 / std.math.PI; 34 35 struct Vec2(T) { 36 static assert(__traits(isArithmetic, T)); 37 38 static if(__traits(isUnsigned, T)) { 39 enum one = Vec2!T(1u, 1u); 40 enum zero = Vec2!T(0u, 0u); 41 } 42 else { 43 static if(__traits(isFloating, T)) { 44 enum one = Vec2!T(1f, 1f); 45 enum half = Vec2!T(.5f, .5f); 46 enum zero = Vec2!T(0f, 0f); 47 } 48 else { 49 enum one = Vec2!T(1, 1); 50 enum zero = Vec2!T(0, 0); 51 } 52 } 53 54 T x, y; 55 56 void set(T nx, T ny) { 57 x = nx; 58 y = ny; 59 } 60 61 T distance(Vec2!T v) { 62 static if(__traits(isUnsigned, T)) 63 alias V = int; 64 else 65 alias V = T; 66 67 V px = x - v.x, 68 py = y - v.y; 69 70 static if(__traits(isFloating, T)) 71 return std.math.sqrt(px * px + py * py); 72 else 73 return cast(T)std.math.sqrt(cast(float)(px * px + py * py)); 74 } 75 76 T distanceSquared(Vec2!T v) const { 77 static if(__traits(isUnsigned, T)) 78 alias V = int; 79 else 80 alias V = T; 81 82 V px = x - v.x, 83 py = y - v.y; 84 85 return px * px + py * py; 86 } 87 88 T dot(const Vec2!T v) const { 89 return x * v.x + y * v.y; 90 } 91 92 T cross(const Vec2!T v) const { 93 static if(__traits(isUnsigned, T)) 94 return cast(int)(x * v.y) - cast(int)(y * v.x); 95 else 96 return (x * v.y) - (y * v.x); 97 } 98 99 Vec2!T normal() const { 100 return Vec2!T(-y, x); 101 } 102 103 Vec2!T reflect(const Vec2!T v) const { 104 static if(__traits(isFloating, T)) { 105 T dotNI2 = 2.0 * x * v.x + y * v.y; 106 return Vec2!T(cast(T)(x - dotNI2 * v.x), cast(T)(y - dotNI2 * v.y)); 107 } 108 else { 109 T dotNI2 = 2 * x * v.x + y * v.y; 110 static if(__traits(isUnsigned, T)) 111 return Vec2!T(cast(int)(x) - cast(int)(dotNI2 * v.x), cast(int)(y) - cast(int)(dotNI2 * v.y)); 112 else 113 return Vec2!T(x - dotNI2 * v.x, y - dotNI2 * v.y); 114 } 115 } 116 117 Vec2!T refract(const Vec2!T v, float eta) const { 118 static if(__traits(isFloating, T)) { 119 T dotNI = (x * v.x + y * v.y); 120 T k = 1.0 - eta * eta * (1.0 - dotNI * dotNI); 121 if (k < .0) 122 return Vec2!T(T.init, T.init); 123 else { 124 double s = (eta * dotNI + sqrt(k)); 125 return Vec2!T(eta * x - s * v.x, eta * y - s * v.y); 126 } 127 } 128 else { 129 float dotNI = cast(float)(x * v.x + y * v.y); 130 float k = 1.0f - eta * eta * (1.0f - dotNI * dotNI); 131 if (k < 0f) 132 return Vec2!T(T.init, T.init); 133 else { 134 float s = (eta * dotNI + sqrt(k)); 135 return Vec2!T(cast(T)(eta * x - s * v.x), cast(T)(eta * y - s * v.y)); 136 } 137 } 138 } 139 140 Vec2!T abs() const { 141 static if(__traits(isFloating, T)) 142 return Vec2!T(x < .0 ? -x : x, y < .0 ? -y : y); 143 else static if(__traits(isUnsigned, T)) 144 return Vec2!T(x < 0U ? -x : x, y < 0U ? -y : y); 145 else 146 return Vec2!T(x, y); 147 } 148 149 Vec2!T floor() const { 150 static if(__traits(isFloating, T)) 151 return Vec2!T(std.math.floor(x), std.math.floor(y)); 152 else 153 return this; 154 } 155 156 Vec2!T ceil() const { 157 static if(__traits(isFloating, T)) 158 return Vec2!T(std.math.ceil(x), std.math.ceil(y)); 159 else 160 return this; 161 } 162 163 Vec2!T round() const { 164 static if(__traits(isFloating, T)) 165 return Vec2!T(std.math.round(x), std.math.round(y)); 166 else 167 return this; 168 } 169 170 static if(__traits(isFloating, T)) { 171 T angle() const { 172 return std.math.atan2(y, x) * radToDeg; 173 } 174 175 Vec2!T rotate(T angle) { 176 T radians = angle * degToRad; 177 T px = x, py = y; 178 T c = std.math.cos(radians); 179 T s = std.math.sin(radians); 180 x = px * c - py * s; 181 y = px * s + py * c; 182 return this; 183 } 184 185 Vec2!T rotated(T angle) const { 186 T radians = angle * degToRad; 187 T c = std.math.cos(radians); 188 T s = std.math.sin(radians); 189 return Vec2f(x * c - y * s, x * s + y * c); 190 } 191 192 static Vec2!T angled(T angle) { 193 T radians = angle * degToRad; 194 return Vec2f(std.math.cos(radians), std.math.sin(radians)); 195 } 196 } 197 198 T sum() const { 199 return x + y; 200 } 201 202 T length() const { 203 static if(__traits(isFloating, T)) 204 return std.math.sqrt(x * x + y * y); 205 else 206 return cast(T)std.math.sqrt(cast(float)(x * x + y * y)); 207 } 208 209 T lengthSquared() const { 210 return x * x + y * y; 211 } 212 213 void normalize() { 214 static if(__traits(isFloating, T)) 215 T len = std.math.sqrt(x * x + y * y); 216 else 217 T len = cast(T)std.math.sqrt(cast(float)(x * x + y * y)); 218 219 x /= len; 220 y /= len; 221 } 222 223 Vec2!T normalized() const { 224 static if(__traits(isFloating, T)) 225 T len = std.math.sqrt(x * x + y * y); 226 else 227 T len = cast(T)std.math.sqrt(cast(float)(x * x + y * y)); 228 229 return Vec2!T(x / len, y / len); 230 } 231 232 Vec2!T clamp(const Vec2!T min, const Vec2!T max) const { 233 Vec2!T v = {x, y}; 234 if (v.x < min.x) v.x = min.x; 235 else if(v.x > max.x) v.x = max.x; 236 if (v.y < min.y) v.y = min.y; 237 else if (v.y > max.y) v.y = max.y; 238 return v; 239 } 240 241 Vec2!T clamp(const Vec4!T clip) const { 242 Vec2!T v = {x, y}; 243 if (v.x < clip.x) v.x = clip.x; 244 else if(v.x > clip.z) v.x = clip.z; 245 if (v.y < clip.y) v.y = clip.y; 246 else if (v.y > clip.w) v.y = clip.w; 247 return v; 248 } 249 250 bool isBetween(const Vec2!T min, const Vec2!T max) const { 251 if (x < min.x) return false; 252 else if (x > max.x) return false; 253 if (y < min.y) return false; 254 else if (y > max.y) return false; 255 return true; 256 } 257 258 static if(__traits(isFloating, T)) { 259 Vec2!T lerp(Vec2!T end, float t) const { 260 return (this * (1.0 - t)) + (end * t); 261 } 262 263 Vec2!T fit(const Vec2!T v) const { 264 Vec2!T u = {v.x / x, v.y / y}; 265 if(u.x < u.y) 266 return Vec2!T(v.x, v.x); 267 else 268 return Vec2!T(v.y, v.y); 269 } 270 } 271 272 bool opEquals(const Vec2!T v) const { 273 return (x == v.x) && (y == v.y); 274 } 275 276 Vec2!T opUnary(string op)() const { 277 return mixin("Vec2!T(" ~ op ~ " x, " ~ op ~ " y)"); 278 } 279 280 Vec2!T opBinary(string op)(const Vec2!T v) const { 281 return mixin("Vec2!T(x " ~ op ~ " v.x, y " ~ op ~ " v.y)"); 282 } 283 284 Vec2!T opBinary(string op)(T s) const { 285 return mixin("Vec2!T(x " ~ op ~ " s, y " ~ op ~ " s)"); 286 } 287 288 Vec2!T opBinaryRight(string op)(T s) const { 289 return mixin("Vec2!T(s " ~ op ~ " x, s " ~ op ~ " y)"); 290 } 291 292 Vec2!T opOpAssign(string op)(Vec2!T v) { 293 mixin("x = x" ~ op ~ "v.x;y = y" ~ op ~ "v.y;"); 294 return this; 295 } 296 297 Vec2!T opOpAssign(string op)(T s) { 298 mixin("x = x" ~ op ~ "s;y = y" ~ op ~ "s;"); 299 return this; 300 } 301 302 Vec2!U opCast(V: Vec2!U, U)() const { 303 return V(cast(U)x, cast(U)y); 304 } 305 } 306 307 alias Vec2f = Vec2!(float); 308 alias Vec2i = Vec2!(int); 309 alias Vec2u = Vec2!(uint);