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 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
@@ -22,6 +22,7 @@ lib/ruby_vor/geo_ruby_extensions.rb
22
22
  lib/ruby_vor/point.rb
23
23
  lib/ruby_vor/priority_queue.rb
24
24
  lib/ruby_vor/version.rb
25
+ lib/ruby_vor/visualizer.rb
25
26
  rubyvor.gemspec
26
27
  test/test_computation.rb
27
28
  test/test_priority_queue.rb
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
- TODO
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
- // Mark oldp as freed, since free() was called by realloc.
90
- //
91
- // TODO: this seems naive; measure if this is a bottleneck & use a hash table or some other scheme if it is.
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
- // Save line to our ruby object
20
+ /* Save line to our ruby object */
21
21
  (*rubyvorState.storeL)(e->a, e->b, e->c);
22
22
 
23
23
 
24
- // printf("l %f %f %f\n", e->a, e->b, e->c);
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
- // Save endpoint to our ruby object
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
- // Save vertex to our ruby object
54
+ /* Save vertex to our ruby object */
55
55
  (*rubyvorState.storeV)(v->coord.x, v->coord.y);
56
56
 
57
- // printf ("v %f %f\n", v->coord.x, v->coord.y) ;
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
- // Save site to our ruby object
66
+ /* Save site to our ruby object */
67
67
  (*rubyvorState.storeS)(s->coord.x, s->coord.y);
68
68
 
69
- //printf("s %f %f\n", s->coord.x, s->coord.y) ;
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
- // Save triplet to our ruby object
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
- float totalSize;
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
- retVal = fscanf(pf, "%u", &size);
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
  }
@@ -7,83 +7,86 @@
7
7
  static Site * nextone(void);
8
8
  static int scomp(const void *, const void *);
9
9
 
10
- //
11
- // See ruby_vor.c for RDOC
12
- //
10
+ /*
11
+ * See ruby_vor.c for RDOC
12
+ */
13
13
 
14
14
 
15
- //
16
- // Class methods for RubyVor::VDDT::Computation
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
- // Require T_ARRAY
27
+ /* Require T_ARRAY */
29
28
  Check_Type(pointsArray, T_ARRAY);
30
29
 
31
- // Intern our point access methods
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
- // Require nonzero size and x & y methods on each array object
40
- if (inSize < 1)
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 < inSize; i++) {
43
- if(!rb_respond_to(inPtr[i], x) || !rb_respond_to(inPtr[i], y))
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
- // Initialize rubyvorState
49
- initialize_state(/* debug? */ 0);
49
+ /* Initialize rubyvorState */
50
+ initialize_state(0 /* debug? */);
50
51
  debug_memory();
51
52
 
52
- // Create our return object.
53
- newComp = rb_funcall(self, rb_intern("new"), 1, pointsArray);
54
- // Store it in rubyvorState so we can populate its values.
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
- // Read in the sites, sort, and compute xmin, xmax, ymin, ymax
58
- //
59
- // TODO: refactor this block into a separate method for clarity?
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
- // Allocate memory for 4000 sites...
63
- rubyvorState.sites = (Site *) myalloc(4000 * sizeof(Site));
66
+ /* Allocate memory for N sites... */
67
+ rubyvorState.sites = (Site *) myalloc(inSize * sizeof(Site));
64
68
 
65
69
 
66
- // Iterate over the arrays, doubling the incoming values.
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
- // Allocate for 4000 more if we just hit a multiple of 4000...
77
- if (rubyvorState.nsites % 4000 == 0)
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+4000)*sizeof(Site),rubyvorState.nsites*sizeof(Site));
82
+ rubyvorState.sites = (Site *)myrealloc(rubyvorState.sites,(rubyvorState.nsites+inSize)*sizeof(Site),rubyvorState.nsites*sizeof(Site));
80
83
  }
81
84
  }
82
85
 
83
- // Sort the Sites
86
+ /* Sort the Sites */
84
87
  qsort((void *)rubyvorState.sites, rubyvorState.nsites, sizeof(Site), scomp) ;
85
88
 
86
- // Pull the minimum values.
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
- // Perform the computation
109
+ /* Perform the computation */
107
110
  voronoi(nextone);
108
111
  debug_memory();
109
112
 
110
- // Get rid of our comp reference
113
+ /* Get rid of our comp reference */
111
114
  rubyvorState.comp = (void *)NULL;
112
115
 
