bribera-rubyvor 0.0.2 → 0.1.1

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.
@@ -1,3 +1,48 @@
1
+ === 0.1.1 / 2009-01-15
2
+
3
+ * LibXML require conflict fix.
4
+
5
+ === 0.1.0 / 2009-01-13
6
+
7
+ * Configurable handling of "no neighbors" case.
8
+ * Tests for "no neighbors" case.
9
+
10
+ === 0.0.9 / 2009-01-01
11
+
12
+ Happy New Year!
13
+ * New speed improvements.
14
+ * Additional visualization tweaks.
15
+ * More tests.
16
+
17
+ === 0.0.8 / 2008-12-23
18
+
19
+ * Several C warning fixes (more ISO C compliance unused variables, etc)
20
+ * Moved minimum_spanning_tree computation into C, yielding large speed gain.
21
+ * Basic SVG visualization.
22
+
23
+ === 0.0.7 / 2008-12-12
24
+
25
+ * 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).
26
+
27
+ * To fix this, the nn_graph function now treats any point that is excluded from Delaunay triangulation as having *all* other points as nearest neighbors. This is a naive approach, but is a good fix while a more efficient solution is considered.
28
+
29
+ === 0.0.6 / 2008-12-11
30
+
31
+ * Implementation of cluster_by_size to partition points into N clusters.
32
+
33
+ === 0.0.5 / 2008-12-10
34
+
35
+ * Beginnings of clustering and useful graph algorithms.
36
+ * Some refactoring of C methods.
37
+
38
+ === 0.0.4 / 2008-12-04
39
+
40
+ * Fixed 64-bit segfaults due to improper definition of the myrealloc() function.
41
+
42
+ === 0.0.3 / 2008-12-03
43
+
44
+ * Fixed a segfault by using rb_ary_push instead of direct pointer manipulation. Much simpler.
45
+
1
46
  === 0.0.2 / 2008-12-03
2
47
 
3
48
  * Computations succeed on a naive Point class, returning raw values for the associated Voronoi Diagram and Delaunay triangulation.
@@ -9,12 +9,22 @@ ext/geometry.c
9
9
  ext/heap.c
10
10
  ext/memory.c
11
11
  ext/output.c
12
+ ext/rb_cComputation.c
13
+ ext/rb_cPoint.c
14
+ ext/rb_cPriorityQueue.c
15
+ ext/ruby_vor_c.c
16
+ ext/ruby_vor_c.h
12
17
  ext/vdefs.h
13
18
  ext/voronoi.c
14
- ext/voronoi_interface.c
15
19
  lib/ruby_vor.rb
16
- lib/ruby_vor/decomposition.rb
20
+ lib/ruby_vor/computation.rb
21
+ lib/ruby_vor/geo_ruby_extensions.rb
17
22
  lib/ruby_vor/point.rb
23
+ lib/ruby_vor/priority_queue.rb
18
24
  lib/ruby_vor/version.rb
25
+ lib/ruby_vor/visualizer.rb
19
26
  rubyvor.gemspec
27
+ test/test_computation.rb
28
+ test/test_point.rb
29
+ test/test_priority_queue.rb
20
30
  test/test_voronoi_interface.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/Rakefile CHANGED
@@ -7,8 +7,7 @@ require './lib/ruby_vor/version.rb'
7
7
  EXT = "ext/voronoi_interface.#{Hoe::DLEXT}"
8
8
 
9
9
  Hoe.new('rubyvor', RubyVor::VERSION) do |p|
10
- p.developer('Brendan Ribera', 'brendan.ribera+rubyvor@gmail.com')
11
-
10
+ p.developer('Brendan Ribera', 'brendan.ribera+rubyvor@gmail.com')
12
11
  p.url = 'http://github.com/bribera/rubyvor'
13
12
 
14
13
  # C extension goodness
@@ -20,7 +19,7 @@ desc "Compile extensions"
20
19
  task :compile => EXT
21
20
  task :test => :compile
