#include "trackball.h"
#include <math.h>
#define PI_2_f 1.57079632679489661923f
#define PI_2_d 1.57079632679489661923
#define PI_2_l 1.570796326794896619231321691639751442l
typedef float num_f;
typedef double num_d;
typedef long double num_l;
typedef struct {
num_f x;
num_f y;
num_f z;
num_f w;
} vec_f;
typedef struct {
num_d x;
num_d y;
num_d z;
num_d w;
} vec_d;
typedef struct {
num_l x;
num_l y;
num_l z;
num_l w;
} vec_l;
_Static_assert(sizeof (vec_f) == sizeof (num_f) * 4, "weird padding");
_Static_assert(sizeof (vec_d) == sizeof (num_d) * 4, "weird padding");
_Static_assert(sizeof (vec_l) == sizeof (num_l) * 4, "weird padding");
typedef struct {
vec_f x;
vec_f y;
vec_f z;
} mat_f;
typedef struct {
vec_d x;
vec_d y;
vec_d z;
} mat_d;
typedef struct {
vec_l x;
vec_l y;
vec_l z;
} mat_l;
#define clamp(val, min, max) _Generic((val), \
num_f: clamp_f, \
num_d: clamp_d, \
num_l: clamp_l \
)(val, min, max)
static num_f
clamp_f(num_f val, num_f min, num_f max) {
return val < min ? min : val > max ? max : val;
}
static num_d
clamp_d(num_d val, num_d min, num_d max) {
return val < min ? min : val > max ? max : val;
}
static num_l
clamp_l(num_l val, num_l min, num_l max) {
return val < min ? min : val > max ? max : val;
}
#define normalize(v) _Generic((v), \
vec_f*: normalize_f, \
vec_d*: normalize_d, \
vec_l*: normalize_l \
)(v)
static num_f
normalize_f(vec_f* v) {
v->w = sqrt(v->x * v->x + v->y * v->y + v->z * v->z);
if (v->w) {
v->x /= v->w;
v->y /= v->w;
v->z /= v->w;
}
return v->w;
}
static num_d
normalize_d(vec_d* v) {
v->w = sqrt(v->x * v->x + v->y * v->y + v->z * v->z);
if (v->w) {
v->x /= v->w;
v->y /= v->w;
v->z /= v->w;
}
return v->w;
}
static num_l
normalize_l(vec_l* v) {
v->w = sqrt(v->x * v->x + v->y * v->y + v->z * v->z);
if (v->w) {
v->x /= v->w;
v->y /= v->w;
v->z /= v->w;
}
return v->w;
}
#define cross(a, b) _Generic((a), \
vec_f: cross_f, \
vec_d: cross_d, \
vec_l: cross_l \
)(a, b)
static vec_f
cross_f(vec_f a, vec_f b) {
return (vec_f) {
.x = a.y * b.z - a.z * b.y,
.y = a.z * b.x - a.x * b.z,
.z = a.x * b.y - a.y * b.x,
};
}
static vec_d
cross_d(vec_d a, vec_d b) {
return (vec_d) {
.x = a.y * b.z - a.z * b.y,
.y = a.z * b.x - a.x * b.z,
.z = a.x * b.y - a.y * b.x,
};
}
static vec_l
cross_l(vec_l a, vec_l b) {
return (vec_l) {
.x = a.y * b.z - a.z * b.y,
.y = a.z * b.x - a.x * b.z,
.z = a.x * b.y - a.y * b.x,
};
}
#define mul(a, b) _Generic((b), \
vec_f: mul_f, \
vec_d: mul_d, \
vec_l: mul_l \
)(a, b)
static vec_f
mul_f(mat_f m, vec_f v) {
return (vec_f) {
.x = m.x.x * v.x + m.y.x * v.y + m.z.x * v.z,
.y = m.x.y * v.x + m.y.y * v.y + m.z.y * v.z,
.z = m.x.z * v.x + m.y.z * v.y + m.z.z * v.z,
};
}
static vec_d
mul_d(mat_d m, vec_d v) {
return (vec_d) {
.x = m.x.x * v.x + m.y.x * v.y + m.z.x * v.z,
.y = m.x.y * v.x + m.y.y * v.y + m.z.y * v.z,
.z = m.x.z * v.x + m.y.z * v.y + m.z.z * v.z,
};
}
static vec_l
mul_l(mat_l m, vec_l v) {
return (vec_l) {
.x = m.x.x * v.x + m.y.x * v.y + m.z.x * v.z,
.y = m.x.y * v.x + m.y.y * v.y + m.z.y * v.z,
.z = m.x.z * v.x + m.y.z * v.y + m.z.z * v.z,
};
}
#define tr_mul(a, b) _Generic((b), \
vec_f: tr_mul_f, \
vec_d: tr_mul_d, \
vec_l: tr_mul_l \
)(a, b)
static vec_f
tr_mul_f(mat_f m, vec_f v) {
return (vec_f) {
.x = m.x.x * v.x + m.x.y * v.y + m.x.z * v.z,
.y = m.y.x * v.x + m.y.y * v.y + m.y.z * v.z,
.z = m.z.x * v.x + m.z.y * v.y + m.z.z * v.z,
};
}
static vec_d
tr_mul_d(mat_d m, vec_d v) {
return (vec_d) {
.x = m.x.x * v.x + m.x.y * v.y + m.x.z * v.z,
.y = m.y.x * v.x + m.y.y * v.y + m.y.z * v.z,
.z = m.z.x * v.x + m.z.y * v.y + m.z.z * v.z,
};
}
static vec_l
tr_mul_l(mat_l m, vec_l v) {
return (vec_l) {
.x = m.x.x * v.x + m.x.y * v.y + m.x.z * v.z,
.y = m.y.x * v.x + m.y.y * v.y + m.y.z * v.z,
.z = m.z.x * v.x + m.z.y * v.y + m.z.z * v.z,
};
}
void
trackball_orbit_f(
num_f xyzw[static restrict 4],
num_f xyzm[static restrict 4],
const num_f xy[static restrict 2],
const num_f wh[static restrict 2]
) {
vec_f* rot = (vec_f*)xyzw;
*rot = (vec_f) { .w = 1.0f };
vec_f* old = (vec_f*)xyzm;
vec_f pza = { .z = 1.0f, .w = 1.0f };
vec_f max = { .x = wh[0] * 0.5f, .y = wh[1] * 0.5f };
vec_f vec = (vec_f) {
.x = clamp(xy[0], 0.0f, wh[0]) - max.x,
.y = max.y - clamp(xy[1], 0.0f, wh[1]),
};
if (!normalize(&vec))
vec = pza;
vec_f pos = *old;
*old = vec;
if (!pos.w)
return;
vec.x = vec.x * vec.w - pos.x * pos.w;
vec.y = vec.y * vec.w - pos.y * pos.w;
if (!normalize(&vec))
return;
max.w = fmax(max.x, max.y);
pos.w = pos.w / max.w * PI_2_f;
num_f s = sin(pos.w), c = cos(pos.w);
vec_f exp = { .x = s * pos.x, .y = s * pos.y, .z = c };
vec_f tan = { .x = c * pos.x, .y = c * pos.y, .z = -s };
vec_f zxp = { .x = -pos.y, .y = pos.x };
mat_f arg = { pza, pos, zxp };
mat_f img = { exp, tan, zxp };
*rot = cross(mul(img, tr_mul(arg, vec)), exp);
normalize(rot);
rot->w = vec.w / max.w;
rot->w *= 0.5f;
num_f im = sin(rot->w), re = cos(rot->w);
*rot = (vec_f) { rot->x * im, rot->y * im, rot->z * im, re };
}
void
trackball_orbit_d(
num_d xyzw[static restrict 4],
num_d xyzm[static restrict 4],
const num_d xy[static restrict 2],
const num_d wh[static restrict 2]
) {
vec_d* rot = (vec_d*)xyzw;
*rot = (vec_d) { .w = 1.0 };
vec_d* old = (vec_d*)xyzm;
vec_d pza = { .z = 1.0, .w = 1.0 };
vec_d max = { .x = wh[0] * 0.5, .y = wh[1] * 0.5 };
vec_d vec = (vec_d) {
.x = clamp(xy[0], 0.0, wh[0]) - max.x,
.y = max.y - clamp(xy[1], 0.0, wh[1]),
};
if (!normalize(&vec))
vec = pza;
vec_d pos = *old;
*old = vec;
if (!pos.w)
return;
vec.x = vec.x * vec.w - pos.x * pos.w;
vec.y = vec.y * vec.w - pos.y * pos.w;
if (!normalize(&vec))
return;
max.w = fmax(max.x, max.y);
pos.w = pos.w / max.w * PI_2_d;
num_d s = sin(pos.w), c = cos(pos.w);
vec_d exp = { .x = s * pos.x, .y = s * pos.y, .z = c };
vec_d tan = { .x = c * pos.x, .y = c * pos.y, .z = -s };
vec_d zxp = { .x = -pos.y, .y = pos.x };
mat_d arg = { pza, pos, zxp };
mat_d img = { exp, tan, zxp };
*rot = cross(mul(img, tr_mul(arg, vec)), exp);
normalize(rot);
rot->w = vec.w / max.w;
rot->w *= 0.5;
num_d im = sin(rot->w), re = cos(rot->w);
*rot = (vec_d) { rot->x * im, rot->y * im, rot->z * im, re };
}
void
trackball_orbit_l(
num_l xyzw[static restrict 4],
num_l xyzm[static restrict 4],
const num_l xy[static restrict 2],
const num_l wh[static restrict 2]
) {
vec_l* rot = (vec_l*)xyzw;
*rot = (vec_l) { .w = 1.0l };
vec_l* old = (vec_l*)xyzm;
vec_l pza = { .z = 1.0l, .w = 1.0l };
vec_l max = { .x = wh[0] * 0.5l, .y = wh[1] * 0.5l };
vec_l vec = (vec_l) {
.x = clamp(xy[0], 0.0l, wh[0]) - max.x,
.y = max.y - clamp(xy[1], 0.0l, wh[1]),
};
if (!normalize(&vec))
vec = pza;
vec_l pos = *old;
*old = vec;
if (!pos.w)
return;
vec.x = vec.x * vec.w - pos.x * pos.w;
vec.y = vec.y * vec.w - pos.y * pos.w;
if (!normalize(&vec))
return;
max.w = fmax(max.x, max.y);
pos.w = pos.w / max.w * PI_2_l;
num_l s = sin(pos.w), c = cos(pos.w);
vec_l exp = { .x = s * pos.x, .y = s * pos.y, .z = c };
vec_l tan = { .x = c * pos.x, .y = c * pos.y, .z = -s };
vec_l zxp = { .x = -pos.y, .y = pos.x };
mat_l arg = { pza, pos, zxp };
mat_l img = { exp, tan, zxp };
*rot = cross(mul(img, tr_mul(arg, vec)), exp);
normalize(rot);
rot->w = vec.w / max.w;
rot->w *= 0.5l;
num_l im = sin(rot->w), re = cos(rot->w);
*rot = (vec_l) { rot->x * im, rot->y * im, rot->z * im, re };
}