Files
cell/source/qjs_math.c
2025-11-25 21:05:48 -06:00

492 lines
11 KiB
C

#include "jsffi.h"
#include "qjs_macros.h"
#include <math.h>
#include <stdlib.h>
#include "HandmadeMath.h"
#include "cell.h"
// Utility function to get number from array index
static double js_getnum_uint32(JSContext *js, JSValue v, unsigned int i)
{
JSValue val = JS_GetPropertyUint32(js,v,i);
double ret = js2number(js, val);
JS_FreeValue(js,val);
return ret;
}
// Convert JS array to float array
static float *js2floats(JSContext *js, JSValue v, size_t *len)
{
*len = JS_ArrayLength(js,v);
float *arr = malloc(sizeof(float)* *len);
for (int i = 0; i < *len; i++)
arr[i] = js_getnum_uint32(js,v,i);
return arr;
}
// Convert float array to JS array
static JSValue floats2array(JSContext *js, float *vals, size_t len) {
JSValue arr = JS_NewArray(js);
for (size_t i = 0; i < len; i++) {
JS_SetPropertyUint32(js, arr, i, number2js(js, vals[i]));
}
return arr;
}
// Calculate vector length
static double arr_vec_length(JSContext *js,JSValue v)
{
int len = JS_ArrayLength(js,v);
switch(len) {
case 2: return HMM_LenV2(js2vec2(js,v));
case 3: return HMM_LenV3(js2vec3(js,v));
case 4: return HMM_LenV4(js2vec4(js,v));
}
double sum = 0;
for (int i = 0; i < len; i++)
sum += pow(js_getnum_uint32(js, v, i), 2);
return sqrt(sum);
}
// GCD helper function
static int gcd(int a, int b) {
if (b == 0)
return a;
return gcd(b, a % b);
}
// MATH FUNCTIONS
JSC_CCALL(math_rotate,
HMM_Vec2 vec = js2vec2(js,argv[0]);
double angle = js2angle(js, argv[1]);
HMM_Vec2 pivot = JS_IsNull(argv[2]) ? v2zero : js2vec2(js,argv[2]);
vec = HMM_SubV2(vec,pivot);
float r = HMM_LenV2(vec);
angle += atan2f(vec.y, vec.x);
vec.x = r * cosf(angle);
vec.y = r * sinf(angle);
vec = HMM_AddV2(vec,pivot);
return vec22js(js, vec);
)
JSC_CCALL(math_norm,
int len = JS_ArrayLength(js,argv[0]);
switch(len) {
case 2: return vec22js(js,HMM_NormV2(js2vec2(js,argv[0])));
case 3: return vec32js(js, HMM_NormV3(js2vec3(js,argv[0])));
case 4: return vec42js(js,HMM_NormV4(js2vec4(js,argv[0])));
}
double length = arr_vec_length(js,argv[0]);
JSValue newarr = JS_NewArray(js);
for (int i = 0; i < len; i++)
JS_SetPropertyUint32(js, newarr, i, number2js(js,js_getnum_uint32(js, argv[0],i)/length));
ret = newarr;
)
JSC_CCALL(math_angle_between,
int len = JS_ArrayLength(js,argv[0]);
switch(len) {
case 2: return angle2js(js,HMM_AngleV2(js2vec2(js,argv[0]), js2vec2(js,argv[1])));
case 3: return angle2js(js,HMM_AngleV3(js2vec3(js,argv[0]), js2vec3(js,argv[1])));
case 4: return angle2js(js,HMM_AngleV4(js2vec4(js,argv[0]), js2vec4(js,argv[1])));
}
return JS_ThrowReferenceError(js, "Input array must have a length between 2 and 4.");
)
JSC_CCALL(math_lerp,
double s = js2number(js,argv[0]);
double f = js2number(js,argv[1]);
double t = js2number(js,argv[2]);
ret = number2js(js,(f-s)*t+s);
)
JSC_CCALL(math_gcd, ret = number2js(js,gcd(js2number(js,argv[0]), js2number(js,argv[1]))); )
JSC_CCALL(math_lcm,
double a = js2number(js,argv[0]);
double b = js2number(js,argv[1]);
ret = number2js(js,(a*b)/gcd(a,b));
)
JSC_CCALL(math_clamp,
double x = js2number(js,argv[0]);
double l = js2number(js,argv[1]);
double h = js2number(js,argv[2]);
return number2js(js,x > h ? h : x < l ? l : x);
)
JSC_CCALL(math_angledist,
double a1 = js2number(js,argv[0]);
double a2 = js2number(js,argv[1]);
a1 = fmod(a1,1);
a2 = fmod(a2,1);
double dist = a2-a1;
if (dist == 0) return number2js(js,dist);
if (dist > 0) {
if (dist > 0.5) return number2js(js,dist-1);
return number2js(js,dist);
}
if (dist < -0.5) return number2js(js,dist+1);
return number2js(js,dist);
)
JSC_CCALL(math_jitter,
double n = js2number(js,argv[0]);
double pct = js2number(js,argv[1]);
return number2js(js,n + (rand_range(js,-pct,pct)*n));
)
JSC_CCALL(math_mean,
double len = JS_ArrayLength(js,argv[0]);
double sum = 0;
for (int i = 0; i < len; i++)
sum += js_getnum_uint32(js, argv[0], i);
return number2js(js,sum/len);
)
JSC_CCALL(math_sum,
double sum = 0.0;
int len = JS_ArrayLength(js,argv[0]);
for (int i = 0; i < len; i++)
sum += js_getnum_uint32(js, argv[0], i);
return number2js(js,sum);
)
JSC_CCALL(math_sigma,
int len = JS_ArrayLength(js,argv[0]);
double sum = 0;
for (int i = 0; i < len; i++)
sum += js_getnum_uint32(js, argv[0], i);
double mean = sum/(double)len;
sum = 0;
for (int i = 0; i < len; i++)
sum += pow(js_getnum_uint32(js, argv[0], i) - mean, 2);
return number2js(js,sqrt(sum/len));
)
JSC_CCALL(math_median,
int len = JS_ArrayLength(js,argv[0]);
double vals[len];
for (int i = 0; i < len; i++)
vals[i] = js_getnum_uint32(js, argv[0], i);
// Simple bubble sort for median calculation
for (int i = 0; i < len-1; i++) {
for (int j = 0; j < len-i-1; j++) {
if (vals[j] > vals[j+1]) {
double temp = vals[j];
vals[j] = vals[j+1];
vals[j+1] = temp;
}
}
}
if (len % 2 == 0)
return number2js(js,(vals[len/2-1] + vals[len/2]) / 2.0);
else
return number2js(js,vals[len/2]);
)
JSC_CCALL(math_length, return number2js(js,arr_vec_length(js,argv[0])); )
JSC_CCALL(math_from_to,
double start = js2number(js,argv[0]);
double end = js2number(js,argv[1]);
double step = js2number(js,argv[2]);
int inclusive = JS_ToBool(js,argv[3]);
int arr = JS_ToBool(js,argv[4]);
JSValue jsarr = JS_NewArray(js);
int i = 0;
for (double val = start; val <= end; val += step) {
if (val == end && !inclusive) break;
JS_SetPropertyUint32(js, jsarr, i++, number2js(js, val));
}
return jsarr;
)
JSC_CCALL(math_dot,
size_t alen, blen;
float *a = js2floats(js,argv[0], &alen);
float *b = js2floats(js,argv[1], &blen);
float dot = 0;
size_t len = alen < blen? alen : blen;
for (size_t i = 0; i < len; i++)
dot += a[i] * b[i];
free(a);
free(b);
return number2js(js,dot);
)
JSC_CCALL(math_project,
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_NULL;
}
// We'll work up to the smaller length
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_NULL;
}
// Compute dot products: a·b and b·b
float ab = 0, bb = 0;
for (size_t i = 0; i < len; i++) {
ab += a[i] * b[i];
bb += b[i] * b[i];
}
// Build the result array
float *proj = (float*)malloc(sizeof(float) * len);
if (!proj) {
free(a);
free(b);
return JS_EXCEPTION; // or some error
}
float scale = (bb != 0.0f) ? (ab / bb) : 0.0f;
for (size_t i = 0; i < len; i++)
proj[i] = scale * b[i];
ret = floats2array(js, proj, len);
free(a);
free(b);
free(proj);
)
JSC_CCALL(math_midpoint,
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_NULL;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_NULL;
}
float *m = (float*)malloc(sizeof(float) * len);
if (!m) {
free(a);
free(b);
return JS_EXCEPTION;
}
for (size_t i = 0; i < len; i++)
m[i] = (a[i] + b[i]) * 0.5f;
ret = floats2array(js, m, len);
free(a);
free(b);
free(m);
)
JSC_CCALL(math_reflect,
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_NULL;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_NULL;
}
// Reflect vector a across normal b
// reflection = a - 2 * (a·b) * b
float ab = 0, bb = 0;
for (size_t i = 0; i < len; i++) {
ab += a[i] * b[i];
bb += b[i] * b[i];
}
float *result = (float*)malloc(sizeof(float) * len);
if (!result) {
free(a);
free(b);
return JS_EXCEPTION;
}
float scale = (bb != 0.0f) ? (2.0f * ab / bb) : 0.0f;
for (size_t i = 0; i < len; i++)
result[i] = a[i] - scale * b[i];
ret = floats2array(js, result, len);
free(a);
free(b);
free(result);
)
JSC_CCALL(math_direction,
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_NULL;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_NULL;
}
// Direction vector from a to b (normalized)
float *dir = (float*)malloc(sizeof(float) * len);
if (!dir) {
free(a);
free(b);
return JS_EXCEPTION;
}
float mag = 0;
for (size_t i = 0; i < len; i++) {
dir[i] = b[i] - a[i];
mag += dir[i] * dir[i];
}
mag = sqrtf(mag);
if (mag > 0.0f) {
for (size_t i = 0; i < len; i++)
dir[i] /= mag;
}
ret = floats2array(js, dir, len);
free(a);
free(b);
free(dir);
)
JSC_CCALL(math_angle,
size_t len;
float *v = js2floats(js, argv[0], &len);
if (!v || len < 2) {
free(v);
return JS_NULL;
}
// Return angle in radians for 2D vector
ret = number2js(js, atan2f(v[1], v[0]));
free(v);
)
JSC_CCALL(math_distance,
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_NULL;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_NULL;
}
float distSq = 0.0f;
for (size_t i = 0; i < len; i++) {
float diff = b[i] - a[i];
distSq += diff * diff;
}
float dist = sqrtf(distSq);
free(a);
free(b);
return number2js(js, dist);
)
static const JSCFunctionListEntry js_math_funcs[] = {
MIST_FUNC_DEF(math, dot,2),
MIST_FUNC_DEF(math, project,2),
MIST_FUNC_DEF(math, rotate, 3),
MIST_FUNC_DEF(math, midpoint, 2),
MIST_FUNC_DEF(math, reflect, 2),
MIST_FUNC_DEF(math, distance, 2),
MIST_FUNC_DEF(math, direction, 2),
MIST_FUNC_DEF(math, angle, 1),
MIST_FUNC_DEF(math, norm, 1),
MIST_FUNC_DEF(math, angle_between, 2),
MIST_FUNC_DEF(math, lerp, 3),
MIST_FUNC_DEF(math, gcd, 2),
MIST_FUNC_DEF(math, lcm, 2),
MIST_FUNC_DEF(math, clamp, 3),
MIST_FUNC_DEF(math, angledist, 2),
MIST_FUNC_DEF(math, jitter, 2),
MIST_FUNC_DEF(math, mean, 1),
MIST_FUNC_DEF(math, sum, 1),
MIST_FUNC_DEF(math, sigma, 1),
MIST_FUNC_DEF(math, median, 1),
MIST_FUNC_DEF(math, length, 1),
MIST_FUNC_DEF(math, from_to, 5),
};
JSValue js_math_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_math_funcs,countof(js_math_funcs));
return mod;
}