diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2023-12-29 06:21:33 +0100 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2023-12-29 06:21:33 +0100 |
commit | 2d6c8fec45b23a8a28668ecf3ef281139ab778a7 (patch) | |
tree | 75d2a8538992129a83c0c2b83688289443d697e5 /source/kit/math.h | |
parent | 820b171245f2f14766f3accdb0246a4e2c0d596a (diff) | |
download | saw-2d6c8fec45b23a8a28668ecf3ef281139ab778a7.zip |
refactor dependencies; include dependencies source code
Diffstat (limited to 'source/kit/math.h')
-rw-r--r-- | source/kit/math.h | 779 |
1 files changed, 779 insertions, 0 deletions
diff --git a/source/kit/math.h b/source/kit/math.h new file mode 100644 index 0000000..f4ab33a --- /dev/null +++ b/source/kit/math.h @@ -0,0 +1,779 @@ +#ifndef KIT_MATH_H +#define KIT_MATH_H + +#include "types.h" + +#ifndef _USE_MATH_DEFINES +# define _USE_MATH_DEFINES +#endif +#include <math.h> + +#include <assert.h> +#include <string.h> + +// TODO +// - Gamma correction. +// + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +#ifndef M_E +# define M_E 2.71828182845904523536 +#endif + +#ifndef KIT_VEC_TYPE +# define KIT_VEC_TYPE f32 +#endif + +#define KIT_EPSILON .00001 + +#define KIT_COLOR_REF_X 95.047f +#define KIT_COLOR_REF_Y 100.0f +#define KIT_COLOR_REF_Z 108.883f + +typedef KIT_VEC_TYPE vec_t; + +typedef union { + vec_t v[2]; +} vec2_t; + +typedef union { + vec_t v[3]; +} vec3_t; + +typedef struct { + vec_t v[4]; +} vec4_t, quat_t; + +typedef struct { + vec_t v[9]; +} mat3_t; + +typedef struct { + vec_t v[16]; +} mat4_t; + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wunknown-pragmas" +# pragma GCC push_options +# pragma GCC optimize("O3") +#endif + +static vec2_t vec2(vec_t x, vec_t y) { + vec2_t v = { { x, y } }; + return v; +} + +static vec3_t vec3(vec_t x, vec_t y, vec_t z) { + vec3_t v = { { x, y, z } }; + return v; +} + +static vec4_t vec4(vec_t x, vec_t y, vec_t z, vec_t w) { + vec4_t v = { { x, y, z, w } }; + return v; +} + +static mat3_t mat3( // + vec_t _00, vec_t _01, vec_t _02, // + vec_t _10, vec_t _11, vec_t _12, // + vec_t _20, vec_t _21, vec_t _22) { + mat3_t m = { { + _00, _01, _02, // + _10, _11, _12, // + _20, _21, _22 // + } }; + return m; +} + +static mat4_t mat4(vec_t _00, vec_t _01, vec_t _02, vec_t _03, + vec_t _10, vec_t _11, vec_t _12, vec_t _13, + vec_t _20, vec_t _21, vec_t _22, vec_t _23, + vec_t _30, vec_t _31, vec_t _32, vec_t _33) { + mat4_t m = { { + _00, _01, _02, _03, // + _10, _11, _12, _13, // + _20, _21, _22, _23, // + _30, _31, _32, _33 // + } }; + return m; +} + +static mat3_t vec3_to_mat3(vec3_t a, vec3_t b, vec3_t c) { + return mat3(a.v[0], a.v[1], a.v[2], // + b.v[0], b.v[1], b.v[2], // + c.v[0], c.v[1], c.v[2]); +} + +static mat4_t vec4_to_mat4(vec4_t a, vec4_t b, vec4_t c, vec4_t d) { + return mat4(a.v[0], a.v[1], a.v[2], a.v[3], // + b.v[0], b.v[1], b.v[2], b.v[3], // + c.v[0], c.v[1], c.v[2], c.v[3], // + d.v[0], d.v[1], d.v[2], d.v[3]); +} + +static mat4_t mat3_to_mat4(mat3_t a) { + return mat4(a.v[0], a.v[1], a.v[2], 0.f, // + a.v[3], a.v[4], a.v[5], 0.f, // + a.v[6], a.v[7], a.v[8], 0.f, // + 0.f, 0.f, 0.f, 1.f); +} + +static mat3_t mat4_to_mat3(mat4_t a) { + return mat3(a.v[0], a.v[1], a.v[2], // + a.v[4], a.v[5], a.v[6], // + a.v[8], a.v[9], a.v[10]); +} + +static quat_t quat(vec_t x, vec_t y, vec_t z, vec_t w) { + return vec4(x, y, z, w); +} + +static quat_t vec3_to_quat(vec3_t v, vec_t w) { + return quat(v.v[0], v.v[1], v.v[2], w); +} + +static vec3_t vec4_to_vec3(vec4_t a) { + return vec3(a.v[0], a.v[1], a.v[2]); +} + +static vec3_t quat_to_vec3(quat_t a) { + return vec4_to_vec3(a); +} + +static vec_t vec_abs(vec_t x) { + return fabs(x); +} + +static vec_t vec_min(vec_t x, vec_t y) { + return fmin(x, y); +} + +static vec_t vec_max(vec_t x, vec_t y) { + return fmax(x, y); +} + +static vec_t vec_clamp(vec_t x, vec_t x0, vec_t x1) { + return fmax(x0, fmin(x, x1)); +} + +static vec3_t vec3_clamp(vec3_t x, vec3_t x0, vec3_t x1) { + return vec3(vec_clamp(x.v[0], x0.v[0], x1.v[0]), + vec_clamp(x.v[1], x0.v[1], x1.v[1]), + vec_clamp(x.v[2], x0.v[2], x1.v[2])); +} + +static vec_t vec_sqrt(vec_t x) { + return sqrtf(x); +} + +static vec_t vec_pow(vec_t x, vec_t y) { + return powf(x, y); +} + +static vec_t vec_sin(vec_t x) { + return sinf(x); +} + +static vec_t vec_cos(vec_t x) { + return cosf(x); +} + +static vec_t vec_asin(vec_t x) { + return asinf(x); +} + +static vec_t vec_acos(vec_t x) { + return acosf(x); +} + +static vec_t vec_atan(vec_t x) { + return atanf(x); +} + +static vec_t vec_atan2(vec_t y, vec_t x) { + return atan2f(y, x); +} + +static vec3_t vec3_neg(vec3_t a) { + return vec3(-a.v[0], -a.v[1], -a.v[2]); +} + +static vec3_t vec3_add(vec3_t a, vec3_t b) { + return vec3(a.v[0] + b.v[0], a.v[1] + b.v[1], a.v[2] + b.v[2]); +} + +static vec3_t vec3_add3(vec3_t a, vec3_t b, vec3_t c) { + return vec3(a.v[0] + b.v[0] + c.v[0], a.v[1] + b.v[1] + c.v[1], + a.v[2] + b.v[2] + c.v[2]); +} + +static vec3_t vec3_sub(vec3_t a, vec3_t b) { + return vec3(a.v[0] - b.v[0], a.v[1] - b.v[1], a.v[2] - b.v[2]); +} + +static vec3_t vec3_mul(vec3_t v, vec_t x) { + return vec3(v.v[0] * x, v.v[1] * x, v.v[2] * x); +} + +static vec_t vec3_dot(vec3_t a, vec3_t b) { + return a.v[0] * b.v[0] + a.v[1] * b.v[1] + a.v[2] * b.v[2]; +} + +static vec3_t vec3_cross(vec3_t a, vec3_t b) { + return vec3(a.v[1] * b.v[2] - a.v[2] * b.v[1], // + a.v[2] * b.v[0] - a.v[0] * b.v[2], // + a.v[0] * b.v[1] - a.v[1] * b.v[0]); +} + +static vec3_t vec3_normal(vec3_t v) { + vec_t x = v.v[0]; + vec_t y = v.v[1]; + vec_t z = v.v[2]; + + vec_t length_squared = x * x + y * y + z * z; + assert(length_squared >= KIT_EPSILON); + + if (length_squared < KIT_EPSILON) { + vec3_t n = { { 0.f, 0.f, 1.f } }; + return n; + } + + vec_t length = vec_sqrt(length_squared); + vec3_t n = { { x / length, y / length, z / length } }; + + return n; +} + +static vec4_t vec4_neg(vec4_t a) { + return vec4(-a.v[0], -a.v[1], -a.v[2], -a.v[3]); +} + +static vec4_t vec4_add(vec4_t a, vec4_t b) { + return vec4(a.v[0] + b.v[0], a.v[1] + b.v[1], a.v[2] + b.v[2], + a.v[3] + b.v[3]); +} + +static vec4_t vec4_mul(vec4_t v, vec_t x) { + return vec4(v.v[0] * x, v.v[1] * x, v.v[2] * x, v.v[3] * x); +} + +static vec4_t vec4_div(vec4_t v, vec_t x) { + assert(x < KIT_EPSILON); + assert(x > -KIT_EPSILON); + + if (x >= -KIT_EPSILON || x < KIT_EPSILON) + return vec4(0.f, 0.f, 0.f, 0.f); + + return vec4(v.v[0] / x, v.v[1] / x, v.v[2] / x, v.v[3] / x); +} + +static vec_t vec4_dot(vec4_t a, vec4_t b) { + return a.v[0] * b.v[0] + a.v[1] * b.v[1] + a.v[2] * b.v[2] + + a.v[3] * b.v[3]; +} + +static vec4_t vec4_normal(vec4_t v) { + vec_t x = v.v[0]; + vec_t y = v.v[1]; + vec_t z = v.v[2]; + vec_t w = v.v[3]; + + vec_t length_squared = x * x + y * y + z * z + w * w; + assert(length_squared >= KIT_EPSILON); + + if (length_squared < KIT_EPSILON) { + vec4_t n = { { 0.f, 0.f, 0.f, 1.f } }; + return n; + } + + vec_t length = vec_sqrt(length_squared); + vec4_t n = { { x / length, y / length, z / length, w / length } }; + + return n; +} + +static quat_t quat_conjugate(quat_t q) { + return quat(-q.v[0], -q.v[1], -q.v[2], q.v[3]); +} + +static quat_t quat_normal(quat_t q) { + return vec4_normal(q); +} + +static quat_t quat_mul(quat_t left, quat_t right) { + vec_t i0 = left.v[0]; + vec_t j0 = left.v[1]; + vec_t k0 = left.v[2]; + vec_t r0 = left.v[3]; + + vec_t i1 = right.v[0]; + vec_t j1 = right.v[1]; + vec_t k1 = right.v[2]; + vec_t r1 = right.v[3]; + + quat_t q = { { r0 * i1 + i0 * r1 + j0 * k1 - k0 * j1, // + r0 * j1 - i0 * k1 + j0 * r1 + k0 * i1, // + r0 * k1 + i0 * j1 - j0 * i1 + k0 * r1, // + r0 * r1 - i0 * i1 - j0 * j1 - k0 * k1 } }; + + return q; +} + +static quat_t quat_rotation(vec_t angle, vec3_t axis) { + vec_t s = vec_sin(angle / 2.f); + vec_t c = vec_cos(angle / 2.f); + + vec_t x = axis.v[0]; + vec_t y = axis.v[1]; + vec_t z = axis.v[2]; + + quat_t q = { { x * s, y * s, z * s, c } }; + + return q; +} + +static mat3_t mat3_transpose(mat3_t m) { + return mat3(m.v[0], m.v[3], m.v[6], // + m.v[1], m.v[4], m.v[7], // + m.v[2], m.v[5], m.v[8]); +} + +static mat4_t mat4_transpose(mat4_t m) { + return mat4(m.v[0], m.v[4], m.v[8], m.v[12], // + m.v[1], m.v[5], m.v[9], m.v[13], // + m.v[2], m.v[6], m.v[10], m.v[14], // + m.v[3], m.v[7], m.v[11], m.v[15]); +} + +static mat3_t mat3_look_at(vec3_t direction, vec3_t up) { + vec3_t f = vec3_normal(vec3_neg(direction)); + vec3_t s = vec3_normal(vec3_cross(f, up)); + vec3_t u = vec3_cross(s, f); + + return mat3(s.v[0], u.v[0], f.v[0], // + s.v[1], u.v[1], f.v[1], // + s.v[2], u.v[2], f.v[2]); +} + +static vec3_t vec3_rotate(vec3_t v, quat_t rotation) { + vec3_t u = vec3(rotation.v[0], rotation.v[1], rotation.v[2]); + vec_t s = rotation.v[3]; + + return vec3_add3(vec3_mul(u, 2.f * vec3_dot(u, v)), + vec3_mul(v, s * s - vec3_dot(u, u)), + vec3_mul(vec3_cross(u, v), 2.f * s)); +} + +static mat3_t quat_to_mat3(quat_t q) { + vec_t xx = q.v[0] * q.v[0]; + vec_t yy = q.v[1] * q.v[1]; + vec_t zz = q.v[2] * q.v[2]; + vec_t xz = q.v[0] * q.v[2]; + vec_t xy = q.v[0] * q.v[1]; + vec_t yz = q.v[1] * q.v[2]; + vec_t wx = q.v[3] * q.v[0]; + vec_t wy = q.v[3] * q.v[1]; + vec_t wz = q.v[3] * q.v[2]; + + return mat3(1.f - 2.f * (yy + zz), // + 2.f * (xy + wz), // + 2.f * (xz - wy), // + 2.f * (xy - wz), // + 1.f - 2.f * (xx + zz), // + 2.f * (yz + wx), // + 2.f * (xz + wy), // + 2.f * (yz - wx), // + 1.f - 2.f * (xx + yy)); +} + +static quat_t mat3_to_quat(mat3_t m) { + vec_t a = m.v[0] - m.v[4] - m.v[8]; + vec_t b = m.v[4] - m.v[0] - m.v[8]; + vec_t c = m.v[8] - m.v[0] - m.v[4]; + vec_t d = m.v[0] + m.v[4] + m.v[8]; + + int n = 0; + vec_t h = d; + if (a > h) { + h = a; + n = 1; + } + if (b > h) { + h = b; + n = 2; + } + if (c > h) { + h = c; + n = 3; + } + + vec_t s = vec_sqrt(h + 1.f) * .5f; + vec_t k = .25f / s; + + switch (n) { + case 0: + return quat((m.v[5] - m.v[7]) * k, // + (m.v[6] - m.v[2]) * k, // + (m.v[1] - m.v[3]) * k, // + s); + case 1: + return quat(s, // + (m.v[1] + m.v[3]) * k, // + (m.v[6] + m.v[2]) * k, // + (m.v[5] - m.v[7]) * k); + case 2: + return quat((m.v[1] + m.v[3]) * k, // + s, // + (m.v[5] + m.v[7]) * k, // + (m.v[6] - m.v[2]) * k); + case 3: + return quat((m.v[6] + m.v[2]) * k, // + (m.v[5] + m.v[7]) * k, // + s, // + (m.v[1] - m.v[3]) * k); + default:; + } + + assert(0); + return quat(0.f, 0.f, 0.f, 1.f); +} + +static quat_t quat_look_at(vec3_t direction, vec3_t up) { + vec3_t z = vec3_normal(vec3_neg(direction)); + vec3_t right = vec3_cross(up, z); + vec3_t x = vec3_mul( + right, + 1.f / vec_sqrt(vec_max(KIT_EPSILON, vec3_dot(right, right)))); + vec3_t y = vec3_cross(z, x); + + return mat3_to_quat(vec3_to_mat3(x, y, z)); +} + +static mat3_t mat3_mul(mat3_t left, mat3_t right) { + mat3_t m; + memset(&m, 0, sizeof m); + + for (int j = 0; j < 3; j++) + for (int i = 0; i < 3; i++) + for (int k = 0; k < 3; k++) + m.v[j * 3 + i] += left.v[k * 3 + i] * right.v[j * 3 + k]; + + return m; +} + +static mat4_t mat4_mul(mat4_t left, mat4_t right) { + mat4_t m; + memset(&m, 0, sizeof m); + + for (int j = 0; j < 4; j++) + for (int i = 0; i < 4; i++) + for (int k = 0; k < 4; k++) + m.v[j * 4 + i] += left.v[k * 4 + i] * right.v[j * 4 + k]; + + return m; +} + +static mat4_t mat4_move(vec3_t offset) { + vec_t x = offset.v[0]; + vec_t y = offset.v[1]; + vec_t z = offset.v[2]; + + return mat4(1.f, 0.f, 0.f, 0.f, // + 0.f, 1.f, 0.f, 0.f, // + 0.f, 0.f, 1.f, 0.f, // + x, y, z, 1.f); +} + +static mat4_t mat4_scale(vec3_t scale) { + vec_t x = scale.v[0]; + vec_t y = scale.v[1]; + vec_t z = scale.v[2]; + + return mat4(x, 0.f, 0.f, 0.f, // + 0.f, y, 0.f, 0.f, // + 0.f, 0.f, z, 0.f, // + 0.f, 0.f, 0.f, 1.f); +} + +static mat4_t mat4_frustum(vec_t left, vec_t right, vec_t bottom, + vec_t top, vec_t znear, vec_t zfar) { + vec_t t0 = 2.f * znear; + vec_t t1 = right - left; + vec_t t2 = top - bottom; + vec_t t3 = zfar - znear; + + return mat4(t0 / t1, // + 0.f, // + 0.f, // + 0.f, // + 0.f, // + t0 / t2, // + 0.f, // + 0.f, // + (right + left) / t1, // + (top + bottom) / t2, // + (-zfar - znear) / t3, // + -1.f, // + 0.f, // + 0.f, // + (-t0 * zfar) / t3, // + 0.f); +} + +static mat4_t mat4_perspective(vec_t fovy, vec_t aspect_ratio_, + vec_t znear, vec_t zfar) { + vec_t ymax = znear * tanf(fovy); + vec_t xmax = ymax * aspect_ratio_; + + return mat4_frustum(-xmax, xmax, -ymax, ymax, znear, zfar); +} + +static vec_t vec_lerp(vec_t x0, vec_t x1, vec_t t) { + assert(t >= 0.f); + assert(t <= 1.f); + + if (t <= 0.f) + return x0; + if (t >= 1.f) + return x1; + + return x0 + (x1 - x0) * t; +} + +static vec3_t vec3_lerp(vec3_t x0, vec3_t x1, vec_t t) { + return vec3(vec_lerp(x0.v[0], x1.v[0], t), + vec_lerp(x0.v[1], x1.v[1], t), + vec_lerp(x0.v[2], x1.v[2], t)); +} + +static vec4_t vec4_lerp(vec4_t x0, vec4_t x1, vec_t t) { + return vec4( + vec_lerp(x0.v[0], x1.v[0], t), vec_lerp(x0.v[1], x1.v[1], t), + vec_lerp(x0.v[2], x1.v[2], t), vec_lerp(x0.v[3], x1.v[3], t)); +} + +static quat_t quat_slerp(quat_t q0, quat_t q1, vec_t t) { + assert(t >= 0.f); + assert(t <= 1.f); + + if (t <= 0.f) + return q0; + if (t >= 1.f) + return q1; + + quat_t q = q1; + + vec_t cos_theta = vec4_dot(q0, q1); + + if (cos_theta < 0.f) { + q = vec4_neg(q1); + cos_theta = -cos_theta; + } + + if (cos_theta > 1.f - KIT_EPSILON) + return vec4_lerp(q0, q1, t); + + vec_t angle = vec_acos(cos_theta); + + return vec4_div(vec4_add(vec4_mul(q0, vec_sin((1.f - t) * angle)), + vec4_mul(q, vec_sin(t * angle))), + vec_sin(angle)); +} + +static vec3_t rgb_to_xyz(vec3_t rgb) { + // FIXME + // Remove unnecessary x100 scaling. + // + + vec_t red = rgb.v[0]; + vec_t green = rgb.v[1]; + vec_t blue = rgb.v[2]; + + // Gamma correction + // + /* + if (red > 0.04045f) + red = vec_pow(((red + 0.055f) / 1.055f), 2.4f); + else + red = red / 12.92f; + + if (green > 0.04045f) + green = vec_pow(((green + 0.055f) / 1.055f), 2.4f); + else + green = green / 12.92f; + + if (blue > 0.04045f) + blue = vec_pow(((blue + 0.055f) / 1.055f), 2.4f); + else + blue = blue / 12.92f; + */ + + red = red * 100.f; + green = green * 100.f; + blue = blue * 100.f; + + return vec3(red * 0.4124f + green * 0.3576f + blue * 0.1805f, + red * 0.2126f + green * 0.7152f + blue * 0.0722f, + red * 0.0193f + green * 0.1192f + blue * 0.9505f); +} + +static vec3_t xyz_to_rgb(vec3_t xyz) { + // FIXME + // Remove unnecessary x100 scaling. + // + + vec_t x = xyz.v[0] / 100.f; + vec_t y = xyz.v[1] / 100.f; + vec_t z = xyz.v[2] / 100.f; + + vec_t red = x * 3.2406f + (y * -1.5372f) + z * (-0.4986f); + vec_t green = x * (-0.9689f) + y * 1.8758f + z * 0.0415f; + vec_t blue = x * 0.0557f + y * (-0.2040f) + z * 1.0570f; + + // Gamma correction + // + /* + if (red > 0.0031308f) + red = 1.055f * vec_pow(red, (1.0f / 2.4)) - 0.055f; + else + red = 12.92f * red; + + if (green > 0.0031308f) + green = 1.055f * vec_pow(green, (1.0f / 2.4)) - 0.055f; + else + green = 12.92f * green; + + if (blue > 0.0031308f) + blue = 1.055f * vec_pow(blue, (1.0f / 2.4)) - 0.055f; + else + blue = 12.92f * blue; + */ + + return vec3_clamp(vec3(red, green, blue), vec3(0.f, 0.f, 0.f), + vec3(1.f, 1.f, 1.f)); +} + +static vec3_t lab_to_xyz(vec3_t lab) { + // FIXME + // Remove unnecessary x100 scaling. + // + + vec_t lightness = lab.v[0]; + vec_t a = lab.v[1]; + vec_t b = lab.v[2]; + + vec_t y = (lightness + 16.f) / 116.f; + vec_t x = (a / 500.f) + y; + vec_t z = y - (b / 200.f); + + if (vec_pow(y, 3.f) > 0.008856) + y = vec_pow(y, 3.f); + else + y = (y - (16.f / 116.f)) / 7.787f; + + if (vec_pow(x, 3.f) > 0.008856f) + x = vec_pow(x, 3.f); + else + x = (x - (16.f / 116.f)) / 7.787f; + + if (vec_pow(z, 3.f) > 0.008856f) + z = vec_pow(z, 3.f); + else + z = (z - (16.f / 116.f)) / 7.787f; + + return vec3(KIT_COLOR_REF_X * x, KIT_COLOR_REF_Y * y, + KIT_COLOR_REF_Z * z); +} + +static vec3_t xyz_to_lab(vec3_t xyz) { + // FIXME + // Remove unnecessary x100 scaling. + // + + vec_t x = (xyz.v[0] / KIT_COLOR_REF_X); + vec_t y = (xyz.v[1] / KIT_COLOR_REF_Y); + vec_t z = (xyz.v[2] / KIT_COLOR_REF_Z); + + if (x > 0.008856f) + x = vec_pow(x, (1.f / 3.f)); + else + x = (7.787f * x) + (16.f / 116.f); + + if (y > 0.008856f) + y = vec_pow(y, (1.f / 3.f)); + else + y = (7.787f * y) + (16.f / 116.f); + + if (z > 0.008856f) + z = vec_pow(z, (1.f / 3.f)); + else + z = (7.787f * z) + (16.f / 116.f); + + vec_t lightness = (116.f * y) - 16.f; + vec_t a = 500.f * (x - y); + vec_t b = 200.f * (y - z); + + return vec3(lightness, a, b); +} + +static vec3_t lab_to_lch(vec3_t lab) { + vec_t lightness = lab.v[0]; + vec_t a = lab.v[1]; + vec_t b = lab.v[2]; + + vec_t chroma = sqrtf(a * a + b * b); + vec_t hue = a == 0.f ? 0.f : vec_atan(b / a); + + return vec3(lightness, chroma, hue); +} + +static vec3_t lch_to_lab(vec3_t lch) { + vec_t lightness = lch.v[0]; + vec_t chroma = lch.v[1]; + vec_t hue = lch.v[2]; + + vec_t a = chroma * cos(hue); + vec_t b = chroma * sin(hue); + + return vec3(lightness, a, b); +} + +static vec3_t rgb_to_lch(vec3_t rgb) { + vec3_t xyz = rgb_to_xyz(rgb); + vec3_t lab = xyz_to_lab(xyz); + + return lab_to_lch(lab); +} + +static vec3_t lch_to_rgb(vec3_t lch) { + vec3_t lab = lch_to_lab(lch); + vec3_t xyz = lab_to_xyz(lab); + + return xyz_to_rgb(xyz); +} + +static vec4_t rgba_from_lcha(vec_t lightness, vec_t chroma, vec_t hue, + vec_t alpha) { + vec3_t rgb = lch_to_rgb(vec3(lightness, chroma, hue)); + return vec4(rgb.v[0], rgb.v[1], rgb.v[2], alpha); +} + +#ifdef __GNUC__ +# pragma GCC pop_options +# pragma GCC diagnostic pop +#endif + +#ifdef __cplusplus +} +#endif + +#endif |