15
15
* limitations under the License.
16
16
*/
17
17
18
- var VizDefaults = {
19
- stageVizOffset : 160
18
+ var VizConstants = {
19
+ stageVizOffset : 160 ,
20
+ rddScopeColor : "#AADFFF" ,
21
+ rddDotColor : "#444444" ,
22
+ stageScopeColor : "#FFDDEE" ,
23
+ scopeLabelColor : "#888888" ,
24
+ edgeColor : "#444444" ,
25
+ edgeWidth : "1.5px"
20
26
} ;
21
27
22
28
/*
@@ -30,7 +36,7 @@ function toggleShowViz(forJob) {
30
36
if ( show ) {
31
37
var shouldRender = d3 . select ( "#viz-graph svg" ) . empty ( ) ;
32
38
if ( shouldRender ) {
33
- renderViz ( ) ;
39
+ renderViz ( forJob ) ;
34
40
styleViz ( forJob ) ;
35
41
}
36
42
$ ( "#viz-graph" ) . show ( ) ;
@@ -49,7 +55,7 @@ function toggleShowViz(forJob) {
49
55
* http://github.com/andrewor14/dagre-d3/dist/dagre-d3.js. For more detail, please
50
56
* track the changes in that project after it was forked.
51
57
*/
52
- function renderViz ( ) {
58
+ function renderViz ( forJob ) {
53
59
54
60
// If there is not a dot file to render, report error
55
61
if ( d3 . select ( "#viz-dot-files" ) . empty ( ) ) {
@@ -61,18 +67,42 @@ function renderViz() {
61
67
62
68
var svg = d3 . select ( "#viz-graph" ) . append ( "svg" ) ;
63
69
64
- // Each div in #viz-dot-files stores the content of one dot file
70
+ // On the job page, the visualization for each stage will rendered separately
71
+ // Thus, we will need to render the edges that cross stages later on our own
72
+ var crossStageEdges = [ ] ;
73
+
74
+ // Each div child in #viz-dot-files stores the content of one dot file
65
75
// Each dot file is used to generate the visualization for a stage
66
- d3 . selectAll ( "#viz-dot-files div" ) . each ( function ( d , i ) {
76
+ d3 . selectAll ( "#viz-dot-files > div" ) . each ( function ( d , i ) {
67
77
var div = d3 . select ( this ) ;
68
78
var stageId = div . attr ( "name" ) ;
69
- var dot = div . text ( ) ;
79
+ var dot = div . select ( "div.dot-file" ) . text ( ) ;
70
80
var container = svg . append ( "g" ) . attr ( "id" , "graph_" + stageId ) ;
71
81
// Move the container so it doesn't overlap with the existing ones
72
- container . attr ( "transform" , "translate(" + VizDefaults . stageVizOffset * i + ", 0)" ) ;
82
+ container . attr ( "transform" , "translate(" + VizConstants . stageVizOffset * i + ", 0)" ) ;
73
83
renderDot ( dot , container ) ;
84
+ // If there are any incoming edges into this graph, keep track of them to
85
+ // render them separately later (job page only). Note that we cannot draw
86
+ // them now because we need to put these edges in a container that is on
87
+ // top of all stage graphs.
88
+ if ( forJob ) {
89
+ div . selectAll ( "div.incoming-edge" ) . each ( function ( v ) {
90
+ var edge = d3 . select ( this ) . text ( ) . split ( "," ) ; // e.g. 3,4 => [3, 4]
91
+ crossStageEdges . push ( edge ) ;
92
+ } ) ;
93
+ }
74
94
} ) ;
75
95
96
+ // Time to draw cross stage edges (job page only)!
97
+ if ( crossStageEdges . length > 0 && forJob ) {
98
+ var container = svg . append ( "g" ) . attr ( "id" , "cross-stage-edges" ) ;
99
+ for ( var i = 0 ; i < crossStageEdges . length ; i ++ ) {
100
+ var fromId = "node_" + crossStageEdges [ i ] [ 0 ] ;
101
+ var toId = "node_" + crossStageEdges [ i ] [ 1 ] ;
102
+ connect ( fromId , toId , container ) ;
103
+ }
104
+ }
105
+
76
106
// Set the appropriate SVG dimensions to ensure that all elements are displayed
77
107
var svgMargin = 20 ;
78
108
var boundingBox = svg . node ( ) . getBBox ( ) ;
@@ -107,9 +137,7 @@ function renderViz() {
107
137
svg . attr ( "viewBox" , newViewBox ) ;
108
138
}
109
139
110
- /*
111
- *
112
- */
140
+ /* Render the dot file as an SVG in the given container. */
113
141
function renderDot ( dot , container ) {
114
142
var escaped_dot = dot
115
143
. replace ( / & l t ; / g, "<" )
@@ -120,56 +148,134 @@ function renderDot(dot, container) {
120
148
renderer ( container , g ) ;
121
149
}
122
150
123
- /*
124
- * Style the visualization we just rendered.
125
- * We apply a different style depending on whether this is a stage or a job.
126
- */
151
+ /* Style the visualization we just rendered. */
127
152
function styleViz ( forJob ) {
153
+ d3 . selectAll ( "svg g.cluster rect" )
154
+ . style ( "fill" , "none" )
155
+ . style ( "stroke" , VizConstants . rddScopeColor )
156
+ . style ( "stroke-width" , "4px" )
157
+ . style ( "stroke-opacity" , "0.5" ) ;
158
+ d3 . selectAll ( "svg g.cluster text" )
159
+ . attr ( "fill" , VizConstants . scopeLabelColor )
160
+ . attr ( "font-size" , "11px" )
161
+ d3 . selectAll ( "svg path" )
162
+ . style ( "stroke" , VizConstants . edgeColor )
163
+ . style ( "stroke-width" , VizConstants . edgeWidth ) ;
164
+ styleEdgeArrow ( ) ;
165
+
166
+ // Apply a different color to stage clusters
167
+ d3 . selectAll ( "svg g.cluster" )
168
+ . filter ( function ( ) {
169
+ var name = d3 . select ( this ) . attr ( "name" ) ;
170
+ return name && name . indexOf ( "Stage" ) > - 1 ;
171
+ } )
172
+ . select ( "rect" )
173
+ . style ( "stroke" , VizConstants . stageScopeColor ) ;
174
+
175
+ // Apply any job or stage specific styles
128
176
if ( forJob ) {
129
177
styleJobViz ( ) ;
130
178
} else {
131
179
styleStageViz ( ) ;
132
180
}
133
181
}
134
182
183
+ /*
184
+ * Put an arrow at the end of every edge.
185
+ * We need to do this because we manually render some edges ourselves through d3.
186
+ * For these edges, we borrow the arrow marker generated by dagre-d3.
187
+ */
188
+ function styleEdgeArrow ( ) {
189
+ var dagreD3Marker = d3 . select ( "svg g.edgePaths marker" ) . node ( )
190
+ d3 . select ( "svg" )
191
+ . append ( function ( ) { return dagreD3Marker . cloneNode ( true ) ; } )
192
+ . attr ( "id" , "marker-arrow" )
193
+ . select ( "path" )
194
+ . attr ( "fill" , VizConstants . edgeColor )
195
+ . attr ( "strokeWidth" , "0px" ) ;
196
+ d3 . selectAll ( "svg g > path" ) . attr ( "marker-end" , "url(#marker-arrow)" )
197
+ d3 . selectAll ( "svg g.edgePaths def" ) . remove ( ) ; // We no longer need these
198
+ }
199
+
200
+ /* Apply job-page-specific style to the visualization. */
135
201
function styleJobViz ( ) {
136
- d3 . selectAll ( "svg g.cluster rect" )
137
- . style ( "fill" , "none" )
138
- . style ( "stroke" , "#AADFFF" )
139
- . style ( "stroke-width" , "4px" )
140
- . style ( "stroke-opacity" , "0.5" ) ;
141
- d3 . selectAll ( "svg g.node rect" )
142
- . style ( "fill" , "white" )
143
- . style ( "stroke" , "black" )
144
- . style ( "stroke-width" , "2px" )
145
- . style ( "fill-opacity" , "0.8" )
146
- . style ( "stroke-opacity" , "0.9" ) ;
147
- d3 . selectAll ( "svg g.edgePath path" )
148
- . style ( "stroke" , "black" )
149
- . style ( "stroke-width" , "2px" ) ;
150
- d3 . selectAll ( "svg g.cluster text" )
151
- . attr ( "fill" , "#AAAAAA" )
152
- . attr ( "font-size" , "11px" )
202
+ d3 . selectAll ( "svg g.node circle" )
203
+ . style ( "fill" , VizConstants . rddDotColor ) ;
204
+ d3 . selectAll ( "svg g#cross-stage-edges path" )
205
+ . style ( "fill" , "none" ) ;
153
206
}
154
207
208
+ /* Apply stage-page-specific style to the visualization. */
155
209
function styleStageViz ( ) {
156
- d3 . selectAll ( "svg g.cluster rect" )
157
- . style ( "fill" , "none" )
158
- . style ( "stroke" , "#AADFFF" )
159
- . style ( "stroke-width" , "4px" )
160
- . style ( "stroke-opacity" , "0.5" ) ;
161
210
d3 . selectAll ( "svg g.node rect" )
162
211
. style ( "fill" , "white" )
163
212
. style ( "stroke" , "black" )
164
213
. style ( "stroke-width" , "2px" )
165
214
. style ( "fill-opacity" , "0.8" )
166
215
. style ( "stroke-opacity" , "0.9" ) ;
167
- d3 . selectAll ( "svg g.edgePath path" )
168
- . style ( "stroke" , "black" )
169
- . style ( "stroke-width" , "2px" ) ;
170
- d3 . selectAll ( "svg g.cluster text" )
171
- . attr ( "fill" , "#AAAAAA" )
172
- . attr ( "font-size" , "11px" )
216
+ }
217
+
218
+ /*
219
+ * (Job page only) Return the absolute position of the
220
+ * group element identified by the given selector.
221
+ */
222
+ function getAbsolutePosition ( groupId ) {
223
+ var obj = d3 . select ( "#" + groupId ) . filter ( "g" ) ;
224
+ var _x = 0 , _y = 0 ;
225
+ while ( ! obj . empty ( ) ) {
226
+ var transformText = obj . attr ( "transform" ) ;
227
+ var translate = d3 . transform ( transformText ) . translate
228
+ _x += translate [ 0 ] ;
229
+ _y += translate [ 1 ] ;
230
+ obj = d3 . select ( obj . node ( ) . parentNode ) . filter ( "g" )
231
+ }
232
+ return { x : _x , y : _y } ;
233
+ }
234
+
235
+ /*
236
+ * (Job page only) Connect two group elements with a curved edge.
237
+ * This assumes that the path will be styled later.
238
+ */
239
+ function connect ( fromNodeId , toNodeId , container ) {
240
+ var from = getAbsolutePosition ( fromNodeId ) ;
241
+ var to = getAbsolutePosition ( toNodeId ) ;
242
+
243
+ // Account for node radius (this is highly-specific to the job page)
244
+ // Otherwise the arrow heads will bleed into the circle itself
245
+ var delta = toFloat ( d3 . select ( "svg g.nodes circle" ) . attr ( "r" ) ) ;
246
+ var markerEndDelta = 2 ; // adjust for arrow stroke width
247
+ if ( from . x < to . x ) {
248
+ from . x = from . x + delta ;
249
+ to . x = to . x - delta - markerEndDelta ;
250
+ } else if ( from . x > to . x ) {
251
+ from . x = from . x - delta ;
252
+ to . x = to . x + delta + markerEndDelta ;
253
+ }
254
+
255
+ if ( from . y == to . y ) {
256
+ // If they are on the same rank, curve the middle part of the edge
257
+ // upward a little to avoid interference with things in between
258
+ var points = [
259
+ [ from . x , from . y ] ,
260
+ [ from . x + ( to . x - from . x ) * 0.2 , from . y ] ,
261
+ [ from . x + ( to . x - from . x ) * 0.3 , from . y - 20 ] ,
262
+ [ from . x + ( to . x - from . x ) * 0.7 , from . y - 20 ] ,
263
+ [ from . x + ( to . x - from . x ) * 0.8 , to . y ] ,
264
+ [ to . x , to . y ]
265
+ ] ;
266
+ } else {
267
+ var points = [
268
+ [ from . x , from . y ] ,
269
+ [ from . x + ( to . x - from . x ) * 0.4 , from . y ] ,
270
+ [ from . x + ( to . x - from . x ) * 0.6 , to . y ] ,
271
+ [ to . x , to . y ]
272
+ ] ;
273
+ }
274
+
275
+ var line = d3 . svg . line ( ) . interpolate ( "basis" ) ;
276
+ container . append ( "path" )
277
+ . datum ( points )
278
+ . attr ( "d" , line ) ;
173
279
}
174
280
175
281
/* Helper method to convert attributes to numeric values. */
0 commit comments