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 +6 -0
- data/Manifest.txt +1 -0
- data/README.txt +32 -1
- data/ext/memory.c +5 -3
- data/ext/output.c +15 -19
- data/ext/rb_cComputation.c +145 -90
- data/ext/rb_cPoint.c +21 -0
- data/ext/rb_cPriorityQueue.c +3 -3
- data/ext/ruby_vor_c.c +20 -13
- data/ext/ruby_vor_c.h +10 -1
- data/ext/voronoi.c +24 -24
- data/lib/ruby_vor/computation.rb +35 -105
- data/lib/ruby_vor/point.rb +19 -4
- data/lib/ruby_vor/version.rb +1 -1
- data/lib/ruby_vor/visualizer.rb +217 -0
- data/lib/ruby_vor.rb +1 -0
- data/rubyvor.gemspec +4 -4
- data/test/test_computation.rb +42 -17
- metadata +4 -3
data/ext/ruby_vor_c.h
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
#ifndef __RUBY_VOR_H
|
2
2
|
#define __RUBY_VOR_H
|
3
3
|
|
4
|
+
#ifndef RB_LONG_BITS
|
5
|
+
#define RB_LONG_BITS sizeof(long)*8
|
6
|
+
#endif
|
7
|
+
|
8
|
+
#ifndef RB_HASH_FILTER
|
9
|
+
#define RB_HASH_FILTER ((2 << (RB_LONG_BITS / 2 - 1)) - 1)
|
10
|
+
#endif
|
11
|
+
|
4
12
|
extern VoronoiState rubyvorState;
|
5
13
|
|
6
14
|
/* Base modules */
|
@@ -11,7 +19,7 @@ VALUE RubyVor_rb_mVDDT;
|
|
11
19
|
VALUE RubyVor_rb_cComputation;
|
12
20
|
VALUE RubyVor_from_points(VALUE, VALUE);
|
13
21
|
VALUE RubyVor_nn_graph(VALUE);
|
14
|
-
|
22
|
+
VALUE RubyVor_minimum_spanning_tree(int, VALUE*, VALUE);
|
15
23
|
|
16
24
|
/* PriorityQueue */
|
17
25
|
VALUE RubyVor_rb_cPriorityQueue;
|
@@ -23,5 +31,6 @@ VALUE RubyVor_heapify(VALUE);
|
|
23
31
|
/* Point */
|
24
32
|
VALUE RubyVor_rb_cPoint;
|
25
33
|
VALUE RubyVor_distance_from(VALUE, VALUE);
|
34
|
+
VALUE RubyVor_point_hash(VALUE);
|
26
35
|
|
27
36
|
#endif
|
data/ext/voronoi.c
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
#include <vdefs.h>
|
6
6
|
#include <stdio.h>
|
7
7
|
|
8
|
-
|
8
|
+
/* Static method definitions: C -> Ruby storage methods. */
|
9
9
|
static void storeTriangulationTriplet(const int, const int, const int);
|
10
10
|
static void storeLine(const float, const float, const float);
|
11
11
|
static void storeEndpoint(const int, const int, const int);
|
@@ -20,7 +20,7 @@ static void storeSite(const float, const float);
|
|
20
20
|
|
21
21
|
void initialize_state(int debug)
|
22
22
|
{
|
23
|
-
|
23
|
+
/* Set up our initial state */
|
24
24
|
rubyvorState.debug = debug;
|
25
25
|
rubyvorState.plot = 0;
|
26
26
|
rubyvorState.nsites = 0;
|
@@ -32,13 +32,13 @@ void initialize_state(int debug)
|
|
32
32
|
rubyvorState.storeV = storeVertex;
|
33
33
|
rubyvorState.storeS = storeSite;
|
34
34
|
|
35
|
-
|
35
|
+
/* Initialize the Site Freelist */
|
36
36
|
freeinit(&(rubyvorState.sfl), sizeof(Site)) ;
|
37
37
|
|
38
|
-
|
38
|
+
/* Initialize the geometry module */
|
39
39
|
geominit() ;
|
40
40
|
|
41
|
-
|
41
|
+
/* TODO: remove C plot references */
|
42
42
|
if (rubyvorState.plot)
|
43
43
|
plotinit();
|
44
44
|
}
|
@@ -52,7 +52,7 @@ voronoi(Site *(*nextsite)(void))
|
|
52
52
|
Halfedge * lbnd, * rbnd, * llbnd, * rrbnd, * bisector ;
|
53
53
|
Edge * e ;
|
54
54
|
|
55
|
-
c = 0;
|
55
|
+
newintstar.x = newintstar.y = c = 0;
|
56
56
|
|
57
57
|
PQinitialize() ;
|
58
58
|
rubyvorState.bottomsite = (*nextsite)() ;
|
@@ -155,9 +155,9 @@ voronoi(Site *(*nextsite)(void))
|
|
155
155
|
|
156
156
|
|
157
157
|
|
158
|
-
|
159
|
-
|
160
|
-
|
158
|
+
/*
|
159
|
+
* Static storage methods
|
160
|
+
*/
|
161
161
|
|
162
162
|
/*** stores a triplet of point indices that comprise a Delaunay triangle ***/
|
163
163
|
static void
|
@@ -165,17 +165,17 @@ storeTriangulationTriplet(const int a, const int b, const int c)
|
|
165
165
|
{
|
166
166
|
VALUE trArray, triplet;
|
167
167
|
|
168
|
-
|
168
|
+
/* Create a new triplet from the three incoming points */
|
169
169
|
triplet = rb_ary_new2(3);
|
170
170
|
|
171
171
|
rb_ary_push(triplet, INT2FIX(a));
|
172
172
|
rb_ary_push(triplet, INT2FIX(b));
|
173
173
|
rb_ary_push(triplet, INT2FIX(c));
|
174
174
|
|
175
|
-
|
175
|
+
/* Get the existing raw triangulation */
|
176
176
|
trArray = rb_funcall(*(VALUE *)rubyvorState.comp, rb_intern("delaunay_triangulation_raw"), 0);
|
177
177
|
|
178
|
-
|
178
|
+
/* Add the new triplet to it */
|
179
179
|
rb_ary_push(trArray, triplet);
|
180
180
|
}
|
181
181
|
|
@@ -186,17 +186,17 @@ storeLine(const float a, const float b, const float c)
|
|
186
186
|
{
|
187
187
|
VALUE lArray, line;
|
188
188
|
|
189
|
-
|
189
|
+
/* Create a new line from the three values */
|
190
190
|
line = rb_ary_new2(4);
|
191
191
|
rb_ary_push(line, ID2SYM(rb_intern("l")));
|
192
192
|
rb_ary_push(line, rb_float_new(a));
|
193
193
|
rb_ary_push(line, rb_float_new(b));
|
194
194
|
rb_ary_push(line, rb_float_new(c));
|
195
195
|
|
196
|
-
|
196
|
+
/* Get the existing raw voronoi diagram */
|
197
197
|
lArray = rb_funcall(*(VALUE *)rubyvorState.comp, rb_intern("voronoi_diagram_raw"), 0);
|
198
198
|
|
199
|
-
|
199
|
+
/* Add the new line to it */
|
200
200
|
rb_ary_push(lArray, line);
|
201
201
|
}
|
202
202
|
|
@@ -211,17 +211,17 @@ storeEndpoint(const int l, const int v1, const int v2)
|
|
211
211
|
{
|
212
212
|
VALUE eArray, endpoint;
|
213
213
|
|
214
|
-
|
214
|
+
/* Create a new endpoint from the three values */
|
215
215
|
endpoint = rb_ary_new2(4);
|
216
216
|
rb_ary_push(endpoint, ID2SYM(rb_intern("e")));
|
217
217
|
rb_ary_push(endpoint, INT2FIX(l));
|
218
218
|
rb_ary_push(endpoint, INT2FIX(v1));
|
219
219
|
rb_ary_push(endpoint, INT2FIX(v2));
|
220
220
|
|
221
|
-
|
221
|
+
/* Get the existing raw voronoi diagram */
|
222
222
|
eArray = rb_funcall(*(VALUE *)rubyvorState.comp, rb_intern("voronoi_diagram_raw"), 0);
|
223
223
|
|
224
|
-
|
224
|
+
/* Add the new endpoint to it */
|
225
225
|
rb_ary_push(eArray, endpoint);
|
226
226
|
}
|
227
227
|
|
@@ -232,16 +232,16 @@ storeVertex(const float a, const float b)
|
|
232
232
|
{
|
233
233
|
VALUE vArray, vertex;
|
234
234
|
|
235
|
-
|
235
|
+
/* Create a new vertex from the coordinates */
|
236
236
|
vertex = rb_ary_new2(3);
|
237
237
|
rb_ary_push(vertex, ID2SYM(rb_intern("v")));
|
238
238
|
rb_ary_push(vertex, rb_float_new(a));
|
239
239
|
rb_ary_push(vertex, rb_float_new(b));
|
240
240
|
|
241
|
-
|
241
|
+
/* Get the existing raw voronoi diagram */
|
242
242
|
vArray = rb_funcall(*(VALUE *)rubyvorState.comp, rb_intern("voronoi_diagram_raw"), 0);
|
243
243
|
|
244
|
-
|
244
|
+
/* Add the new vertex to it */
|
245
245
|
rb_ary_push(vArray, vertex);
|
246
246
|
}
|
247
247
|
|
@@ -255,15 +255,15 @@ storeSite(const float x, const float y)
|
|
255
255
|
{
|
256
256
|
VALUE sArray, site;
|
257
257
|
|
258
|
-
|
258
|
+
/* Create a new site from the coordinates */
|
259
259
|
site = rb_ary_new2(3);
|
260
260
|
rb_ary_push(site, ID2SYM(rb_intern("s")));
|
261
261
|
rb_ary_push(site, rb_float_new(x));
|
262
262
|
rb_ary_push(site, rb_float_new(y));
|
263
263
|
|
264
|
-
|
264
|
+
/* Get the existing raw voronoi diagram */
|
265
265
|
sArray = rb_funcall(*(VALUE *)rubyvorState.comp, rb_intern("voronoi_diagram_raw"), 0);
|
266
266
|
|
267
|
-
|
267
|
+
/* Add the new site to it */
|
268
268
|
rb_ary_push(sArray, site);
|
269
269
|
}
|
data/lib/ruby_vor/computation.rb
CHANGED
@@ -6,11 +6,14 @@ module RubyVor
|
|
6
6
|
DIST_PROC = lambda{|a,b| a.distance_from(b)}
|
7
7
|
|
8
8
|
# Create a computation from an existing set of raw data.
|
9
|
-
def initialize
|
10
|
-
@points =
|
11
|
-
@
|
12
|
-
@
|
9
|
+
def initialize
|
10
|
+
@points = []
|
11
|
+
@unique_points = []
|
12
|
+
@point_map = {}
|
13
|
+
@voronoi_diagram_raw = []
|
14
|
+
@delaunay_triangulation_raw = []
|
13
15
|
@nn_graph = nil
|
16
|
+
@mst = nil
|
14
17
|
end
|
15
18
|
|
16
19
|
# Uses the nearest-neighbors information encapsulated by the Delaunay triangulation as a seed for clustering:
|
@@ -30,6 +33,7 @@ module RubyVor
|
|
30
33
|
dist_proc[points[v], points[neighbor]] < max_distance
|
31
34
|
end
|
32
35
|
end
|
36
|
+
|
33
37
|
|
34
38
|
until nodes.empty?
|
35
39
|
v = nodes.pop
|
@@ -56,81 +60,10 @@ module RubyVor
|
|
56
60
|
clusters
|
57
61
|
end
|
58
62
|
|
59
|
-
# Computes the minimum spanning tree for given points, using the Delaunay triangulation as a seed.
|
60
|
-
#
|
61
|
-
# 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.
|
62
|
-
def minimum_spanning_tree(dist_proc=DIST_PROC)
|
63
|
-
nodes = []
|
64
|
-
q = RubyVor::PriorityQueue.new
|
65
|
-
(0..points.length-1).to_a.map do |n|
|
66
|
-
q.push([
|
67
|
-
n, # :node == 0
|
68
|
-
nil, # :parent == 1
|
69
|
-
nn_graph[n].clone, # :adjacency_list == 2
|
70
|
-
[], # :min_adjacency_list == 3
|
71
|
-
true # :in_q == 4
|
72
|
-
],
|
73
|
-
# Set min_distance of first node to 0, all others (effectively) to Infinity.
|
74
|
-
(n == 0) ? 0.0 : Float::MAX)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Adjacency list accces
|
78
|
-
nodes = q.data.clone
|
79
|
-
|
80
|
-
# We have now prepped to run through Prim's algorithm.
|
81
|
-
while latest_addition = q.pop
|
82
|
-
# check :in_q
|
83
|
-
latest_addition.data[4] = false
|
84
|
-
|
85
|
-
# check :parent
|
86
|
-
if latest_addition.data[1]
|
87
|
-
# push this node into :adjacency_list of :parent
|
88
|
-
latest_addition.data[1].data[3].push(latest_addition)
|
89
|
-
# push :parent into :adjacency_list of this node
|
90
|
-
latest_addition.data[3].push(latest_addition.data[1])
|
91
|
-
end
|
92
|
-
|
93
|
-
latest_addition.data[2].each do |adjacent|
|
94
|
-
# grab indexed node
|
95
|
-
adjacent = nodes[adjacent]
|
96
|
-
|
97
|
-
# check :in_q -- only look at new nodes
|
98
|
-
if adjacent.data[4]
|
99
|
-
# compare points by :node -- adjacent against current
|
100
|
-
adjacent_distance = dist_proc[ points[latest_addition.data[0]], points[adjacent.data[0]] ]
|
101
|
-
|
102
|
-
# if the new distance is better than our current priorty, exchange them
|
103
|
-
if adjacent_distance < adjacent.priority
|
104
|
-
# set new :parent
|
105
|
-
adjacent.data[1] = latest_addition
|
106
|
-
# update priority
|
107
|
-
adjacent.priority = adjacent_distance
|
108
|
-
# percolate up into correct position
|
109
|
-
q.percolate_up(adjacent.index)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
end
|
115
|
-
|
116
|
-
nodes.inject({}) do |h,adjacency_list|
|
117
|
-
v = adjacency_list.data[0]
|
118
|
-
adjacency_list.data[3].each do |node|
|
119
|
-
h[ (v < node.data[0]) ? [v,node.data[0]] : [node.data[0],v] ] ||= node.priority
|
120
|
-
end
|
121
|
-
h
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
63
|
def cluster_by_size(sizes=[], dist_proc=DIST_PROC)
|
126
|
-
# TODO
|
127
|
-
# * (DONE) Create a minimum_spanning_tree routine to:
|
128
|
-
# 1. (DONE) compute weights (should be done for us in C?)
|
129
|
-
# 2. (DONE) Use Prim's algorithm for computation
|
130
|
-
#
|
131
64
|
# * Take MST, and
|
132
65
|
# 1. For n in sizes (taken in descending order), delete the n most expensive edges from MST
|
133
|
-
# * use a MaxHeap?
|
66
|
+
# * TODO: use a MaxHeap?
|
134
67
|
# 2. Determine remaining connectivity using BFS as above?
|
135
68
|
# 3. Some other more efficient connectivity test?
|
136
69
|
# 4. Return {n1 => cluster, n2 => cluster} for all n.
|
@@ -140,54 +73,51 @@ module RubyVor
|
|
140
73
|
|
141
74
|
mst = minimum_spanning_tree(dist_proc).to_a
|
142
75
|
mst.sort!{|a,b|a.last <=> b.last}
|
143
|
-
|
76
|
+
|
144
77
|
sizes = sizes.sort
|
145
78
|
last_size = 0
|
79
|
+
|
146
80
|
while current_size = sizes.shift
|
147
81
|
current_size -= 1
|
148
|
-
|
82
|
+
|
149
83
|
# Remove edge count delta
|
150
84
|
delta = current_size - last_size
|
151
85
|
mst.slice!(-delta,delta)
|
152
|
-
|
153
|
-
visited = [nil] * points.length
|
154
86
|
|
87
|
+
graph = (1..points.length).to_a.map{|v| []}
|
88
|
+
visited = [nil] * points.length
|
89
|
+
clusters = []
|
155
90
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
next if !visited[i].nil? && visited[i] == visited[j]
|
161
|
-
|
162
|
-
if visited[i] && visited[j]
|
163
|
-
visited[i].concat(visited[j])
|
164
|
-
visited[i].uniq!
|
165
|
-
visited[j].each{|v| visited[v] = visited[i]}
|
166
|
-
visited[j] = visited[i]
|
167
|
-
else
|
168
|
-
cluster = visited[i] || visited[j] || []
|
169
|
-
cluster.push(i) unless visited[i]
|
170
|
-
cluster.push(j) unless visited[j]
|
171
|
-
visited[i] = visited[j] = cluster
|
172
|
-
end
|
91
|
+
mst.each do |edge,weight|
|
92
|
+
graph[edge[1]].push(edge[0])
|
93
|
+
graph[edge[0]].push(edge[1])
|
173
94
|
end
|
174
95
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
96
|
+
for node in 0..points.length-1
|
97
|
+
next if visited[node]
|
98
|
+
|
99
|
+
cluster = [node]
|
100
|
+
visited[node] = true
|
101
|
+
|
102
|
+
neighbors = graph[node]
|
103
|
+
while v = neighbors.pop
|
104
|
+
next if visited[v]
|
105
|
+
|
106
|
+
cluster.push(v)
|
107
|
+
visited[v] = true
|
108
|
+
neighbors.concat(graph[v])
|
179
109
|
end
|
110
|
+
|
111
|
+
clusters.push(cluster)
|
180
112
|
end
|
181
|
-
|
182
|
-
sized_clusters[current_size + 1] =
|
183
|
-
|
113
|
+
|
114
|
+
sized_clusters[current_size + 1] = clusters
|
184
115
|
last_size = current_size
|
185
116
|
end
|
186
117
|
|
187
118
|
sized_clusters
|
188
119
|
end
|
189
120
|
|
190
|
-
|
191
121
|
end
|
192
122
|
end
|
193
123
|
end
|
data/lib/ruby_vor/point.rb
CHANGED
@@ -2,16 +2,31 @@ module RubyVor
|
|
2
2
|
class Point
|
3
3
|
attr_reader :x, :y
|
4
4
|
def initialize(x=0.0,y=0.0)
|
5
|
-
|
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
|
6
20
|
end
|
7
21
|
|
8
22
|
def ==(p)
|
9
23
|
@x == p.x && @y == p.y
|
10
24
|
end
|
11
25
|
alias :eql? :==
|
12
|
-
|
13
|
-
|
14
|
-
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"(#{@x},#{@y})"
|
29
|
+
end
|
15
30
|
|
16
31
|
end
|
17
32
|
end
|
data/lib/ruby_vor/version.rb
CHANGED
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'libxml'
|
2
|
+
module RubyVor
|
3
|
+
class Visualizer
|
4
|
+
|
5
|
+
COLORS = %w{black blue red purple gray lime yellow orange pink}
|
6
|
+
|
7
|
+
include LibXML
|
8
|
+
|
9
|
+
def self.make_svg(computation, opts={})
|
10
|
+
@opts = opts = {
|
11
|
+
:name => 'vddt.svg',
|
12
|
+
:triangulation => true,
|
13
|
+
:voronoi_diagram => false,
|
14
|
+
:mst => false,
|
15
|
+
:cbd => false,
|
16
|
+
}.merge(opts)
|
17
|
+
|
18
|
+
line_colors = COLORS.clone
|
19
|
+
|
20
|
+
doc = XML::Document.new()
|
21
|
+
|
22
|
+
doc.root = XML::Node.new('svg')
|
23
|
+
doc.root['xmlns'] = 'http://www.w3.org/2000/svg'
|
24
|
+
doc.root['xml:space'] = 'preserve'
|
25
|
+
|
26
|
+
max_x = 0
|
27
|
+
min_x = Float::MAX
|
28
|
+
max_y = 0
|
29
|
+
min_y = Float::MAX
|
30
|
+
pmax_x = 0
|
31
|
+
pmin_x = Float::MAX
|
32
|
+
pmax_y = 0
|
33
|
+
pmin_y = Float::MAX
|
34
|
+
|
35
|
+
computation.points.each do |point|
|
36
|
+
max_x = point.x if point.x > max_x
|
37
|
+
min_x = point.x if point.x < min_x
|
38
|
+
max_y = point.y if point.y > max_y
|
39
|
+
min_y = point.y if point.y < min_y
|
40
|
+
pmax_x = point.x if point.x > pmax_x
|
41
|
+
pmin_x = point.x if point.x < pmin_x
|
42
|
+
pmax_y = point.y if point.y > pmax_y
|
43
|
+
pmin_y = point.y if point.y < pmin_y
|
44
|
+
end
|
45
|
+
|
46
|
+
if opts[:voronoi_diagram]
|
47
|
+
computation.voronoi_diagram_raw.each do |item|
|
48
|
+
if item.first == :v
|
49
|
+
max_x = item[1] if item[1] > max_x
|
50
|
+
min_x = item[1] if item[1] < min_x
|
51
|
+
max_y = item[2] if item[2] > max_y
|
52
|
+
min_y = item[2] if item[2] < min_y
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
opts[:offset_x] = -1.0 * min_x + 20
|
58
|
+
opts[:offset_y] = -1.0 * min_y + 20
|
59
|
+
|
60
|
+
if opts[:triangulation]
|
61
|
+
# Draw in the triangulation
|
62
|
+
color = line_colors.shift
|
63
|
+
computation.delaunay_triangulation_raw.each do |triplet|
|
64
|
+
for i in 0..2
|
65
|
+
line = line_from_points(computation.points[triplet[i % 2 + 1]], computation.points[triplet[i & 6]])
|
66
|
+
line['style'] = "stroke:#{color};stroke-width:1;"
|
67
|
+
doc.root << line
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if opts[:mst]
|
73
|
+
color = line_colors.shift
|
74
|
+
computation.minimum_spanning_tree.each do |edge, weight|
|
75
|
+
line = line_from_points(computation.points[edge[0]], computation.points[edge[1]])
|
76
|
+
line['style'] = "stroke:#{color};stroke-width:1;"
|
77
|
+
doc.root << line
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if opts[:cbd]
|
82
|
+
mst = computation.minimum_spanning_tree
|
83
|
+
|
84
|
+
computation.cluster_by_distance(opts[:cbd]).each do |cluster|
|
85
|
+
|
86
|
+
color = new_color
|
87
|
+
min_dist = Float::MAX
|
88
|
+
set_min = false
|
89
|
+
cluster.each do |a|
|
90
|
+
cluster.each do |b|
|
91
|
+
next if a == b
|
92
|
+
k = a < b ? [a,b] : [b,a]
|
93
|
+
next unless mst.has_key?(k)
|
94
|
+
|
95
|
+
if mst[k] < min_dist
|
96
|
+
min_dist = mst[k]
|
97
|
+
set_min = true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
min_dist = 10 unless set_min
|
103
|
+
|
104
|
+
|
105
|
+
cluster.each do |v|
|
106
|
+
node = circle_from_point(computation.points[v])
|
107
|
+
node['r'] = (min_dist + 10).to_s
|
108
|
+
node['style'] = "fill:#{color};stroke:#{color};"
|
109
|
+
node['opacity'] = '0.4'
|
110
|
+
|
111
|
+
doc.root << node
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
if opts[:voronoi_diagram]
|
118
|
+
voronoi_vertices = []
|
119
|
+
draw_lines = []
|
120
|
+
draw_points = []
|
121
|
+
line_functions = []
|
122
|
+
|
123
|
+
xcut = (pmax_x + pmin_x) / 2.0
|
124
|
+
ycut = (pmax_y + pmin_y) / 2.0
|
125
|
+
|
126
|
+
color = line_colors.shift
|
127
|
+
|
128
|
+
computation.voronoi_diagram_raw.each do |item|
|
129
|
+
case item.first
|
130
|
+
when :v
|
131
|
+
# Draw a voronoi vertex
|
132
|
+
v = RubyVor::Point.new(item[1], item[2])
|
133
|
+
voronoi_vertices.push(v)
|
134
|
+
node = circle_from_point(v)
|
135
|
+
node['fill'] = 'red'
|
136
|
+
node['r'] = '2'
|
137
|
+
node['stroke'] = 'black'
|
138
|
+
node['stroke-width'] = '1'
|
139
|
+
|
140
|
+
draw_points << node
|
141
|
+
when :l
|
142
|
+
# :l a b c --> ax + by = c
|
143
|
+
a = item[1]
|
144
|
+
b = item[2]
|
145
|
+
c = item[3]
|
146
|
+
line_functions.push({ :y => lambda{|x| (c - a * x) / b},
|
147
|
+
:x => lambda{|y| (c - b * y) / a} })
|
148
|
+
when :e
|
149
|
+
if item[2] == -1 || item[3] == -1
|
150
|
+
from_vertex = voronoi_vertices[item[2] == -1 ? item[3] : item[2]]
|
151
|
+
|
152
|
+
next if from_vertex < RubyVor::Point.new(0,0)
|
153
|
+
|
154
|
+
if item[2] == -1
|
155
|
+
inf_vertex = RubyVor::Point.new(0, line_functions[item[1]][:y][0])
|
156
|
+
else
|
157
|
+
inf_vertex = RubyVor::Point.new(max_x, line_functions[item[1]][:y][max_x])
|
158
|
+
end
|
159
|
+
|
160
|
+
line = line_from_points(from_vertex, inf_vertex)
|
161
|
+
else
|
162
|
+
line = line_from_points(voronoi_vertices[item[2]], voronoi_vertices[item[3]])
|
163
|
+
end
|
164
|
+
|
165
|
+
line['style'] = "stroke:#{color};stroke-width:1;"
|
166
|
+
draw_lines << line
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
draw_lines.each {|l| doc.root << l}
|
172
|
+
draw_points.each {|p| doc.root << p}
|
173
|
+
end
|
174
|
+
|
175
|
+
# Now draw in nodes
|
176
|
+
computation.points.each do |point|
|
177
|
+
node = circle_from_point(point)
|
178
|
+
node['fill'] = 'lime'
|
179
|
+
|
180
|
+
doc.root << node
|
181
|
+
end
|
182
|
+
|
183
|
+
doc.root['width'] = (max_x + opts[:offset_x] + 50).to_s
|
184
|
+
doc.root['height'] = (max_y + opts[:offset_y] + 50).to_s
|
185
|
+
|
186
|
+
doc.save(opts[:name] || 'vddt.svg')
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
def self.line_from_points(a, b)
|
191
|
+
line = XML::Node.new('line')
|
192
|
+
line['x1'] = (a.x + @opts[:offset_x] + 10).to_s
|
193
|
+
line['y1'] = (a.y + @opts[:offset_y] + 10).to_s
|
194
|
+
line['x2'] = (b.x + @opts[:offset_x] + 10).to_s
|
195
|
+
line['y2'] = (b.y + @opts[:offset_y] + 10).to_s
|
196
|
+
line
|
197
|
+
end
|
198
|
+
|
199
|
+
def self.circle_from_point(point)
|
200
|
+
node = XML::Node.new('circle')
|
201
|
+
node['cx'] = (point.x + @opts[:offset_x] + 10).to_s
|
202
|
+
node['cy'] = (point.y + @opts[:offset_y] + 10).to_s
|
203
|
+
node['r'] = 5.to_s
|
204
|
+
node['stroke'] = 'black'
|
205
|
+
node['stroke-width'] = 2.to_s
|
206
|
+
node
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.new_color
|
210
|
+
a = rand(256)
|
211
|
+
b = rand(256)# ^ a
|
212
|
+
c = rand(256)# ^ (a | b)
|
213
|
+
|
214
|
+
"rgb(#{[a,b,c].sort{|k,l| rand(3)-1}.join(',')})"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
data/lib/ruby_vor.rb
CHANGED
data/rubyvor.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{rubyvor}
|
5
|
-
s.version = "0.0.
|
5
|
+
s.version = "0.0.8"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Brendan Ribera"]
|
9
|
-
s.date = %q{2008-12-
|
9
|
+
s.date = %q{2008-12-23}
|
10
10
|
s.description = %q{RubyVor provides efficient computation of Voronoi diagrams and Delaunay triangulation for a set of Ruby points. It is intended to function as a complemenet to GeoRuby. These structures can be used to compute a nearest-neighbor graph for a set of points. This graph can in turn be used for proximity-based clustering of the input points.}
|
11
11
|
s.email = ["brendan.ribera+rubyvor@gmail.com"]
|
12
12
|
s.extensions = ["ext/extconf.rb"]
|
13
13
|
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
|
14
|
-
s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "ext/Doc", "ext/edgelist.c", "ext/extconf.rb", "ext/geometry.c", "ext/heap.c", "ext/memory.c", "ext/output.c", "ext/rb_cComputation.c", "ext/rb_cPoint.c", "ext/rb_cPriorityQueue.c", "ext/ruby_vor_c.c", "ext/ruby_vor_c.h", "ext/vdefs.h", "ext/voronoi.c", "lib/ruby_vor.rb", "lib/ruby_vor/computation.rb", "lib/ruby_vor/geo_ruby_extensions.rb", "lib/ruby_vor/point.rb", "lib/ruby_vor/priority_queue.rb", "lib/ruby_vor/version.rb", "rubyvor.gemspec", "test/test_computation.rb", "test/test_priority_queue.rb", "test/test_voronoi_interface.rb"]
|
14
|
+
s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "ext/Doc", "ext/edgelist.c", "ext/extconf.rb", "ext/geometry.c", "ext/heap.c", "ext/memory.c", "ext/output.c", "ext/rb_cComputation.c", "ext/rb_cPoint.c", "ext/rb_cPriorityQueue.c", "ext/ruby_vor_c.c", "ext/ruby_vor_c.h", "ext/vdefs.h", "ext/voronoi.c", "lib/ruby_vor.rb", "lib/ruby_vor/computation.rb", "lib/ruby_vor/geo_ruby_extensions.rb", "lib/ruby_vor/point.rb", "lib/ruby_vor/priority_queue.rb", "lib/ruby_vor/version.rb", "lib/ruby_vor/visualizer.rb", "rubyvor.gemspec", "test/test_computation.rb", "test/test_priority_queue.rb", "test/test_voronoi_interface.rb"]
|
15
15
|
s.has_rdoc = true
|
16
16
|
s.homepage = %q{http://github.com/bribera/rubyvor}
|
17
17
|
s.rdoc_options = ["--main", "README.txt"]
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.rubyforge_project = %q{rubyvor}
|
20
20
|
s.rubygems_version = %q{1.3.1}
|
21
21
|
s.summary = %q{RubyVor provides efficient computation of Voronoi diagrams and Delaunay triangulation for a set of Ruby points}
|
22
|
-
s.test_files = ["test/
|
22
|
+
s.test_files = ["test/test_computation.rb", "test/test_priority_queue.rb", "test/test_voronoi_interface.rb"]
|
23
23
|
|
24
24
|
if s.respond_to? :specification_version then
|
25
25
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|