From 3ee947ced94ce24463838e0c587cb076ac81ec98 Mon Sep 17 00:00:00 2001 From: pommicket Date: Sun, 1 Jan 2023 23:03:25 -0500 Subject: move math.c functions to util.c --- main.c | 2 +- math.c | 856 ----------------------------------------------------------------- ui.c | 2 +- util.c | 803 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ util.h | 161 +++++++++++++ 5 files changed, 966 insertions(+), 858 deletions(-) delete mode 100644 math.c diff --git a/main.c b/main.c index 4dd52af..9ae5b68 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,6 @@ /* @TODO: +- rename v[234] to vec[234] - handle multiple symbols with same name in go-to-definition menu - better non-error window/showMessage(Request) - document lsp.h and lsp.c. @@ -95,7 +96,6 @@ no_warn_end #error "Unrecognized operating system." #endif -#include "math.c" #if _WIN32 #include "process-win.c" #elif __unix__ diff --git a/math.c b/math.c deleted file mode 100644 index db8e4f2..0000000 --- a/math.c +++ /dev/null @@ -1,856 +0,0 @@ -#ifdef MATH_GL -#undef MATH_GL -#define MATH_GL 1 -#endif - -#include -#include -#include - -#define PIf 3.14159265358979f -#define HALF_PIf 1.5707963267948966f -#define TAUf 6.283185307179586f -#define SQRT2f 1.4142135623730951f -#define HALF_SQRT2f 0.7071067811865476f -#define SQRT3f 1.7320508075688772f -#define HALF_SQRT3f 0.8660254037844386f - -#include - -static inline float degrees(float r) { - return r * (180.0f / PIf); -} -static inline float radians(float r) { - return r * (PIf / 180.f); -} - -// map x from the interval [0, 1] to the interval [a, b]. does NOT clamp. -static inline float lerpf(float x, float a, float b) { - return x * (b-a) + a; -} - -// opposite of lerp; map x from the interval [a, b] to the interval [0, 1]. does NOT clamp. -static inline float normf(float x, float a, float b) { - return (x-a) / (b-a); -} - -static inline float clampf(float x, float a, float b) { - if (x < a) return a; - if (x > b) return b; - return x; -} - -static inline int clampi(int x, int a, int b) { - if (x < a) return a; - if (x > b) return b; - return x; -} - -static inline i16 clamp_i16(i16 x, i16 a, i16 b) { - if (x < a) return a; - if (x > b) return b; - return x; -} - -static inline u16 clamp_u16(u16 x, u16 a, u16 b) { - if (x < a) return a; - if (x > b) return b; - return x; -} - -static inline i32 clamp_i32(i32 x, i32 a, i32 b) { - if (x < a) return a; - if (x > b) return b; - return x; -} - -static inline u32 clamp_u32(u32 x, u32 a, u32 b) { - if (x < a) return a; - if (x > b) return b; - return x; -} - -static inline u8 ndigits_u64(u64 x) { - u8 ndigits = 1; - while (x > 9) { - x /= 10; - ++ndigits; - } - return ndigits; -} - -// remap x from the interval [from_a, from_b] to the interval [to_a, to_b], NOT clamping if x is outside the "from" interval. -static inline float remapf(float x, float from_a, float from_b, float to_a, float to_b) { - float pos = (x - from_a) / (from_b - from_a); - return lerpf(pos, to_a, to_b); -} - -static inline float minf(float a, float b) { - return a < b ? a : b; -} - -static inline float maxf(float a, float b) { - return a > b ? a : b; -} - -static inline double maxd(double a, double b) { - return a > b ? a : b; -} - -static inline double mind(double a, double b) { - return a < b ? a : b; -} - -static inline u32 min_u32(u32 a, u32 b) { - return a < b ? a : b; -} - -static inline u32 max_u32(u32 a, u32 b) { - return a > b ? a : b; -} - -// set *a to the minimum of *a and *b, and *b to the maximum -static inline void sort2_u32(u32 *a, u32 *b) { - u32 x = *a, y = *b; - if (x > y) { - *a = y; - *b = x; - } -} - -static inline i32 min_i32(i32 a, i32 b) { - return a < b ? a : b; -} - -static inline i32 max_i32(i32 a, i32 b) { - return a > b ? a : b; -} - -static inline u64 min_u64(u64 a, u64 b) { - return a < b ? a : b; -} - -static inline u64 max_u64(u64 a, u64 b) { - return a > b ? a : b; -} - -static inline i64 min_i64(i64 a, i64 b) { - return a < b ? a : b; -} - -static inline i64 max_i64(i64 a, i64 b) { - return a > b ? a : b; -} - -static inline i64 mod_i64(i64 a, i64 b) { - i64 ret = a % b; - if (ret < 0) ret += b; - return ret; -} -static inline i32 mod_i32(i32 a, i32 b) { - i32 ret = a % b; - if (ret < 0) ret += b; - return ret; -} - -static inline i64 abs_i64(i64 x) { - return x < 0 ? -x : +x; -} - -static inline i64 sgn_i64(i64 x) { - if (x < 0) return -1; - if (x > 0) return +1; - return 0; -} - -static inline float sgnf(float x) { - if (x < 0) return -1; - if (x > 0) return +1; - return 0; -} - -static inline float smoothstepf(float x) { - if (x <= 0) return 0; - if (x >= 1) return 1; - return x * x * (3 - 2 * x); -} - -static inline float randf(void) { - return (float)rand() / (float)((ulong)RAND_MAX + 1); -} - -static float rand_gauss(void) { - // https://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution - float U, V; - do - U = randf(), V = randf(); - while (U == 0 || V == 0); - return sqrtf(-2 * logf(U)) * cosf(TAUf * V); -} - -static u32 rand_u32(void) { - return ((u32)rand() & 0xfff) - | ((u32)rand() & 0xfff) << 12 - | ((u32)rand() & 0xff) << 24; -} - -static float rand_uniform(float from, float to) { - return lerpf(randf(), from, to); -} - -static float sigmoidf(float x) { - return 1.0f / (1.0f + expf(-x)); -} - -// returns ⌈x/y⌉ (x/y rounded up) -static i32 ceildivi32(i32 x, i32 y) { - if (y < 0) { - // negating both operands doesn't change the answer - x = -x; - y = -y; - } - if (x < 0) { - // truncation is the same as ceiling for negative numbers - return x / y; - } else { - return (x + (y-1)) / y; - } -} - -typedef struct { - float x, y; -} v2; - -static v2 const v2_zero = {0, 0}; -static v2 V2(float x, float y) { - v2 v; - v.x = x; - v.y = y; - return v; -} - -static v2 v2_add(v2 a, v2 b) { - return V2(a.x + b.x, a.y + b.y); -} - -static v2 v2_add_const(v2 a, float c) { - return V2(a.x + c, a.y + c); -} - -static v2 v2_sub(v2 a, v2 b) { - return V2(a.x - b.x, a.y - b.y); -} - -static v2 v2_scale(v2 v, float s) { - return V2(v.x * s, v.y * s); -} - -static v2 v2_mul(v2 a, v2 b) { - return V2(a.x * b.x, a.y * b.y); -} - -static v2 v2_clamp(v2 x, v2 a, v2 b) { - return V2(clampf(x.x, a.x, b.x), clampf(x.y, a.y, b.y)); -} - -static float v2_dot(v2 a, v2 b) { - return a.x * b.x + a.y * b.y; -} - -static float v2_len(v2 v) { - return sqrtf(v2_dot(v, v)); -} - -static v2 v2_lerp(float x, v2 a, v2 b) { - return V2(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y)); -} - -// rotate v theta radians counterclockwise -static v2 v2_rotate(v2 v, float theta) { - float c = cosf(theta), s = sinf(theta); - return V2( - c * v.x - s * v.y, - s * v.x + c * v.y - ); -} - -static v2 v2_normalize(v2 v) { - float len = v2_len(v); - float mul = len == 0.0f ? 1.0f : 1.0f/len; - return v2_scale(v, mul); -} - -static float v2_dist(v2 a, v2 b) { - return v2_len(v2_sub(a, b)); -} - -static float v2_dist_squared(v2 a, v2 b) { - v2 diff = v2_sub(a, b); - return v2_dot(diff, diff); -} - -static void v2_print(v2 v) { - printf("(%f, %f)\n", v.x, v.y); -} - -static v2 v2_rand_unit(void) { - float theta = rand_uniform(0, TAUf); - return V2(cosf(theta), sinf(theta)); -} - -static v2 v2_polar(float r, float theta) { - return V2(r * cosf(theta), r * sinf(theta)); -} - -typedef struct { - float x, y, z; -} v3; - -static v3 const v3_zero = {0, 0, 0}; - -static v3 V3(float x, float y, float z) { - v3 v; - v.x = x; - v.y = y; - v.z = z; - return v; -} - -static v3 v3_from_v2(v2 v) { - return V3(v.x, v.y, 0); -} - -static v3 v3_add(v3 a, v3 b) { - return V3(a.x + b.x, a.y + b.y, a.z + b.z); -} - -static v3 v3_sub(v3 a, v3 b) { - return V3(a.x - b.x, a.y - b.y, a.z - b.z); -} - -static v3 v3_scale(v3 v, float s) { - return V3(v.x * s, v.y * s, v.z * s); -} - -static v3 v3_lerp(float x, v3 a, v3 b) { - return V3(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y), lerpf(x, a.z, b.z)); -} - -static float v3_dot(v3 u, v3 v) { - return u.x*v.x + u.y*v.y + u.z*v.z; -} - -static v3 v3_cross(v3 u, v3 v) { - v3 prod = V3(u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x); - return prod; -} - -static float v3_len(v3 v) { - return sqrtf(v3_dot(v, v)); -} - -static float v3_dist(v3 a, v3 b) { - return v3_len(v3_sub(a, b)); -} - -static float v3_dist_squared(v3 a, v3 b) { - v3 diff = v3_sub(a, b); - return v3_dot(diff, diff); -} - -static v3 v3_normalize(v3 v) { - float len = v3_len(v); - float mul = len == 0.0f ? 1.0f : 1.0f/len; - return v3_scale(v, mul); -} - -static v2 v3_xy(v3 v) { - return V2(v.x, v.y); -} - -// a point on a unit sphere -static v3 v3_on_sphere(float yaw, float pitch) { - return V3(cosf(yaw) * cosf(pitch), sinf(pitch), sinf(yaw) * cosf(pitch)); -} - -static void v3_print(v3 v) { - printf("(%f, %f, %f)\n", v.x, v.y, v.z); -} - -static v3 v3_rand(void) { - return V3(randf(), randf(), randf()); -} - -static v3 v3_rand_unit(void) { - /* - monte carlo method - keep generating random points in cube of radius 1 (width 2) centered at origin, - until you get a point in the unit sphere, then extend it to find the point lying - on the sphere. - */ - while (1) { - v3 v = V3(rand_uniform(-1.0f, +1.0f), rand_uniform(-1.0f, +1.0f), rand_uniform(-1.0f, +1.0f)); - float dist_squared_to_origin = v3_dot(v, v); - if (dist_squared_to_origin <= 1 && dist_squared_to_origin != 0.0f) { - return v3_scale(v, 1.0f / sqrtf(dist_squared_to_origin)); - } - } - return V3(0, 0, 0); -} - -typedef struct { - float x, y, z, w; -} v4; - -static v4 const v4_zero = {0, 0, 0, 0}; - -static v4 V4(float x, float y, float z, float w) { - v4 v; - v.x = x; - v.y = y; - v.z = z; - v.w = w; - return v; -} - -static v4 v4_add(v4 a, v4 b) { - return V4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); -} - -static v4 v4_sub(v4 a, v4 b) { - return V4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); -} - -static v4 v4_scale(v4 v, float s) { - return V4(v.x * s, v.y * s, v.z * s, v.w * s); -} - -static v4 v4_scale_xyz(v4 v, float s) { - return V4(v.x * s, v.y * s, v.z * s, v.w); -} - -static v4 v4_lerp(float x, v4 a, v4 b) { - return V4(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y), lerpf(x, a.z, b.z), lerpf(x, a.w, b.w)); -} - -static float v4_dot(v4 u, v4 v) { - return u.x*v.x + u.y*v.y + u.z*v.z + u.w*v.w; -} - -// create a new vector by multiplying the respective components of u and v -static v4 v4_mul(v4 u, v4 v) { - return V4(u.x * v.x, u.y * v.y, u.z * v.z, u.w * v.w); -} - -static float v4_len(v4 v) { - return sqrtf(v4_dot(v, v)); -} - -static v4 v4_normalize(v4 v) { - float len = v4_len(v); - float mul = len == 0.0f ? 1.0f : 1.0f/len; - return v4_scale(v, mul); -} - -static v3 v4_xyz(v4 v) { - return V3(v.x, v.y, v.z); -} - -static v4 v4_rand(void) { - return V4(randf(), randf(), randf(), randf()); -} - -static void v4_print(v4 v) { - printf("(%f, %f, %f, %f)\n", v.x, v.y, v.z, v.w); -} - -typedef struct { - double x, y; -} v2d; - -static v2d V2D(double x, double y) { - v2d v; - v.x = x; - v.y = y; - return v; -} - -// matrices are column-major, because that's what they are in OpenGL -typedef struct { - float e[16]; -} m4; - -static m4 const m4_identity = {{ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 -}}; - -static void m4_print(m4 m) { - int i; - for (i = 0; i < 4; ++i) - printf("[ %f %f %f %f ]\n", m.e[i], m.e[i+4], m.e[i+8], m.e[i+12]); - printf("\n"); -} - -static m4 M4( - float a, float b, float c, float d, - float e, float f, float g, float h, - float i, float j, float k, float l, - float m, float n, float o, float p) { - m4 ret; - float *x = ret.e; - x[0] = a; x[4] = b; x[ 8] = c; x[12] = d; - x[1] = e; x[5] = f; x[ 9] = g; x[13] = h; - x[2] = i; x[6] = j; x[10] = k; x[14] = l; - x[3] = m; x[7] = n; x[11] = o; x[15] = p; - return ret; -} - -// see https://en.wikipedia.org/wiki/Rotation_matrix#General_rotations -static m4 m4_yaw(float yaw) { - float c = cosf(yaw), s = sinf(yaw); - return M4( - c, 0, -s, 0, - 0, 1, 0, 0, - s, 0, c, 0, - 0, 0, 0, 1 - ); -} - -static m4 m4_pitch(float pitch) { - float c = cosf(pitch), s = sinf(pitch); - return M4( - 1, 0, 0, 0, - 0, c, -s, 0, - 0, s, c, 0, - 0, 0, 0, 1 - ); -} - -// https://en.wikipedia.org/wiki/Translation_(geometry) -static m4 m4_translate(v3 t) { - return M4( - 1, 0, 0, t.x, - 0, 1, 0, t.y, - 0, 0, 1, t.z, - 0, 0, 0, 1 - ); -} - -// multiply m by [v.x, v.y, v.z, 1] -static v3 m4_mul_v3(m4 m, v3 v) { - return v3_add(v3_scale(V3(m.e[0], m.e[1], m.e[2]), v.x), v3_add(v3_scale(V3(m.e[4], m.e[5], m.e[6]), v.y), - v3_add(v3_scale(V3(m.e[8], m.e[9], m.e[10]), v.z), V3(m.e[12], m.e[13], m.e[14])))); -} - -/* -4x4 perspective matrix. -fov - field of view in radians, aspect - width:height aspect ratio, z_near/z_far - clipping planes -math stolen from gluPerspective (https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml) -*/ -static m4 m4_perspective(float fov, float aspect, float z_near, float z_far) { - float f = 1.0f / tanf(fov / 2.0f); - return M4( - f/aspect, 0, 0, 0, - 0, f, 0, 0, - 0, 0, (z_far+z_near) / (z_near-z_far), (2.0f*z_far*z_near) / (z_near-z_far), - 0, 0, -1, 0 - ); -} - -// windows.h defines near and far, so let's not use those -static m4 m4_ortho(float left, float right, float bottom, float top, float near_, float far_) { - float tx = -(right + left)/(right - left); - float ty = -(top + bottom)/(top - bottom); - float tz = -(far_ + near_)/(far_ - near_); - return M4( - 2.0f / (right - left), 0, 0, tx, - 0, 2.0f / (top - bottom), 0, ty, - 0, 0, -2.0f / (far_ - near_), tz, - 0, 0, 0, 1 - ); -} - - -static m4 m4_mul(m4 a, m4 b) { - m4 prod = {0}; - int i, j; - float *x = prod.e; - for (i = 0; i < 4; ++i) { - for (j = 0; j < 4; ++j, ++x) { - float *as = &a.e[j]; - float *bs = &b.e[4*i]; - *x = as[0]*bs[0] + as[4]*bs[1] + as[8]*bs[2] + as[12]*bs[3]; - } - } - return prod; -} - -static m4 m4_inv(m4 mat) { - m4 ret; - float *inv = ret.e; - float *m = mat.e; - - inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; - inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; - inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; - inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; - inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; - inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; - inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; - inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; - inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; - inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; - inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; - inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; - inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; - inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; - inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; - inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; - - float det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; - - if (det == 0) { - memset(inv, 0, sizeof *inv); - } else { - det = 1 / det; - - for (int i = 0; i < 16; i++) - inv[i] *= det; - } - - return ret; -} -typedef struct { - int x, y; -} v2i; - -static v2i V2I(int x, int y) { - v2i v; - v.x = x; - v.y = y; - return v; -} - -static void rgba_u32_to_floats(u32 rgba, float floats[4]) { - floats[0] = (float)((rgba >> 24) & 0xFF) / 255.f; - floats[1] = (float)((rgba >> 16) & 0xFF) / 255.f; - floats[2] = (float)((rgba >> 8) & 0xFF) / 255.f; - floats[3] = (float)((rgba >> 0) & 0xFF) / 255.f; -} - -static v4 rgba_u32_to_v4(u32 rgba) { - float c[4]; - rgba_u32_to_floats(rgba, c); - return V4(c[0], c[1], c[2], c[3]); -} - -static u32 rgba_v4_to_u32(v4 color) { - return (u32)(color.x * 255) << 24 - | (u32)(color.y * 255) << 16 - | (u32)(color.z * 255) << 8 - | (u32)(color.w * 255); -} - -// returns average of red green and blue components of color -static float rgba_brightness(u32 color) { - u8 r = (u8)(color >> 24), g = (u8)(color >> 16), b = (u8)(color >> 8); - return ((float)r+(float)g+(float)b) * (1.0f / 3); -} - -static bool rect_contains_point_v2(v2 pos, v2 size, v2 point) { - float x1 = pos.x, y1 = pos.y, x2 = pos.x + size.x, y2 = pos.y + size.y, - x = point.x, y = point.y; - return x >= x1 && x < x2 && y >= y1 && y < y2; -} - -static bool centered_rect_contains_point(v2 center, v2 size, v2 point) { - return rect_contains_point_v2(v2_sub(center, v2_scale(size, 0.5f)), size, point); -} - -typedef struct { - v2 pos, size; -} Rect; - -static Rect rect(v2 pos, v2 size) { - Rect r; - r.pos = pos; - r.size = size; - return r; -} - -static Rect rect_endpoints(v2 e1, v2 e2) { - Rect r; - r.pos = e1; - r.size = v2_sub(e2, e1); - return r; -} - -static Rect rect4(float x1, float y1, float x2, float y2) { - assert(x2 >= x1); - assert(y2 >= y1); - return rect(V2(x1,y1), V2(x2-x1, y2-y1)); -} - -static Rect rect_xywh(float x, float y, float w, float h) { - assert(w >= 0); - assert(h >= 0); - return rect(V2(x, y), V2(w, h)); -} - -static Rect rect_centered(v2 center, v2 size) { - Rect r; - r.pos = v2_sub(center, v2_scale(size, 0.5f)); - r.size = size; - return r; -} - -static v2 rect_center(Rect r) { - return v2_add(r.pos, v2_scale(r.size, 0.5f)); -} - -static bool rect_contains_point(Rect r, v2 point) { - return rect_contains_point_v2(r.pos, r.size, point); -} - -static Rect rect_translate(Rect r, v2 by) { - return rect(v2_add(r.pos, by), r.size); -} - -static float rect_x1(Rect r) { return r.pos.x; } -static float rect_y1(Rect r) { return r.pos.y; } -static float rect_x2(Rect r) { return r.pos.x + r.size.x; } -static float rect_y2(Rect r) { return r.pos.y + r.size.y; } -static float rect_xmid(Rect r) { return r.pos.x + r.size.x * 0.5f; } -static float rect_ymid(Rect r) { return r.pos.y + r.size.y * 0.5f; } - -static void rect_coords(Rect r, float *x1, float *y1, float *x2, float *y2) { - *x1 = r.pos.x; - *y1 = r.pos.y; - *x2 = r.pos.x + r.size.x; - *y2 = r.pos.y + r.size.y; -} - -static void rect_print(Rect r) { - printf("Position: (%f, %f), Size: (%f, %f)\n", r.pos.x, r.pos.y, r.size.x, r.size.y); -} - - -static float rects_intersect(Rect r1, Rect r2) { - if (r1.pos.x >= r2.pos.x + r2.size.x) return false; // r1 is to the right of r2 - if (r2.pos.x >= r1.pos.x + r1.size.x) return false; // r2 is to the right of r1 - if (r1.pos.y >= r2.pos.y + r2.size.y) return false; // r1 is above r2 - if (r2.pos.y >= r1.pos.y + r1.size.y) return false; // r2 is above r1 - return true; -} - -// returns whether or not there is any of the clipped rectangle left -static bool rect_clip_to_rect(Rect *clipped, Rect clipper) { - v2 start_pos = clipped->pos; - clipped->pos.x = maxf(clipped->pos.x, clipper.pos.x); - clipped->pos.y = maxf(clipped->pos.y, clipper.pos.y); - clipped->size = v2_add(clipped->size, v2_sub(start_pos, clipped->pos)); - - clipped->size.x = clampf(clipped->size.x, 0, clipper.pos.x + clipper.size.x - clipped->pos.x); - clipped->size.y = clampf(clipped->size.y, 0, clipper.pos.y + clipper.size.y - clipped->pos.y); - return clipped->size.x > 0 && clipped->size.y > 0; -} - -// removes `amount` from all sides of r -static Rect rect_shrink(Rect r, float amount) { - r.pos.x += amount; - r.pos.y += amount; - r.size.x -= 2 * amount; - r.size.y -= 2 * amount; - r.size.x = maxf(r.size.x, 0); - r.size.y = maxf(r.size.y, 0); - return r; -} - -// adds `amount` to all sides of r -static Rect rect_grow(Rect r, float amount) { - r.pos.x -= amount; - r.pos.y -= amount; - r.size.x += 2 * amount; - r.size.y += 2 * amount; - return r; -} - -static v4 color_rgba_to_hsva(v4 rgba) { - float R = rgba.x, G = rgba.y, B = rgba.z, A = rgba.w; - float M = maxf(R, maxf(G, B)); - float m = minf(R, minf(G, B)); - float C = M - m; - float H = 0; - if (C == 0) - H = 0; - else if (M == R) - H = fmodf((G - B) / C, 6); - else if (M == G) - H = (B - R) / C + 2; - else if (M == B) - H = (R - G) / C + 4; - H *= 60; - float V = M; - float S = V == 0 ? 0 : C / V; - return V4(H, S, V, A); -} - -static v4 color_hsva_to_rgba(v4 hsva) { - float H = hsva.x, S = hsva.y, V = hsva.z, A = hsva.w; - H /= 60; - float C = S * V; - float X = C * (1 - fabsf(fmodf(H, 2) - 1)); - float R, G, B; - if (H <= 1) - R=C, G=X, B=0; - else if (H <= 2) - R=X, G=C, B=0; - else if (H <= 3) - R=0, G=C, B=X; - else if (H <= 4) - R=0, G=X, B=C; - else if (H <= 5) - R=X, G=0, B=C; - else - R=C, G=0, B=X; - - float m = V-C; - R += m; - G += m; - B += m; - return V4(R, G, B, A); -} - -static u32 color_interpolate(float x, u32 color1, u32 color2) { - x = x * x * (3 - 2*x); // hermite interpolation - - v4 c1 = rgba_u32_to_v4(color1), c2 = rgba_u32_to_v4(color2); - // to make it interpolate more nicely, convert to hsv, interpolate in that space, then convert back - c1 = color_rgba_to_hsva(c1); - c2 = color_rgba_to_hsva(c2); - // v_1/2 named differently to avoid shadowing - float h1 = c1.x, s1 = c1.y, v_1 = c1.z, a1 = c1.w; - float h2 = c2.x, s2 = c2.y, v_2 = c2.z, a2 = c2.w; - - float s_out = lerpf(x, s1, s2); - float v_out = lerpf(x, v_1, v_2); - float a_out = lerpf(x, a1, a2); - - float h_out; - // because hue is on a circle, we need to make sure we take the shorter route around the circle - if (fabsf(h1 - h2) < 180) { - h_out = lerpf(x, h1, h2); - } else if (h1 > h2) { - h_out = lerpf(x, h1, h2 + 360); - } else { - h_out = lerpf(x, h1 + 360, h2); - } - h_out = fmodf(h_out, 360); - - v4 c_out = V4(h_out, s_out, v_out, a_out); - c_out = color_hsva_to_rgba(c_out); - return rgba_v4_to_u32(c_out); -} diff --git a/ui.c b/ui.c index a187d26..01e8b47 100644 --- a/ui.c +++ b/ui.c @@ -587,7 +587,7 @@ static void popup_get_rects(Ted const *ted, u32 options, Rect *popup, Rect *butt float button_height = 30; u16 nbuttons = util_popcount(options); float button_width = popup->size.x / nbuttons; - popup->size = v2_clamp(popup->size, v2_zero, V2(window_width, window_height)); + popup->size = v2_clamp(popup->size, V2(0, 0), V2(window_width, window_height)); Rect r = rect(V2(popup->pos.x, rect_y2(*popup) - button_height), V2(button_width, button_height)); if (options & POPUP_YES) { *button_yes = r; diff --git a/util.c b/util.c index 3da5f30..39d1e39 100644 --- a/util.c +++ b/util.c @@ -425,3 +425,806 @@ bool copy_file(const char *src, const char *dst) { } +#ifdef MATH_GL +#undef MATH_GL +#define MATH_GL 1 +#endif + +float degrees(float r) { + return r * (180.0f / PIf); +} +float radians(float r) { + return r * (PIf / 180.f); +} + +// map x from the interval [0, 1] to the interval [a, b]. does NOT clamp. +float lerpf(float x, float a, float b) { + return x * (b-a) + a; +} + +// opposite of lerp; map x from the interval [a, b] to the interval [0, 1]. does NOT clamp. +float normf(float x, float a, float b) { + return (x-a) / (b-a); +} + +float clampf(float x, float a, float b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + +int clampi(int x, int a, int b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + +i16 clamp_i16(i16 x, i16 a, i16 b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + +u16 clamp_u16(u16 x, u16 a, u16 b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + +i32 clamp_i32(i32 x, i32 a, i32 b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + +u32 clamp_u32(u32 x, u32 a, u32 b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + +u8 ndigits_u64(u64 x) { + u8 ndigits = 1; + while (x > 9) { + x /= 10; + ++ndigits; + } + return ndigits; +} + +// remap x from the interval [from_a, from_b] to the interval [to_a, to_b], NOT clamping if x is outside the "from" interval. +float remapf(float x, float from_a, float from_b, float to_a, float to_b) { + float pos = (x - from_a) / (from_b - from_a); + return lerpf(pos, to_a, to_b); +} + +float minf(float a, float b) { + return a < b ? a : b; +} + +float maxf(float a, float b) { + return a > b ? a : b; +} + +double maxd(double a, double b) { + return a > b ? a : b; +} + +double mind(double a, double b) { + return a < b ? a : b; +} + +u32 min_u32(u32 a, u32 b) { + return a < b ? a : b; +} + +u32 max_u32(u32 a, u32 b) { + return a > b ? a : b; +} + +// set *a to the minimum of *a and *b, and *b to the maximum +void sort2_u32(u32 *a, u32 *b) { + u32 x = *a, y = *b; + if (x > y) { + *a = y; + *b = x; + } +} + +i32 min_i32(i32 a, i32 b) { + return a < b ? a : b; +} + +i32 max_i32(i32 a, i32 b) { + return a > b ? a : b; +} + +u64 min_u64(u64 a, u64 b) { + return a < b ? a : b; +} + +u64 max_u64(u64 a, u64 b) { + return a > b ? a : b; +} + +i64 min_i64(i64 a, i64 b) { + return a < b ? a : b; +} + +i64 max_i64(i64 a, i64 b) { + return a > b ? a : b; +} + +i64 mod_i64(i64 a, i64 b) { + i64 ret = a % b; + if (ret < 0) ret += b; + return ret; +} +i32 mod_i32(i32 a, i32 b) { + i32 ret = a % b; + if (ret < 0) ret += b; + return ret; +} + +i64 abs_i64(i64 x) { + return x < 0 ? -x : +x; +} + +i64 sgn_i64(i64 x) { + if (x < 0) return -1; + if (x > 0) return +1; + return 0; +} + +float sgnf(float x) { + if (x < 0) return -1; + if (x > 0) return +1; + return 0; +} + +float smoothstepf(float x) { + if (x <= 0) return 0; + if (x >= 1) return 1; + return x * x * (3 - 2 * x); +} + +float randf(void) { + return (float)rand() / (float)((ulong)RAND_MAX + 1); +} + +float rand_gauss(void) { + // https://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution + float U, V; + do + U = randf(), V = randf(); + while (U == 0 || V == 0); + return sqrtf(-2 * logf(U)) * cosf(TAUf * V); +} + +u32 rand_u32(void) { + return ((u32)rand() & 0xfff) + | ((u32)rand() & 0xfff) << 12 + | ((u32)rand() & 0xff) << 24; +} + +float rand_uniform(float from, float to) { + return lerpf(randf(), from, to); +} + +float sigmoidf(float x) { + return 1.0f / (1.0f + expf(-x)); +} + +// returns ⌈x/y⌉ (x/y rounded up) +i32 ceildivi32(i32 x, i32 y) { + if (y < 0) { + // negating both operands doesn't change the answer + x = -x; + y = -y; + } + if (x < 0) { + // truncation is the same as ceiling for negative numbers + return x / y; + } else { + return (x + (y-1)) / y; + } +} + +v2 V2(float x, float y) { + v2 v; + v.x = x; + v.y = y; + return v; +} + +v2 v2_add(v2 a, v2 b) { + return V2(a.x + b.x, a.y + b.y); +} + +v2 v2_add_const(v2 a, float c) { + return V2(a.x + c, a.y + c); +} + +v2 v2_sub(v2 a, v2 b) { + return V2(a.x - b.x, a.y - b.y); +} + +v2 v2_scale(v2 v, float s) { + return V2(v.x * s, v.y * s); +} + +v2 v2_mul(v2 a, v2 b) { + return V2(a.x * b.x, a.y * b.y); +} + +v2 v2_clamp(v2 x, v2 a, v2 b) { + return V2(clampf(x.x, a.x, b.x), clampf(x.y, a.y, b.y)); +} + +float v2_dot(v2 a, v2 b) { + return a.x * b.x + a.y * b.y; +} + +float v2_len(v2 v) { + return sqrtf(v2_dot(v, v)); +} + +v2 v2_lerp(float x, v2 a, v2 b) { + return V2(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y)); +} + +// rotate v theta radians counterclockwise +v2 v2_rotate(v2 v, float theta) { + float c = cosf(theta), s = sinf(theta); + return V2( + c * v.x - s * v.y, + s * v.x + c * v.y + ); +} + +v2 v2_normalize(v2 v) { + float len = v2_len(v); + float mul = len == 0.0f ? 1.0f : 1.0f/len; + return v2_scale(v, mul); +} + +float v2_dist(v2 a, v2 b) { + return v2_len(v2_sub(a, b)); +} + +float v2_dist_squared(v2 a, v2 b) { + v2 diff = v2_sub(a, b); + return v2_dot(diff, diff); +} + +void v2_print(v2 v) { + printf("(%f, %f)\n", v.x, v.y); +} + +v2 v2_rand_unit(void) { + float theta = rand_uniform(0, TAUf); + return V2(cosf(theta), sinf(theta)); +} + +v2 v2_polar(float r, float theta) { + return V2(r * cosf(theta), r * sinf(theta)); +} + +v3 V3(float x, float y, float z) { + v3 v; + v.x = x; + v.y = y; + v.z = z; + return v; +} + +v3 v3_from_v2(v2 v) { + return V3(v.x, v.y, 0); +} + +v3 v3_add(v3 a, v3 b) { + return V3(a.x + b.x, a.y + b.y, a.z + b.z); +} + +v3 v3_sub(v3 a, v3 b) { + return V3(a.x - b.x, a.y - b.y, a.z - b.z); +} + +v3 v3_scale(v3 v, float s) { + return V3(v.x * s, v.y * s, v.z * s); +} + +v3 v3_lerp(float x, v3 a, v3 b) { + return V3(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y), lerpf(x, a.z, b.z)); +} + +float v3_dot(v3 u, v3 v) { + return u.x*v.x + u.y*v.y + u.z*v.z; +} + +v3 v3_cross(v3 u, v3 v) { + v3 prod = V3(u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x); + return prod; +} + +float v3_len(v3 v) { + return sqrtf(v3_dot(v, v)); +} + +float v3_dist(v3 a, v3 b) { + return v3_len(v3_sub(a, b)); +} + +float v3_dist_squared(v3 a, v3 b) { + v3 diff = v3_sub(a, b); + return v3_dot(diff, diff); +} + +v3 v3_normalize(v3 v) { + float len = v3_len(v); + float mul = len == 0.0f ? 1.0f : 1.0f/len; + return v3_scale(v, mul); +} + +v2 v3_xy(v3 v) { + return V2(v.x, v.y); +} + +// a point on a unit sphere +v3 v3_on_sphere(float yaw, float pitch) { + return V3(cosf(yaw) * cosf(pitch), sinf(pitch), sinf(yaw) * cosf(pitch)); +} + +void v3_print(v3 v) { + printf("(%f, %f, %f)\n", v.x, v.y, v.z); +} + +v3 v3_rand(void) { + return V3(randf(), randf(), randf()); +} + +v3 v3_rand_unit(void) { + /* + monte carlo method + keep generating random points in cube of radius 1 (width 2) centered at origin, + until you get a point in the unit sphere, then extend it to find the point lying + on the sphere. + */ + while (1) { + v3 v = V3(rand_uniform(-1.0f, +1.0f), rand_uniform(-1.0f, +1.0f), rand_uniform(-1.0f, +1.0f)); + float dist_squared_to_origin = v3_dot(v, v); + if (dist_squared_to_origin <= 1 && dist_squared_to_origin != 0.0f) { + return v3_scale(v, 1.0f / sqrtf(dist_squared_to_origin)); + } + } + return V3(0, 0, 0); +} + +v4 V4(float x, float y, float z, float w) { + v4 v; + v.x = x; + v.y = y; + v.z = z; + v.w = w; + return v; +} + +v4 v4_add(v4 a, v4 b) { + return V4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); +} + +v4 v4_sub(v4 a, v4 b) { + return V4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); +} + +v4 v4_scale(v4 v, float s) { + return V4(v.x * s, v.y * s, v.z * s, v.w * s); +} + +v4 v4_scale_xyz(v4 v, float s) { + return V4(v.x * s, v.y * s, v.z * s, v.w); +} + +v4 v4_lerp(float x, v4 a, v4 b) { + return V4(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y), lerpf(x, a.z, b.z), lerpf(x, a.w, b.w)); +} + +float v4_dot(v4 u, v4 v) { + return u.x*v.x + u.y*v.y + u.z*v.z + u.w*v.w; +} + +// create a new vector by multiplying the respective components of u and v +v4 v4_mul(v4 u, v4 v) { + return V4(u.x * v.x, u.y * v.y, u.z * v.z, u.w * v.w); +} + +float v4_len(v4 v) { + return sqrtf(v4_dot(v, v)); +} + +v4 v4_normalize(v4 v) { + float len = v4_len(v); + float mul = len == 0.0f ? 1.0f : 1.0f/len; + return v4_scale(v, mul); +} + +v3 v4_xyz(v4 v) { + return V3(v.x, v.y, v.z); +} + +v4 v4_rand(void) { + return V4(randf(), randf(), randf(), randf()); +} + +void v4_print(v4 v) { + printf("(%f, %f, %f, %f)\n", v.x, v.y, v.z, v.w); +} + + +v2d V2D(double x, double y) { + v2d v; + v.x = x; + v.y = y; + return v; +} + +void m4_print(m4 m) { + int i; + for (i = 0; i < 4; ++i) + printf("[ %f %f %f %f ]\n", m.e[i], m.e[i+4], m.e[i+8], m.e[i+12]); + printf("\n"); +} + +m4 M4( + float a, float b, float c, float d, + float e, float f, float g, float h, + float i, float j, float k, float l, + float m, float n, float o, float p) { + m4 ret; + float *x = ret.e; + x[0] = a; x[4] = b; x[ 8] = c; x[12] = d; + x[1] = e; x[5] = f; x[ 9] = g; x[13] = h; + x[2] = i; x[6] = j; x[10] = k; x[14] = l; + x[3] = m; x[7] = n; x[11] = o; x[15] = p; + return ret; +} + +// see https://en.wikipedia.org/wiki/Rotation_matrix#General_rotations +m4 m4_yaw(float yaw) { + float c = cosf(yaw), s = sinf(yaw); + return M4( + c, 0, -s, 0, + 0, 1, 0, 0, + s, 0, c, 0, + 0, 0, 0, 1 + ); +} + +m4 m4_pitch(float pitch) { + float c = cosf(pitch), s = sinf(pitch); + return M4( + 1, 0, 0, 0, + 0, c, -s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + ); +} + +// https://en.wikipedia.org/wiki/Translation_(geometry) +m4 m4_translate(v3 t) { + return M4( + 1, 0, 0, t.x, + 0, 1, 0, t.y, + 0, 0, 1, t.z, + 0, 0, 0, 1 + ); +} + +// multiply m by [v.x, v.y, v.z, 1] +v3 m4_mul_v3(m4 m, v3 v) { + return v3_add(v3_scale(V3(m.e[0], m.e[1], m.e[2]), v.x), v3_add(v3_scale(V3(m.e[4], m.e[5], m.e[6]), v.y), + v3_add(v3_scale(V3(m.e[8], m.e[9], m.e[10]), v.z), V3(m.e[12], m.e[13], m.e[14])))); +} + +/* +4x4 perspective matrix. +fov - field of view in radians, aspect - width:height aspect ratio, z_near/z_far - clipping planes +math stolen from gluPerspective (https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml) +*/ +m4 m4_perspective(float fov, float aspect, float z_near, float z_far) { + float f = 1.0f / tanf(fov / 2.0f); + return M4( + f/aspect, 0, 0, 0, + 0, f, 0, 0, + 0, 0, (z_far+z_near) / (z_near-z_far), (2.0f*z_far*z_near) / (z_near-z_far), + 0, 0, -1, 0 + ); +} + +// windows.h defines near and far, so let's not use those +m4 m4_ortho(float left, float right, float bottom, float top, float near_, float far_) { + float tx = -(right + left)/(right - left); + float ty = -(top + bottom)/(top - bottom); + float tz = -(far_ + near_)/(far_ - near_); + return M4( + 2.0f / (right - left), 0, 0, tx, + 0, 2.0f / (top - bottom), 0, ty, + 0, 0, -2.0f / (far_ - near_), tz, + 0, 0, 0, 1 + ); +} + + +m4 m4_mul(m4 a, m4 b) { + m4 prod = {0}; + int i, j; + float *x = prod.e; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 4; ++j, ++x) { + float *as = &a.e[j]; + float *bs = &b.e[4*i]; + *x = as[0]*bs[0] + as[4]*bs[1] + as[8]*bs[2] + as[12]*bs[3]; + } + } + return prod; +} + +m4 m4_inv(m4 mat) { + m4 ret; + float *inv = ret.e; + float *m = mat.e; + + inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; + inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; + inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; + inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; + inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; + inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; + inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; + inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; + inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; + inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; + inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; + inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; + inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; + inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; + inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; + inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; + + float det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; + + if (det == 0) { + memset(inv, 0, sizeof *inv); + } else { + det = 1 / det; + + for (int i = 0; i < 16; i++) + inv[i] *= det; + } + + return ret; +} + +v2i V2I(int x, int y) { + v2i v; + v.x = x; + v.y = y; + return v; +} + +void rgba_u32_to_floats(u32 rgba, float floats[4]) { + floats[0] = (float)((rgba >> 24) & 0xFF) / 255.f; + floats[1] = (float)((rgba >> 16) & 0xFF) / 255.f; + floats[2] = (float)((rgba >> 8) & 0xFF) / 255.f; + floats[3] = (float)((rgba >> 0) & 0xFF) / 255.f; +} + +v4 rgba_u32_to_v4(u32 rgba) { + float c[4]; + rgba_u32_to_floats(rgba, c); + return V4(c[0], c[1], c[2], c[3]); +} + +u32 rgba_v4_to_u32(v4 color) { + return (u32)(color.x * 255) << 24 + | (u32)(color.y * 255) << 16 + | (u32)(color.z * 255) << 8 + | (u32)(color.w * 255); +} + +// returns average of red green and blue components of color +float rgba_brightness(u32 color) { + u8 r = (u8)(color >> 24), g = (u8)(color >> 16), b = (u8)(color >> 8); + return ((float)r+(float)g+(float)b) * (1.0f / 3); +} + +bool rect_contains_point_v2(v2 pos, v2 size, v2 point) { + float x1 = pos.x, y1 = pos.y, x2 = pos.x + size.x, y2 = pos.y + size.y, + x = point.x, y = point.y; + return x >= x1 && x < x2 && y >= y1 && y < y2; +} + +bool centered_rect_contains_point(v2 center, v2 size, v2 point) { + return rect_contains_point_v2(v2_sub(center, v2_scale(size, 0.5f)), size, point); +} + +Rect rect(v2 pos, v2 size) { + Rect r; + r.pos = pos; + r.size = size; + return r; +} + +Rect rect_endpoints(v2 e1, v2 e2) { + Rect r; + r.pos = e1; + r.size = v2_sub(e2, e1); + return r; +} + +Rect rect4(float x1, float y1, float x2, float y2) { + assert(x2 >= x1); + assert(y2 >= y1); + return rect(V2(x1,y1), V2(x2-x1, y2-y1)); +} + +Rect rect_xywh(float x, float y, float w, float h) { + assert(w >= 0); + assert(h >= 0); + return rect(V2(x, y), V2(w, h)); +} + +Rect rect_centered(v2 center, v2 size) { + Rect r; + r.pos = v2_sub(center, v2_scale(size, 0.5f)); + r.size = size; + return r; +} + +v2 rect_center(Rect r) { + return v2_add(r.pos, v2_scale(r.size, 0.5f)); +} + +bool rect_contains_point(Rect r, v2 point) { + return rect_contains_point_v2(r.pos, r.size, point); +} + +Rect rect_translate(Rect r, v2 by) { + return rect(v2_add(r.pos, by), r.size); +} + +float rect_x1(Rect r) { return r.pos.x; } +float rect_y1(Rect r) { return r.pos.y; } +float rect_x2(Rect r) { return r.pos.x + r.size.x; } +float rect_y2(Rect r) { return r.pos.y + r.size.y; } +float rect_xmid(Rect r) { return r.pos.x + r.size.x * 0.5f; } +float rect_ymid(Rect r) { return r.pos.y + r.size.y * 0.5f; } + +void rect_coords(Rect r, float *x1, float *y1, float *x2, float *y2) { + *x1 = r.pos.x; + *y1 = r.pos.y; + *x2 = r.pos.x + r.size.x; + *y2 = r.pos.y + r.size.y; +} + +void rect_print(Rect r) { + printf("Position: (%f, %f), Size: (%f, %f)\n", r.pos.x, r.pos.y, r.size.x, r.size.y); +} + + +float rects_intersect(Rect r1, Rect r2) { + if (r1.pos.x >= r2.pos.x + r2.size.x) return false; // r1 is to the right of r2 + if (r2.pos.x >= r1.pos.x + r1.size.x) return false; // r2 is to the right of r1 + if (r1.pos.y >= r2.pos.y + r2.size.y) return false; // r1 is above r2 + if (r2.pos.y >= r1.pos.y + r1.size.y) return false; // r2 is above r1 + return true; +} + +// returns whether or not there is any of the clipped rectangle left +bool rect_clip_to_rect(Rect *clipped, Rect clipper) { + v2 start_pos = clipped->pos; + clipped->pos.x = maxf(clipped->pos.x, clipper.pos.x); + clipped->pos.y = maxf(clipped->pos.y, clipper.pos.y); + clipped->size = v2_add(clipped->size, v2_sub(start_pos, clipped->pos)); + + clipped->size.x = clampf(clipped->size.x, 0, clipper.pos.x + clipper.size.x - clipped->pos.x); + clipped->size.y = clampf(clipped->size.y, 0, clipper.pos.y + clipper.size.y - clipped->pos.y); + return clipped->size.x > 0 && clipped->size.y > 0; +} + +// removes `amount` from all sides of r +Rect rect_shrink(Rect r, float amount) { + r.pos.x += amount; + r.pos.y += amount; + r.size.x -= 2 * amount; + r.size.y -= 2 * amount; + r.size.x = maxf(r.size.x, 0); + r.size.y = maxf(r.size.y, 0); + return r; +} + +// adds `amount` to all sides of r +Rect rect_grow(Rect r, float amount) { + r.pos.x -= amount; + r.pos.y -= amount; + r.size.x += 2 * amount; + r.size.y += 2 * amount; + return r; +} + +v4 color_rgba_to_hsva(v4 rgba) { + float R = rgba.x, G = rgba.y, B = rgba.z, A = rgba.w; + float M = maxf(R, maxf(G, B)); + float m = minf(R, minf(G, B)); + float C = M - m; + float H = 0; + if (C == 0) + H = 0; + else if (M == R) + H = fmodf((G - B) / C, 6); + else if (M == G) + H = (B - R) / C + 2; + else if (M == B) + H = (R - G) / C + 4; + H *= 60; + float V = M; + float S = V == 0 ? 0 : C / V; + return V4(H, S, V, A); +} + +v4 color_hsva_to_rgba(v4 hsva) { + float H = hsva.x, S = hsva.y, V = hsva.z, A = hsva.w; + H /= 60; + float C = S * V; + float X = C * (1 - fabsf(fmodf(H, 2) - 1)); + float R, G, B; + if (H <= 1) + R=C, G=X, B=0; + else if (H <= 2) + R=X, G=C, B=0; + else if (H <= 3) + R=0, G=C, B=X; + else if (H <= 4) + R=0, G=X, B=C; + else if (H <= 5) + R=X, G=0, B=C; + else + R=C, G=0, B=X; + + float m = V-C; + R += m; + G += m; + B += m; + return V4(R, G, B, A); +} + +u32 color_interpolate(float x, u32 color1, u32 color2) { + x = x * x * (3 - 2*x); // hermite interpolation + + v4 c1 = rgba_u32_to_v4(color1), c2 = rgba_u32_to_v4(color2); + // to make it interpolate more nicely, convert to hsv, interpolate in that space, then convert back + c1 = color_rgba_to_hsva(c1); + c2 = color_rgba_to_hsva(c2); + // v_1/2 named differently to avoid shadowing + float h1 = c1.x, s1 = c1.y, v_1 = c1.z, a1 = c1.w; + float h2 = c2.x, s2 = c2.y, v_2 = c2.z, a2 = c2.w; + + float s_out = lerpf(x, s1, s2); + float v_out = lerpf(x, v_1, v_2); + float a_out = lerpf(x, a1, a2); + + float h_out; + // because hue is on a circle, we need to make sure we take the shorter route around the circle + if (fabsf(h1 - h2) < 180) { + h_out = lerpf(x, h1, h2); + } else if (h1 > h2) { + h_out = lerpf(x, h1, h2 + 360); + } else { + h_out = lerpf(x, h1 + 360, h2); + } + h_out = fmodf(h_out, 360); + + v4 c_out = V4(h_out, s_out, v_out, a_out); + c_out = color_hsva_to_rgba(c_out); + return rgba_v4_to_u32(c_out); +} diff --git a/util.h b/util.h index 8d5aeef..cbde09c 100644 --- a/util.h +++ b/util.h @@ -14,6 +14,49 @@ #define strbuf_cpy(dst, src) str_cpy(dst, sizeof dst, src) #define strbuf_cat(dst, src) str_cat(dst, sizeof dst, src) + +#define PIf 3.14159265358979f +#define HALF_PIf 1.5707963267948966f +#define TAUf 6.283185307179586f +#define SQRT2f 1.4142135623730951f +#define HALF_SQRT2f 0.7071067811865476f +#define SQRT3f 1.7320508075688772f +#define HALF_SQRT3f 0.8660254037844386f + + +typedef struct { + float x, y; +} v2; +typedef struct { + float x, y, z; +} v3; +typedef struct { + float x, y, z, w; +} v4; +typedef struct { + double x, y; +} v2d; +typedef struct { + int x, y; +} v2i; + +// matrices are column-major, because that's what they are in OpenGL +typedef struct { + float e[16]; +} m4; + +static const m4 m4_identity = {{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 +}}; + +typedef struct { + v2 pos, size; +} Rect; + + // ctype functions for 32-bit chars. bool is_word(char32_t c); bool is_digit(char32_t c); @@ -88,5 +131,123 @@ bool copy_file(const char *src, const char *dst); void qsort_with_context(void *base, size_t nmemb, size_t size, int (*compar)(void *, const void *, const void *), void *arg); +float degrees(float r); +float radians(float r); +float lerpf(float x, float a, float b); +float normf(float x, float a, float b); +float clampf(float x, float a, float b); +int clampi(int x, int a, int b); +i16 clamp_i16(i16 x, i16 a, i16 b); +u16 clamp_u16(u16 x, u16 a, u16 b); +i32 clamp_i32(i32 x, i32 a, i32 b); +u32 clamp_u32(u32 x, u32 a, u32 b); +u8 ndigits_u64(u64 x); +float remapf(float x, float from_a, float from_b, float to_a, float to_b); +float minf(float a, float b); +float maxf(float a, float b); +double maxd(double a, double b); +double mind(double a, double b); +u32 min_u32(u32 a, u32 b); +u32 max_u32(u32 a, u32 b); +void sort2_u32(u32 *a, u32 *b); +i32 min_i32(i32 a, i32 b); +i32 max_i32(i32 a, i32 b); +u64 min_u64(u64 a, u64 b); +u64 max_u64(u64 a, u64 b); +i64 min_i64(i64 a, i64 b); +i64 max_i64(i64 a, i64 b); +i64 mod_i64(i64 a, i64 b); +i32 mod_i32(i32 a, i32 b); +i64 abs_i64(i64 x); +i64 sgn_i64(i64 x); +float sgnf(float x); +float smoothstepf(float x); +float randf(void); +float rand_gauss(void); +u32 rand_u32(void); +float rand_uniform(float from, float to); +float sigmoidf(float x); +i32 ceildivi32(i32 x, i32 y); +v2 V2(float x, float y); +v2 v2_add(v2 a, v2 b); +v2 v2_add_const(v2 a, float c); +v2 v2_sub(v2 a, v2 b); +v2 v2_scale(v2 v, float s); +v2 v2_mul(v2 a, v2 b); +v2 v2_clamp(v2 x, v2 a, v2 b); +float v2_dot(v2 a, v2 b); +float v2_len(v2 v); +v2 v2_lerp(float x, v2 a, v2 b); +v2 v2_rotate(v2 v, float theta); +v2 v2_normalize(v2 v); +float v2_dist(v2 a, v2 b); +float v2_dist_squared(v2 a, v2 b); +void v2_print(v2 v); +v2 v2_rand_unit(void); +v2 v2_polar(float r, float theta); +v3 V3(float x, float y, float z); +v3 v3_from_v2(v2 v); +v3 v3_add(v3 a, v3 b); +v3 v3_sub(v3 a, v3 b); +v3 v3_scale(v3 v, float s); +v3 v3_lerp(float x, v3 a, v3 b); +float v3_dot(v3 u, v3 v); +v3 v3_cross(v3 u, v3 v); +float v3_len(v3 v); +float v3_dist(v3 a, v3 b); +float v3_dist_squared(v3 a, v3 b); +v3 v3_normalize(v3 v); +v2 v3_xy(v3 v); +v3 v3_on_sphere(float yaw, float pitch); +void v3_print(v3 v); +v3 v3_rand(void); +v3 v3_rand_unit(void); +v4 V4(float x, float y, float z, float w); +v4 v4_add(v4 a, v4 b); +v4 v4_sub(v4 a, v4 b); +v4 v4_scale(v4 v, float s); +v4 v4_scale_xyz(v4 v, float s); +v4 v4_lerp(float x, v4 a, v4 b); +float v4_dot(v4 u, v4 v); +v4 v4_mul(v4 u, v4 v); +float v4_len(v4 v); +v4 v4_normalize(v4 v); +v3 v4_xyz(v4 v); +v4 v4_rand(void); +void v4_print(v4 v); +v2d V2D(double x, double y); +void m4_print(m4 m); +m4 m4_yaw(float yaw); +m4 m4_pitch(float pitch); +m4 m4_translate(v3 t); +v3 m4_mul_v3(m4 m, v3 v); +m4 m4_perspective(float fov, float aspect, float z_near, float z_far); +m4 m4_ortho(float left, float right, float bottom, float top, float near_, float far_); +m4 m4_mul(m4 a, m4 b); +m4 m4_inv(m4 mat); +v2i V2I(int x, int y); +void rgba_u32_to_floats(u32 rgba, float floats[4]); +v4 rgba_u32_to_v4(u32 rgba); +u32 rgba_v4_to_u32(v4 color); +float rgba_brightness(u32 color); +bool rect_contains_point_v2(v2 pos, v2 size, v2 point); +bool centered_rect_contains_point(v2 center, v2 size, v2 point); +Rect rect(v2 pos, v2 size); +Rect rect_endpoints(v2 e1, v2 e2); +Rect rect4(float x1, float y1, float x2, float y2); +Rect rect_xywh(float x, float y, float w, float h); +Rect rect_centered(v2 center, v2 size); +v2 rect_center(Rect r); +bool rect_contains_point(Rect r, v2 point); +Rect rect_translate(Rect r, v2 by); +void rect_coords(Rect r, float *x1, float *y1, float *x2, float *y2); +void rect_print(Rect r); +float rects_intersect(Rect r1, Rect r2); +bool rect_clip_to_rect(Rect *clipped, Rect clipper); +Rect rect_shrink(Rect r, float amount); +Rect rect_grow(Rect r, float amount); +v4 color_rgba_to_hsva(v4 rgba); +v4 color_hsva_to_rgba(v4 hsva); +u32 color_interpolate(float x, u32 color1, u32 color2); #endif // UTIL_H_ -- cgit v1.2.3