#include "qjs_geometry.h" #include "jsffi.h" #include "qjs_macros.h" #include #include #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; }