abscondment-rubyvor 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.
@@ -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
+ }