diff options
Diffstat (limited to 'framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js')
-rw-r--r-- | framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js new file mode 100644 index 00000000..38f3a6c3 --- /dev/null +++ b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js @@ -0,0 +1,338 @@ +/* + * 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, flash, tss, tps; + + // internal state + var api, + td3, + network, + showPorts = true, // enable port highlighting by default + enhancedLink = null, // the link over which the mouse is hovering + selectedLink = null; // the link which is currently selected + + // SVG elements; + var svg; + + + // ======== ALGORITHM TO FIND LINK CLOSEST TO MOUSE ======== + + function getLogicalMousePosition(container) { + var m = d3.mouse(container), + sc = api.zoomer.scale(), + tr = api.zoomer.translate(), + mx = (m[0] - tr[0]) / sc, + my = (m[1] - tr[1]) / sc; + return {x: mx, y: my}; + } + + + function sq(x) { return x * x; } + + function mdist(p, m) { + return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y)); + } + + function prox(dist) { + return dist / api.zoomer.scale(); + } + + function computeNearestNode(mouse) { + var proximity = prox(30), + nearest = null, + minDist; + + if (network.nodes.length) { + minDist = proximity * 2; + + network.nodes.forEach(function (d) { + var dist; + + if (!api.showHosts() && d.class === 'host') { + return; // skip hidden hosts + } + + dist = mdist({x: d.x, y: d.y}, mouse); + if (dist < minDist && dist < proximity) { + minDist = dist; + nearest = d; + } + }); + } + return nearest; + } + + + function computeNearestLink(mouse) { + var proximity = prox(30), + nearest = null, + minDist; + + 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 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 intersects, but are we close enough? + return mdist(p, m) <= proximity; + } + + if (network.links.length) { + minDist = proximity * 2; + + network.links.forEach(function (d) { + if (!api.showHosts() && d.type() === 'hostLink') { + return; // skip hidden host links + } + + var line = d.position, + point = pdrop(line, mouse), + hit = lineHit(line, point, mouse), + dist; + + if (hit) { + dist = mdist(point, mouse); + if (dist < minDist) { + minDist = dist; + nearest = d; + } + } + }); + } + return nearest; + } + + function enhanceLink(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) { + // guard against link element not set + if (d.el) { + d.el.classed('enhanced', false); + } + api.portLabelG().selectAll('.portLabel').remove(); + } + + function enhance(d) { + var data = [], + point; + + // guard against link element not set + if (!d.el) return; + + d.el.classed('enhanced', true); + + // Define port label data objects. + // NOTE: src port is absent in the case of host-links. + + point = locatePortLabel(d); + angular.extend(point, { + id: 'topo-port-tgt', + num: d.tgtPort + }); + data.push(point); + + if (d.srcPort) { + point = locatePortLabel(d, 1); + angular.extend(point, { + id: 'topo-port-src', + num: d.srcPort + }); + data.push(point); + } + + td3.applyPortLabels(data, api.portLabelG()); + } + + function locatePortLabel(link, src) { + var offset = 32, + pos = link.position, + nearX = src ? pos.x1 : pos.x2, + nearY = src ? pos.y1 : pos.y2, + farX = src ? pos.x2 : pos.x1, + farY = src ? pos.y2 : pos.y1; + + function dist(x, y) { return Math.sqrt(x*x + y*y); } + + var dx = farX - nearX, + dy = farY - nearY, + k = offset / dist(dx, dy); + + return {x: k * dx + nearX, y: k * dy + nearY}; + } + + function selectLink(ldata) { + // if the new link is same as old link, do nothing + if (selectedLink && ldata && selectedLink.key === ldata.key) return; + + // make sure no nodes are selected + tss.deselectAll(); + + // first, unenhance the currently enhanced link + if (selectedLink) { + unselLink(selectedLink); + } + selectedLink = ldata; + if (selectedLink) { + selLink(selectedLink); + } + } + + function unselLink(d) { + // guard against link element not set + if (d.el) { + d.el.classed('selected', false); + } + } + + function selLink(d) { + // guard against link element not set + if (!d.el) return; + + d.el.classed('selected', true); + + tps.displayLink(d); + tps.displaySomething(); + } + + // ====== MOUSE EVENT HANDLERS ====== + + function mouseMoveHandler() { + var mp = getLogicalMousePosition(this), + link = computeNearestLink(mp); + enhanceLink(link); + } + + function mouseClickHandler() { + var mp, link, node; + + if (!tss.clickConsumed()) { + mp = getLogicalMousePosition(this); + node = computeNearestNode(mp); + if (node) { + $log.debug('found nearest node:', node.labels[1]); + tss.selectObject(node); + } else { + link = computeNearestLink(mp); + selectLink(link); + } + } + } + + + // ====================== + + function togglePorts(x) { + var kev = (x === 'keyev'), + on = kev ? !showPorts : !!x, + what = on ? 'Enable' : 'Disable', + handler = on ? mouseMoveHandler : null; + + showPorts = on; + + if (!on) { + enhanceLink(null); + } + svg.on('mousemove', handler); + flash.flash(what + ' port highlighting'); + return on; + } + + function deselectLink() { + if (selectedLink) { + unselLink(selectedLink); + selectedLink = null; + return true; + } + return false; + } + + // ========================== + // Module definition + + angular.module('ovTopo') + .factory('TopoLinkService', + ['$log', 'FnService', 'SvgUtilService', 'ThemeService', 'FlashService', + 'TopoSelectService', 'TopoPanelService', + + function (_$log_, _fs_, _sus_, _ts_, _flash_, _tss_, _tps_) { + $log = _$log_; + fs = _fs_; + sus = _sus_; + ts = _ts_; + flash = _flash_; + tss = _tss_; + tps = _tps_; + + function initLink(_api_, _td3_) { + api = _api_; + td3 = _td3_; + svg = api.svg; + network = api.network; + if (showPorts && !fs.isMobile()) { + svg.on('mousemove', mouseMoveHandler); + } + svg.on('click', mouseClickHandler); + } + + function destroyLink() { + // unconditionally remove any event handlers + svg.on('mousemove', null); + svg.on('click', null); + } + + return { + initLink: initLink, + destroyLink: destroyLink, + togglePorts: togglePorts, + deselectLink: deselectLink + }; + }]); +}()); |