22
21
 
23
- file EXT => ['ext/extconf.rb', 'ext/voronoi_interface.c'] do
22
+ file EXT => ['ext/extconf.rb', 'ext/ruby_vor_c.c'] do
24
23
  Dir.chdir 'ext' do
25
24
  ruby 'extconf.rb'
26
25
  sh 'make'
@@ -1,3 +1,3 @@
1
1
  require 'mkmf'
2
- dir_config('voronoi_interface')
3
- create_makefile('voronoi_interface')
2
+ dir_config('ruby_vor_c')
3
+ create_makefile('ruby_vor_c')
@@ -1,8 +1,9 @@
1
1
 
2
2
  /*** MEMORY.C ***/
3
3
 
4
+ #include <ruby.h>
4
5
  #include <stdio.h>
5
- #include <stdlib.h> /* malloc(), exit() */
6
+ #include <stdlib.h> /* malloc() */
6
7
 
7
8
  #include "vdefs.h"
8
9
 
@@ -61,17 +62,15 @@ update_memory_map(char * newp)
61
62
  char *
62
63
  myalloc(unsigned n)
63
64
  {
64
- char * t ;
65
+ char * t;
66
+
65
67
  if ((t=(char*)malloc(n)) == (char *) 0)
66
- {
67
- fprintf(stderr,"Insufficient memory processing site %d (%d bytes in use)\n",
68
- rubyvorState.siteidx, total_alloc) ;
69
- exit(0) ;
70
- }
71
- total_alloc += n ;
68
+ rb_raise(rb_eNoMemError, "Insufficient memory processing site %d (%d bytes in use)\n", rubyvorState.siteidx, total_alloc);
69
+
70
+ total_alloc += n;
72
71
 
73
72
  update_memory_map(t);
74
- return (t) ;
73
+ return (t);
75
74
  }
76
75
 
77
76
  char *
@@ -81,24 +80,27 @@ myrealloc(void * oldp, unsigned n, unsigned oldn)
81
80
  int i;
82
81
 
83
82
  if ((newp=(char*)realloc(oldp, n)) == (char *) 0)
84
- {
85
- fprintf(stderr,"Insufficient memory processing site %d (%d bytes in use)\n",
86
- rubyvorState.siteidx, total_alloc) ;
87
- exit(0) ;
88
- }
83
+ rb_raise(rb_eNoMemError, "Insufficient memory processing site %d (%d bytes in use)\n", rubyvorState.siteidx, total_alloc);
89
84
 
90
- total_alloc += (n - oldn) ;
85
+ total_alloc += (n - oldn);
91
86
 
92
87
  update_memory_map(newp);
93
88
 
94
- // Mark oldp as freed, since free() was called by realloc.
95
- for (i=0; i<nallocs; i++) {
96
- if (memory_map[i] != (char*)0 && memory_map[i] == oldp) {
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
+ */
94
+ for (i=0; i<nallocs; i++)
95
+ {
96
+ if (memory_map[i] != (char*)0 && memory_map[i] == oldp)
97
+ {
97
98
  memory_map[i] = (char*)0;
98
99
  break;
99
100
  }
100
101
  }
101
- return (newp) ;
102
+
103
+ return (newp);
102
104
  }
103
105
 
104
106
 
@@ -115,6 +117,4 @@ void free_all(void)
115
117
  }
116
118
  free(memory_map);
117
119
  nallocs = 0;
118
-
119
- debug_memory();
120
120
  }
@@ -1,34 +1,27 @@
1
1
 
2
2
  /*** OUTPUT.C ***/
3
3
 
4
-
4
+ #include <vdefs.h>
5
5
  #include <stdio.h>
6
-
7
- #include "vdefs.h"
6
+ #include <unistd.h>
8
7
 
9
8
  VoronoiState rubyvorState;
10
9
 
11
10
  static float pxmin, pxmax, pymin, pymax, cradius;
12
11
 
13
12
  void openpl(void) {}
