iconofthestoneage-doodl 0.0.2 → 0.0.4
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/lib/app/selfrunning.rb +185 -0
- data/lib/app/simple_app.rb +49 -0
- data/lib/app/simple_controller.rb +584 -0
- data/lib/app/simple_model.rb +292 -0
- data/lib/app/simple_view.rb +148 -0
- data/lib/breadth_first_search.rb +69 -0
- data/lib/connected_components.rb +29 -0
- data/lib/depth_first_search.rb +73 -0
- data/lib/edge.rb +57 -0
- data/lib/graph.rb +365 -0
- data/lib/graph_canvas.rb +187 -0
- data/lib/graph_generator.rb +121 -0
- data/lib/jruby/renderer.rb +413 -0
- data/lib/layout/collapse_layout.rb +23 -0
- data/lib/layout/fr_layout.rb +105 -0
- data/lib/layout/isom_layout.rb +77 -0
- data/lib/layout/kk_layout.rb +203 -0
- data/lib/layout/layout.rb +240 -0
- data/lib/layout/morph_layout.rb +65 -0
- data/lib/node.rb +57 -0
- data/lib/shortest_path/all_pair.rb +35 -0
- data/lib/shortest_path/bellman_ford.rb +60 -0
- data/lib/shortest_path/dijkstra.rb +74 -0
- data/lib/shortest_path/floyd_warshall.rb +68 -0
- data/lib/shortest_path/johnson_all_pair.rb +64 -0
- data/lib/shortest_path/single_source.rb +32 -0
- data/spec/breadth_first_search_spec.rb +145 -0
- data/spec/connected_components_spec.rb +50 -0
- data/spec/depth_first_search_spec.rb +89 -0
- data/spec/edge_spec.rb +58 -0
- data/spec/graph_generator_spec.rb +277 -0
- data/spec/graph_spec.rb +269 -0
- data/spec/jruby/renderer_spec.rb +214 -0
- data/spec/layout/layout_spec.rb +146 -0
- data/spec/node_spec.rb +179 -0
- data/spec/rspec_helper.rb +9 -0
- data/spec/rspec_suite.rb +12 -0
- data/spec/shortest_path/bellman_ford_spec.rb +101 -0
- data/spec/shortest_path/dijkstra_spec.rb +133 -0
- data/spec/shortest_path/floyd_warshall_spec.rb +84 -0
- data/spec/shortest_path/johnson_all_pair_spec.rb +90 -0
- metadata +43 -2
@@ -0,0 +1,292 @@
|
|
1
|
+
require "observer"
|
2
|
+
|
3
|
+
require "graph"
|
4
|
+
require "layout/morph_layout"
|
5
|
+
require "layout/collapse_layout"
|
6
|
+
require "layout/fr_layout"
|
7
|
+
require "layout/kk_layout"
|
8
|
+
require "layout/isom_layout"
|
9
|
+
require "layout/layout"
|
10
|
+
require "shortest_path/dijkstra"
|
11
|
+
|
12
|
+
class SimpleModel
|
13
|
+
include Observable
|
14
|
+
|
15
|
+
attr_reader :layout, :graph
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@graph_type = DirectedGraph
|
19
|
+
end
|
20
|
+
|
21
|
+
def view=(view)
|
22
|
+
@view = view
|
23
|
+
end
|
24
|
+
|
25
|
+
#Graph
|
26
|
+
def new_graph
|
27
|
+
@graph = @graph_type.new
|
28
|
+
@graph.add_observer(self)
|
29
|
+
@graph.attach_node_data(:is_clicked?, Hash.new(false))
|
30
|
+
@graph.attach_edge_data(:is_clicked?, Hash.new(false))
|
31
|
+
@layout.graph = @graph
|
32
|
+
end
|
33
|
+
|
34
|
+
def new_empty_graph
|
35
|
+
new_graph
|
36
|
+
@graph.notify_observers(nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
def new_connected_graph(size)
|
40
|
+
$LOG.debug("new_connected_graph called on #{self}")
|
41
|
+
self.new_graph
|
42
|
+
@graph.gen_connected_graph(size)
|
43
|
+
end
|
44
|
+
|
45
|
+
def new_random_graph(node_number)
|
46
|
+
$LOG.debug("new_random_graph called on #{self}")
|
47
|
+
self.new_graph
|
48
|
+
@graph.gen_random_graph(node_number)
|
49
|
+
end
|
50
|
+
|
51
|
+
def new_ring_graph(node_number)
|
52
|
+
$LOG.debug("new_ring_graph called on #{self}")
|
53
|
+
self.new_graph
|
54
|
+
@graph.gen_ring_graph(node_number)
|
55
|
+
end
|
56
|
+
|
57
|
+
def new_binary_tree(height)
|
58
|
+
$LOG.debug("new_bintree_graph called on #{self}")
|
59
|
+
self.new_graph
|
60
|
+
@graph.gen_binary_tree(height)
|
61
|
+
end
|
62
|
+
|
63
|
+
def new_mesh_graph(rows)
|
64
|
+
$LOG.debug("new_mesh_graph called on #{self}")
|
65
|
+
self.new_graph
|
66
|
+
@graph.gen_mesh_graph(rows)
|
67
|
+
end
|
68
|
+
|
69
|
+
def new_linear_graph(size)
|
70
|
+
$LOG.debug("new_linear_graph called on #{self}")
|
71
|
+
self.new_graph
|
72
|
+
@graph.gen_linear_graph(size)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def add_node_at(x, y)
|
77
|
+
$LOG.debug("add_node_at #{x}, #{y} called on #{self}")
|
78
|
+
node = @graph.add_node(false)
|
79
|
+
@layout.set_location(node, x, y, true)
|
80
|
+
return node
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_nearest_node(x, y)
|
84
|
+
$LOG.debug("get_nearest_node #{x}, #{y} called on #{self}")
|
85
|
+
@layout.get_nearest_node(x, y)
|
86
|
+
end
|
87
|
+
|
88
|
+
def set_location(node, x, y)
|
89
|
+
@layout.set_location(node, x, y, true)
|
90
|
+
end
|
91
|
+
|
92
|
+
def del_node(node)
|
93
|
+
@layout.delete(node)
|
94
|
+
@graph.del_node(node, true)
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_edge(source, target)
|
98
|
+
@graph.add_edge(source, target, true)
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_edge(source, target)
|
102
|
+
edge = nil
|
103
|
+
@graph.each_adjacent_edge(source) do |e|
|
104
|
+
if e.target == target
|
105
|
+
edge = e
|
106
|
+
elsif (not e.directed?) and e.source == target and e.target == source
|
107
|
+
edge = e
|
108
|
+
end
|
109
|
+
end
|
110
|
+
return edge
|
111
|
+
end
|
112
|
+
|
113
|
+
def del_edge(edge)
|
114
|
+
@graph.del_edge(edge, true)
|
115
|
+
end
|
116
|
+
|
117
|
+
def select_node(node)
|
118
|
+
end
|
119
|
+
|
120
|
+
def deselect_node(node)
|
121
|
+
end
|
122
|
+
|
123
|
+
def select(edge)
|
124
|
+
end
|
125
|
+
|
126
|
+
def deselect(edge)
|
127
|
+
end
|
128
|
+
|
129
|
+
#Layout
|
130
|
+
def new_layout(layout)
|
131
|
+
@layout = layout
|
132
|
+
@layout.add_observer(self)
|
133
|
+
if @graph
|
134
|
+
@layout.graph = @graph
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def set_random_layout
|
139
|
+
self.new_layout(RandomLayout.new(@view.canvas))
|
140
|
+
end
|
141
|
+
|
142
|
+
def set_circle_layout
|
143
|
+
self.new_layout(CircleLayout.new(@view.canvas))
|
144
|
+
end
|
145
|
+
|
146
|
+
def set_fr_layout
|
147
|
+
self.new_layout(FRLayout.new(@view.canvas))
|
148
|
+
end
|
149
|
+
|
150
|
+
def set_kk_layout
|
151
|
+
self.new_layout(KKLayout.new(@view.canvas))
|
152
|
+
end
|
153
|
+
|
154
|
+
def set_morph_layout(target_layout)
|
155
|
+
if target_layout
|
156
|
+
self.new_layout(MorphLayout.new(@view.canvas, Kernel.const_get(target_layout)))
|
157
|
+
else
|
158
|
+
self.new_layout(MorphLayout.new(@view.canvas))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def set_collapse_layout
|
163
|
+
new_layout(CollapseLayout.new(@view.canvas))
|
164
|
+
end
|
165
|
+
|
166
|
+
def set_directed
|
167
|
+
@graph_type = DirectedGraph
|
168
|
+
end
|
169
|
+
|
170
|
+
def set_undirected
|
171
|
+
@graph_type = UndirectedGraph
|
172
|
+
end
|
173
|
+
|
174
|
+
def set_isom_layout
|
175
|
+
self.new_layout(ISOMLayout.new(@view.canvas))
|
176
|
+
end
|
177
|
+
|
178
|
+
def click(node)
|
179
|
+
@graph.node_data(:is_clicked?)[node] = true
|
180
|
+
changed
|
181
|
+
notify_observers(nil)
|
182
|
+
end
|
183
|
+
|
184
|
+
def unclick(node)
|
185
|
+
@graph.node_data(:is_clicked?)[node] = false
|
186
|
+
changed
|
187
|
+
notify_observers(nil)
|
188
|
+
end
|
189
|
+
|
190
|
+
def click_edge(edge)
|
191
|
+
@graph.edge_data(:is_clicked?)[edge] = true
|
192
|
+
changed
|
193
|
+
notify_observers(nil)
|
194
|
+
end
|
195
|
+
|
196
|
+
def unclick_edge(edge)
|
197
|
+
@graph.edge_data(:is_clicked?)[edge] = false
|
198
|
+
changed
|
199
|
+
notify_observers(nil)
|
200
|
+
end
|
201
|
+
|
202
|
+
def dsp(source, target)
|
203
|
+
stdata = @graph.attach_node_data(:start)
|
204
|
+
stdata[source] = true
|
205
|
+
endata = @graph.attach_node_data(:end)
|
206
|
+
endata[target] = true
|
207
|
+
|
208
|
+
dsp = DijkstraShortestPath.new(@graph, source)
|
209
|
+
npath = dsp.node_path_to(target)
|
210
|
+
epath = dsp.edge_path_to(target)
|
211
|
+
|
212
|
+
edata = @graph.attach_edge_data(:path)
|
213
|
+
epath.each do |edge|
|
214
|
+
edata[edge] = true
|
215
|
+
end
|
216
|
+
ndata = @graph.attach_node_data(:path)
|
217
|
+
npath.each do |node|
|
218
|
+
ndata[node] = true
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def dspt(source)
|
223
|
+
@graph.attach_node_data(:start)[source] = true
|
224
|
+
path = []
|
225
|
+
edata = @graph.attach_edge_data(:path)
|
226
|
+
ndata = @graph.attach_node_data(:end)
|
227
|
+
dijk = DijkstraShortestPath.new(@graph, source)
|
228
|
+
@graph.each_node do |node|
|
229
|
+
if (node != source)
|
230
|
+
dijk.edge_path_to(node).each { |edge| edata[edge] = true }
|
231
|
+
dijk.node_path_to(node).each { |node| ndata[node] = true }
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def count_nodes
|
237
|
+
index = 0
|
238
|
+
data = @graph.attach_node_data(:text)
|
239
|
+
@graph.each_node do |node|
|
240
|
+
data[node] = index.to_s
|
241
|
+
index += 1
|
242
|
+
end
|
243
|
+
index = 0
|
244
|
+
data = @graph.attach_edge_data(:text)
|
245
|
+
@graph.each_edge do |edge|
|
246
|
+
data[edge] = index.to_s
|
247
|
+
index += 1
|
248
|
+
end
|
249
|
+
changed
|
250
|
+
notify_observers(nil)
|
251
|
+
end
|
252
|
+
|
253
|
+
def rotate_layout(theta)
|
254
|
+
@layout.rotate(theta)
|
255
|
+
changed
|
256
|
+
notify_observers(nil)
|
257
|
+
end
|
258
|
+
|
259
|
+
def move_layout(x, y)
|
260
|
+
@layout.move(x, y)
|
261
|
+
changed
|
262
|
+
notify_observers(nil)
|
263
|
+
end
|
264
|
+
|
265
|
+
def center_x
|
266
|
+
@layout.center_x
|
267
|
+
changed
|
268
|
+
notify_observers(nil)
|
269
|
+
end
|
270
|
+
|
271
|
+
def center_y
|
272
|
+
@layout.center_y
|
273
|
+
changed
|
274
|
+
notify_observers(nil)
|
275
|
+
end
|
276
|
+
|
277
|
+
def center_xy
|
278
|
+
@layout.center_x
|
279
|
+
@layout.center_y
|
280
|
+
changed
|
281
|
+
notify_observers(nil)
|
282
|
+
end
|
283
|
+
|
284
|
+
#being a listener
|
285
|
+
def update(observable)
|
286
|
+
$LOG.debug("Observer #{self} being updated by #{observable}")
|
287
|
+
observable = nil if observable.is_a?(Graph)
|
288
|
+
changed
|
289
|
+
notify_observers(observable)
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
require "../lib/jruby/renderer"
|
4
|
+
|
5
|
+
include_class 'java.awt.geom.Rectangle2D'
|
6
|
+
include_class 'java.awt.BasicStroke'
|
7
|
+
include_class 'java.awt.Color'
|
8
|
+
|
9
|
+
class SimpleView
|
10
|
+
|
11
|
+
attr_writer :frame
|
12
|
+
attr_reader :canvas
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def canvas=(canvas)
|
19
|
+
@canvas = canvas
|
20
|
+
# @canvas.node_shape_function { |g, l, n| Rectangle2D::Double.new(l[n].x-10, l[n].y-10, 20, 20) }
|
21
|
+
@canvas.edge_stroke_function do |g, l, e|
|
22
|
+
if (g.has_edge_data_key?(:path) and g.edge_data(:path)[e])
|
23
|
+
BasicStroke.new(3)
|
24
|
+
else
|
25
|
+
BasicStroke.new(2)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@canvas.node_stroke_function do |g, l, n|
|
29
|
+
if (g.has_node_data_key?(:path) and g.node_data(:path)[n])
|
30
|
+
BasicStroke.new(3)
|
31
|
+
else
|
32
|
+
BasicStroke.new(2)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@canvas.node_stroke_color_function do |g, l, n|
|
36
|
+
if g.node_data(:is_clicked?) and g.node_data(:is_clicked?)[n]
|
37
|
+
Color::BLUE
|
38
|
+
elsif g.node_data(:start) and g.node_data(:start)[n]
|
39
|
+
Color::GREEN
|
40
|
+
elsif g.node_data(:end) and g.node_data(:end)[n]
|
41
|
+
Color::YELLOW
|
42
|
+
else
|
43
|
+
Color::WHITE
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@canvas.edge_stroke_color_function do |g, l, e|
|
47
|
+
if g.edge_data(:is_clicked?) and g.edge_data(:is_clicked?)[e]
|
48
|
+
Color::BLUE
|
49
|
+
else
|
50
|
+
Color::WHITE
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@canvas.instance_variable_set(:@arrow_head_stroke_color_function, @canvas.instance_variable_get(:@edge_stroke_color_function))
|
54
|
+
@canvas.instance_variable_set(:@arrow_head_fill_color_function, @canvas.instance_variable_get(:@edge_stroke_color_function))
|
55
|
+
@canvas.instance_variable_set(:@edge_stroke_function, @canvas.instance_variable_get(:@arrow_head_stroke_function))
|
56
|
+
@canvas.instance_variable_set(:@node_label_color_function, @canvas.instance_variable_get(:@node_stroke_color_function))
|
57
|
+
@canvas.requestFocusInWindow
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_key_listener(key_listener)
|
61
|
+
$LOG.debug("adding key_listener #{key_listener} on #{self}")
|
62
|
+
@canvas.addKeyListener(key_listener)
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_mouse_listener(mouse_listener)
|
66
|
+
$LOG.debug("adding mouse_listener #{mouse_listener} on #{self}")
|
67
|
+
@canvas.addMouseListener(mouse_listener)
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_mouse_motion_listener(mouse_motion_listener)
|
71
|
+
$LOG.debug("adding mouse_motion_listener #{mouse_motion_listener} on #{self}")
|
72
|
+
@canvas.addMouseMotionListener(mouse_motion_listener)
|
73
|
+
end
|
74
|
+
|
75
|
+
def remove_mouse_listener(mouse_listener)
|
76
|
+
$LOG.debug("removing mouse_listener #{mouse_listener} on #{self}")
|
77
|
+
@canvas.removeMouseListener(mouse_listener)
|
78
|
+
end
|
79
|
+
|
80
|
+
def remove_mouse_motion_listener(mouse_motion_listener)
|
81
|
+
$LOG.debug("removing mouse_motion_listener #{mouse_motion_listener} on #{self}")
|
82
|
+
@canvas.removeMouseMotionListener(mouse_motion_listener)
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_info_panel(info_text)
|
86
|
+
$LOG.debug("adding info panel with text: #{info_text}")
|
87
|
+
if (@frame.isAncestorOf(@infopanel))
|
88
|
+
@frame.getContentPane.remove(@infopanel)
|
89
|
+
end
|
90
|
+
@infopanel = javax.swing.JLabel.new(info_text)
|
91
|
+
@infopanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(2, 4, 2, 2))
|
92
|
+
@frame.getContentPane().add(@infopanel, java.awt.BorderLayout::NORTH)
|
93
|
+
@frame.validate
|
94
|
+
end
|
95
|
+
|
96
|
+
def remove_info_panel
|
97
|
+
@frame.getContentPane().remove(@infopanel)
|
98
|
+
@infopanel = nil
|
99
|
+
@frame.validate
|
100
|
+
end
|
101
|
+
|
102
|
+
def add_text_field(action_listener)
|
103
|
+
$LOG.debug("adding text field with action listener: #{action_listener}")
|
104
|
+
@textfield = javax.swing.JTextField.new
|
105
|
+
@textfield.addActionListener(action_listener)
|
106
|
+
@frame.getContentPane().add(@textfield, java.awt.BorderLayout::SOUTH)
|
107
|
+
@frame.validate
|
108
|
+
@textfield.requestFocusInWindow
|
109
|
+
end
|
110
|
+
|
111
|
+
def remove_text_field
|
112
|
+
$LOG.debug("removing text field")
|
113
|
+
@frame.remove(@textfield)
|
114
|
+
@textfield = nil
|
115
|
+
@frame.validate
|
116
|
+
@canvas.requestFocusInWindow
|
117
|
+
end
|
118
|
+
|
119
|
+
def toggle_paint_iterations
|
120
|
+
if @canvas.paint_iterations?
|
121
|
+
@canvas.paint_iterations = false
|
122
|
+
else
|
123
|
+
@canvas.paint_iterations = true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def width
|
128
|
+
@canvas.width
|
129
|
+
end
|
130
|
+
|
131
|
+
def height
|
132
|
+
@canvas.height
|
133
|
+
end
|
134
|
+
|
135
|
+
def update(layout)
|
136
|
+
$LOG.debug("Observer #{self} being updated with #{layout}")
|
137
|
+
if (layout)
|
138
|
+
@canvas.update_layout(layout)
|
139
|
+
else
|
140
|
+
@canvas.repaint
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def repaint
|
145
|
+
@canvas.repaint
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "graph"
|
4
|
+
|
5
|
+
require "facets/infinity"
|
6
|
+
|
7
|
+
module Doodl
|
8
|
+
|
9
|
+
class BreadthFirstSearch
|
10
|
+
|
11
|
+
attr_reader :dist, :prev, :list
|
12
|
+
|
13
|
+
def initialize(graph, source, visitor = nil)
|
14
|
+
raise ArgumentError unless (graph.is_a?(Graph) and graph.contains_node?(source))
|
15
|
+
@graph, @visitor = graph, visitor
|
16
|
+
@dist, @prev, @color = {}, {}, {}
|
17
|
+
@queue, @list = [], []
|
18
|
+
init_maps
|
19
|
+
main_algorithm(source)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def init_maps
|
25
|
+
@graph.each_node do |node|
|
26
|
+
visit(:init, @graph, node)
|
27
|
+
@color[node] = :white
|
28
|
+
@dist[node] = INFINITY
|
29
|
+
@prev[node] = node
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def visit_edge(edge)
|
34
|
+
visit(:examine_edge, @graph, edge)
|
35
|
+
if (@color[edge.target] == :white)
|
36
|
+
@color[edge.target] = :gray
|
37
|
+
@dist[edge.target] = @dist[edge.source] + 1
|
38
|
+
@prev[edge.target] = edge.source
|
39
|
+
@queue.push(edge.target)
|
40
|
+
visit(:discover, @graph, edge.target)
|
41
|
+
@list.push(edge.target)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def main_algorithm(source)
|
46
|
+
@color[source] = :gray
|
47
|
+
@dist[source] = 0
|
48
|
+
@queue.push(source)
|
49
|
+
visit(:discover, @graph, source)
|
50
|
+
@list.push(source)
|
51
|
+
until (@queue.empty?)
|
52
|
+
node = @queue.shift
|
53
|
+
visit(:examine_node, @graph, node)
|
54
|
+
@graph.each_adjacent_edge(node) do |edge|
|
55
|
+
visit_edge(edge)
|
56
|
+
end
|
57
|
+
@color[node] = :black
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def visit(position, *arg)
|
62
|
+
if @visitor.respond_to?(position)
|
63
|
+
@visitor.send(position, *arg)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|