113
- // Free our allocated objects
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, tmpHash, tmpArray, adjList, latestAddition;
130
- long i, j;
131
+ VALUE mst, dist_proc, nodes, nnGraph, points, queue, tmp, adjacent, adjacentData, adjacentDistance, latestAddition, latestAdditionData, floatMax;
132
+ long i;
131
133
 
132
- // 0 mandatory, 1 optional
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
- // Use our default Proc
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
- // Blow up if we have a non-nil, non-Proc
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
- nodes = rb_ary_new2(RARRAY(points)->len);
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
- for (i=0; i < RARRAY(points)->len; i++) {
149
- tmpHash = rb_hash_new();
150
-
151
- // :node => n,
152
- rb_hash_aset(tmpHash, ID2SYM(rb_intern("node")), INT2FIX(i));
153
- // :min_distance => (n == 0) ? 0.0 : Float::MAX,
154
- rb_hash_aset(tmpHash, ID2SYM(rb_intern("min_distance")), (i == 0) ? INT2FIX(0) : rb_const_get(rb_cFloat, rb_intern("MAX")));
155
- // :parent => nil,
156
- rb_hash_aset(tmpHash, ID2SYM(rb_intern("parent")), Qnil);
157
- // :adjacency_list => RubyVor_nn_graph[n].clone,
158
- rb_hash_aset(tmpHash, ID2SYM(rb_intern("adjacency_list")), rb_funcall(RARRAY(nnGraph)->ptr[i], rb_intern("clone"), 0));
159
- // :min_adjacency_list => [],
160
- rb_hash_aset(tmpHash, ID2SYM(rb_intern("min_adjacency_list")), rb_ary_new());
161
- // :in_q => true
162
- rb_hash_aset(tmpHash, ID2SYM(rb_intern("in_q")), Qtrue);
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
- tmpArray = rb_funcall(queue, rb_intern("data"), 0);
168
- for (i = 0; i < RARRAY(tmpArray)->len; i++) {
169
- tmpHash = RARRAY(tmpArray)->ptr[i];
170
- adjList = rb_hash_aref(tmpHash, ID2SYM(rb_intern("adjacency_list")));
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
- for (j = 0; j < RARRAY(adjList)->len; j++)
173
- rb_ary_store(adjList, j, RARRAY(tmpArray)->ptr[ FIX2INT(RARRAY(adjList)->ptr[j]) ]);
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
- latestAddition = rb_funcall(queue, rb_intern("pop"), 0);
177
- while (RTEST(latestAddition)) {
178
- rb_hash_aset(latestAddition, ID2SYM(rb_intern("in_q")), Qfalse);
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
- if (RTEST(rb_hash_aref(latestAddition, ID2SYM(rb_intern("parent"))))) {
231
+ }
181
232
  }
182
- break;
183
233
  }
234
+
235
+ rb_iv_set(self, "@mst", mst);
184
236
 
185
- return tmpArray;
237
+ return mst;
186
238
  }
187
- */
239
+
188
240
 
189
241
  VALUE
190
242
  RubyVor_nn_graph(VALUE self)
191
243
  {
192
- long i, j;
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
- // Create an array of same size as points for the graph
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
- // Get our pointer into this array.
259
+ /* Get our pointer into this array. */
207
260
  graphPtr = RARRAY(graph)->ptr;
208
261
 
209
- // Iterate over the triangulation
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
- // No valid triangles touched this node -- include *all* possible neighbors
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
- // Static C helper methods
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
+ }
@@ -2,9 +2,9 @@
2
2
  #include <vdefs.h>
3
3
  #include <ruby_vor_c.h>
4
4
 
5
- //
6
- // Instance methods for RubyVor::VDDT::PriorityQueue
7
- //
5
+ /*
6
+ * Instance methods for RubyVor::VDDT::PriorityQueue
7
+ */
8
8
 
9
9
 
10
10
  static VALUE
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
- // Extension initialization
7
- //
5
+ /*
6
+ * Extension initialization
7
+ */
8
8
  void
9
9
  Init_ruby_vor_c(void)
10
10
  {
11
- //
12
- // Set up our Modules and Class.
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
- // rb_define_method(RubyVor_rb_cComputation, "minimum_spanning_tree", RubyVor_minimum_spanning_tree, -1);
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
- // Method declarations duplicated here for RDOC
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
- * Compute the MST for the nearest-nearest graph using a Priority Queue and Prim's algorithm.
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
- // VALUE RubyVor_minimum_spanning_tree(int, VALUE*, VALUE);
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
- // keep comment so RDOC will find the last method definition
106
+ /* keep comment so RDOC will find the last method definition */