geo 0.1.2
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.
- data/README +24 -0
- data/examples/intersects.rb +26 -0
- data/ext/common.c +35 -0
- data/ext/common.h +97 -0
- data/ext/extconf.rb +51 -0
- data/ext/geo.c +163 -0
- data/ext/geo_set.c +488 -0
- data/ext/geo_set.h +156 -0
- data/ext/intersection.c +58 -0
- data/ext/intersection.h +38 -0
- data/ext/line.c +436 -0
- data/ext/line.h +170 -0
- data/ext/line_set.c +596 -0
- data/ext/line_set.h +87 -0
- data/ext/point.c +265 -0
- data/ext/point.h +123 -0
- data/ext/point_set.c +93 -0
- data/ext/point_set.h +44 -0
- data/ext/triangle.c +412 -0
- data/ext/triangle.h +83 -0
- data/ext/triangle_set.c +347 -0
- data/ext/triangle_set.h +68 -0
- data/ext/types.h +48 -0
- metadata +69 -0
data/ext/geo_set.c
ADDED
@@ -0,0 +1,488 @@
|
|
1
|
+
|
2
|
+
#include "common.h"
|
3
|
+
|
4
|
+
VALUE
|
5
|
+
rb_geo_set_inspect(VALUE self) {
|
6
|
+
gchar rval[1024];
|
7
|
+
GeoSet *l;
|
8
|
+
GEO_SET(self, l);
|
9
|
+
snprintf(rval, 1024, "<%s:%p size=%u>", rb_obj_classname(self), l, g_hash_table_size(l->table));
|
10
|
+
return rb_str_new2(rval);
|
11
|
+
}
|
12
|
+
|
13
|
+
VALUE
|
14
|
+
rb_geo_set_size(VALUE self) {
|
15
|
+
GeoSet *set;
|
16
|
+
GEO_SET(self, set);
|
17
|
+
return INT2NUM(g_hash_table_size(set->table));
|
18
|
+
}
|
19
|
+
|
20
|
+
static void
|
21
|
+
g_hash_table_destroy_notify(gpointer p) {
|
22
|
+
g_hash_table_destroy((GHashTable *) p);
|
23
|
+
}
|
24
|
+
|
25
|
+
static GeoSet*
|
26
|
+
new_geo_set_without_table() {
|
27
|
+
GeoSet *rval = ALLOC(GeoSet);
|
28
|
+
rval->iterating = FALSE;
|
29
|
+
rval->bottom_left = new_point(0,0);
|
30
|
+
rval->top_right = new_point(0,0);
|
31
|
+
rval->segment_side = 0;
|
32
|
+
rval->segments = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_hash_table_destroy_notify);
|
33
|
+
rval->segment_lines = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, line_destroy_notify);
|
34
|
+
rval->index_dirty = TRUE;
|
35
|
+
return rval;
|
36
|
+
}
|
37
|
+
|
38
|
+
void
|
39
|
+
geo_set_free(GeoSet *t) {
|
40
|
+
free(t->bottom_left);
|
41
|
+
free(t->top_right);
|
42
|
+
g_hash_table_destroy(t->table);
|
43
|
+
g_hash_table_destroy(t->segments);
|
44
|
+
g_hash_table_destroy(t->segment_lines);
|
45
|
+
free(t);
|
46
|
+
}
|
47
|
+
|
48
|
+
GeoSet*
|
49
|
+
new_geo_set_with_destroy_notifier(GDestroyNotify destroy_notify) {
|
50
|
+
GeoSet *rval = new_geo_set_without_table();
|
51
|
+
rval->table = g_hash_table_new_full(g_direct_hash, g_direct_equal, destroy_notify, NULL);
|
52
|
+
return rval;
|
53
|
+
}
|
54
|
+
|
55
|
+
GeoSet*
|
56
|
+
new_geo_set() {
|
57
|
+
GeoSet *rval = new_geo_set_without_table();
|
58
|
+
rval->table = g_hash_table_new(g_direct_hash, g_direct_equal);
|
59
|
+
return rval;
|
60
|
+
}
|
61
|
+
|
62
|
+
void
|
63
|
+
geo_set_insert(GeoSet *set, gpointer content) {
|
64
|
+
CHECK_ITERATING(set);
|
65
|
+
g_hash_table_insert(set->table, content, content);
|
66
|
+
set->index_dirty = TRUE;
|
67
|
+
}
|
68
|
+
|
69
|
+
|
70
|
+
static void
|
71
|
+
g_hash_table_insert_into_other(gpointer key, gpointer value, gpointer user_data) {
|
72
|
+
g_hash_table_insert((GHashTable *) user_data, key, value);
|
73
|
+
}
|
74
|
+
|
75
|
+
static void
|
76
|
+
g_hash_table_insert_line_copy_into_other(gpointer key, gpointer value, gpointer user_data) {
|
77
|
+
Line *l = (Line *) value;
|
78
|
+
Line *copy = new_line_with_coordinates(l->p1->x, l->p1->y, l->p2->x, l->p2->y);
|
79
|
+
g_hash_table_insert((GHashTable *) user_data, key, copy);
|
80
|
+
}
|
81
|
+
|
82
|
+
static void
|
83
|
+
g_hash_table_insert_segments_into_other(gpointer key, gpointer value, gpointer user_data) {
|
84
|
+
GHashTable *target_segments = (GHashTable *) user_data;
|
85
|
+
GHashTable *source_segment = (GHashTable *) value;
|
86
|
+
GHashTable *target_segment = g_hash_table_new(g_direct_hash, g_direct_equal);
|
87
|
+
g_hash_table_foreach(source_segment, g_hash_table_insert_into_other, target_segment);
|
88
|
+
g_hash_table_insert(target_segments, key, target_segment);
|
89
|
+
}
|
90
|
+
|
91
|
+
GeoSet*
|
92
|
+
geo_set_clone(GeoSet *set) {
|
93
|
+
GeoSet *rval = new_geo_set();
|
94
|
+
g_hash_table_foreach(set->table, g_hash_table_insert_into_other, rval->table);
|
95
|
+
g_hash_table_foreach(set->segments, g_hash_table_insert_segments_into_other, rval->segments);
|
96
|
+
g_hash_table_foreach(set->segment_lines, g_hash_table_insert_line_copy_into_other, rval->segment_lines);
|
97
|
+
return rval;
|
98
|
+
}
|
99
|
+
|
100
|
+
void
|
101
|
+
geo_set_handle_bounds(GeoSet *set, Point *bottom_left, Point *top_right) {
|
102
|
+
if (set->bottom_left == NULL) {
|
103
|
+
set->bottom_left = new_point(bottom_left->x, bottom_left->y);
|
104
|
+
} else {
|
105
|
+
if (set->bottom_left->x > bottom_left->x)
|
106
|
+
set->bottom_left->x = bottom_left->x;
|
107
|
+
if (set->bottom_left->y > bottom_left->y)
|
108
|
+
set->bottom_left->y = bottom_left->y;
|
109
|
+
}
|
110
|
+
if (set->top_right == NULL) {
|
111
|
+
set->top_right = new_point(top_right->x, top_right->y);
|
112
|
+
} else {
|
113
|
+
if (set->top_right->x < top_right->x)
|
114
|
+
set->top_right->x = top_right->x;
|
115
|
+
if (set->top_right->y < top_right->y)
|
116
|
+
set->top_right->y = top_right->y;
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
static void
|
121
|
+
geo_set_find_bounds(GeoSet *set, geo_set_bound_finder bound_finder) {
|
122
|
+
free(set->bottom_left);
|
123
|
+
free(set->top_right);
|
124
|
+
set->bottom_left = NULL;
|
125
|
+
set->top_right = NULL;
|
126
|
+
bound_finder(set);
|
127
|
+
set->segment_side = sqrt(GEO_SET_WIDTH(set) * GEO_SET_HEIGHT(set) / g_hash_table_size(set->table));
|
128
|
+
if (GEO_SET_HORIZONTAL_SEGMENTS(set) > MAX_GEO_SET_SEGMENTS)
|
129
|
+
set->segment_side = GEO_SET_WIDTH(set) / MAX_GEO_SET_SEGMENTS;
|
130
|
+
if (GEO_SET_VERTICAL_SEGMENTS(set) > MAX_GEO_SET_SEGMENTS)
|
131
|
+
set->segment_side = GEO_SET_HEIGHT(set) / MAX_GEO_SET_SEGMENTS;
|
132
|
+
}
|
133
|
+
|
134
|
+
static void
|
135
|
+
geo_set_create_segment_lines(GeoSet *set) {
|
136
|
+
guint32 tmp;
|
137
|
+
g_hash_table_destroy(set->segment_lines);
|
138
|
+
set->segment_lines = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, line_destroy_notify);
|
139
|
+
for (tmp = 0; set->bottom_left->x + (tmp * set->segment_side) < set->top_right->x + set->segment_side; tmp ++) {
|
140
|
+
g_hash_table_insert(set->segment_lines,
|
141
|
+
GUINT_TO_POINTER((tmp << 16) + G_MAXUINT16),
|
142
|
+
new_line_with_coordinates(set->bottom_left->x + tmp * set->segment_side,
|
143
|
+
set->bottom_left->y,
|
144
|
+
set->bottom_left->x + tmp * set->segment_side,
|
145
|
+
set->top_right->y));
|
146
|
+
}
|
147
|
+
for (tmp = 0; set->bottom_left->y + (tmp * set->segment_side) < set->top_right->y + set->segment_side; tmp++) {
|
148
|
+
g_hash_table_insert(set->segment_lines,
|
149
|
+
GUINT_TO_POINTER(tmp),
|
150
|
+
new_line_with_coordinates(set->bottom_left->x,
|
151
|
+
set->bottom_left->y + tmp * set->segment_side,
|
152
|
+
set->top_right->x,
|
153
|
+
set->bottom_left->y + tmp * set->segment_side));
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
VALUE
|
158
|
+
rb_geo_set_structures_in_segment(VALUE self, VALUE xV, VALUE yV, geo_structure_to_value converter) {
|
159
|
+
guint32 segment_id;
|
160
|
+
GeoSet *me;
|
161
|
+
VALUE rval;
|
162
|
+
gpointer args[3];
|
163
|
+
gpointer inner_args[2];
|
164
|
+
guint16 x;
|
165
|
+
guint16 y;
|
166
|
+
GHashTable *seen_structures = g_hash_table_new(g_direct_hash, g_direct_equal);
|
167
|
+
CHECK_NUMERICALITY(xV);
|
168
|
+
CHECK_NUMERICALITY(yV);
|
169
|
+
GEO_SET(self, me);
|
170
|
+
x = (guint16) NUM2INT(xV);
|
171
|
+
y = (guint16) NUM2INT(yV);
|
172
|
+
segment_id = (x << 16) + y;
|
173
|
+
rval = rb_ary_new();
|
174
|
+
inner_args[0] = &rval;
|
175
|
+
inner_args[1] = converter;
|
176
|
+
args[0] = seen_structures;
|
177
|
+
args[1] = geo_set_insert_structure_into_rb_ary;
|
178
|
+
args[2] = inner_args;
|
179
|
+
geo_set_each_structure_in_segment_id_until(me, segment_id, args);
|
180
|
+
g_hash_table_destroy(seen_structures);
|
181
|
+
return rval;
|
182
|
+
}
|
183
|
+
|
184
|
+
static gboolean
|
185
|
+
g_hash_table_call_handler_for_structure_in_id_until(gpointer key, gpointer value, gpointer user_data) {
|
186
|
+
gpointer *args = (gpointer *) user_data;
|
187
|
+
GeoSet *set = (GeoSet *) args[0];
|
188
|
+
geo_set_structure_handler handler = (geo_set_structure_handler) args[1];
|
189
|
+
GHashTable *seen_structures = (GHashTable *) args[2];
|
190
|
+
gpointer *rval = (gpointer *) args[3];
|
191
|
+
if (g_hash_table_lookup(seen_structures, key) == NULL) {
|
192
|
+
g_hash_table_insert(seen_structures, key, GUINT_TO_POINTER(1));
|
193
|
+
if ((*rval = handler(set, key, args[4])) != NULL) {
|
194
|
+
return TRUE;
|
195
|
+
} else {
|
196
|
+
return FALSE;
|
197
|
+
}
|
198
|
+
}
|
199
|
+
return FALSE;
|
200
|
+
}
|
201
|
+
|
202
|
+
/*
|
203
|
+
* To enable calling this function from functions requiring geo_set_segment_id_handlers
|
204
|
+
* we have this signature.
|
205
|
+
*
|
206
|
+
* meta is supposed to look like { GHashTable *seen_structures, geo_set_structure_handler handler, gpointer handler_args }
|
207
|
+
*/
|
208
|
+
gpointer
|
209
|
+
geo_set_each_structure_in_segment_id_until(GeoSet *set, guint32 segment_id, gpointer meta) {
|
210
|
+
gpointer *args = (gpointer *) meta;
|
211
|
+
GHashTable *seen_structures = (GHashTable *) args[0];
|
212
|
+
geo_set_structure_handler handler = (geo_set_structure_handler) args[1];
|
213
|
+
gpointer handler_args = args[2];
|
214
|
+
GHashTable *structures_in_id = (GHashTable *) g_hash_table_lookup(set->segments, GUINT_TO_POINTER(segment_id));
|
215
|
+
if (structures_in_id != NULL) {
|
216
|
+
gpointer rval = NULL;
|
217
|
+
gpointer args[5] = { set, handler, seen_structures, &rval, handler_args };
|
218
|
+
g_hash_table_find(structures_in_id, g_hash_table_call_handler_for_structure_in_id_until, args);
|
219
|
+
return rval;
|
220
|
+
} else {
|
221
|
+
return NULL;
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
gpointer
|
226
|
+
geo_set_insert_into_segment(GeoSet *set, guint32 segment_id, gpointer geo_struct) {
|
227
|
+
GHashTable *segment = (GHashTable *) g_hash_table_lookup(set->segments, GUINT_TO_POINTER(segment_id));
|
228
|
+
if (segment == NULL) {
|
229
|
+
segment = g_hash_table_new(g_direct_hash, g_direct_equal);
|
230
|
+
g_hash_table_insert(set->segments, GUINT_TO_POINTER(segment_id), segment);
|
231
|
+
}
|
232
|
+
g_hash_table_insert(segment, geo_struct, geo_struct);
|
233
|
+
return NULL;
|
234
|
+
}
|
235
|
+
|
236
|
+
gpointer
|
237
|
+
geo_set_insert_segment_id_into_rb_ary(GeoSet *set, guint32 segment_id, gpointer ary_ptr) {
|
238
|
+
rb_ary_push(*( (VALUE *) ary_ptr), rb_ary_new3(2,
|
239
|
+
INT2NUM((guint16) (segment_id >> 16)),
|
240
|
+
INT2NUM((guint16) ((segment_id << 16) >> 16))));
|
241
|
+
return NULL;
|
242
|
+
}
|
243
|
+
|
244
|
+
gpointer
|
245
|
+
geo_set_insert_structure_into_rb_ary(GeoSet *set, gpointer structure, gpointer user_data) {
|
246
|
+
gpointer *args = (gpointer *) user_data;
|
247
|
+
rb_ary_push(*( (VALUE *) args[0] ), ( (geo_structure_to_value) args[1] )(structure));
|
248
|
+
return NULL;
|
249
|
+
}
|
250
|
+
|
251
|
+
static gpointer
|
252
|
+
call_geo_set_segment_handler_if_unseen_id(geo_set_segment_id_handler handler,
|
253
|
+
GeoSet *set,
|
254
|
+
gpointer user_data,
|
255
|
+
GHashTable *seen_segment_ids,
|
256
|
+
guint16 x,
|
257
|
+
guint16 y) {
|
258
|
+
guint32 segment_id = (x << 16) + y;
|
259
|
+
if (g_hash_table_lookup(seen_segment_ids, GUINT_TO_POINTER(segment_id)) == NULL) {
|
260
|
+
gpointer rval = NULL;
|
261
|
+
rval = handler(set, segment_id, user_data);
|
262
|
+
g_hash_table_insert(seen_segment_ids, GUINT_TO_POINTER(segment_id), GUINT_TO_POINTER(1));
|
263
|
+
return rval;
|
264
|
+
}
|
265
|
+
return NULL;
|
266
|
+
}
|
267
|
+
|
268
|
+
gpointer
|
269
|
+
geo_set_each_segment_id_for_point_until(GeoSet *set,
|
270
|
+
Point *point,
|
271
|
+
GHashTable *seen_segment_ids,
|
272
|
+
geo_set_segment_id_handler handler,
|
273
|
+
gpointer user_data) {
|
274
|
+
if (point->x < set->bottom_left->x ||
|
275
|
+
point->y < set->bottom_left->y ||
|
276
|
+
point->x > set->top_right->x + set->segment_side ||
|
277
|
+
point->y > set->top_right->y + set->segment_side)
|
278
|
+
return NULL;
|
279
|
+
gdouble horizontal_segment_quota = (point->x - set->bottom_left->x) / set->segment_side;
|
280
|
+
gdouble vertical_segment_quota = (point->y - set->bottom_left->y) / set->segment_side;
|
281
|
+
gpointer rval = NULL;
|
282
|
+
if (IS_INTLIKE(vertical_segment_quota)) {
|
283
|
+
guint16 rounded_vertical = (guint16) round(vertical_segment_quota);
|
284
|
+
if (IS_INTLIKE(horizontal_segment_quota)) {
|
285
|
+
guint16 rounded_horizontal = (guint16) round(horizontal_segment_quota);
|
286
|
+
if (rounded_horizontal > 0) {
|
287
|
+
if (rounded_vertical > 0) {
|
288
|
+
if ((rval = call_geo_set_segment_handler_if_unseen_id(handler,
|
289
|
+
set,
|
290
|
+
user_data,
|
291
|
+
seen_segment_ids,
|
292
|
+
rounded_horizontal - 1,
|
293
|
+
rounded_vertical - 1)) != NULL)
|
294
|
+
return rval;
|
295
|
+
}
|
296
|
+
if ((rval = call_geo_set_segment_handler_if_unseen_id(handler,
|
297
|
+
set,
|
298
|
+
user_data,
|
299
|
+
seen_segment_ids,
|
300
|
+
rounded_horizontal - 1,
|
301
|
+
rounded_vertical)) != NULL)
|
302
|
+
return rval;
|
303
|
+
}
|
304
|
+
if (rounded_vertical > 0) {
|
305
|
+
if ((rval = call_geo_set_segment_handler_if_unseen_id(handler,
|
306
|
+
set,
|
307
|
+
user_data,
|
308
|
+
seen_segment_ids,
|
309
|
+
rounded_horizontal,
|
310
|
+
rounded_vertical - 1)) != NULL)
|
311
|
+
return rval;
|
312
|
+
}
|
313
|
+
if ((rval = call_geo_set_segment_handler_if_unseen_id(handler,
|
314
|
+
set,
|
315
|
+
user_data,
|
316
|
+
seen_segment_ids,
|
317
|
+
rounded_horizontal,
|
318
|
+
rounded_vertical)) != NULL)
|
319
|
+
return rval;
|
320
|
+
} else {
|
321
|
+
if (rounded_vertical > 0) {
|
322
|
+
if ((rval = call_geo_set_segment_handler_if_unseen_id(handler,
|
323
|
+
set,
|
324
|
+
user_data,
|
325
|
+
seen_segment_ids,
|
326
|
+
(guint16) horizontal_segment_quota,
|
327
|
+
rounded_vertical - 1)) != NULL)
|
328
|
+
return rval;
|
329
|
+
}
|
330
|
+
if ((rval = call_geo_set_segment_handler_if_unseen_id(handler,
|
331
|
+
set,
|
332
|
+
user_data,
|
333
|
+
seen_segment_ids,
|
334
|
+
(guint16) horizontal_segment_quota,
|
335
|
+
rounded_vertical)) != NULL)
|
336
|
+
return rval;
|
337
|
+
}
|
338
|
+
} else {
|
339
|
+
if (IS_INTLIKE(horizontal_segment_quota)) {
|
340
|
+
guint16 rounded_horizontal = (gint) round(horizontal_segment_quota);
|
341
|
+
if (rounded_horizontal > 0) {
|
342
|
+
if ((rval = call_geo_set_segment_handler_if_unseen_id(handler,
|
343
|
+
set,
|
344
|
+
user_data,
|
345
|
+
seen_segment_ids,
|
346
|
+
rounded_horizontal - 1,
|
347
|
+
(guint16) vertical_segment_quota)) != NULL)
|
348
|
+
return rval;
|
349
|
+
}
|
350
|
+
if ((rval = call_geo_set_segment_handler_if_unseen_id(handler,
|
351
|
+
set,
|
352
|
+
user_data,
|
353
|
+
seen_segment_ids,
|
354
|
+
rounded_horizontal,
|
355
|
+
(guint16) vertical_segment_quota)) != NULL)
|
356
|
+
return rval;
|
357
|
+
} else {
|
358
|
+
if ((rval = call_geo_set_segment_handler_if_unseen_id(handler,
|
359
|
+
set,
|
360
|
+
user_data,
|
361
|
+
seen_segment_ids,
|
362
|
+
(guint16) horizontal_segment_quota,
|
363
|
+
(guint16) vertical_segment_quota)) != NULL)
|
364
|
+
return rval;
|
365
|
+
}
|
366
|
+
}
|
367
|
+
return NULL;
|
368
|
+
}
|
369
|
+
|
370
|
+
gpointer
|
371
|
+
geo_set_each_segment_line_until(GeoSet *set,
|
372
|
+
Point *bottom_left,
|
373
|
+
Point *top_right,
|
374
|
+
geo_set_segment_line_handler handler,
|
375
|
+
gpointer user_data) {
|
376
|
+
gpointer rval = NULL;
|
377
|
+
guint16 tmp;
|
378
|
+
Line *tmp_line;
|
379
|
+
gint left_n = MAX((gint) ceil((bottom_left->x - set->bottom_left->x) / set->segment_side),
|
380
|
+
0);
|
381
|
+
gint bottom_n = MAX((gint) ceil((bottom_left->y - set->bottom_left->y) / set->segment_side),
|
382
|
+
0);
|
383
|
+
gint right_n = MIN((gint) ((top_right->x - set->bottom_left->x) / set->segment_side),
|
384
|
+
GEO_SET_HORIZONTAL_SEGMENTS(set));
|
385
|
+
gint top_n = MIN((gint) ((top_right->y - set->bottom_left->y) / set->segment_side),
|
386
|
+
GEO_SET_VERTICAL_SEGMENTS(set));
|
387
|
+
for (tmp = left_n; tmp <= right_n; tmp++) {
|
388
|
+
tmp_line = (Line *) g_hash_table_lookup(set->segment_lines, GUINT_TO_POINTER((guint32) ((tmp << 16) + G_MAXUINT16)));
|
389
|
+
if (tmp_line != NULL) {
|
390
|
+
if ((rval = handler(set, tmp_line, user_data)) != NULL)
|
391
|
+
return rval;
|
392
|
+
}
|
393
|
+
}
|
394
|
+
for (tmp = bottom_n; tmp <= top_n; tmp++) {
|
395
|
+
tmp_line = (Line *) g_hash_table_lookup(set->segment_lines, GUINT_TO_POINTER((guint32) tmp));
|
396
|
+
if (tmp_line != NULL) {
|
397
|
+
if ((rval = handler(set, tmp_line, user_data)) != NULL)
|
398
|
+
return rval;
|
399
|
+
}
|
400
|
+
}
|
401
|
+
return NULL;
|
402
|
+
}
|
403
|
+
|
404
|
+
static void
|
405
|
+
geo_set_find_segments(GeoSet *set, geo_set_segment_finder segment_finder) {
|
406
|
+
g_hash_table_destroy(set->segments);
|
407
|
+
set->segments = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_hash_table_destroy_notify);
|
408
|
+
segment_finder(set);
|
409
|
+
}
|
410
|
+
|
411
|
+
gboolean
|
412
|
+
geo_set_reindex(GeoSet *set, geo_set_bound_finder bound_finder, geo_set_segment_finder segment_finder) {
|
413
|
+
if (set->index_dirty && g_hash_table_size(set->table) > 0) {
|
414
|
+
set->index_dirty = FALSE;
|
415
|
+
geo_set_find_bounds(set, bound_finder);
|
416
|
+
if (set->segment_side > 0) {
|
417
|
+
geo_set_create_segment_lines(set);
|
418
|
+
geo_set_find_segments(set, segment_finder);
|
419
|
+
return TRUE;
|
420
|
+
} else {
|
421
|
+
return FALSE;
|
422
|
+
}
|
423
|
+
} else {
|
424
|
+
return FALSE;
|
425
|
+
}
|
426
|
+
}
|
427
|
+
|
428
|
+
void
|
429
|
+
geo_set_foreach(GeoSet *set, GHFunc func) {
|
430
|
+
set->iterating = TRUE;
|
431
|
+
g_hash_table_foreach(set->table, func, NULL);
|
432
|
+
set->iterating = FALSE;
|
433
|
+
}
|
434
|
+
|
435
|
+
VALUE
|
436
|
+
geo_set_delete(GeoSet *set, gpointer p, VALUE rval) {
|
437
|
+
CHECK_ITERATING(set);
|
438
|
+
if (g_hash_table_remove(set->table, p)) {
|
439
|
+
set->index_dirty = TRUE;
|
440
|
+
return rval;
|
441
|
+
} else {
|
442
|
+
return Qnil;
|
443
|
+
}
|
444
|
+
}
|
445
|
+
|
446
|
+
VALUE
|
447
|
+
rb_geo_set_segment_side(VALUE self) {
|
448
|
+
GeoSet *set;
|
449
|
+
GEO_SET(self, set);
|
450
|
+
return rb_float_new(set->segment_side);
|
451
|
+
}
|
452
|
+
|
453
|
+
VALUE
|
454
|
+
rb_geo_set_segment_lines(VALUE self) {
|
455
|
+
GeoSet *set;
|
456
|
+
VALUE rval = rb_ary_new();
|
457
|
+
GEO_SET(self, set);
|
458
|
+
g_hash_table_foreach(set->segment_lines, g_hash_table_clone_line_value_into_rb_value, (gpointer) &rval);
|
459
|
+
return rval;
|
460
|
+
}
|
461
|
+
|
462
|
+
VALUE
|
463
|
+
rb_geo_set_bottom_left(VALUE self) {
|
464
|
+
GeoSet *set;
|
465
|
+
GEO_SET(self, set);
|
466
|
+
return RB_POINT(new_point(set->bottom_left->x, set->bottom_left->y), rb_point);
|
467
|
+
}
|
468
|
+
|
469
|
+
VALUE
|
470
|
+
rb_geo_set_top_right(VALUE self) {
|
471
|
+
GeoSet *set;
|
472
|
+
GEO_SET(self, set);
|
473
|
+
return RB_POINT(new_point(set->top_right->x, set->top_right->y), rb_point);
|
474
|
+
}
|
475
|
+
|
476
|
+
VALUE
|
477
|
+
rb_geo_set_width(VALUE self) {
|
478
|
+
GeoSet *set;
|
479
|
+
GEO_SET(self, set);
|
480
|
+
return rb_float_new(GEO_SET_WIDTH(set));
|
481
|
+
}
|
482
|
+
|
483
|
+
VALUE
|
484
|
+
rb_geo_set_height(VALUE self) {
|
485
|
+
GeoSet *set;
|
486
|
+
GEO_SET(self, set);
|
487
|
+
return rb_float_new(GEO_SET_HEIGHT(set));
|
488
|
+
}
|
data/ext/geo_set.h
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
|
2
|
+
#ifndef GEO_SET_H
|
3
|
+
#define GEO_SET_H
|
4
|
+
|
5
|
+
#include "common.h"
|
6
|
+
|
7
|
+
struct IntersectionStruct;
|
8
|
+
enum IntersectionTypeEnum;
|
9
|
+
|
10
|
+
#define GEO_SET(rb_set,geo_set_pointer) Data_Get_Struct((rb_set), GeoSet, (geo_set_pointer))
|
11
|
+
#define GEO_SET_WIDTH(table) ((table)->top_right->x - (table)->bottom_left->x)
|
12
|
+
#define GEO_SET_HEIGHT(table) ((table)->top_right->y - (table)->bottom_left->y)
|
13
|
+
#define GEO_SET_HORIZONTAL_SEGMENTS(table) ((gint) ceil(GEO_SET_WIDTH((table)) / (table)->segment_side))
|
14
|
+
#define GEO_SET_VERTICAL_SEGMENTS(table) ((gint) ceil(GEO_SET_HEIGHT((table)) / (table)->segment_side))
|
15
|
+
#define CHECK_ITERATING(geo_set) if ((geo_set)->iterating) rb_raise(rb_eRuntimeError, "You can not modify this instance while iterating over it!")
|
16
|
+
|
17
|
+
#define MAX_GEO_SET_SEGMENTS G_MAXUINT16
|
18
|
+
|
19
|
+
/*
|
20
|
+
* Should call geo_set_handle_bounds for each contained element with the bounds of
|
21
|
+
* the element as arguments.
|
22
|
+
*/
|
23
|
+
typedef void
|
24
|
+
(*geo_set_bound_finder)(GeoSet *set);
|
25
|
+
|
26
|
+
/*
|
27
|
+
* Should call geo_set_insert_under_segment_ids_for_point for each
|
28
|
+
* intersection between each contained element and the segment lines
|
29
|
+
* of the given set.
|
30
|
+
*
|
31
|
+
* Use geo_set_each_segment_line to walk through the segment lines
|
32
|
+
* relevant for any contained element.
|
33
|
+
*/
|
34
|
+
typedef void
|
35
|
+
(*geo_set_segment_finder)(GeoSet *set);
|
36
|
+
|
37
|
+
/*
|
38
|
+
* Should return the ruby VALUE containing the given geo structure.
|
39
|
+
*/
|
40
|
+
typedef VALUE
|
41
|
+
(*geo_structure_to_value)(gpointer structure);
|
42
|
+
|
43
|
+
typedef gpointer
|
44
|
+
(*geo_set_segment_id_handler)(GeoSet *set, guint32 segment_id, gpointer user_data);
|
45
|
+
|
46
|
+
typedef gpointer
|
47
|
+
(*geo_set_structure_handler)(GeoSet *set, gpointer structure_in_id, gpointer user_data);
|
48
|
+
|
49
|
+
typedef gpointer
|
50
|
+
(*geo_set_segment_line_handler)(GeoSet *set, Line *line, gpointer user_data);
|
51
|
+
|
52
|
+
void
|
53
|
+
geo_set_free(GeoSet *t);
|
54
|
+
|
55
|
+
VALUE
|
56
|
+
rb_geo_set_inspect(VALUE self);
|
57
|
+
|
58
|
+
VALUE
|
59
|
+
rb_geo_set_size(VALUE self);
|
60
|
+
|
61
|
+
GeoSet*
|
62
|
+
new_geo_set_with_destroy_notifier(GDestroyNotify destroy_notify);
|
63
|
+
|
64
|
+
GeoSet*
|
65
|
+
new_geo_set();
|
66
|
+
|
67
|
+
void
|
68
|
+
geo_set_insert(GeoSet *set, gpointer content);
|
69
|
+
|
70
|
+
GeoSet*
|
71
|
+
geo_set_clone(GeoSet *set);
|
72
|
+
|
73
|
+
gboolean
|
74
|
+
geo_set_reindex(GeoSet *set, geo_set_bound_finder bound_finder, geo_set_segment_finder segment_finder);
|
75
|
+
|
76
|
+
void
|
77
|
+
geo_set_foreach(GeoSet *set, GHFunc func);
|
78
|
+
|
79
|
+
VALUE
|
80
|
+
geo_set_delete(GeoSet *set, gpointer p, VALUE rval);
|
81
|
+
|
82
|
+
VALUE
|
83
|
+
rb_geo_set_segment_side(VALUE self);
|
84
|
+
|
85
|
+
/*
|
86
|
+
* Will call handler with set, segment_id and user_data for all segment ids in set that
|
87
|
+
* point is a member of until handler returns non NULL, in which case it will stop and
|
88
|
+
* return what the handler returned. Will not call handler with segment ids in
|
89
|
+
* seen_segment_ids, and will put all segment ids that handler is called for in
|
90
|
+
* seen_segment_ids.
|
91
|
+
*/
|
92
|
+
gpointer
|
93
|
+
geo_set_each_segment_id_for_point_until(GeoSet *set,
|
94
|
+
Point *point,
|
95
|
+
GHashTable *seen_segment_ids,
|
96
|
+
geo_set_segment_id_handler handler,
|
97
|
+
gpointer user_data);
|
98
|
+
|
99
|
+
void
|
100
|
+
geo_set_handle_bounds(GeoSet *set, Point *bottom_left, Point *top_right);
|
101
|
+
|
102
|
+
/*
|
103
|
+
* Will call handler with segment line and user_data for each segment line between bottom_left and top_right
|
104
|
+
* until handler returns non NULL, in which case it will stop and return what the handler returned.
|
105
|
+
*/
|
106
|
+
gpointer
|
107
|
+
geo_set_each_segment_line_until(GeoSet *set,
|
108
|
+
Point *bottom_left,
|
109
|
+
Point *top_right,
|
110
|
+
geo_set_segment_line_handler handler,
|
111
|
+
gpointer user_data);
|
112
|
+
|
113
|
+
VALUE
|
114
|
+
rb_geo_set_structures_in_segment(VALUE self, VALUE x, VALUE y, geo_structure_to_value converter);
|
115
|
+
|
116
|
+
gpointer
|
117
|
+
geo_set_insert_into_segment(GeoSet *set, guint32 segment_id, gpointer geo_struct);
|
118
|
+
|
119
|
+
gpointer
|
120
|
+
geo_set_insert_segment_id_into_rb_ary(GeoSet *set, guint32 segment_id, gpointer ary_ptr);
|
121
|
+
|
122
|
+
VALUE
|
123
|
+
rb_geo_set_segment_lines(VALUE self);
|
124
|
+
|
125
|
+
VALUE
|
126
|
+
rb_geo_set_bottom_left(VALUE self);
|
127
|
+
|
128
|
+
VALUE
|
129
|
+
rb_geo_set_top_right(VALUE self);
|
130
|
+
|
131
|
+
VALUE
|
132
|
+
rb_geo_set_width(VALUE self);
|
133
|
+
|
134
|
+
VALUE
|
135
|
+
rb_geo_set_height(VALUE self);
|
136
|
+
|
137
|
+
/*
|
138
|
+
* To enable calling this function from functions requiring geo_set_segment_id_handlers
|
139
|
+
* we have this signature.
|
140
|
+
*
|
141
|
+
* meta is supposed to look like { GHashTable *seen_structures, geo_set_structure_handler handler, gpointer handler_args }
|
142
|
+
*
|
143
|
+
* Will call the handler with set, structure and user_data for each
|
144
|
+
* structure in segment with segment_id in set, until handler returns
|
145
|
+
* non NULL - in which case it will stop and return what the handler returned.
|
146
|
+
* Will not call handler with structures in seen_structures, and
|
147
|
+
* will put all structures that handler is called for in seen_structures.
|
148
|
+
*/
|
149
|
+
gpointer
|
150
|
+
geo_set_each_structure_in_segment_id_until(GeoSet *set, guint32 segment_id, gpointer meta);
|
151
|
+
|
152
|
+
gpointer
|
153
|
+
geo_set_insert_structure_into_rb_ary(GeoSet *set, gpointer structure, gpointer user_data);
|
154
|
+
|
155
|
+
|
156
|
+
#endif
|
data/ext/intersection.c
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
#include "common.h"
|
3
|
+
|
4
|
+
|
5
|
+
void
|
6
|
+
free_intersection(Intersection *intersection) {
|
7
|
+
if (intersection->type == itPOINT)
|
8
|
+
free(intersection->point);
|
9
|
+
else if (intersection->type == itLINE)
|
10
|
+
free(intersection->line);
|
11
|
+
free(intersection);
|
12
|
+
}
|
13
|
+
|
14
|
+
Intersection*
|
15
|
+
new_intersection_with_line(Line *l, Line *with) {
|
16
|
+
Intersection *rval = ALLOC(Intersection);
|
17
|
+
rval->point = NULL;
|
18
|
+
rval->line = l;
|
19
|
+
rval->with = with;
|
20
|
+
rval->type = itLINE;
|
21
|
+
return rval;
|
22
|
+
}
|
23
|
+
|
24
|
+
Intersection *
|
25
|
+
new_intersection_with_point(Point *p, Line *with) {
|
26
|
+
Intersection *rval = ALLOC(Intersection);
|
27
|
+
rval->point = p;
|
28
|
+
rval->line = NULL;
|
29
|
+
rval->with = with;
|
30
|
+
rval->type = itPOINT;
|
31
|
+
return rval;
|
32
|
+
}
|
33
|
+
|
34
|
+
VALUE
|
35
|
+
rb_intersection(Intersection *intersection, Point *from) {
|
36
|
+
if (intersection == NULL) {
|
37
|
+
return Qnil;
|
38
|
+
} else {
|
39
|
+
VALUE rval;
|
40
|
+
if (intersection->type == itLINE) {
|
41
|
+
if (DISTANCE(from, intersection->line->p1) < DISTANCE(from, intersection->line->p2)) {
|
42
|
+
Point *point = new_point(intersection->line->p1->x, intersection->line->p1->y);
|
43
|
+
rval = RB_POINT(point, rb_point);
|
44
|
+
} else {
|
45
|
+
Point *point = new_point(intersection->line->p2->x, intersection->line->p2->y);
|
46
|
+
rval = RB_POINT(point, rb_point);
|
47
|
+
}
|
48
|
+
} else if (intersection->type == itPOINT) {
|
49
|
+
Point *point = new_point(intersection->point->x, intersection->point->y);
|
50
|
+
rval = RB_POINT(point, rb_point);
|
51
|
+
} else {
|
52
|
+
rb_raise(rb_eRuntimeError, "Weird, an intersection should contain either a point or a line!");
|
53
|
+
}
|
54
|
+
free_intersection(intersection);
|
55
|
+
return rval;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|