This is an automated email from the ASF dual-hosted git repository. kbhatt pushed a commit to branch branch-1.0 in repository https://gitbox.apache.org/repos/asf/atlas.git
commit 0de3be895c905a76b20ad76c271fb71baccace68 Author: kevalbhatt <[email protected]> AuthorDate: Thu Feb 28 18:14:58 2019 +0530 ATLAS-3060 : UI: Allow to drag node and place it accordingly Signed-off-by: kevalbhatt <[email protected]> (cherry picked from commit 546bc22f211351e479a65cfc401254ead2d86889) --- .../public/js/views/graph/LineageLayoutView.js | 112 ++++------ dashboardv2/public/js/views/graph/LineageUtils.js | 243 +++++++++++++++++++++ 2 files changed, 285 insertions(+), 70 deletions(-) diff --git a/dashboardv2/public/js/views/graph/LineageLayoutView.js b/dashboardv2/public/js/views/graph/LineageLayoutView.js index e98a6fa..d252b65 100644 --- a/dashboardv2/public/js/views/graph/LineageLayoutView.js +++ b/dashboardv2/public/js/views/graph/LineageLayoutView.js @@ -22,6 +22,7 @@ define(['require', 'collection/VLineageList', 'models/VEntity', 'utils/Utils', + 'views/graph/LineageUtils', 'dagreD3', 'd3-tip', 'utils/Enums', @@ -29,7 +30,7 @@ define(['require', 'utils/Globals', 'platform', 'jquery-ui' -], function(require, Backbone, LineageLayoutViewtmpl, VLineageList, VEntity, Utils, dagreD3, d3Tip, Enums, UrlLinks, Globals, platform) { +], function(require, Backbone, LineageLayoutViewtmpl, VLineageList, VEntity, Utils, LineageUtils, dagreD3, d3Tip, Enums, UrlLinks, Globals, platform) { 'use strict'; var LineageLayoutView = Backbone.Marionette.LayoutView.extend( @@ -469,39 +470,7 @@ define(['require', this.$('.lineage-edge-details').removeClass('open'); } }, - centerNode: function(nodeID) { - var zoom = function() { - svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); - }; - var selectedNode = this.$('svg').find("g.nodes").find('>g#' + nodeID); - - if (selectedNode.length > 0) { - selectedNode = selectedNode; - var matrix = selectedNode.attr('transform').replace(/[^0-9\-.,]/g, '').split(','), - x = matrix[0], - y = matrix[1]; - } else { - selectedNode = this.$('svg').find("g.nodes").find('g').eq(1); - var x = this.g.graph().width / 2, - y = this.g.graph().height / 2; - } - var viewerWidth = this.$('svg').width(), - viewerHeight = this.$('svg').height(), - gBBox = d3.select('g').node().getBBox(), - zoomListener = d3.behavior.zoom().scaleExtent([0.01, 50]).on("zoom", zoom), - scale = 1.2, - xa = -((x * scale) - (viewerWidth / 2)), - ya = -((y * scale) - (viewerHeight / 2)); - - this.zoom.translate([xa, ya]); - d3.select('g').transition() - .duration(350) - .attr("transform", "translate(" + xa + "," + ya + ")scale(" + scale + ")"); - zoomListener.scale(scale); - zoomListener.translate([xa, ya]); - this.zoom.scale(scale); - }, zoomed: function(that) { this.$('svg').find('>g').attr("transform", "translate(" + this.zoom.translate() + ")" + @@ -654,36 +623,6 @@ define(['require', svg.on("dblclick.zoom", null) // .on("wheel.zoom", null); //change text postion - - function isConnected(a, b, o) { - //console.log(a, b, o); - if (a === o || (b && b.length && b.indexOf(o) != -1)) { - return true; - } - } - - function fade(opacity, d, nodesToHighlight) { - var node = svg.selectAll('.node'); - var path = svg.selectAll('.edgePath'); - node.classed("hover-active", function(selectedNode, i, nodes) { - if (isConnected(d, nodesToHighlight, selectedNode)) { - return true; - } else { - return false; - } - }); - path.classed('hover-active-node', function(c) { - var _thisOpacity = c.v === d || c.w === d ? 1 : 0; - if (_thisOpacity) { - return true; - } else { - return false; - } - - }); - - } - //Highlight on click svgGroup.selectAll("g.nodes g.label") .attr("transform", "translate(2,-35)"); var waitForDoubleClick = null; @@ -723,7 +662,12 @@ define(['require', var nextNode = that.g.successors(d), previousNode = that.g.predecessors(d), nodesToHighlight = nextNode.concat(previousNode); - fade(0.3, d, nodesToHighlight); + LineageUtils.onHoverFade({ + opacity: 0.3, + selectedNode: d, + highlight: nodesToHighlight, + svg: that.svg + }).init(); }) .on('mouseleave', function(d) { that.activeNode = false; @@ -735,12 +679,16 @@ define(['require', tooltip.hide(d); } } - }, 400); + }, 150); if (!that.ui.showOnlyHoverPath.prop('checked')) { return; } that.$('svg').removeClass('hover'); - fade(1, d); + LineageUtils.onHoverFade({ + opacity: 1, + selectedNode: d, + svg: that.svg + }).init(); }) .on('click', function(d) { if (d3.event.defaultPrevented) return; // ignore drag @@ -797,7 +745,17 @@ define(['require', }); // Center the graph - this.centerNode(that.guid); + LineageUtils.centerNode({ + guid: that.guid, + svg: that.$('svg'), + g: this.g, + afterCenterZoomed: function(options) { + var newScale = options.newScale, + newTranslate = options.newTranslate; + that.zoom.scale(newScale); + that.zoom.translate(newTranslate); + } + }).init(); zoom.event(svg); //svg.attr('height', this.g.graph().height * initialScale + 40); if (platform.name === "IE") { @@ -817,6 +775,11 @@ define(['require', }, 1000); }); } + LineageUtils.DragNode({ + svg: this.svg, + g: this.g, + guid: this.guid + }).init(); }, renderLineageTypeSearch: function() { var that = this; @@ -842,9 +805,18 @@ define(['require', e.stopImmediatePropagation(); d3.selectAll(".serach-rect").remove(); var selectedNode = $('[data-id="typeSearch"]').val(); - that.searchNodeObj.selectedNode = $('[data-id="typeSearch"]').val(); - that.centerNode(selectedNode); - + that.searchNodeObj.selectedNode = selectedNode; + LineageUtils.centerNode({ + guid: selectedNode, + svg: $(that.svg[0]), + g: that.g, + afterCenterZoomed: function(options) { + var newScale = options.newScale, + newTranslate = options.newTranslate; + that.zoom.scale(newScale); + that.zoom.translate(newTranslate); + } + }).init(); that.svg.selectAll('.nodes g.label').attr('stroke', function(c, d) { if (c == selectedNode) { return "#316132"; diff --git a/dashboardv2/public/js/views/graph/LineageUtils.js b/dashboardv2/public/js/views/graph/LineageUtils.js new file mode 100644 index 0000000..3e53ddd --- /dev/null +++ b/dashboardv2/public/js/views/graph/LineageUtils.js @@ -0,0 +1,243 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +define(['require', ''], function(require) { + 'use strict'; + var LinegaeUtils = {}; + LinegaeUtils.DragNode = function(options) { + var that = this, + g = options.g, + svg = options.svg, + guid = options.guid; + return { + init: function() { + var that = this; + //give IDs to each of the nodes so that they can be accessed + svg.selectAll("g.node rect") + .attr("id", function(d) { + return "node" + d; + }); + svg.selectAll("g.edgePath path") + .attr("id", function(e) { + return e.v + "-" + e.w; + }); + svg.selectAll("g.edgeLabel g") + .attr("id", function(e) { + return 'label_' + e.v + "-" + e.w; + }); + + g.nodes().forEach(function(v) { + var node = g.node(v); + node.customId = "node" + v; + }) + g.edges().forEach(function(e) { + var edge = g.edge(e.v, e.w); + edge.customId = e.v + "-" + e.w + }); + var nodeDrag = d3.behavior.drag() + .on("dragstart", this.dragstart) + .on("drag", function(d) { that.dragmove(this, d) }); + + var edgeDrag = d3.behavior.drag() + .on("dragstart", this.dragstart) + .on('drag', function(d) { + that.translateEdge(g.edge(d.v, d.w), d3.event.dx, d3.event.dy); + $('#' + g.edge(d.v, d.w).customId).attr('d', that.calcPoints(d)); + }); + + nodeDrag.call(svg.selectAll("g.node")); + edgeDrag.call(svg.selectAll("g.edgePath")); + }, + dragstart: function(d) { + d3.event.sourceEvent.stopPropagation(); + }, + dragmove: function(elem, d) { + var that = this, + node = d3.select(elem), + selectedNode = g.node(d), + prevX = selectedNode.x, + prevY = selectedNode.y; + + selectedNode.x += d3.event.dx; + selectedNode.y += d3.event.dy; + node.attr('transform', 'translate(' + selectedNode.x + ',' + selectedNode.y + ')'); + + var dx = selectedNode.x - prevX, + dy = selectedNode.y - prevY; + + g.edges().forEach(function(e) { + if (e.v == d || e.w == d) { + var edge = g.edge(e.v, e.w); + that.translateEdge(g.edge(e.v, e.w), dx, dy); + $('#' + edge.customId).attr('d', that.calcPoints(e)); + var label = $('#label_' + edge.customId); + var xforms = label.attr('transform'); + if (xforms != "") { + var parts = /translate\(\s*([^\s,)]+)[ ,]?([^\s,)]+)?/.exec(xforms), + X = parseInt(parts[1]) + dx, + Y = parseInt(parts[2]) + dy; + // console.log(X, Y); + if (isNaN(Y)) { + Y = dy; + } + label.attr('transform', 'translate(' + X + ',' + Y + ')'); + } + } + }) + + }, + translateEdge: function(e, dx, dy) { + e.points.forEach(function(p) { + p.x = p.x + dx; + p.y = p.y + dy; + }); + }, + calcPoints: function(e) { + var edge = g.edge(e.v, e.w), + tail = g.node(e.v), + head = g.node(e.w), + points = edge.points.slice(1, edge.points.length - 1), + afterslice = edge.points.slice(1, edge.points.length - 1); + points.unshift(this.intersectRect(tail, points[0])); + points.push(this.intersectRect(head, points[points.length - 1])); + return d3.svg.line() + .x(function(d) { + return d.x; + }) + .y(function(d) { + return d.y; + }) + .interpolate("basis") + (points); + }, + intersectRect: function(node, point) { + var that = this, + x = node.x, + y = node.y, + dx = point.x - x, + dy = point.y - y, + w = node.id == guid ? 24 : 21, + h = node.id == guid ? 24 : 21, + sx = 0, + sy = 0; + + if (Math.abs(dy) * w > Math.abs(dx) * h) { + // Intersection is top or bottom of rect. + if (dy < 0) { + h = -h; + } + sx = dy === 0 ? 0 : h * dx / dy; + sy = h; + } else { + // Intersection is left or right of rect. + if (dx < 0) { + w = -w; + } + sx = w; + sy = dx === 0 ? 0 : w * dy / dx; + } + return { + x: x + sx, + y: y + sy + }; + } + + } + } + + + LinegaeUtils.centerNode = function(options) { + var nodeID = options.guid, + svg = options.svg, + g = options.g, + afterCenterZoomed = options.afterCenterZoomed, + zoom = d3.behavior.zoom(), + svgGroup = svg.find("g"), + zoomBind = function() { + svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); + }, + selectedNode = $(svg).find("g.nodes").find('>g#' + nodeID); + return { + init: function() { + if (selectedNode.length > 0) { + selectedNode = selectedNode; + var matrix = selectedNode.attr('transform').replace(/[^0-9\-.,]/g, '').split(','), + x = matrix[0], + y = matrix[1]; + } else { + selectedNode = $(svg).find("g.nodes").find('g').eq(1); + var x = g.graph().width / 2, + y = g.graph().height / 2; + + } + + var viewerWidth = $(svg).width(), + viewerHeight = $(svg).height(), + gBBox = d3.select('g').node().getBBox(), + zoomListener = zoom.scaleExtent([0.01, 50]).on("zoom", zoomBind), + scale = 1.2, + xa = -((x * scale) - (viewerWidth / 2)), + ya = -((y * scale) - (viewerHeight / 2)); + + zoom.translate([xa, ya]); + d3.select('g').transition() + .duration(350) + .attr("transform", "translate(" + xa + "," + ya + ")scale(" + scale + ")"); + zoomListener.scale(scale); + zoomListener.translate([xa, ya]); + zoom.scale(scale); + afterCenterZoomed({ newScale: scale, newTranslate: [xa, ya] }); + } + } + } + LinegaeUtils.onHoverFade = function(options) { + var opacity = options.opacity, + d = options.selectedNode, + nodesToHighlight = options.highlight, + svg = options.svg, + isConnected = function(a, b, o) { + if (a === o || (b && b.length && b.indexOf(o) != -1)) { + return true; + } + }, + node = svg.selectAll('.node'), + path = svg.selectAll('.edgePath'); + return { + init: function() { + node.classed("hover-active", function(selectedNode, i, nodes) { + if (isConnected(d, nodesToHighlight, selectedNode)) { + return true; + } else { + return false; + } + }); + path.classed('hover-active-node', function(c) { + var _thisOpacity = c.v === d || c.w === d ? 1 : 0; + if (_thisOpacity) { + return true; + } else { + return false; + } + }); + + } + } + + } + return LinegaeUtils; +}); \ No newline at end of file