14
-
15
- #pragma argsused
16
13
  void line(float ax, float ay, float bx, float by) {}
17
-
18
- #pragma argsused
19
14
  void circle(float ax, float ay, float radius) {}
20
-
21
- #pragma argsused
22
15
  void range(float pxmin, float pxmax, float pymin, float pymax) {}
23
16
 
24
17
  void
25
18
  out_bisector(Edge * e)
26
19
  {
27
- // Save line to our ruby object
20
+ /* Save line to our ruby object */
28
21
  (*rubyvorState.storeL)(e->a, e->b, e->c);
29
22
 
30
23
 
31
- // 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); */
32
25
 
33
26
  if (rubyvorState.plot)
34
27
  line(e->reg[0]->coord.x, e->reg[0]->coord.y, e->reg[1]->coord.x, e->reg[1]->coord.y);
@@ -40,7 +33,7 @@ out_bisector(Edge * e)
40
33
  void
41
34
  out_ep(Edge * e)
42
35
  {
43
- // Save endpoint to our ruby object
36
+ /* Save endpoint to our ruby object */
44
37
  (*rubyvorState.storeE)(e->edgenbr,
45
38
  e->ep[le] != (Site *)NULL ? e->ep[le]->sitenbr : -1,
46
39
  e->ep[re] != (Site *)NULL ? e->ep[re]->sitenbr : -1);
@@ -58,10 +51,10 @@ out_ep(Edge * e)
58
51
  void
59
52
  out_vertex(Site * v)
60
53
  {
61
- // Save vertex to our ruby object
54
+ /* Save vertex to our ruby object */
62
55
  (*rubyvorState.storeV)(v->coord.x, v->coord.y);
63
56
 
64
- // printf ("v %f %f\n", v->coord.x, v->coord.y) ;
57
+ /* printf ("v %f %f\n", v->coord.x, v->coord.y) ; */
65
58
 
66
59
  if (rubyvorState.debug)
67
60
  printf("vertex(%d) at %f %f\n", v->sitenbr, v->coord.x, v->coord.y);
@@ -70,10 +63,10 @@ out_vertex(Site * v)
70
63
  void
71
64
  out_site(Site * s)
72
65
  {
73
- // Save site to our ruby object
66
+ /* Save site to our ruby object */
74
67
  (*rubyvorState.storeS)(s->coord.x, s->coord.y);
75
68
 
76
- //printf("s %f %f\n", s->coord.x, s->coord.y) ;
69
+ /* printf("s %f %f\n", s->coord.x, s->coord.y) ; */
77
70
 
78
71
  if (rubyvorState.plot)
79
72
  circle (s->coord.x, s->coord.y, cradius);
@@ -85,7 +78,7 @@ out_site(Site * s)
85
78
  void
86
79
  out_triple(Site * s1, Site * s2, Site * s3)
87
80
  {
88
- // Save triplet to our ruby object
81
+ /* Save triplet to our ruby object */
89
82
  (*rubyvorState.storeT)(s1->sitenbr, s2->sitenbr, s3->sitenbr);
90
83
 
91
84
  if (rubyvorState.debug)
@@ -229,32 +222,30 @@ clip_line(Edge * e)
229
222
  /* Linux-specific. */
230
223
  void
231
224
  debug_memory(void)
232
- {
233
- if (!rubyvorState.debug)
234
- return;
235
-
225
+ {
236
226
  char buf[30];
237
227
  FILE* pf;
228
+ int tmp;
229
+ float totalSize;
230
+ unsigned size;/* total program size */
238
231
 
239
- unsigned size;// total program size
232
+ /*
240
233
  unsigned resident;// resident set size
241
234
  unsigned share;// shared pages
242
235
  unsigned text;// text (code)
243
236
  unsigned lib;// library
244
237
  unsigned data;// data/stack
245
238
  unsigned dt;// dirty pages (unused in Linux 2.6)
246
-
247
- int retVal;
239
+ */
248
240
 
249
- float totalSize;
241
+ if (!rubyvorState.debug)
242
+ return;
250
243
 
251
244
  snprintf(buf, 30, "/proc/%u/statm", (unsigned)getpid());
252
245
  pf = fopen(buf, "r");
253
246
  if (NULL != pf)
254
247
  {
255
- retVal = fscanf(pf, "%u", &size);
256
- //, %u, %u ... etc &resident, &share, &text, &lib, &data);
257
-
248
+ tmp = fscanf(pf, "%u", &size); /*, %u, %u ... etc &resident, &share, &text, &lib, &data); */
258
249
  totalSize = (float)size / 1024.0;
259
250
  fprintf(stderr, "%f ", totalSize);
260
251
  }
@@ -0,0 +1,370 @@
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, "points array 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_minimum_spanning_tree(int argc, VALUE *argv, VALUE self)
130
+ {
131
+ VALUE mst, dist_proc, nodes, nnGraph, points, queue, tmp, adjacent, adjacentData, adjacentDistance, current, currentData, floatMax;
132
+ ID i_call, i_push, i_pop, i_has_key;
133
+ long i;
134
+
135
+ /* 0 mandatory, 1 optional */
136
+ rb_scan_args(argc, argv, "01", &dist_proc);
137
+
138
+ mst = rb_iv_get(self, "@mst");
139
+
140
+ if (RTEST(mst))
141
+ return mst;
142
+
143
+
144
+ if (NIL_P(dist_proc)) {
145
+ /* Use our default Proc */
146
+ dist_proc = rb_eval_string("lambda{|a,b| a.distance_from(b)}");
147
+ } else if (rb_class_of(dist_proc) != rb_cProc) {
148
+ /* Blow up if we have a non-nil, non-Proc */
149
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", rb_obj_classname(dist_proc), rb_class2name(rb_cProc));
150
+ }
151
+
152
+ // Set up interned values
153
+ i_call = rb_intern("call");
154
+ i_push = rb_intern("push");
155
+ i_pop = rb_intern("pop");
156
+ i_has_key = rb_intern("has_key?");
157
+
158
+ points = rb_iv_get(self, "@points");
159
+ queue = rb_eval_string("RubyVor::PriorityQueue.new");
160
+ nnGraph = RubyVor_nn_graph(self);
161
+ floatMax= rb_iv_get(rb_cFloat, "MAX");
162
+
163
+ for (i = 0; i < RARRAY(points)->len; i++) {
164
+ tmp = rb_ary_new2(5);
165
+ /* 0: node index */
166
+ rb_ary_push(tmp, LONG2FIX(i));
167
+ /* 1: parent */
168
+ rb_ary_push(tmp, Qnil);
169
+ /* 2: adjacency_list */
170
+ rb_ary_push(tmp, rb_obj_clone(RARRAY(nnGraph)->ptr[i]));
171
+ /* 3: min_adjacency_list */
172
+ rb_ary_push(tmp, rb_ary_new());
173
+ /* 4: in_q */
174
+ rb_ary_push(tmp, Qtrue);
175
+
176
+ rb_funcall(queue, i_push, 2, tmp, (i == 0) ? rb_float_new(0.0) : floatMax);
177
+ }
178
+ nodes = rb_obj_clone(rb_iv_get(queue, "@data"));
179
+
180
+ while(RTEST(current = rb_funcall(queue, i_pop, 0))) {
181
+ currentData = rb_iv_get(current, "@data");
182
+
183
+ /* mark in_q */
184
+ rb_ary_store(currentData, 4, Qfalse);
185
+
186
+ /* check for presence of parent */
187
+ if (RTEST(RARRAY(currentData)->ptr[1])) {
188
+ /* push this node into adjacency_list of parent */
189
+ rb_ary_push(RARRAY(rb_iv_get(RARRAY(currentData)->ptr[1], "@data"))->ptr[3], current);
190
+ /* push parent into adjacency_list of this node */
191
+ rb_ary_push(RARRAY(currentData)->ptr[3], RARRAY(currentData)->ptr[1]);
192
+ }
193
+
194
+ for (i = 0; i < RARRAY(RARRAY(currentData)->ptr[2])->len; i++) {
195
+ /* grab indexed node */
196
+ adjacent = RARRAY(nodes)->ptr[FIX2LONG(RARRAY(RARRAY(currentData)->ptr[2])->ptr[i])];
197
+ adjacentData = rb_iv_get(adjacent, "@data");
198
+
199
+ /* check in_q -- only look at new nodes */
200
+ if (Qtrue == RARRAY(adjacentData)->ptr[4]) {
201
+
202
+ /* compare points by node -- adjacent against current */
203
+ adjacentDistance = rb_funcall(dist_proc, i_call, 2,
204
+ RARRAY(points)->ptr[FIX2LONG(RARRAY(currentData)->ptr[0])],
205
+ RARRAY(points)->ptr[FIX2LONG(RARRAY(adjacentData)->ptr[0])]);
206
+
207
+ /* If the new distance is better than our current priority, exchange them. */
208
+ if (RFLOAT(adjacentDistance)->value < RFLOAT(rb_iv_get(adjacent, "@priority"))->value) {
209
+ /* set new :parent */
210
+ rb_ary_store(adjacentData, 1, current);
211
+ /* update priority */
212
+ rb_iv_set(adjacent, "@priority", adjacentDistance);
213
+ /* percolate up into correctn position */
214
+ RubyVor_percolate_up(queue, rb_iv_get(adjacent, "@index"));
215
+ }
216
+ }
217
+ }
218
+ }
219
+
220
+ mst = rb_hash_new();
221
+ for (i = 0; i < RARRAY(nodes)->len; i++) {
222
+ current = RARRAY(nodes)->ptr[i];
223
+ currentData = rb_iv_get(current, "@data");
224
+ if (!NIL_P(RARRAY(currentData)->ptr[1])) {
225
+ adjacentData = rb_iv_get(RARRAY(currentData)->ptr[1], "@data");
226
+ tmp = rb_ary_new2(2);
227
+ if (FIX2LONG(RARRAY(currentData)->ptr[0]) < FIX2LONG(RARRAY(adjacentData)->ptr[0])) {
228
+ rb_ary_push(tmp, RARRAY(currentData)->ptr[0]);
229
+ rb_ary_push(tmp, RARRAY(adjacentData)->ptr[0]);
230
+ } else {
231
+ rb_ary_push(tmp, RARRAY(adjacentData)->ptr[0]);
232
+ rb_ary_push(tmp, RARRAY(currentData)->ptr[0]);
233
+ }
234
+
235
+ /* if (!st_lookup(RHASH(mst)->tbl, tmp, 0)) { */
236
+ rb_hash_aset(mst, tmp, rb_iv_get(current, "@priority"));
237
+ /* } */
238
+ }
239
+ }
240
+
241
+ rb_iv_set(self, "@mst", mst);
242
+
243
+ return mst;
244
+ }
245
+
246
+
247
+ VALUE
248
+ RubyVor_nn_graph(VALUE self)
249
+ {
250
+ VALUE dtRaw, graph, points, * dtPtr, * tripletPtr, * graphPtr;
251
+ long i, j, noNeighborResponse;
252
+
253
+ graph = rb_iv_get(self, "@nn_graph");
254
+
255
+ if (RTEST(graph))
256
+ return graph;
257
+
258
+ /* Figure out our "no neighbor" response value */
259
+ if (SYM2ID(rb_iv_get(self, "@no_neighbor_response")) == rb_intern("raise")) {
260
+ noNeighborResponse = 1;
261
+ } else if (SYM2ID(rb_iv_get(self, "@no_neighbor_response")) == rb_intern("use_all")) {
262
+ noNeighborResponse = 2;
263
+ } else {
264
+ noNeighborResponse = 0;
265
+ }
266
+
267
+ /* Create an array of same size as points for the graph */
268
+ points = rb_iv_get(self, "@points");
269
+
270
+ graph = rb_ary_new2(RARRAY(points)->len);
271
+ for (i = 0; i < RARRAY(points)->len; i++)
272
+ rb_ary_push(graph, rb_ary_new());
273
+
274
+ /* Get our pointer into this array. */
275
+ graphPtr = RARRAY(graph)->ptr;
276
+
277
+ /* Iterate over the triangulation */
278
+ dtRaw = rb_iv_get(self, "@delaunay_triangulation_raw");
279
+ dtPtr = RARRAY(dtRaw)->ptr;
280
+ for (i = 0; i < RARRAY(dtRaw)->len; i++) {
281
+ tripletPtr = RARRAY(dtPtr[i])->ptr;
282
+
283
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[0])], tripletPtr[1]);
284
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[1])], tripletPtr[0]);
285
+
286
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[0])], tripletPtr[2]);
287
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[2])], tripletPtr[0]);
288
+
289
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[1])], tripletPtr[2]);
290
+ rb_ary_push(graphPtr[FIX2INT(tripletPtr[2])], tripletPtr[1]);
291
+
292
+ }
293
+ for (i = 0; i < RARRAY(graph)->len; i++) {
294
+ if (RARRAY(graphPtr[i])->len < 1) {
295
+ /* Evaluate noNeighborResponse and respond accordingly */
296
+ if (noNeighborResponse == 1) {
297
+ rb_raise(rb_eIndexError, "index of 0 (no neighbors) at %i", i);
298
+ } else if (noNeighborResponse == 2) {
299
+ /* No valid triangles touched this node -- include *all* possible neighbors
300
+ *
301
+ * Note that this can make for exceptionally slow (ie O(n^2) time) clustering,
302
+ * but should only happen in rare cases.
303
+ */
304
+ for(j = 0; j < RARRAY(points)->len; j++) {
305
+ if (j != i) {
306
+ rb_ary_push(graphPtr[i], INT2FIX(j));
307
+ if (RARRAY(graphPtr[j])->len > 0 && !rb_ary_includes(graphPtr[j], INT2FIX(i)))
308
+ rb_ary_push(graphPtr[j], INT2FIX(i));
309
+ }
310
+ }
311
+ }
312
+ } else {
313
+ rb_funcall(graphPtr[i], rb_intern("uniq!"), 0);
314
+ }
315
+ }
316
+
317
+ rb_iv_set(self, "@nn_graph", graph);
318
+
319
+ return graph;
320
+ }
321
+
322
+
323
+
324
+ /*
325
+ * Static C helper methods
326
+ */
327
+
328
+
329
+ /*** sort sites on y, then x, coord ***/
330
+ static int
331
+ scomp(const void * vs1, const void * vs2)
332
+ {
333
+ Point * s1 = (Point *)vs1 ;
334
+ Point * s2 = (Point *)vs2 ;
335
+
336
+ if (s1->y < s2->y)
337
+ {
338
+ return (-1) ;
339
+ }
340
+ if (s1->y > s2->y)
341
+ {
342
+ return (1) ;
343
+ }
344
+ if (s1->x < s2->x)
345
+ {
346
+ return (-1) ;
347
+ }
348
+ if (s1->x > s2->x)
349
+ {
350
+ return (1) ;
351
+ }
352
+ return (0) ;
353
+ }
354
+
355
+ /*** return a single in-storage site ***/
356
+ static Site *
357
+ nextone(void)
358
+ {
359
+ Site * s ;
360
+
361
+ if (rubyvorState.siteidx < rubyvorState.nsites)
362
+ {
363
+ s = &rubyvorState.sites[rubyvorState.siteidx++];
364
+ return (s) ;
365
+ }
366
+ else
367
+ {
368
+ return ((Site *)NULL) ;
369
+ }
370
+ }