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/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
@@ -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
+