���� JFIF �� � ( %"1"%)+...383,7(-.-
![]() Server : Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.4.20 System : Linux st2.domain.com 3.10.0-1127.10.1.el7.x86_64 #1 SMP Wed Jun 3 14:28:03 UTC 2020 x86_64 User : apache ( 48) PHP Version : 7.4.20 Disable Function : NONE Directory : /home/real/node-v13.0.1/deps/v8/tools/turbolizer/src/ |
// 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. import * as d3 from "d3"; import { layoutNodeGraph } from "../src/graph-layout"; import { GNode, nodeToStr } from "../src/node"; import { NODE_INPUT_WIDTH } from "../src/node"; import { DEFAULT_NODE_BUBBLE_RADIUS } from "../src/node"; import { Edge, edgeToStr } from "../src/edge"; import { PhaseView } from "../src/view"; import { MySelection } from "../src/selection"; import { partial } from "../src/util"; import { NodeSelectionHandler, ClearableHandler } from "./selection-handler"; import { Graph } from "./graph"; import { SelectionBroker } from "./selection-broker"; function nodeToStringKey(n: GNode) { return "" + n.id; } interface GraphState { showTypes: boolean; selection: MySelection; mouseDownNode: any; justDragged: boolean; justScaleTransGraph: boolean; hideDead: boolean; } export class GraphView extends PhaseView { divElement: d3.Selection<any, any, any, any>; svg: d3.Selection<any, any, any, any>; showPhaseByName: (p: string, s: Set<any>) => void; state: GraphState; selectionHandler: NodeSelectionHandler & ClearableHandler; graphElement: d3.Selection<any, any, any, any>; visibleNodes: d3.Selection<any, GNode, any, any>; visibleEdges: d3.Selection<any, Edge, any, any>; drag: d3.DragBehavior<any, GNode, GNode>; panZoom: d3.ZoomBehavior<SVGElement, any>; visibleBubbles: d3.Selection<any, any, any, any>; transitionTimout: number; graph: Graph; broker: SelectionBroker; phaseName: string; toolbox: HTMLElement; createViewElement() { const pane = document.createElement('div'); pane.setAttribute('id', "graph"); return pane; } constructor(idOrContainer: string | HTMLElement, broker: SelectionBroker, showPhaseByName: (s: string) => void, toolbox: HTMLElement) { super(idOrContainer); const view = this; this.broker = broker; this.showPhaseByName = showPhaseByName; this.divElement = d3.select(this.divNode); this.phaseName = ""; this.toolbox = toolbox; const svg = this.divElement.append("svg") .attr('version', '2.0') .attr("width", "100%") .attr("height", "100%"); svg.on("click", function (d) { view.selectionHandler.clear(); }); // Listen for key events. Note that the focus handler seems // to be important even if it does nothing. svg .attr("focusable", false) .on("focus", e => { }) .on("keydown", e => { view.svgKeyDown(); }); view.svg = svg; this.state = { selection: null, mouseDownNode: null, justDragged: false, justScaleTransGraph: false, showTypes: false, hideDead: false }; this.selectionHandler = { clear: function () { view.state.selection.clear(); broker.broadcastClear(this); view.updateGraphVisibility(); }, select: function (nodes: Array<GNode>, selected: boolean) { const locations = []; for (const node of nodes) { if (node.nodeLabel.sourcePosition) { locations.push(node.nodeLabel.sourcePosition); } if (node.nodeLabel.origin && node.nodeLabel.origin.bytecodePosition) { locations.push({ bytecodePosition: node.nodeLabel.origin.bytecodePosition }); } } view.state.selection.select(nodes, selected); broker.broadcastSourcePositionSelect(this, locations, selected); view.updateGraphVisibility(); }, brokeredNodeSelect: function (locations, selected: boolean) { if (!view.graph) return; const selection = view.graph.nodes(n => { return locations.has(nodeToStringKey(n)) && (!view.state.hideDead || n.isLive()); }); view.state.selection.select(selection, selected); // Update edge visibility based on selection. for (const n of view.graph.nodes()) { if (view.state.selection.isSelected(n)) { n.visible = true; n.inputs.forEach(e => { e.visible = e.visible || view.state.selection.isSelected(e.source); }); n.outputs.forEach(e => { e.visible = e.visible || view.state.selection.isSelected(e.target); }); } } view.updateGraphVisibility(); }, brokeredClear: function () { view.state.selection.clear(); view.updateGraphVisibility(); } }; view.state.selection = new MySelection(nodeToStringKey); const 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"); view.visibleEdges = this.graphElement.append("g"); view.visibleNodes = this.graphElement.append("g"); view.drag = d3.drag<any, GNode, GNode>() .on("drag", function (d) { d.x += d3.event.dx; d.y += d3.event.dy; view.updateGraphVisibility(); }); function zoomed() { if (d3.event.shiftKey) return false; view.graphElement.attr("transform", d3.event.transform); return true; } const zoomSvg = d3.zoom<SVGElement, any>() .scaleExtent([0.2, 40]) .on("zoom", zoomed) .on("start", function () { if (d3.event.shiftKey) return; d3.select('body').style("cursor", "move"); }) .on("end", function () { d3.select('body').style("cursor", "auto"); }); svg.call(zoomSvg).on("dblclick.zoom", null); view.panZoom = zoomSvg; } getEdgeFrontier(nodes: Iterable<GNode>, inEdges: boolean, edgeFilter: (e: Edge, i: number) => boolean) { const frontier: Set<Edge> = new Set(); for (const n of nodes) { const edges = inEdges ? n.inputs : n.outputs; let edgeNumber = 0; edges.forEach((edge: Edge) => { if (edgeFilter == undefined || edgeFilter(edge, edgeNumber)) { frontier.add(edge); } ++edgeNumber; }); } return frontier; } getNodeFrontier(nodes: Iterable<GNode>, inEdges: boolean, edgeFilter: (e: Edge, i: number) => boolean) { const view = this; const frontier: Set<GNode> = new Set(); let newState = true; const edgeFrontier = view.getEdgeFrontier(nodes, inEdges, edgeFilter); // Control key toggles edges rather than just turning them on if (d3.event.ctrlKey) { edgeFrontier.forEach(function (edge: Edge) { if (edge.visible) { newState = false; } }); } edgeFrontier.forEach(function (edge: Edge) { edge.visible = newState; if (newState) { const node = inEdges ? edge.source : edge.target; node.visible = true; frontier.add(node); } }); view.updateGraphVisibility(); if (newState) { return frontier; } else { return undefined; } } initializeContent(data, rememberedSelection) { this.show(); function createImgInput(id: string, title: string, onClick): HTMLElement { const input = document.createElement("input"); input.setAttribute("id", id); input.setAttribute("type", "image"); input.setAttribute("title", title); input.setAttribute("src", `img/${id}-icon.png`); input.className = "button-input graph-toolbox-item"; input.addEventListener("click", onClick); return input; } this.toolbox.appendChild(createImgInput("layout", "layout graph", partial(this.layoutAction, this))); this.toolbox.appendChild(createImgInput("show-all", "show all nodes", partial(this.showAllAction, this))); this.toolbox.appendChild(createImgInput("show-control", "show only control nodes", partial(this.showControlAction, this))); this.toolbox.appendChild(createImgInput("toggle-hide-dead", "toggle hide dead nodes", partial(this.toggleHideDead, this))); this.toolbox.appendChild(createImgInput("hide-unselected", "hide unselected", partial(this.hideUnselectedAction, this))); this.toolbox.appendChild(createImgInput("hide-selected", "hide selected", partial(this.hideSelectedAction, this))); this.toolbox.appendChild(createImgInput("zoom-selection", "zoom selection", partial(this.zoomSelectionAction, this))); this.toolbox.appendChild(createImgInput("toggle-types", "toggle types", partial(this.toggleTypesAction, this))); this.phaseName = data.name; this.createGraph(data.data, rememberedSelection); this.broker.addNodeHandler(this.selectionHandler); if (rememberedSelection != null && rememberedSelection.size > 0) { this.attachSelection(rememberedSelection); this.connectVisibleSelectedNodes(); this.viewSelection(); } else { this.viewWholeGraph(); } } deleteContent() { for (const item of this.toolbox.querySelectorAll(".graph-toolbox-item")) { item.parentElement.removeChild(item); } for (const n of this.graph.nodes()) { n.visible = false; } this.graph.forEachEdge((e: Edge) => { e.visible = false; }); this.updateGraphVisibility(); } public hide(): void { super.hide(); this.deleteContent(); } createGraph(data, rememberedSelection) { this.graph = new Graph(data); this.showControlAction(this); if (rememberedSelection != undefined) { for (const n of this.graph.nodes()) { n.visible = n.visible || rememberedSelection.has(nodeToStringKey(n)); } } this.graph.forEachEdge(e => e.visible = e.source.visible && e.target.visible); this.layoutGraph(); this.updateGraphVisibility(); } connectVisibleSelectedNodes() { const view = this; for (const n of view.state.selection) { n.inputs.forEach(function (edge: Edge) { if (edge.source.visible && edge.target.visible) { edge.visible = true; } }); n.outputs.forEach(function (edge: Edge) { if (edge.source.visible && edge.target.visible) { edge.visible = true; } }); } } updateInputAndOutputBubbles() { const view = this; const g = this.graph; const s = this.visibleBubbles; s.classed("filledBubbleStyle", function (c) { const components = this.id.split(','); if (components[0] == "ib") { const edge = g.nodeMap[components[3]].inputs[components[2]]; return edge.isVisible(); } else { return g.nodeMap[components[1]].areAnyOutputsVisible() == 2; } }).classed("halfFilledBubbleStyle", function (c) { const components = this.id.split(','); if (components[0] == "ib") { return false; } else { return g.nodeMap[components[1]].areAnyOutputsVisible() == 1; } }).classed("bubbleStyle", function (c) { const components = this.id.split(','); if (components[0] == "ib") { const edge = g.nodeMap[components[3]].inputs[components[2]]; return !edge.isVisible(); } else { return g.nodeMap[components[1]].areAnyOutputsVisible() == 0; } }); s.each(function (c) { const components = this.id.split(','); if (components[0] == "ob") { const from = g.nodeMap[components[1]]; const x = from.getOutputX(); const y = from.getNodeHeight(view.state.showTypes) + DEFAULT_NODE_BUBBLE_RADIUS; const transform = "translate(" + x + "," + y + ")"; this.setAttribute('transform', transform); } }); } attachSelection(s) { if (!(s instanceof Set)) return; this.selectionHandler.clear(); const selected = [...this.graph.nodes(n => s.has(this.state.selection.stringKey(n)) && (!this.state.hideDead || n.isLive()))]; this.selectionHandler.select(selected, true); } detachSelection() { return this.state.selection.detachSelection(); } selectAllNodes() { if (!d3.event.shiftKey) { this.state.selection.clear(); } const allVisibleNodes = [...this.graph.nodes(n => n.visible)]; this.state.selection.select(allVisibleNodes, true); this.updateGraphVisibility(); } layoutAction(graph: GraphView) { graph.layoutGraph(); graph.updateGraphVisibility(); graph.viewWholeGraph(); } showAllAction(view: GraphView) { for (const n of view.graph.nodes()) { n.visible = !view.state.hideDead || n.isLive(); } view.graph.forEachEdge((e: Edge) => { e.visible = e.source.visible || e.target.visible; }); view.updateGraphVisibility(); view.viewWholeGraph(); } showControlAction(view: GraphView) { for (const n of view.graph.nodes()) { n.visible = n.cfg && (!view.state.hideDead || n.isLive()); } view.graph.forEachEdge((e: Edge) => { e.visible = e.type == 'control' && e.source.visible && e.target.visible; }); view.updateGraphVisibility(); view.viewWholeGraph(); } toggleHideDead(view: GraphView) { view.state.hideDead = !view.state.hideDead; if (view.state.hideDead) { view.hideDead(); } else { view.showDead(); } const element = document.getElementById('toggle-hide-dead'); element.classList.toggle('button-input-toggled', view.state.hideDead); } hideDead() { for (const n of this.graph.nodes()) { if (!n.isLive()) { n.visible = false; this.state.selection.select([n], false); } } this.updateGraphVisibility(); } showDead() { for (const n of this.graph.nodes()) { if (!n.isLive()) { n.visible = true; } } this.updateGraphVisibility(); } hideUnselectedAction(view: GraphView) { for (const n of view.graph.nodes()) { if (!view.state.selection.isSelected(n)) { n.visible = false; } } view.updateGraphVisibility(); } hideSelectedAction(view: GraphView) { for (const n of view.graph.nodes()) { if (view.state.selection.isSelected(n)) { n.visible = false; } } view.selectionHandler.clear(); } zoomSelectionAction(view: GraphView) { view.viewSelection(); } toggleTypesAction(view: GraphView) { view.toggleTypes(); } searchInputAction(searchBar: HTMLInputElement, e: KeyboardEvent, onlyVisible: boolean) { if (e.keyCode == 13) { this.selectionHandler.clear(); const query = searchBar.value; window.sessionStorage.setItem("lastSearch", query); if (query.length == 0) return; const reg = new RegExp(query); const filterFunction = (n: GNode) => { return (reg.exec(n.getDisplayLabel()) != null || (this.state.showTypes && reg.exec(n.getDisplayType())) || (reg.exec(n.getTitle())) || reg.exec(n.nodeLabel.opcode) != null); }; const selection = [...this.graph.nodes(n => { if ((e.ctrlKey || n.visible || !onlyVisible) && filterFunction(n)) { if (e.ctrlKey || !onlyVisible) n.visible = true; return true; } return false; })]; this.selectionHandler.select(selection, true); this.connectVisibleSelectedNodes(); this.updateGraphVisibility(); searchBar.blur(); this.viewSelection(); } e.stopPropagation(); } svgKeyDown() { const view = this; const state = this.state; const showSelectionFrontierNodes = (inEdges: boolean, filter: (e: Edge, i: number) => boolean, doSelect: boolean) => { const frontier = view.getNodeFrontier(state.selection, inEdges, filter); if (frontier != undefined && frontier.size) { if (doSelect) { if (!d3.event.shiftKey) { state.selection.clear(); } state.selection.select([...frontier], true); } view.updateGraphVisibility(); } }; let eventHandled = true; // unless the below switch defaults switch (d3.event.keyCode) { case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // '1'-'9' showSelectionFrontierNodes(true, (edge: Edge, index: number) => index == (d3.event.keyCode - 49), !d3.event.ctrlKey); break; case 97: case 98: case 99: case 100: case 101: case 102: case 103: case 104: case 105: // 'numpad 1'-'numpad 9' showSelectionFrontierNodes(true, (edge, index) => index == (d3.event.keyCode - 97), !d3.event.ctrlKey); break; case 67: // 'c' showSelectionFrontierNodes(d3.event.altKey, (edge, index) => edge.type == 'control', true); break; case 69: // 'e' showSelectionFrontierNodes(d3.event.altKey, (edge, index) => edge.type == 'effect', true); break; case 79: // 'o' showSelectionFrontierNodes(false, undefined, false); break; case 73: // 'i' if (!d3.event.ctrlKey && !d3.event.shiftKey) { showSelectionFrontierNodes(true, undefined, false); } else { eventHandled = false; } break; case 65: // 'a' view.selectAllNodes(); break; case 38: // UP case 40: { // DOWN showSelectionFrontierNodes(d3.event.keyCode == 38, undefined, true); break; } case 82: // 'r' if (!d3.event.ctrlKey && !d3.event.shiftKey) { this.layoutAction(this); } else { eventHandled = false; } break; case 83: // 's' view.selectOrigins(); break; default: eventHandled = false; break; } if (eventHandled) { d3.event.preventDefault(); } } layoutGraph() { console.time("layoutGraph"); layoutNodeGraph(this.graph, this.state.showTypes); const extent = this.graph.redetermineGraphBoundingBox(this.state.showTypes); this.panZoom.translateExtent(extent); this.minScale(); console.timeEnd("layoutGraph"); } selectOrigins() { const state = this.state; const origins = []; let phase = this.phaseName; const selection = new Set<any>(); for (const n of state.selection) { const origin = n.nodeLabel.origin; if (origin) { phase = origin.phase; const node = this.graph.nodeMap[origin.nodeId]; if (phase === this.phaseName && node) { origins.push(node); } else { selection.add(`${origin.nodeId}`); } } } // Only go through phase reselection if we actually need // to display another phase. if (selection.size > 0 && phase !== this.phaseName) { this.showPhaseByName(phase, selection); } else if (origins.length > 0) { this.selectionHandler.clear(); this.selectionHandler.select(origins, true); } } // call to propagate changes to graph updateGraphVisibility() { const view = this; const graph = this.graph; const state = this.state; if (!graph) return; const filteredEdges = [...graph.filteredEdges(function (e) { return e.source.visible && e.target.visible; })]; const selEdges = view.visibleEdges.selectAll<SVGPathElement, Edge>("path").data(filteredEdges, edgeToStr); // remove old links selEdges.exit().remove(); // add new paths const newEdges = selEdges.enter() .append('path'); newEdges.style('marker-end', 'url(#end-arrow)') .attr("id", function (edge) { return "e," + edge.stringID(); }) .on("click", function (edge) { d3.event.stopPropagation(); if (!d3.event.shiftKey) { view.selectionHandler.clear(); } view.selectionHandler.select([edge.source, edge.target], true); }) .attr("adjacentToHover", "false") .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" : ""; }); const newAndOldEdges = newEdges.merge(selEdges); newAndOldEdges.classed('hidden', e => !e.isVisible()); // select existing nodes const filteredNodes = [...graph.nodes(n => n.visible)]; const allNodes = view.visibleNodes.selectAll<SVGGElement, GNode>("g"); const selNodes = allNodes.data(filteredNodes, nodeToStr); // remove old nodes selNodes.exit().remove(); // add new nodes const newGs = selNodes.enter() .append("g"); newGs.classed("turbonode", function (n) { return true; }) .classed("control", function (n) { return n.isControl(); }) .classed("live", function (n) { return n.isLive(); }) .classed("dead", function (n) { return !n.isLive(); }) .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(); }) .on('mouseenter', function (node) { const visibleEdges = view.visibleEdges.selectAll<SVGPathElement, Edge>('path'); const adjInputEdges = visibleEdges.filter(e => e.target === node); const adjOutputEdges = visibleEdges.filter(e => e.source === node); adjInputEdges.attr('relToHover', "input"); adjOutputEdges.attr('relToHover', "output"); const adjInputNodes = adjInputEdges.data().map(e => e.source); const visibleNodes = view.visibleNodes.selectAll<SVGGElement, GNode>("g"); visibleNodes.data<GNode>(adjInputNodes, nodeToStr).attr('relToHover', "input"); const adjOutputNodes = adjOutputEdges.data().map(e => e.target); visibleNodes.data<GNode>(adjOutputNodes, nodeToStr).attr('relToHover', "output"); view.updateGraphVisibility(); }) .on('mouseleave', function (node) { const visibleEdges = view.visibleEdges.selectAll<SVGPathElement, Edge>('path'); const adjEdges = visibleEdges.filter(e => e.target === node || e.source === node); adjEdges.attr('relToHover', "none"); const adjNodes = adjEdges.data().map(e => e.target).concat(adjEdges.data().map(e => e.source)); const visibleNodes = view.visibleNodes.selectAll<SVGPathElement, GNode>("g"); visibleNodes.data(adjNodes, nodeToStr).attr('relToHover', "none"); view.updateGraphVisibility(); }) .on("click", d => { if (!d3.event.shiftKey) view.selectionHandler.clear(); view.selectionHandler.select([d], undefined); d3.event.stopPropagation(); }) .call(view.drag); newGs.append("rect") .attr("rx", 10) .attr("ry", 10) .attr('width', function (d) { return d.getTotalNodeWidth(); }) .attr('height', function (d) { return d.getNodeHeight(view.state.showTypes); }); function appendInputAndOutputBubbles(g, d) { for (let i = 0; i < d.inputs.length; ++i) { const x = d.getInputX(i); const y = -DEFAULT_NODE_BUBBLE_RADIUS; 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("click", function (this: SVGCircleElement, d) { const components = this.id.split(','); const node = graph.nodeMap[components[3]]; const edge = node.inputs[components[2]]; const visible = !edge.isVisible(); node.setInputVisibility(components[2], visible); d3.event.stopPropagation(); view.updateGraphVisibility(); }); } if (d.outputs.length != 0) { const x = d.getOutputX(); const y = d.getNodeHeight(view.state.showTypes) + DEFAULT_NODE_BUBBLE_RADIUS; 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("click", function (d) { d.setOutputVisibility(d.areAnyOutputsVisible() == 0); d3.event.stopPropagation(); view.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", 5) .append('tspan') .text(function (l) { return d.getDisplayLabel(); }) .append("title") .text(function (l) { return d.getTitle(); }); if (d.nodeLabel.type != undefined) { d3.select(this).append("text") .classed("label", true) .classed("type", true) .attr("text-anchor", "right") .attr("dx", 5) .attr("dy", d.labelbbox.height + 5) .append('tspan') .text(function (l) { return d.getDisplayType(); }) .append("title") .text(function (l) { return d.getType(); }); } }); const newAndOldNodes = newGs.merge(selNodes); newAndOldNodes.select<SVGTextElement>('.type').each(function (d) { this.setAttribute('visibility', view.state.showTypes ? 'visible' : 'hidden'); }); newAndOldNodes .classed("selected", function (n) { if (state.selection.isSelected(n)) return true; return false; }) .attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; }) .select('rect') .attr('height', function (d) { return d.getNodeHeight(view.state.showTypes); }); view.visibleBubbles = d3.selectAll('circle'); view.updateInputAndOutputBubbles(); graph.maxGraphX = graph.maxGraphNodeX; newAndOldEdges.attr("d", function (edge) { return edge.generatePath(graph, view.state.showTypes); }); } getSvgViewDimensions() { return [this.container.clientWidth, this.container.clientHeight]; } getSvgExtent(): [[number, number], [number, number]] { return [[0, 0], [this.container.clientWidth, this.container.clientHeight]]; } minScale() { const dimensions = this.getSvgViewDimensions(); const minXScale = dimensions[0] / (2 * this.graph.width); const minYScale = dimensions[1] / (2 * this.graph.height); const minScale = Math.min(minXScale, minYScale); this.panZoom.scaleExtent([minScale, 40]); return minScale; } onresize() { const trans = d3.zoomTransform(this.svg.node()); const ctrans = this.panZoom.constrain()(trans, this.getSvgExtent(), this.panZoom.translateExtent()); this.panZoom.transform(this.svg, ctrans); } toggleTypes() { const view = this; view.state.showTypes = !view.state.showTypes; const element = document.getElementById('toggle-types'); element.classList.toggle('button-input-toggled', view.state.showTypes); view.updateGraphVisibility(); } viewSelection() { const view = this; let minX; let maxX; let minY; let maxY; let hasSelection = false; view.visibleNodes.selectAll<SVGGElement, GNode>("g").each(function (n) { if (view.state.selection.isSelected(n)) { 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 + n.getNodeHeight(view.state.showTypes)) : n.y + n.getNodeHeight(view.state.showTypes); } }); if (hasSelection) { view.viewGraphRegion(minX - NODE_INPUT_WIDTH, minY - 60, maxX + NODE_INPUT_WIDTH, maxY + 60); } } viewGraphRegion(minX, minY, maxX, maxY) { const [width, height] = this.getSvgViewDimensions(); const dx = maxX - minX; const dy = maxY - minY; const x = (minX + maxX) / 2; const y = (minY + maxY) / 2; const scale = Math.min(width / (1.1 * dx), height / (1.1 * dy)); this.svg .transition().duration(300).call(this.panZoom.translateTo, x, y) .transition().duration(300).call(this.panZoom.scaleTo, scale) .transition().duration(300).call(this.panZoom.translateTo, x, y); } viewWholeGraph() { this.panZoom.scaleTo(this.svg, 0); this.panZoom.translateTo(this.svg, this.graph.minGraphX + this.graph.width / 2, this.graph.minGraphY + this.graph.height / 2); } }