geo 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|