};\r
\r
module.exports = function ( grunt ) {\r
- \r
- /** \r
+\r
+ /**\r
* Load required Grunt tasks. These are installed based on the versions listed\r
* in `package.json` when you do `npm install` in this directory.\r
*/\r
+ grunt.loadNpmTasks('grunt-browserify');\r
grunt.loadNpmTasks('grunt-contrib-clean');\r
grunt.loadNpmTasks('grunt-contrib-copy');\r
grunt.loadNpmTasks('grunt-contrib-jshint');\r
}\r
\r
/**\r
- * This is the configuration object Grunt uses to give each plugin its \r
+ * This is the configuration object Grunt uses to give each plugin its\r
* instructions.\r
*/\r
var taskConfig = {\r
pkg: grunt.file.readJSON("package.json"),\r
\r
/**\r
- * The banner is the comment that is placed at the top of our compiled \r
+ * The banner is the comment that is placed at the top of our compiled\r
* source files. It is first processed as a Grunt template, where the `<%=`\r
* pairs are evaluated based on this very configuration object.\r
*/\r
meta: {\r
- banner: \r
+ banner:\r
'/**\n' +\r
' * <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +\r
' * <%= pkg.homepage %>\n' +\r
bump: {\r
options: {\r
files: [\r
- "package.json", \r
+ "package.json",\r
"bower.json"\r
],\r
commit: false,\r
commitMessage: 'chore(release): v%VERSION%',\r
commitFiles: [\r
- "package.json", \r
+ "package.json",\r
"client/bower.json"\r
],\r
createTag: false,\r
push: false,\r
pushTo: 'origin'\r
}\r
- }, \r
+ },\r
\r
/**\r
* The directories to delete when `grunt clean` is executed.\r
*/\r
- clean: [ \r
- '<%= build_dir %>', \r
+ clean: [\r
+ '<%= build_dir %>',\r
'<%= compile_dir %>'\r
],\r
\r
copy: {\r
build_app_assets: {\r
files: [\r
- { \r
+ {\r
src: [ '**' ],\r
dest: '<%= build_dir %>/assets/',\r
cwd: 'src/assets',\r
expand: true\r
}\r
- ] \r
+ ]\r
},\r
build_vendor_assets: {\r
files: [\r
- { \r
+ {\r
src: [ '<%= vendor_files.assets %>' ],\r
dest: '<%= build_dir %>/assets/',\r
cwd: '.',\r
expand: true,\r
flatten: true\r
}\r
- ] \r
+ ]\r
},\r
build_appjs: {\r
files: [\r
]\r
}\r
},\r
+ browserify: {\r
+ dist: {\r
+ src: ['src/app/graph/index.js'],\r
+ dest: 'src/assets/js/graphRenderer.js',\r
+ options: {\r
+ browserifyOptions: {\r
+ standalone: 'DLUX'\r
+ }\r
+ }\r
+ }\r
+ },\r
\r
/**\r
* `grunt concat` concatenates multiple source files into a single file.\r
options: {\r
banner: '<%= meta.banner %>'\r
},\r
- src: [ \r
- '<%= vendor_files.js %>', \r
- 'module.prefix', \r
- '<%= build_dir %>/src/**/*.js', \r
- '<%= html2js.common.dest %>', \r
- '<%= html2js.app.dest %>', \r
- 'module.suffix' \r
+ src: [\r
+ '<%= vendor_files.js %>',\r
+ 'module.prefix',\r
+ '<%= build_dir %>/src/**/*.js',\r
+ '<%= html2js.common.dest %>',\r
+ '<%= html2js.app.dest %>',\r
+ 'module.suffix'\r
],\r
dest: '<%= compile_dir %>/assets/<%= pkg.name %>-<%= pkg.version %>.js'\r
}\r
}\r
}\r
},\r
- \r
+\r
/**\r
* `less` less plugin handles the LESS compilation and minification automatically\r
* this has been changed to the LESS plugin from recess plugin above because of\r
* nonetheless inside `src/`.\r
*/\r
jshint: {\r
- src: [ \r
+ src: [\r
'<%= app_files.js %>'\r
],\r
test: [\r
},\r
globals: {}\r
},\r
- \r
+\r
\r
/**\r
* HTML2JS is a Grunt plugin that takes all of your template files and\r
karma: {\r
options: {\r
configFile: '<%= build_dir %>/karma-unit.js'\r
- }, \r
+ },\r
unit: {\r
runnerPort: 9102,\r
background: true,\r
karmaconfig: {\r
unit: {\r
dir: '<%= build_dir %>',\r
- src: [ \r
+ src: [\r
'<%= vendor_files.js %>',\r
'<%= html2js.app.dest %>',\r
'<%= html2js.common.dest %>',\r
},\r
/**\r
* And for rapid development, we have a watch set up that checks to see if\r
- * any of the files listed below change, and then to execute the listed \r
+ * any of the files listed below change, and then to execute the listed\r
* tasks when they do. This just saves us from having to type "grunt" into\r
* the command-line every time we want to see what we're working on; we can\r
* instead just leave "grunt watch" running in a background terminal. Set it\r
* and forget it, as Ron Popeil used to tell us.\r
*\r
- * But we don't need the same thing to happen for all the files. \r
+ * But we don't need the same thing to happen for all the files.\r
*/\r
delta: {\r
/**\r
* run our unit tests.\r
*/\r
jssrc: {\r
- files: [ \r
+ files: [\r
'<%= app_files.js %>'\r
],\r
tasks: [ 'jshint:src', 'karma:unit:run', 'copy:build_appjs' ]\r
* files, so this is probably not very useful.\r
*/\r
assets: {\r
- files: [ \r
+ files: [\r
'src/assets/**/*'\r
],\r
tasks: [ 'copy:build_app_assets' ]\r
* When our templates change, we only rewrite the template cache.\r
*/\r
tpls: {\r
- files: [ \r
- '<%= app_files.atpl %>', \r
+ files: [\r
+ '<%= app_files.atpl %>',\r
'<%= app_files.ctpl %>'\r
],\r
tasks: [ 'html2js' ]\r
*/\r
grunt.registerTask( 'common', [\r
'clean', 'html2js', 'jshint', 'less:development',\r
- 'concat:build_css', 'copy:build_app_assets', 'copy:build_vendor_assets',\r
+ 'concat:build_css', 'browserify:dist', 'copy:build_app_assets', 'copy:build_vendor_assets',\r
'copy:build_appjs', 'copy:copy_template', 'copy:build_vendorimages', 'copy:build_vendorjs', 'copy:build_vendorcss', 'karmaconfig', 'index:build'\r
]);\r
\r
});\r
}\r
\r
- /** \r
+ /**\r
* The index.html template includes the stylesheet and javascript sources\r
* based on dynamic names calculated in this Gruntfile. This task assembles\r
* the list into variables for the template to use and then runs the\r
return file.replace( dirRE, '' );\r
});\r
\r
- grunt.file.copy('src/index.html', this.data.dir + '/index.html', { \r
+ grunt.file.copy('src/index.html', this.data.dir + '/index.html', {\r
process: function ( contents, path ) {\r
return grunt.template.process( contents, {\r
data: {\r
*/\r
grunt.registerMultiTask( 'karmaconfig', 'Process karma config templates', function () {\r
var jsFiles = filterForJS( this.filesSrc );\r
- \r
- grunt.file.copy( 'karma/karma-unit.tpl.js', grunt.config( 'build_dir' ) + '/karma-unit.js', { \r
+\r
+ grunt.file.copy( 'karma/karma-unit.tpl.js', grunt.config( 'build_dir' ) + '/karma-unit.js', {\r
process: function ( contents, path ) {\r
return grunt.template.process( contents, {\r
data: {\r
-{\r
- "name": "opendaylight-dlux",\r
- "version": "0.1.0",\r
- "dependencies": {\r
- "angular": "1.2.16",\r
- "json3": "~3.2.4",\r
- "jquery": "~1.9.1",\r
- "jquery-ui": "1.11.1",\r
- "es5-shim": "~2.0.8",\r
- "angular-resource": "1.2.16",\r
- "angular-cookies": "1.2.16",\r
- "angular-sanitize": "1.2.16",\r
- "angular-ui-date": "latest",\r
- "requirejs": "2.1.14",\r
- "font-awesome": "~4.0.3",\r
- "bootstrap": "~3.0.2",\r
- "angular-ui-router": "~0.2.10",\r
- "angular-ui-utils": "~0.0.3",\r
- "d3": "~3.3.2",\r
- "restangular": "1.2.1",\r
- "angular-ui-select2": "https://raw.github.com/angular-ui/ui-select2/master/src/select2.js",\r
- "underscore": "latest",\r
- "underscore.string": "latest",\r
- "select2": "master",\r
- "select2-bootstrap-css": "https://github.com/fk/select2-bootstrap-css/archive/master.zip",\r
- "footable": "2.0.1",\r
- "angular-translate": "2.2.0",\r
- "angular-translate-loader-static-files": "2.2.0",\r
- "vis": "2.0.0",\r
- "sigma": "https://github.com/jacomyal/sigma.js/releases/download/v1.0.3/release-v1.0.3.zip"\r
- },\r
- "devDependencies": {\r
- "angular": "1.2.16",\r
- "angular-mocks": "1.2.16",\r
- "bootstrap": "~3.0.2",\r
- "angular-bootstrap": "~0.7.0",\r
- "ng-grid": "~2.0.7",\r
- "angularAMD": "~0.1.1",\r
- "requirejs-domready": "~2.0.1",\r
- "ocLazyLoad": "0.3.0",\r
- "angular-css-injector": "~1.0.3",\r
- "grunt-shell": "~0.7.0"\r
- }\r
-}\r
+{
+ "name": "opendaylight-dlux",
+ "version": "0.1.0",
+ "dependencies": {
+ "angular": "1.2.16",
+ "json3": "~3.2.4",
+ "jquery": "~1.9.1",
+ "jquery-ui": "1.11.1",
+ "es5-shim": "~2.0.8",
+ "angular-resource": "1.2.16",
+ "angular-cookies": "1.2.16",
+ "angular-sanitize": "1.2.16",
+ "angular-ui-date": "latest",
+ "requirejs": "2.1.14",
+ "font-awesome": "~4.0.3",
+ "bootstrap": "~3.0.2",
+ "angular-ui-router": "~0.2.10",
+ "angular-ui-utils": "~0.0.3",
+ "d3": "~3.3.2",
+ "restangular": "1.2.1",
+ "angular-ui-select2": "https://raw.github.com/angular-ui/ui-select2/master/src/select2.js",
+ "underscore": "latest",
+ "underscore.string": "latest",
+ "select2": "master",
+ "select2-bootstrap-css": "https://github.com/fk/select2-bootstrap-css/archive/master.zip",
+ "footable": "2.0.1",
+ "angular-translate": "2.2.0",
+ "angular-translate-loader-static-files": "2.2.0",
+ "vis": "2.0.0",
+ "sigma": "https://github.com/jacomyal/sigma.js/releases/download/v1.0.3/release-v1.0.3.zip",
+ "pixi": "~2.2.5"
+ },
+ "devDependencies": {
+ "angular": "1.2.16",
+ "angular-mocks": "1.2.16",
+ "bootstrap": "~3.0.2",
+ "angular-bootstrap": "~0.7.0",
+ "ng-grid": "~2.0.7",
+ "angularAMD": "~0.1.1",
+ "requirejs-domready": "~2.0.1",
+ "ocLazyLoad": "0.3.0",
+ "angular-css-injector": "~1.0.3",
+ "grunt-shell": "~0.7.0"
+ }
+}
\r
html: [ 'src/index.html'],\r
less: 'src/less/main.less',\r
+ graph_path: 'src/app/graph',\r
+ graph_entry_point: 'index.js',\r
templates: ['src/**/*.tpl.html']\r
},\r
\r
'vendor/sigma/plugins/sigma.parsers.gexf.min.js',\r
'vendor/sigma/plugins/sigma.layout.forceAtlas2.min.js',\r
'vendor/sigma/plugins/sigma.plugins.dragNodes.min.js',\r
- 'vendor/sigma/plugins/sigma.renderers.customShapes.min.js'\r
+ 'vendor/sigma/plugins/sigma.renderers.customShapes.min.js',\r
+ 'vendor/pixi/bin/pixi.js'\r
],\r
css: [\r
'vendor/ng-grid/ng-grid.min.css',\r
"bower": "~1.3.12",
"grunt": "~0.4.5",
"grunt-cli": "~0.1.13",
+ "grunt-browserify": "^3.4.0",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-coffee": "~0.7.0",
"karma-coverage": "~0.2.6",
"karma-phantomjs-launcher": "~0.1.4",
"angular-mocks": "~1.2.22",
- "jasmine": "~2.0.1"
+ "jasmine": "~2.0.1",
+ "ngraph.graph": "~0.0.8",
+ "ngraph.forcelayout": "0.0.16",
+ "ngraph.fromjson": "^0.1.4",
+ "ngraph.merge": "0.0.1",
+ "ngraph.physics.simulator": "0.0.10",
+ "node-transform-matrix": "^1.0.2"
},
"engines": {
"node": ">=0.8.0"
<artifactId>dlux.common.login.resources</artifactId>\r
<version>${common.login.resources.version}</version>\r
</dependency>\r
- <dependency>\r
+ <dependency>\r
<groupId>org.opendaylight.dlux</groupId>\r
<artifactId>dlux.common.general.resources</artifactId>\r
<version>${common.general.resources.version}</version>\r
<artifactId>dlux.core.resources</artifactId>\r
<version>${core.resources.version}</version>\r
</dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.dlux</groupId>\r
+ <artifactId>dlux.graph.resources</artifactId>\r
+ <version>${graph.resources.version}</version>\r
+ </dependency>\r
<dependency>\r
<groupId>org.opendaylight.dlux</groupId>\r
<artifactId>dlux.common.topbar.resources</artifactId>\r
<include>app/topology/</include>\r
<include>app/connection_manager/</include>\r
<include>app/core/</include>\r
+ <include>app/graph/</include>\r
<include>common/yangutils/</include>\r
<include>common/sigmatopology/</include>\r
<include>common/navigation/</include>\r
dlux.network.resources,\r
dlux.flow.resources,\r
dlux.core.resources,\r
+ dlux.graph.resources,\r
</includeArtifactIds>\r
<excludes>META-INF\/**</excludes>\r
<excludeTransitive>true</excludeTransitive>\r
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
<feature name='odl-dlux-all' version='${project.version}'>
- <feature>odl-dlux-core</feature>
+ <feature>odl-dlux-core</feature>
<feature>odl-dlux-minimal</feature>
<feature>odl-dlux-node</feature>
<feature>odl-dlux-yangui</feature>
- <feature>odl-dlux-yangvisualizer</feature>
+ <feature>odl-dlux-yangvisualizer</feature>
</feature>
<bundle>mvn:org.opendaylight.dlux/dlux.yangui/${project.version}</bundle>
<bundle>mvn:org.opendaylight.dlux/dlux.common.yangutils/${project.version}</bundle>
</feature>
-
+
<feature name="odl-dlux-yangvisualizer">
<feature>odl-dlux-core</feature>
<bundle>mvn:org.opendaylight.dlux/dlux.yangvisualizer/${project.version}</bundle>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2014 Inocybe Technologies, and others. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-insta
+nce" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.x
+sd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.dlux</groupId>
+ <artifactId>dlux-parent</artifactId>
+ <version>0.2.0-SNAPSHOT</version>
+ <relativePath>../../</relativePath>
+ </parent>
+
+ <artifactId>dlux.graph.resources</artifactId>
+ <name>${project.artifactId}</name>
+ <description>Graph Module Resources</description>
+ <version>${graph.resources.version}</version>
+ <packaging>jar</packaging>
+
+</project>
--- /dev/null
+// Math.sign is used a lot here and outdated brownser don't have it.
+if (typeof Math.sign !== 'function') {
+ Math.sign = function(value) {
+ return value ? value < 0 ? -1 : 1 : 0;
+ };
+}
+
+module.exports = function(stage, graph) {
+ // constant TODO: change to use settings
+ var MAX_SCALE = 2.0;
+ var MIN_SCALE = 0.1;
+ //------//
+
+ // pointer options
+ var POINTER_DEFAULT = 'auto';
+ var POINTER_DRAG = 'all-scroll';
+
+ var event = require('events');
+ var emitter = new event.EventEmitter();
+ var mouseDown = false;
+ var draggedNode = null;
+
+ var camera = stage.camera;
+ var oldPos = new PIXI.Point(0, 0);
+
+ // Start dragging the node
+ stage.interactionManager.onMouseDown = function(e) {
+ mouseDown = true;
+ oldPos.set(-1, -1);
+
+ draggedNode = graph.getNodeAt(e.layerX, e.layerY);
+
+ if (draggedNode) {
+ emitter.emit('nodeDown', draggedNode);
+ }
+ };
+
+ // If the node is dragged, update the position
+ // otherwise emit a callable event
+ stage.interactionManager.onMouseMove = function(e) {
+ if (draggedNode) { // drag node
+ setPointerStyle(POINTER_DRAG);
+ var realPos = camera.transform.inverse().transformPoint(e.layerX, e.layerY);
+ draggedNode.pos.x = realPos[0];
+ draggedNode.pos.y = realPos[1];
+ }
+ else if (mouseDown) { // do a pan
+ setPointerStyle(POINTER_DRAG);
+ if (oldPos.x == -1 && oldPos.y == -1) {
+ oldPos.set(e.layerX, e.layerY);
+ }
+
+ var pos = new PIXI.Point(0,0);
+ var deltaX = e.layerX - oldPos.x;
+ var deltaY = e.layerY - oldPos.y;
+
+ pos.set(
+ Math.sign(deltaX) * Math.abs(deltaX),
+ Math.sign(deltaY) * Math.abs(deltaY)
+ );
+
+ oldPos.set(e.layerX, e.layerY);
+ camera.transform.translate(pos.x, pos.y);
+ }
+ else {
+ var node = graph.getNodeAt(e.layerX, e.layerY);
+
+ if (node) {
+ emitter.emit('nodeOver', node);
+ return;
+ }
+
+ emitter.emit('mouseMove', e);
+ }
+ };
+
+ // Stop dragging the node
+ stage.interactionManager.onMouseUp = function(e) {
+ mouseDown = false;
+ setPointerStyle(POINTER_DEFAULT);
+
+ if (draggedNode) {
+ emitter.emit('nodeUp', draggedNode);
+ draggedNode = null;
+ }
+ };
+
+ // Release drag or pan if out of the canvas
+ stage.interactionManager.onMouseOut = function(e) {
+ mouseDown = false;
+ draggedNode = null;
+ setPointerStyle(POINTER_DEFAULT);
+ };
+
+ function setPointerStyle(style) {
+ if (document.body.style.cursor != style) {
+ document.body.style.cursor = style;
+ }
+ }
+
+ function zoom(e) {
+
+ // stop scrolling while zooming
+ e.preventDefault();
+
+ /* Each brownser on each platform
+ * set the wheel distance a different value. That's why the arbitrary value
+ */
+ var factor = 0.10;
+ var sign = Math.sign(e.detail || e.wheelDelta);
+
+ var currentScale = camera.scaleFactor;
+ var scale = currentScale + (sign * factor);
+
+ if (scale > MAX_SCALE) {
+ scale = MAX_SCALE;
+ }
+ else if (scale < MIN_SCALE) {
+ scale = MIN_SCALE;
+ }
+ camera.scaleFactor = scale;
+ }
+
+ // Mouse wheel to Zoom
+ document.getElementById(stage.containerId).addEventListener('mousewheel', zoom, false); // chrome, safari
+ document.getElementById(stage.containerId).addEventListener('DOMMouseScroll', zoom, false); // firefox
+
+ return emitter;
+};
--- /dev/null
+module.exports = function() {
+ var Graph = require('ngraph.graph');
+ var GraphRenderer = require('./renderer.js');
+ var topology = null;
+
+ // public API
+ return {
+ start : function(id) {
+ try {
+ if (!topology) {
+ throw new Error('Cannot create a graph, you need to load one first');
+ }
+
+ renderer = new GraphRenderer(id, topology);
+ renderer.run();
+ } catch(e) {
+ console.error(e.name, e.message);
+ }
+ },
+ refresh: function() {
+ //TODO : Implement me (:
+ },
+ loadGraph: function(nodes, links) {
+ topology = new Graph();
+
+ nodes.forEach(function (node) {
+ topology.addNode(node.id, {
+ title:node.title,
+ group:node.group,
+ label:node.label,
+ value:node.value
+ });
+ });
+
+ links.forEach(function (link) {
+ topology.addLink(link.from, link.to, {
+ id: link.id,
+ title: link.title
+ });
+ });
+ }
+ };
+};
--- /dev/null
+var defaultConfig = {
+ width: 800,
+ height: 600,
+ switch: {
+ src : '../assets/images/Device_switch_3062_unknown_64.png',
+ width: 64,
+ height: 64
+ },
+ host: {
+ src : '../assets/images/Device_pc_3045_default_64.png',
+ width: 64,
+ height: 64
+ }
+};
+module.exports = function(id, graph, config) {
+ var merge = require('ngraph.merge');
+ var Matrix = require('node-transform-matrix');
+ var container = validateContainer(id);
+
+ // merge current config with default
+ config = merge(config, defaultConfig);
+
+ var stage = new PIXI.Stage(0xFFFFFF, true);
+
+ // use to keep a reference of the node
+ // and not always search in the layout.
+ var nodes = {}, links = {};
+
+ var width = config.width;
+ var height = config.height;
+
+ var renderer = PIXI.autoDetectRenderer(width, height);
+ stage['containerId'] = id;
+ stage['camera'] = {
+ transform :new Matrix(),
+ scaleFactor: 1
+ };
+
+ container.appendChild(renderer.view);
+
+ var emitter = require('./eventListener.js')(stage, graph);
+ var switchTexture = new PIXI.Texture.fromImage(config.switch.src);
+ var hostTexture = new PIXI.Texture.fromImage(config.host.src);
+
+ // add a physic layout to place the node in the screen
+ var layoutCreator = require('ngraph.forcelayout'),
+ physics = require('ngraph.physics.simulator');
+
+ var layout = layoutCreator(graph, physics({
+ springLength: 100,
+ springCoeff: 0.0008,
+ dragCoeff: 0.01,
+ gravity: -1.2,
+ theta: 1
+ }));
+
+ // link renderer
+ var graphics = new PIXI.Graphics();
+
+ graph.forEachNode(initializeNode);
+ graph.forEachLink(initializeLink);
+
+ // loop into the graph to find a node with his position.
+ // certainly a better way to do it
+ graph.getNodeAt = function(x, y) {
+ var half = config.switch.width * 0.5 * stage.camera.scaleFactor;
+ for (var key in nodes) {
+ if (nodes.hasOwnProperty(key)) {
+ var node = nodes[key];
+ var pos = stage.camera.transform.transformPoint(node.pos.x, node.pos.y);
+ var inside = pos[0] - half < x && x < pos[0] + half &&
+ pos[1] - half < y && y < pos[1] + half;
+ if (inside) {
+ return nodes[key];
+ }
+ }
+ }
+ };
+
+ // public API
+ return {
+ run : function() {
+ // step the layout to make a good looking graph
+ // remove after cause it's heavy for nothing
+ for (var i = 0; i < 1000; ++i) {
+ layout.step();
+ }
+ layout.dispose();
+ centerGraph();
+ requestAnimFrame(loop);
+ }
+ };
+
+ // add the node sprite to the stage
+ // and add it to the ref array
+ function initializeNode(node) {
+ var nodeSprite = null;
+
+ if (node.data.group === 'switch') {
+ nodeSprite = new PIXI.Sprite(switchTexture);
+ } else if (node.data.group === 'host') {
+ nodeSprite = new PIXI.Sprite(hostTexture);
+ }
+
+ var textOffset = 10;
+ var text = new PIXI.Text(node.data.label, {
+ font : '11px Arial'
+ });
+
+ var centerWidth = width * 0.5;
+ var centerHeight = height * 0.5;
+
+ nodeSprite.anchor.set(0.5, 0.5);
+
+ layout.setNodePosition(node.id, Math.random() * centerWidth, Math.random() * centerHeight);
+ nodeSprite.position.set(centerWidth, centerHeight);
+
+ // text is a child of the sprite, we don't need to apply a transform
+ text.anchor.set(0.5, 0.5);
+
+ text.position.y = config.switch.width * 0.5 + textOffset;
+
+ nodes[node.id] = {
+ sprite: nodeSprite,
+ pos: layout.getNodePosition(node.id)
+ };
+
+ nodeSprite.addChild(text);
+ stage.addChild(nodeSprite);
+ }
+
+ // set the link into the ref array
+ function initializeLink(link) {
+ links[link.id] = {
+ from: layout.getNodePosition(link.fromId),
+ to : layout.getNodePosition(link.toId)
+ };
+ stage.addChild(graphics);
+ }
+
+ // update node position
+ function updateNode(node) {
+ var transPos = stage.camera.transform.transformPoint(node.pos.x, node.pos.y);
+ var scale = stage.camera.scaleFactor;
+ node.sprite.position.set(transPos[0], transPos[1]);
+ node.sprite.scale.set(scale, scale);
+ }
+
+ // draw the link
+ function updateLink(link) {
+ var transFrom = stage.camera.transform.transformPoint(link.from.x, link.from.y);
+ var transTo = stage.camera.transform.transformPoint(link.to.x, link.to.y);
+ graphics.lineStyle(1, 0x000000, 1);
+ graphics.moveTo(transFrom[0], transFrom[1]);
+ graphics.lineTo(transTo[0], transTo[1]);
+ }
+
+ // render loop
+ function loop() {
+ graphics.clear();
+ var key;
+
+ // update link
+ for (key in links) {
+ if (links.hasOwnProperty(key)) {
+ updateLink(links[key]);
+ }
+ }
+
+ // update positions
+ for (key in nodes) {
+ if (nodes.hasOwnProperty(key)) {
+ updateNode(nodes[key]);
+ }
+ }
+
+ // render everything
+ renderer.render(stage);
+
+ // loop
+ requestAnimFrame(loop);
+ }
+
+ // Move the graph near the center.
+ // TODO : use a better algorithm like finding the centroid and move based on that position
+ function centerGraph() {
+ var size = getGraphSize();
+ var moveX = Math.abs(config.width - size[0]) * 0.5;
+ var moveY = Math.abs(config.height - size[1]) * 0.5;
+ stage.camera.transform.translate(moveX, moveY);
+ }
+
+ // get the size of the graph by finding min and max pos
+ function getGraphSize() {
+ var minX = 10000,
+ minY = 10000,
+ maxX = -1,
+ maxY = -1;
+
+ for (var key in nodes) {
+ var node = nodes[key];
+ if (node.pos.x < minX) {
+ minX = node.pos.x;
+ } else if (node.pos.x > maxX) {
+ maxX = node.pos.x;
+ }
+ if (node.pos.y < minY) {
+ minY = node.pos.y;
+ } else if (node.pos.y > maxY) {
+ maxY = node.pos.y;
+ }
+ }
+
+ return [maxX - minX, maxY - minY];
+ }
+
+ // check if the given id exist
+ function validateContainer(id) {
+ var container = document.getElementById(id);
+ if (!container) {
+ throw new NotFoundException('No container found for the givent id ' + id);
+ }
+ return container;
+ }
+
+ // custom exception
+ // TODO : Move to another file
+ function NotFoundException(message) {
+ this.message = message;
+ this.name = 'NotFoundException';
+ }
+
+};
'jquery' : '../vendor/jquery/jquery.min',
'jquery-ui' : '../vendor/jquery-ui/jquery-ui.min',
'footable' : '../vendor/footable/dist/footable.min',
+ 'pixi': '../vendor/pixi/bin/pixi',
'd3' : '../vendor/d3/d3.min',
- 'vis' : '../vendor/vis/dist/vis.min',
'ocLazyLoad' : '../vendor/ocLazyLoad/dist/ocLazyLoad',
+ 'vis' : '../vendor/vis/dist/vis.min',
'sigma' : '../vendor/sigma/sigma.min',
'sigma-parsers-gexf' : '../vendor/sigma/plugins/sigma.parsers.gexf.min',
'sigma-forceAtlas2' : '../vendor/sigma/plugins/sigma.layout.forceAtlas2.min',
'sigma-dragNodes' : '../vendor/sigma/plugins/sigma.plugins.dragNodes.min',
- 'sigma-customShapes' : '../vendor/sigma/plugins/sigma.renderers.customShapes.min'
+ 'sigma-customShapes' : '../vendor/sigma/plugins/sigma.renderers.customShapes.min',
+ 'graphRenderer' : '../assets/js/graphRenderer'
},
shim : {
'angular-translate': ['angular'],
'angular-translate-loader-static-files' : ['angular-translate'],
'ngload' : ['angularAMD'],
+ 'pixi' : {
+ exports: 'PIXI'
+ },
+ 'graphRenderer' : ['pixi'],
'jquery' : {
exports : '$'
},
'angular-translate-loader-static-files' : '../vendor/angular-translate-loader-static-files/angular-translate-loader-static-files.min',
'jquery' : '../vendor/jquery/jquery',
'footable' : '../vendor/footable/dist/footable.min',
+ 'pixi': '../vendor/pixi/bin/pixi',
'd3' : '../vendor/d3/d3.min',
'vis' : '../vendor/vis/dist/vis.min',
- 'ocLazyLoad' : '../vendor/ocLazyLoad/dist/ocLazyLoad'
+ 'ocLazyLoad' : '../vendor/ocLazyLoad/dist/ocLazyLoad',
+ 'graphRenderer' : '../assets/js/graphRenderer'
},
shim : {
'angular-translate': ['angular'],
'angular-translate-loader-static-files' : ['angular-translate'],
'ngload' : ['angularAMD'],
+ 'pixi' : {
+ exports: 'PIXI'
+ },
+ 'graphRenderer' : ['pixi'],
'jquery' : {
exports : '$'
},
<module>topology-resources</module>
<module>loader-resources</module>
<module>connection_manager-resources</module>
+ <module>graph-resources</module>
<module>core-resources</module>
<module>common-topbar-resources</module>
</modules>
-
+
</project>
<version>${topology.resources.version}</version>
<packaging>jar</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.dlux</groupId>
+ <artifactId>dlux.graph.resources</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
</project>
-define(['app/topology/topology.module', 'app/topology/topology.directives','app/topology/topology.services'], function(topology) {
+define(['app/topology/topology.module','app/topology/topology.services', 'graphRenderer', 'pixi'], function(topology, service, GraphRenderer, PIXI) {
+ window.PIXI = PIXI; // attach pixijs to global scope
+
topology.register.controller('TopologyCtrl', ['$scope', '$rootScope', 'NetworkTopologySvc' , function ($scope, $rootScope, NetworkTopologySvc) {
$rootScope['section_logo'] = 'logo_topology';
+ var graphRenderer = null;
$scope.createTopology = function() {
NetworkTopologySvc.getNode("flow:1", function(data) {
- var x = 50;
+ /*var x = 50;
var y = 50;
var step = 30;
data.nodes.push({id: 1001, x: x, y: y + step, label: 'Switch', group: 'switch',value:20});
- data.nodes.push({id: 1003, x: x, y: y + 3 * step, label: 'Host', group: 'host',value:20});
- $scope.topologyData = data;
+ data.nodes.push({id: 1003, x: x, y: y + 3 * step, label: 'Host', group: 'host',value:20});*/
+
+ var inNodes = data.nodes;
+ var inEdges = data.links;
+ graphRenderer = new GraphRenderer();
+ graphRenderer.loadGraph(inNodes, inEdges);
+ graphRenderer.start('topology_simple');
});
};
<h3>Controls</h3>
<button class="btn btn-primary" ng-click="createTopology()">Reload</button>
</div>
- <topology-simple class="col-md-10 col-lg-offset-1" topology-data="topologyData"></topology-simple>
+ <div class="col-md-10 col-lg-offset-1" id="topology_simple"></div>
</div>
</div>
<commons.opendaylight.version>1.5.0-SNAPSHOT</commons.opendaylight.version>
<loader.resources.version>0.1.0-SNAPSHOT</loader.resources.version>
<core.resources.version>0.1.0-SNAPSHOT</core.resources.version>
+ <graph.resources.version>0.1.0-SNAPSHOT</graph.resources.version>
<node.resources.version>0.1.0-SNAPSHOT</node.resources.version>
<connection_manager.resources.version>0.1.0-SNAPSHOT</connection_manager.resources.version>
<container.resources.version>0.1.0-SNAPSHOT</container.resources.version>