diff --git a/meson.build b/meson.build index 81d3bd45..6a89fd72 100644 --- a/meson.build +++ b/meson.build @@ -274,10 +274,57 @@ endif link_args = link sources = [] src += [ - 'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c', - 'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c', - 'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_sdl_gpu.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c', - 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c', 'qjs_staef.c', 'qjs_layout.c', 'cell_qoi.c' + 'qjs_http.c', + 'qjs_blob.c', + 'qjs_nota.c', + 'monocypher.c', + 'qjs_crypto.c', + 'qjs_time.c', + 'qjs_js.c', + 'qjs_debug.c', + 'qjs_miniz.c', + 'timer.c', + 'qjs_socket.c', + 'qjs_kim.c', + 'qjs_utf8.c', + 'qjs_fit.c', + 'qjs_text.c', + 'jsffi.c', + 'cell.c', + 'wildmatch.c', + 'qjs_io.c', + 'qjs_fd.c', + 'qjs_os.c', + 'qjs_actor.c', + 'qjs_wota.c', +] + +src += [ + 'anim.c', + 'config.c', + 'datastream.c', + 'font.c', + 'HandmadeMath.c', + 'model.c', + 'render.c', + 'simplex.c', + 'spline.c', + 'transform.c', + 'sprite.c', + 'qjs_soloud.c', + 'qjs_sdl.c', + 'qjs_sdl_input.c', + 'qjs_sdl_video.c', + 'qjs_sdl_surface.c', + 'qjs_sdl_gpu.c', + 'qjs_math.c', + 'qjs_geometry.c', + 'qjs_transform.c', + 'qjs_sprite.c', + 'qjs_spline.c', + 'qjs_layout.c', + 'qjs_staef.c', + 'cell_qoi.c' ] # quirc src src += [ diff --git a/source/jsffi.c b/source/jsffi.c index ce495cff..37097a2f 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -42,7 +42,6 @@ #include "qjs_sdl_gpu.h" #include "qjs_os.h" #include "qjs_actor.h" -#include "qjs_rtree.h" #include "qjs_spline.h" #include "qjs_js.h" #include "qjs_debug.h" @@ -1168,7 +1167,6 @@ void ffi_load(JSContext *js) // arrput(rt->module_registry, MISTLINE(imgui)); arrput(rt->module_registry, ((ModuleEntry){"camera", js_camera_use})); - arrput(rt->module_registry, MISTLINE(rtree)); arrput(rt->module_registry, MISTLINE(sprite)); arrput(rt->module_registry, MISTLINE(transform)); diff --git a/source/qjs_rtree.c b/source/qjs_rtree.c deleted file mode 100644 index 6785cc81..00000000 --- a/source/qjs_rtree.c +++ /dev/null @@ -1,203 +0,0 @@ -#include "qjs_rtree.h" -#include "qjs_macros.h" -#include "jsffi.h" -#include "rtree.h" -#include "cell.h" -#include -#include - -// External declarations -typedef struct rtree rtree; - -void rtree_free(JSRuntime *rt, rtree *tree) -{ - rtree_destroy(tree); -} - -QJSCLASS(rtree,) - -JSC_CCALL(rtree_add, - rtree *tree = js2rtree(js,self); - JSValue v = argv[0]; - rect r; - JS_GETATOM(js,r,v,rect,rect) - NUMTYPE min[3]; - NUMTYPE max[3]; - min[0] = r.x; - min[1] = r.y; - min[2] = 0; - max[0] = r.x+r.w; - max[1] = r.y+r.h; - max[2] = 0; - JSValue *ins = malloc(sizeof(*ins)); - *ins = JS_DupValue(js,v); - if (!rtree_insert(tree, min, max, ins)) { - JS_FreeValue(js,*ins); - return JS_ThrowOutOfMemory(js); - } -) - -int rtree_cmp(const JSValue *a, const JSValue *b, JSContext *js) -{ - int same = JS_SameValue(js, *a, *b); - if (same) - JS_FreeValue(js,*a); - - return !same; -} - -JSC_CCALL(rtree_delete, - rtree *tree = js2rtree(js,self); - JSValue v = argv[0]; - rect r; - JS_GETATOM(js,r,v,rect,rect) - NUMTYPE min[3]; - NUMTYPE max[3]; - min[0] = r.x; - min[1] = r.y; - min[2] = 0; - max[0] = r.x+r.w; - max[1] = r.y+r.h; - max[2] = 0; - - if (!rtree_delete_with_comparator(tree, min, max, &v, rtree_cmp, js)) - return JS_ThrowOutOfMemory(js); -) - -struct rtree_iter_data { - JSContext *js; - JSValue arr; - int n; -}; - -bool rtree_iter(const NUMTYPE *min, const NUMTYPE *max, const JSValue *data, struct rtree_iter_data *ctx) -{ - JS_SetPropertyUint32(ctx->js,ctx->arr,ctx->n, JS_DupValue(ctx->js,*data)); - ctx->n++; - return 1; -} - -JSC_CCALL(rtree_query, - rtree *tree = js2rtree(js,self); - rect r = js2rect(js,argv[0]); - NUMTYPE min[3]; - NUMTYPE max[3]; - min[0] = r.x; - min[1] = r.y; - min[2] = 0; - max[0] = r.x+r.w; - max[1] = r.y+r.h; - max[2] = 0; - - struct rtree_iter_data data = {0}; - data.js = js; - data.arr = JS_NewArray(js); - data.n = 0; - rtree_search(tree, min, max, rtree_iter, &data); - ret = data.arr; -) - -struct rtree_each -{ - JSValue fn; - JSContext *js; -}; - -int rtree_foreach(const NUMTYPE *min, const NUMTYPE *max, const JSValue *value, struct rtree_each *each) -{ - JSValue ret = JS_Call(each->js, each->fn, JS_NULL, 0, NULL); - uncaught_exception(each->js, ret); - return 1; -} - -JSC_CCALL(rtree_forEach, - rtree *tree = js2rtree(js,self); - struct rtree_each each; - each.fn = JS_DupValue(js,argv[0]); - each.js = js; - - rtree_scan(tree, rtree_foreach, &each); - JS_FreeValue(js,each.fn); -) - -typedef struct { - JSContext *js; - JSValue v; - int has; -} rtree_has; - -int rtree_hasfn(const NUMTYPE *min, const NUMTYPE *max, const JSValue *value, rtree_has *has) -{ - if (JS_SameValue(has->js, has->v, *value)) { - has->has = 1; - return 0; - } - return 1; -} - -JSC_CCALL(rtree_has, - rtree *tree = js2rtree(js,self); - rtree_has has; - has.js = js; - has.v = JS_DupValue(js,argv[0]); - has.has = 0; - rtree_scan(tree, rtree_hasfn, &has); - JS_FreeValue(js,argv[0]); - return JS_NewBool(js,has.has); -) - -JSValue js_rtree_get_size(JSContext *js, JSValue self, int magic) -{ - rtree *tree = js2rtree(js,self); - return number2js(js,rtree_count(tree)); -} - -int rtree_valuefn(const NUMTYPE *min, const NUMTYPE *max, const JSValue *value, struct rtree_iter_data *data) -{ - JS_SetPropertyUint32(data->js, data->arr, data->n, JS_DupValue(data->js, *value)); - data->n++; - return 1; -} - -JSC_CCALL(rtree_values, - rtree *tree = js2rtree(js,self); - struct rtree_iter_data data = {0}; - data.js = js; - data.arr = JS_NewArray(js); - data.n = 0; - rtree_scan(tree, rtree_valuefn, &data); - - ret = data.arr; -) - -// Constructor function for rtree -static JSValue js_rtree_constructor(JSContext *js, JSValueConst new_target, int argc, JSValueConst *argv) { - struct rtree *tree = rtree_new(); - if (!tree) return JS_ThrowOutOfMemory(js); - return rtree2js(js,tree); -} - -static const JSCFunctionListEntry js_rtree_funcs[] = { - MIST_FUNC_DEF(rtree, add, 1), - MIST_FUNC_DEF(rtree, delete, 1), - MIST_FUNC_DEF(rtree, query, 1), - JS_CGETSET_DEF("size", js_rtree_get_size,NULL), - MIST_FUNC_DEF(rtree, forEach, 1), - MIST_FUNC_DEF(rtree, has, 1), - MIST_FUNC_DEF(rtree,values,0), -}; - -JSValue js_rtree_use(JSContext *js) { - // Register the rtree class - QJSCLASSPREP_FUNCS(rtree); - - // Create the constructor function - JSValue ctor = JS_NewCFunction2(js, js_rtree_constructor, "rtree", 0, JS_CFUNC_constructor, 0); - - // Set the prototype on the constructor - JSValue proto = JS_GetClassProto(js, js_rtree_id); - JS_SetConstructor(js, ctor, proto); - JS_FreeValue(js, proto); - - return ctor; -} \ No newline at end of file diff --git a/source/qjs_rtree.h b/source/qjs_rtree.h deleted file mode 100644 index 7d33df92..00000000 --- a/source/qjs_rtree.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef QJS_RTREE_H -#define QJS_RTREE_H - -#include "cell.h" - -JSValue js_rtree_use(JSContext *ctx); - -#endif diff --git a/source/rtree.c b/source/rtree.c deleted file mode 100644 index 4389dec5..00000000 --- a/source/rtree.c +++ /dev/null @@ -1,839 +0,0 @@ -// Copyright 2023 Joshua J Baker. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -#include -#include -#include -#include "rtree.h" - -//////////////////////////////// - -#define DATATYPE void * -#define DIMS 3 -#define MAXITEMS 64 - -//////////////////////////////// - -// used for splits -#define MINITEMS_PERCENTAGE 10 -#define MINITEMS ((MAXITEMS) * (MINITEMS_PERCENTAGE) / 100 + 1) - -#ifndef RTREE_NOPATHHINT -#define USE_PATHHINT -#endif - -#ifdef RTREE_MAXITEMS -#undef MAXITEMS -#define MAXITEMS RTREE_MAXITEMS -#endif - -#ifdef RTREE_NOATOMICS -typedef int rc_t; -static int rc_load(rc_t *ptr, bool relaxed) { - (void)relaxed; // nothing to do - return *ptr; -} -static int rc_fetch_sub(rc_t *ptr, int val) { - int rc = *ptr; - *ptr -= val; - return rc; -} -static int rc_fetch_add(rc_t *ptr, int val) { - int rc = *ptr; - *ptr += val; - return rc; -} -#else -#include -typedef atomic_int rc_t; -static int rc_load(rc_t *ptr, bool relaxed) { - if (relaxed) { - return atomic_load_explicit(ptr, memory_order_relaxed); - } else { - return atomic_load(ptr); - } -} -static int rc_fetch_sub(rc_t *ptr, int delta) { - return atomic_fetch_sub(ptr, delta); -} -static int rc_fetch_add(rc_t *ptr, int delta) { - return atomic_fetch_add(ptr, delta); -} -#endif - -enum kind { - LEAF = 1, - BRANCH = 2, -}; - -struct rect { - NUMTYPE min[DIMS]; - NUMTYPE max[DIMS]; -}; - -struct item { - const DATATYPE data; -}; - -struct node { - rc_t rc; // reference counter for copy-on-write - enum kind kind; // LEAF or BRANCH - int count; // number of rects - struct rect rects[MAXITEMS]; - union { - struct node *nodes[MAXITEMS]; - struct item datas[MAXITEMS]; - }; -}; - -struct rtree { - struct rect rect; - struct node *root; - size_t count; - size_t height; -#ifdef USE_PATHHINT - int path_hint[16]; -#endif - bool relaxed; - void *(*malloc)(size_t); - void (*free)(void *); - void *udata; - bool (*item_clone)(const DATATYPE item, DATATYPE *into, void *udata); - void (*item_free)(const DATATYPE item, void *udata); -}; - -static inline NUMTYPE min0(NUMTYPE x, NUMTYPE y) { - return x < y ? x : y; -} - -static inline NUMTYPE max0(NUMTYPE x, NUMTYPE y) { - return x > y ? x : y; -} - -static bool feq(NUMTYPE a, NUMTYPE b) { - return !(a < b || a > b); -} - - -void rtree_set_udata(struct rtree *tr, void *udata) { - tr->udata = udata; -} - -static struct node *node_new(struct rtree *tr, enum kind kind) { - struct node *node = (struct node *)tr->malloc(sizeof(struct node)); - if (!node) return NULL; - memset(node, 0, sizeof(struct node)); - node->kind = kind; - return node; -} - -static struct node *node_copy(struct rtree *tr, struct node *node) { - struct node *node2 = (struct node *)tr->malloc(sizeof(struct node)); - if (!node2) return NULL; - memcpy(node2, node, sizeof(struct node)); - node2->rc = 0; - if (node2->kind == BRANCH) { - for (int i = 0; i < node2->count; i++) { - rc_fetch_add(&node2->nodes[i]->rc, 1); - } - } else { - if (tr->item_clone) { - int n = 0; - bool oom = false; - for (int i = 0; i < node2->count; i++) { - if (!tr->item_clone(node->datas[i].data, - (DATATYPE*)&node2->datas[i].data, tr->udata)) - { - oom = true; - break; - } - n++; - } - if (oom) { - if (tr->item_free) { - for (int i = 0; i < n; i++) { - tr->item_free(node2->datas[i].data, tr->udata); - } - } - tr->free(node2); - return NULL; - } - } - } - return node2; -} - -static void node_free(struct rtree *tr, struct node *node) { - if (rc_fetch_sub(&node->rc, 1) > 0) return; - if (node->kind == BRANCH) { - for (int i = 0; i < node->count; i++) { - node_free(tr, node->nodes[i]); - } - } else { - if (tr->item_free) { - for (int i = 0; i < node->count; i++) { - tr->item_free(node->datas[i].data, tr->udata); - } - } - } - tr->free(node); -} - -#define cow_node_or(rnode, code) { \ - if (rc_load(&(rnode)->rc, tr->relaxed) > 0) { \ - struct node *node2 = node_copy(tr, (rnode)); \ - if (!node2) { code; } \ - node_free(tr, rnode); \ - (rnode) = node2; \ - } \ -} - -static void rect_expand(struct rect *rect, const struct rect *other) { - for (int i = 0; i < DIMS; i++) { - rect->min[i] = min0(rect->min[i], other->min[i]); - rect->max[i] = max0(rect->max[i], other->max[i]); - } -} - -static NUMTYPE rect_area(const struct rect *rect) { - NUMTYPE result = 1; - for (int i = 0; i < DIMS; i++) { - result *= (rect->max[i] - rect->min[i]); - } - return result; -} - -// return the area of two rects expanded -static NUMTYPE rect_unioned_area(const struct rect *rect, - const struct rect *other) -{ - NUMTYPE result = 1; - for (int i = 0; i < DIMS; i++) { - result *= (max0(rect->max[i], other->max[i]) - - min0(rect->min[i], other->min[i])); - } - return result; -} - -static bool rect_contains(const struct rect *rect, const struct rect *other) { - int bits = 0; - for (int i = 0; i < DIMS; i++) { - bits |= other->min[i] < rect->min[i]; - bits |= other->max[i] > rect->max[i]; - } - return bits == 0; -} - -static bool rect_intersects(const struct rect *rect, const struct rect *other) { - int bits = 0; - for (int i = 0; i < DIMS; i++) { - bits |= other->min[i] > rect->max[i]; - bits |= other->max[i] < rect->min[i]; - } - return bits == 0; -} - -static bool rect_onedge(const struct rect *rect, const struct rect *other) { - for (int i = 0; i < DIMS; i++) { - if (feq(rect->min[i], other->min[i]) || - feq(rect->max[i], other->max[i])) - { - return true; - } - } - return false; -} - -static bool rect_equals(const struct rect *rect, const struct rect *other) { - for (int i = 0; i < DIMS; i++) { - if (!feq(rect->min[i], other->min[i]) || - !feq(rect->max[i], other->max[i])) - { - return false; - } - } - return true; -} - -static bool rect_equals_bin(const struct rect *rect, const struct rect *other) { - for (int i = 0; i < DIMS; i++) { - if (rect->min[i] != other->min[i] || - rect->max[i] != other->max[i]) - { - return false; - } - } - return true; -} - -static int rect_largest_axis(const struct rect *rect) { - int axis = 0; - NUMTYPE nlength = rect->max[0] - rect->min[0]; - for (int i = 1; i < DIMS; i++) { - NUMTYPE length = rect->max[i] - rect->min[i]; - if (length > nlength) { - nlength = length; - axis = i; - } - } - return axis; -} - -// swap two rectangles -static void node_swap(struct node *node, int i, int j) { - struct rect tmp = node->rects[i]; - node->rects[i] = node->rects[j]; - node->rects[j] = tmp; - if (node->kind == LEAF) { - struct item tmp = node->datas[i]; - node->datas[i] = node->datas[j]; - node->datas[j] = tmp; - } else { - struct node *tmp = node->nodes[i]; - node->nodes[i] = node->nodes[j]; - node->nodes[j] = tmp; - } -} - -struct rect4 { - NUMTYPE all[DIMS*2]; -}; - -static void node_qsort(struct node *node, int s, int e, int index) { - int nrects = e - s; - if (nrects < 2) { - return; - } - int left = 0; - int right = nrects-1; - int pivot = nrects / 2; - node_swap(node, s+pivot, s+right); - struct rect4 *rects = (struct rect4 *)&node->rects[s]; - for (int i = 0; i < nrects; i++) { - if (rects[right].all[index] < rects[i].all[index]) { - node_swap(node, s+i, s+left); - left++; - } - } - node_swap(node, s+left, s+right); - node_qsort(node, s, s+left, index); - node_qsort(node, s+left+1, e, index); -} - -// sort the node rectangles by the axis. used during splits -static void node_sort_by_axis(struct node *node, int axis, bool max) { - int by_index = max ? DIMS+axis : axis; - node_qsort(node, 0, node->count, by_index); -} - -static void node_move_rect_at_index_into(struct node *from, int index, - struct node *into) -{ - into->rects[into->count] = from->rects[index]; - from->rects[index] = from->rects[from->count-1]; - if (from->kind == LEAF) { - into->datas[into->count] = from->datas[index]; - from->datas[index] = from->datas[from->count-1]; - } else { - into->nodes[into->count] = from->nodes[index]; - from->nodes[index] = from->nodes[from->count-1]; - } - from->count--; - into->count++; -} - -static bool node_split_largest_axis_edge_snap(struct rtree *tr, - struct rect *rect, struct node *node, struct node **right_out) -{ - int axis = rect_largest_axis(rect); - struct node *right = node_new(tr, node->kind); - if (!right) { - return false; - } - for (int i = 0; i < node->count; i++) { - NUMTYPE min_dist = node->rects[i].min[axis] - rect->min[axis]; - NUMTYPE max_dist = rect->max[axis] - node->rects[i].max[axis]; - if (max_dist < min_dist) { - // move to right - node_move_rect_at_index_into(node, i, right); - i--; - } - } - // Make sure that both left and right nodes have at least - // MINITEMS by moving datas into underflowed nodes. - if (node->count < MINITEMS) { - // reverse sort by min axis - node_sort_by_axis(right, axis, false); - do { - node_move_rect_at_index_into(right, right->count-1, node); - } while (node->count < MINITEMS); - } else if (right->count < MINITEMS) { - // reverse sort by max axis - node_sort_by_axis(node, axis, true); - do { - node_move_rect_at_index_into(node, node->count-1, right); - } while (right->count < MINITEMS); - } - if (node->kind == BRANCH) { - node_sort_by_axis(node, 0, false); - node_sort_by_axis(right, 0, false); - } - *right_out = right; - return true; -} - -static bool node_split(struct rtree *tr, struct rect *rect, struct node *node, - struct node **right) -{ - return node_split_largest_axis_edge_snap(tr, rect, node, right); -} - -static int node_choose_least_enlargement(const struct node *node, - const struct rect *ir) -{ - int j = 0; - NUMTYPE jenlarge = INFINITY; - for (int i = 0; i < node->count; i++) { - // calculate the enlarged area - NUMTYPE uarea = rect_unioned_area(&node->rects[i], ir); - NUMTYPE area = rect_area(&node->rects[i]); - NUMTYPE enlarge = uarea - area; - if (enlarge < jenlarge) { - j = i; - jenlarge = enlarge; - } - } - return j; -} - -static int node_choose(struct rtree *tr, const struct node *node, - const struct rect *rect, int depth) -{ -#ifdef USE_PATHHINT - int h = tr->path_hint[depth]; - if (h < node->count) { - if (rect_contains(&node->rects[h], rect)) { - return h; - } - } -#endif - // Take a quick look for the first node that contain the rect. - for (int i = 0; i < node->count; i++) { - if (rect_contains(&node->rects[i], rect)) { -#ifdef USE_PATHHINT - tr->path_hint[depth] = i; -#endif - return i; - } - } - // Fallback to using che "choose least enlargment" algorithm. - int i = node_choose_least_enlargement(node, rect); -#ifdef USE_PATHHINT - tr->path_hint[depth] = i; -#endif - return i; -} - -static struct rect node_rect_calc(const struct node *node) { - struct rect rect = node->rects[0]; - for (int i = 1; i < node->count; i++) { - rect_expand(&rect, &node->rects[i]); - } - return rect; -} - -// node_insert returns false if out of memory -static bool node_insert(struct rtree *tr, struct rect *nr, struct node *node, - struct rect *ir, struct item item, int depth, bool *split) -{ - if (node->kind == LEAF) { - if (node->count == MAXITEMS) { - *split = true; - return true; - } - int index = node->count; - node->rects[index] = *ir; - node->datas[index] = item; - node->count++; - *split = false; - return true; - } - // Choose a subtree for inserting the rectangle. - int i = node_choose(tr, node, ir, depth); - cow_node_or(node->nodes[i], return false); - if (!node_insert(tr, &node->rects[i], node->nodes[i], ir, item, depth+1, - split)) - { - return false; - } - if (!*split) { - rect_expand(&node->rects[i], ir); - *split = false; - return true; - } - // split the child node - if (node->count == MAXITEMS) { - *split = true; - return true; - } - struct node *right; - if (!node_split(tr, &node->rects[i], node->nodes[i], &right)) { - return false; - } - node->rects[i] = node_rect_calc(node->nodes[i]); - node->rects[node->count] = node_rect_calc(right); - node->nodes[node->count] = right; - node->count++; - return node_insert(tr, nr, node, ir, item, depth, split); -} - -struct rtree *rtree_new_with_allocator(void *(*_malloc)(size_t), - void (*_free)(void*) -) { - _malloc = _malloc ? _malloc : malloc; - _free = _free ? _free : free; - struct rtree *tr = (struct rtree *)_malloc(sizeof(struct rtree)); - if (!tr) return NULL; - memset(tr, 0, sizeof(struct rtree)); - tr->malloc = _malloc; - tr->free = _free; - return tr; -} - -struct rtree *rtree_new(void) { - return rtree_new_with_allocator(NULL, NULL); -} - -void rtree_set_item_callbacks(struct rtree *tr, - bool (*clone)(const DATATYPE item, DATATYPE *into, void *udata), - void (*free)(const DATATYPE item, void *udata)) -{ - tr->item_clone = clone; - tr->item_free = free; -} - -bool rtree_insert(struct rtree *tr, const NUMTYPE *min, - const NUMTYPE *max, const DATATYPE data) -{ - // copy input rect - struct rect rect; - memcpy(&rect.min[0], min, sizeof(NUMTYPE)*DIMS); - memcpy(&rect.max[0], max?max:min, sizeof(NUMTYPE)*DIMS); - - // copy input data - struct item item; - if (tr->item_clone) { - if (!tr->item_clone(data, (DATATYPE*)&item.data, tr->udata)) { - return false; - } - } else { - memcpy(&item.data, &data, sizeof(DATATYPE)); - } - - while (1) { - if (!tr->root) { - struct node *new_root = node_new(tr, LEAF); - if (!new_root) { - break; - } - tr->root = new_root; - tr->rect = rect; - tr->height = 1; - } - bool split = false; - cow_node_or(tr->root, break); - if (!node_insert(tr, &tr->rect, tr->root, &rect, item, 0, &split)) { - break; - } - if (!split) { - rect_expand(&tr->rect, &rect); - tr->count++; - return true; - } - struct node *new_root = node_new(tr, BRANCH); - if (!new_root) { - break; - } - struct node *right; - if (!node_split(tr, &tr->rect, tr->root, &right)) { - tr->free(new_root); - break; - } - new_root->rects[0] = node_rect_calc(tr->root); - new_root->rects[1] = node_rect_calc(right); - new_root->nodes[0] = tr->root; - new_root->nodes[1] = right; - tr->root = new_root; - tr->root->count = 2; - tr->height++; - } - // out of memory - if (tr->item_free) { - tr->item_free(item.data, tr->udata); - } - return false; -} - -void rtree_destroy(struct rtree *tr) { - if (tr->root) { - node_free(tr, tr->root); - } - tr->free(tr); -} - -static bool node_search(struct node *node, struct rect *rect, - bool (*iter)(const NUMTYPE *min, const NUMTYPE *max, const DATATYPE data, - void *udata), - void *udata) -{ - if (node->kind == LEAF) { - for (int i = 0; i < node->count; i++) { - if (rect_intersects(&node->rects[i], rect)) { - if (!iter(node->rects[i].min, node->rects[i].max, - node->datas[i].data, udata)) - { - return false; - } - } - } - return true; - } - for (int i = 0; i < node->count; i++) { - if (rect_intersects(&node->rects[i], rect)) { - if (!node_search(node->nodes[i], rect, iter, udata)) { - return false; - } - } - } - return true; -} - -void rtree_search(const struct rtree *tr, const NUMTYPE min[], - const NUMTYPE max[], - bool (*iter)(const NUMTYPE min[], const NUMTYPE max[], const DATATYPE data, - void *udata), - void *udata) -{ - // copy input rect - struct rect rect; - memcpy(&rect.min[0], min, sizeof(NUMTYPE)*DIMS); - memcpy(&rect.max[0], max?max:min, sizeof(NUMTYPE)*DIMS); - - if (tr->root) { - node_search(tr->root, &rect, iter, udata); - } -} - -static bool node_scan(struct node *node, - bool (*iter)(const NUMTYPE *min, const NUMTYPE *max, const DATATYPE data, - void *udata), - void *udata) -{ - if (node->kind == LEAF) { - for (int i = 0; i < node->count; i++) { - if (!iter(node->rects[i].min, node->rects[i].max, - node->datas[i].data, udata)) - { - return false; - } - } - return true; - } - for (int i = 0; i < node->count; i++) { - if (!node_scan(node->nodes[i], iter, udata)) { - return false; - } - } - return true; -} - -void rtree_scan(const struct rtree *tr, - bool (*iter)(const NUMTYPE *min, const NUMTYPE *max, const DATATYPE data, - void *udata), - void *udata) -{ - if (tr->root) { - node_scan(tr->root, iter, udata); - } -} - -size_t rtree_count(const struct rtree *tr) { - return tr->count; -} - -static bool node_delete(struct rtree *tr, struct rect *nr, struct node *node, - struct rect *ir, struct item item, int depth, bool *removed, bool *shrunk, - int (*compare)(const DATATYPE a, const DATATYPE b, void *udata), - void *udata) -{ - *removed = false; - *shrunk = false; - if (node->kind == LEAF) { - for (int i = 0; i < node->count; i++) { - if (!rect_equals_bin(ir, &node->rects[i])) { - // Must be exactly the same, binary comparison. - continue; - } - int cmp = compare ? - compare(node->datas[i].data, item.data, udata) : - memcmp(&node->datas[i].data, &item.data, sizeof(DATATYPE)); - if (cmp != 0) { - continue; - } - // Found the target item to delete. - if (tr->item_free) { - tr->item_free(node->datas[i].data, tr->udata); - } - node->rects[i] = node->rects[node->count-1]; - node->datas[i] = node->datas[node->count-1]; - node->count--; - if (rect_onedge(ir, nr)) { - // The item rect was on the edge of the node rect. - // We need to recalculate the node rect. - *nr = node_rect_calc(node); - // Notify the caller that we shrunk the rect. - *shrunk = true; - } - *removed = true; - return true; - } - return true; - } - int h = 0; -#ifdef USE_PATHHINT - h = tr->path_hint[depth]; - if (h < node->count) { - if (rect_contains(&node->rects[h], ir)) { - cow_node_or(node->nodes[h], return false); - if (!node_delete(tr, &node->rects[h], node->nodes[h], ir, item, - depth+1,removed, shrunk, compare, udata)) - { - return false; - } - if (*removed) { - goto removed; - } - } - } - h = 0; -#endif - for (; h < node->count; h++) { - if (!rect_contains(&node->rects[h], ir)) { - continue; - } - struct rect crect = node->rects[h]; - cow_node_or(node->nodes[h], return false); - if (!node_delete(tr, &node->rects[h], node->nodes[h], ir, item, depth+1, - removed, shrunk, compare, udata)) - { - return false; - } - if (!*removed) { - continue; - } - removed: - if (node->nodes[h]->count == 0) { - // underflow - node_free(tr, node->nodes[h]); - node->rects[h] = node->rects[node->count-1]; - node->nodes[h] = node->nodes[node->count-1]; - node->count--; - *nr = node_rect_calc(node); - *shrunk = true; - return true; - } -#ifdef USE_PATHHINT - tr->path_hint[depth] = h; -#endif - if (*shrunk) { - *shrunk = !rect_equals(&node->rects[h], &crect); - if (*shrunk) { - *nr = node_rect_calc(node); - } - } - return true; - } - return true; -} - -// returns false if out of memory -static bool rtree_delete0(struct rtree *tr, const NUMTYPE *min, - const NUMTYPE *max, const DATATYPE data, - int (*compare)(const DATATYPE a, const DATATYPE b, void *udata), - void *udata) -{ - // copy input rect - struct rect rect; - memcpy(&rect.min[0], min, sizeof(NUMTYPE)*DIMS); - memcpy(&rect.max[0], max?max:min, sizeof(NUMTYPE)*DIMS); - - // copy input data - struct item item; - memcpy(&item.data, &data, sizeof(DATATYPE)); - - if (!tr->root) { - return true; - } - bool removed = false; - bool shrunk = false; - cow_node_or(tr->root, return false); - if (!node_delete(tr, &tr->rect, tr->root, &rect, item, 0, &removed, &shrunk, - compare, udata)) - { - return false; - } - if (!removed) { - return true; - } - tr->count--; - if (tr->count == 0) { - node_free(tr, tr->root); - tr->root = NULL; - memset(&tr->rect, 0, sizeof(struct rect)); - tr->height = 0; - } else { - while (tr->root->kind == BRANCH && tr->root->count == 1) { - struct node *prev = tr->root; - tr->root = tr->root->nodes[0]; - prev->count = 0; - node_free(tr, prev); - tr->height--; - } - if (shrunk) { - tr->rect = node_rect_calc(tr->root); - } - } - return true; -} - -bool rtree_delete(struct rtree *tr, const NUMTYPE *min, const NUMTYPE *max, - const DATATYPE data) -{ - return rtree_delete0(tr, min, max, data, NULL, NULL); -} - -bool rtree_delete_with_comparator(struct rtree *tr, const NUMTYPE *min, - const NUMTYPE *max, const DATATYPE data, - int (*compare)(const DATATYPE a, const DATATYPE b, void *udata), - void *udata) -{ - return rtree_delete0(tr, min, max, data, compare, udata); -} - -struct rtree *rtree_clone(struct rtree *tr) { - if (!tr) return NULL; - struct rtree *tr2 = tr->malloc(sizeof(struct rtree)); - if (!tr2) return NULL; - memcpy(tr2, tr, sizeof(struct rtree)); - if (tr2->root) rc_fetch_add(&tr2->root->rc, 1); - return tr2; -} - -void rtree_opt_relaxed_atomics(struct rtree *tr) { - tr->relaxed = true; -} - -#ifdef TEST_PRIVATE_FUNCTIONS -#include "tests/priv_funcs.h" -#endif diff --git a/source/rtree.h b/source/rtree.h deleted file mode 100644 index 74523a0d..00000000 --- a/source/rtree.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2023 Joshua J Baker. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -#ifndef RTREE_H -#define RTREE_H - -#define NUMTYPE double - -#include -#include - -// rtree_new returns a new rtree -// -// Returns NULL if the system is out of memory. -struct rtree *rtree_new(void); - - -// rtree_new returns a new rtree using a custom allocator -// -// Returns NULL if the system is out of memory. -struct rtree *rtree_new_with_allocator(void *(*malloc)(size_t), void (*free)(void*)); - -// rtree_free frees an rtree -void rtree_destroy(struct rtree *tr); - -// rtree_clone makes an instant copy of the btree. -// -// This operation uses shadowing / copy-on-write. -struct rtree *rtree_clone(struct rtree *tr); - -// rtree_set_item_callbacks sets the item clone and free callbacks that will be -// called internally by the rtree when items are inserted and removed. -// -// These callbacks are optional but may be needed by programs that require -// copy-on-write support by using the rtree_clone function. -// -// The clone function should return true if the clone succeeded or false if the -// system is out of memory. -void rtree_set_item_callbacks(struct rtree *tr, - bool (*clone)(const void *item, void **into, void *udata), - void (*free)(const void *item, void *udata)); - -// rtree_set_udata sets the user-defined data. -// -// This should be called once after rtree_new() and is only used for -// the item callbacks as defined in rtree_set_item_callbacks(). -void rtree_set_udata(struct rtree *tr, void *udata); - -// rtree_insert inserts an item into the rtree. -// -// This operation performs a copy of the data that is pointed to in the second -// and third arguments. The R-tree expects a rectangle, which is two arrays of -// doubles. The first N values as the minimum corner of the rect, and the next -// N values as the maximum corner of the rect, where N is the number of -// dimensions. -// -// When inserting points, the max coordinates is optional (set to NULL). -// -// Returns false if the system is out of memory. -bool rtree_insert(struct rtree *tr, const NUMTYPE *min, const NUMTYPE *max, const void *data); - - -// rtree_search searches the rtree and iterates over each item that intersect -// the provided rectangle. -// -// Returning false from the iter will stop the search. -void rtree_search(const struct rtree *tr, const NUMTYPE *min, const NUMTYPE *max, - bool (*iter)(const NUMTYPE *min, const NUMTYPE *max, const void *data, void *udata), - void *udata); - -// rtree_scan iterates over every item in the rtree. -// -// Returning false from the iter will stop the scan. -void rtree_scan(const struct rtree *tr, - bool (*iter)(const NUMTYPE *min, const NUMTYPE *max, const void *data, void *udata), - void *udata); - -// rtree_count returns the number of items in the rtree. -size_t rtree_count(const struct rtree *tr); - -// rtree_delete deletes an item from the rtree. -// -// This searches the tree for an item that is contained within the provided -// rectangle, and perform a binary comparison of its data to the provided -// data. The first item that is found is deleted. -// -// Returns false if the system is out of memory. -bool rtree_delete(struct rtree *tr, const NUMTYPE *min, const NUMTYPE *max, const void *data); - -// rtree_delete_with_comparator deletes an item from the rtree. -// This searches the tree for an item that is contained within the provided -// rectangle, and perform a comparison of its data to the provided data using -// a compare function. The first item that is found is deleted. -// -// Returns false if the system is out of memory. -bool rtree_delete_with_comparator(struct rtree *tr, const NUMTYPE *min, - const NUMTYPE *max, const void *data, - int (*compare)(const void *a, const void *b, void *udata), - void *udata); - -// rtree_opt_relaxed_atomics activates memory_order_relaxed for all atomic -// loads. This may increase performance for single-threaded programs. -// Optionally, define RTREE_NOATOMICS to disbale all atomics. -void rtree_opt_relaxed_atomics(struct rtree *tr); - -#endif // RTREE_H diff --git a/subprojects/qjs-layout.wrap b/subprojects/qjs-layout.wrap deleted file mode 100644 index 9bccb366..00000000 --- a/subprojects/qjs-layout.wrap +++ /dev/null @@ -1,7 +0,0 @@ -[wrap-git] -url = https://github.com/johnalanbrook/qjs-layout.git -revision = head -depth = 1 - -[provide] -qjs-layout = qjs_layout_dep diff --git a/subprojects/qjs-miniz.wrap b/subprojects/qjs-miniz.wrap deleted file mode 100644 index 104434b5..00000000 --- a/subprojects/qjs-miniz.wrap +++ /dev/null @@ -1,7 +0,0 @@ -[wrap-git] -url = https://github.com/johnalanbrook/qjs-miniz.git -revision = head -depth = 1 - -[provide] -qjs-miniz = qjs_miniz_dep diff --git a/subprojects/qjs-steam.wrap b/subprojects/qjs-steam.wrap deleted file mode 100644 index 7a0b2726..00000000 --- a/subprojects/qjs-steam.wrap +++ /dev/null @@ -1,7 +0,0 @@ -[wrap-git] -url = https://github.com/johnalanbrook/qjs-steam.git -revision = head -depth = 1 - -[provide] -qjs-steam = qjs_steam_dep