rubyvor 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,271 @@
1
+
2
+ /*** VORONOI.C ***/
3
+
4
+ #include <ruby.h>
5
+ #include <vdefs.h>
6
+ #include <stdio.h>
7
+
8
+ VoronoiState rubyvorState;
9
+
10
+ /* Static method definitions: C -> Ruby storage methods. */
11
+ static void storeTriangulationTriplet(const int, const int, const int);
12
+ static void storeLine(const float, const float, const float);
13
+ static void storeEndpoint(const int, const int, const int);
14
+ static void storeVertex(const float, const float);
15
+ static void storeSite(const float, const float);
16
+
17
+ /*** implicit parameters: nsites, sqrt_nsites, xmin, xmax, ymin, ymax,
18
+ : deltax, deltay (can all be estimates).
19
+ : Performance suffers if they are wrong; better to make nsites,
20
+ : deltax, and deltay too big than too small. (?)
21
+ ***/
22
+
23
+ void initialize_state(int debug)
24
+ {
25
+ /* Set up our initial state */
26
+ rubyvorState.debug = debug;
27
+ rubyvorState.plot = 0;
28
+ rubyvorState.nsites = 0;
29
+ rubyvorState.siteidx = 0;
30
+
31
+ rubyvorState.storeT = storeTriangulationTriplet;
32
+ rubyvorState.storeL = storeLine;
33
+ rubyvorState.storeE = storeEndpoint;
34
+ rubyvorState.storeV = storeVertex;
35
+ rubyvorState.storeS = storeSite;
36
+
37
+ /* Initialize the Site Freelist */
38
+ freeinit(&(rubyvorState.sfl), sizeof(Site)) ;
39
+
40
+ /* Initialize the geometry module */
41
+ geominit() ;
42
+
43
+ /* TODO: remove C plot references */
44
+ if (rubyvorState.plot)
45
+ plotinit();
46
+ }
47
+
48
+ void
49
+ voronoi(Site *(*nextsite)(void))
50
+ {
51
+ Site * newsite, * bot, * top, * temp, * p, * v ;
52
+ Point newintstar ;
53
+ int pm , c;
54
+ Halfedge * lbnd, * rbnd, * llbnd, * rrbnd, * bisector ;
55
+ Edge * e ;
56
+
57
+ newintstar.x = newintstar.y = c = 0;
58
+
59
+ PQinitialize() ;
60
+ rubyvorState.bottomsite = (*nextsite)() ;
61
+ out_site(rubyvorState.bottomsite) ;
62
+ ELinitialize() ;
63
+ newsite = (*nextsite)() ;
64
+
65
+ while (1)
66
+ {
67
+ if(!PQempty())
68
+ newintstar = PQ_min() ;
69
+
70
+ if (newsite != (Site *)NULL && (PQempty()
71
+ || newsite -> coord.y < newintstar.y
72
+ || (newsite->coord.y == newintstar.y
73
+ && newsite->coord.x < newintstar.x)))
74
+ {
75
+ /* new site is smallest */
76
+ {
77
+ out_site(newsite) ;
78
+ }
79
+ lbnd = ELleftbnd(&(newsite->coord)) ;
80
+ rbnd = ELright(lbnd) ;
81
+ bot = rightreg(lbnd) ;
82
+ e = bisect(bot, newsite) ;
83
+ bisector = HEcreate(e, le) ;
84
+ ELinsert(lbnd, bisector) ;
85
+ p = intersect(lbnd, bisector) ;
86
+ if (p != (Site *)NULL)
87
+ {
88
+ PQdelete(lbnd) ;
89
+ PQinsert(lbnd, p, dist(p,newsite)) ;
90
+ }
91
+ lbnd = bisector ;
92
+ bisector = HEcreate(e, re) ;
93
+ ELinsert(lbnd, bisector) ;
94
+ p = intersect(bisector, rbnd) ;
95
+ if (p != (Site *)NULL)
96
+ {
97
+ PQinsert(bisector, p, dist(p,newsite)) ;
98
+ }
99
+ newsite = (*nextsite)() ;
100
+ }
101
+ else if (!PQempty()) /* intersection is smallest */
102
+ {
103
+ lbnd = PQextractmin() ;
104
+ llbnd = ELleft(lbnd) ;
105
+ rbnd = ELright(lbnd) ;
106
+ rrbnd = ELright(rbnd) ;
107
+ bot = leftreg(lbnd) ;
108
+ top = rightreg(rbnd) ;
109
+ out_triple(bot, top, rightreg(lbnd)) ;
110
+ v = lbnd->vertex ;
111
+ makevertex(v) ;
112
+ endpoint(lbnd->ELedge, lbnd->ELpm, v);
113
+ endpoint(rbnd->ELedge, rbnd->ELpm, v) ;
114
+ ELdelete(lbnd) ;
115
+ PQdelete(rbnd) ;
116
+ ELdelete(rbnd) ;
117
+ pm = le ;
118
+ if (bot->coord.y > top->coord.y)
119
+ {
120
+ temp = bot ;
121
+ bot = top ;
122
+ top = temp ;
123
+ pm = re ;
124
+ }
125
+ e = bisect(bot, top) ;
126
+ bisector = HEcreate(e, pm) ;
127
+ ELinsert(llbnd, bisector) ;
128
+ endpoint(e, re-pm, v) ;
129
+ deref(v) ;
130
+ p = intersect(llbnd, bisector) ;
131
+ if (p != (Site *) NULL)
132
+ {
133
+ PQdelete(llbnd) ;
134
+ PQinsert(llbnd, p, dist(p,bot)) ;
135
+ }
136
+ p = intersect(bisector, rrbnd) ;
137
+ if (p != (Site *) NULL)
138
+ {
139
+ PQinsert(bisector, p, dist(p,bot)) ;
140
+ }
141
+ }
142
+ else
143
+ {
144
+ break ;
145
+ }
146
+ }
147
+
148
+ for( lbnd = ELright(getELleftend()) ;
149
+ lbnd != getELrightend() ;
150
+ lbnd = ELright(lbnd))
151
+ {
152
+ e = lbnd->ELedge ;
153
+ out_ep(e) ;
154
+ }
155
+ }
156
+
157
+
158
+
159
+
160
+ /*
161
+ * Static storage methods
162
+ */
163
+
164
+ /*** stores a triplet of point indices that comprise a Delaunay triangle ***/
165
+ static void
166
+ storeTriangulationTriplet(const int a, const int b, const int c)
167
+ {
168
+ VALUE trArray, triplet;
169
+
170
+ /* Create a new triplet from the three incoming points */
171
+ triplet = rb_ary_new2(3);
172
+
173
+ rb_ary_push(triplet, INT2FIX(a));
174
+ rb_ary_push(triplet, INT2FIX(b));
175
+ rb_ary_push(triplet, INT2FIX(c));
176
+
177
+ /* Get the existing raw triangulation */
178
+ trArray = rb_funcall(*(VALUE *)rubyvorState.comp, rb_intern("delaunay_triangulation_raw"), 0);
179
+
180
+ /* Add the new triplet to it */
181
+ rb_ary_push(trArray, triplet);
182
+ }
183
+
184
+
185
+ /*** stores a line defined by ax + by = c ***/
186
+ static void
187
+ storeLine(const float a, const float b, const float c)
188
+ {
189
+ VALUE lArray, line;
190
+
191
+ /* Create a new line from the three values */
192
+ line = rb_ary_new2(4);
193
+ rb_ary_push(line, ID2SYM(rb_intern("l")));
194
+ rb_ary_push(line, rb_float_new(a));
195
+ rb_ary_push(line, rb_float_new(b));
196
+ rb_ary_push(line, rb_float_new(c));
197
+
198
+ /* Get the existing raw voronoi diagram */
199
+ lArray = rb_funcall(*(VALUE *)rubyvorState.comp, rb_intern("voronoi_diagram_raw"), 0);
200
+
201
+ /* Add the new line to it */
202
+ rb_ary_push(lArray, line);
203
+ }
204
+
205
+
206
+ /***
207
+ * Stores a Voronoi segment which is a subsegment of line number l;
208
+ * with endpoints numbered v1 and v2. If v1 or v2 is -1, the line
209
+ * extends to infinity.
210
+ ***/
211
+ static void
212
+ storeEndpoint(const int l, const int v1, const int v2)
213
+ {
214
+ VALUE eArray, endpoint;
215
+
216
+ /* Create a new endpoint from the three values */
217
+ endpoint = rb_ary_new2(4);
218
+ rb_ary_push(endpoint, ID2SYM(rb_intern("e")));
219
+ rb_ary_push(endpoint, INT2FIX(l));
220
+ rb_ary_push(endpoint, INT2FIX(v1));
221
+ rb_ary_push(endpoint, INT2FIX(v2));
222
+
223
+ /* Get the existing raw voronoi diagram */
224
+ eArray = rb_funcall(*(VALUE *)rubyvorState.comp, rb_intern("voronoi_diagram_raw"), 0);
225
+
226
+ /* Add the new endpoint to it */
227
+ rb_ary_push(eArray, endpoint);
228
+ }
229
+
230
+
231
+ /*** stores a vertex at (a,b) ***/
232
+ static void
233
+ storeVertex(const float a, const float b)
234
+ {
235
+ VALUE vArray, vertex;
236
+
237
+ /* Create a new vertex from the coordinates */
238
+ vertex = rb_ary_new2(3);
239
+ rb_ary_push(vertex, ID2SYM(rb_intern("v")));
240
+ rb_ary_push(vertex, rb_float_new(a));
241
+ rb_ary_push(vertex, rb_float_new(b));
242
+
243
+ /* Get the existing raw voronoi diagram */
244
+ vArray = rb_funcall(*(VALUE *)rubyvorState.comp, rb_intern("voronoi_diagram_raw"), 0);
245
+
246
+ /* Add the new vertex to it */
247
+ rb_ary_push(vArray, vertex);
248
+ }
249
+
250
+
251
+ /***
252
+ * stores an input site at (x,y)
253
+ * TODO: redundant?
254
+ ***/
255
+ static void
256
+ storeSite(const float x, const float y)
257
+ {
258
+ VALUE sArray, site;
259
+
260
+ /* Create a new site from the coordinates */
261
+ site = rb_ary_new2(3);
262
+ rb_ary_push(site, ID2SYM(rb_intern("s")));
263
+ rb_ary_push(site, rb_float_new(x));
264
+ rb_ary_push(site, rb_float_new(y));
265
+
266
+ /* Get the existing raw voronoi diagram */
267
+ sArray = rb_funcall(*(VALUE *)rubyvorState.comp, rb_intern("voronoi_diagram_raw"), 0);
268
+
269
+ /* Add the new site to it */
270
+ rb_ary_push(sArray, site);
271
+ }
@@ -0,0 +1,16 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'ext')
3
+
4
+ require 'ruby_vor/version'
5
+ require 'ruby_vor/point'
6
+ require 'ruby_vor/priority_queue'
7
+ require 'ruby_vor/computation'
8
+ require 'ruby_vor/geo_ruby_extensions'
9
+ require 'ruby_vor/visualizer'
10
+
11
+ # Require ruby_vor.so last to clobber old from_points
12
+ require 'ruby_vor_c.so'
13
+
14
+ # DOC HERE
15
+ module RubyVor
16
+ end
@@ -0,0 +1,136 @@
1
+ module RubyVor
2
+ module VDDT
3
+ class Computation
4
+ attr_reader :points, :voronoi_diagram_raw, :delaunay_triangulation_raw, :no_neighbor_response
5
+
6
+ DIST_PROC = lambda{|a,b| a.distance_from(b)}
7
+ NO_NEIGHBOR_RESPONSES = [:raise, :use_all, :ignore]
8
+
9
+ # Create a computation from an existing set of raw data.
10
+ def initialize
11
+ @points = []
12
+
13
+ @voronoi_diagram_raw = []
14
+ @delaunay_triangulation_raw = []
15
+
16
+ @nn_graph = nil
17
+ @mst = nil
18
+
19
+ @no_neighbor_response = :use_all
20
+ end
21
+
22
+ # Decided what action to take if we find a point with no neighbors
23
+ # while computing the nn_graph.
24
+ #
25
+ # Choices are:
26
+ # * :use_all - include all other nodes as potential neighbors. This choice is the default, and can lead to higher big-O lower bounds when clustering.
27
+ # * :raise - raise an error
28
+ # * :ignore - leave this node disconnected from the rest of the graph.
29
+ def no_neighbor_response=(v)
30
+ @no_neighbor_response = v if NO_NEIGHBOR_RESPONSES.include?(v)
31
+ end
32
+
33
+ # Uses the nearest-neighbors information encapsulated by the Delaunay triangulation as a seed for clustering:
34
+ # We take the edges (there are O(n) of them, because defined by the triangulation and delete any edge above a certain distance.
35
+ #
36
+ # This method allows the caller to pass in a lambda for customizing distance calculations. For instance, to use a GeoRuby::SimpleFeatures::Point, one would:
37
+ # > cluster_by_distance(50 lambda{|a,b| a.spherical_distance(b, 3958.754)}) # this rejects edges greater than 50 miles, using spherical distance as a measure
38
+ def cluster_by_distance(max_distance, dist_proc=DIST_PROC)
39
+ clusters = []
40
+ nodes = (0..points.length-1).to_a
41
+ visited = [false] * points.length
42
+ graph = []
43
+ v = 0
44
+
45
+ nn_graph.each_with_index do |neighbors,nv|
46
+ graph[nv] = neighbors.select do |neighbor|
47
+ dist_proc[points[nv], points[neighbor]] < max_distance
48
+ end
49
+ end
50
+
51
+ until nodes.empty?
52
+ v = nodes.pop
53
+
54
+ next if visited[v]
55
+
56
+ cluster = []
57
+ visited[v] = true
58
+ cluster.push(v)
59
+
60
+ children = graph[v]
61
+ until children.nil? || children.empty?
62
+ cnode = children.pop
63
+ next if cnode.nil? || visited[cnode]
64
+
65
+ visited[cnode] = true
66
+ cluster.push(cnode)
67
+ children.concat(graph[cnode])
68
+ end
69
+
70
+ clusters.push(cluster)
71
+ end
72
+
73
+ clusters
74
+ end
75
+
76
+ def cluster_by_size(sizes=[], dist_proc=DIST_PROC)
77
+ # * Take MST, and
78
+ # 1. For n in sizes (taken in descending order), delete the n most expensive edges from MST
79
+ # * TODO: use a MaxHeap?
80
+ # 2. Determine remaining connectivity using BFS as above?
81
+ # 3. Some other more efficient connectivity test?
82
+ # 4. Return {n1 => cluster, n2 => cluster} for all n.
83
+
84
+ sized_clusters = sizes.inject({}) {|h,s| h[s] = []; h}
85
+
86
+
87
+ mst = minimum_spanning_tree(dist_proc).to_a
88
+ mst.sort!{|a,b|a.last <=> b.last}
89
+
90
+ sizes = sizes.sort
91
+ last_size = 0
92
+
93
+ while current_size = sizes.shift
94
+ current_size -= 1
95
+
96
+ # Remove edge count delta
97
+ delta = current_size - last_size
98
+ mst.slice!(-delta,delta)
99
+
100
+ graph = (1..points.length).to_a.map{|v| []}
101
+ visited = [nil] * points.length
102
+ clusters = []
103
+
104
+ mst.each do |edge,weight|
105
+ graph[edge[1]].push(edge[0])
106
+ graph[edge[0]].push(edge[1])
107
+ end
108
+
109
+ for node in 0..points.length-1
110
+ next if visited[node]
111
+
112
+ cluster = [node]
113
+ visited[node] = true
114
+
115
+ neighbors = graph[node]
116
+ while v = neighbors.pop
117
+ next if visited[v]
118
+
119
+ cluster.push(v)
120
+ visited[v] = true
121
+ neighbors.concat(graph[v])
122
+ end
123
+
124
+ clusters.push(cluster)
125
+ end
126
+
127
+ sized_clusters[current_size + 1] = clusters
128
+ last_size = current_size
129
+ end
130
+
131
+ sized_clusters
132
+ end
133
+
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,15 @@
1
+ if require 'geo_ruby'
2
+ # Let us call uniq on a set of Points or use one as a Hash key.
3
+ module GeoRuby
4
+ module SimpleFeatures
5
+ class Point < Geometry
6
+ def eql?(other)
7
+ self == other
8
+ end
9
+ def hash
10
+ [x,y,z,m].hash
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,32 @@
1
+ module RubyVor
2
+ class Point
3
+ attr_reader :x, :y
4
+ def initialize(x=0.0,y=0.0)
5
+ raise TypeError, 'Must be able to convert point values into floats' unless x.respond_to?(:to_f) && y.respond_to?(:to_f)
6
+ @x = x.to_f
7
+ @y = y.to_f
8
+ end
9
+
10
+ def <=>(p)
11
+ (@x != p.x) ? @x <=> p.x : @y <=> p.y
12
+ end
13
+
14
+ def <(p)
15
+ (self <=> p) == -1
16
+ end
17
+
18
+ def >(p)
19
+ (self <=> p) == 1
20
+ end
21
+
22
+ def ==(p)
23
+ @x == p.x && @y == p.y
24
+ end
25
+ alias :eql? :==
26
+
27
+ def to_s
28
+ "(#{@x},#{@y})"
29
+ end
30
+
31
+ end
32
+ end