geo 0.1.2

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