Simon Hunt
Committed by Gerrit Code Review

GUI -- Augmented sprite JSON definition.

- refactored topoSprite.js to handle the changes.

Change-Id: Ib0ed7dbacbc93777d8849bf82f52ad6ac974af2c
......@@ -542,46 +542,60 @@
fill: #eee;
}
/* ------------------------------------------------- */
/* Sprite Layer */
#ov-topo svg #topo-sprites use {
stroke-width: 1.0;
}
#ov-topo svg #topo-sprites text {
text-anchor: middle;
font-size: 10pt;
font-style: italic;
}
.light #ov-topo svg #topo-sprites .sprite1 use {
stroke-width: 1.0;
stroke: goldenrod;
.light #ov-topo svg #topo-sprites .gold1 use {
stroke: #da2;
fill: none;
}
.dark #ov-topo svg #topo-sprites .sprite1 use {
stroke-width: 1.0;
.dark #ov-topo svg #topo-sprites .gold1 use {
stroke: #541;
fill: none;
}
.light #ov-topo svg #topo-sprites .sprite1 text {
.light #ov-topo svg #topo-sprites .gold1 text {
fill: #eda;
}
.dark #ov-topo svg #topo-sprites .sprite1 text {
.dark #ov-topo svg #topo-sprites .gold1 text {
fill: #543;
}
.light #ov-topo svg #topo-sprites .sprite2 use {
.light #ov-topo svg #topo-sprites .blue1 use {
stroke: #bbd;
stroke-width: 1.0;
fill: none;
}
.dark #ov-topo svg #topo-sprites .sprite2 use {
.dark #ov-topo svg #topo-sprites .blue1 use {
stroke: #445;
stroke-width: 1.0;
fill: none;
}
.light #ov-topo svg #topo-sprites .sprite2 text {
.light #ov-topo svg #topo-sprites .blue1 text {
fill: #cce;
}
.dark #ov-topo svg #topo-sprites .sprite2 text {
.dark #ov-topo svg #topo-sprites .blue1 text {
fill: #446;
}
.light #ov-topo svg #topo-sprites .gray1 use {
stroke: #bbb;
fill: none;
}
.dark #ov-topo svg #topo-sprites .gray1 use {
stroke: #333;
fill: none;
}
.light #ov-topo svg #topo-sprites .gray1 text {
fill: #ccc;
}
.dark #ov-topo svg #topo-sprites .gray1 text {
fill: #444;
}
......
......@@ -366,7 +366,7 @@
}
);
spriteG = zoomLayer.append ('g').attr('id', 'topo-sprites');
tspr.loadSprites(spriteG, $loc.search().sprites);
tspr.loadSprites(spriteG, defs, $loc.search().sprites);
forceG = zoomLayer.append('g').attr('id', 'topo-force');
tfs.initForce(svg, forceG, uplink, dim);
......
......@@ -16,47 +16,85 @@
/*
ONOS GUI -- Topology Sprite Module.
Defines behavior for loading sprites.
Defines behavior for loading sprites into the sprite layer.
*/
(function () {
'use strict';
// injected refs
var $log, $http, fs, sus, wss;
var $log, $http, fs, gs, sus, wss;
var tssid = 'TopoSpriteService: ';
// internal state
var spriteLayer;
var spriteLayer, defsElement;
function doSprite(def, item) {
var g;
function registerPathsAsGlyphs(paths) {
var custom = {},
ids = [];
function xfm(x, y, s) {
return sus.translate([x,y]) + sus.scale(s, s);
function mkd(d) {
return fs.isA(d) ? d.join('') : d;
}
g = spriteLayer.append('g')
.classed(def['class'], true)
.attr('transform', xfm(item.x, item.y, def.scale));
if (paths) {
paths.forEach(function (path) {
var tag = 'spr_' + path.tag;
custom['_' + tag] = path.viewbox || '0 0 1000 1000';
custom[tag] = mkd(path.d);
ids.push(tag);
});
if (item.label) {
g.append('text')
.text(item.label)
.attr({
x: def.width / 2,
y: def.height * def.textyoff
});
gs.registerGlyphs(custom);
gs.loadDefs(defsElement, ids, true);
}
}
function labAttr(def) {
var dim = def.dim || [1000,1000],
w = dim[0],
h = dim[1],
dy = def.labelyoff || 1;
return { x: w / 2, y: h * dy };
}
function doSprite(spr, def) {
var c = spr.class || 'gray1',
p = spr.pos || [0,0],
lab = spr.label,
dim = def.dim || [1000,1000],
w = dim[0],
h = dim[1],
use = def.glyph || 'spr_' + def.path,
g = spriteLayer.append('g')
.classed(c, true)
.attr('transform', sus.translate(p));
g.append('use').attr({
width: def.width,
height: def.height,
'xlink:href': '#' + def.use
width: w,
height: h,
'xlink:href': '#' + use
});
if (lab) {
g.append('text')
.text(lab)
.attr(labAttr(def));
}
}
function doLabel(label) {
var c = label.class || 'gray1',
p = label.pos || [0,0];
spriteLayer.append('text')
.text(label.text)
.attr('transform', sus.translate(p))
.classed(c, true);
}
// ==========================
// event handlers
......@@ -73,30 +111,49 @@
// data for the requested sprite definition.
function inData(payload) {
var data = payload.data,
name = data && data.defn_name,
desc = data && data.defn_desc,
name, desc, sprites, labels,
paths = {},
defs = {};
if (!data) {
$log.warn(tssid + 'No sprite data loaded.')
return;
}
name = data.defn_name;
desc = data.defn_desc;
$log.debug("Loading sprites...[" + name + "]", desc);
data.defn.forEach(function (d) {
defs[d.id] = d;
});
registerPathsAsGlyphs(data.paths);
data.load.forEach(function (item) {
doSprite(defs[item.id], item);
});
if (data.defn) {
data.defn.forEach(function (d) {
defs[d.id] = d;
});
}
// pull out the sprite and label items
if (data.load) {
sprites = data.load.sprites;
labels = data.load.labels;
}
if (sprites) {
sprites.forEach(function (spr) {
doSprite(spr, defs[spr.id]);
});
}
if (labels) {
labels.forEach(doLabel);
}
}
function loadSprites(layer, defname) {
function loadSprites(layer, defsElem, defname) {
var name = defname || 'sprites';
spriteLayer = layer;
defsElement = defsElem;
$log.info(tssid + 'Requesting sprite definition ['+name+']...');
......@@ -109,12 +166,14 @@
angular.module('ovTopo')
.factory('TopoSpriteService',
['$log', '$http', 'FnService', 'SvgUtilService', 'WebSocketService',
['$log', '$http', 'FnService', 'GlyphService',
'SvgUtilService', 'WebSocketService',
function (_$log_, _$http_, _fs_, _sus_, _wss_) {
function (_$log_, _$http_, _fs_, _gs_, _sus_, _wss_) {
$log = _$log_;
$http = _$http_;
fs = _fs_;
gs = _gs_;
sus = _sus_;
wss = _wss_;
......
{
"defn_name": "layout",
"defn_desc": "Sample Layout Sprite Data",
"_comment": [
"Sample sprite layout file, demonstrating user-defined outlines",
"(1) Register on the server with ...",
" onos-upload-sprites localhost layout.json",
"(2) Load into topology view with ...",
" http://localhost:8181/onos/ui/index.html#/topo?sprites=layout"
],
"_comment_paths": [
"The 'paths' array contains custom path data.",
"Note that viewbox defaults to [0 0 1000 1000], which is the logical",
"coordinate space of the topology view."
],
"paths": [
{
"tag": "border",
"d": "M0,0h1000v1000h-1000z",
"_comment": "bounds of viewbox 0 0 1000 1000"
},
{
"tag": "multi",
"d": [
"M500,500l-50,50v-200h100v200z",
"M600,400h200v50h-200z"
],
"_comment": "shows path constructed from multiple strings"
},
{
"tag": "triangle",
"viewbox": "0 0 1 1",
"d": "M.5,.2l.3,.6,h-.6z",
"_comment": "defines its own viewbox"
},
{
"tag": "diamond",
"viewbox": "0 0 1 1",
"d": "M.2,.5l.3,-.3l.3,.3l-.3,.3z"
}
],
"_comment_defn": [
"The 'defn' array contains sprite definitions that combine",
"path, dimensions, and label-offset into 'sprites' that can be",
"replicated (stamped) in different positions in the view.",
"",
"The 'glyph' property refers to glyphs registered with the UI.",
"Alternatively, the 'path' property refers to a custom path defined in",
"the path array above. The 'dim' property provides the [width,height]",
"bounds within which the glyph/path is drawn. The 'labelyoff' property",
"defines the Y-offset of the label as a percentage from the top of the",
"sprite; for example, 0.4 = 40%. The label is centered horizontally.",
"",
"Note that dimension (dim) defaults to [1000,1000] so that, by default,",
"there is a 1:1 scale mapping of custom paths to the topology view."
],
"defn": [
{
"id": "border",
"path": "border"
},
{
"id": "multi",
"path": "multi"
},
{
"id": "small_tri",
"path": "triangle",
"dim":[80,80]
},
{
"id": "big_tri",
"path": "triangle",
"dim":[160,160]
},
{
"id": "subnet",
"glyph": "cloud",
"dim":[120,120],
"labelyoff": 0.4
},
{
"id": "subnet2",
"glyph": "cloud",
"dim":[200,200],
"labelyoff": 0.4
}
],
"_comment_load": [
"The 'load' object contains sprites and labels to load into the view.",
"",
"Items in the sprite list associate sprites with a position,",
"style class, and optional label. Note that the coordinates of",
"the position define the top-left corner of the sprite.",
"Default 'pos' is [0,0]. Default 'class' is 'gray1'.",
"",
"Items in the label list associate labels with a position and",
"style class. Note that the text is centered on the x-coordinate."
],
"load": {
"sprites": [
{ "id": "border" },
{ "id": "multi", "class": "gray1" },
{ "id": "subnet", "pos":[-40,20], "label":"apples", "class": "blue1" },
{ "id": "subnet", "pos":[400,40], "label":"bananas", "class": "blue1" },
{ "id": "subnet", "pos":[840,60], "label":"cherries", "class": "blue1" },
{ "id": "subnet2", "pos":[300,400], "class": "gray1" },
{ "id": "small_tri", "pos":[10, 20] },
{ "id": "small_tri", "pos":[110, 20] },
{ "id": "small_tri", "pos":[210, 20] },
{ "id": "small_tri", "pos":[310, 20] }
],
"labels": [
{ "pos":[500,940], "text":"Sample Layout", "class":"blue1" },
{ "pos":[500,1000], "text":"Illustrating Sprites", "class":"gray1" }
]
}
}