Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(484)

Side by Side Diff: tools/turbolizer/graph-view.js

Issue 729913004: Add a html-based visualizer for TurboFan graphs (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Review feedback Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/turbolizer/graph-layout.js ('k') | tools/turbolizer/hide-selected.png » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 "use strict";
6
7 class GraphView extends View {
8 constructor (d3, id, nodes, edges, broker) {
9 super(id, broker);
10 var graph = this;
11
12 var svg = this.divElement.append("svg").attr('version','1.1').attr("width", "100%");
13 graph.svg = svg;
14
15 graph.nodes = nodes || [];
16 graph.edges = edges || [];
17
18 graph.minGraphX = 0;
19 graph.maxGraphX = 1;
20 graph.minGraphY = 0;
21 graph.maxGraphY = 1;
22
23 graph.state = {
24 selection: null,
25 mouseDownNode: null,
26 justDragged: false,
27 justScaleTransGraph: false,
28 lastKeyDown: -1,
29 showTypes: false
30 };
31
32 var selectionHandler = {
33 clear: function() {
34 broker.clear(selectionHandler);
35 },
36 select: function(items, selected) {
37 var ranges = [];
38 for (var d of items) {
39 if (selected) {
40 d.classList.add("selected");
41 } else {
42 d.classList.remove("selected");
43 }
44 var data = d.__data__;
45 ranges.push([data.pos, data.pos + 1, data.id]);
46 }
47 broker.select(selectionHandler, ranges, selected);
48 },
49 selectionDifference: function(span1, inclusive1, span2, inclusive2) {
50 // Should not be called
51 },
52 brokeredSelect: function(ranges, selected) {
53 var test = [].entries().next();
54 var selection = graph.nodes
55 .filter(function(n) {
56 var pos = n.pos;
57 for (var range of ranges) {
58 var start = range[0];
59 var end = range[1];
60 var id = range[2];
61 if (end != undefined) {
62 if (pos >= start && pos < end) {
63 return true;
64 }
65 } else if (start != undefined) {
66 if (pos === start) {
67 return true;
68 }
69 } else {
70 if (n.id === id) {
71 return true;
72 }
73 }
74 }
75 return false;
76 });
77 var newlySelected = new Set();
78 selection.forEach(function(n) {
79 newlySelected.add(n);
80 if (!n.visible) {
81 n.visible = true;
82 }
83 });
84 graph.updateGraphVisibility();
85 graph.visibleNodes.each(function(n) {
86 if (newlySelected.has(n)) {
87 graph.state.selection.select(this, selected);
88 }
89 });
90 graph.updateGraphVisibility();
91 graph.viewSelection();
92 },
93 brokeredClear: function() {
94 graph.state.selection.clear();
95 }
96 };
97 broker.addSelectionHandler(selectionHandler);
98
99 graph.state.selection = new Selection(selectionHandler);
100
101 var defs = svg.append('svg:defs');
102 defs.append('svg:marker')
103 .attr('id', 'end-arrow')
104 .attr('viewBox', '0 -4 8 8')
105 .attr('refX', 2)
106 .attr('markerWidth', 2.5)
107 .attr('markerHeight', 2.5)
108 .attr('orient', 'auto')
109 .append('svg:path')
110 .attr('d', 'M0,-4L8,0L0,4');
111
112 this.graphElement = svg.append("g");
113 graph.visibleEdges = this.graphElement.append("g").selectAll("g");
114 graph.visibleNodes = this.graphElement.append("g").selectAll("g");
115
116 graph.drag = d3.behavior.drag()
117 .origin(function(d){
118 return {x: d.x, y: d.y};
119 })
120 .on("drag", function(args){
121 graph.state.justDragged = true;
122 graph.dragmove.call(graph, args);
123 })
124
125 d3.select("#upload").on("click", function(){
126 document.getElementById("hidden-file-upload").click();
127 });
128
129 d3.select("#layout").on("click", function(){
130 graph.updateGraphVisibility();
131 graph.layoutGraph();
132 graph.updateGraphVisibility();
133 graph.viewWholeGraph();
134 });
135
136 d3.select("#show-all").on("click", function(){
137 graph.nodes.filter(function(n) { n.visible = true; })
138 graph.edges.filter(function(e) { e.visible = true; })
139 graph.updateGraphVisibility();
140 graph.viewWholeGraph();
141 });
142
143 d3.select("#hide-unselected").on("click", function() {
144 var unselected = graph.visibleNodes.filter(function(n) {
145 return !this.classList.contains("selected");
146 });
147 unselected.each(function(n) {
148 n.visible = false;
149 });
150 graph.updateGraphVisibility();
151 });
152
153 d3.select("#hide-selected").on("click", function() {
154 var selected = graph.visibleNodes.filter(function(n) {
155 return this.classList.contains("selected");
156 });
157 selected.each(function(n) {
158 n.visible = false;
159 });
160 graph.state.selection.clear();
161 graph.updateGraphVisibility();
162 });
163
164 d3.select("#zoom-selection").on("click", function() {
165 graph.viewSelection();
166 });
167
168 d3.select("#toggle-types").on("click", function() {
169 graph.toggleTypes();
170 });
171
172 d3.select("#search-input").on("keydown", function() {
173 if (d3.event.keyCode == 13) {
174 graph.state.selection.clear();
175 var reg = new RegExp(this.value);
176 var selected = graph.visibleNodes.each(function(n) {
177 if (reg.exec(n.getDisplayLabel()) != null ||
178 (graph.state.showTypes && reg.exec(n.getDisplayType())) ||
179 reg.exec(n.opcode) != null) {
180 graph.state.selection.select(this, true);
181 }
182 });
183 this.blur();
184 graph.viewSelection();
185 }
186 });
187
188 // listen for key events
189 d3.select(window).on("keydown", function(e){
190 graph.svgKeyDown.call(graph);
191 })
192 .on("keyup", function(){
193 graph.svgKeyUp.call(graph);
194 });
195 svg.on("mousedown", function(d){graph.svgMouseDown.call(graph, d);});
196 svg.on("mouseup", function(d){graph.svgMouseUp.call(graph, d);});
197
198 graph.dragSvg = d3.behavior.zoom()
199 .on("zoom", function(){
200 if (d3.event.sourceEvent.shiftKey){
201 return false;
202 } else{
203 graph.zoomed.call(graph);
204 }
205 return true;
206 })
207 .on("zoomstart", function(){
208 if (!d3.event.sourceEvent.shiftKey) d3.select('body').style("cursor", "m ove");
209 })
210 .on("zoomend", function(){
211 d3.select('body').style("cursor", "auto");
212 });
213
214 svg.call(graph.dragSvg).on("dblclick.zoom", null);
215 }
216
217 static get selectedClass() {
218 return "selected";
219 }
220 static get rectClass() {
221 return "nodeStyle";
222 }
223 static get activeEditId() {
224 return "active-editing";
225 }
226 static get nodeRadius() {
227 return 50;
228 }
229
230 getNodeHeight(graph) {
231 if (this.state.showTypes) {
232 return DEFAULT_NODE_HEIGHT + TYPE_HEIGHT;
233 } else {
234 return DEFAULT_NODE_HEIGHT;
235 }
236 }
237
238 dragmove(d) {
239 var graph = this;
240 d.x += d3.event.dx;
241 d.y += d3.event.dy;
242 graph.updateGraphVisibility();
243 }
244
245 initializeContent(data, rememberedSelection) {
246 this.createGraph(data);
247 if (rememberedSelection != null) {
248 this.attachSelection(rememberedSelection);
249 }
250 this.updateGraphVisibility();
251 }
252
253 deleteContent() {
254 if (this.visibleNodes) {
255 this.nodes = [];
256 this.edges = [];
257 this.nodeMap = [];
258 this.updateGraphVisibility();
259 }
260 };
261
262 createGraph(data) {
263 var g = this;
264 g.nodes = data.nodes;
265 g.nodeMap = [];
266 var textMeasure = document.getElementById('text-measure');
267 g.nodes.forEach(function(n, i){
268 n.__proto__ = Node;
269 n.visible = false;
270 n.x = 0;
271 n.y = 0;
272 n.rank = MAX_RANK_SENTINEL;
273 n.inputs = [];
274 n.outputs = [];
275 n.rpo = -1;
276 n.outputApproach = MINIMUM_NODE_OUTPUT_APPROACH;
277 n.cfg = n.control;
278 g.nodeMap[n.id] = n;
279 n.displayLabel = n.getDisplayLabel();
280 textMeasure.textContent = n.getDisplayLabel();
281 var width = textMeasure.getComputedTextLength();
282 textMeasure.textContent = n.getDisplayType();
283 width = Math.max(width, textMeasure.getComputedTextLength());
284 n.width = Math.alignUp(width + NODE_INPUT_WIDTH * 2,
285 NODE_INPUT_WIDTH);
286 });
287 g.edges = [];
288 data.edges.forEach(function(e, i){
289 var t = g.nodeMap[e.target];
290 var s = g.nodeMap[e.source];
291 var newEdge = new Edge(t, e.index, s, e.type);
292 t.inputs.push(newEdge);
293 s.outputs.push(newEdge);
294 g.edges.push(newEdge);
295 if (e.type == 'control') {
296 s.cfg = true;
297 }
298 });
299 g.nodes.forEach(function(n, i) {
300 n.visible = isNodeInitiallyVisible(n);
301 });
302 g.fitGraphViewToWindow();
303 g.updateGraphVisibility();
304 g.layoutGraph();
305 g.updateGraphVisibility();
306 g.viewWholeGraph();
307 }
308
309 updateInputAndOutputBubbles() {
310 var g = this;
311 var s = g.visibleBubbles;
312 s.classed("filledBubbleStyle", function(c) {
313 var components = this.id.split(',');
314 if (components[0] == "ib") {
315 var edge = g.nodeMap[components[3]].inputs[components[2]];
316 return edge.isVisible();
317 } else {
318 return g.nodeMap[components[1]].areAnyOutputsVisible() == 2;
319 }
320 }).classed("halfFilledBubbleStyle", function(c) {
321 var components = this.id.split(',');
322 if (components[0] == "ib") {
323 var edge = g.nodeMap[components[3]].inputs[components[2]];
324 return false;
325 } else {
326 return g.nodeMap[components[1]].areAnyOutputsVisible() == 1;
327 }
328 }).classed("bubbleStyle", function(c) {
329 var components = this.id.split(',');
330 if (components[0] == "ib") {
331 var edge = g.nodeMap[components[3]].inputs[components[2]];
332 return !edge.isVisible();
333 } else {
334 return g.nodeMap[components[1]].areAnyOutputsVisible() == 0;
335 }
336 });
337 s.each(function(c) {
338 var components = this.id.split(',');
339 if (components[0] == "ob") {
340 var from = g.nodeMap[components[1]];
341 var x = from.getOutputX();
342 var y = g.getNodeHeight() + DEFAULT_NODE_BUBBLE_RADIUS / 2 + 4;
343 var transform = "translate(" + x + "," + y + ")";
344 this.setAttribute('transform', transform);
345 }
346 });
347 }
348
349 attachSelection(s) {
350 var graph = this;
351 if (s.size != 0) {
352 this.visibleNodes.each(function(n) {
353 if (s.has(this.__data__.id)) {
354 graph.state.selection.select(this, true);
355 }
356 });
357 }
358 }
359
360 detachSelection() {
361 var selection = this.state.selection.detachSelection();
362 var result = new Set();
363 for (var i of selection) {
364 result.add(i.__data__.id);
365 };
366 return result;
367 }
368
369 pathMouseDown(path, d) {
370 d3.event.stopPropagation();
371 this.state.selection.clear();
372 this.state.selection.add(path);
373 };
374
375 nodeMouseDown(node, d) {
376 d3.event.stopPropagation();
377 this.state.mouseDownNode = d;
378 }
379
380 nodeMouseUp(d3node, d) {
381 var graph = this,
382 state = graph.state,
383 consts = graph.consts;
384
385 var mouseDownNode = state.mouseDownNode;
386
387 if (!mouseDownNode) return;
388
389 if (mouseDownNode !== d){
390 // we're in a different node: create new edge for mousedown edge and add t o graph
391 var newEdge = {source: mouseDownNode, target: d};
392 var filtRes = graph.visibleEdges.filter(function(d){
393 if (d.source === newEdge.target && d.target === newEdge.source){
394 graph.edges.splice(graph.edges.indexOf(d), 1);
395 }
396 return d.source === newEdge.source && d.target === newEdge.target;
397 });
398 if (!filtRes[0].length){
399 graph.edges.push(newEdge);
400 graph.updateGraphVisibility();
401 }
402 } else{
403 // we're in the same node
404 if (state.justDragged) {
405 // dragged, not clicked
406 state.justDragged = false;
407 } else{
408 // clicked, not dragged
409 var extend = d3.event.shiftKey;
410 var selection = graph.state.selection;
411 if (!extend) {
412 selection.clear();
413 }
414 selection.select(d3node[0][0], true);
415 }
416 }
417 }
418
419 selectSourcePositions(start, end, selected) {
420 var graph = this;
421 var map = [];
422 var sel = graph.nodes.filter(function(n) {
423 var pos = (n.pos === undefined)
424 ? -1
425 : n.getFunctionRelativeSourcePosition(graph);
426 if (pos >= start && pos < end) {
427 map[n.id] = true;
428 n.visible = true;
429 }
430 });
431 graph.updateGraphVisibility();
432 graph.visibleNodes.filter(function(n) { return map[n.id]; })
433 .each(function(n) {
434 var selection = graph.state.selection;
435 selection.select(d3.select(this), selected);
436 });
437 }
438
439 svgMouseDown() {
440 this.state.graphMouseDown = true;
441 }
442
443 svgMouseUp() {
444 var graph = this,
445 state = graph.state;
446 if (state.justScaleTransGraph) {
447 // Dragged
448 state.justScaleTransGraph = false;
449 } else {
450 // Clicked
451 if (state.mouseDownNode == null) {
452 graph.state.selection.clear();
453 }
454 }
455 state.mouseDownNode = null;
456 state.graphMouseDown = false;
457 }
458
459 svgKeyDown() {
460 var state = this.state;
461 var graph = this;
462
463 // Don't handle key press repetition
464 if(state.lastKeyDown !== -1) return;
465
466 var getEdgeFrontier = function(inEdges) {
467 var frontierSet = new Set();
468 state.selection.selection.forEach(function(element) {
469 var nodes = inEdges ? element.__data__.inputs : element.__data__.outputs ;
470 nodes.forEach(function(i) {
471 i.visible = true;
472 var candidate = inEdges ? i.source : i.target;
473 candidate.visible = true;
474 frontierSet.add(candidate);
475 });
476 });
477 graph.updateGraphVisibility();
478 return graph.visibleNodes.filter(function(n) {
479 return frontierSet.has(n);
480 });
481 }
482
483 var allowRepetition = true;
484 switch(d3.event.keyCode) {
485 case 38:
486 case 40: {
487 var frontier = getEdgeFrontier(d3.event.keyCode == 38);
488 if (!d3.event.shiftKey) {
489 state.selection.clear();
490 }
491 frontier.each(function(n) {
492 state.selection.select(this, true);
493 });
494 graph.updateGraphVisibility();
495 allowRepetition = false;
496 break;
497 }
498 }
499 if (!allowRepetition) {
500 state.lastKeyDown = d3.event.keyCode;
501 }
502 }
503
504 svgKeyUp() {
505 this.state.lastKeyDown = -1
506 };
507
508 layoutEdges() {
509 var graph = this;
510 graph.maxGraphX = graph.maxGraphNodeX;
511 this.visibleEdges.attr("d", function(edge){
512 return edge.generatePath(graph);
513 });
514 }
515
516 layoutGraph() {
517 layoutNodeGraph(this);
518 }
519
520 // call to propagate changes to graph
521 updateGraphVisibility() {
522
523 var graph = this,
524 state = graph.state;
525
526 var filteredEdges = graph.edges.filter(function(e) { return e.isVisible(); } );
527 var visibleEdges = graph.visibleEdges.data(filteredEdges, function(edge) {
528 return edge.stringID();
529 });
530
531 // add new paths
532 visibleEdges.enter()
533 .append('path')
534 .style('marker-end','url(#end-arrow)')
535 .classed('hidden', function(e) {
536 return !e.isVisible();
537 })
538 .attr("id", function(edge){ return "e," + edge.stringID(); })
539 .on("mousedown", function(d){
540 graph.pathMouseDown.call(graph, d3.select(this), d);
541 })
542
543 // Set the correct styles on all of the paths
544 visibleEdges.classed('value', function(e) {
545 return e.type == 'value' || e.type == 'context';
546 }).classed('control', function(e) {
547 return e.type == 'control';
548 }).classed('effect', function(e) {
549 return e.type == 'effect';
550 }).classed('frame-state', function(e) {
551 return e.type == 'frame-state';
552 }).attr('stroke-dasharray', function(e) {
553 if (e.type == 'frame-state') return "10,10";
554 return (e.type == 'effect') ? "5,5" : "";
555 });
556
557 // remove old links
558 visibleEdges.exit().remove();
559
560 graph.visibleEdges = visibleEdges;
561
562 // update existing nodes
563 var filteredNodes = graph.nodes.filter(function(n) { return n.visible; });
564 graph.visibleNodes = graph.visibleNodes.data(filteredNodes, function(d) {
565 return d.id;
566 });
567 graph.visibleNodes.attr("transform", function(n){
568 return "translate(" + n.x + "," + n.y + ")";
569 }).select('rect').
570 attr(HEIGHT, function(d) { return graph.getNodeHeight(); });
571
572 // add new nodes
573 var newGs = graph.visibleNodes.enter()
574 .append("g");
575
576 newGs.classed("control", function(n) { return n.isControl(); })
577 .classed("javascript", function(n) { return n.isJavaScript(); })
578 .classed("input", function(n) { return n.isInput(); })
579 .classed("simplified", function(n) { return n.isSimplified(); })
580 .classed("machine", function(n) { return n.isMachine(); })
581 .attr("transform", function(d){ return "translate(" + d.x + "," + d.y + ") ";})
582 .on("mousedown", function(d){
583 graph.nodeMouseDown.call(graph, d3.select(this), d);
584 })
585 .on("mouseup", function(d){
586 graph.nodeMouseUp.call(graph, d3.select(this), d);
587 })
588 .call(graph.drag);
589
590 newGs.append("rect")
591 .attr("rx", 10)
592 .attr("ry", 10)
593 .attr(WIDTH, function(d) { return d.getTotalNodeWidth(); })
594 .attr(HEIGHT, function(d) { return graph.getNodeHeight(); })
595
596 function appendInputAndOutputBubbles(g, d) {
597 for (var i = 0; i < d.inputs.length; ++i) {
598 var x = d.getInputX(i);
599 var y = -DEFAULT_NODE_BUBBLE_RADIUS / 2 - 4;
600 var s = g.append('circle')
601 .classed("filledBubbleStyle", function(c) {
602 return d.inputs[i].isVisible();
603 } )
604 .classed("bubbleStyle", function(c) {
605 return !d.inputs[i].isVisible();
606 } )
607 .attr("id", "ib," + d.inputs[i].stringID())
608 .attr("r", DEFAULT_NODE_BUBBLE_RADIUS)
609 .attr("transform", function(d) {
610 return "translate(" + x + "," + y + ")";
611 })
612 .on("mousedown", function(d){
613 var components = this.id.split(',');
614 var node = graph.nodeMap[components[3]];
615 var edge = node.inputs[components[2]];
616 var visible = !edge.isVisible();
617 node.setInputVisibility(components[2], visible);
618 d3.event.stopPropagation();
619 graph.updateGraphVisibility();
620 });
621 }
622 if (d.outputs.length != 0) {
623 var x = d.getOutputX();
624 var y = graph.getNodeHeight() + DEFAULT_NODE_BUBBLE_RADIUS / 2 + 4;
625 var s = g.append('circle')
626 .classed("filledBubbleStyle", function(c) {
627 return d.areAnyOutputsVisible() == 2;
628 } )
629 .classed("halFilledBubbleStyle", function(c) {
630 return d.areAnyOutputsVisible() == 1;
631 } )
632 .classed("bubbleStyle", function(c) {
633 return d.areAnyOutputsVisible() == 0;
634 } )
635 .attr("id", "ob," + d.id)
636 .attr("r", DEFAULT_NODE_BUBBLE_RADIUS)
637 .attr("transform", function(d) {
638 return "translate(" + x + "," + y + ")";
639 })
640 .on("mousedown", function(d) {
641 d.setOutputVisibility(d.areAnyOutputsVisible() == 0);
642 d3.event.stopPropagation();
643 graph.updateGraphVisibility();
644 });
645 }
646 }
647
648 newGs.each(function(d){
649 appendInputAndOutputBubbles(d3.select(this), d);
650 });
651
652 newGs.each(function(d){
653 d3.select(this).append("text")
654 .classed("label", true)
655 .attr("text-anchor","right")
656 .attr("dx", "5")
657 .attr("dy", DEFAULT_NODE_HEIGHT / 2 + 5)
658 .append('tspan')
659 .text(function(l) {
660 return d.getDisplayLabel();
661 })
662 .append("title")
663 .text(function(l) {
664 return d.getLabel();
665 })
666 if (d.type != undefined) {
667 d3.select(this).append("text")
668 .classed("label", true)
669 .classed("type", true)
670 .attr("text-anchor","right")
671 .attr("dx", "5")
672 .attr("dy", DEFAULT_NODE_HEIGHT / 2 + TYPE_HEIGHT + 5)
673 .append('tspan')
674 .text(function(l) {
675 return d.getDisplayType();
676 })
677 .append("title")
678 .text(function(l) {
679 return d.getType();
680 })
681 }
682 });
683
684 graph.visibleNodes.select('.type').each(function (d) {
685 this.setAttribute('visibility', graph.state.showTypes ? 'visible' : 'hidde n');
686 });
687
688 // remove old nodes
689 graph.visibleNodes.exit().remove();
690
691 graph.visibleBubbles = d3.selectAll('circle');
692
693 graph.updateInputAndOutputBubbles();
694
695 graph.layoutEdges();
696
697 graph.svg.style.height = '100%';
698 }
699
700 getVisibleTranslation(translate, scale) {
701 var graph = this;
702 var height = (graph.maxGraphY - graph.minGraphY + 2 * GRAPH_MARGIN) * scale;
703 var width = (graph.maxGraphX - graph.minGraphX + 2 * GRAPH_MARGIN) * scale;
704
705 var dimensions = this.getSvgViewDimensions();
706
707 var baseY = translate[1];
708 var minY = (graph.minGraphY - GRAPH_MARGIN) * scale;
709 var maxY = (graph.maxGraphY + GRAPH_MARGIN) * scale;
710
711 var adjustY = 0;
712 var adjustYCandidate = 0;
713 if ((maxY + baseY) < dimensions[1]) {
714 adjustYCandidate = dimensions[1] - (maxY + baseY);
715 if ((minY + baseY + adjustYCandidate) > 0) {
716 adjustY = (dimensions[1] / 2) - (maxY - (height / 2)) - baseY;
717 } else {
718 adjustY = adjustYCandidate;
719 }
720 } else if (-baseY < minY) {
721 adjustYCandidate = -(baseY + minY);
722 if ((maxY + baseY + adjustYCandidate) < dimensions[1]) {
723 adjustY = (dimensions[1] / 2) - (maxY - (height / 2)) - baseY;
724 } else {
725 adjustY = adjustYCandidate;
726 }
727 }
728 translate[1] += adjustY;
729
730 var baseX = translate[0];
731 var minX = (graph.minGraphX - GRAPH_MARGIN) * scale;
732 var maxX = (graph.maxGraphX + GRAPH_MARGIN) * scale;
733
734 var adjustX = 0;
735 var adjustXCandidate = 0;
736 if ((maxX + baseX) < dimensions[0]) {
737 adjustXCandidate = dimensions[0] - (maxX + baseX);
738 if ((minX + baseX + adjustXCandidate) > 0) {
739 adjustX = (dimensions[0] / 2) - (maxX - (width / 2)) - baseX;
740 } else {
741 adjustX = adjustXCandidate;
742 }
743 } else if (-baseX < minX) {
744 adjustXCandidate = -(baseX + minX);
745 if ((maxX + baseX + adjustXCandidate) < dimensions[0]) {
746 adjustX = (dimensions[0] / 2) - (maxX - (width / 2)) - baseX;
747 } else {
748 adjustX = adjustXCandidate;
749 }
750 }
751 translate[0] += adjustX;
752 return translate;
753 }
754
755 translateClipped(translate, scale, transition) {
756 var graph = this;
757 var graphNode = this.graphElement[0][0];
758 var translate = this.getVisibleTranslation(translate, scale);
759 if (transition) {
760 graphNode.classList.add('visible-transition');
761 clearTimeout(graph.transitionTimout);
762 graph.transitionTimout = setTimeout(function(){
763 graphNode.classList.remove('visible-transition');
764 }, 1000);
765 }
766 var translateString = "translate(" + translate[0] + "px," + translate[1] + " px) scale(" + scale + ")";
767 graphNode.style.transform = translateString;
768 graph.dragSvg.translate(translate);
769 graph.dragSvg.scale(scale);
770 }
771
772 zoomed(){
773 this.state.justScaleTransGraph = true;
774 var scale = this.dragSvg.scale();
775 this.translateClipped(d3.event.translate, scale);
776 }
777
778
779 getSvgViewDimensions() {
780 var canvasWidth = this.parentNode.clientWidth;
781 var documentElement = document.documentElement;
782 var canvasHeight = documentElement.clientHeight;
783 return [canvasWidth, canvasHeight];
784 }
785
786
787 minScale() {
788 var graph = this;
789 var dimensions = this.getSvgViewDimensions();
790 var width = graph.maxGraphX - graph.minGraphX;
791 var height = graph.maxGraphY - graph.minGraphY;
792 var minScale = dimensions[0] / (width + GRAPH_MARGIN * 2);
793 var minScaleYCandidate = dimensions[1] / (height + GRAPH_MARGIN * 2);
794 if (minScaleYCandidate < minScale) {
795 minScale = minScaleYCandidate;
796 }
797 this.dragSvg.scaleExtent([minScale, 1.5]);
798 return minScale;
799 }
800
801 fitGraphViewToWindow() {
802 this.svg.attr("height", document.documentElement.clientHeight + "px");
803 this.translateClipped(this.dragSvg.translate(), this.dragSvg.scale());
804 }
805
806 toggleTypes() {
807 var graph = this;
808 graph.state.showTypes = !graph.state.showTypes;
809 var element = document.getElementById('toggle-types');
810 if (graph.state.showTypes) {
811 element.classList.add('button-input-toggled');
812 } else {
813 element.classList.remove('button-input-toggled');
814 }
815 graph.updateGraphVisibility();
816 }
817
818 viewSelection() {
819 var graph = this;
820 var minX, maxX, minY, maxY;
821 var hasSelection = false;
822 graph.visibleNodes.each(function(n) {
823 if (this.classList.contains("selected")) {
824 hasSelection = true;
825 minX = minX ? Math.min(minX, n.x) : n.x;
826 maxX = maxX ? Math.max(maxX, n.x + n.getTotalNodeWidth()) :
827 n.x + n.getTotalNodeWidth();
828 minY = minY ? Math.min(minY, n.y) : n.y;
829 maxY = maxY ? Math.max(maxY, n.y + DEFAULT_NODE_HEIGHT) :
830 n.y + DEFAULT_NODE_HEIGHT;
831 }
832 });
833 if (hasSelection) {
834 graph.viewGraphRegion(minX - NODE_INPUT_WIDTH, minY - 60,
835 maxX + NODE_INPUT_WIDTH, maxY + 60,
836 true);
837 }
838 }
839
840 viewGraphRegion(minX, minY, maxX, maxY, transition) {
841 var graph = this;
842 var dimensions = this.getSvgViewDimensions();
843 var width = maxX - minX;
844 var height = maxY - minY;
845 var scale = Math.min(dimensions[0] / width, dimensions[1] / height);
846 scale = Math.min(1.5, scale);
847 scale = Math.max(graph.minScale(), scale);
848 var translation = [-minX*scale, -minY*scale];
849 translation = graph.getVisibleTranslation(translation, scale);
850 graph.translateClipped(translation, scale, transition);
851 }
852
853 viewWholeGraph() {
854 var graph = this;
855 var minScale = graph.minScale();
856 var translation = [0, 0];
857 translation = graph.getVisibleTranslation(translation, minScale);
858 graph.translateClipped(translation, minScale);
859 }
860 }
OLDNEW
« no previous file with comments | « tools/turbolizer/graph-layout.js ('k') | tools/turbolizer/hide-selected.png » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698