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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/turbolizer/graph-layout.js ('k') | tools/turbolizer/hide-selected.png » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/turbolizer/graph-view.js
diff --git a/tools/turbolizer/graph-view.js b/tools/turbolizer/graph-view.js
new file mode 100644
index 0000000000000000000000000000000000000000..aa1b638f57ba248de205754882c872ff48231405
--- /dev/null
+++ b/tools/turbolizer/graph-view.js
@@ -0,0 +1,860 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+"use strict";
+
+class GraphView extends View {
+ constructor (d3, id, nodes, edges, broker) {
+ super(id, broker);
+ var graph = this;
+
+ var svg = this.divElement.append("svg").attr('version','1.1').attr("width", "100%");
+ graph.svg = svg;
+
+ graph.nodes = nodes || [];
+ graph.edges = edges || [];
+
+ graph.minGraphX = 0;
+ graph.maxGraphX = 1;
+ graph.minGraphY = 0;
+ graph.maxGraphY = 1;
+
+ graph.state = {
+ selection: null,
+ mouseDownNode: null,
+ justDragged: false,
+ justScaleTransGraph: false,
+ lastKeyDown: -1,
+ showTypes: false
+ };
+
+ var selectionHandler = {
+ clear: function() {
+ broker.clear(selectionHandler);
+ },
+ select: function(items, selected) {
+ var ranges = [];
+ for (var d of items) {
+ if (selected) {
+ d.classList.add("selected");
+ } else {
+ d.classList.remove("selected");
+ }
+ var data = d.__data__;
+ ranges.push([data.pos, data.pos + 1, data.id]);
+ }
+ broker.select(selectionHandler, ranges, selected);
+ },
+ selectionDifference: function(span1, inclusive1, span2, inclusive2) {
+ // Should not be called
+ },
+ brokeredSelect: function(ranges, selected) {
+ var test = [].entries().next();
+ var selection = graph.nodes
+ .filter(function(n) {
+ var pos = n.pos;
+ for (var range of ranges) {
+ var start = range[0];
+ var end = range[1];
+ var id = range[2];
+ if (end != undefined) {
+ if (pos >= start && pos < end) {
+ return true;
+ }
+ } else if (start != undefined) {
+ if (pos === start) {
+ return true;
+ }
+ } else {
+ if (n.id === id) {
+ return true;
+ }
+ }
+ }
+ return false;
+ });
+ var newlySelected = new Set();
+ selection.forEach(function(n) {
+ newlySelected.add(n);
+ if (!n.visible) {
+ n.visible = true;
+ }
+ });
+ graph.updateGraphVisibility();
+ graph.visibleNodes.each(function(n) {
+ if (newlySelected.has(n)) {
+ graph.state.selection.select(this, selected);
+ }
+ });
+ graph.updateGraphVisibility();
+ graph.viewSelection();
+ },
+ brokeredClear: function() {
+ graph.state.selection.clear();
+ }
+ };
+ broker.addSelectionHandler(selectionHandler);
+
+ graph.state.selection = new Selection(selectionHandler);
+
+ var defs = svg.append('svg:defs');
+ defs.append('svg:marker')
+ .attr('id', 'end-arrow')
+ .attr('viewBox', '0 -4 8 8')
+ .attr('refX', 2)
+ .attr('markerWidth', 2.5)
+ .attr('markerHeight', 2.5)
+ .attr('orient', 'auto')
+ .append('svg:path')
+ .attr('d', 'M0,-4L8,0L0,4');
+
+ this.graphElement = svg.append("g");
+ graph.visibleEdges = this.graphElement.append("g").selectAll("g");
+ graph.visibleNodes = this.graphElement.append("g").selectAll("g");
+
+ graph.drag = d3.behavior.drag()
+ .origin(function(d){
+ return {x: d.x, y: d.y};
+ })
+ .on("drag", function(args){
+ graph.state.justDragged = true;
+ graph.dragmove.call(graph, args);
+ })
+
+ d3.select("#upload").on("click", function(){
+ document.getElementById("hidden-file-upload").click();
+ });
+
+ d3.select("#layout").on("click", function(){
+ graph.updateGraphVisibility();
+ graph.layoutGraph();
+ graph.updateGraphVisibility();
+ graph.viewWholeGraph();
+ });
+
+ d3.select("#show-all").on("click", function(){
+ graph.nodes.filter(function(n) { n.visible = true; })
+ graph.edges.filter(function(e) { e.visible = true; })
+ graph.updateGraphVisibility();
+ graph.viewWholeGraph();
+ });
+
+ d3.select("#hide-unselected").on("click", function() {
+ var unselected = graph.visibleNodes.filter(function(n) {
+ return !this.classList.contains("selected");
+ });
+ unselected.each(function(n) {
+ n.visible = false;
+ });
+ graph.updateGraphVisibility();
+ });
+
+ d3.select("#hide-selected").on("click", function() {
+ var selected = graph.visibleNodes.filter(function(n) {
+ return this.classList.contains("selected");
+ });
+ selected.each(function(n) {
+ n.visible = false;
+ });
+ graph.state.selection.clear();
+ graph.updateGraphVisibility();
+ });
+
+ d3.select("#zoom-selection").on("click", function() {
+ graph.viewSelection();
+ });
+
+ d3.select("#toggle-types").on("click", function() {
+ graph.toggleTypes();
+ });
+
+ d3.select("#search-input").on("keydown", function() {
+ if (d3.event.keyCode == 13) {
+ graph.state.selection.clear();
+ var reg = new RegExp(this.value);
+ var selected = graph.visibleNodes.each(function(n) {
+ if (reg.exec(n.getDisplayLabel()) != null ||
+ (graph.state.showTypes && reg.exec(n.getDisplayType())) ||
+ reg.exec(n.opcode) != null) {
+ graph.state.selection.select(this, true);
+ }
+ });
+ this.blur();
+ graph.viewSelection();
+ }
+ });
+
+ // listen for key events
+ d3.select(window).on("keydown", function(e){
+ graph.svgKeyDown.call(graph);
+ })
+ .on("keyup", function(){
+ graph.svgKeyUp.call(graph);
+ });
+ svg.on("mousedown", function(d){graph.svgMouseDown.call(graph, d);});
+ svg.on("mouseup", function(d){graph.svgMouseUp.call(graph, d);});
+
+ graph.dragSvg = d3.behavior.zoom()
+ .on("zoom", function(){
+ if (d3.event.sourceEvent.shiftKey){
+ return false;
+ } else{
+ graph.zoomed.call(graph);
+ }
+ return true;
+ })
+ .on("zoomstart", function(){
+ if (!d3.event.sourceEvent.shiftKey) d3.select('body').style("cursor", "move");
+ })
+ .on("zoomend", function(){
+ d3.select('body').style("cursor", "auto");
+ });
+
+ svg.call(graph.dragSvg).on("dblclick.zoom", null);
+ }
+
+ static get selectedClass() {
+ return "selected";
+ }
+ static get rectClass() {
+ return "nodeStyle";
+ }
+ static get activeEditId() {
+ return "active-editing";
+ }
+ static get nodeRadius() {
+ return 50;
+ }
+
+ getNodeHeight(graph) {
+ if (this.state.showTypes) {
+ return DEFAULT_NODE_HEIGHT + TYPE_HEIGHT;
+ } else {
+ return DEFAULT_NODE_HEIGHT;
+ }
+ }
+
+ dragmove(d) {
+ var graph = this;
+ d.x += d3.event.dx;
+ d.y += d3.event.dy;
+ graph.updateGraphVisibility();
+ }
+
+ initializeContent(data, rememberedSelection) {
+ this.createGraph(data);
+ if (rememberedSelection != null) {
+ this.attachSelection(rememberedSelection);
+ }
+ this.updateGraphVisibility();
+ }
+
+ deleteContent() {
+ if (this.visibleNodes) {
+ this.nodes = [];
+ this.edges = [];
+ this.nodeMap = [];
+ this.updateGraphVisibility();
+ }
+ };
+
+ createGraph(data) {
+ var g = this;
+ g.nodes = data.nodes;
+ g.nodeMap = [];
+ var textMeasure = document.getElementById('text-measure');
+ g.nodes.forEach(function(n, i){
+ n.__proto__ = Node;
+ n.visible = false;
+ n.x = 0;
+ n.y = 0;
+ n.rank = MAX_RANK_SENTINEL;
+ n.inputs = [];
+ n.outputs = [];
+ n.rpo = -1;
+ n.outputApproach = MINIMUM_NODE_OUTPUT_APPROACH;
+ n.cfg = n.control;
+ g.nodeMap[n.id] = n;
+ n.displayLabel = n.getDisplayLabel();
+ textMeasure.textContent = n.getDisplayLabel();
+ var width = textMeasure.getComputedTextLength();
+ textMeasure.textContent = n.getDisplayType();
+ width = Math.max(width, textMeasure.getComputedTextLength());
+ n.width = Math.alignUp(width + NODE_INPUT_WIDTH * 2,
+ NODE_INPUT_WIDTH);
+ });
+ g.edges = [];
+ data.edges.forEach(function(e, i){
+ var t = g.nodeMap[e.target];
+ var s = g.nodeMap[e.source];
+ var newEdge = new Edge(t, e.index, s, e.type);
+ t.inputs.push(newEdge);
+ s.outputs.push(newEdge);
+ g.edges.push(newEdge);
+ if (e.type == 'control') {
+ s.cfg = true;
+ }
+ });
+ g.nodes.forEach(function(n, i) {
+ n.visible = isNodeInitiallyVisible(n);
+ });
+ g.fitGraphViewToWindow();
+ g.updateGraphVisibility();
+ g.layoutGraph();
+ g.updateGraphVisibility();
+ g.viewWholeGraph();
+ }
+
+ updateInputAndOutputBubbles() {
+ var g = this;
+ var s = g.visibleBubbles;
+ s.classed("filledBubbleStyle", function(c) {
+ var components = this.id.split(',');
+ if (components[0] == "ib") {
+ var edge = g.nodeMap[components[3]].inputs[components[2]];
+ return edge.isVisible();
+ } else {
+ return g.nodeMap[components[1]].areAnyOutputsVisible() == 2;
+ }
+ }).classed("halfFilledBubbleStyle", function(c) {
+ var components = this.id.split(',');
+ if (components[0] == "ib") {
+ var edge = g.nodeMap[components[3]].inputs[components[2]];
+ return false;
+ } else {
+ return g.nodeMap[components[1]].areAnyOutputsVisible() == 1;
+ }
+ }).classed("bubbleStyle", function(c) {
+ var components = this.id.split(',');
+ if (components[0] == "ib") {
+ var edge = g.nodeMap[components[3]].inputs[components[2]];
+ return !edge.isVisible();
+ } else {
+ return g.nodeMap[components[1]].areAnyOutputsVisible() == 0;
+ }
+ });
+ s.each(function(c) {
+ var components = this.id.split(',');
+ if (components[0] == "ob") {
+ var from = g.nodeMap[components[1]];
+ var x = from.getOutputX();
+ var y = g.getNodeHeight() + DEFAULT_NODE_BUBBLE_RADIUS / 2 + 4;
+ var transform = "translate(" + x + "," + y + ")";
+ this.setAttribute('transform', transform);
+ }
+ });
+ }
+
+ attachSelection(s) {
+ var graph = this;
+ if (s.size != 0) {
+ this.visibleNodes.each(function(n) {
+ if (s.has(this.__data__.id)) {
+ graph.state.selection.select(this, true);
+ }
+ });
+ }
+ }
+
+ detachSelection() {
+ var selection = this.state.selection.detachSelection();
+ var result = new Set();
+ for (var i of selection) {
+ result.add(i.__data__.id);
+ };
+ return result;
+ }
+
+ pathMouseDown(path, d) {
+ d3.event.stopPropagation();
+ this.state.selection.clear();
+ this.state.selection.add(path);
+ };
+
+ nodeMouseDown(node, d) {
+ d3.event.stopPropagation();
+ this.state.mouseDownNode = d;
+ }
+
+ nodeMouseUp(d3node, d) {
+ var graph = this,
+ state = graph.state,
+ consts = graph.consts;
+
+ var mouseDownNode = state.mouseDownNode;
+
+ if (!mouseDownNode) return;
+
+ if (mouseDownNode !== d){
+ // we're in a different node: create new edge for mousedown edge and add to graph
+ var newEdge = {source: mouseDownNode, target: d};
+ var filtRes = graph.visibleEdges.filter(function(d){
+ if (d.source === newEdge.target && d.target === newEdge.source){
+ graph.edges.splice(graph.edges.indexOf(d), 1);
+ }
+ return d.source === newEdge.source && d.target === newEdge.target;
+ });
+ if (!filtRes[0].length){
+ graph.edges.push(newEdge);
+ graph.updateGraphVisibility();
+ }
+ } else{
+ // we're in the same node
+ if (state.justDragged) {
+ // dragged, not clicked
+ state.justDragged = false;
+ } else{
+ // clicked, not dragged
+ var extend = d3.event.shiftKey;
+ var selection = graph.state.selection;
+ if (!extend) {
+ selection.clear();
+ }
+ selection.select(d3node[0][0], true);
+ }
+ }
+ }
+
+ selectSourcePositions(start, end, selected) {
+ var graph = this;
+ var map = [];
+ var sel = graph.nodes.filter(function(n) {
+ var pos = (n.pos === undefined)
+ ? -1
+ : n.getFunctionRelativeSourcePosition(graph);
+ if (pos >= start && pos < end) {
+ map[n.id] = true;
+ n.visible = true;
+ }
+ });
+ graph.updateGraphVisibility();
+ graph.visibleNodes.filter(function(n) { return map[n.id]; })
+ .each(function(n) {
+ var selection = graph.state.selection;
+ selection.select(d3.select(this), selected);
+ });
+ }
+
+ svgMouseDown() {
+ this.state.graphMouseDown = true;
+ }
+
+ svgMouseUp() {
+ var graph = this,
+ state = graph.state;
+ if (state.justScaleTransGraph) {
+ // Dragged
+ state.justScaleTransGraph = false;
+ } else {
+ // Clicked
+ if (state.mouseDownNode == null) {
+ graph.state.selection.clear();
+ }
+ }
+ state.mouseDownNode = null;
+ state.graphMouseDown = false;
+ }
+
+ svgKeyDown() {
+ var state = this.state;
+ var graph = this;
+
+ // Don't handle key press repetition
+ if(state.lastKeyDown !== -1) return;
+
+ var getEdgeFrontier = function(inEdges) {
+ var frontierSet = new Set();
+ state.selection.selection.forEach(function(element) {
+ var nodes = inEdges ? element.__data__.inputs : element.__data__.outputs;
+ nodes.forEach(function(i) {
+ i.visible = true;
+ var candidate = inEdges ? i.source : i.target;
+ candidate.visible = true;
+ frontierSet.add(candidate);
+ });
+ });
+ graph.updateGraphVisibility();
+ return graph.visibleNodes.filter(function(n) {
+ return frontierSet.has(n);
+ });
+ }
+
+ var allowRepetition = true;
+ switch(d3.event.keyCode) {
+ case 38:
+ case 40: {
+ var frontier = getEdgeFrontier(d3.event.keyCode == 38);
+ if (!d3.event.shiftKey) {
+ state.selection.clear();
+ }
+ frontier.each(function(n) {
+ state.selection.select(this, true);
+ });
+ graph.updateGraphVisibility();
+ allowRepetition = false;
+ break;
+ }
+ }
+ if (!allowRepetition) {
+ state.lastKeyDown = d3.event.keyCode;
+ }
+ }
+
+ svgKeyUp() {
+ this.state.lastKeyDown = -1
+ };
+
+ layoutEdges() {
+ var graph = this;
+ graph.maxGraphX = graph.maxGraphNodeX;
+ this.visibleEdges.attr("d", function(edge){
+ return edge.generatePath(graph);
+ });
+ }
+
+ layoutGraph() {
+ layoutNodeGraph(this);
+ }
+
+ // call to propagate changes to graph
+ updateGraphVisibility() {
+
+ var graph = this,
+ state = graph.state;
+
+ var filteredEdges = graph.edges.filter(function(e) { return e.isVisible(); });
+ var visibleEdges = graph.visibleEdges.data(filteredEdges, function(edge) {
+ return edge.stringID();
+ });
+
+ // add new paths
+ visibleEdges.enter()
+ .append('path')
+ .style('marker-end','url(#end-arrow)')
+ .classed('hidden', function(e) {
+ return !e.isVisible();
+ })
+ .attr("id", function(edge){ return "e," + edge.stringID(); })
+ .on("mousedown", function(d){
+ graph.pathMouseDown.call(graph, d3.select(this), d);
+ })
+
+ // Set the correct styles on all of the paths
+ visibleEdges.classed('value', function(e) {
+ return e.type == 'value' || e.type == 'context';
+ }).classed('control', function(e) {
+ return e.type == 'control';
+ }).classed('effect', function(e) {
+ return e.type == 'effect';
+ }).classed('frame-state', function(e) {
+ return e.type == 'frame-state';
+ }).attr('stroke-dasharray', function(e) {
+ if (e.type == 'frame-state') return "10,10";
+ return (e.type == 'effect') ? "5,5" : "";
+ });
+
+ // remove old links
+ visibleEdges.exit().remove();
+
+ graph.visibleEdges = visibleEdges;
+
+ // update existing nodes
+ var filteredNodes = graph.nodes.filter(function(n) { return n.visible; });
+ graph.visibleNodes = graph.visibleNodes.data(filteredNodes, function(d) {
+ return d.id;
+ });
+ graph.visibleNodes.attr("transform", function(n){
+ return "translate(" + n.x + "," + n.y + ")";
+ }).select('rect').
+ attr(HEIGHT, function(d) { return graph.getNodeHeight(); });
+
+ // add new nodes
+ var newGs = graph.visibleNodes.enter()
+ .append("g");
+
+ newGs.classed("control", function(n) { return n.isControl(); })
+ .classed("javascript", function(n) { return n.isJavaScript(); })
+ .classed("input", function(n) { return n.isInput(); })
+ .classed("simplified", function(n) { return n.isSimplified(); })
+ .classed("machine", function(n) { return n.isMachine(); })
+ .attr("transform", function(d){ return "translate(" + d.x + "," + d.y + ")";})
+ .on("mousedown", function(d){
+ graph.nodeMouseDown.call(graph, d3.select(this), d);
+ })
+ .on("mouseup", function(d){
+ graph.nodeMouseUp.call(graph, d3.select(this), d);
+ })
+ .call(graph.drag);
+
+ newGs.append("rect")
+ .attr("rx", 10)
+ .attr("ry", 10)
+ .attr(WIDTH, function(d) { return d.getTotalNodeWidth(); })
+ .attr(HEIGHT, function(d) { return graph.getNodeHeight(); })
+
+ function appendInputAndOutputBubbles(g, d) {
+ for (var i = 0; i < d.inputs.length; ++i) {
+ var x = d.getInputX(i);
+ var y = -DEFAULT_NODE_BUBBLE_RADIUS / 2 - 4;
+ var s = g.append('circle')
+ .classed("filledBubbleStyle", function(c) {
+ return d.inputs[i].isVisible();
+ } )
+ .classed("bubbleStyle", function(c) {
+ return !d.inputs[i].isVisible();
+ } )
+ .attr("id", "ib," + d.inputs[i].stringID())
+ .attr("r", DEFAULT_NODE_BUBBLE_RADIUS)
+ .attr("transform", function(d) {
+ return "translate(" + x + "," + y + ")";
+ })
+ .on("mousedown", function(d){
+ var components = this.id.split(',');
+ var node = graph.nodeMap[components[3]];
+ var edge = node.inputs[components[2]];
+ var visible = !edge.isVisible();
+ node.setInputVisibility(components[2], visible);
+ d3.event.stopPropagation();
+ graph.updateGraphVisibility();
+ });
+ }
+ if (d.outputs.length != 0) {
+ var x = d.getOutputX();
+ var y = graph.getNodeHeight() + DEFAULT_NODE_BUBBLE_RADIUS / 2 + 4;
+ var s = g.append('circle')
+ .classed("filledBubbleStyle", function(c) {
+ return d.areAnyOutputsVisible() == 2;
+ } )
+ .classed("halFilledBubbleStyle", function(c) {
+ return d.areAnyOutputsVisible() == 1;
+ } )
+ .classed("bubbleStyle", function(c) {
+ return d.areAnyOutputsVisible() == 0;
+ } )
+ .attr("id", "ob," + d.id)
+ .attr("r", DEFAULT_NODE_BUBBLE_RADIUS)
+ .attr("transform", function(d) {
+ return "translate(" + x + "," + y + ")";
+ })
+ .on("mousedown", function(d) {
+ d.setOutputVisibility(d.areAnyOutputsVisible() == 0);
+ d3.event.stopPropagation();
+ graph.updateGraphVisibility();
+ });
+ }
+ }
+
+ newGs.each(function(d){
+ appendInputAndOutputBubbles(d3.select(this), d);
+ });
+
+ newGs.each(function(d){
+ d3.select(this).append("text")
+ .classed("label", true)
+ .attr("text-anchor","right")
+ .attr("dx", "5")
+ .attr("dy", DEFAULT_NODE_HEIGHT / 2 + 5)
+ .append('tspan')
+ .text(function(l) {
+ return d.getDisplayLabel();
+ })
+ .append("title")
+ .text(function(l) {
+ return d.getLabel();
+ })
+ if (d.type != undefined) {
+ d3.select(this).append("text")
+ .classed("label", true)
+ .classed("type", true)
+ .attr("text-anchor","right")
+ .attr("dx", "5")
+ .attr("dy", DEFAULT_NODE_HEIGHT / 2 + TYPE_HEIGHT + 5)
+ .append('tspan')
+ .text(function(l) {
+ return d.getDisplayType();
+ })
+ .append("title")
+ .text(function(l) {
+ return d.getType();
+ })
+ }
+ });
+
+ graph.visibleNodes.select('.type').each(function (d) {
+ this.setAttribute('visibility', graph.state.showTypes ? 'visible' : 'hidden');
+ });
+
+ // remove old nodes
+ graph.visibleNodes.exit().remove();
+
+ graph.visibleBubbles = d3.selectAll('circle');
+
+ graph.updateInputAndOutputBubbles();
+
+ graph.layoutEdges();
+
+ graph.svg.style.height = '100%';
+ }
+
+ getVisibleTranslation(translate, scale) {
+ var graph = this;
+ var height = (graph.maxGraphY - graph.minGraphY + 2 * GRAPH_MARGIN) * scale;
+ var width = (graph.maxGraphX - graph.minGraphX + 2 * GRAPH_MARGIN) * scale;
+
+ var dimensions = this.getSvgViewDimensions();
+
+ var baseY = translate[1];
+ var minY = (graph.minGraphY - GRAPH_MARGIN) * scale;
+ var maxY = (graph.maxGraphY + GRAPH_MARGIN) * scale;
+
+ var adjustY = 0;
+ var adjustYCandidate = 0;
+ if ((maxY + baseY) < dimensions[1]) {
+ adjustYCandidate = dimensions[1] - (maxY + baseY);
+ if ((minY + baseY + adjustYCandidate) > 0) {
+ adjustY = (dimensions[1] / 2) - (maxY - (height / 2)) - baseY;
+ } else {
+ adjustY = adjustYCandidate;
+ }
+ } else if (-baseY < minY) {
+ adjustYCandidate = -(baseY + minY);
+ if ((maxY + baseY + adjustYCandidate) < dimensions[1]) {
+ adjustY = (dimensions[1] / 2) - (maxY - (height / 2)) - baseY;
+ } else {
+ adjustY = adjustYCandidate;
+ }
+ }
+ translate[1] += adjustY;
+
+ var baseX = translate[0];
+ var minX = (graph.minGraphX - GRAPH_MARGIN) * scale;
+ var maxX = (graph.maxGraphX + GRAPH_MARGIN) * scale;
+
+ var adjustX = 0;
+ var adjustXCandidate = 0;
+ if ((maxX + baseX) < dimensions[0]) {
+ adjustXCandidate = dimensions[0] - (maxX + baseX);
+ if ((minX + baseX + adjustXCandidate) > 0) {
+ adjustX = (dimensions[0] / 2) - (maxX - (width / 2)) - baseX;
+ } else {
+ adjustX = adjustXCandidate;
+ }
+ } else if (-baseX < minX) {
+ adjustXCandidate = -(baseX + minX);
+ if ((maxX + baseX + adjustXCandidate) < dimensions[0]) {
+ adjustX = (dimensions[0] / 2) - (maxX - (width / 2)) - baseX;
+ } else {
+ adjustX = adjustXCandidate;
+ }
+ }
+ translate[0] += adjustX;
+ return translate;
+ }
+
+ translateClipped(translate, scale, transition) {
+ var graph = this;
+ var graphNode = this.graphElement[0][0];
+ var translate = this.getVisibleTranslation(translate, scale);
+ if (transition) {
+ graphNode.classList.add('visible-transition');
+ clearTimeout(graph.transitionTimout);
+ graph.transitionTimout = setTimeout(function(){
+ graphNode.classList.remove('visible-transition');
+ }, 1000);
+ }
+ var translateString = "translate(" + translate[0] + "px," + translate[1] + "px) scale(" + scale + ")";
+ graphNode.style.transform = translateString;
+ graph.dragSvg.translate(translate);
+ graph.dragSvg.scale(scale);
+ }
+
+ zoomed(){
+ this.state.justScaleTransGraph = true;
+ var scale = this.dragSvg.scale();
+ this.translateClipped(d3.event.translate, scale);
+ }
+
+
+ getSvgViewDimensions() {
+ var canvasWidth = this.parentNode.clientWidth;
+ var documentElement = document.documentElement;
+ var canvasHeight = documentElement.clientHeight;
+ return [canvasWidth, canvasHeight];
+ }
+
+
+ minScale() {
+ var graph = this;
+ var dimensions = this.getSvgViewDimensions();
+ var width = graph.maxGraphX - graph.minGraphX;
+ var height = graph.maxGraphY - graph.minGraphY;
+ var minScale = dimensions[0] / (width + GRAPH_MARGIN * 2);
+ var minScaleYCandidate = dimensions[1] / (height + GRAPH_MARGIN * 2);
+ if (minScaleYCandidate < minScale) {
+ minScale = minScaleYCandidate;
+ }
+ this.dragSvg.scaleExtent([minScale, 1.5]);
+ return minScale;
+ }
+
+ fitGraphViewToWindow() {
+ this.svg.attr("height", document.documentElement.clientHeight + "px");
+ this.translateClipped(this.dragSvg.translate(), this.dragSvg.scale());
+ }
+
+ toggleTypes() {
+ var graph = this;
+ graph.state.showTypes = !graph.state.showTypes;
+ var element = document.getElementById('toggle-types');
+ if (graph.state.showTypes) {
+ element.classList.add('button-input-toggled');
+ } else {
+ element.classList.remove('button-input-toggled');
+ }
+ graph.updateGraphVisibility();
+ }
+
+ viewSelection() {
+ var graph = this;
+ var minX, maxX, minY, maxY;
+ var hasSelection = false;
+ graph.visibleNodes.each(function(n) {
+ if (this.classList.contains("selected")) {
+ hasSelection = true;
+ minX = minX ? Math.min(minX, n.x) : n.x;
+ maxX = maxX ? Math.max(maxX, n.x + n.getTotalNodeWidth()) :
+ n.x + n.getTotalNodeWidth();
+ minY = minY ? Math.min(minY, n.y) : n.y;
+ maxY = maxY ? Math.max(maxY, n.y + DEFAULT_NODE_HEIGHT) :
+ n.y + DEFAULT_NODE_HEIGHT;
+ }
+ });
+ if (hasSelection) {
+ graph.viewGraphRegion(minX - NODE_INPUT_WIDTH, minY - 60,
+ maxX + NODE_INPUT_WIDTH, maxY + 60,
+ true);
+ }
+ }
+
+ viewGraphRegion(minX, minY, maxX, maxY, transition) {
+ var graph = this;
+ var dimensions = this.getSvgViewDimensions();
+ var width = maxX - minX;
+ var height = maxY - minY;
+ var scale = Math.min(dimensions[0] / width, dimensions[1] / height);
+ scale = Math.min(1.5, scale);
+ scale = Math.max(graph.minScale(), scale);
+ var translation = [-minX*scale, -minY*scale];
+ translation = graph.getVisibleTranslation(translation, scale);
+ graph.translateClipped(translation, scale, transition);
+ }
+
+ viewWholeGraph() {
+ var graph = this;
+ var minScale = graph.minScale();
+ var translation = [0, 0];
+ translation = graph.getVisibleTranslation(translation, minScale);
+ graph.translateClipped(translation, minScale);
+ }
+}
« 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