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/line_set.c
ADDED
@@ -0,0 +1,596 @@
|
|
1
|
+
|
2
|
+
#include "common.h"
|
3
|
+
|
4
|
+
VALUE rb_line_set;
|
5
|
+
|
6
|
+
static gboolean
|
7
|
+
line_set_reindex(GeoSet *line_set);
|
8
|
+
|
9
|
+
static void
|
10
|
+
g_hash_table_mark_line(gpointer key, gpointer value, gpointer user_data) {
|
11
|
+
rb_gc_mark( ( (Line *) key )->rbLine);
|
12
|
+
}
|
13
|
+
|
14
|
+
void
|
15
|
+
line_set_mark(GeoSet *set) {
|
16
|
+
g_hash_table_foreach(set->table, g_hash_table_mark_line, NULL);
|
17
|
+
}
|
18
|
+
|
19
|
+
VALUE
|
20
|
+
rb_line_set_alloc(VALUE class) {
|
21
|
+
GeoSet *set = new_geo_set();
|
22
|
+
return RB_LINE_SET(set, class);
|
23
|
+
}
|
24
|
+
|
25
|
+
VALUE
|
26
|
+
rb_line_set_insert(VALUE self, VALUE line) {
|
27
|
+
GeoSet *me;
|
28
|
+
Line *l;
|
29
|
+
CHECK_LINE(line);
|
30
|
+
GEO_SET(self, me);
|
31
|
+
LINE(line, l);
|
32
|
+
geo_set_insert(me, (gpointer ) l);
|
33
|
+
return self;
|
34
|
+
}
|
35
|
+
|
36
|
+
VALUE
|
37
|
+
rb_line_set_include(VALUE self, VALUE line) {
|
38
|
+
GeoSet *me;
|
39
|
+
Line *t;
|
40
|
+
CHECK_LINE(line);
|
41
|
+
LINE(line, t);
|
42
|
+
GEO_SET(self, me);
|
43
|
+
return GBOOL2RB(g_hash_table_lookup_extended(me->table, (gpointer) t, NULL, NULL));
|
44
|
+
}
|
45
|
+
|
46
|
+
VALUE
|
47
|
+
rb_line_set_delete(VALUE self, VALUE line) {
|
48
|
+
GeoSet *me;
|
49
|
+
Line *t;
|
50
|
+
CHECK_LINE(line);
|
51
|
+
GEO_SET(self, me);
|
52
|
+
LINE(line, t);
|
53
|
+
return geo_set_delete(me, (gpointer) t, line);
|
54
|
+
}
|
55
|
+
|
56
|
+
static void
|
57
|
+
g_hash_table_yield_line(gpointer key, gpointer value, gpointer user_data) {
|
58
|
+
rb_yield(( (Line *) key )->rbLine);
|
59
|
+
}
|
60
|
+
|
61
|
+
VALUE
|
62
|
+
rb_line_set_each(VALUE self) {
|
63
|
+
GeoSet *set;
|
64
|
+
GEO_SET(self, set);
|
65
|
+
geo_set_foreach(set, g_hash_table_yield_line);
|
66
|
+
return self;
|
67
|
+
}
|
68
|
+
|
69
|
+
static gpointer
|
70
|
+
line_set_intersection(GeoSet *set, gpointer set_line_gp, gpointer line_gp) {
|
71
|
+
Intersection *intersection;
|
72
|
+
Line *set_line = (Line *) set_line_gp;
|
73
|
+
Line *line = (Line *) line_gp;
|
74
|
+
if ((intersection = line_intersection(set_line, line)) != NULL)
|
75
|
+
return intersection;
|
76
|
+
return NULL;
|
77
|
+
}
|
78
|
+
|
79
|
+
gboolean
|
80
|
+
line_set_intersects(GeoSet *set, Line *line) {
|
81
|
+
Intersection *intersection;
|
82
|
+
LINE_SET_REINDEX(set);
|
83
|
+
intersection = geo_set_each_structure_having_common_segment_id_with_line_until(set, line, line_set_intersection, line);
|
84
|
+
if (intersection != NULL)
|
85
|
+
free_intersection(intersection);
|
86
|
+
return GBOOL2RB(intersection != NULL);
|
87
|
+
}
|
88
|
+
|
89
|
+
VALUE
|
90
|
+
rb_line_set_intersects(VALUE self, VALUE l) {
|
91
|
+
GeoSet *set;
|
92
|
+
Line *line;
|
93
|
+
CHECK_LINE(l);
|
94
|
+
GEO_SET(self, set);
|
95
|
+
LINE(l, line);
|
96
|
+
if (line_set_intersects(set, line)) {
|
97
|
+
return Qtrue;
|
98
|
+
} else {
|
99
|
+
return Qfalse;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
static gpointer
|
104
|
+
line_set_closest_intersection_in_segments(GeoSet *set, gpointer segment_line_gp, gpointer user_data) {
|
105
|
+
gpointer *args = (gpointer *) user_data;
|
106
|
+
gdouble *closest_distance = (gdouble *) args[0];
|
107
|
+
Intersection **closest_intersection = (Intersection **) args[1];
|
108
|
+
Line *line = (Line *) args[2];
|
109
|
+
Line *segment_line = (Line *) segment_line_gp;
|
110
|
+
Intersection *this;
|
111
|
+
gdouble this_distance;
|
112
|
+
if ((this = line_intersection(segment_line, line)) != NULL) {
|
113
|
+
this_distance = INTERSECTION_DISTANCE(this, line->p1);
|
114
|
+
if (*closest_intersection == NULL) {
|
115
|
+
*closest_intersection = this;
|
116
|
+
*closest_distance = this_distance;
|
117
|
+
} else if (this_distance < *closest_distance) {
|
118
|
+
free_intersection(*closest_intersection);
|
119
|
+
*closest_intersection = this;
|
120
|
+
*closest_distance = this_distance;
|
121
|
+
} else {
|
122
|
+
free_intersection(this);
|
123
|
+
}
|
124
|
+
}
|
125
|
+
return NULL;
|
126
|
+
}
|
127
|
+
|
128
|
+
static Intersection*
|
129
|
+
line_set_closest_intersection(GeoSet *set, Line *line) {
|
130
|
+
Intersection *closest = NULL;
|
131
|
+
double closest_distance = -1;
|
132
|
+
gpointer args[3] = { &closest_distance, &closest, line };
|
133
|
+
LINE_SET_REINDEX(set);
|
134
|
+
geo_set_each_structure_having_common_segment_id_with_line_until(set, line, line_set_closest_intersection_in_segments, args);
|
135
|
+
return closest;
|
136
|
+
}
|
137
|
+
|
138
|
+
static void
|
139
|
+
line_set_slide(GeoSet *set, Line *line, gint ttl) {
|
140
|
+
ZERO_DISTANCE_CHECK(line->p1, line->p2);
|
141
|
+
Intersection *intersection = line_set_closest_intersection(set, line);
|
142
|
+
if (intersection != NULL) {
|
143
|
+
Point touchPoint;
|
144
|
+
Point destOne;
|
145
|
+
Point destTwo;
|
146
|
+
gdouble distanceLeft;
|
147
|
+
gdouble errorOne;
|
148
|
+
gdouble errorTwo;
|
149
|
+
/*
|
150
|
+
* The line we want to adjust against. If the intersection is with a line of zero
|
151
|
+
* length, then it is against ourselves. Otherwise with the intersecting line.
|
152
|
+
*/
|
153
|
+
gboolean zero_intersection = intersection->type == itLINE && ZERO_DISTANCE_P(intersection->with->p1, intersection->with->p2);
|
154
|
+
gdouble adjustment_length = zero_intersection ? DISTANCE(line->p1, line->p2) : DISTANCE(intersection->with->p1, intersection->with->p2);
|
155
|
+
gdouble adjustment_xunit = zero_intersection ? \
|
156
|
+
(line->p2->x - line->p1->x) / adjustment_length : \
|
157
|
+
(intersection->with->p2->x - intersection->with->p1->x) / adjustment_length;
|
158
|
+
gdouble adjustment_yunit = zero_intersection ? \
|
159
|
+
(line->p2->y - line->p1->y) / adjustment_length : \
|
160
|
+
(intersection->with->p2->y - intersection->with->p1->y) / adjustment_length;
|
161
|
+
/*
|
162
|
+
* Calculate where we actually touch the other line.
|
163
|
+
*/
|
164
|
+
if (intersection->type == itLINE) {
|
165
|
+
if (DISTANCE(intersection->line->p1, line->p1) <
|
166
|
+
DISTANCE(intersection->line->p2, line->p1)) {
|
167
|
+
touchPoint = *(intersection->line->p1);
|
168
|
+
} else {
|
169
|
+
touchPoint = *(intersection->line->p2);
|
170
|
+
}
|
171
|
+
} else {
|
172
|
+
touchPoint = *(intersection->point);
|
173
|
+
}
|
174
|
+
if (intersection->type == itPOINT) {
|
175
|
+
/*
|
176
|
+
* If we intersected with a point, move the touch point back towards the start point to not actually touch the other line.
|
177
|
+
*/
|
178
|
+
if (line->p1->x > touchPoint.x)
|
179
|
+
touchPoint.x += MIN(1, line->p1->x - touchPoint.x);
|
180
|
+
if (line->p1->x < touchPoint.x)
|
181
|
+
touchPoint.x -= MIN(1, touchPoint.x - line->p1->x);
|
182
|
+
if (line->p1->y > touchPoint.y)
|
183
|
+
touchPoint.y += MIN(1, line->p1->y - touchPoint.y);
|
184
|
+
if (line->p1->y < touchPoint.y)
|
185
|
+
touchPoint.y -= MIN(1, touchPoint.y - line->p1->y);
|
186
|
+
} else {
|
187
|
+
/*
|
188
|
+
* If we intersected with a line, move perpendicular to our adjustment-line.
|
189
|
+
*/
|
190
|
+
if (rand() < RAND_MAX / 2) {
|
191
|
+
touchPoint.x += adjustment_yunit;
|
192
|
+
touchPoint.y -= adjustment_xunit;
|
193
|
+
} else {
|
194
|
+
touchPoint.x -= adjustment_yunit;
|
195
|
+
touchPoint.y += adjustment_xunit;
|
196
|
+
}
|
197
|
+
}
|
198
|
+
/*
|
199
|
+
* Now that we wont use the intersection anymore, lets free it.
|
200
|
+
*/
|
201
|
+
free_intersection(intersection);
|
202
|
+
/*
|
203
|
+
* If the ttl is reached then we just set the line's p2 to the touchPoint.
|
204
|
+
*/
|
205
|
+
if (ttl < 1) {
|
206
|
+
line->p2->x = touchPoint.x;
|
207
|
+
line->p2->y = touchPoint.y;
|
208
|
+
} else {
|
209
|
+
/*
|
210
|
+
* Otherwise we calculate two alternate destinations.
|
211
|
+
*
|
212
|
+
* If we intersected with a zero length line, then we just go ahead in our
|
213
|
+
* original direction, otherwise we follow the intersecting line.
|
214
|
+
*
|
215
|
+
*/
|
216
|
+
destOne = touchPoint;
|
217
|
+
destTwo = touchPoint;
|
218
|
+
distanceLeft = DISTANCE(line->p1, line->p2) - DISTANCE(&touchPoint, line->p1);
|
219
|
+
if (zero_intersection) {
|
220
|
+
destOne.x += adjustment_xunit * distanceLeft;
|
221
|
+
destOne.y += adjustment_yunit * distanceLeft;
|
222
|
+
destTwo.x += adjustment_xunit * distanceLeft;
|
223
|
+
destTwo.y += adjustment_yunit * distanceLeft;
|
224
|
+
} else {
|
225
|
+
destOne.x += adjustment_xunit * distanceLeft;
|
226
|
+
destOne.y += adjustment_yunit * distanceLeft;
|
227
|
+
destTwo.x -= adjustment_xunit * distanceLeft;
|
228
|
+
destTwo.y -= adjustment_yunit * distanceLeft;
|
229
|
+
}
|
230
|
+
/*
|
231
|
+
* And select the one with the least error.
|
232
|
+
*/
|
233
|
+
errorOne = DISTANCE(line->p2, &destOne);
|
234
|
+
errorTwo = DISTANCE(line->p2, &destTwo);
|
235
|
+
if (DBL_EQL(errorOne, errorTwo)) {
|
236
|
+
line->p2->x = touchPoint.x;
|
237
|
+
line->p2->y = touchPoint.y;
|
238
|
+
} else {
|
239
|
+
if (errorOne < errorTwo) {
|
240
|
+
line->p2->x = destOne.x;
|
241
|
+
line->p2->y = destOne.y;
|
242
|
+
} else if (errorOne > errorTwo) {
|
243
|
+
line->p2->x = destTwo.x;
|
244
|
+
line->p2->y = destTwo.y;
|
245
|
+
}
|
246
|
+
line->p1->x = touchPoint.x;
|
247
|
+
line->p1->y = touchPoint.y;
|
248
|
+
/*
|
249
|
+
* And slide along, in case something else gets in the way.
|
250
|
+
*/
|
251
|
+
line_set_slide(set, line, ttl - 1);
|
252
|
+
}
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
|
257
|
+
VALUE
|
258
|
+
rb_line_set_slide(VALUE self, VALUE l) {
|
259
|
+
GeoSet *set;
|
260
|
+
Line *line;
|
261
|
+
CHECK_LINE(l);
|
262
|
+
GEO_SET(self, set);
|
263
|
+
LINE(l, line);
|
264
|
+
line_set_slide(set, line, 3);
|
265
|
+
return line->p2->rbPoint;
|
266
|
+
}
|
267
|
+
|
268
|
+
static void
|
269
|
+
g_hash_table_intersection_free_endpoint(gpointer key, gpointer data, gpointer user_data) {
|
270
|
+
gpointer *arg_wrapper = (gpointer *) user_data;
|
271
|
+
|
272
|
+
gpointer *original_args = (gpointer *) arg_wrapper[0];
|
273
|
+
gdouble *max_distance = (gdouble *) original_args[1];
|
274
|
+
Line *tmpLine = (Line *) original_args[3];
|
275
|
+
|
276
|
+
*(tmpLine->p1) = *( (Point *) arg_wrapper[1] );
|
277
|
+
*(tmpLine->p2) = *( (Point *) key);
|
278
|
+
|
279
|
+
if (*max_distance < 0 || DISTANCE(tmpLine->p1, tmpLine->p2) < *max_distance) {
|
280
|
+
if (!line_set_intersects((GeoSet *) original_args[4], tmpLine)) {
|
281
|
+
rb_ary_push(*( (VALUE *) original_args[2] ), ( (Point *) key )->rbPoint);
|
282
|
+
}
|
283
|
+
}
|
284
|
+
}
|
285
|
+
|
286
|
+
static void
|
287
|
+
g_hash_table_intersection_free_endpoints(gpointer key, gpointer data, gpointer user_data) {
|
288
|
+
gpointer my_args[2];
|
289
|
+
gpointer *given_args = (gpointer *) user_data;
|
290
|
+
GeoSet *table = (GeoSet *) given_args[0];
|
291
|
+
my_args[0] = user_data;
|
292
|
+
my_args[1] = key;
|
293
|
+
g_hash_table_foreach(table->table, g_hash_table_intersection_free_endpoint, (gpointer) my_args);
|
294
|
+
}
|
295
|
+
|
296
|
+
static VALUE
|
297
|
+
line_set_intersection_free_endpoints(GeoSet *set, GeoSet *origins, GeoSet *endpoints, gdouble max_distance) {
|
298
|
+
VALUE rval = rb_ary_new();
|
299
|
+
Line *line = new_line_with_coordinates(0,0,0,0);
|
300
|
+
gpointer args[5] = { endpoints, &max_distance, &rval, line, set };
|
301
|
+
g_hash_table_foreach(origins->table, g_hash_table_intersection_free_endpoints, args);
|
302
|
+
free(line->p1);
|
303
|
+
free(line->p2);
|
304
|
+
free(line);
|
305
|
+
return rval;
|
306
|
+
}
|
307
|
+
|
308
|
+
static gpointer
|
309
|
+
line_set_count_intersections(GeoSet *set, gpointer segment_line_gp, gpointer user_data) {
|
310
|
+
gpointer *args = (gpointer *) user_data;
|
311
|
+
Line *segment_line = (Line *) segment_line_gp;
|
312
|
+
gint *rval = (gint *) args[0];
|
313
|
+
Line *line = (Line *) args[1];
|
314
|
+
Intersection *intersection = line_intersection(line, segment_line);
|
315
|
+
if (intersection != NULL) {
|
316
|
+
free_intersection(intersection);
|
317
|
+
(*rval)++;
|
318
|
+
}
|
319
|
+
return NULL;
|
320
|
+
}
|
321
|
+
|
322
|
+
static gint
|
323
|
+
line_set_n_intersections_with_line(GeoSet *set, Line *line) {
|
324
|
+
gint rval = 0;
|
325
|
+
gpointer args[2] = { &rval, line };
|
326
|
+
LINE_SET_REINDEX(set);
|
327
|
+
geo_set_each_structure_having_common_segment_id_with_line_until(set, line, line_set_count_intersections, args);
|
328
|
+
return rval;
|
329
|
+
}
|
330
|
+
|
331
|
+
VALUE
|
332
|
+
rb_line_set_n_intersections(VALUE self, VALUE line) {
|
333
|
+
GeoSet *me;
|
334
|
+
Line *l;
|
335
|
+
CHECK_LINE(line);
|
336
|
+
GEO_SET(self, me);
|
337
|
+
LINE(line, l);
|
338
|
+
return INT2NUM(line_set_n_intersections_with_line(me, l));
|
339
|
+
}
|
340
|
+
|
341
|
+
static void
|
342
|
+
g_hash_table_each_intersection(gpointer key, gpointer value, gpointer user_data) {
|
343
|
+
gpointer *args = (gpointer *) user_data;
|
344
|
+
Intersection *intersection = line_intersection((Line *) key, (Line *) args[0]);
|
345
|
+
if (intersection != NULL) {
|
346
|
+
((GFunc) args[1])(intersection, args[2]);
|
347
|
+
free_intersection(intersection);
|
348
|
+
}
|
349
|
+
}
|
350
|
+
|
351
|
+
void
|
352
|
+
line_set_each_intersection(GeoSet *set, Line *line, GFunc func, gpointer user_data) {
|
353
|
+
gpointer args[3] = { line, func, user_data };
|
354
|
+
g_hash_table_foreach(set->table, g_hash_table_each_intersection, args);
|
355
|
+
}
|
356
|
+
|
357
|
+
static gpointer
|
358
|
+
line_set_collect_intersections_in_rb_ary(GeoSet *set, gpointer segment_line_gp, gpointer user_data) {
|
359
|
+
gpointer *args = (gpointer *) user_data;
|
360
|
+
Line *segment_line = (Line *) segment_line_gp;
|
361
|
+
Line *line = (Line *) args[0];
|
362
|
+
VALUE *rval = (VALUE *) args[1];
|
363
|
+
Intersection *intersection = line_intersection(segment_line, line);
|
364
|
+
if (intersection != NULL) {
|
365
|
+
rb_ary_push(*rval, rb_ary_new3(2, rb_intersection(intersection, line->p1), intersection->with->rbLine));
|
366
|
+
}
|
367
|
+
return NULL;
|
368
|
+
}
|
369
|
+
|
370
|
+
static VALUE
|
371
|
+
line_set_intersections(GeoSet *set, Line *line) {
|
372
|
+
VALUE rval = rb_ary_new();
|
373
|
+
gpointer args[2] = { line, &rval };
|
374
|
+
LINE_SET_REINDEX(set);
|
375
|
+
geo_set_each_structure_having_common_segment_id_with_line_until(set, line, line_set_collect_intersections_in_rb_ary, args);
|
376
|
+
return rval;
|
377
|
+
}
|
378
|
+
|
379
|
+
VALUE
|
380
|
+
rb_line_set_intersections(VALUE self, VALUE l) {
|
381
|
+
GeoSet *me;
|
382
|
+
Line *line;
|
383
|
+
CHECK_LINE(l);
|
384
|
+
GEO_SET(self, me);
|
385
|
+
LINE(l, line);
|
386
|
+
return line_set_intersections(me, line);
|
387
|
+
}
|
388
|
+
|
389
|
+
VALUE
|
390
|
+
rb_line_set_intersection_free_endpoints(int argc, VALUE *argv, VALUE self) {
|
391
|
+
GeoSet *me;
|
392
|
+
GeoSet *ops;
|
393
|
+
GeoSet *eps;
|
394
|
+
VALUE origins;
|
395
|
+
VALUE endpoints;
|
396
|
+
VALUE rval;
|
397
|
+
gdouble max_distance;
|
398
|
+
gboolean created_ops = FALSE;
|
399
|
+
if (argc == 2) {
|
400
|
+
origins = argv[0];
|
401
|
+
endpoints = argv[1];
|
402
|
+
max_distance = -1;
|
403
|
+
} else if (argc == 3) {
|
404
|
+
origins = argv[0];
|
405
|
+
endpoints = argv[1];
|
406
|
+
max_distance = NUM2DBL(argv[2]);
|
407
|
+
} else {
|
408
|
+
rb_raise(rb_eTypeError, "Arguments to %s#intersection_free_endpoints have to be (Geo::Point | Geo::PointSet, Geo::PointSet [, Number])");
|
409
|
+
}
|
410
|
+
if (POINT_P(origins)) {
|
411
|
+
ops = new_geo_set();
|
412
|
+
Point *p;
|
413
|
+
POINT(origins, p);
|
414
|
+
geo_set_insert(ops, p);
|
415
|
+
created_ops = TRUE;
|
416
|
+
} else if (POINT_SET_P(origins)) {
|
417
|
+
GEO_SET(origins, ops);
|
418
|
+
} else {
|
419
|
+
rb_raise(rb_eTypeError, "Arguments to %s#intersection_free_endpoints have to be (Geo::Point | Geo::PointSet, Geo::PointSet [, Number])");
|
420
|
+
}
|
421
|
+
CHECK_POINT_SET(endpoints);
|
422
|
+
GEO_SET(self, me);
|
423
|
+
GEO_SET(endpoints, eps);
|
424
|
+
rval = line_set_intersection_free_endpoints(me, ops, eps, max_distance);
|
425
|
+
if (created_ops)
|
426
|
+
geo_set_free(ops);
|
427
|
+
return rval;
|
428
|
+
}
|
429
|
+
|
430
|
+
VALUE
|
431
|
+
rb_line_set_closest_intersection(VALUE self, VALUE l) {
|
432
|
+
GeoSet *set;
|
433
|
+
Line *line;
|
434
|
+
Line *with;
|
435
|
+
Intersection *intersection;
|
436
|
+
VALUE rval;
|
437
|
+
CHECK_LINE(l);
|
438
|
+
GEO_SET(self, set);
|
439
|
+
LINE(l, line);
|
440
|
+
if ((intersection = line_set_closest_intersection(set, line)) != NULL) {
|
441
|
+
with = intersection->with;
|
442
|
+
rval = rb_ary_new3(2, rb_intersection(intersection, line->p1), with->rbLine);
|
443
|
+
} else {
|
444
|
+
rval = Qnil;
|
445
|
+
}
|
446
|
+
return rval;
|
447
|
+
}
|
448
|
+
|
449
|
+
VALUE
|
450
|
+
rb_line_set_clone(VALUE self) {
|
451
|
+
GeoSet *me;
|
452
|
+
GEO_SET(self, me);
|
453
|
+
return RB_LINE_SET(geo_set_clone(me), CLASS(self));
|
454
|
+
}
|
455
|
+
|
456
|
+
VALUE
|
457
|
+
rb_line_set_structures_in_segment(VALUE self, VALUE x, VALUE y) {
|
458
|
+
return rb_geo_set_structures_in_segment(self, x, y, rb_line_from_gpointer);
|
459
|
+
}
|
460
|
+
|
461
|
+
void
|
462
|
+
g_hash_table_clone_line_value_into_rb_value(gpointer key, gpointer value, gpointer user_data) {
|
463
|
+
Line *l = (Line *) value;
|
464
|
+
Line *new_line = new_line_with_coordinates(l->p1->x, l->p1->y, l->p2->x, l->p2->y);
|
465
|
+
RB_POINT(new_line->p1, rb_point);
|
466
|
+
RB_POINT(new_line->p2, rb_point);
|
467
|
+
rb_ary_push(*( (VALUE *) user_data ), RB_LINE(new_line, rb_line));
|
468
|
+
}
|
469
|
+
|
470
|
+
/*
|
471
|
+
* Below is the hell you have to go through to get the segment index to work or line sets.
|
472
|
+
*/
|
473
|
+
|
474
|
+
static void
|
475
|
+
g_hash_table_call_handle_bounds(gpointer key, gpointer value, gpointer user_data) {
|
476
|
+
Point bottom_left;
|
477
|
+
Point top_right;
|
478
|
+
Line *line = (Line *) key;
|
479
|
+
LINE_BOUNDS(line, &bottom_left, &top_right);
|
480
|
+
geo_set_handle_bounds((GeoSet *) user_data, &bottom_left, &top_right);
|
481
|
+
}
|
482
|
+
|
483
|
+
static void
|
484
|
+
line_set_bound_finder(GeoSet *set) {
|
485
|
+
g_hash_table_foreach(set->table, g_hash_table_call_handle_bounds, set);
|
486
|
+
}
|
487
|
+
|
488
|
+
gpointer
|
489
|
+
geo_set_each_segment_id_of_intersection_until(GeoSet *set, Line *segment_line, gpointer user_data) {
|
490
|
+
gpointer *args = (gpointer *) user_data;
|
491
|
+
Line *line = (Line *) args[0];
|
492
|
+
geo_set_segment_id_handler handler = (geo_set_segment_id_handler) args[1];
|
493
|
+
gpointer handler_args = args[2];
|
494
|
+
GHashTable *seen_segment_ids = args[3];
|
495
|
+
Intersection *intersection;
|
496
|
+
gpointer rval = NULL;
|
497
|
+
if ((intersection = line_intersection(line, segment_line)) != NULL) {
|
498
|
+
if (intersection->type == itPOINT) {
|
499
|
+
rval = geo_set_each_segment_id_for_point_until(set, intersection->point, seen_segment_ids, handler, handler_args);
|
500
|
+
}
|
501
|
+
free_intersection(intersection);
|
502
|
+
}
|
503
|
+
return rval;
|
504
|
+
}
|
505
|
+
|
506
|
+
/*
|
507
|
+
* Will call handler with set, segment id and user_data for each segment id of line in set.
|
508
|
+
*/
|
509
|
+
gpointer
|
510
|
+
geo_set_each_segment_id_of_line_until(GeoSet *set, Line *line, geo_set_segment_id_handler handler, gpointer user_data) {
|
511
|
+
Point bottom_left;
|
512
|
+
Point top_right;
|
513
|
+
GHashTable *seen_segment_ids = g_hash_table_new(g_direct_hash, g_direct_equal);
|
514
|
+
gpointer args[4] = { line, handler, user_data, seen_segment_ids };
|
515
|
+
gpointer rval = NULL;
|
516
|
+
LINE_BOUNDS(line, &bottom_left, &top_right);
|
517
|
+
rval = geo_set_each_segment_line_until(set, &bottom_left, &top_right, geo_set_each_segment_id_of_intersection_until, args);
|
518
|
+
if (rval == NULL)
|
519
|
+
rval = geo_set_each_segment_id_for_point_until(set, line->p1, seen_segment_ids, handler, user_data);
|
520
|
+
if (rval == NULL)
|
521
|
+
rval = geo_set_each_segment_id_for_point_until(set, line->p2, seen_segment_ids, handler, user_data);
|
522
|
+
g_hash_table_destroy(seen_segment_ids);
|
523
|
+
return rval;
|
524
|
+
}
|
525
|
+
|
526
|
+
gpointer
|
527
|
+
geo_set_each_structure_having_common_segment_id_with_line_until(GeoSet *set,
|
528
|
+
Line *line,
|
529
|
+
geo_set_structure_handler handler,
|
530
|
+
gpointer user_data) {
|
531
|
+
gpointer rval = NULL;
|
532
|
+
GHashTable *seen_structures = g_hash_table_new(g_direct_hash, g_direct_equal);
|
533
|
+
gpointer args[3] = { seen_structures, handler, user_data };
|
534
|
+
rval = geo_set_each_segment_id_of_line_until(set, line, geo_set_each_structure_in_segment_id_until, args);
|
535
|
+
g_hash_table_destroy(seen_structures);
|
536
|
+
return rval;
|
537
|
+
}
|
538
|
+
|
539
|
+
VALUE
|
540
|
+
rb_line_set_lines_with_common_segment_id(VALUE self, VALUE l) {
|
541
|
+
GeoSet *me;
|
542
|
+
Line *line;
|
543
|
+
VALUE rval;
|
544
|
+
gpointer args[2];
|
545
|
+
CHECK_LINE(l);
|
546
|
+
GEO_SET(self, me);
|
547
|
+
LINE(l, line);
|
548
|
+
LINE_SET_REINDEX(me);
|
549
|
+
rval = rb_ary_new();
|
550
|
+
args[0] = &rval;
|
551
|
+
args[1] = rb_line_from_gpointer;
|
552
|
+
geo_set_each_structure_having_common_segment_id_with_line_until(me, line, geo_set_insert_structure_into_rb_ary, args);
|
553
|
+
return rval;
|
554
|
+
}
|
555
|
+
|
556
|
+
static void
|
557
|
+
g_hash_table_each_segment_id_for_line(gpointer key, gpointer value, gpointer user_data) {
|
558
|
+
gpointer *args = (gpointer *) user_data;
|
559
|
+
geo_set_each_segment_id_of_line_until((GeoSet *) args[0],
|
560
|
+
(Line *) key,
|
561
|
+
(geo_set_segment_id_handler) args[1],
|
562
|
+
(Line *) key);
|
563
|
+
}
|
564
|
+
|
565
|
+
static void
|
566
|
+
line_set_segment_finder(GeoSet *set) {
|
567
|
+
gpointer args[2] = { set, geo_set_insert_into_segment };
|
568
|
+
g_hash_table_foreach(set->table, g_hash_table_each_segment_id_for_line, args);
|
569
|
+
}
|
570
|
+
|
571
|
+
static gboolean
|
572
|
+
line_set_reindex(GeoSet *line_set) {
|
573
|
+
return geo_set_reindex(line_set, line_set_bound_finder, line_set_segment_finder);
|
574
|
+
}
|
575
|
+
|
576
|
+
VALUE
|
577
|
+
rb_line_set_segment_ids_for_line(VALUE self, VALUE l) {
|
578
|
+
GeoSet *me;
|
579
|
+
Line *line;
|
580
|
+
VALUE rval;
|
581
|
+
CHECK_LINE(l);
|
582
|
+
GEO_SET(self, me);
|
583
|
+
LINE(l, line);
|
584
|
+
LINE_SET_REINDEX(me);
|
585
|
+
rval = rb_ary_new();
|
586
|
+
geo_set_each_segment_id_of_line_until(me, line, geo_set_insert_segment_id_into_rb_ary, &rval);
|
587
|
+
return rval;
|
588
|
+
}
|
589
|
+
|
590
|
+
VALUE
|
591
|
+
rb_line_set_reindex(VALUE self) {
|
592
|
+
GeoSet *me;
|
593
|
+
GEO_SET(self, me);
|
594
|
+
return GBOOL2RB(line_set_reindex(me));
|
595
|
+
}
|
596
|
+
|
data/ext/line_set.h
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
|
2
|
+
#ifndef LINE_SET_H
|
3
|
+
#define LINE_SET_H
|
4
|
+
|
5
|
+
#include "common.h"
|
6
|
+
|
7
|
+
extern VALUE rb_line_set;
|
8
|
+
|
9
|
+
#define RB_LINE_SET(line_set_pointer,klass) Data_Wrap_Struct(klass, line_set_mark, geo_set_free, (line_set_pointer))
|
10
|
+
#define LINE_SET_REINDEX(set) if (set->index_dirty) line_set_reindex(set)
|
11
|
+
|
12
|
+
typedef gboolean
|
13
|
+
(*line_set_line_handler)(GeoSet *set, Line *line, gpointer user_data);
|
14
|
+
|
15
|
+
void
|
16
|
+
line_set_mark(GeoSet *set);
|
17
|
+
|
18
|
+
VALUE
|
19
|
+
rb_line_set_alloc(VALUE class);
|
20
|
+
|
21
|
+
VALUE
|
22
|
+
rb_line_set_insert(VALUE self, VALUE line);
|
23
|
+
|
24
|
+
VALUE
|
25
|
+
rb_line_set_include(VALUE self, VALUE line);
|
26
|
+
|
27
|
+
VALUE
|
28
|
+
rb_line_set_delete(VALUE self, VALUE line);
|
29
|
+
|
30
|
+
VALUE
|
31
|
+
rb_line_set_each(VALUE self);
|
32
|
+
|
33
|
+
VALUE
|
34
|
+
rb_line_set_intersects(VALUE self, VALUE l);
|
35
|
+
|
36
|
+
VALUE
|
37
|
+
rb_line_set_slide(VALUE self, VALUE l);
|
38
|
+
|
39
|
+
VALUE
|
40
|
+
rb_line_set_intersections(VALUE self, VALUE l);
|
41
|
+
|
42
|
+
VALUE
|
43
|
+
rb_line_set_intersection_free_endpoints(int argc, VALUE *argv, VALUE self);
|
44
|
+
|
45
|
+
VALUE
|
46
|
+
rb_line_set_closest_intersection(VALUE self, VALUE l);
|
47
|
+
|
48
|
+
VALUE
|
49
|
+
rb_line_set_clone(VALUE self);
|
50
|
+
|
51
|
+
VALUE
|
52
|
+
rb_line_set_n_intersections(VALUE self, VALUE line);
|
53
|
+
|
54
|
+
VALUE
|
55
|
+
rb_line_set_reindex(VALUE self);
|
56
|
+
|
57
|
+
void
|
58
|
+
g_hash_table_clone_line_value_into_rb_value(gpointer key, gpointer value, gpointer user_data);
|
59
|
+
|
60
|
+
VALUE
|
61
|
+
rb_line_set_segment_lines(VALUE self, VALUE line);
|
62
|
+
|
63
|
+
void
|
64
|
+
line_set_each_intersection(GeoSet *set, Line *line, GFunc func, gpointer user_data);
|
65
|
+
|
66
|
+
VALUE
|
67
|
+
rb_line_set_structures_in_segment(VALUE self, VALUE x, VALUE y);
|
68
|
+
|
69
|
+
VALUE
|
70
|
+
rb_line_set_segment_ids_for_line(VALUE self, VALUE l);
|
71
|
+
|
72
|
+
VALUE
|
73
|
+
rb_line_set_lines_with_common_segment_id(VALUE self, VALUE l);
|
74
|
+
|
75
|
+
gpointer
|
76
|
+
geo_set_each_segment_id_of_intersection_until(GeoSet *set, Line *segment_line, gpointer user_data);
|
77
|
+
|
78
|
+
gpointer
|
79
|
+
geo_set_each_segment_id_of_line_until(GeoSet *set, Line *line, geo_set_segment_id_handler handler, gpointer user_data);
|
80
|
+
|
81
|
+
gpointer
|
82
|
+
geo_set_each_structure_having_common_segment_id_with_line_until(GeoSet *set,
|
83
|
+
Line *line,
|
84
|
+
geo_set_structure_handler handler,
|
85
|
+
gpointer user_data);
|
86
|
+
|
87
|
+
#endif
|