Some checks failed
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-macos (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
448 lines
12 KiB
C
448 lines
12 KiB
C
#include "qjs_geometry.h"
|
|
#include "jsffi.h"
|
|
#include "qjs_macros.h"
|
|
|
|
#include <SDL3/SDL.h>
|
|
#include <math.h>
|
|
#include "HandmadeMath.h"
|
|
#include "prosperon.h"
|
|
#include "font.h"
|
|
|
|
|
|
// GEOMETRY FUNCTIONS
|
|
|
|
JSC_CCALL(geometry_rect_intersection,
|
|
rect a = js2rect(js,argv[0]);
|
|
rect b = js2rect(js,argv[1]);
|
|
rect c;
|
|
SDL_GetRectIntersectionFloat(&a, &b, &c);
|
|
return rect2js(js,c);
|
|
)
|
|
|
|
JSC_CCALL(geometry_rect_intersects,
|
|
rect a = js2rect(js,argv[0]);
|
|
rect b = js2rect(js,argv[1]);
|
|
return JS_NewBool(js, SDL_HasRectIntersectionFloat(&a,&b));
|
|
)
|
|
|
|
JSC_CCALL(geometry_rect_inside,
|
|
rect inner = js2rect(js,argv[0]);
|
|
rect outer = js2rect(js,argv[1]);
|
|
return JS_NewBool(js,
|
|
inner.x >= outer.x &&
|
|
inner.x + inner.w <= outer.x + outer.w &&
|
|
inner.y >= outer.y &&
|
|
inner.y + inner.h <= outer.y + outer.h
|
|
);
|
|
)
|
|
|
|
JSC_CCALL(geometry_rect_random,
|
|
rect a = js2rect(js,argv[0]);
|
|
return vec22js(js,(HMM_Vec2){
|
|
a.x + rand_range(js,-0.5,0.5)*a.w,
|
|
a.y + rand_range(js,-0.5,0.5)*a.h
|
|
});
|
|
)
|
|
|
|
JSC_CCALL(geometry_rect_point_inside,
|
|
rect a = js2rect(js,argv[0]);
|
|
HMM_Vec2 p = js2vec2(js,argv[1]);
|
|
return JS_NewBool(js,p.x >= a.x && p.x <= a.x+a.w && p.y <= a.y+a.h && p.y >= a.y);
|
|
)
|
|
|
|
JSC_CCALL(geometry_cwh2rect,
|
|
HMM_Vec2 c = js2vec2(js,argv[0]);
|
|
HMM_Vec2 wh = js2vec2(js,argv[1]);
|
|
rect r;
|
|
r.x = c.x;
|
|
r.y = c.y;
|
|
r.w = wh.x;
|
|
r.h = wh.y;
|
|
return rect2js(js,r);
|
|
)
|
|
|
|
JSC_CCALL(geometry_rect_pos,
|
|
rect r = js2rect(js,argv[0]);
|
|
return vec22js(js,(HMM_Vec2){
|
|
.x = r.x,
|
|
.y = r.y
|
|
});
|
|
)
|
|
|
|
JSC_CCALL(geometry_rect_move,
|
|
rect r = js2rect(js,argv[0]);
|
|
HMM_Vec2 move = js2vec2(js,argv[1]);
|
|
r.x += move.x;
|
|
r.y += move.y;
|
|
return rect2js(js,r);
|
|
)
|
|
|
|
JSC_CCALL(geometry_rect_expand,
|
|
rect a = js2rect(js,argv[0]);
|
|
rect b = js2rect(js,argv[1]);
|
|
rect c = {0};
|
|
c.x = fmin(a.x,b.x);
|
|
c.y = fmin(a.y,b.y);
|
|
c.w = fmax(a.x+a.w,b.x+b.w);
|
|
c.h = fmax(a.y+a.h,b.y+b.h);
|
|
return rect2js(js,c);
|
|
)
|
|
|
|
// Helper functions for geometry operations
|
|
|
|
|
|
|
|
static inline void add_quad(text_vert **verts, rect *restrict src, rect *restrict dst)
|
|
{
|
|
text_vert v = (text_vert){
|
|
.pos = (HMM_Vec2){dst->x, dst->y},
|
|
.uv = (HMM_Vec2){src->x,src->y},
|
|
.color = (HMM_Vec4){1,1,1,1}
|
|
};
|
|
arrput(*verts, v);
|
|
|
|
v = (text_vert){
|
|
.pos = (HMM_Vec2){dst->x+dst->w, dst->y},
|
|
.uv = (HMM_Vec2){src->x+src->w,src->y},
|
|
.color = (HMM_Vec4){1,1,1,1}
|
|
};
|
|
arrput(*verts, v);
|
|
|
|
v = (text_vert){
|
|
.pos = (HMM_Vec2){dst->x, dst->y+dst->h},
|
|
.uv = (HMM_Vec2){src->x,src->y+src->h},
|
|
.color = (HMM_Vec4){1,1,1,1}
|
|
};
|
|
arrput(*verts, v);
|
|
|
|
v = (text_vert){
|
|
.pos = (HMM_Vec2){dst->x+dst->w, dst->y+dst->h},
|
|
.uv = (HMM_Vec2){src->x+src->w,src->y+src->h},
|
|
.color = (HMM_Vec4){1,1,1,1}
|
|
};
|
|
arrput(*verts, v);
|
|
}
|
|
|
|
static inline void tile_region(text_vert **verts, rect src_uv, rect dst, float tex_w, float tex_h, bool tile_x, bool tile_y)
|
|
{
|
|
// Convert the incoming UV rect into pixel coords
|
|
rect src_px = {
|
|
.x = src_uv.x * tex_w,
|
|
.y = src_uv.y * tex_h,
|
|
.w = src_uv.w * tex_w,
|
|
.h = src_uv.h * tex_h
|
|
};
|
|
|
|
// If src_px or dst is degenerate, early out
|
|
if (src_px.w <= 0.0f || src_px.h <= 0.0f || dst.w <= 0.0f || dst.h <= 0.0f)
|
|
return;
|
|
|
|
// How many full tiles horizontally/vertically?
|
|
// If not tiling in a dimension, we treat it as exactly 1 tile (no leftover).
|
|
float cols_f, rows_f;
|
|
float remain_wf = 0.0f;
|
|
float remain_hf = 0.0f;
|
|
|
|
if (tile_x) {
|
|
remain_wf = modff(dst.w / src_px.w, &cols_f);
|
|
} else {
|
|
// Only 1 "column" covering entire width, no leftover
|
|
cols_f = 1.0f;
|
|
remain_wf = 0.0f;
|
|
}
|
|
|
|
if (tile_y) {
|
|
remain_hf = modff(dst.h / src_px.h, &rows_f);
|
|
} else {
|
|
// Only 1 "row" covering entire height, no leftover
|
|
rows_f = 1.0f;
|
|
remain_hf = 0.0f;
|
|
}
|
|
|
|
int cols = (int)cols_f;
|
|
int rows = (int)rows_f;
|
|
|
|
// The leftover portion in screen coords (pixels)
|
|
float remain_dst_w = remain_wf * src_px.w;
|
|
float remain_dst_h = remain_hf * src_px.h;
|
|
|
|
// Build the UV rect for a "full" tile
|
|
rect src_full = src_uv;
|
|
|
|
// Partial leftover in UV
|
|
float remain_src_w_uv = remain_dst_w / tex_w;
|
|
float remain_src_h_uv = remain_dst_h / tex_h;
|
|
|
|
// For partial leftover in X dimension
|
|
rect src_partial_x = src_full;
|
|
src_partial_x.w = remain_src_w_uv;
|
|
|
|
// For partial leftover in Y dimension
|
|
rect src_partial_y = src_full;
|
|
src_partial_y.h = remain_src_h_uv;
|
|
|
|
// For partial leftover in both X & Y
|
|
rect src_partial_xy = src_full;
|
|
src_partial_xy.w = remain_src_w_uv;
|
|
src_partial_xy.h = remain_src_h_uv;
|
|
|
|
// Each tile is drawn 1:1 in screen coords
|
|
float tile_w = tile_x ? src_px.w : dst.w; // If not tiling horizontally, match the entire dst width
|
|
float tile_h = tile_y ? src_px.h : dst.h; // If not tiling vertically, match the entire dst height
|
|
|
|
rect curr_dst;
|
|
curr_dst.w = tile_w;
|
|
curr_dst.h = tile_h;
|
|
curr_dst.y = dst.y;
|
|
|
|
// Loop over rows
|
|
for (int y = 0; y < rows; y++) {
|
|
curr_dst.x = dst.x;
|
|
|
|
// Loop over columns
|
|
for (int x = 0; x < cols; x++) {
|
|
add_quad(verts, &src_full, &curr_dst);
|
|
curr_dst.x += tile_w;
|
|
}
|
|
|
|
// Right-side leftover tile (only if tile_x is true)
|
|
if (tile_x && remain_dst_w > 0.0f) {
|
|
rect partial_dst = {
|
|
.x = curr_dst.x, .y = curr_dst.y,
|
|
.w = remain_dst_w, .h = tile_h
|
|
};
|
|
add_quad(verts, &src_partial_x, &partial_dst);
|
|
}
|
|
curr_dst.y += tile_h;
|
|
}
|
|
|
|
// Bottom leftover row (only if tile_y is true)
|
|
if (tile_y && remain_dst_h > 0.0f) {
|
|
rect partial_row_dst;
|
|
partial_row_dst.w = tile_w;
|
|
partial_row_dst.h = remain_dst_h;
|
|
partial_row_dst.y = curr_dst.y;
|
|
partial_row_dst.x = dst.x;
|
|
|
|
// Full columns in leftover row
|
|
for (int x = 0; x < cols; x++) {
|
|
add_quad(verts, &src_partial_y, &partial_row_dst);
|
|
partial_row_dst.x += tile_w;
|
|
}
|
|
|
|
// Partial leftover corner (both X & Y leftover)
|
|
if (tile_x && remain_dst_w > 0.0f) {
|
|
rect partial_corner_dst = {
|
|
.x = partial_row_dst.x, .y = partial_row_dst.y,
|
|
.w = remain_dst_w, .h = remain_dst_h
|
|
};
|
|
add_quad(verts, &src_partial_xy, &partial_corner_dst);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
JSC_CCALL(gpu_slice9,
|
|
JSValue jstex = argv[0];
|
|
rect dst = js2rect(js, argv[1]);
|
|
|
|
// Full texture in UV coords
|
|
rect src = {
|
|
.x = 0, .y = 0,
|
|
.w = 1, .h = 1
|
|
};
|
|
|
|
// The "slice" LRTB in PIXELS, but we convert to UV below
|
|
lrtb src_slice = js2lrtb(js, argv[2]);
|
|
lrtb dst_slice = src_slice;
|
|
|
|
HMM_Vec2 size;
|
|
JS_GETPROP(js, size.x, jstex, width, number)
|
|
JS_GETPROP(js, size.y, jstex, height, number)
|
|
|
|
JSValue info = argv[3];
|
|
int tile_top, tile_bottom, tile_left, tile_right, center_x, center_y;
|
|
JS_GETPROP(js,tile_top, info, tile_top, bool)
|
|
JS_GETPROP(js,tile_bottom,info,tile_bottom,bool)
|
|
JS_GETPROP(js,tile_left,info,tile_left,bool)
|
|
JS_GETPROP(js,tile_right,info,tile_right,bool)
|
|
JS_GETPROP(js, center_x, info, tile_center_x, bool)
|
|
JS_GETPROP(js, center_y, info, tile_center_y, bool)
|
|
|
|
// Convert the slice edges from pixel to UV
|
|
src_slice.l /= size.x;
|
|
src_slice.r /= size.x;
|
|
src_slice.t /= size.y;
|
|
src_slice.b /= size.y;
|
|
|
|
text_vert *verts = NULL;
|
|
rect curr_src;
|
|
rect curr_dst;
|
|
|
|
// bottom-left corner (single quad)
|
|
curr_src = src;
|
|
curr_src.w = src_slice.l;
|
|
curr_src.h = src_slice.b;
|
|
|
|
curr_dst = dst;
|
|
curr_dst.w = dst_slice.l;
|
|
curr_dst.h = dst_slice.b;
|
|
add_quad(&verts, &curr_src, &curr_dst);
|
|
|
|
// top-left corner (single quad)
|
|
curr_src = src;
|
|
curr_src.x = src.x;
|
|
curr_src.y = src.y + src.h - src_slice.t;
|
|
curr_src.w = src_slice.l;
|
|
curr_src.h = src_slice.t;
|
|
|
|
curr_dst = dst;
|
|
curr_dst.x = dst.x;
|
|
curr_dst.y = dst.y + dst.h - dst_slice.t;
|
|
curr_dst.w = dst_slice.l;
|
|
curr_dst.h = dst_slice.t;
|
|
add_quad(&verts, &curr_src, &curr_dst);
|
|
|
|
// bottom-right corner (single quad)
|
|
curr_src = src;
|
|
curr_src.x = src.x + src.w - src_slice.r;
|
|
curr_src.y = src.y;
|
|
curr_src.w = src_slice.r;
|
|
curr_src.h = src_slice.b;
|
|
|
|
curr_dst = dst;
|
|
curr_dst.x = dst.x + dst.w - dst_slice.r;
|
|
curr_dst.y = dst.y;
|
|
curr_dst.w = dst_slice.r;
|
|
curr_dst.h = dst_slice.b;
|
|
add_quad(&verts, &curr_src, &curr_dst);
|
|
|
|
// top-right corner (single quad)
|
|
curr_src = src;
|
|
curr_src.x = src.x + src.w - src_slice.r;
|
|
curr_src.y = src.y + src.h - src_slice.t;
|
|
curr_src.w = src_slice.r;
|
|
curr_src.h = src_slice.t;
|
|
|
|
curr_dst = dst;
|
|
curr_dst.x = dst.x + dst.w - dst_slice.r;
|
|
curr_dst.y = dst.y + dst.h - dst_slice.t;
|
|
curr_dst.w = dst_slice.r;
|
|
curr_dst.h = dst_slice.t;
|
|
add_quad(&verts, &curr_src, &curr_dst);
|
|
|
|
// left bar (tiled)
|
|
curr_src = src;
|
|
curr_src.x = src.x;
|
|
curr_src.y = src.y + src_slice.b;
|
|
curr_src.w = src_slice.l;
|
|
curr_src.h = src.h - src_slice.t - src_slice.b;
|
|
|
|
curr_dst = dst;
|
|
curr_dst.x = dst.x;
|
|
curr_dst.y = dst.y + dst_slice.b;
|
|
curr_dst.w = dst_slice.l;
|
|
curr_dst.h = dst.h - dst_slice.t - dst_slice.b;
|
|
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_left,tile_left);
|
|
|
|
// right bar (tiled)
|
|
curr_src = src;
|
|
curr_src.x = src.x + src.w - src_slice.r;
|
|
curr_src.y = src.y + src_slice.b;
|
|
curr_src.w = src_slice.r;
|
|
curr_src.h = src.h - src_slice.t - src_slice.b;
|
|
|
|
curr_dst = dst;
|
|
curr_dst.x = dst.x + dst.w - dst_slice.r;
|
|
curr_dst.y = dst.y + dst_slice.b;
|
|
curr_dst.w = dst_slice.r;
|
|
curr_dst.h = dst.h - dst_slice.t - dst_slice.b;
|
|
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_right,tile_right);
|
|
|
|
// bottom bar (tiled)
|
|
curr_src = src;
|
|
curr_src.x = src.x + src_slice.l;
|
|
curr_src.y = src.y;
|
|
curr_src.w = src.w - src_slice.l - src_slice.r;
|
|
curr_src.h = src_slice.b;
|
|
|
|
curr_dst = dst;
|
|
curr_dst.x = dst.x + dst_slice.l;
|
|
curr_dst.y = dst.y;
|
|
curr_dst.w = dst.w - dst_slice.l - dst_slice.r;
|
|
curr_dst.h = dst_slice.b;
|
|
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_bottom,tile_bottom);
|
|
|
|
// top bar (tiled)
|
|
curr_src = src;
|
|
curr_src.x = src.x + src_slice.l;
|
|
curr_src.y = src.y + src.h - src_slice.t;
|
|
curr_src.w = src.w - src_slice.l - src_slice.r;
|
|
curr_src.h = src_slice.t;
|
|
|
|
curr_dst = dst;
|
|
curr_dst.x = dst.x + dst_slice.l;
|
|
curr_dst.y = dst.y + dst.h - dst_slice.t;
|
|
curr_dst.w = dst.w - dst_slice.l - dst_slice.r;
|
|
curr_dst.h = dst_slice.t;
|
|
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_top,tile_top);
|
|
|
|
// center (tiled)
|
|
curr_src = src;
|
|
curr_src.x = src.x + src_slice.l;
|
|
curr_src.y = src.y + src_slice.b;
|
|
curr_src.w = src.w - src_slice.l - src_slice.r;
|
|
curr_src.h = src.h - src_slice.t - src_slice.b;
|
|
|
|
curr_dst = dst;
|
|
curr_dst.x = dst.x + dst_slice.l;
|
|
curr_dst.y = dst.y + dst_slice.b;
|
|
curr_dst.w = dst.w - dst_slice.l - dst_slice.r;
|
|
curr_dst.h = dst.h - dst_slice.t - dst_slice.b;
|
|
tile_region(&verts, curr_src, curr_dst, size.x, size.y, center_x,center_y);
|
|
|
|
JSValue mesh = quads_to_mesh(js, verts);
|
|
arrfree(verts);
|
|
ret = mesh;
|
|
)
|
|
|
|
JSC_CCALL(gpu_tile,
|
|
HMM_Vec2 size;
|
|
JSValue jstex = argv[0];
|
|
JS_GETATOM(js,size.x,jstex,width,number)
|
|
JS_GETATOM(js, size.y, jstex, height, number)
|
|
|
|
rect src_pixels = js2rect(js, argv[1]); // 'src' as pixel dimensions
|
|
rect dst = js2rect(js, argv[2]); // 'dst' as screen coords
|
|
|
|
int tilex, tiley;
|
|
JSValue jstile = argv[3];
|
|
JS_GETPROP(js,tilex,jstile,repeat_x,bool)
|
|
JS_GETPROP(js,tiley,jstile,repeat_y,bool)
|
|
|
|
text_vert *verts = NULL;
|
|
tile_region(&verts, src_pixels, dst, size.x, size.y,tilex,tiley);
|
|
ret = quads_to_mesh(js,verts);
|
|
arrfree(verts);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_geometry_funcs[] = {
|
|
MIST_FUNC_DEF(geometry, rect_intersection, 2),
|
|
MIST_FUNC_DEF(geometry, rect_intersects, 2),
|
|
MIST_FUNC_DEF(geometry, rect_expand, 2),
|
|
MIST_FUNC_DEF(geometry, rect_inside, 2),
|
|
MIST_FUNC_DEF(geometry, rect_random, 1),
|
|
MIST_FUNC_DEF(geometry, cwh2rect, 2),
|
|
MIST_FUNC_DEF(geometry, rect_point_inside, 2),
|
|
MIST_FUNC_DEF(geometry, rect_pos, 1),
|
|
MIST_FUNC_DEF(geometry, rect_move, 2),
|
|
MIST_FUNC_DEF(gpu, tile, 4),
|
|
MIST_FUNC_DEF(gpu, slice9, 3),
|
|
};
|
|
|
|
JSValue js_geometry_use(JSContext *js) {
|
|
JSValue mod = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js,mod,js_geometry_funcs,countof(js_geometry_funcs));
|
|
return mod;
|
|
}
|