bribera-rubyvor 0.0.7 → 0.0.8
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.txt +6 -0
- data/Manifest.txt +1 -0
- data/README.txt +32 -1
- data/ext/memory.c +5 -3
- data/ext/output.c +15 -19
- data/ext/rb_cComputation.c +145 -90
- data/ext/rb_cPoint.c +21 -0
- data/ext/rb_cPriorityQueue.c +3 -3
- data/ext/ruby_vor_c.c +20 -13
- data/ext/ruby_vor_c.h +10 -1
- data/ext/voronoi.c +24 -24
- data/lib/ruby_vor/computation.rb +35 -105
- data/lib/ruby_vor/point.rb +19 -4
- data/lib/ruby_vor/version.rb +1 -1
- data/lib/ruby_vor/visualizer.rb +217 -0
- data/lib/ruby_vor.rb +1 -0
- data/rubyvor.gemspec +4 -4
- data/test/test_computation.rb +42 -17
- metadata +4 -3
data/History.txt
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
=== 0.0.8 / 2008-12-23
|
2
|
+
|
3
|
+
* Several C warning fixes (more ISO C compliance unused variables, etc)
|
4
|
+
* Moved minimum_spanning_tree computation into C, yielding large speed gain.
|
5
|
+
* Basic SVG visualization.
|
6
|
+
|
1
7
|
=== 0.0.7 / 2008-12-12
|
2
8
|
|
3
9
|
* Bugfix: there are cases where performing a Delaunay triangulation on a set of points will not yield a complete nearest neighbor graph. This causes computation of the minimum spanning tree and clustering to fail for these nodes (they always appear as disconnected from the main graph).
|
data/Manifest.txt
CHANGED
data/README.txt
CHANGED
@@ -12,7 +12,38 @@ in turn be used for proximity-based clustering of the input points.
|
|
12
12
|
|
13
13
|
== Usage:
|
14
14
|
|
15
|
-
|
15
|
+
require 'lib/ruby_vor'
|
16
|
+
require 'pp'
|
17
|
+
|
18
|
+
points = [
|
19
|
+
RubyVor::Point.new(120, 290),
|
20
|
+
RubyVor::Point.new(110, 120),
|
21
|
+
RubyVor::Point.new(160, 90.2),
|
22
|
+
RubyVor::Point.new(3.14159265, 3.14159265)
|
23
|
+
]
|
24
|
+
|
25
|
+
# Compute the diagram & triangulation
|
26
|
+
comp = RubyVor::VDDT::Computation.from_points(points)
|
27
|
+
|
28
|
+
puts "The nearest-neighbor graph:"
|
29
|
+
pp comp.nn_graph
|
30
|
+
|
31
|
+
puts "\nThe minimum-spanning tree:"
|
32
|
+
pp comp.minimum_spanning_tree
|
33
|
+
|
34
|
+
#
|
35
|
+
# Visualize these things as SVGs
|
36
|
+
#
|
37
|
+
|
38
|
+
# Just the triangulation
|
39
|
+
RubyVor::Visualizer.make_svg(comp, :name => 'tri.svg')
|
40
|
+
|
41
|
+
# Just the MST
|
42
|
+
RubyVor::Visualizer.make_svg(comp, :name => 'mst.svg', :triangulation => false, :mst => true)
|
43
|
+
|
44
|
+
# Voronoi diagram and the triangulation
|
45
|
+
RubyVor::Visualizer.make_svg(comp, :name => 'dia.svg', :voronoi_diagram => true)
|
46
|
+
|
16
47
|
|
17
48
|
== LICENSE:
|
18
49
|
|
data/ext/memory.c
CHANGED
@@ -86,9 +86,11 @@ myrealloc(void * oldp, unsigned n, unsigned oldn)
|
|
86
86
|
|
87
87
|
update_memory_map(newp);
|
88
88
|
|
89
|
-
|
90
|
-
|
91
|
-
|
89
|
+
/*
|
90
|
+
* Mark oldp as freed, since free() was called by realloc.
|
91
|
+
*
|
92
|
+
* TODO: this seems naive; measure if this is a bottleneck & use a hash table or some other scheme if it is.
|
93
|
+
*/
|
92
94
|
for (i=0; i<nallocs; i++)
|
93
95
|
{
|
94
96
|
if (memory_map[i] != (char*)0 && memory_map[i] == oldp)
|
data/ext/output.c
CHANGED
@@ -17,11 +17,11 @@ void range(float pxmin, float pxmax, float pymin, float pymax) {}
|
|
17
17
|
void
|
18
18
|
out_bisector(Edge * e)
|
19
19
|
{
|
20
|
-
|
20
|
+
/* Save line to our ruby object */
|
21
21
|
(*rubyvorState.storeL)(e->a, e->b, e->c);
|
22
22
|
|
23
23
|
|
24
|
-
|
24
|
+
/* printf("l %f %f %f\n", e->a, e->b, e->c); */
|
25
25
|
|
26
26
|
if (rubyvorState.plot)
|
27
27
|
line(e->reg[0]->coord.x, e->reg[0]->coord.y, e->reg[1]->coord.x, e->reg[1]->coord.y);
|
@@ -33,7 +33,7 @@ out_bisector(Edge * e)
|
|
33
33
|
void
|
34
34
|
out_ep(Edge * e)
|
35
35
|
{
|
36
|
-
|
36
|
+
/* Save endpoint to our ruby object */
|
37
37
|
(*rubyvorState.storeE)(e->edgenbr,
|
38
38
|
e->ep[le] != (Site *)NULL ? e->ep[le]->sitenbr : -1,
|
39
39
|
e->ep[re] != (Site *)NULL ? e->ep[re]->sitenbr : -1);
|
@@ -51,10 +51,10 @@ out_ep(Edge * e)
|
|
51
51
|
void
|
52
52
|
out_vertex(Site * v)
|
53
53
|
{
|
54
|
-
|
54
|
+
/* Save vertex to our ruby object */
|
55
55
|
(*rubyvorState.storeV)(v->coord.x, v->coord.y);
|
56
56
|
|
57
|
-
|
57
|
+
/* printf ("v %f %f\n", v->coord.x, v->coord.y) ; */
|
58
58
|
|
59
59
|
if (rubyvorState.debug)
|
60
60
|
printf("vertex(%d) at %f %f\n", v->sitenbr, v->coord.x, v->coord.y);
|
@@ -63,10 +63,10 @@ out_vertex(Site * v)
|
|
63
63
|
void
|
64
64
|
out_site(Site * s)
|
65
65
|
{
|
66
|
-
|
66
|
+
/* Save site to our ruby object */
|
67
67
|
(*rubyvorState.storeS)(s->coord.x, s->coord.y);
|
68
68
|
|
69
|
-
|
69
|
+
/* printf("s %f %f\n", s->coord.x, s->coord.y) ; */
|
70
70
|
|
71
71
|
if (rubyvorState.plot)
|
72
72
|
circle (s->coord.x, s->coord.y, cradius);
|
@@ -78,7 +78,7 @@ out_site(Site * s)
|
|
78
78
|
void
|
79
79
|
out_triple(Site * s1, Site * s2, Site * s3)
|
80
80
|
{
|
81
|
-
|
81
|
+
/* Save triplet to our ruby object */
|
82
82
|
(*rubyvorState.storeT)(s1->sitenbr, s2->sitenbr, s3->sitenbr);
|
83
83
|
|
84
84
|
if (rubyvorState.debug)
|
@@ -222,14 +222,13 @@ clip_line(Edge * e)
|
|
222
222
|
/* Linux-specific. */
|
223
223
|
void
|
224
224
|
debug_memory(void)
|
225
|
-
{
|
226
|
-
if (!rubyvorState.debug)
|
227
|
-
return;
|
228
|
-
|
225
|
+
{
|
229
226
|
char buf[30];
|
230
227
|
FILE* pf;
|
228
|
+
int tmp;
|
229
|
+
float totalSize;
|
230
|
+
unsigned size;/* total program size */
|
231
231
|
|
232
|
-
unsigned size;// total program size
|
233
232
|
/*
|
234
233
|
unsigned resident;// resident set size
|
235
234
|
unsigned share;// shared pages
|
@@ -238,18 +237,15 @@ debug_memory(void)
|
|
238
237
|
unsigned data;// data/stack
|
239
238
|
unsigned dt;// dirty pages (unused in Linux 2.6)
|
240
239
|
*/
|
241
|
-
|
242
|
-
int retVal;
|
243
240
|
|
244
|
-
|
241
|
+
if (!rubyvorState.debug)
|
242
|
+
return;
|
245
243
|
|
246
244
|
snprintf(buf, 30, "/proc/%u/statm", (unsigned)getpid());
|
247
245
|
pf = fopen(buf, "r");
|
248
246
|
if (NULL != pf)
|
249
247
|
{
|
250
|
-
|
251
|
-
//, %u, %u ... etc &resident, &share, &text, &lib, &data);
|
252
|
-
|
248
|
+
tmp = fscanf(pf, "%u", &size); /*, %u, %u ... etc &resident, &share, &text, &lib, &data); */
|
253
249
|
totalSize = (float)size / 1024.0;
|
254
250
|
fprintf(stderr, "%f ", totalSize);
|
255
251
|
}
|
data/ext/rb_cComputation.c
CHANGED
@@ -7,83 +7,86 @@
|
|
7
7
|
static Site * nextone(void);
|
8
8
|
static int scomp(const void *, const void *);
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
/*
|
11
|
+
* See ruby_vor.c for RDOC
|
12
|
+
*/
|
13
13
|
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
/*
|
16
|
+
* Class methods for RubyVor::VDDT::Computation
|
17
|
+
*/
|
18
18
|
|
19
19
|
VALUE
|
20
20
|
RubyVor_from_points(VALUE self, VALUE pointsArray)
|
21
21
|
{
|
22
|
-
//VALUE returnValue;
|
23
22
|
VALUE * inPtr, newComp;
|
24
23
|
ID x, y;
|
25
24
|
|
26
25
|
long i, inSize;
|
27
26
|
|
28
|
-
|
27
|
+
/* Require T_ARRAY */
|
29
28
|
Check_Type(pointsArray, T_ARRAY);
|
30
29
|
|
31
|
-
|
30
|
+
/* Intern our point access methods */
|
32
31
|
x = rb_intern("x");
|
33
32
|
y = rb_intern("y");
|
34
33
|
|
35
|
-
// Load up point count & points pointer.
|
36
|
-
inSize = RARRAY(pointsArray)->len;
|
37
|
-
inPtr = RARRAY(pointsArray)->ptr;
|
38
34
|
|
39
|
-
|
40
|
-
if (
|
35
|
+
/* Require nonzero size and x & y methods on each array object */
|
36
|
+
if (RARRAY(pointsArray)->len < 1)
|
41
37
|
rb_raise(rb_eRuntimeError, "points array have a nonzero length");
|
42
|
-
for (i = 0; i <
|
43
|
-
if(!rb_respond_to(
|
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))
|
44
40
|
rb_raise(rb_eRuntimeError, "members of points array must respond to 'x' and 'y'");
|
45
41
|
}
|
46
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
|
+
|
47
48
|
|
48
|
-
|
49
|
-
initialize_state(/* debug? */
|
49
|
+
/* Initialize rubyvorState */
|
50
|
+
initialize_state(0 /* debug? */);
|
50
51
|
debug_memory();
|
51
52
|
|
52
|
-
|
53
|
-
newComp = rb_funcall(self, rb_intern("new"),
|
54
|
-
|
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. */
|
55
58
|
rubyvorState.comp = (void *) &newComp;
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
+
*/
|
61
65
|
{
|
62
|
-
|
63
|
-
rubyvorState.sites = (Site *) myalloc(
|
66
|
+
/* Allocate memory for N sites... */
|
67
|
+
rubyvorState.sites = (Site *) myalloc(inSize * sizeof(Site));
|
64
68
|
|
65
69
|
|
66
|
-
|
70
|
+
/* Iterate over the arrays, doubling the incoming values. */
|
67
71
|
for (i=0; i<inSize; i++)
|
68
72
|
{
|
69
73
|
rubyvorState.sites[rubyvorState.nsites].coord.x = NUM2DBL(rb_funcall(inPtr[i], x, 0));
|
70
74
|
rubyvorState.sites[rubyvorState.nsites].coord.y = NUM2DBL(rb_funcall(inPtr[i], y, 0));
|
71
|
-
|
72
|
-
//
|
75
|
+
|
73
76
|
rubyvorState.sites[rubyvorState.nsites].sitenbr = rubyvorState.nsites;
|
74
77
|
rubyvorState.sites[rubyvorState.nsites++].refcnt = 0;
|
75
78
|
|
76
|
-
|
77
|
-
if (rubyvorState.nsites %
|
79
|
+
/* Allocate for N more if we just hit a multiple of N... */
|
80
|
+
if (rubyvorState.nsites % inSize == 0)
|
78
81
|
{
|
79
|
-
rubyvorState.sites = (Site *)myrealloc(rubyvorState.sites,(rubyvorState.nsites+
|
82
|
+
rubyvorState.sites = (Site *)myrealloc(rubyvorState.sites,(rubyvorState.nsites+inSize)*sizeof(Site),rubyvorState.nsites*sizeof(Site));
|
80
83
|
}
|
81
84
|
}
|
82
85
|
|
83
|
-
|
86
|
+
/* Sort the Sites */
|
84
87
|
qsort((void *)rubyvorState.sites, rubyvorState.nsites, sizeof(Site), scomp) ;
|
85
88
|
|
86
|
-
|
89
|
+
/* Pull the minimum values. */
|
87
90
|
rubyvorState.xmin = rubyvorState.sites[0].coord.x;
|
88
91
|
rubyvorState.xmax = rubyvorState.sites[0].coord.x;
|
89
92
|
for (i=1; i < rubyvorState.nsites; ++i)
|
@@ -103,14 +106,14 @@ RubyVor_from_points(VALUE self, VALUE pointsArray)
|
|
103
106
|
}
|
104
107
|
|
105
108
|
|
106
|
-
|
109
|
+
/* Perform the computation */
|
107
110
|
voronoi(nextone);
|
108
111
|
debug_memory();
|
109
112
|
|
110
|
-
|
113
|
+
/* Get rid of our comp reference */
|
111
114
|
rubyvorState.comp = (void *)NULL;
|
112
115
|
|
113
|
-
|
116
|
+
/* Free our allocated objects */
|
114
117
|
free_all();
|
115
118
|
debug_memory();
|
116
119
|
|
@@ -118,78 +121,127 @@ RubyVor_from_points(VALUE self, VALUE pointsArray)
|
|
118
121
|
}
|
119
122
|
|
120
123
|
|
121
|
-
//
|
122
|
-
// Instance methods
|
123
|
-
//
|
124
|
-
|
125
124
|
/*
|
125
|
+
* Instance methods
|
126
|
+
*/
|
127
|
+
|
126
128
|
VALUE
|
127
129
|
RubyVor_minimum_spanning_tree(int argc, VALUE *argv, VALUE self)
|
128
130
|
{
|
129
|
-
VALUE dist_proc, nodes, nnGraph, points, queue,
|
130
|
-
long i
|
131
|
+
VALUE mst, dist_proc, nodes, nnGraph, points, queue, tmp, adjacent, adjacentData, adjacentDistance, latestAddition, latestAdditionData, floatMax;
|
132
|
+
long i;
|
131
133
|
|
132
|
-
|
134
|
+
/* 0 mandatory, 1 optional */
|
133
135
|
rb_scan_args(argc, argv, "01", &dist_proc);
|
134
136
|
|
137
|
+
mst = rb_iv_get(self, "@mst");
|
138
|
+
|
139
|
+
if (RTEST(mst))
|
140
|
+
return mst;
|
141
|
+
|
142
|
+
|
135
143
|
if (NIL_P(dist_proc)) {
|
136
|
-
|
144
|
+
/* Use our default Proc */
|
137
145
|
dist_proc = rb_eval_string("lambda{|a,b| a.distance_from(b)}");
|
138
146
|
} else if (rb_class_of(dist_proc) != rb_cProc) {
|
139
|
-
|
147
|
+
/* Blow up if we have a non-nil, non-Proc */
|
140
148
|
rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", rb_obj_classname(dist_proc), rb_class2name(rb_cProc));
|
141
149
|
}
|
142
150
|
|
143
151
|
points = rb_iv_get(self, "@points");
|
144
|
-
|
145
|
-
queue = rb_eval_string("RubyVor::PriorityQueue.new(lambda{|a,b| a[:min_distance] < b[:min_distance]})");
|
152
|
+
queue = rb_eval_string("RubyVor::PriorityQueue.new");
|
146
153
|
nnGraph = RubyVor_nn_graph(self);
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
rb_funcall(queue, rb_intern("push"), 1, tmpHash);
|
154
|
+
floatMax= rb_iv_get(rb_cFloat, "MAX");
|
155
|
+
|
156
|
+
for (i = 0; i < RARRAY(points)->len; i++) {
|
157
|
+
tmp = rb_ary_new2(5);
|
158
|
+
/* 0: node index */
|
159
|
+
rb_ary_push(tmp, LONG2FIX(i));
|
160
|
+
/* 1: parent */
|
161
|
+
rb_ary_push(tmp, Qnil);
|
162
|
+
/* 2: adjacency_list */
|
163
|
+
rb_ary_push(tmp, rb_obj_clone(RARRAY(nnGraph)->ptr[i]));
|
164
|
+
/* 3: min_adjacency_list */
|
165
|
+
rb_ary_push(tmp, rb_ary_new());
|
166
|
+
/* 4: in_q */
|
167
|
+
rb_ary_push(tmp, Qtrue);
|
168
|
+
|
169
|
+
rb_funcall(queue, rb_intern("push"), 2, tmp, (i == 0) ? rb_float_new(0.0) : floatMax);
|
165
170
|
}
|
171
|
+
nodes = rb_obj_clone(rb_funcall(queue, rb_intern("data"), 0));
|
172
|
+
|
173
|
+
while(RTEST(latestAddition = rb_funcall(queue, rb_intern("pop"), 0))) {
|
174
|
+
latestAdditionData = rb_funcall(latestAddition, rb_intern("data"), 0);
|
175
|
+
|
176
|
+
/* mark in_q */
|
177
|
+
rb_ary_store(latestAdditionData, 4, Qfalse);
|
178
|
+
|
179
|
+
/* check for presence of parent */
|
180
|
+
if (RTEST(RARRAY(latestAdditionData)->ptr[1])) {
|
181
|
+
/* push this node into adjacency_list of parent */
|
182
|
+
rb_ary_push(RARRAY(rb_funcall(RARRAY(latestAdditionData)->ptr[1], rb_intern("data"), 0))->ptr[3], latestAddition);
|
183
|
+
/* push parent into adjacency_list of this node */
|
184
|
+
rb_ary_push(RARRAY(latestAdditionData)->ptr[3], RARRAY(latestAdditionData)->ptr[1]);
|
185
|
+
}
|
166
186
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
187
|
+
for (i = 0; i < RARRAY(RARRAY(latestAdditionData)->ptr[2])->len; i++) {
|
188
|
+
/* grab indexed node */
|
189
|
+
adjacent = RARRAY(nodes)->ptr[FIX2LONG(RARRAY(RARRAY(latestAdditionData)->ptr[2])->ptr[i])];
|
190
|
+
adjacentData = rb_funcall(adjacent, rb_intern("data"), 0);
|
171
191
|
|
172
|
-
|
173
|
-
|
192
|
+
/* check in_q -- only look at new nodes */
|
193
|
+
if (Qtrue == RARRAY(adjacentData)->ptr[4]) {
|
194
|
+
|
195
|
+
/* compare points by node -- adjacent against current */
|
196
|
+
adjacentDistance = rb_funcall(dist_proc,
|
197
|
+
rb_intern("call"), 2,
|
198
|
+
RARRAY(points)->ptr[FIX2LONG(RARRAY(latestAdditionData)->ptr[0])],
|
199
|
+
RARRAY(points)->ptr[FIX2LONG(RARRAY(adjacentData)->ptr[0])]);
|
200
|
+
|
201
|
+
/* If the new distance is better than our current priority, exchange them. */
|
202
|
+
if (RFLOAT(adjacentDistance)->value < RFLOAT(rb_funcall(adjacent, rb_intern("priority"), 0))->value) {
|
203
|
+
/* set new :parent */
|
204
|
+
rb_ary_store(adjacentData, 1, latestAddition);
|
205
|
+
/* update priority */
|
206
|
+
rb_funcall(adjacent, rb_intern("priority="), 1, adjacentDistance);
|
207
|
+
/* percolate up into correctn position */
|
208
|
+
RubyVor_percolate_up(queue, rb_funcall(adjacent, rb_intern("index"), 0));
|
209
|
+
}
|
210
|
+
}
|
211
|
+
}
|
174
212
|
}
|
175
213
|
|
176
|
-
|
177
|
-
|
178
|
-
|
214
|
+
mst = rb_hash_new();
|
215
|
+
for (i = 0; i < RARRAY(nodes)->len; i++) {
|
216
|
+
latestAddition = RARRAY(nodes)->ptr[i];
|
217
|
+
latestAdditionData = rb_funcall(latestAddition, rb_intern("data"), 0);
|
218
|
+
if (!NIL_P(RARRAY(latestAdditionData)->ptr[1])) {
|
219
|
+
adjacentData = rb_funcall(RARRAY(latestAdditionData)->ptr[1], rb_intern("data"), 0);
|
220
|
+
tmp = rb_ary_new2(2);
|
221
|
+
if (FIX2LONG(RARRAY(latestAdditionData)->ptr[0]) < FIX2LONG(RARRAY(adjacentData)->ptr[0])) {
|
222
|
+
rb_ary_push(tmp, RARRAY(latestAdditionData)->ptr[0]);
|
223
|
+
rb_ary_push(tmp, RARRAY(adjacentData)->ptr[0]);
|
224
|
+
} else {
|
225
|
+
rb_ary_push(tmp, RARRAY(adjacentData)->ptr[0]);
|
226
|
+
rb_ary_push(tmp, RARRAY(latestAdditionData)->ptr[0]);
|
227
|
+
}
|
228
|
+
if (!RTEST(rb_funcall(mst, rb_intern("has_key?"), 1, tmp))) {
|
229
|
+
rb_hash_aset(mst, tmp, rb_funcall(latestAddition, rb_intern("priority"), 0));
|
179
230
|
|
180
|
-
|
231
|
+
}
|
181
232
|
}
|
182
|
-
break;
|
183
233
|
}
|
234
|
+
|
235
|
+
rb_iv_set(self, "@mst", mst);
|
184
236
|
|
185
|
-
return
|
237
|
+
return mst;
|
186
238
|
}
|
187
|
-
|
239
|
+
|
188
240
|
|
189
241
|
VALUE
|
190
242
|
RubyVor_nn_graph(VALUE self)
|
191
243
|
{
|
192
|
-
long i
|
244
|
+
long i;
|
193
245
|
VALUE dtRaw, graph, points, * dtPtr, * tripletPtr, * graphPtr;
|
194
246
|
|
195
247
|
graph = rb_iv_get(self, "@nn_graph");
|
@@ -197,16 +249,17 @@ RubyVor_nn_graph(VALUE self)
|
|
197
249
|
if (RTEST(graph))
|
198
250
|
return graph;
|
199
251
|
|
200
|
-
|
252
|
+
/* Create an array of same size as points for the graph */
|
201
253
|
points = rb_iv_get(self, "@points");
|
254
|
+
|
202
255
|
graph = rb_ary_new2(RARRAY(points)->len);
|
203
256
|
for (i = 0; i < RARRAY(points)->len; i++)
|
204
257
|
rb_ary_push(graph, rb_ary_new());
|
205
258
|
|
206
|
-
|
259
|
+
/* Get our pointer into this array. */
|
207
260
|
graphPtr = RARRAY(graph)->ptr;
|
208
261
|
|
209
|
-
|
262
|
+
/* Iterate over the triangulation */
|
210
263
|
dtRaw = rb_iv_get(self, "@delaunay_triangulation_raw");
|
211
264
|
dtPtr = RARRAY(dtRaw)->ptr;
|
212
265
|
for (i = 0; i < RARRAY(dtRaw)->len; i++) {
|
@@ -220,11 +273,12 @@ RubyVor_nn_graph(VALUE self)
|
|
220
273
|
|
221
274
|
rb_ary_push(graphPtr[FIX2INT(tripletPtr[1])], tripletPtr[2]);
|
222
275
|
rb_ary_push(graphPtr[FIX2INT(tripletPtr[2])], tripletPtr[1]);
|
276
|
+
|
223
277
|
}
|
224
|
-
|
225
278
|
for (i = 0; i < RARRAY(graph)->len; i++) {
|
226
279
|
if (RARRAY(graphPtr[i])->len < 1) {
|
227
|
-
|
280
|
+
rb_raise(rb_eIndexError, "index of 0 (no neighbors) at %i", i);
|
281
|
+
/* No valid triangles touched this node -- include *all* possible neighbors
|
228
282
|
for(j = 0; j < RARRAY(points)->len; j++) {
|
229
283
|
if (j != i) {
|
230
284
|
rb_ary_push(graphPtr[i], INT2FIX(j));
|
@@ -232,6 +286,7 @@ RubyVor_nn_graph(VALUE self)
|
|
232
286
|
rb_ary_push(graphPtr[j], INT2FIX(i));
|
233
287
|
}
|
234
288
|
}
|
289
|
+
*/
|
235
290
|
} else {
|
236
291
|
rb_funcall(graphPtr[i], rb_intern("uniq!"), 0);
|
237
292
|
}
|
@@ -244,9 +299,9 @@ RubyVor_nn_graph(VALUE self)
|
|
244
299
|
|
245
300
|
|
246
301
|
|
247
|
-
|
248
|
-
|
249
|
-
|
302
|
+
/*
|
303
|
+
* Static C helper methods
|
304
|
+
*/
|
250
305
|
|
251
306
|
|
252
307
|
/*** sort sites on y, then x, coord ***/
|
data/ext/rb_cPoint.c
CHANGED
@@ -12,3 +12,24 @@ RubyVor_distance_from(VALUE self, VALUE other)
|
|
12
12
|
return rb_float_new(sqrt(pow(NUM2DBL(rb_iv_get(self, "@x")) - NUM2DBL(rb_iv_get(other, "@x")), 2.0) +
|
13
13
|
pow(NUM2DBL(rb_iv_get(self, "@y")) - NUM2DBL(rb_iv_get(other, "@y")), 2.0)));
|
14
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
|
+
}
|
data/ext/rb_cPriorityQueue.c
CHANGED
data/ext/ruby_vor_c.c
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
#include <vdefs.h>
|
3
3
|
#include <ruby_vor_c.h>
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
/*
|
6
|
+
* Extension initialization
|
7
|
+
*/
|
8
8
|
void
|
9
9
|
Init_ruby_vor_c(void)
|
10
10
|
{
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
/*
|
12
|
+
* Set up our Modules and Class.
|
13
|
+
*/
|
14
14
|
|
15
15
|
/*
|
16
16
|
* Main RubyVor namespace.
|
@@ -30,7 +30,7 @@ Init_ruby_vor_c(void)
|
|
30
30
|
RubyVor_rb_cComputation = rb_define_class_under(RubyVor_rb_mVDDT, "Computation", rb_cObject);
|
31
31
|
rb_define_singleton_method(RubyVor_rb_cComputation, "from_points", RubyVor_from_points, 1);
|
32
32
|
rb_define_method(RubyVor_rb_cComputation, "nn_graph", RubyVor_nn_graph, 0);
|
33
|
-
|
33
|
+
rb_define_method(RubyVor_rb_cComputation, "minimum_spanning_tree", RubyVor_minimum_spanning_tree, -1);
|
34
34
|
|
35
35
|
|
36
36
|
/*
|
@@ -48,14 +48,15 @@ Init_ruby_vor_c(void)
|
|
48
48
|
*/
|
49
49
|
RubyVor_rb_cPoint = rb_define_class_under(RubyVor_rb_mRubyVor, "Point", rb_cObject);
|
50
50
|
rb_define_method(RubyVor_rb_cPoint, "distance_from", RubyVor_distance_from, 1);
|
51
|
+
rb_define_method(RubyVor_rb_cPoint, "hash", RubyVor_point_hash, 0);
|
51
52
|
}
|
52
53
|
|
53
54
|
|
54
55
|
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
57
|
+
/*
|
58
|
+
* Method declarations duplicated here for RDOC
|
59
|
+
*/
|
59
60
|
|
60
61
|
/*
|
61
62
|
* Compute the voronoi diagram and delaunay triangulation from a set of points.
|
@@ -71,9 +72,11 @@ VALUE RubyVor_from_points(VALUE, VALUE);
|
|
71
72
|
VALUE RubyVor_nn_graph(VALUE);
|
72
73
|
|
73
74
|
/*
|
74
|
-
*
|
75
|
+
* Computes the minimum spanning tree for given points, using the Delaunay triangulation as a seed.
|
76
|
+
*
|
77
|
+
* For points on a Euclidean plane, the MST is always comprised of a subset of the edges in a Delaunay triangulation. This makes computation of the tree very efficient: simply compute the Delaunay triangulation, and then run Prim's algorithm on the resulting edges.
|
75
78
|
*/
|
76
|
-
|
79
|
+
VALUE RubyVor_minimum_spanning_tree(int, VALUE*, VALUE);
|
77
80
|
|
78
81
|
/*
|
79
82
|
* Move from the given index up, restoring the heap-order property.
|
@@ -95,5 +98,9 @@ VALUE RubyVor_heapify(VALUE);
|
|
95
98
|
*/
|
96
99
|
VALUE RubyVor_distance_from(VALUE, VALUE);
|
97
100
|
|
101
|
+
/*
|
102
|
+
* Hash value for a point.
|
103
|
+
*/
|
104
|
+
VALUE RubyVor_point_hash(VALUE);
|
98
105
|
|
99
|
-
|
106
|
+
/* keep comment so RDOC will find the last method definition */
|