/* geo.c */

#include <math.h>
#include "geo.h"

num	minf(num a, num b) {
	return a < b ? a : b;
}

num maxf(num a, num b) {
	return a > b ? a : b;
}

void swapf(num *a, num *b) {
	num tmp = *a;
	*a = *b;
	*b = tmp;
}

tuple geo_new(num x, num y, num z) {
	tuple ret = {x, y, z};
	return ret;
}

int geo_eq(tuple a, tuple b) {
	return (a.a==b.a && a.b==b.b && a.c==b.c);
}

tuple geo_add(tuple a, tuple b) {
	return geo_new(a.a+b.a, a.b+b.b, a.c+b.c);
}

tuple geo_neg(tuple a) {
	return geo_new(-a.a, -a.b, -a.c);
}

tuple geo_sub(tuple l, tuple r) {
	return geo_add(l, geo_neg(r));
}

tuple geo_scale(tuple a, num f) {
	return geo_new(a.a * f, a.b * f, a.c * f);
}

tuple geo_cross(tuple l, tuple r) {
	return geo_new(l.b*r.c - l.c*r.b, l.c*r.a - l.a*r.c, l.a*r.b - l.b*r.a);
}

num geo_dot(tuple l, tuple r) {
	return (l.a*r.a)+(l.b*r.b)+(l.c*r.c);
}

num geo_len(tuple t) {
	return sqrt(geo_dot(t,t));
} 

tuple geo_unit(tuple t) {
	return geo_scale(t, 1.0f / geo_len(t));
}

tuple geo_abs (tuple t) {
	return geo_new(ABS(t.a), ABS(t.b), ABS(t.c));
}

int geo_isset(tuple a) {
	return (a.a != 0 || a.b != 0 || a.c != 0) ? 1 : 0;
}

/* Rays */

tuple ray_pos(ray r, num t) {
#ifdef GEO_DEBUG
	if (t<0) fputs("WARNING: attempt to solve ray position for -ve t\n",stderr);
#endif
	return geo_add(r.start, geo_scale(r.dir, t));
}

tuple ray_nudge(tuple point, tuple dir) {
	return geo_add(point, geo_scale(dir, EPS));
}

/* Packing (ARGB) */

u32 geo_as_u32(tuple t) {
	uint8_t
		r = MAX(t.a, 1.0) * 255.0f,
		g = MAX(t.b, 1.0) * 255.0f,
		b = MAX(t.c, 1.0) * 255.0f
	;
	return (0xFF << 24) | (b << 16) | (g << 8) | (r << 0);
}

tuple geo_from_u32(uint32_t col) {
	return geo_new(
		((col >> 0 ) & 0xFF) / 255.0f,
		((col >> 8 ) & 0xFF) / 255.0f,
		((col >> 16) & 0xFF) / 255.0f
	);
}
