rubyvor 0.1.3
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/History.rdoc +60 -0
- data/Manifest.txt +30 -0
- data/README.rdoc +76 -0
- data/Rakefile +41 -0
- data/ext/Doc +30 -0
- data/ext/edgelist.c +204 -0
- data/ext/extconf.rb +3 -0
- data/ext/geometry.c +219 -0
- data/ext/heap.c +118 -0
- data/ext/memory.c +118 -0
- data/ext/output.c +251 -0
- data/ext/rb_cComputation.c +369 -0
- data/ext/rb_cPoint.c +35 -0
- data/ext/rb_cPriorityQueue.c +120 -0
- data/ext/ruby_vor_c.c +115 -0
- data/ext/ruby_vor_c.h +40 -0
- data/ext/vdefs.h +150 -0
- data/ext/voronoi.c +271 -0
- data/lib/ruby_vor.rb +16 -0
- data/lib/ruby_vor/computation.rb +136 -0
- data/lib/ruby_vor/geo_ruby_extensions.rb +15 -0
- data/lib/ruby_vor/point.rb +32 -0
- data/lib/ruby_vor/priority_queue.rb +87 -0
- data/lib/ruby_vor/version.rb +3 -0
- data/lib/ruby_vor/visualizer.rb +218 -0
- data/rubyvor.gemspec +35 -0
- data/test/test_computation.rb +354 -0
- data/test/test_point.rb +100 -0
- data/test/test_priority_queue.rb +129 -0
- data/test/test_voronoi_interface.rb +161 -0
- metadata +99 -0
data/ext/output.c
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
|
2
|
+
/*** OUTPUT.C ***/
|
3
|
+
|
4
|
+
#include <vdefs.h>
|
5
|
+
#include <stdio.h>
|
6
|
+
#include <unistd.h>
|
7
|
+
|
8
|
+
static float pxmin, pxmax, pymin, pymax, cradius;
|
9
|
+
|
10
|
+
void openpl(void) {}
|
11
|
+
void line(float ax, float ay, float bx, float by) {}
|
12
|
+
void circle(float ax, float ay, float radius) {}
|
13
|
+
void range(float pxmin, float pxmax, float pymin, float pymax) {}
|
14
|
+
|
15
|
+
void
|
16
|
+
out_bisector(Edge * e)
|
17
|
+
{
|
18
|
+
/* Save line to our ruby object */
|
19
|
+
(*rubyvorState.storeL)(e->a, e->b, e->c);
|
20
|
+
|
21
|
+
|
22
|
+
/* printf("l %f %f %f\n", e->a, e->b, e->c); */
|
23
|
+
|
24
|
+
if (rubyvorState.plot)
|
25
|
+
line(e->reg[0]->coord.x, e->reg[0]->coord.y, e->reg[1]->coord.x, e->reg[1]->coord.y);
|
26
|
+
|
27
|
+
if (rubyvorState.debug)
|
28
|
+
printf("line(%d) %gx+%gy=%g, bisecting %d %d\n", e->edgenbr, e->a, e->b, e->c, e->reg[le]->sitenbr, e->reg[re]->sitenbr);
|
29
|
+
}
|
30
|
+
|
31
|
+
void
|
32
|
+
out_ep(Edge * e)
|
33
|
+
{
|
34
|
+
/* Save endpoint to our ruby object */
|
35
|
+
(*rubyvorState.storeE)(e->edgenbr,
|
36
|
+
e->ep[le] != (Site *)NULL ? e->ep[le]->sitenbr : -1,
|
37
|
+
e->ep[re] != (Site *)NULL ? e->ep[re]->sitenbr : -1);
|
38
|
+
|
39
|
+
/*
|
40
|
+
printf("e %d", e->edgenbr);
|
41
|
+
printf(" %d ", e->ep[le] != (Site *)NULL ? e->ep[le]->sitenbr : -1) ;
|
42
|
+
printf("%d\n", e->ep[re] != (Site *)NULL ? e->ep[re]->sitenbr : -1) ;
|
43
|
+
*/
|
44
|
+
|
45
|
+
if (rubyvorState.plot)
|
46
|
+
clip_line(e) ;
|
47
|
+
}
|
48
|
+
|
49
|
+
void
|
50
|
+
out_vertex(Site * v)
|
51
|
+
{
|
52
|
+
/* Save vertex to our ruby object */
|
53
|
+
(*rubyvorState.storeV)(v->coord.x, v->coord.y);
|
54
|
+
|
55
|
+
/* printf ("v %f %f\n", v->coord.x, v->coord.y) ; */
|
56
|
+
|
57
|
+
if (rubyvorState.debug)
|
58
|
+
printf("vertex(%d) at %f %f\n", v->sitenbr, v->coord.x, v->coord.y);
|
59
|
+
}
|
60
|
+
|
61
|
+
void
|
62
|
+
out_site(Site * s)
|
63
|
+
{
|
64
|
+
/* Save site to our ruby object */
|
65
|
+
(*rubyvorState.storeS)(s->coord.x, s->coord.y);
|
66
|
+
|
67
|
+
/* printf("s %f %f\n", s->coord.x, s->coord.y) ; */
|
68
|
+
|
69
|
+
if (rubyvorState.plot)
|
70
|
+
circle (s->coord.x, s->coord.y, cradius);
|
71
|
+
|
72
|
+
if (rubyvorState.debug)
|
73
|
+
printf("site (%d) at %f %f\n", s->sitenbr, s->coord.x, s->coord.y);
|
74
|
+
}
|
75
|
+
|
76
|
+
void
|
77
|
+
out_triple(Site * s1, Site * s2, Site * s3)
|
78
|
+
{
|
79
|
+
/* Save triplet to our ruby object */
|
80
|
+
(*rubyvorState.storeT)(s1->sitenbr, s2->sitenbr, s3->sitenbr);
|
81
|
+
|
82
|
+
if (rubyvorState.debug)
|
83
|
+
{
|
84
|
+
printf("%d %d %d\n", s1->sitenbr, s2->sitenbr, s3->sitenbr);
|
85
|
+
printf("circle through left=%d right=%d bottom=%d\n",
|
86
|
+
s1->sitenbr, s2->sitenbr, s3->sitenbr) ;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
void
|
91
|
+
plotinit(void)
|
92
|
+
{
|
93
|
+
float dx, dy, d ;
|
94
|
+
|
95
|
+
dy = rubyvorState.ymax - rubyvorState.ymin ;
|
96
|
+
dx = rubyvorState.xmax - rubyvorState.xmin ;
|
97
|
+
d = ( dx > dy ? dx : dy) * 1.1 ;
|
98
|
+
pxmin = rubyvorState.xmin - (d-dx) / 2.0 ;
|
99
|
+
pxmax = rubyvorState.xmax + (d-dx) / 2.0 ;
|
100
|
+
pymin = rubyvorState.ymin - (d-dy) / 2.0 ;
|
101
|
+
pymax = rubyvorState.ymax + (d-dy) / 2.0 ;
|
102
|
+
cradius = (pxmax - pxmin) / 350.0 ;
|
103
|
+
openpl() ;
|
104
|
+
range(pxmin, pymin, pxmax, pymax) ;
|
105
|
+
}
|
106
|
+
|
107
|
+
void
|
108
|
+
clip_line(Edge * e)
|
109
|
+
{
|
110
|
+
Site * s1, * s2 ;
|
111
|
+
float x1, x2, y1, y2 ;
|
112
|
+
|
113
|
+
if (e->a == 1.0 && e->b >= 0.0)
|
114
|
+
{
|
115
|
+
s1 = e->ep[1] ;
|
116
|
+
s2 = e->ep[0] ;
|
117
|
+
}
|
118
|
+
else
|
119
|
+
{
|
120
|
+
s1 = e->ep[0] ;
|
121
|
+
s2 = e->ep[1] ;
|
122
|
+
}
|
123
|
+
if (e->a == 1.0)
|
124
|
+
{
|
125
|
+
y1 = pymin ;
|
126
|
+
if (s1 != (Site *)NULL && s1->coord.y > pymin)
|
127
|
+
{
|
128
|
+
y1 = s1->coord.y ;
|
129
|
+
}
|
130
|
+
if (y1 > pymax)
|
131
|
+
{
|
132
|
+
return ;
|
133
|
+
}
|
134
|
+
x1 = e->c - e->b * y1 ;
|
135
|
+
y2 = pymax ;
|
136
|
+
if (s2 != (Site *)NULL && s2->coord.y < pymax)
|
137
|
+
{
|
138
|
+
y2 = s2->coord.y ;
|
139
|
+
}
|
140
|
+
if (y2 < pymin)
|
141
|
+
{
|
142
|
+
return ;
|
143
|
+
}
|
144
|
+
x2 = e->c - e->b * y2 ;
|
145
|
+
if (((x1 > pxmax) && (x2 > pxmax)) || ((x1 < pxmin) && (x2 < pxmin)))
|
146
|
+
{
|
147
|
+
return ;
|
148
|
+
}
|
149
|
+
if (x1 > pxmax)
|
150
|
+
{
|
151
|
+
x1 = pxmax ;
|
152
|
+
y1 = (e->c - x1) / e->b ;
|
153
|
+
}
|
154
|
+
if (x1 < pxmin)
|
155
|
+
{
|
156
|
+
x1 = pxmin ;
|
157
|
+
y1 = (e->c - x1) / e->b ;
|
158
|
+
}
|
159
|
+
if (x2 > pxmax)
|
160
|
+
{
|
161
|
+
x2 = pxmax ;
|
162
|
+
y2 = (e->c - x2) / e->b ;
|
163
|
+
}
|
164
|
+
if (x2 < pxmin)
|
165
|
+
{
|
166
|
+
x2 = pxmin ;
|
167
|
+
y2 = (e->c - x2) / e->b ;
|
168
|
+
}
|
169
|
+
}
|
170
|
+
else
|
171
|
+
{
|
172
|
+
x1 = pxmin ;
|
173
|
+
if (s1 != (Site *)NULL && s1->coord.x > pxmin)
|
174
|
+
{
|
175
|
+
x1 = s1->coord.x ;
|
176
|
+
}
|
177
|
+
if (x1 > pxmax)
|
178
|
+
{
|
179
|
+
return ;
|
180
|
+
}
|
181
|
+
y1 = e->c - e->a * x1 ;
|
182
|
+
x2 = pxmax ;
|
183
|
+
if (s2 != (Site *)NULL && s2->coord.x < pxmax)
|
184
|
+
{
|
185
|
+
x2 = s2->coord.x ;
|
186
|
+
}
|
187
|
+
if (x2 < pxmin)
|
188
|
+
{
|
189
|
+
return ;
|
190
|
+
}
|
191
|
+
y2 = e->c - e->a * x2 ;
|
192
|
+
if (((y1 > pymax) && (y2 > pymax)) || ((y1 < pymin) && (y2 <pymin)))
|
193
|
+
{
|
194
|
+
return ;
|
195
|
+
}
|
196
|
+
if (y1> pymax)
|
197
|
+
{
|
198
|
+
y1 = pymax ;
|
199
|
+
x1 = (e->c - y1) / e->a ;
|
200
|
+
}
|
201
|
+
if (y1 < pymin)
|
202
|
+
{
|
203
|
+
y1 = pymin ;
|
204
|
+
x1 = (e->c - y1) / e->a ;
|
205
|
+
}
|
206
|
+
if (y2 > pymax)
|
207
|
+
{
|
208
|
+
y2 = pymax ;
|
209
|
+
x2 = (e->c - y2) / e->a ;
|
210
|
+
}
|
211
|
+
if (y2 < pymin)
|
212
|
+
{
|
213
|
+
y2 = pymin ;
|
214
|
+
x2 = (e->c - y2) / e->a ;
|
215
|
+
}
|
216
|
+
}
|
217
|
+
line(x1,y1,x2,y2);
|
218
|
+
}
|
219
|
+
|
220
|
+
/* Linux-specific. */
|
221
|
+
void
|
222
|
+
debug_memory(void)
|
223
|
+
{
|
224
|
+
char buf[30];
|
225
|
+
FILE* pf;
|
226
|
+
int tmp;
|
227
|
+
float totalSize;
|
228
|
+
unsigned size;/* total program size */
|
229
|
+
|
230
|
+
/*
|
231
|
+
unsigned resident;// resident set size
|
232
|
+
unsigned share;// shared pages
|
233
|
+
unsigned text;// text (code)
|
234
|
+
unsigned lib;// library
|
235
|
+
unsigned data;// data/stack
|
236
|
+
unsigned dt;// dirty pages (unused in Linux 2.6)
|
237
|
+
*/
|
238
|
+
|
239
|
+
if (!rubyvorState.debug)
|
240
|
+
return;
|
241
|
+
|
242
|
+
snprintf(buf, 30, "/proc/%u/statm", (unsigned)getpid());
|
243
|
+
pf = fopen(buf, "r");
|
244
|
+
if (NULL != pf)
|
245
|
+
{
|
246
|
+
tmp = fscanf(pf, "%u", &size); /*, %u, %u ... etc &resident, &share, &text, &lib, &data); */
|
247
|
+
totalSize = (float)size / 1024.0;
|
248
|
+
fprintf(stderr, "%f ", totalSize);
|
249
|
+
}
|
250
|
+
fclose(pf);
|
251
|
+
}
|
@@ -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_LEN(pointsArray) < 1)
|
37
|
+
rb_raise(rb_eRuntimeError, "input points array must have a nonzero length");
|
38
|
+
for (i = 0; i < RARRAY_LEN(pointsArray); i++) {
|
39
|
+
if(!rb_respond_to(RARRAY_PTR(pointsArray)[i], x) || !rb_respond_to(RARRAY_PTR(pointsArray)[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_LEN(pointsArray);
|
46
|
+
inPtr = RARRAY_PTR(pointsArray);
|
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_LEN(points));
|
152
|
+
for (i = 0; i < RARRAY_LEN(points); i++)
|
153
|
+
rb_ary_push(graph, rb_ary_new());
|
154
|
+
|
155
|
+
/* Get our pointer into this array. */
|
156
|
+
graphPtr = RARRAY_PTR(graph);
|
157
|
+
|
158
|
+
/* Iterate over the triangulation */
|
159
|
+
dtRaw = rb_iv_get(self, "@delaunay_triangulation_raw");
|
160
|
+
dtPtr = RARRAY_PTR(dtRaw);
|
161
|
+
for (i = 0; i < RARRAY_LEN(dtRaw); i++) {
|
162
|
+
tripletPtr = RARRAY_PTR(dtPtr[i]);
|
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_LEN(graph); i++) {
|
175
|
+
if (RARRAY_LEN(graphPtr[i]) < 1) {
|
176
|
+
/* Evaluate noNeighborResponse and respond accordingly */
|
177
|
+
if (noNeighborResponse == 1) {
|
178
|
+
rb_raise(rb_eIndexError, "index of 0 (no neighbors) at %li", 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_LEN(points); j++) {
|
186
|
+
if (j != i) {
|
187
|
+
rb_ary_push(graphPtr[i], INT2FIX(j));
|
188
|
+
if (RARRAY_LEN(graphPtr[j]) > 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_LEN(points); 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_PTR(nnGraph)[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_PTR(currentData)[1])) {
|
264
|
+
/* push this node into adjacency_list of parent */
|
265
|
+
rb_ary_push(RARRAY_PTR(rb_iv_get(RARRAY_PTR(currentData)[1], "@data"))[3], current);
|
266
|
+
/* push parent into adjacency_list of this node */
|
267
|
+
rb_ary_push(RARRAY_PTR(currentData)[3], RARRAY_PTR(currentData)[1]);
|
268
|
+
}
|
269
|
+
|
270
|
+
for (i = 0; i < RARRAY_LEN(RARRAY_PTR(currentData)[2]); i++) {
|
271
|
+
/* grab indexed node */
|
272
|
+
adjacent = RARRAY_PTR(nodes)[FIX2LONG(RARRAY_PTR(RARRAY_PTR(currentData)[2])[i])];
|
273
|
+
adjacentData = rb_iv_get(adjacent, "@data");
|
274
|
+
|
275
|
+
/* check in_q -- only look at new nodes */
|
276
|
+
if (Qtrue == RARRAY_PTR(adjacentData)[4]) {
|
277
|
+
|
278
|
+
/* compare points by node -- adjacent against current */
|
279
|
+
adjacentDistance = rb_funcall(dist_proc, i_call, 2,
|
280
|
+
RARRAY_PTR(points)[FIX2LONG(RARRAY_PTR(currentData)[0])],
|
281
|
+
RARRAY_PTR(points)[FIX2LONG(RARRAY_PTR(adjacentData)[0])]);
|
282
|
+
|
283
|
+
/* If the new distance is better than our current priority, exchange them. */
|
284
|
+
if (RFLOAT_VALUE(adjacentDistance) < RFLOAT_VALUE(rb_iv_get(adjacent, "@priority"))) {
|
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_LEN(nodes); i++) {
|
298
|
+
current = RARRAY_PTR(nodes)[i];
|
299
|
+
currentData = rb_iv_get(current, "@data");
|
300
|
+
if (!NIL_P(RARRAY_PTR(currentData)[1])) {
|
301
|
+
adjacentData = rb_iv_get(RARRAY_PTR(currentData)[1], "@data");
|
302
|
+
tmp = rb_ary_new2(2);
|
303
|
+
if (FIX2LONG(RARRAY_PTR(currentData)[0]) < FIX2LONG(RARRAY_PTR(adjacentData)[0])) {
|
304
|
+
rb_ary_push(tmp, RARRAY_PTR(currentData)[0]);
|
305
|
+
rb_ary_push(tmp, RARRAY_PTR(adjacentData)[0]);
|
306
|
+
} else {
|
307
|
+
rb_ary_push(tmp, RARRAY_PTR(adjacentData)[0]);
|
308
|
+
rb_ary_push(tmp, RARRAY_PTR(currentData)[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
|
+
}
|