red_grape 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/bin/juicer +23 -5
- data/bin/redgrape +2 -2
- data/bin/trellis +3 -3
- data/lib/red_grape/element.rb +4 -0
- data/lib/red_grape/graph.rb +4 -3
- data/lib/red_grape/graph_store.rb +29 -0
- data/lib/red_grape/pipe/base.rb +2 -4
- data/lib/red_grape/pipe/context.rb +72 -22
- data/lib/red_grape/pipe/order_pipe.rb +12 -0
- data/lib/red_grape/pipe/{paths_pipe.rb → path_pipe.rb} +12 -3
- data/lib/red_grape/pipe/scatter_pipe.rb +1 -1
- data/lib/red_grape/pipe/select_pipe.rb +8 -1
- data/lib/red_grape/tools/irg.rb +2 -2
- data/lib/red_grape/tools/{graph_store.rb → trellis.rb} +2 -21
- data/lib/red_grape.rb +4 -2
- data/public/cellar/graph.html +220 -0
- data/public/cellar/index.html +78 -0
- data/public/cellar/vertex.html +103 -0
- data/public/js/d3.v2.js +9387 -0
- data/public/js/jquery-1.7.2.min.js +4 -0
- data/test/test_graph_store.rb +35 -0
- data/test/test_on_the_nature_of_pipes.rb +1 -2
- data/test/test_order_pipe.rb +13 -0
- data/test/test_select_pipe.rb +9 -0
- data/test/test_traversal_patterns.rb +31 -31
- metadata +17 -7
- data/lib/red_grape/path_group.rb +0 -12
data/.gitignore
CHANGED
data/bin/juicer
CHANGED
@@ -1,14 +1,33 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
$: << File.expand_path('../../lib', __FILE__)
|
3
3
|
|
4
|
+
require 'optparse'
|
4
5
|
require 'sinatra'
|
5
6
|
require 'red_grape'
|
7
|
+
require 'red_grape/tools/trellis'
|
6
8
|
|
9
|
+
# interprete options
|
10
|
+
::Version = RedGrape::VERSION
|
11
|
+
COMMAND_LINE = "#{$0} #{ARGV.join(' ')}"
|
12
|
+
OPT = {}
|
13
|
+
# port:RedGrape::Tools::Trellis::DEFAULT_PORT
|
14
|
+
opts = OptionParser.new
|
15
|
+
opts.on('-p', '--port <port number>',
|
16
|
+
"Set the port number (default: #{OPT[:port]})"){|v| OPT[:port] = v.to_i}
|
17
|
+
opts.on_tail('-v', '--version', 'Show version'){puts(opts.ver); exit}
|
18
|
+
opts.on_tail('-h', '--help', 'Show this message'){puts(opts.help); exit}
|
19
|
+
opts.order! ARGV
|
20
|
+
|
21
|
+
# start
|
7
22
|
use Rack::Static, :urls => ['/cellar', '/js', '/css'], :root => 'public'
|
8
23
|
|
9
|
-
graphs =
|
10
|
-
|
11
|
-
|
24
|
+
graphs = nil
|
25
|
+
if OPT[:port]
|
26
|
+
graphs = RedGrape::Tools::Trellis.open OPT[:port]
|
27
|
+
else
|
28
|
+
graphs = RedGrape::GraphStore.new
|
29
|
+
graphs[:tinkergraph] = RedGrape.create_tinker_graph
|
30
|
+
end
|
12
31
|
|
13
32
|
get '/' do
|
14
33
|
{
|
@@ -57,7 +76,7 @@ get %r{/graphs/?$} do
|
|
57
76
|
{
|
58
77
|
version: RedGrape::VERSION,
|
59
78
|
name: 'Juicer: A Graph Server',
|
60
|
-
graphs: graphs.
|
79
|
+
graphs: graphs.graph_names
|
61
80
|
}.to_pretty_json
|
62
81
|
end
|
63
82
|
|
@@ -108,4 +127,3 @@ get %r{/graphs/([^/]+)/vertices/([^/]+)/(out_e|in_e|both_e|outE|inE|bothE)/?$} d
|
|
108
127
|
vertex.send(dir)[].map(&:to_h).to_pretty_json
|
109
128
|
end
|
110
129
|
end
|
111
|
-
|
data/bin/redgrape
CHANGED
@@ -3,14 +3,14 @@ $: << File.expand_path('../../lib', __FILE__)
|
|
3
3
|
|
4
4
|
require 'optparse'
|
5
5
|
require 'red_grape'
|
6
|
-
require 'red_grape/tools/
|
6
|
+
require 'red_grape/tools/trellis'
|
7
7
|
require 'red_grape/tools/irg'
|
8
8
|
|
9
9
|
# interprete options
|
10
10
|
::Version = RedGrape::VERSION
|
11
11
|
COMMAND_LINE = "#{$0} #{ARGV.join(' ')}"
|
12
12
|
OPT = {
|
13
|
-
port:RedGrape::Tools::
|
13
|
+
port:RedGrape::Tools::Trellis::DEFAULT_PORT
|
14
14
|
}
|
15
15
|
opts = OptionParser.new
|
16
16
|
opts.on('-p', '--port <port number>',
|
data/bin/trellis
CHANGED
@@ -3,13 +3,13 @@ $: << File.expand_path('../../lib', __FILE__)
|
|
3
3
|
|
4
4
|
require 'optparse'
|
5
5
|
require 'red_grape'
|
6
|
-
require 'red_grape/tools/
|
6
|
+
require 'red_grape/tools/trellis'
|
7
7
|
|
8
8
|
# interprete options
|
9
9
|
::Version = RedGrape::VERSION
|
10
10
|
COMMAND_LINE = "#{$0} #{ARGV.join(' ')}"
|
11
11
|
OPT = {
|
12
|
-
port:RedGrape::Tools::
|
12
|
+
port:RedGrape::Tools::Trellis::DEFAULT_PORT,
|
13
13
|
dbfile:'dbfile.bin'
|
14
14
|
}
|
15
15
|
opts = OptionParser.new
|
@@ -23,7 +23,7 @@ opts.order! ARGV
|
|
23
23
|
|
24
24
|
# start
|
25
25
|
Signal.trap :INT, 'EXIT'
|
26
|
-
RedGrape::Tools::
|
26
|
+
RedGrape::Tools::Trellis.start(OPT[:port], OPT[:dbfile]) do
|
27
27
|
puts <<-EOS
|
28
28
|
+=================+
|
29
29
|
| + T + |
|
data/lib/red_grape/element.rb
CHANGED
data/lib/red_grape/graph.rb
CHANGED
@@ -17,7 +17,7 @@ module RedGrape
|
|
17
17
|
|
18
18
|
# Returns the default graph which has 6 vertices and 6 edges.
|
19
19
|
def create_tinker_graph
|
20
|
-
self.new do |g|
|
20
|
+
self.new :tinkergraph do |g|
|
21
21
|
v1 = g.add_vertex 1, name:'marko', age:29
|
22
22
|
v2 = g.add_vertex 2, name:'vadas', age:27
|
23
23
|
v3 = g.add_vertex 3, name:'lop', lang:'java'
|
@@ -67,12 +67,13 @@ module RedGrape
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
attr_accessor :serializer
|
70
|
+
attr_accessor :name, :serializer
|
71
71
|
|
72
72
|
# Returns a new instance
|
73
73
|
# _block_ :: a block which is given the instance as the first argument
|
74
74
|
# to initialize it
|
75
|
-
def initialize(&block)
|
75
|
+
def initialize(name=nil, &block)
|
76
|
+
@name = name || uid
|
76
77
|
@serializer = Serializer::GraphMLSerializer.new self
|
77
78
|
@vertices = {}
|
78
79
|
@edges = {}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'red_grape'
|
2
|
+
|
3
|
+
module RedGrape
|
4
|
+
class GraphStore
|
5
|
+
def initialize
|
6
|
+
@graphs = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def graph_names
|
10
|
+
@graphs.keys.sort
|
11
|
+
end
|
12
|
+
|
13
|
+
def graph(key)
|
14
|
+
@graphs[key.to_s.to_sym]
|
15
|
+
end
|
16
|
+
alias [] graph
|
17
|
+
|
18
|
+
def put_graph(key, graph)
|
19
|
+
graph.name = key.to_s.to_sym
|
20
|
+
self << graph
|
21
|
+
end
|
22
|
+
alias []= put_graph
|
23
|
+
|
24
|
+
def <<(graph)
|
25
|
+
raise ArgumentError.new('The given graph does not have its name.') unless graph.name
|
26
|
+
@graphs[graph.name.to_sym] = graph
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/red_grape/pipe/base.rb
CHANGED
@@ -62,10 +62,8 @@ module RedGrape
|
|
62
62
|
context.resume_from_aggregating
|
63
63
|
elsif context.grouping?
|
64
64
|
context.resume_from_grouping
|
65
|
-
elsif context.
|
66
|
-
context.
|
67
|
-
elsif context.counting?
|
68
|
-
context.resume_from_counting
|
65
|
+
elsif context.accumulating?
|
66
|
+
context.resume_from_accumulating
|
69
67
|
else
|
70
68
|
val
|
71
69
|
end
|
@@ -7,11 +7,13 @@ module RedGrape
|
|
7
7
|
def initialize
|
8
8
|
@history = []
|
9
9
|
@marks = {}
|
10
|
+
|
10
11
|
@aggregating_items = {}
|
11
12
|
@aggregated_items = {}
|
12
13
|
@grouping_items = {}
|
13
|
-
|
14
|
-
@
|
14
|
+
|
15
|
+
@accumulated_items = {}
|
16
|
+
|
15
17
|
@loops = 1
|
16
18
|
end
|
17
19
|
|
@@ -88,44 +90,92 @@ module RedGrape
|
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
91
|
-
def
|
92
|
-
|
93
|
+
def accumulate(key, obj, &block)
|
94
|
+
items = (@accumulated_items[key] ||= {})
|
95
|
+
(items[:objects] ||= []) << obj
|
96
|
+
items[:resume_block] = block if block
|
93
97
|
end
|
94
98
|
|
95
|
-
def
|
96
|
-
|
99
|
+
def accumulating?(key=nil)
|
100
|
+
if key
|
101
|
+
not @accumulated_items[key].nil?
|
102
|
+
else
|
103
|
+
@accumulated_items.any?{|k, _| accumulating? k}
|
104
|
+
end
|
97
105
|
end
|
98
106
|
|
99
|
-
def
|
100
|
-
|
107
|
+
def clear_accumulating(key=nil)
|
108
|
+
if key
|
109
|
+
@accumulated_items[key] = nil if @accumulated_items[key]
|
110
|
+
else
|
111
|
+
@accumulated_items.clear
|
112
|
+
end
|
101
113
|
end
|
102
114
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
push_history objs do |ctx|
|
109
|
-
pipe.prev = objs
|
110
|
-
pipe.take ctx
|
115
|
+
def resume_from_accumulating(key=nil, should_clear=true)
|
116
|
+
ret = nil
|
117
|
+
if key
|
118
|
+
if items = @accumulated_items[key]
|
119
|
+
ret = items[:resume_block].call(items[:objects]) if items[:resume_block]
|
111
120
|
end
|
121
|
+
@accumulated_items[key] = nil if should_clear
|
112
122
|
else
|
113
|
-
|
123
|
+
@accumulated_items.each do |k, _|
|
124
|
+
ret = resume_from_accumulating k
|
125
|
+
end
|
126
|
+
end
|
127
|
+
ret
|
128
|
+
end
|
129
|
+
|
130
|
+
def gather(obj, next_pipe)
|
131
|
+
accumulate :gather, obj do |objs|
|
132
|
+
objs = objs.dup
|
133
|
+
objs.should_pass_through_whole = true
|
134
|
+
if next_pipe
|
135
|
+
push_history objs do |ctx|
|
136
|
+
next_pipe.prev = objs
|
137
|
+
next_pipe.take ctx
|
138
|
+
end
|
139
|
+
else
|
140
|
+
objs
|
141
|
+
end
|
114
142
|
end
|
115
143
|
end
|
116
144
|
|
145
|
+
def gathering?
|
146
|
+
accumulating? :gather
|
147
|
+
end
|
148
|
+
|
149
|
+
def resume_from_gathering
|
150
|
+
resume_from_accumulating :gather
|
151
|
+
end
|
152
|
+
|
117
153
|
def count(obj, next_pipe)
|
118
|
-
|
154
|
+
accumulate :count, obj do |objs|
|
155
|
+
objs.size
|
156
|
+
end
|
119
157
|
end
|
120
158
|
|
121
159
|
def counting?
|
122
|
-
|
160
|
+
accumulating? :count
|
123
161
|
end
|
124
162
|
|
125
163
|
def resume_from_counting
|
126
|
-
|
127
|
-
|
128
|
-
|
164
|
+
resume_from_accumulating :count
|
165
|
+
end
|
166
|
+
|
167
|
+
def order(obj, next_pipe)
|
168
|
+
accumulate :order, obj do |objs|
|
169
|
+
objs.sort
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def ordering?
|
174
|
+
accumulating? :order
|
175
|
+
end
|
176
|
+
|
177
|
+
def resume_from_ordering
|
178
|
+
resume_from_accumulating :order
|
129
179
|
end
|
130
180
|
|
131
181
|
def eval(args={}, &block)
|
@@ -1,9 +1,18 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
require 'red_grape/pipe/base'
|
2
|
-
require 'red_grape/path_group'
|
3
3
|
|
4
4
|
module RedGrape
|
5
|
+
class PathGroup
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :@paths, :size, :to_s, :[], :first, :last
|
8
|
+
|
9
|
+
def initialize(ary)
|
10
|
+
@paths = ary.dup
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
module Pipe
|
6
|
-
class
|
15
|
+
class PathPipe < Pipe::Base
|
7
16
|
def pass(obj, context)
|
8
17
|
context.push_history obj do |ctx|
|
9
18
|
history = ctx.history.dup
|
@@ -26,6 +35,6 @@ module RedGrape
|
|
26
35
|
end
|
27
36
|
end
|
28
37
|
|
29
|
-
|
38
|
+
PathsPipe = PathPipe
|
30
39
|
end
|
31
40
|
end
|
@@ -4,7 +4,14 @@ module RedGrape
|
|
4
4
|
module Pipe
|
5
5
|
class SelectPipe < Pipe::Base
|
6
6
|
def pass(obj, context)
|
7
|
-
|
7
|
+
if converter = self.opts[0]
|
8
|
+
context.marks.inject({}) do |h, kv|
|
9
|
+
h[kv[0]] = context.eval(:it => kv[1], &converter)
|
10
|
+
h
|
11
|
+
end
|
12
|
+
else
|
13
|
+
context.marks
|
14
|
+
end
|
8
15
|
end
|
9
16
|
end
|
10
17
|
end
|
data/lib/red_grape/tools/irg.rb
CHANGED
@@ -2,7 +2,7 @@ require 'drb/drb'
|
|
2
2
|
require 'irb'
|
3
3
|
require 'irb/completion'
|
4
4
|
require 'red_grape'
|
5
|
-
require 'red_grape/tools/
|
5
|
+
require 'red_grape/tools/trellis'
|
6
6
|
|
7
7
|
module RedGrape
|
8
8
|
module Tools
|
@@ -61,7 +61,7 @@ Subcommands:
|
|
61
61
|
}
|
62
62
|
|
63
63
|
RedGrape.set_auto_take
|
64
|
-
$store = RedGrape::Tools::
|
64
|
+
$store = RedGrape::Tools::Trellis.open port if port
|
65
65
|
block.call if block
|
66
66
|
IRB.start
|
67
67
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'drb/drb'
|
2
2
|
require 'red_grape'
|
3
|
+
require 'red_grape/graph_store'
|
3
4
|
|
4
5
|
module RedGrape
|
5
6
|
module Tools
|
6
|
-
class GraphStore
|
7
|
+
class Trellis < GraphStore
|
7
8
|
DEFAULT_PORT = 28282
|
8
9
|
|
9
10
|
class << self
|
@@ -32,26 +33,6 @@ module RedGrape
|
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
|
-
def graphs
|
36
|
-
@graphs.keys.sort
|
37
|
-
end
|
38
|
-
|
39
|
-
def graph(key)
|
40
|
-
self[key]
|
41
|
-
end
|
42
|
-
|
43
|
-
def put_graph(key, graph)
|
44
|
-
self[key] = graph
|
45
|
-
end
|
46
|
-
|
47
|
-
def [](key)
|
48
|
-
@graphs[key.to_sym]
|
49
|
-
end
|
50
|
-
|
51
|
-
def []=(key, graph)
|
52
|
-
@graphs[key.to_sym] = graph
|
53
|
-
end
|
54
|
-
|
55
36
|
def start(port=nil, &block)
|
56
37
|
at_exit do
|
57
38
|
File.open @filename, 'w' do |file|
|
data/lib/red_grape.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module RedGrape
|
2
|
-
VERSION = '0.0.
|
2
|
+
VERSION = '0.0.13'
|
3
3
|
end
|
4
4
|
|
5
5
|
require 'ext/nil_class_ext'
|
@@ -27,11 +27,12 @@ require 'red_grape/pipe/in_e_pipe'
|
|
27
27
|
require 'red_grape/pipe/in_v_pipe'
|
28
28
|
require 'red_grape/pipe/loop_pipe'
|
29
29
|
require 'red_grape/pipe/map_pipe'
|
30
|
+
require 'red_grape/pipe/order_pipe'
|
30
31
|
require 'red_grape/pipe/out_pipe'
|
31
32
|
require 'red_grape/pipe/out_e_pipe'
|
32
33
|
require 'red_grape/pipe/out_v_pipe'
|
33
34
|
require 'red_grape/pipe/property_pipe'
|
34
|
-
require 'red_grape/pipe/
|
35
|
+
require 'red_grape/pipe/path_pipe'
|
35
36
|
require 'red_grape/pipe/retain_pipe'
|
36
37
|
require 'red_grape/pipe/scatter_pipe'
|
37
38
|
require 'red_grape/pipe/select_pipe'
|
@@ -40,6 +41,7 @@ require 'red_grape/pipe/transform_pipe'
|
|
40
41
|
require 'red_grape/pipe/v_pipe'
|
41
42
|
require 'red_grape/pipe/e_pipe'
|
42
43
|
require 'red_grape/graph'
|
44
|
+
require 'red_grape/graph_store'
|
43
45
|
|
44
46
|
module RedGrape
|
45
47
|
module_function
|
@@ -0,0 +1,220 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Force-Directed Layout</title>
|
5
|
+
<!--script type='text/javascript' src='/js/d3.v3.js'></script-->
|
6
|
+
<script type='text/javascript' src='/js/d3.v2.js'></script>
|
7
|
+
<style type='text/css'>
|
8
|
+
|
9
|
+
div.node {
|
10
|
+
border-radius: 6px;
|
11
|
+
width: 12px;
|
12
|
+
height: 12px;
|
13
|
+
margin: -6px 0 0 -6px;
|
14
|
+
position: absolute;
|
15
|
+
|
16
|
+
border-radius: 10px;
|
17
|
+
width: 20px;
|
18
|
+
height: 20px;
|
19
|
+
margin: -10px 0 0 -10px;
|
20
|
+
color: white;
|
21
|
+
text-align:center;
|
22
|
+
vertical-align:middle;
|
23
|
+
font-size:0.5em;
|
24
|
+
}
|
25
|
+
|
26
|
+
div.link {
|
27
|
+
position: absolute;
|
28
|
+
border-bottom: solid #999 1px;
|
29
|
+
height: 0;
|
30
|
+
-webkit-transform-origin: 0 0;
|
31
|
+
-moz-transform-origin: 0 0;
|
32
|
+
-ms-transform-origin: 0 0;
|
33
|
+
-o-transform-origin: 0 0;
|
34
|
+
transform-origin: 0 0;
|
35
|
+
|
36
|
+
font-size:0.1em;
|
37
|
+
padding-left:10px;
|
38
|
+
white-space:nowrap;
|
39
|
+
color:#999;
|
40
|
+
}
|
41
|
+
|
42
|
+
div.graph {
|
43
|
+
position:absolute;
|
44
|
+
left:200px;
|
45
|
+
top:0;
|
46
|
+
height:auto;
|
47
|
+
}
|
48
|
+
|
49
|
+
div.inspector {
|
50
|
+
position:absolute;
|
51
|
+
top:0;
|
52
|
+
left:0;
|
53
|
+
width:200px;
|
54
|
+
height:500px;
|
55
|
+
height:100%;
|
56
|
+
background-color:#eee;
|
57
|
+
box-shadow: -2px 2px 3px #888 inset;
|
58
|
+
}
|
59
|
+
|
60
|
+
div.inspector h2 {
|
61
|
+
margin:0;
|
62
|
+
padding-left:10px;
|
63
|
+
margin-top:10px;
|
64
|
+
font-size:1em;
|
65
|
+
font-weight:normal;
|
66
|
+
background-color:gray;
|
67
|
+
color:white;
|
68
|
+
}
|
69
|
+
|
70
|
+
div.inspector dt {
|
71
|
+
border-bottom:1px solid gray;
|
72
|
+
display:block;
|
73
|
+
width:80px;
|
74
|
+
float:left;
|
75
|
+
padding-left:10px;
|
76
|
+
font-weight:bold;
|
77
|
+
}
|
78
|
+
|
79
|
+
div.inspector dd {
|
80
|
+
border-bottom:1px solid gray;
|
81
|
+
display:block;
|
82
|
+
width:160px;
|
83
|
+
height:auto;
|
84
|
+
}
|
85
|
+
</style>
|
86
|
+
</head>
|
87
|
+
<body>
|
88
|
+
<script type='text/javascript'>
|
89
|
+
function isIn(array, key) {
|
90
|
+
for (var i in array) {
|
91
|
+
if (array[i] == key) return true
|
92
|
+
}
|
93
|
+
return false;
|
94
|
+
}
|
95
|
+
|
96
|
+
var height = window.document.body.scrollHeight;
|
97
|
+
var width = window.document.body.scrollWidth - 200;
|
98
|
+
var graphId = window.location.hash.substring(1);
|
99
|
+
var radius = 6,
|
100
|
+
fill = d3.scale.category20();
|
101
|
+
var force = d3.layout.force()
|
102
|
+
.charge(-120)
|
103
|
+
.linkDistance(100)
|
104
|
+
.gravity(0)
|
105
|
+
.size([width, height]);
|
106
|
+
var vis = d3.select('body').append('div')
|
107
|
+
.attr('class', 'graph')
|
108
|
+
.style('width', width + 'px')
|
109
|
+
.style('height', height + 'px');
|
110
|
+
|
111
|
+
/*
|
112
|
+
setTimeout(function() {
|
113
|
+
force.linkDistance(30);
|
114
|
+
force.stop();
|
115
|
+
vis.selectAll('div.link').style('font-size', '0');
|
116
|
+
force.start();
|
117
|
+
}, 5000);
|
118
|
+
|
119
|
+
setTimeout(function() {
|
120
|
+
force.linkDistance(100);
|
121
|
+
force.stop();
|
122
|
+
vis.selectAll('div.link').style('font-size', '0.1em');
|
123
|
+
force.start();
|
124
|
+
}, 10000);
|
125
|
+
*/
|
126
|
+
|
127
|
+
var vertices = [];
|
128
|
+
var edges = [];
|
129
|
+
d3.json('/graphs/' + graphId + '/vertices', function(json) {
|
130
|
+
for (var i = 0; i < json.length; i++) {
|
131
|
+
vertices.push(json[i].results);
|
132
|
+
}
|
133
|
+
|
134
|
+
function getVertexIndex(id) {
|
135
|
+
for (var i = 0; i < vertices.length; i++) {
|
136
|
+
if (vertices[i]._id == id) return i;
|
137
|
+
}
|
138
|
+
return -1;
|
139
|
+
}
|
140
|
+
|
141
|
+
d3.json('/graphs/' + graphId + '/edges', function(json) {
|
142
|
+
for (var i = 0; i < json.length; i++) {
|
143
|
+
var result = json[i].results;
|
144
|
+
edges.push({
|
145
|
+
source:getVertexIndex(result._source),
|
146
|
+
target:getVertexIndex(result._target),
|
147
|
+
data:result
|
148
|
+
});
|
149
|
+
}
|
150
|
+
|
151
|
+
var link = vis.selectAll('div.link')
|
152
|
+
.data(edges)
|
153
|
+
.enter().append('div')
|
154
|
+
.attr('class', 'link');
|
155
|
+
|
156
|
+
link.append('text')
|
157
|
+
.attr('text-anchor', 'middle')
|
158
|
+
.attr('dy', '.1em')
|
159
|
+
.text(function(d) { return '-[' + d.data._label + ']->'; });
|
160
|
+
|
161
|
+
var node = vis.selectAll('div.node')
|
162
|
+
.data(vertices)
|
163
|
+
.enter().append('div')
|
164
|
+
.attr('class', 'node')
|
165
|
+
.style('background', function(d) { return fill(d.group); })
|
166
|
+
.style('border-color', function(d) { return d3.rgb(fill(d.group)).darker(); })
|
167
|
+
.on('click', function(d) {
|
168
|
+
var props = '';
|
169
|
+
for (var k in d) {
|
170
|
+
if (!isIn(['_type', 'index', 'weight', 'x', 'y', 'px', 'py', 'fixed'], k)) {
|
171
|
+
props += '<dt>' + k + '</dt><dd>' + d[k] + '</dd>';
|
172
|
+
}
|
173
|
+
}
|
174
|
+
d3.select('.inspector').html('<h2>Properties</h2><dl>' + props + '</dl>');
|
175
|
+
})
|
176
|
+
.call(force.drag);
|
177
|
+
|
178
|
+
node.append('text')
|
179
|
+
.attr('text-anchor', 'middle')
|
180
|
+
.attr('dy', '.3em')
|
181
|
+
.text(function(d) { return d._id; });
|
182
|
+
|
183
|
+
force
|
184
|
+
.nodes(vertices)
|
185
|
+
.links(edges)
|
186
|
+
.on('tick', tick)
|
187
|
+
.start();
|
188
|
+
|
189
|
+
function tick() {
|
190
|
+
node.style('left', function(d) { return (d.x = Math.max(radius, Math.min(width - radius, d.x))) + 'px'; })
|
191
|
+
.style('top', function(d) { return (d.y = Math.max(radius, Math.min(height - radius, d.y))) + 'px'; });
|
192
|
+
|
193
|
+
link.style('left', function(d) { return d.source.x + 'px'; })
|
194
|
+
.style('top', function(d) { return d.source.y + 'px'; })
|
195
|
+
.style('width', length)
|
196
|
+
.style('-webkit-transform', transform)
|
197
|
+
.style('-moz-transform', transform)
|
198
|
+
.style('-ms-transform', transform)
|
199
|
+
.style('-o-transform', transform)
|
200
|
+
.style('transform', transform);
|
201
|
+
}
|
202
|
+
|
203
|
+
function transform(d) {
|
204
|
+
return 'rotate(' + Math.atan2(d.target.y - d.source.y, d.target.x - d.source.x) * 180 / Math.PI + 'deg)';
|
205
|
+
}
|
206
|
+
|
207
|
+
function length(d) {
|
208
|
+
var dx = d.target.x - d.source.x,
|
209
|
+
dy = d.target.y - d.source.y;
|
210
|
+
return Math.sqrt(dx * dx + dy * dy) + 'px';
|
211
|
+
}
|
212
|
+
});
|
213
|
+
});
|
214
|
+
</script>
|
215
|
+
<div class='inspector'>
|
216
|
+
<h2>Properties</h2>
|
217
|
+
Click a node.
|
218
|
+
</div>
|
219
|
+
</body>
|
220
|
+
</html>
|