You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by kb...@apache.org on 2019/02/28 13:12:01 UTC
[atlas] 02/02: ATLAS-3060 : UI: Allow to drag node and place it
accordingly
This is an automated email from the ASF dual-hosted git repository.
kbhatt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git
commit 546bc22f211351e479a65cfc401254ead2d86889
Author: kevalbhatt <kb...@apache.org>
AuthorDate: Thu Feb 28 18:14:58 2019 +0530
ATLAS-3060 : UI: Allow to drag node and place it accordingly
Signed-off-by: kevalbhatt <kb...@apache.org>
---
.../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