bribera-rubyvor 0.0.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ === 0.0.2 / 2008-12-03
2
+
3
+ * Computations succeed on a naive Point class, returning raw values for the associated Voronoi Diagram and Delaunay triangulation.
4
+
5
+ === 0.0.1 / 2008-12-01
6
+
7
+ * Initial release. Nothing works.
data/Manifest.txt ADDED
@@ -0,0 +1,20 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ ext/Doc
6
+ ext/edgelist.c
7
+ ext/extconf.rb
8
+ ext/geometry.c
9
+ ext/heap.c
10
+ ext/memory.c
11
+ ext/output.c
12
+ ext/vdefs.h
13
+ ext/voronoi.c
14
+ ext/voronoi_interface.c
15
+ lib/ruby_vor.rb
16
+ lib/ruby_vor/decomposition.rb
17
+ lib/ruby_vor/point.rb
18
+ lib/ruby_vor/version.rb
19
+ rubyvor.gemspec
20
+ test/test_voronoi_interface.rb
data/README.txt ADDED
@@ -0,0 +1,45 @@
1
+ = rubyvor
2
+
3
+ Efficient Voronoi diagrams and Delaunay trianglation for Ruby
4
+
5
+ == Description
6
+
7
+ RubyVor provides efficient computation of Voronoi diagrams and
8
+ Delaunay triangulation for a set of Ruby points. It is intended to
9
+ function as a complemenet to GeoRuby. These structures can be used to
10
+ compute a nearest-neighbor graph for a set of points. This graph can
11
+ in turn be used for proximity-based clustering of the input points.
12
+
13
+ == Usage:
14
+
15
+ TODO
16
+
17
+ == LICENSE:
18
+
19
+ Original public-domain C code (by Steven Fortune; http://ect.bell-labs.com/who/sjf/) and
20
+ memory-management fixes for said C code (by Derek Bradley; http://www.derekbradley.ca)
21
+ used (and released under the MIT-LICENSE) with permission.
22
+
23
+
24
+ (The MIT License)
25
+
26
+ Copyright (c) 2008 Brendan Ribera <brendan.ribera+rubyvor@gmail.com>
27
+
28
+
29
+ Permission is hereby granted, free of charge, to any person obtaining a copy
30
+ of this software and associated documentation files (the "Software"), to deal
31
+ in the Software without restriction, including without limitation the rights
32
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33
+ copies of the Software, and to permit persons to whom the Software is
34
+ furnished to do so, subject to the following conditions:
35
+
36
+ The above copyright notice and this permission notice shall be included in
37
+ all copies or substantial portions of the Software.
38
+
39
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
42
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
45
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/ruby_vor/version.rb'
6
+
7
+ EXT = "ext/voronoi_interface.#{Hoe::DLEXT}"
8
+
9
+ Hoe.new('rubyvor', RubyVor::VERSION) do |p|
10
+ p.developer('Brendan Ribera', 'brendan.ribera+rubyvor@gmail.com')
11
+
12
+ p.url = 'http://github.com/bribera/rubyvor'
13
+
14
+ # C extension goodness
15
+ p.spec_extras[:extensions] = "ext/extconf.rb"
16
+ p.clean_globs << EXT << 'ext/*.o' << 'ext/Makefile'
17
+ end
18
+
19
+ desc "Compile extensions"
20
+ task :compile => EXT
21
+ task :test => :compile
22
+
23
+ file EXT => ['ext/extconf.rb', 'ext/voronoi_interface.c'] do
24
+ Dir.chdir 'ext' do
25
+ ruby 'extconf.rb'
26
+ sh 'make'
27
+ end
28
+ end
29
+
30
+ desc "Prepare for github upload"
31
+ task :github do
32
+ system "git ls-files | egrep -v \"\\.gitignore\" > Manifest.txt"
33
+ system "rake debug_gem | egrep -v \"^\\(in\" > rubyvor.gemspec"
34
+ end
35
+
36
+ task :gem => :github
data/ext/Doc ADDED
@@ -0,0 +1,30 @@
1
+ voronoi - compute Voronoi diagram or Delaunay triangulation
2
+ SYNOPSIS
3
+ voronoi [-s -t] <pointfile >outputfile
4
+
5
+ Voronoi reads the standard input for a set of points in the plane and writes either
6
+ the Voronoi diagram or the Delaunay triangulation to the standard output.
7
+ Each input line should consist of two real numbers, separated by white space.
8
+
9
+ If option
10
+ -t
11
+ is present, the Delaunay triangulation is produced.
12
+ Each output line is a triple
13
+ i j k
14
+ which are the indices of the three points in a Delaunay triangle. Points are
15
+ numbered starting at 0. If this option is not present, the
16
+ Voronoi diagram is produced. There are four output record types.
17
+ s a b
18
+ indicates that an input point at coordinates
19
+ l a b c
20
+ indicates a line with equation ax + by = c.
21
+ v a b
22
+ indicates a vertex at a b.
23
+ e l v1 v2
24
+ indicates a Voronoi segment which is a subsegment of line number l;
25
+ with endpoints numbered v1 and v2. If v1 or v2 is -1, the line
26
+ extends to infinity.
27
+
28
+ AUTHOR
29
+ Steve J. Fortune (1987) A Sweepline Algorithm for Voronoi Diagrams,
30
+ Algorithmica 2, 153-174.
data/ext/edgelist.c ADDED
@@ -0,0 +1,206 @@
1
+
2
+ /*** EDGELIST.C ***/
3
+
4
+ #include "vdefs.h"
5
+
6
+ VoronoiState rubyvorState;
7
+
8
+ static int ELhashsize ;
9
+ static Halfedge * ELleftend, * ELrightend, ** ELhash ;
10
+ static Freelist hfl ;
11
+ static int ntry, totalsearch ;
12
+
13
+ void
14
+ ELinitialize(void)
15
+ {
16
+ int i ;
17
+
18
+ freeinit(&hfl, sizeof(Halfedge)) ;
19
+ ELhashsize = 2 * rubyvorState.sqrt_nsites ;
20
+ ELhash = (Halfedge **)myalloc( sizeof(*ELhash) * ELhashsize) ;
21
+ for (i = 0 ; i < ELhashsize ; i++)
22
+ {
23
+ ELhash[i] = (Halfedge *)NULL ;
24
+ }
25
+ ELleftend = HEcreate((Edge *)NULL, 0) ;
26
+ ELrightend = HEcreate((Edge *)NULL, 0) ;
27
+ ELleftend->ELleft = (Halfedge *)NULL ;
28
+ ELleftend->ELright = ELrightend ;
29
+ ELrightend->ELleft = ELleftend ;
30
+ ELrightend->ELright = (Halfedge *)NULL ;
31
+ ELhash[0] = ELleftend ;
32
+ ELhash[ELhashsize-1] = ELrightend ;
33
+ }
34
+
35
+ Halfedge *
36
+ HEcreate(Edge * e, int pm)
37
+ {
38
+ Halfedge * answer ;
39
+
40
+ answer = (Halfedge *)getfree(&hfl) ;
41
+ answer->ELedge = e ;
42
+ answer->ELpm = pm ;
43
+ answer->PQnext = (Halfedge *)NULL ;
44
+ answer->vertex = (Site *)NULL ;
45
+ answer->ELrefcnt = 0 ;
46
+ return (answer) ;
47
+ }
48
+
49
+ void
50
+ ELinsert(Halfedge * lb, Halfedge * new)
51
+ {
52
+ new->ELleft = lb ;
53
+ new->ELright = lb->ELright ;
54
+ (lb->ELright)->ELleft = new ;
55
+ lb->ELright = new ;
56
+ }
57
+
58
+ /* Get entry from hash table, pruning any deleted nodes */
59
+
60
+ Halfedge *
61
+ ELgethash(int b)
62
+ {
63
+ Halfedge * he ;
64
+
65
+ if ((b < 0) || (b >= ELhashsize))
66
+ {
67
+ return ((Halfedge *)NULL) ;
68
+ }
69
+ he = ELhash[b] ;
70
+ if ((he == (Halfedge *)NULL) || (he->ELedge != (Edge *)DELETED))
71
+ {
72
+ return (he) ;
73
+ }
74
+ /* Hash table points to deleted half edge. Patch as necessary. */
75
+ ELhash[b] = (Halfedge *)NULL ;
76
+ if ((--(he->ELrefcnt)) == 0)
77
+ {
78
+ makefree((Freenode *)he, (Freelist *)&hfl) ;
79
+ }
80
+ return ((Halfedge *)NULL) ;
81
+ }
82
+
83
+ Halfedge *
84
+ ELleftbnd(Point * p)
85
+ {
86
+ int i, bucket ;
87
+ Halfedge * he ;
88
+
89
+ /* Use hash table to get close to desired halfedge */
90
+ bucket = (p->x - rubyvorState.xmin) / rubyvorState.deltax * ELhashsize ;
91
+ if (bucket < 0)
92
+ {
93
+ bucket = 0 ;
94
+ }
95
+ if (bucket >= ELhashsize)
96
+ {
97
+ bucket = ELhashsize - 1 ;
98
+ }
99
+ he = ELgethash(bucket) ;
100
+ if (he == (Halfedge *)NULL)
101
+ {
102
+ for (i = 1 ; 1 ; i++)
103
+ {
104
+ if ((he = ELgethash(bucket-i)) != (Halfedge *)NULL)
105
+ {
106
+ break ;
107
+ }
108
+ if ((he = ELgethash(bucket+i)) != (Halfedge *)NULL)
109
+ {
110
+ break ;
111
+ }
112
+ }
113
+ totalsearch += i ;
114
+ }
115
+ ntry++ ;
116
+ /* Now search linear list of halfedges for the corect one */
117
+ if (he == ELleftend || (he != ELrightend && right_of(he,p)))
118
+ {
119
+ do {
120
+ he = he->ELright ;
121
+ } while (he != ELrightend && right_of(he,p)) ;
122
+ he = he->ELleft ;
123
+ }
124
+ else
125
+ {
126
+ do {
127
+ he = he->ELleft ;
128
+ } while (he != ELleftend && !right_of(he,p)) ;
129
+ }
130
+ /*** Update hash table and reference counts ***/
131
+ if ((bucket > 0) && (bucket < ELhashsize-1))
132
+ {
133
+ if (ELhash[bucket] != (Halfedge *)NULL)
134
+ {
135
+ (ELhash[bucket]->ELrefcnt)-- ;
136
+ }
137
+ ELhash[bucket] = he ;
138
+ (ELhash[bucket]->ELrefcnt)++ ;
139
+ }
140
+ return (he) ;
141
+ }
142
+
143
+ /*** This delete routine can't reclaim node, since pointers from hash
144
+ : table may be present.
145
+ ***/
146
+
147
+ void
148
+ ELdelete(Halfedge * he)
149
+ {
150
+ (he->ELleft)->ELright = he->ELright ;
151
+ (he->ELright)->ELleft = he->ELleft ;
152
+ he->ELedge = (Edge *)DELETED ;
153
+ }
154
+
155
+ Halfedge *
156
+ ELright(Halfedge * he)
157
+ {
158
+ return (he->ELright) ;
159
+ }
160
+
161
+ Halfedge *
162
+ ELleft(Halfedge * he)
163
+ {
164
+ return (he->ELleft) ;
165
+ }
166
+
167
+ Site *
168
+ leftreg(Halfedge * he)
169
+ {
170
+ if (he->ELedge == (Edge *)NULL)
171
+ {
172
+ return(rubyvorState.bottomsite) ;
173
+ }
174
+ return (he->ELpm == le ? he->ELedge->reg[le] :
175
+ he->ELedge->reg[re]) ;
176
+ }
177
+
178
+ Site *
179
+ rightreg(Halfedge * he)
180
+ {
181
+ if (he->ELedge == (Edge *)NULL)
182
+ {
183
+ return(rubyvorState.bottomsite) ;
184
+ }
185
+ return (he->ELpm == le ? he->ELedge->reg[re] :
186
+ he->ELedge->reg[le]) ;
187
+ }
188
+
189
+ /*
190
+ * Semi-hacky way to access these static variables. Placing them inside rubyvorState
191
+ * causes pointer issues that I don't want to debug, and they're only accessed briefly
192
+ * inside of voronoi.c. Since we're just doing pointer comparison there, this is an
193
+ * acceptable compromise.
194
+ */
195
+
196
+ Halfedge *
197
+ getELleftend(void)
198
+ {
199
+ return(ELleftend);
200
+ }
201
+
202
+ Halfedge *
203
+ getELrightend(void)
204
+ {
205
+ return(ELrightend);
206
+ }
data/ext/extconf.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+ dir_config('voronoi_interface')
3
+ create_makefile('voronoi_interface')
data/ext/geometry.c ADDED
@@ -0,0 +1,221 @@
1
+
2
+ /*** GEOMETRY.C ***/
3
+
4
+ #include <math.h>
5
+ #include "vdefs.h"
6
+
7
+ VoronoiState rubyvorState;
8
+
9
+ static Freelist efl;
10
+
11
+ void
12
+ geominit(void)
13
+ {
14
+ freeinit(&efl, sizeof(Edge)) ;
15
+ rubyvorState.nvertices = rubyvorState.nedges = 0 ;
16
+ rubyvorState.sqrt_nsites = sqrt(rubyvorState.nsites+4) ;
17
+ rubyvorState.deltay = rubyvorState.ymax - rubyvorState.ymin ;
18
+ rubyvorState.deltax = rubyvorState.xmax - rubyvorState.xmin ;
19
+ }
20
+
21
+ Edge *
22
+ bisect(Site * s1, Site * s2)
23
+ {
24
+ float dx, dy, adx, ady ;
25
+ Edge * newedge ;
26
+
27
+ newedge = (Edge *)getfree(&efl) ;
28
+ newedge->reg[0] = s1 ;
29
+ newedge->reg[1] = s2 ;
30
+ ref(s1) ;
31
+ ref(s2) ;
32
+ newedge->ep[0] = newedge->ep[1] = (Site *)NULL ;
33
+ dx = s2->coord.x - s1->coord.x ;
34
+ dy = s2->coord.y - s1->coord.y ;
35
+ adx = dx>0 ? dx : -dx ;
36
+ ady = dy>0 ? dy : -dy ;
37
+ newedge->c = s1->coord.x * dx + s1->coord.y * dy + (dx*dx +
38
+ dy*dy) * 0.5 ;
39
+ if (adx > ady)
40
+ {
41
+ newedge->a = 1.0 ;
42
+ newedge->b = dy/dx ;
43
+ newedge->c /= dx ;
44
+ }
45
+ else
46
+ {
47
+ newedge->b = 1.0 ;
48
+ newedge->a = dx/dy ;
49
+ newedge->c /= dy ;
50
+ }
51
+ newedge->edgenbr = rubyvorState.nedges ;
52
+ out_bisector(newedge) ;
53
+ rubyvorState.nedges++ ;
54
+ return (newedge) ;
55
+ }
56
+
57
+ Site *
58
+ intersect(Halfedge * el1, Halfedge * el2)
59
+ {
60
+ Edge * e1, * e2, * e ;
61
+ Halfedge * el ;
62
+ float d, xint, yint ;
63
+ int right_of_site ;
64
+ Site * v ;
65
+
66
+ e1 = el1->ELedge ;
67
+ e2 = el2->ELedge ;
68
+ if ((e1 == (Edge*)NULL) || (e2 == (Edge*)NULL))
69
+ {
70
+ return ((Site *)NULL) ;
71
+ }
72
+ if (e1->reg[1] == e2->reg[1])
73
+ {
74
+ return ((Site *)NULL) ;
75
+ }
76
+ d = (e1->a * e2->b) - (e1->b * e2->a) ;
77
+ if ((-1.0e-10 < d) && (d < 1.0e-10))
78
+ {
79
+ return ((Site *)NULL) ;
80
+ }
81
+ xint = (e1->c * e2->b - e2->c * e1->b) / d ;
82
+ yint = (e2->c * e1->a - e1->c * e2->a) / d ;
83
+ if ((e1->reg[1]->coord.y < e2->reg[1]->coord.y) ||
84
+ (e1->reg[1]->coord.y == e2->reg[1]->coord.y &&
85
+ e1->reg[1]->coord.x < e2->reg[1]->coord.x))
86
+ {
87
+ el = el1 ;
88
+ e = e1 ;
89
+ }
90
+ else
91
+ {
92
+ el = el2 ;
93
+ e = e2 ;
94
+ }
95
+ right_of_site = (xint >= e->reg[1]->coord.x) ;
96
+ if ((right_of_site && (el->ELpm == le)) ||
97
+ (!right_of_site && (el->ELpm == re)))
98
+ {
99
+ return ((Site *)NULL) ;
100
+ }
101
+ v = (Site *)getfree(&(rubyvorState.sfl)) ;
102
+ v->refcnt = 0 ;
103
+ v->coord.x = xint ;
104
+ v->coord.y = yint ;
105
+ return (v) ;
106
+ }
107
+
108
+ /*** returns 1 if p is to right of halfedge e ***/
109
+
110
+ int
111
+ right_of(Halfedge * el, Point * p)
112
+ {
113
+ Edge * e ;
114
+ Site * topsite ;
115
+ int right_of_site, above, fast ;
116
+ float dxp, dyp, dxs, t1, t2, t3, yl ;
117
+
118
+ e = el->ELedge ;
119
+ topsite = e->reg[1] ;
120
+ right_of_site = (p->x > topsite->coord.x) ;
121
+ if (right_of_site && (el->ELpm == le))
122
+ {
123
+ return (1) ;
124
+ }
125
+ if(!right_of_site && (el->ELpm == re))
126
+ {
127
+ return (0) ;
128
+ }
129
+ if (e->a == 1.0)
130
+ {
131
+ dyp = p->y - topsite->coord.y ;
132
+ dxp = p->x - topsite->coord.x ;
133
+ fast = 0 ;
134
+ if ((!right_of_site & (e->b < 0.0)) ||
135
+ (right_of_site & (e->b >= 0.0)))
136
+ {
137
+ fast = above = (dyp >= e->b*dxp) ;
138
+ }
139
+ else
140
+ {
141
+ above = ((p->x + p->y * e->b) > (e->c)) ;
142
+ if (e->b < 0.0)
143
+ {
144
+ above = !above ;
145
+ }
146
+ if (!above)
147
+ {
148
+ fast = 1 ;
149
+ }
150
+ }
151
+ if (!fast)
152
+ {
153
+ dxs = topsite->coord.x - (e->reg[0])->coord.x ;
154
+ above = (e->b * (dxp*dxp - dyp*dyp))
155
+ <
156
+ (dxs * dyp * (1.0 + 2.0 * dxp /
157
+ dxs + e->b * e->b)) ;
158
+ if (e->b < 0.0)
159
+ {
160
+ above = !above ;
161
+ }
162
+ }
163
+ }
164
+ else /*** e->b == 1.0 ***/
165
+ {
166
+ yl = e->c - e->a * p->x ;
167
+ t1 = p->y - yl ;
168
+ t2 = p->x - topsite->coord.x ;
169
+ t3 = yl - topsite->coord.y ;
170
+ above = ((t1*t1) > ((t2 * t2) + (t3 * t3))) ;
171
+ }
172
+ return (el->ELpm == le ? above : !above) ;
173
+ }
174
+
175
+ void
176
+ endpoint(Edge * e, int lr, Site * s)
177
+ {
178
+ e->ep[lr] = s ;
179
+ ref(s) ;
180
+ if (e->ep[re-lr] == (Site *)NULL)
181
+ {
182
+ return ;
183
+ }
184
+ out_ep(e) ;
185
+ deref(e->reg[le]) ;
186
+ deref(e->reg[re]) ;
187
+ makefree((Freenode *)e, (Freelist *) &efl) ;
188
+ }
189
+
190
+ float
191
+ dist(Site * s, Site * t)
192
+ {
193
+ float dx,dy ;
194
+
195
+ dx = s->coord.x - t->coord.x ;
196
+ dy = s->coord.y - t->coord.y ;
197
+ return (sqrt(dx*dx + dy*dy)) ;
198
+ }
199
+
200
+ void
201
+ makevertex(Site * v)
202
+ {
203
+ v->sitenbr = rubyvorState.nvertices++ ;
204
+ out_vertex(v) ;
205
+ }
206
+
207
+ void
208
+ deref(Site * v)
209
+ {
210
+ if (--(v->refcnt) == 0 )
211
+ {
212
+ makefree((Freenode *)v, (Freelist *)&(rubyvorState.sfl)) ;
213
+ }
214
+ }
215
+
216
+ void
217
+ ref(Site * v)
218
+ {
219
+ ++(v->refcnt) ;
220
+ }
221
+