abscondment-rubyvor 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,369 @@
1
+ #include <ruby.h>
2
+ #include <vdefs.h>
3
+ #include <ruby_vor_c.h>
4
+ #include <stdio.h>
5
+ #include <stdlib.h>
6
+
7
+ static Site * nextone(void);
8
+ static int scomp(const void *, const void *);
9
+
10
+ /*
11
+ * See ruby_vor.c for RDOC
12
+ */
13
+
14
+
15
+ /*
16
+ * Class methods for RubyVor::VDDT::Computation
17
+ */
18
+
19
+ VALUE
20
+ RubyVor_from_points(VALUE self, VALUE pointsArray)
21
+ {
22
+ VALUE * inPtr, newComp;
23
+ ID x, y;
24
+
25
+ long i, inSize;
26
+
27
+ /* Require T_ARRAY */
28
+ Check_Type(pointsArray, T_ARRAY);
29
+
30
+ /* Intern our point access methods */
31
+ x = rb_intern("x");
32
+ y = rb_intern("y");
33
+
34
+
35
+ /* Require nonzero size and x & y methods on each array object */
36
+ if (RARRAY(pointsArray)->len < 1)
37
+ rb_raise(rb_eRuntimeError, "input points array must have a nonzero length");
38
+ for (i = 0; i < RARRAY(pointsArray)->len; i++) {
39
+ if(!rb_respond_to(RARRAY(pointsArray)->ptr[i], x) || !rb_respond_to(RARRAY(pointsArray)->ptr[i], y))
40
+ rb_raise(rb_eRuntimeError, "members of points array must respond to 'x' and 'y'");
41
+ }
42
+
43
+ /* Load up point count & points pointer. */
44
+ pointsArray = rb_funcall(pointsArray, rb_intern("uniq"), 0);
45
+ inSize = RARRAY(pointsArray)->len;
46
+ inPtr = RARRAY(pointsArray)->ptr;
47
+
48
+
49
+ /* Initialize rubyvorState */
50
+ initialize_state(0 /* debug? */);
51
+ debug_memory();
52
+
53
+ /* Create our return object. */
54
+ newComp = rb_funcall(self, rb_intern("new"), 0);
55
+ rb_iv_set(newComp, "@points", pointsArray);
56
+
57
+ /* Store it in rubyvorState so we can populate its values. */
58
+ rubyvorState.comp = (void *) &newComp;
59
+
60
+ /*
61
+ * Read in the sites, sort, and compute xmin, xmax, ymin, ymax
62
+ *
63
+ * TODO: refactor this block into a separate method for clarity?
64
+ */
65
+ {
66
+ /* Allocate memory for N sites... */
67
+ rubyvorState.sites = (Site *) myalloc(inSize * sizeof(Site));
68
+
69
+
70
+ /* Iterate over the arrays, doubling the incoming values. */
71
+ for (i=0; i<inSize; i++)
72
+ {
73
+ rubyvorState.sites[rubyvorState.nsites].coord.x = NUM2DBL(rb_funcall(inPtr[i], x, 0));
74
+ rubyvorState.sites[rubyvorState.nsites].coord.y = NUM2DBL(rb_funcall(inPtr[i], y, 0));
75
+
76
+ rubyvorState.sites[rubyvorState.nsites].sitenbr = rubyvorState.nsites;
77
+ rubyvorState.sites[rubyvorState.nsites++].refcnt = 0;
78
+
79
+ /* Allocate for N more if we just hit a multiple of N... */
80
+ if (rubyvorState.nsites % inSize == 0)
81
+ {
82
+ rubyvorState.sites = (Site *)myrealloc(rubyvorState.sites,(rubyvorState.nsites+inSize)*sizeof(Site),rubyvorState.nsites*sizeof(Site));
83
+ }
84
+ }
85
+
86
+ /* Sort the Sites */
87
+ qsort((void *)rubyvorState.sites, rubyvorState.nsites, sizeof(Site), scomp) ;
88
+
89
+ /* Pull the minimum values. */
90
+ rubyvorState.xmin = rubyvorState.sites[0].coord.x;
91
+ rubyvorState.xmax = rubyvorState.sites[0].coord.x;
92
+ for (i=1; i < rubyvorState.nsites; ++i)
93
+ {
94
+ if (rubyvorState.sites[i].coord.x < rubyvorState.xmin)
95
+ {
96
+ rubyvorState.xmin = rubyvorState.sites[i].coord.x;
97
+ }
98
+ if (rubyvorState.sites[i].coord.x > rubyvorState.xmax)
99
+ {
100
+ rubyvorState.xmax = rubyvorState.sites[i].coord.x;
101
+ }
102
+ }
103
+ rubyvorState.ymin = rubyvorState.sites[0].coord.y;
104
+ rubyvorState.ymax = rubyvorState.sites[rubyvorState.nsites-1].coord.y;
105
+
106
+ }
107
+
108
+
109
+ /* Perform the computation */
110
+ voronoi(nextone);
111
+ debug_memory();
112
+
113
+ /* Get rid of our comp reference */
114
+ rubyvorState.comp = (void *)NULL;
115
+
116
+ /* Free our allocated objects */
117
+ free_all();
118
+ debug_memory();
119
+
120
+ return newComp;
121
+ }
122
+
123
+
124
+ /*
125
+ * Instance methods
126
+ */
127
+
128
+ VALUE
129
+ RubyVor_nn_graph(VALUE self)
130
+ {
131
+ VALUE dtRaw, graph, points, * dtPtr, * tripletPtr, * graphPtr;
132
+ long i, j, noNeighborResponse;
133
+
134
+ graph = rb_iv_get(self, "@nn_graph");
135
+
136
+ if (RTEST(graph))
137
+ return graph;
138
+
139
+ /* Figure out our "no neighbor" response value */
140
+ if (SYM2ID(rb_iv_get(self, "@no_neighbor_response")) == rb_intern("raise")) {
141
+ noNeighborResponse = 1;
142
+ } else if (SYM2ID(rb_iv_get(self, "@no_neighbor_response")) == rb_intern("use_all")) {
143
+ noNeighborResponse = 2;
144
+ } else {
145
+ noNeighborResponse = 0;
146
+ }
147
+
148
+ /* Create an array of same size as points for the graph */
149
+ points = rb_iv_get(self, "@points");
150
+
151
+ graph = rb_ary_new2(RARRAY(points)->len);
152
+ for (i = 0; i < RARRAY(points)->len; i++)
153
+ rb_ary_push(graph, rb_ary_new());
154
+
155
+ /* Get our pointer into this array. */
156
+ graphPtr = RARRAY(graph)->ptr;
157
+
158
+ /* Iterate over the triangulation */
159
+ dtRaw = rb_iv_get(self, "@delaunay_triangulation_raw");
160
+ dtPtr = RARRAY(dtRaw)->ptr;
161
+ for (i = 0; i < RARRAY(dtRaw)->len; i++) {
162
+ tripletPtr = RARRAY(dtPtr[i])->ptr;
163
+
164
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[0])], tripletPtr[1]);
165
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[1])], tripletPtr[0]);
166
+
167
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[0])], tripletPtr[2]);
168
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[2])], tripletPtr[0]);
169
+
170
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[1])], tripletPtr[2]);
171
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[2])], tripletPtr[1]);
172
+
173
+ }
174
+ for (i = 0; i < RARRAY(graph)->len; i++) {
175
+ if (RARRAY(graphPtr[i])->len < 1) {
176
+ /* Evaluate noNeighborResponse and respond accordingly */
177
+ if (noNeighborResponse == 1) {
178
+ rb_raise(rb_eIndexError, "index of 0 (no neighbors) at %i", i);
179
+ } else if (noNeighborResponse == 2) {
180
+ /* No valid triangles touched this node -- include *all* possible neighbors
181
+ *
182
+ * Note that this can make for exceptionally slow (ie O(n^2) time) clustering,
183
+ * but should only happen in rare cases.
184
+ */
185
+ for(j = 0; j < RARRAY(points)->len; j++) {
186
+ if (j != i) {
187
+ rb_ary_push(graphPtr[i], INT2FIX(j));
188
+ if (RARRAY(graphPtr[j])->len > 0 && !rb_ary_includes(graphPtr[j], INT2FIX(i)))
189
+ rb_ary_push(graphPtr[j], INT2FIX(i));
190
+ }
191
+ }
192
+ }
193
+ } else {
194
+ rb_funcall(graphPtr[i], rb_intern("uniq!"), 0);
195
+ }
196
+ }
197
+
198
+ rb_iv_set(self, "@nn_graph", graph);
199
+
200
+ return graph;
201
+ }
202
+
203
+
204
+ VALUE
205
+ RubyVor_minimum_spanning_tree(int argc, VALUE *argv, VALUE self)
206
+ {
207
+ VALUE mst, dist_proc, nodes, nnGraph, points, queue, tmp, adjacent, adjacentData, adjacentDistance, current, currentData, floatMax;
208
+ ID i_call, i_push, i_pop, i_has_key;
209
+ long i;
210
+
211
+ /* 0 mandatory, 1 optional */
212
+ rb_scan_args(argc, argv, "01", &dist_proc);
213
+
214
+ mst = rb_iv_get(self, "@mst");
215
+
216
+ if (RTEST(mst))
217
+ return mst;
218
+
219
+
220
+ if (NIL_P(dist_proc)) {
221
+ /* Use our default Proc */
222
+ dist_proc = rb_eval_string("lambda{|a,b| a.distance_from(b)}");
223
+ } else if (rb_class_of(dist_proc) != rb_cProc) {
224
+ /* Blow up if we have a non-nil, non-Proc */
225
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", rb_obj_classname(dist_proc), rb_class2name(rb_cProc));
226
+ }
227
+
228
+ // Set up interned values
229
+ i_call = rb_intern("call");
230
+ i_push = rb_intern("push");
231
+ i_pop = rb_intern("pop");
232
+ i_has_key = rb_intern("has_key?");
233
+
234
+ points = rb_iv_get(self, "@points");
235
+ queue = rb_eval_string("RubyVor::PriorityQueue.new");
236
+ nnGraph = RubyVor_nn_graph(self);
237
+ floatMax= rb_iv_get(rb_cFloat, "MAX");
238
+
239
+ for (i = 0; i < RARRAY(points)->len; i++) {
240
+ tmp = rb_ary_new2(5);
241
+ /* 0: node index */
242
+ rb_ary_push(tmp, LONG2FIX(i));
243
+ /* 1: parent */
244
+ rb_ary_push(tmp, Qnil);
245
+ /* 2: adjacency_list */
246
+ rb_ary_push(tmp, rb_obj_clone(RARRAY(nnGraph)->ptr[i]));
247
+ /* 3: min_adjacency_list */
248
+ rb_ary_push(tmp, rb_ary_new());
249
+ /* 4: in_q */
250
+ rb_ary_push(tmp, Qtrue);
251
+
252
+ rb_funcall(queue, i_push, 2, tmp, (i == 0) ? rb_float_new(0.0) : floatMax);
253
+ }
254
+ nodes = rb_obj_clone(rb_iv_get(queue, "@data"));
255
+
256
+ while(RTEST(current = rb_funcall(queue, i_pop, 0))) {
257
+ currentData = rb_iv_get(current, "@data");
258
+
259
+ /* mark in_q */
260
+ rb_ary_store(currentData, 4, Qfalse);
261
+
262
+ /* check for presence of parent */
263
+ if (RTEST(RARRAY(currentData)->ptr[1])) {
264
+ /* push this node into adjacency_list of parent */
265
+ rb_ary_push(RARRAY(rb_iv_get(RARRAY(currentData)->ptr[1], "@data"))->ptr[3], current);
266
+ /* push parent into adjacency_list of this node */
267
+ rb_ary_push(RARRAY(currentData)->ptr[3], RARRAY(currentData)->ptr[1]);
268
+ }
269
+
270
+ for (i = 0; i < RARRAY(RARRAY(currentData)->ptr[2])->len; i++) {
271
+ /* grab indexed node */
272
+ adjacent = RARRAY(nodes)->ptr[FIX2LONG(RARRAY(RARRAY(currentData)->ptr[2])->ptr[i])];
273
+ adjacentData = rb_iv_get(adjacent, "@data");
274
+
275
+ /* check in_q -- only look at new nodes */
276
+ if (Qtrue == RARRAY(adjacentData)->ptr[4]) {
277
+
278
+ /* compare points by node -- adjacent against current */
279
+ adjacentDistance = rb_funcall(dist_proc, i_call, 2,
280
+ RARRAY(points)->ptr[FIX2LONG(RARRAY(currentData)->ptr[0])],
281
+ RARRAY(points)->ptr[FIX2LONG(RARRAY(adjacentData)->ptr[0])]);
282
+
283
+ /* If the new distance is better than our current priority, exchange them. */
284
+ if (RFLOAT(adjacentDistance)->value < RFLOAT(rb_iv_get(adjacent, "@priority"))->value) {
285
+ /* set new :parent */
286
+ rb_ary_store(adjacentData, 1, current);
287
+ /* update priority */
288
+ rb_iv_set(adjacent, "@priority", adjacentDistance);
289
+ /* percolate up into correctn position */
290
+ RubyVor_percolate_up(queue, rb_iv_get(adjacent, "@index"));
291
+ }
292
+ }
293
+ }
294
+ }
295
+
296
+ mst = rb_hash_new();
297
+ for (i = 0; i < RARRAY(nodes)->len; i++) {
298
+ current = RARRAY(nodes)->ptr[i];
299
+ currentData = rb_iv_get(current, "@data");
300
+ if (!NIL_P(RARRAY(currentData)->ptr[1])) {
301
+ adjacentData = rb_iv_get(RARRAY(currentData)->ptr[1], "@data");
302
+ tmp = rb_ary_new2(2);
303
+ if (FIX2LONG(RARRAY(currentData)->ptr[0]) < FIX2LONG(RARRAY(adjacentData)->ptr[0])) {
304
+ rb_ary_push(tmp, RARRAY(currentData)->ptr[0]);
305
+ rb_ary_push(tmp, RARRAY(adjacentData)->ptr[0]);
306
+ } else {
307
+ rb_ary_push(tmp, RARRAY(adjacentData)->ptr[0]);
308
+ rb_ary_push(tmp, RARRAY(currentData)->ptr[0]);
309
+ }
310
+
311
+ /* if (!st_lookup(RHASH(mst)->tbl, tmp, 0)) { */
312
+ rb_hash_aset(mst, tmp, rb_iv_get(current, "@priority"));
313
+ /* } */
314
+ }
315
+ }
316
+
317
+ rb_iv_set(self, "@mst", mst);
318
+
319
+ return mst;
320
+ }
321
+
322
+
323
+ /*
324
+ * Static C helper methods
325
+ */
326
+
327
+
328
+ /*** sort sites on y, then x, coord ***/
329
+ static int
330
+ scomp(const void * vs1, const void * vs2)
331
+ {
332
+ Point * s1 = (Point *)vs1 ;
333
+ Point * s2 = (Point *)vs2 ;
334
+
335
+ if (s1->y < s2->y)
336
+ {
337
+ return (-1) ;
338
+ }
339
+ if (s1->y > s2->y)
340
+ {
341
+ return (1) ;
342
+ }
343
+ if (s1->x < s2->x)
344
+ {
345
+ return (-1) ;
346
+ }
347
+ if (s1->x > s2->x)
348
+ {
349
+ return (1) ;
350
+ }
351
+ return (0) ;
352
+ }
353
+
354
+ /*** return a single in-storage site ***/
355
+ static Site *
356
+ nextone(void)
357
+ {
358
+ Site * s ;
359
+
360
+ if (rubyvorState.siteidx < rubyvorState.nsites)
361
+ {
362
+ s = &rubyvorState.sites[rubyvorState.siteidx++];
363
+ return (s) ;
364
+ }
365
+ else
366
+ {
367
+ return ((Site *)NULL) ;
368
+ }
369
+ }
@@ -0,0 +1,35 @@
1
+ #include <ruby.h>
2
+ #include <vdefs.h>
3
+ #include <ruby_vor_c.h>
4
+ #include <math.h>
5
+
6
+ /*
7
+ * Euclidean distance between two Points
8
+ */
9
+ VALUE
10
+ RubyVor_distance_from(VALUE self, VALUE other)
11
+ {
12
+ return rb_float_new(sqrt(pow(NUM2DBL(rb_iv_get(self, "@x")) - NUM2DBL(rb_iv_get(other, "@x")), 2.0) +
13
+ pow(NUM2DBL(rb_iv_get(self, "@y")) - NUM2DBL(rb_iv_get(other, "@y")), 2.0)));
14
+ }
15
+
16
+ VALUE
17
+ RubyVor_point_hash(VALUE self)
18
+ {
19
+ double x, y;
20
+ char *c;
21
+ long i, xHash, yHash;
22
+
23
+ x = NUM2DBL(rb_iv_get(self, "@x"));
24
+ y = NUM2DBL(rb_iv_get(self, "@y"));
25
+
26
+ /* Bastardized from Ruby's numeric.c */
27
+
28
+ for (c = (char*)&x, xHash = 0, i = 0; i < sizeof(double); i++) xHash += c[i] * 971;
29
+ for (c = (char*)&y, yHash = 0, i = 0; i < sizeof(double); i++) yHash += c[i] * 971;
30
+
31
+ xHash = xHash & RB_HASH_FILTER;
32
+ yHash = yHash & RB_HASH_FILTER;
33
+
34
+ return LONG2FIX((xHash << (RB_LONG_BITS / 2)) | yHash);
35
+ }
@@ -0,0 +1,121 @@
1
+ #include <ruby.h>
2
+ #include <vdefs.h>
3
+ #include <ruby_vor_c.h>
4
+
5
+ /*
6
+ * Instance methods for RubyVor::VDDT::PriorityQueue
7
+ */
8
+
9
+
10
+ static VALUE
11
+ compare(VALUE a, VALUE b)
12
+ {
13
+ double aD, bD;
14
+ ID minDistance;
15
+ minDistance = ID2SYM(rb_intern("min_distance"));
16
+
17
+ if (CLASS_OF(a) == rb_path2class("RubyVor::PriorityQueue::QueueItem"))
18
+ aD = NUM2DBL(rb_funcall(a, rb_intern("priority"), 0));
19
+ else
20
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", rb_obj_classname(a), rb_path2class("RubyVor::PriorityQueue::QueueItem"));
21
+
22
+ if (CLASS_OF(a) == rb_path2class("RubyVor::PriorityQueue::QueueItem"))
23
+
24
+ bD = NUM2DBL(rb_funcall(b, rb_intern("priority"), 0));
25
+ else
26
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", rb_obj_classname(a), rb_path2class("RubyVor::PriorityQueue::QueueItem"));
27
+
28
+ return RTEST(aD < bD);
29
+ }
30
+
31
+ VALUE
32
+ RubyVor_percolate_up(VALUE self, VALUE index)
33
+ {
34
+ VALUE item, data;
35
+ long i, j, size;
36
+
37
+ Check_Type(index, T_FIXNUM);
38
+
39
+ data = rb_iv_get(self, "@data");
40
+ Check_Type(data, T_ARRAY);
41
+
42
+ size = FIX2INT(rb_iv_get(self, "@size"));
43
+ i = FIX2INT(index) + 1;
44
+
45
+ if (i < 1 || i > size)
46
+ rb_raise(rb_eIndexError, "index %i out of range", i-1);
47
+
48
+ j = i / 2;
49
+
50
+ item = RARRAY(data)->ptr[i - 1];
51
+
52
+ while(j > 0 && compare(item, RARRAY(data)->ptr[j - 1]))
53
+ {
54
+ rb_ary_store(data, i-1, RARRAY(data)->ptr[j - 1]);
55
+ rb_funcall(RARRAY(data)->ptr[i-1], rb_intern("index="), 1, INT2FIX(i-1));
56
+ i = j;
57
+ j = j / 2;
58
+ }
59
+
60
+ rb_ary_store(data, i-1, item);
61
+ rb_funcall(RARRAY(data)->ptr[i-1], rb_intern("index="), 1, INT2FIX(i-1));
62
+
63
+ return Qnil;
64
+ }
65
+
66
+
67
+ VALUE
68
+ RubyVor_percolate_down(VALUE self, VALUE index)
69
+ {
70
+ VALUE item, data;
71
+ long i, j, k, size;
72
+
73
+ Check_Type(index, T_FIXNUM);
74
+
75
+ data = rb_iv_get(self, "@data");
76
+ Check_Type(data, T_ARRAY);
77
+
78
+ size = FIX2INT(rb_iv_get(self, "@size"));
79
+ i = FIX2INT(index) + 1;
80
+
81
+ if (i < 1 || i > size)
82
+ rb_raise(rb_eIndexError, "index %i out of range", i-1);
83
+
84
+ j = size / 2;
85
+
86
+ item = RARRAY(data)->ptr[i - 1];
87
+
88
+ while (!(i > j))
89
+ {
90
+ k = i * 2;
91
+ if (k < size && compare(RARRAY(data)->ptr[k], RARRAY(data)->ptr[k - 1]))
92
+ k++;
93
+
94
+ if (compare(item, RARRAY(data)->ptr[k - 1]))
95
+ j = -1;
96
+ else
97
+ {
98
+ rb_ary_store(data, i-1, RARRAY(data)->ptr[k - 1]);
99
+ rb_funcall(RARRAY(data)->ptr[i-1], rb_intern("index="), 1, INT2FIX(i-1));
100
+ i = k;
101
+ }
102
+ }
103
+
104
+ rb_ary_store(data, i-1, item);
105
+ rb_funcall(RARRAY(data)->ptr[i-1], rb_intern("index="), 1, INT2FIX(i-1));
106
+
107
+ return Qnil;
108
+ }
109
+
110
+
111
+ VALUE
112
+ RubyVor_heapify(VALUE self)
113
+ {
114
+ long i, size;
115
+ size = FIX2INT(rb_iv_get(self, "@size"));
116
+
117
+ for(i = size / 2; i >= 1; i--)
118
+ RubyVor_percolate_down(self, INT2FIX(i-1));
119
+
120
+ return Qnil;
121
+ }