diff --git a/.cell/cell.toml b/.cell/cell.toml new file mode 100644 index 0000000..8587966 --- /dev/null +++ b/.cell/cell.toml @@ -0,0 +1,2 @@ +[compilation] +CFLAGS = "-Wno-incompatible-function-pointer-types" \ No newline at end of file diff --git a/qjs_rtree.c b/qjs_rtree.c deleted file mode 100644 index 6785cc8..0000000 --- a/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/qjs_rtree.h b/qjs_rtree.h deleted file mode 100644 index 7d33df9..0000000 --- a/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/rtree.c b/rtree.c index 4389dec..4dede8b 100644 --- a/rtree.c +++ b/rtree.c @@ -1,839 +1,234 @@ -// 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 "cell.h" +#include "src/rtree.h" +#include #include -#include "rtree.h" -//////////////////////////////// +// External declarations +typedef struct rtree rtree; -#define DATATYPE void * -#define DIMS 3 -#define MAXITEMS 64 +typedef struct rect { + float x; + float y; + float w; + float h; +} rect; -//////////////////////////////// - -// 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 rect js2rect(JSContext *js, JSValue v) +{ + rect r; + JSValue js_x = JS_GetPropertyStr(js, v, "x"); + JSValue js_y = JS_GetPropertyStr(js, v, "y"); + JSValue js_w = JS_GetPropertyStr(js, v, "w"); + JSValue js_h = JS_GetPropertyStr(js, v, "h"); + + double x, y, w, h; + JS_ToFloat64(js, &x, js_x); + JS_ToFloat64(js, &y, js_y); + JS_ToFloat64(js, &w, js_w); + JS_ToFloat64(js, &h, js_h); + + r.x = (float)x; + r.y = (float)y; + r.w = (float)w; + r.h = (float)h; + + JS_FreeValue(js, js_x); + JS_FreeValue(js, js_y); + JS_FreeValue(js, js_w); + JS_FreeValue(js, js_h); + + return r; } -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, +static JSValue rect2js(JSContext *js, rect r) +{ + JSValue js_rect = JS_NewObject(js); + JS_SetPropertyStr(js, js_rect, "x", JS_NewFloat64(js, r.x)); + JS_SetPropertyStr(js, js_rect, "y", JS_NewFloat64(js, r.y)); + JS_SetPropertyStr(js, js_rect, "w", JS_NewFloat64(js, r.w)); + JS_SetPropertyStr(js, js_rect, "h", JS_NewFloat64(js, r.h)); + return js_rect; +} + +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); + } +) + +static 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; }; -struct rect { - NUMTYPE min[DIMS]; - NUMTYPE max[DIMS]; +static 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; }; -struct item { - const DATATYPE data; +static 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); + if (JS_HasException(each->js)) + return 0; + 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; + +static 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)); +} + +static 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), }; -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 +CELL_USE_INIT( + return QJSCLASSPREP_FUNCS_CTOR(rtree, 0) +) \ No newline at end of file diff --git a/src/rtree.c b/src/rtree.c new file mode 100644 index 0000000..4389dec --- /dev/null +++ b/src/rtree.c @@ -0,0 +1,839 @@ +// 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/rtree.h b/src/rtree.h similarity index 100% rename from rtree.h rename to src/rtree.h