red_grape 0.0.12 → 0.0.13
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/.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>
|