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