Commit 417bd685 authored by Sebastien Jourdain's avatar Sebastien Jourdain
Browse files

Add Graph Layout example in VTK-Web

The example generate a random graph and display it
within the Web browser window in full screen.
The user can also switch from several layout by
changing the selected value in the combo-box on
the top right corner.

To run it, execute the following command line from the build tree:

$ ./bin/vtkpython Wrapping/Python/vtk/web/vtk_web_graph.py --content www --vertices 10 --edges 10

Change-Id: I9072afa3896c48de7cdaa89857b77773234db06e
parent 99101fdf
......@@ -5,6 +5,7 @@ find_package(PythonInterp REQUIRED)
set(WEB_APPLICATIONS
Cone
GraphLayout
PhylogeneticTree
)
......
r"""
This module is a VTK Web server application.
The following command line illustrate how to use it::
$ vtkpython .../vtk_web_graph.py --vertices 1000 --edges 400
Any VTK Web executable script come with a set of standard arguments that
can be overriden if need be::
--host localhost
Interface on which the HTTP server will listen on.
--port 8080
Port number on which the HTTP server will listen to.
--content /path-to-web-content/
Directory that you want to server as static web content.
By default, this variable is empty which mean that we rely on another server
to deliver the static content and the current process only focus on the
WebSocket connectivity of clients.
--authKey vtk-secret
Secret key that should be provided by the client to allow it to make any
WebSocket communication. The client will assume if none is given that the
server expect "vtk-secret" as secret key.
"""
# import to process args
import sys
import os
# import vtk modules.
from vtk import *
import json
import math
# import vtk web modules
from vtk.web import server, wamp, protocols
# import annotations
from autobahn.wamp import exportRpc
try:
import argparse
except ImportError:
# since Python 2.6 and earlier don't have argparse, we simply provide
# the source for the same as _argparse and we use it instead.
import _argparse as argparse
# =============================================================================
# Create custom File Opener class to handle clients requests
# =============================================================================
class _WebGraph(wamp.ServerProtocol):
# Application configuration
vertices = 1000
edges = 400
view = None
authKey = "vtkweb-secret"
def initialize(self):
global renderer, renderWindow, renderWindowInteractor, cone, mapper, actor
# Bring used components
self.registerVtkWebProtocol(protocols.vtkWebMouseHandler())
self.registerVtkWebProtocol(protocols.vtkWebViewPort())
self.registerVtkWebProtocol(protocols.vtkWebViewPortImageDelivery())
# Update authentication key to use
self.updateSecret(_WebGraph.authKey)
# Create default pipeline (Only once for all the sessions)
if not _WebGraph.view:
# Generate Random graph
random = vtkRandomGraphSource()
random.SetNumberOfVertices(_WebGraph.vertices)
random.SetNumberOfEdges(_WebGraph.edges)
random.SetStartWithTree(True)
random.Update()
graphData = random.GetOutput()
# Create view
view = vtkGraphLayoutView()
view.AddRepresentationFromInput(graphData)
# Customize Rendering
view.SetVertexLabelArrayName("vertex id")
view.SetVertexLabelVisibility(True)
view.SetVertexColorArrayName("vertex id")
view.SetColorVertices(True)
view.SetScalingArrayName("vertex id")
view.ScaledGlyphsOn()
view.HideVertexLabelsOnInteractionOn()
view.SetEdgeColorArrayName("edge id")
view.SetColorEdges(True)
view.SetLayoutStrategyToSpanTree()
# Set trackball interaction style
style = vtkInteractorStyleTrackballCamera()
view.GetRenderWindow().GetInteractor().SetInteractorStyle(style)
# VTK Web application specific
_WebGraph.view = view
view.ResetCamera()
view.Render()
self.Application.GetObjectIdMap().SetActiveObject("VIEW", view.GetRenderWindow())
@exportRpc("changeLayout")
def changeLayout(self, layoutName):
if layoutName == 'ForceDirected' :
print 'Layout Strategy = Force Directed'
_WebGraph.view.SetLayoutStrategyToForceDirected()
_WebGraph.view.GetLayoutStrategy().ThreeDimensionalLayoutOn()
if layoutName == 'SpanTree' :
print 'Layout Strategy = Span Tree (Depth First Off)'
_WebGraph.view.SetLayoutStrategyToSpanTree()
_WebGraph.view.GetLayoutStrategy().DepthFirstSpanningTreeOff()
elif layoutName == 'SpanTreeDepthFirst' :
print 'Layout Strategy = Span Tree (Depth First On)'
_WebGraph.view.SetLayoutStrategyToSpanTree()
_WebGraph.view.GetLayoutStrategy().DepthFirstSpanningTreeOn()
elif layoutName == 'Circular' :
print 'Layout Strategy = Circular'
_WebGraph.view.SetLayoutStrategyToCircular()
elif layoutName == 'Random' :
print 'Layout Strategy = Random'
_WebGraph.view.SetLayoutStrategyToRandom()
elif layoutName == 'Fast2D' :
print 'Layout Strategy = Fast 2D'
_WebGraph.view.SetLayoutStrategyToFast2D()
elif layoutName == 'Clustering2D' :
print 'Layout Strategy = Clustering 2D'
_WebGraph.view.SetLayoutStrategyToClustering2D()
elif layoutName == 'Community2D' :
print 'Layout Strategy = Community 2D'
_WebGraph.view.SetLayoutStrategyToCommunity2D()
_WebGraph.view.ResetCamera()
_WebGraph.view.Render()
# =============================================================================
# Main: Parse args and start server
# =============================================================================
if __name__ == "__main__":
# Create argument parser
parser = argparse.ArgumentParser(description="VTK/Web Graph web-application")
# Add default arguments
server.add_arguments(parser)
# Add local arguments
parser.add_argument("--vertices", help="Number of vertices used to generate graph", dest="vertices", type=int, default=1000)
parser.add_argument("--edges", help="Number of edges used to generate graph", dest="edges", type=int, default=400)
# Exctract arguments
args = parser.parse_args()
# Configure our current application
_WebGraph.authKey = args.authKey
_WebGraph.vertices = args.vertices
_WebGraph.edges = args.edges
# Start server
server.start_webserver(options=args, protocol=_WebGraph)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='style.css' type='text/css'>
</head>
<body class="page" onbeforeunload="exit()" onunload="exit()">
<select>
<option value="ForceDirected">Force Directed</option>
<option value="SpanTree">Span Tree</option>
<option value="SpanTreeDepthFirst">Span Tree Depth First</option>
<option value="Circular">Circular</option>
<option value="Random">Random</option>
<option value="Fast2D">Fast 2D</option>
<option value="Clustering2D">Clustering 2D</option>
<option value="Community2D">Community 2D</option>
</select>
<div class="viewport-container"></div>
<script src="../../lib/js/vtkweb-loader-min.js" load="all-min"></script>
<script type="text/javascript">
var connection = {
sessionURL: "ws://" + window.location.host + "/ws",
name: "WebGraphLayoutDemo",
application: "graph"
},
viewport = null;
// Method call at exit time
function exit() {
if(connection.session) {
viewport.unbind();
connection.session.call('vtk:exit');
connection.session.close();
connection.session = null;
}
}
// Connect to remote server
vtkWeb.connect(connection, function(serverConnection) {
connection = serverConnection;
// Create viewport
viewport = vtkWeb.createViewport({session:connection.session});
viewport.bind(".viewport-container");
// Attach listener to layout controller
$('select').change(function(){
var me = $(this);
connection.session.call("vtk:changeLayout", me.val()).then(function(){
viewport.render();
});
}).trigger("change");
// Handle window resize
$(window).resize(function() {
if(viewport) {
viewport.render();
}
}).trigger('resize');
}, function(code, reason) {
alert(reason);
});
</script>
</body>
</html>
html, body, div, span, h1, h2, h3, h4, p, a, big, em, font, img, s, small, strong,
tt, var, b,u,i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, table,
tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
vertical-align: baseline;
font-family: Arial,sans-serif; font-size: 1em;
}
.viewport-container {
margin: 0;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
select {
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
}
\ No newline at end of file
......@@ -21,23 +21,31 @@ set(lib_js_min_files
)
set(vtkweb_all_min_js "${lib_binary_dir}/vtkweb-all.min.js")
set(vtkweb_all_js "${lib_binary_dir}/vtkweb-all.js")
set(vtkweb_loader_js "${lib_binary_dir}/vtkweb-loader.js")
set(vtkweb_loader_min_js "${lib_binary_dir}/vtkweb-loader-min.js")
set(args)
list(APPEND args "-b" "${CMAKE_CURRENT_SOURCE_DIR}/banner.js.in")
list(APPEND args "-v" "v2.0")
list(APPEND args "-i")
set(min_all_args ${args})
foreach(_file ${lib_js_min_files})
list(APPEND args "${_file}")
list(APPEND min_all_args "${_file}")
endforeach()
list(APPEND args "-o" ${vtkweb_all_min_js})
list(APPEND args "-m" ${vtkweb_all_js})
list(APPEND min_all_args "-o" ${vtkweb_all_min_js})
list(APPEND min_all_args "-m" ${vtkweb_all_js})
add_custom_command(OUTPUT ${vtkweb_all_min_js} ${vtkweb_all_js}
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/Minimizer/build.py" ${args}
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/Minimizer/build.py" ${min_all_args}
DEPENDS ${lib_js_min_files})
add_custom_command(OUTPUT ${vtkweb_loader_js} ${vtkweb_loader_min_js}
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/Minimizer/build.py" ${args}
"${lib_source_dir}/vtkweb-loader.js" -o ${vtkweb_loader_min_js} -m ${vtkweb_loader_js}
DEPENDS "${lib_source_dir}/vtkweb-loader.js")
# Copy Ext js files
include(vtkPythonPackages)
copy_files_recursive(${ext_source_dir}
......@@ -47,5 +55,5 @@ copy_files_recursive(${ext_source_dir}
LABEL "Copying JavaScript files")
add_custom_target(vtkWebJavaScript ALL DEPENDS
${vtkweb_all_min_js}
${vtkweb_all_min_js} ${vtkweb_loader_min_js}
"${CMAKE_CURRENT_BINARY_DIR}/ext-js-copy-complete")
......@@ -8,12 +8,7 @@
*
* @singleton
*/
(function (GLOBAL, $) {
function insertBefore(referenceNode, newNode) {
"use strict";
referenceNode.parentNode.insertBefore(newNode, referenceNode);
};
(function (GLOBAL) {
var vtkWebLibs = {
"core-min" : [
......@@ -55,7 +50,8 @@
},
modules = [],
script = document.getElementsByTagName("script")[document.getElementsByTagName("script").length - 1],
basePath = "";
basePath = "",
extraScripts = [];
// Extract modules to load
try {
......@@ -67,13 +63,23 @@
// We don't care we will use the default setup
}
// Extract extra script to load
try {
extraScripts = script.getAttribute("extra").split(",");
for(var j in extraScripts) {
extraScripts[j] = extraScripts[j].replace(/^\s+|\s+$/g, ''); // Trim
}
} catch(e) {
// We don't care we will use the default setup
}
// If no modules have been defined, just pick the default
if(modules.length == 0) {
modules = [ "all-min" ];
}
// Extract basePath
var lastSlashIndex = script.getAttribute("src").lastIndexOf('lib/js/vtkweb-loader.js');
var lastSlashIndex = script.getAttribute("src").lastIndexOf('lib/js/vtkweb-loader');
if(lastSlashIndex != -1) {
basePath = script.getAttribute("src").substr(0, lastSlashIndex);
}
......@@ -81,13 +87,15 @@
// Add missing libs
for(var i in modules) {
for(var j in vtkWebLibs[modules[i]]) {
var newScript = document.createElement("script");
newScript.setAttribute("src", basePath + vtkWebLibs[modules[i]][j]);
insertBefore(script, newScript);
document.write('<script src="' + basePath + vtkWebLibs[modules[i]][j] + '"></script>');
}
}
// Add extra libs
for(var i in extraScripts) {
document.write('<script src="' + extraScripts[i] + '"></script>');
}
// Remove loader
script.parentNode.removeChild(script);
}(window));
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment