1
+ /**
2
+ @author David Piegza
1
3
4
+ Implements a simple graph drawing with force-directed placement in 2D and 3D.
5
+
6
+ It uses the force-directed-layout implemented in:
7
+ https://github.com/davidpiegza/Graph-Visualization/blob/master/layouts/force-directed-layout.js
8
+
9
+ Drawing is done with Three.js: http://github.com/mrdoob/three.js
10
+
11
+ To use this drawing, include the graph-min.js file and create a SimpleGraph object:
12
+
13
+ <!DOCTYPE html>
14
+ <html>
15
+ <head>
16
+ <title>Graph Visualization</title>
17
+ <script type="text/javascript" src="path/to/graph-min.js"></script>
18
+ </head>
19
+ <body onload="new Drawing.SimpleGraph({layout: '3d', showStats: true, showInfo: true})">
20
+ </bod>
21
+ </html>
22
+
23
+ Parameters:
24
+ options = {
25
+ layout: "2d" or "3d"
26
+
27
+ showStats: <bool>, displays FPS box
28
+ showInfo: <bool>, displays some info on the graph and layout
29
+ The info box is created as <div id="graph-info">, it must be
30
+ styled and positioned with CSS.
31
+
32
+
33
+ selection: <bool>, enables selection of nodes on mouse over (it displays some info
34
+ when the showInfo flag is set)
35
+
36
+
37
+ limit: <int>, maximum number of nodes
38
+
39
+ numNodes: <int> - sets the number of nodes to create.
40
+ numEdges: <int> - sets the maximum number of edges for a node. A node will have
41
+ 1 to numEdges edges, this is set randomly.
42
+ }
43
+
44
+
45
+ Feel free to contribute a new drawing!
46
+
47
+ */
48
+
2
49
var Drawing = Drawing || { } ;
3
50
4
51
Drawing . SimpleGraph = function ( options ) {
5
52
var options = options || { } ;
6
- var layout = options . layout || "2d" ;
7
- var selection = options . selection || false ;
8
- var nodes_count = options . nodes || 20 ;
9
- var edges_count = options . edges || 10 ;
10
-
11
- var camera , scene , renderer , interaction , stats , geometry , object_selection ;
53
+
54
+ this . layout = options . layout || "2d" ;
55
+ this . show_stats = options . showStats || false ;
56
+ this . show_info = options . showInfo || false ;
57
+ this . selection = options . selection || false ;
58
+ this . limit = options . limit || 10 ;
59
+ this . nodes_count = options . numNodes || 20 ;
60
+ this . edges_count = options . numEdges || 10 ;
61
+
62
+ var camera , scene , renderer , interaction , geometry , object_selection ;
63
+ var stats ;
64
+ var info_text = { } ;
12
65
var graph = new Graph ( { limit : options . limit } ) ;
13
66
14
67
var geometries = [ ] ;
15
- var info ;
68
+
69
+ var that = this ;
16
70
17
71
init ( ) ;
18
72
createGraph ( ) ;
@@ -43,16 +97,24 @@ Drawing.SimpleGraph = function(options) {
43
97
scene = new THREE . Scene ( ) ;
44
98
45
99
// Node geometry
46
- if ( layout === "3d" ) {
100
+ if ( that . layout === "3d" ) {
47
101
geometry = new THREE . SphereGeometry ( 50 , 50 , 50 ) ;
48
102
} else {
49
103
geometry = new THREE . SphereGeometry ( 50 , 50 , 0 ) ;
50
104
}
51
105
52
- if ( selection ) {
53
- object_selection = new THREE . ObjectSelection ( { selected : function ( obj ) {
54
- // display info
55
- } } ) ;
106
+ if ( that . selection ) {
107
+ object_selection = new THREE . ObjectSelection ( {
108
+ selected : function ( obj ) {
109
+ // display info
110
+ if ( obj != null ) {
111
+ info_text . select = "Object " + obj . id ;
112
+ } else {
113
+ delete info_text . select ;
114
+ }
115
+
116
+ }
117
+ } ) ;
56
118
}
57
119
58
120
renderer = new THREE . WebGLRenderer ( { antialias : true } ) ;
@@ -61,32 +123,35 @@ Drawing.SimpleGraph = function(options) {
61
123
document . body . appendChild ( renderer . domElement ) ;
62
124
63
125
// Stats.js
64
- stats = new Stats ( ) ;
65
- stats . domElement . style . position = 'absolute' ;
66
- stats . domElement . style . top = '0px' ;
67
- document . body . appendChild ( stats . domElement ) ;
68
-
69
- info = document . createElement ( "div" ) ;
70
- info . style . position = 'absolute' ;
71
- info . style . top = '100px' ;
72
-
73
- document . body . appendChild ( stats . domElement ) ;
74
- document . body . appendChild ( info ) ;
75
- }
126
+ if ( that . show_stats ) {
127
+ stats = new Stats ( ) ;
128
+ stats . domElement . style . position = 'absolute' ;
129
+ stats . domElement . style . top = '0px' ;
130
+ document . body . appendChild ( stats . domElement ) ;
131
+ }
76
132
133
+ if ( that . show_info ) {
134
+ var info = document . createElement ( "div" ) ;
135
+ var id_attr = document . createAttribute ( "id" ) ;
136
+ id_attr . nodeValue = "graph-info" ;
137
+ info . setAttributeNode ( id_attr ) ;
138
+ document . body . appendChild ( info ) ;
139
+ }
140
+ }
141
+
77
142
function createGraph ( ) {
78
143
var node = new Node ( 0 ) ;
79
144
graph . addNode ( node ) ;
80
145
drawNode ( node ) ;
81
146
82
147
var nodes = [ ] ;
83
148
nodes . push ( node ) ;
84
-
149
+
85
150
var steps = 1 ;
86
- do {
151
+ while ( nodes . length != 0 && steps < that . nodes_count ) {
87
152
var node = nodes . shift ( ) ;
88
153
89
- var numEdges = randomFromTo ( 1 , edges_count ) ;
154
+ var numEdges = randomFromTo ( 1 , that . edges_count ) ;
90
155
for ( var i = 1 ; i <= numEdges ; i ++ ) {
91
156
var target_node = new Node ( i * steps ) ;
92
157
if ( graph . addNode ( target_node ) ) {
@@ -98,14 +163,12 @@ Drawing.SimpleGraph = function(options) {
98
163
}
99
164
}
100
165
steps ++ ;
101
- } while ( nodes . length != 0 && steps < nodes_count ) ;
166
+ }
102
167
103
- if ( layout === "3d" ) {
104
- graph . layout = new Layout . ForceDirected3D ( graph , { width : 2000 , height : 2000 , iterations : 1000 } ) ;
105
- } else {
106
- graph . layout = new Layout . ForceDirected ( graph , { width : 2000 , height : 2000 , iterations : 1000 } ) ;
107
- }
168
+ graph . layout = new Layout . ForceDirected ( graph , { width : 2000 , height : 2000 , iterations : 1000 , layout : that . layout } ) ;
108
169
graph . layout . init ( ) ;
170
+ info_text . nodes = "Nodes " + graph . nodes . length ;
171
+ info_text . edges = "Edges " + graph . edges . length ;
109
172
}
110
173
111
174
@@ -132,7 +195,7 @@ Drawing.SimpleGraph = function(options) {
132
195
draw_object . position . x = Math . floor ( Math . random ( ) * ( area + area + 1 ) - area ) ;
133
196
draw_object . position . y = Math . floor ( Math . random ( ) * ( area + area + 1 ) - area ) ;
134
197
135
- if ( layout === "3d" ) {
198
+ if ( that . layout === "3d" ) {
136
199
draw_object . position . z = Math . floor ( Math . random ( ) * ( area + area + 1 ) - area ) ;
137
200
}
138
201
@@ -172,14 +235,16 @@ Drawing.SimpleGraph = function(options) {
172
235
function animate ( ) {
173
236
requestAnimationFrame ( animate ) ;
174
237
render ( ) ;
238
+ printInfo ( ) ;
175
239
}
176
240
177
241
178
242
function render ( ) {
179
- if ( graph . layout . generate ( ) ) {
180
- info . style . border = '10px solid red' ;
243
+ if ( ! graph . layout . finished ) {
244
+ info_text . calc = "Calculating layout..."
245
+ graph . layout . generate ( ) ;
181
246
} else {
182
- info . style . border = 'none' ;
247
+ info_text . calc = ""
183
248
}
184
249
185
250
for ( var i = 0 ; i < geometries . length ; i ++ ) {
@@ -201,14 +266,28 @@ Drawing.SimpleGraph = function(options) {
201
266
// }
202
267
// });
203
268
204
- if ( selection ) {
269
+ if ( that . selection ) {
205
270
object_selection . render ( scene , camera ) ;
206
271
}
207
- renderer . render ( scene , camera ) ;
208
272
// interaction.update();
209
- stats . update ( ) ;
273
+ if ( that . show_stats ) {
274
+ stats . update ( ) ;
275
+ }
276
+ renderer . render ( scene , camera ) ;
277
+ }
278
+
279
+ function printInfo ( text ) {
280
+ var str = '' ;
281
+ for ( var index in info_text ) {
282
+ if ( str != '' && info_text [ index ] != '' ) {
283
+ str += " - " ;
284
+ }
285
+ str += info_text [ index ] ;
286
+ }
287
+ if ( str != '' ) {
288
+ document . getElementById ( "graph-info" ) . innerHTML = str ;
289
+ }
210
290
}
211
-
212
291
213
292
function drawText ( draw_object , text ) {
214
293
draw_object . materials [ 0 ] . map . image = null ;
0 commit comments