tg_geometry 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +103 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +385 -0
- data/Rakefile +129 -0
- data/benchmark/_support.rb +115 -0
- data/benchmark/batch_packed_vs_loop.rb +27 -0
- data/benchmark/falcon_concurrency.rb +25 -0
- data/benchmark/flat_vs_rtree.rb +27 -0
- data/benchmark/gvl_threshold.rb +41 -0
- data/benchmark/objectspace_memsize.rb +17 -0
- data/benchmark/parse_throughput.rb +38 -0
- data/benchmark/rss_stability.rb +70 -0
- data/docs/ACTIVE_RECORD.md +26 -0
- data/docs/ARCHITECTURE.md +130 -0
- data/docs/AUTO_STRATEGY.md +15 -0
- data/docs/BENCHMARKING.md +75 -0
- data/docs/CASUAL_EXAMPLE.md +618 -0
- data/docs/CONCURRENCY.md +65 -0
- data/docs/ERROR_HANDLING.md +55 -0
- data/docs/EXPANSION_E_TO_H_STATUS.md +51 -0
- data/docs/FORMAT_COVERAGE.md +23 -0
- data/docs/FULL_TG_API_COVERAGE.md +109 -0
- data/docs/LIMITATIONS.md +61 -0
- data/docs/LOW_LEVEL_GEOMETRY.md +121 -0
- data/docs/MEMORY_OWNERSHIP.md +94 -0
- data/docs/RACTOR.md +40 -0
- data/docs/REGISTRY.md +37 -0
- data/docs/RELEASE_CHECKLIST.md +39 -0
- data/ext/tg_geometry/extconf.rb +91 -0
- data/ext/tg_geometry/tg_geometry_ext.c +3054 -0
- data/ext/tg_geometry/tg_geometry_vendor_rtree.c +1 -0
- data/ext/tg_geometry/tg_geometry_vendor_tg.c +24 -0
- data/ext/tg_geometry/vendor/.vendored +16 -0
- data/ext/tg_geometry/vendor/rtree/LICENSE +20 -0
- data/ext/tg_geometry/vendor/rtree/README.md +202 -0
- data/ext/tg_geometry/vendor/rtree/VERSION +3 -0
- data/ext/tg_geometry/vendor/rtree/rtree.c +840 -0
- data/ext/tg_geometry/vendor/rtree/rtree.h +105 -0
- data/ext/tg_geometry/vendor/tg/LICENSE +19 -0
- data/ext/tg_geometry/vendor/tg/README.md +197 -0
- data/ext/tg_geometry/vendor/tg/VERSION +3 -0
- data/ext/tg_geometry/vendor/tg/tg.c +16010 -0
- data/ext/tg_geometry/vendor/tg/tg.h +359 -0
- data/lib/tg/geometry/active_record_source.rb +57 -0
- data/lib/tg/geometry/registry.rb +119 -0
- data/lib/tg/geometry/version.rb +7 -0
- data/lib/tg/geometry.rb +6 -0
- data/lib/tg_geometry.rb +3 -0
- data/script/vendor_libs.rb +264 -0
- data/spec/block_10_rtree_strategy_spec.rb +82 -0
- data/spec/block_11_rtree_order_spec.rb +53 -0
- data/spec/block_12_batch_packed_spec.rb +55 -0
- data/spec/block_13_error_hardening_spec.rb +65 -0
- data/spec/block_14_memory_gc_hardening_spec.rb +116 -0
- data/spec/block_1_skeleton_spec.rb +45 -0
- data/spec/block_20_concurrency_spec.rb +157 -0
- data/spec/block_20_fuzz_spec.rb +145 -0
- data/spec/block_2_vendor_spec.rb +79 -0
- data/spec/block_3_geom_parse_spec.rb +89 -0
- data/spec/block_4_geom_api_spec.rb +90 -0
- data/spec/block_5_rect_api_spec.rb +96 -0
- data/spec/block_6_index_build_spec.rb +111 -0
- data/spec/block_7_index_owned_geometry_spec.rb +143 -0
- data/spec/block_8_index_borrowed_geometry_spec.rb +106 -0
- data/spec/block_9_flat_query_spec.rb +65 -0
- data/spec/expansion_a_auto_strategy_spec.rb +14 -0
- data/spec/expansion_b_registry_spec.rb +47 -0
- data/spec/expansion_c_active_record_source_spec.rb +42 -0
- data/spec/expansion_d_format_coverage_spec.rb +30 -0
- data/spec/expansion_e_low_level_geometry_spec.rb +82 -0
- data/spec/expansion_i_ractor_spec.rb +25 -0
- data/spec/expansion_j_full_tg_api_coverage_spec.rb +114 -0
- data/spec/spec_helper.rb +15 -0
- metadata +157 -0
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
// Copyright 2023 Joshua J Baker. All rights reserved.
|
|
2
|
+
// Use of this source code is governed by an MIT-style
|
|
3
|
+
// license that can be found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
#include <string.h>
|
|
6
|
+
#include <math.h>
|
|
7
|
+
#include <stdbool.h>
|
|
8
|
+
#include "rtree.h"
|
|
9
|
+
|
|
10
|
+
////////////////////////////////
|
|
11
|
+
|
|
12
|
+
#define DATATYPE void *
|
|
13
|
+
#define DIMS 2
|
|
14
|
+
#define NUMTYPE double
|
|
15
|
+
#define MAXITEMS 64
|
|
16
|
+
|
|
17
|
+
////////////////////////////////
|
|
18
|
+
|
|
19
|
+
// used for splits
|
|
20
|
+
#define MINITEMS_PERCENTAGE 10
|
|
21
|
+
#define MINITEMS ((MAXITEMS) * (MINITEMS_PERCENTAGE) / 100 + 1)
|
|
22
|
+
|
|
23
|
+
#ifndef RTREE_NOPATHHINT
|
|
24
|
+
#define USE_PATHHINT
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
#ifdef RTREE_MAXITEMS
|
|
28
|
+
#undef MAXITEMS
|
|
29
|
+
#define MAXITEMS RTREE_MAXITEMS
|
|
30
|
+
#endif
|
|
31
|
+
|
|
32
|
+
#ifdef RTREE_NOATOMICS
|
|
33
|
+
typedef int rc_t;
|
|
34
|
+
static int rc_load(rc_t *ptr, bool relaxed) {
|
|
35
|
+
(void)relaxed; // nothing to do
|
|
36
|
+
return *ptr;
|
|
37
|
+
}
|
|
38
|
+
static int rc_fetch_sub(rc_t *ptr, int val) {
|
|
39
|
+
int rc = *ptr;
|
|
40
|
+
*ptr -= val;
|
|
41
|
+
return rc;
|
|
42
|
+
}
|
|
43
|
+
static int rc_fetch_add(rc_t *ptr, int val) {
|
|
44
|
+
int rc = *ptr;
|
|
45
|
+
*ptr += val;
|
|
46
|
+
return rc;
|
|
47
|
+
}
|
|
48
|
+
#else
|
|
49
|
+
#include <stdatomic.h>
|
|
50
|
+
typedef atomic_int rc_t;
|
|
51
|
+
static int rc_load(rc_t *ptr, bool relaxed) {
|
|
52
|
+
if (relaxed) {
|
|
53
|
+
return atomic_load_explicit(ptr, memory_order_relaxed);
|
|
54
|
+
} else {
|
|
55
|
+
return atomic_load(ptr);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
static int rc_fetch_sub(rc_t *ptr, int delta) {
|
|
59
|
+
return atomic_fetch_sub(ptr, delta);
|
|
60
|
+
}
|
|
61
|
+
static int rc_fetch_add(rc_t *ptr, int delta) {
|
|
62
|
+
return atomic_fetch_add(ptr, delta);
|
|
63
|
+
}
|
|
64
|
+
#endif
|
|
65
|
+
|
|
66
|
+
enum kind {
|
|
67
|
+
LEAF = 1,
|
|
68
|
+
BRANCH = 2,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
struct rect {
|
|
72
|
+
NUMTYPE min[DIMS];
|
|
73
|
+
NUMTYPE max[DIMS];
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
struct item {
|
|
77
|
+
const DATATYPE data;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
struct node {
|
|
81
|
+
rc_t rc; // reference counter for copy-on-write
|
|
82
|
+
enum kind kind; // LEAF or BRANCH
|
|
83
|
+
int count; // number of rects
|
|
84
|
+
struct rect rects[MAXITEMS];
|
|
85
|
+
union {
|
|
86
|
+
struct node *nodes[MAXITEMS];
|
|
87
|
+
struct item datas[MAXITEMS];
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
struct rtree {
|
|
92
|
+
struct rect rect;
|
|
93
|
+
struct node *root;
|
|
94
|
+
size_t count;
|
|
95
|
+
size_t height;
|
|
96
|
+
#ifdef USE_PATHHINT
|
|
97
|
+
int path_hint[16];
|
|
98
|
+
#endif
|
|
99
|
+
bool relaxed;
|
|
100
|
+
void *(*malloc)(size_t);
|
|
101
|
+
void (*free)(void *);
|
|
102
|
+
void *udata;
|
|
103
|
+
bool (*item_clone)(const DATATYPE item, DATATYPE *into, void *udata);
|
|
104
|
+
void (*item_free)(const DATATYPE item, void *udata);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
static inline NUMTYPE min0(NUMTYPE x, NUMTYPE y) {
|
|
108
|
+
return x < y ? x : y;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static inline NUMTYPE max0(NUMTYPE x, NUMTYPE y) {
|
|
112
|
+
return x > y ? x : y;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static bool feq(NUMTYPE a, NUMTYPE b) {
|
|
116
|
+
return !(a < b || a > b);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
void rtree_set_udata(struct rtree *tr, void *udata) {
|
|
121
|
+
tr->udata = udata;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static struct node *node_new(struct rtree *tr, enum kind kind) {
|
|
125
|
+
struct node *node = (struct node *)tr->malloc(sizeof(struct node));
|
|
126
|
+
if (!node) return NULL;
|
|
127
|
+
memset(node, 0, sizeof(struct node));
|
|
128
|
+
node->kind = kind;
|
|
129
|
+
return node;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static struct node *node_copy(struct rtree *tr, struct node *node) {
|
|
133
|
+
struct node *node2 = (struct node *)tr->malloc(sizeof(struct node));
|
|
134
|
+
if (!node2) return NULL;
|
|
135
|
+
memcpy(node2, node, sizeof(struct node));
|
|
136
|
+
node2->rc = 0;
|
|
137
|
+
if (node2->kind == BRANCH) {
|
|
138
|
+
for (int i = 0; i < node2->count; i++) {
|
|
139
|
+
rc_fetch_add(&node2->nodes[i]->rc, 1);
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
if (tr->item_clone) {
|
|
143
|
+
int n = 0;
|
|
144
|
+
bool oom = false;
|
|
145
|
+
for (int i = 0; i < node2->count; i++) {
|
|
146
|
+
if (!tr->item_clone(node->datas[i].data,
|
|
147
|
+
(DATATYPE*)&node2->datas[i].data, tr->udata))
|
|
148
|
+
{
|
|
149
|
+
oom = true;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
n++;
|
|
153
|
+
}
|
|
154
|
+
if (oom) {
|
|
155
|
+
if (tr->item_free) {
|
|
156
|
+
for (int i = 0; i < n; i++) {
|
|
157
|
+
tr->item_free(node2->datas[i].data, tr->udata);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
tr->free(node2);
|
|
161
|
+
return NULL;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return node2;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static void node_free(struct rtree *tr, struct node *node) {
|
|
169
|
+
if (rc_fetch_sub(&node->rc, 1) > 0) return;
|
|
170
|
+
if (node->kind == BRANCH) {
|
|
171
|
+
for (int i = 0; i < node->count; i++) {
|
|
172
|
+
node_free(tr, node->nodes[i]);
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
if (tr->item_free) {
|
|
176
|
+
for (int i = 0; i < node->count; i++) {
|
|
177
|
+
tr->item_free(node->datas[i].data, tr->udata);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
tr->free(node);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#define cow_node_or(rnode, code) { \
|
|
185
|
+
if (rc_load(&(rnode)->rc, tr->relaxed) > 0) { \
|
|
186
|
+
struct node *node2 = node_copy(tr, (rnode)); \
|
|
187
|
+
if (!node2) { code; } \
|
|
188
|
+
node_free(tr, rnode); \
|
|
189
|
+
(rnode) = node2; \
|
|
190
|
+
} \
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
static void rect_expand(struct rect *rect, const struct rect *other) {
|
|
194
|
+
for (int i = 0; i < DIMS; i++) {
|
|
195
|
+
rect->min[i] = min0(rect->min[i], other->min[i]);
|
|
196
|
+
rect->max[i] = max0(rect->max[i], other->max[i]);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static NUMTYPE rect_area(const struct rect *rect) {
|
|
201
|
+
NUMTYPE result = 1;
|
|
202
|
+
for (int i = 0; i < DIMS; i++) {
|
|
203
|
+
result *= (rect->max[i] - rect->min[i]);
|
|
204
|
+
}
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// return the area of two rects expanded
|
|
209
|
+
static NUMTYPE rect_unioned_area(const struct rect *rect,
|
|
210
|
+
const struct rect *other)
|
|
211
|
+
{
|
|
212
|
+
NUMTYPE result = 1;
|
|
213
|
+
for (int i = 0; i < DIMS; i++) {
|
|
214
|
+
result *= (max0(rect->max[i], other->max[i]) -
|
|
215
|
+
min0(rect->min[i], other->min[i]));
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
static bool rect_contains(const struct rect *rect, const struct rect *other) {
|
|
221
|
+
int bits = 0;
|
|
222
|
+
for (int i = 0; i < DIMS; i++) {
|
|
223
|
+
bits |= other->min[i] < rect->min[i];
|
|
224
|
+
bits |= other->max[i] > rect->max[i];
|
|
225
|
+
}
|
|
226
|
+
return bits == 0;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
static bool rect_intersects(const struct rect *rect, const struct rect *other) {
|
|
230
|
+
int bits = 0;
|
|
231
|
+
for (int i = 0; i < DIMS; i++) {
|
|
232
|
+
bits |= other->min[i] > rect->max[i];
|
|
233
|
+
bits |= other->max[i] < rect->min[i];
|
|
234
|
+
}
|
|
235
|
+
return bits == 0;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
static bool rect_onedge(const struct rect *rect, const struct rect *other) {
|
|
239
|
+
for (int i = 0; i < DIMS; i++) {
|
|
240
|
+
if (feq(rect->min[i], other->min[i]) ||
|
|
241
|
+
feq(rect->max[i], other->max[i]))
|
|
242
|
+
{
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
static bool rect_equals(const struct rect *rect, const struct rect *other) {
|
|
250
|
+
for (int i = 0; i < DIMS; i++) {
|
|
251
|
+
if (!feq(rect->min[i], other->min[i]) ||
|
|
252
|
+
!feq(rect->max[i], other->max[i]))
|
|
253
|
+
{
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
static bool rect_equals_bin(const struct rect *rect, const struct rect *other) {
|
|
261
|
+
for (int i = 0; i < DIMS; i++) {
|
|
262
|
+
if (rect->min[i] != other->min[i] ||
|
|
263
|
+
rect->max[i] != other->max[i])
|
|
264
|
+
{
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
static int rect_largest_axis(const struct rect *rect) {
|
|
272
|
+
int axis = 0;
|
|
273
|
+
NUMTYPE nlength = rect->max[0] - rect->min[0];
|
|
274
|
+
for (int i = 1; i < DIMS; i++) {
|
|
275
|
+
NUMTYPE length = rect->max[i] - rect->min[i];
|
|
276
|
+
if (length > nlength) {
|
|
277
|
+
nlength = length;
|
|
278
|
+
axis = i;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return axis;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// swap two rectangles
|
|
285
|
+
static void node_swap(struct node *node, int i, int j) {
|
|
286
|
+
struct rect tmp = node->rects[i];
|
|
287
|
+
node->rects[i] = node->rects[j];
|
|
288
|
+
node->rects[j] = tmp;
|
|
289
|
+
if (node->kind == LEAF) {
|
|
290
|
+
struct item tmp = node->datas[i];
|
|
291
|
+
node->datas[i] = node->datas[j];
|
|
292
|
+
node->datas[j] = tmp;
|
|
293
|
+
} else {
|
|
294
|
+
struct node *tmp = node->nodes[i];
|
|
295
|
+
node->nodes[i] = node->nodes[j];
|
|
296
|
+
node->nodes[j] = tmp;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
struct rect4 {
|
|
301
|
+
NUMTYPE all[DIMS*2];
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
static void node_qsort(struct node *node, int s, int e, int index) {
|
|
305
|
+
int nrects = e - s;
|
|
306
|
+
if (nrects < 2) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
int left = 0;
|
|
310
|
+
int right = nrects-1;
|
|
311
|
+
int pivot = nrects / 2;
|
|
312
|
+
node_swap(node, s+pivot, s+right);
|
|
313
|
+
struct rect4 *rects = (struct rect4 *)&node->rects[s];
|
|
314
|
+
for (int i = 0; i < nrects; i++) {
|
|
315
|
+
if (rects[right].all[index] < rects[i].all[index]) {
|
|
316
|
+
node_swap(node, s+i, s+left);
|
|
317
|
+
left++;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
node_swap(node, s+left, s+right);
|
|
321
|
+
node_qsort(node, s, s+left, index);
|
|
322
|
+
node_qsort(node, s+left+1, e, index);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// sort the node rectangles by the axis. used during splits
|
|
326
|
+
static void node_sort_by_axis(struct node *node, int axis, bool max) {
|
|
327
|
+
int by_index = max ? DIMS+axis : axis;
|
|
328
|
+
node_qsort(node, 0, node->count, by_index);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
static void node_move_rect_at_index_into(struct node *from, int index,
|
|
332
|
+
struct node *into)
|
|
333
|
+
{
|
|
334
|
+
into->rects[into->count] = from->rects[index];
|
|
335
|
+
from->rects[index] = from->rects[from->count-1];
|
|
336
|
+
if (from->kind == LEAF) {
|
|
337
|
+
into->datas[into->count] = from->datas[index];
|
|
338
|
+
from->datas[index] = from->datas[from->count-1];
|
|
339
|
+
} else {
|
|
340
|
+
into->nodes[into->count] = from->nodes[index];
|
|
341
|
+
from->nodes[index] = from->nodes[from->count-1];
|
|
342
|
+
}
|
|
343
|
+
from->count--;
|
|
344
|
+
into->count++;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
static bool node_split_largest_axis_edge_snap(struct rtree *tr,
|
|
348
|
+
struct rect *rect, struct node *node, struct node **right_out)
|
|
349
|
+
{
|
|
350
|
+
int axis = rect_largest_axis(rect);
|
|
351
|
+
struct node *right = node_new(tr, node->kind);
|
|
352
|
+
if (!right) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
for (int i = 0; i < node->count; i++) {
|
|
356
|
+
NUMTYPE min_dist = node->rects[i].min[axis] - rect->min[axis];
|
|
357
|
+
NUMTYPE max_dist = rect->max[axis] - node->rects[i].max[axis];
|
|
358
|
+
if (max_dist < min_dist) {
|
|
359
|
+
// move to right
|
|
360
|
+
node_move_rect_at_index_into(node, i, right);
|
|
361
|
+
i--;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// Make sure that both left and right nodes have at least
|
|
365
|
+
// MINITEMS by moving datas into underflowed nodes.
|
|
366
|
+
if (node->count < MINITEMS) {
|
|
367
|
+
// reverse sort by min axis
|
|
368
|
+
node_sort_by_axis(right, axis, false);
|
|
369
|
+
do {
|
|
370
|
+
node_move_rect_at_index_into(right, right->count-1, node);
|
|
371
|
+
} while (node->count < MINITEMS);
|
|
372
|
+
} else if (right->count < MINITEMS) {
|
|
373
|
+
// reverse sort by max axis
|
|
374
|
+
node_sort_by_axis(node, axis, true);
|
|
375
|
+
do {
|
|
376
|
+
node_move_rect_at_index_into(node, node->count-1, right);
|
|
377
|
+
} while (right->count < MINITEMS);
|
|
378
|
+
}
|
|
379
|
+
if (node->kind == BRANCH) {
|
|
380
|
+
node_sort_by_axis(node, 0, false);
|
|
381
|
+
node_sort_by_axis(right, 0, false);
|
|
382
|
+
}
|
|
383
|
+
*right_out = right;
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
static bool node_split(struct rtree *tr, struct rect *rect, struct node *node,
|
|
388
|
+
struct node **right)
|
|
389
|
+
{
|
|
390
|
+
return node_split_largest_axis_edge_snap(tr, rect, node, right);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
static int node_choose_least_enlargement(const struct node *node,
|
|
394
|
+
const struct rect *ir)
|
|
395
|
+
{
|
|
396
|
+
int j = 0;
|
|
397
|
+
NUMTYPE jenlarge = INFINITY;
|
|
398
|
+
for (int i = 0; i < node->count; i++) {
|
|
399
|
+
// calculate the enlarged area
|
|
400
|
+
NUMTYPE uarea = rect_unioned_area(&node->rects[i], ir);
|
|
401
|
+
NUMTYPE area = rect_area(&node->rects[i]);
|
|
402
|
+
NUMTYPE enlarge = uarea - area;
|
|
403
|
+
if (enlarge < jenlarge) {
|
|
404
|
+
j = i;
|
|
405
|
+
jenlarge = enlarge;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return j;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
static int node_choose(struct rtree *tr, const struct node *node,
|
|
412
|
+
const struct rect *rect, int depth)
|
|
413
|
+
{
|
|
414
|
+
#ifdef USE_PATHHINT
|
|
415
|
+
int h = tr->path_hint[depth];
|
|
416
|
+
if (h < node->count) {
|
|
417
|
+
if (rect_contains(&node->rects[h], rect)) {
|
|
418
|
+
return h;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
#endif
|
|
422
|
+
// Take a quick look for the first node that contain the rect.
|
|
423
|
+
for (int i = 0; i < node->count; i++) {
|
|
424
|
+
if (rect_contains(&node->rects[i], rect)) {
|
|
425
|
+
#ifdef USE_PATHHINT
|
|
426
|
+
tr->path_hint[depth] = i;
|
|
427
|
+
#endif
|
|
428
|
+
return i;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// Fallback to using che "choose least enlargment" algorithm.
|
|
432
|
+
int i = node_choose_least_enlargement(node, rect);
|
|
433
|
+
#ifdef USE_PATHHINT
|
|
434
|
+
tr->path_hint[depth] = i;
|
|
435
|
+
#endif
|
|
436
|
+
return i;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
static struct rect node_rect_calc(const struct node *node) {
|
|
440
|
+
struct rect rect = node->rects[0];
|
|
441
|
+
for (int i = 1; i < node->count; i++) {
|
|
442
|
+
rect_expand(&rect, &node->rects[i]);
|
|
443
|
+
}
|
|
444
|
+
return rect;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// node_insert returns false if out of memory
|
|
448
|
+
static bool node_insert(struct rtree *tr, struct rect *nr, struct node *node,
|
|
449
|
+
struct rect *ir, struct item item, int depth, bool *split)
|
|
450
|
+
{
|
|
451
|
+
if (node->kind == LEAF) {
|
|
452
|
+
if (node->count == MAXITEMS) {
|
|
453
|
+
*split = true;
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
int index = node->count;
|
|
457
|
+
node->rects[index] = *ir;
|
|
458
|
+
node->datas[index] = item;
|
|
459
|
+
node->count++;
|
|
460
|
+
*split = false;
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
// Choose a subtree for inserting the rectangle.
|
|
464
|
+
int i = node_choose(tr, node, ir, depth);
|
|
465
|
+
cow_node_or(node->nodes[i], return false);
|
|
466
|
+
if (!node_insert(tr, &node->rects[i], node->nodes[i], ir, item, depth+1,
|
|
467
|
+
split))
|
|
468
|
+
{
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
if (!*split) {
|
|
472
|
+
rect_expand(&node->rects[i], ir);
|
|
473
|
+
*split = false;
|
|
474
|
+
return true;
|
|
475
|
+
}
|
|
476
|
+
// split the child node
|
|
477
|
+
if (node->count == MAXITEMS) {
|
|
478
|
+
*split = true;
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
struct node *right;
|
|
482
|
+
if (!node_split(tr, &node->rects[i], node->nodes[i], &right)) {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
node->rects[i] = node_rect_calc(node->nodes[i]);
|
|
486
|
+
node->rects[node->count] = node_rect_calc(right);
|
|
487
|
+
node->nodes[node->count] = right;
|
|
488
|
+
node->count++;
|
|
489
|
+
return node_insert(tr, nr, node, ir, item, depth, split);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
struct rtree *rtree_new_with_allocator(void *(*_malloc)(size_t),
|
|
493
|
+
void (*_free)(void*)
|
|
494
|
+
) {
|
|
495
|
+
_malloc = _malloc ? _malloc : malloc;
|
|
496
|
+
_free = _free ? _free : free;
|
|
497
|
+
struct rtree *tr = (struct rtree *)_malloc(sizeof(struct rtree));
|
|
498
|
+
if (!tr) return NULL;
|
|
499
|
+
memset(tr, 0, sizeof(struct rtree));
|
|
500
|
+
tr->malloc = _malloc;
|
|
501
|
+
tr->free = _free;
|
|
502
|
+
return tr;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
struct rtree *rtree_new(void) {
|
|
506
|
+
return rtree_new_with_allocator(NULL, NULL);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
void rtree_set_item_callbacks(struct rtree *tr,
|
|
510
|
+
bool (*clone)(const DATATYPE item, DATATYPE *into, void *udata),
|
|
511
|
+
void (*free)(const DATATYPE item, void *udata))
|
|
512
|
+
{
|
|
513
|
+
tr->item_clone = clone;
|
|
514
|
+
tr->item_free = free;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
bool rtree_insert(struct rtree *tr, const NUMTYPE *min,
|
|
518
|
+
const NUMTYPE *max, const DATATYPE data)
|
|
519
|
+
{
|
|
520
|
+
// copy input rect
|
|
521
|
+
struct rect rect;
|
|
522
|
+
memcpy(&rect.min[0], min, sizeof(NUMTYPE)*DIMS);
|
|
523
|
+
memcpy(&rect.max[0], max?max:min, sizeof(NUMTYPE)*DIMS);
|
|
524
|
+
|
|
525
|
+
// copy input data
|
|
526
|
+
struct item item;
|
|
527
|
+
if (tr->item_clone) {
|
|
528
|
+
if (!tr->item_clone(data, (DATATYPE*)&item.data, tr->udata)) {
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
} else {
|
|
532
|
+
memcpy(&item.data, &data, sizeof(DATATYPE));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
while (1) {
|
|
536
|
+
if (!tr->root) {
|
|
537
|
+
struct node *new_root = node_new(tr, LEAF);
|
|
538
|
+
if (!new_root) {
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
tr->root = new_root;
|
|
542
|
+
tr->rect = rect;
|
|
543
|
+
tr->height = 1;
|
|
544
|
+
}
|
|
545
|
+
bool split = false;
|
|
546
|
+
cow_node_or(tr->root, break);
|
|
547
|
+
if (!node_insert(tr, &tr->rect, tr->root, &rect, item, 0, &split)) {
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
if (!split) {
|
|
551
|
+
rect_expand(&tr->rect, &rect);
|
|
552
|
+
tr->count++;
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
struct node *new_root = node_new(tr, BRANCH);
|
|
556
|
+
if (!new_root) {
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
struct node *right;
|
|
560
|
+
if (!node_split(tr, &tr->rect, tr->root, &right)) {
|
|
561
|
+
tr->free(new_root);
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
new_root->rects[0] = node_rect_calc(tr->root);
|
|
565
|
+
new_root->rects[1] = node_rect_calc(right);
|
|
566
|
+
new_root->nodes[0] = tr->root;
|
|
567
|
+
new_root->nodes[1] = right;
|
|
568
|
+
tr->root = new_root;
|
|
569
|
+
tr->root->count = 2;
|
|
570
|
+
tr->height++;
|
|
571
|
+
}
|
|
572
|
+
// out of memory
|
|
573
|
+
if (tr->item_free) {
|
|
574
|
+
tr->item_free(item.data, tr->udata);
|
|
575
|
+
}
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
void rtree_free(struct rtree *tr) {
|
|
580
|
+
if (tr->root) {
|
|
581
|
+
node_free(tr, tr->root);
|
|
582
|
+
}
|
|
583
|
+
tr->free(tr);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
static bool node_search(struct node *node, struct rect *rect,
|
|
587
|
+
bool (*iter)(const NUMTYPE *min, const NUMTYPE *max, const DATATYPE data,
|
|
588
|
+
void *udata),
|
|
589
|
+
void *udata)
|
|
590
|
+
{
|
|
591
|
+
if (node->kind == LEAF) {
|
|
592
|
+
for (int i = 0; i < node->count; i++) {
|
|
593
|
+
if (rect_intersects(&node->rects[i], rect)) {
|
|
594
|
+
if (!iter(node->rects[i].min, node->rects[i].max,
|
|
595
|
+
node->datas[i].data, udata))
|
|
596
|
+
{
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return true;
|
|
602
|
+
}
|
|
603
|
+
for (int i = 0; i < node->count; i++) {
|
|
604
|
+
if (rect_intersects(&node->rects[i], rect)) {
|
|
605
|
+
if (!node_search(node->nodes[i], rect, iter, udata)) {
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return true;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
void rtree_search(const struct rtree *tr, const NUMTYPE min[],
|
|
614
|
+
const NUMTYPE max[],
|
|
615
|
+
bool (*iter)(const NUMTYPE min[], const NUMTYPE max[], const DATATYPE data,
|
|
616
|
+
void *udata),
|
|
617
|
+
void *udata)
|
|
618
|
+
{
|
|
619
|
+
// copy input rect
|
|
620
|
+
struct rect rect;
|
|
621
|
+
memcpy(&rect.min[0], min, sizeof(NUMTYPE)*DIMS);
|
|
622
|
+
memcpy(&rect.max[0], max?max:min, sizeof(NUMTYPE)*DIMS);
|
|
623
|
+
|
|
624
|
+
if (tr->root) {
|
|
625
|
+
node_search(tr->root, &rect, iter, udata);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
static bool node_scan(struct node *node,
|
|
630
|
+
bool (*iter)(const NUMTYPE *min, const NUMTYPE *max, const DATATYPE data,
|
|
631
|
+
void *udata),
|
|
632
|
+
void *udata)
|
|
633
|
+
{
|
|
634
|
+
if (node->kind == LEAF) {
|
|
635
|
+
for (int i = 0; i < node->count; i++) {
|
|
636
|
+
if (!iter(node->rects[i].min, node->rects[i].max,
|
|
637
|
+
node->datas[i].data, udata))
|
|
638
|
+
{
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
for (int i = 0; i < node->count; i++) {
|
|
645
|
+
if (!node_scan(node->nodes[i], iter, udata)) {
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return true;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
void rtree_scan(const struct rtree *tr,
|
|
653
|
+
bool (*iter)(const NUMTYPE *min, const NUMTYPE *max, const DATATYPE data,
|
|
654
|
+
void *udata),
|
|
655
|
+
void *udata)
|
|
656
|
+
{
|
|
657
|
+
if (tr->root) {
|
|
658
|
+
node_scan(tr->root, iter, udata);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
size_t rtree_count(const struct rtree *tr) {
|
|
663
|
+
return tr->count;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
static bool node_delete(struct rtree *tr, struct rect *nr, struct node *node,
|
|
667
|
+
struct rect *ir, struct item item, int depth, bool *removed, bool *shrunk,
|
|
668
|
+
int (*compare)(const DATATYPE a, const DATATYPE b, void *udata),
|
|
669
|
+
void *udata)
|
|
670
|
+
{
|
|
671
|
+
*removed = false;
|
|
672
|
+
*shrunk = false;
|
|
673
|
+
if (node->kind == LEAF) {
|
|
674
|
+
for (int i = 0; i < node->count; i++) {
|
|
675
|
+
if (!rect_equals_bin(ir, &node->rects[i])) {
|
|
676
|
+
// Must be exactly the same, binary comparison.
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
int cmp = compare ?
|
|
680
|
+
compare(node->datas[i].data, item.data, udata) :
|
|
681
|
+
memcmp(&node->datas[i].data, &item.data, sizeof(DATATYPE));
|
|
682
|
+
if (cmp != 0) {
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
// Found the target item to delete.
|
|
686
|
+
if (tr->item_free) {
|
|
687
|
+
tr->item_free(node->datas[i].data, tr->udata);
|
|
688
|
+
}
|
|
689
|
+
node->rects[i] = node->rects[node->count-1];
|
|
690
|
+
node->datas[i] = node->datas[node->count-1];
|
|
691
|
+
node->count--;
|
|
692
|
+
if (rect_onedge(ir, nr)) {
|
|
693
|
+
// The item rect was on the edge of the node rect.
|
|
694
|
+
// We need to recalculate the node rect.
|
|
695
|
+
*nr = node_rect_calc(node);
|
|
696
|
+
// Notify the caller that we shrunk the rect.
|
|
697
|
+
*shrunk = true;
|
|
698
|
+
}
|
|
699
|
+
*removed = true;
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
return true;
|
|
703
|
+
}
|
|
704
|
+
int h = 0;
|
|
705
|
+
#ifdef USE_PATHHINT
|
|
706
|
+
h = tr->path_hint[depth];
|
|
707
|
+
if (h < node->count) {
|
|
708
|
+
if (rect_contains(&node->rects[h], ir)) {
|
|
709
|
+
cow_node_or(node->nodes[h], return false);
|
|
710
|
+
if (!node_delete(tr, &node->rects[h], node->nodes[h], ir, item,
|
|
711
|
+
depth+1,removed, shrunk, compare, udata))
|
|
712
|
+
{
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
if (*removed) {
|
|
716
|
+
goto removed;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
h = 0;
|
|
721
|
+
#endif
|
|
722
|
+
for (; h < node->count; h++) {
|
|
723
|
+
if (!rect_contains(&node->rects[h], ir)) {
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
struct rect crect = node->rects[h];
|
|
727
|
+
cow_node_or(node->nodes[h], return false);
|
|
728
|
+
if (!node_delete(tr, &node->rects[h], node->nodes[h], ir, item, depth+1,
|
|
729
|
+
removed, shrunk, compare, udata))
|
|
730
|
+
{
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
if (!*removed) {
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
removed:
|
|
737
|
+
if (node->nodes[h]->count == 0) {
|
|
738
|
+
// underflow
|
|
739
|
+
node_free(tr, node->nodes[h]);
|
|
740
|
+
node->rects[h] = node->rects[node->count-1];
|
|
741
|
+
node->nodes[h] = node->nodes[node->count-1];
|
|
742
|
+
node->count--;
|
|
743
|
+
*nr = node_rect_calc(node);
|
|
744
|
+
*shrunk = true;
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
#ifdef USE_PATHHINT
|
|
748
|
+
tr->path_hint[depth] = h;
|
|
749
|
+
#endif
|
|
750
|
+
if (*shrunk) {
|
|
751
|
+
*shrunk = !rect_equals(&node->rects[h], &crect);
|
|
752
|
+
if (*shrunk) {
|
|
753
|
+
*nr = node_rect_calc(node);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return true;
|
|
757
|
+
}
|
|
758
|
+
return true;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// returns false if out of memory
|
|
762
|
+
static bool rtree_delete0(struct rtree *tr, const NUMTYPE *min,
|
|
763
|
+
const NUMTYPE *max, const DATATYPE data,
|
|
764
|
+
int (*compare)(const DATATYPE a, const DATATYPE b, void *udata),
|
|
765
|
+
void *udata)
|
|
766
|
+
{
|
|
767
|
+
// copy input rect
|
|
768
|
+
struct rect rect;
|
|
769
|
+
memcpy(&rect.min[0], min, sizeof(NUMTYPE)*DIMS);
|
|
770
|
+
memcpy(&rect.max[0], max?max:min, sizeof(NUMTYPE)*DIMS);
|
|
771
|
+
|
|
772
|
+
// copy input data
|
|
773
|
+
struct item item;
|
|
774
|
+
memcpy(&item.data, &data, sizeof(DATATYPE));
|
|
775
|
+
|
|
776
|
+
if (!tr->root) {
|
|
777
|
+
return true;
|
|
778
|
+
}
|
|
779
|
+
bool removed = false;
|
|
780
|
+
bool shrunk = false;
|
|
781
|
+
cow_node_or(tr->root, return false);
|
|
782
|
+
if (!node_delete(tr, &tr->rect, tr->root, &rect, item, 0, &removed, &shrunk,
|
|
783
|
+
compare, udata))
|
|
784
|
+
{
|
|
785
|
+
return false;
|
|
786
|
+
}
|
|
787
|
+
if (!removed) {
|
|
788
|
+
return true;
|
|
789
|
+
}
|
|
790
|
+
tr->count--;
|
|
791
|
+
if (tr->count == 0) {
|
|
792
|
+
node_free(tr, tr->root);
|
|
793
|
+
tr->root = NULL;
|
|
794
|
+
memset(&tr->rect, 0, sizeof(struct rect));
|
|
795
|
+
tr->height = 0;
|
|
796
|
+
} else {
|
|
797
|
+
while (tr->root->kind == BRANCH && tr->root->count == 1) {
|
|
798
|
+
struct node *prev = tr->root;
|
|
799
|
+
tr->root = tr->root->nodes[0];
|
|
800
|
+
prev->count = 0;
|
|
801
|
+
node_free(tr, prev);
|
|
802
|
+
tr->height--;
|
|
803
|
+
}
|
|
804
|
+
if (shrunk) {
|
|
805
|
+
tr->rect = node_rect_calc(tr->root);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return true;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
bool rtree_delete(struct rtree *tr, const NUMTYPE *min, const NUMTYPE *max,
|
|
812
|
+
const DATATYPE data)
|
|
813
|
+
{
|
|
814
|
+
return rtree_delete0(tr, min, max, data, NULL, NULL);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
bool rtree_delete_with_comparator(struct rtree *tr, const NUMTYPE *min,
|
|
818
|
+
const NUMTYPE *max, const DATATYPE data,
|
|
819
|
+
int (*compare)(const DATATYPE a, const DATATYPE b, void *udata),
|
|
820
|
+
void *udata)
|
|
821
|
+
{
|
|
822
|
+
return rtree_delete0(tr, min, max, data, compare, udata);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
struct rtree *rtree_clone(struct rtree *tr) {
|
|
826
|
+
if (!tr) return NULL;
|
|
827
|
+
struct rtree *tr2 = tr->malloc(sizeof(struct rtree));
|
|
828
|
+
if (!tr2) return NULL;
|
|
829
|
+
memcpy(tr2, tr, sizeof(struct rtree));
|
|
830
|
+
if (tr2->root) rc_fetch_add(&tr2->root->rc, 1);
|
|
831
|
+
return tr2;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
void rtree_opt_relaxed_atomics(struct rtree *tr) {
|
|
835
|
+
tr->relaxed = true;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
#ifdef TEST_PRIVATE_FUNCTIONS
|
|
839
|
+
#include "tests/priv_funcs.h"
|
|
840
|
+
#endif
|