| /* |
| * Copyright 2015 Open Networking Laboratory |
| * |
| * Licensed 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. |
| */ |
| |
| /* |
| ONOS GUI -- Topology Link Module. |
| Functions for highlighting/selecting links |
| */ |
| |
| (function () { |
| 'use strict'; |
| |
| // injected refs |
| var $log, fs, sus, ts; |
| |
| var api, |
| network, |
| enhancedLink = null; // the link which the mouse is hovering over |
| |
| // SVG elements; |
| var svg, mouseG; |
| |
| |
| // ======== ALGORITHM TO FIND LINK CLOSEST TO MOUSE ======== |
| |
| function setupMouse(forceG, zoomer) { |
| $log.debug('set up mouse handlers for mouse move'); |
| mouseG = forceG.append('g').attr('id', 'topo-mouse'); |
| //mouseG.append('circle') |
| // .attr({ |
| // r: 5, |
| // opacity: 0 |
| // }) |
| // .style('fill', 'red'); |
| |
| svg.on('mouseenter', function () { |
| //$log.log('M--ENTER'); |
| //mouseG.selectAll('circle').attr('opacity', 1); |
| }) |
| .on('mouseleave', function () { |
| //$log.log('M--LEAVE'); |
| //mouseG.selectAll('circle').attr('opacity', 0); |
| }) |
| .on('mousemove', function () { |
| var m = d3.mouse(this), |
| sc = zoomer.scale(), |
| tr = zoomer.translate(), |
| mx = (m[0] - tr[0]) / sc, |
| my = (m[1] - tr[1]) / sc; |
| |
| //$log.log('M--MOVE', m); |
| |
| //mouseG.selectAll('circle') |
| // .attr({ |
| // cx: mx, |
| // cy: my |
| // }); |
| updatePerps({x: mx, y: my}, zoomer); |
| }); |
| } |
| |
| function updatePerps(mouse, zoomer) { |
| var proximity = 30 / zoomer.scale(), |
| perpData, perps, nearest, minDist; |
| |
| function sq(x) { return x * x; } |
| |
| function pdrop(line, mouse) { |
| var x1 = line.x1, |
| y1 = line.y1, |
| x2 = line.x2, |
| y2 = line.y2, |
| x3 = mouse.x, |
| y3 = mouse.y, |
| k = ((y2-y1) * (x3-x1) - (x2-x1) * (y3-y1)) / |
| (sq(y2-y1) + sq(x2-x1)), |
| x4 = x3 - k * (y2-y1), |
| y4 = y3 + k * (x2-x1); |
| return {x:x4, y:y4}; |
| } |
| |
| function mdist(p, m) { |
| return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y)); |
| } |
| |
| function lineSeg(d) { |
| return { |
| x1: d.source.x, |
| y1: d.source.y, |
| x2: d.target.x, |
| y2: d.target.y |
| }; |
| } |
| |
| function lineHit(line, p, m) { |
| if (p.x < line.x1 && p.x < line.x2) return false; |
| if (p.x > line.x1 && p.x > line.x2) return false; |
| if (p.y < line.y1 && p.y < line.y2) return false; |
| if (p.y > line.y1 && p.y > line.y2) return false; |
| // line bisects, but are we close enough? |
| return mdist(p, m) <= proximity; |
| } |
| |
| if (network.links.length) { |
| perpData = []; |
| nearest = null; |
| minDist = proximity * 2; |
| |
| network.links.forEach(function (d) { |
| if (!api.showHosts() && d.type() === 'hostLink') { |
| return; // skip hidden host links |
| } |
| |
| var line = lineSeg(d), |
| point = pdrop(line, mouse), |
| hit = lineHit(line, point, mouse), |
| dist; |
| |
| if (hit) { |
| dist = mdist(point, mouse); |
| if (dist < minDist) { |
| minDist = dist; |
| nearest = d; |
| } |
| /* |
| perpData.push({ |
| key: d.key, |
| x1: mouse.x, |
| y1: mouse.y, |
| x2: point.x, |
| y2: point.y |
| }); |
| */ |
| } |
| }); |
| |
| /* |
| perps = mouseG.selectAll('line') |
| .data(perpData, function (d) { return d.key; }) |
| .attr({ |
| x1: function (d) { return d.x1; }, |
| y1: function (d) { return d.y1; }, |
| x2: function (d) { return d.x2; }, |
| y2: function (d) { return d.y2; } |
| }); |
| |
| perps.enter().append('line') |
| .attr({ |
| x1: function (d) { return d.x1; }, |
| y1: function (d) { return d.y1; }, |
| x2: function (d) { return d.x2; }, |
| y2: function (d) { return d.y2; } |
| }) |
| .style('stroke-width', 2) |
| .style('stroke', 'limegreen'); |
| |
| perps.exit().remove(); |
| */ |
| |
| enhanceNearestLink(nearest); |
| } |
| } |
| |
| |
| function enhanceNearestLink(ldata) { |
| // if the new link is same as old link, do nothing |
| if (enhancedLink && ldata && enhancedLink.key === ldata.key) return; |
| |
| // first, unenhance the currently enhanced link |
| if (enhancedLink) { |
| unenhance(enhancedLink); |
| } |
| enhancedLink = ldata; |
| if (enhancedLink) { |
| enhance(enhancedLink); |
| } |
| } |
| |
| function unenhance(d) { |
| d.el.style('stroke', '#666'); |
| $log.debug('UN-enhancing link: ', d.key); |
| } |
| |
| function enhance(d) { |
| d.el.style('stroke', 'gold'); |
| $log.debug('enhancing link: ', d.key); |
| } |
| |
| |
| |
| |
| // ========================== |
| // Module definition |
| |
| angular.module('ovTopo') |
| .factory('TopoLinkService', |
| ['$log', 'FnService', 'SvgUtilService', 'ThemeService', |
| |
| function (_$log_, _fs_, _sus_, _ts_) { |
| $log = _$log_; |
| fs = _fs_; |
| sus = _sus_; |
| ts = _ts_; |
| |
| function initLink(_api_) { |
| api = _api_; |
| svg = api.svg; |
| network = api.network; |
| setupMouse(api.forceG, api.zoomer); |
| } |
| |
| function destroyLink() { |
| svg.on('mouseenter', null) |
| .on('mouseleave', null) |
| .on('mousemove', null); |
| } |
| |
| return { |
| initLink: initLink, |
| destroyLink: destroyLink |
| }; |
| }]); |
| }()); |