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.
- data/History.txt +45 -0
- data/Manifest.txt +12 -2
- data/README.txt +32 -1
- data/Rakefile +2 -3
- data/ext/extconf.rb +2 -2
- data/ext/memory.c +21 -21
- data/ext/output.c +19 -28
- data/ext/rb_cComputation.c +370 -0
- data/ext/rb_cPoint.c +35 -0
- data/ext/rb_cPriorityQueue.c +120 -0
- data/ext/ruby_vor_c.c +106 -0
- data/ext/ruby_vor_c.h +36 -0
- data/ext/vdefs.h +3 -2
- data/ext/voronoi.c +57 -65
- data/lib/ruby_vor.rb +9 -6
- data/lib/ruby_vor/computation.rb +137 -0
- data/lib/ruby_vor/geo_ruby_extensions.rb +15 -0
- data/lib/ruby_vor/point.rb +24 -11
- data/lib/ruby_vor/priority_queue.rb +84 -0
- data/lib/ruby_vor/version.rb +1 -1
- data/lib/ruby_vor/visualizer.rb +218 -0
- data/rubyvor.gemspec +8 -6
- data/test/test_computation.rb +344 -0
- data/test/test_point.rb +100 -0
- data/test/test_priority_queue.rb +129 -0
- data/test/test_voronoi_interface.rb +12 -9
- metadata +17 -4
- data/ext/voronoi_interface.c +0 -213
- data/lib/ruby_vor/decomposition.rb +0 -22
data/History.txt
CHANGED
@@ -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.
|
data/Manifest.txt
CHANGED
@@ -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/
|
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
|
-
|
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/
|
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'
|
data/ext/extconf.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
require 'mkmf'
|
2
|
-
dir_config('
|
3
|
-
create_makefile('
|
2
|
+
dir_config('ruby_vor_c')
|
3
|
+
create_makefile('ruby_vor_c')
|
data/ext/memory.c
CHANGED
@@ -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()
|
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
|
-
|
68
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
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
|
}
|
data/ext/output.c
CHANGED
@@ -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
|
-
|
20
|
+
/* Save line to our ruby object */
|
28
21
|
(*rubyvorState.storeL)(e->a, e->b, e->c);
|
29
22
|
|
30
23
|
|
31
|
-
|
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
|
-
|
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
|
-
|
54
|
+
/* Save vertex to our ruby object */
|
62
55
|
(*rubyvorState.storeV)(v->coord.x, v->coord.y);
|
63
56
|
|
64
|
-
|
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
|
-
|
66
|
+
/* Save site to our ruby object */
|
74
67
|
(*rubyvorState.storeS)(s->coord.x, s->coord.y);
|
75
68
|
|
76
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
+
}
|