ParaView-5.0.1: Added the source-tree to ThirdParty-dev and patched as described in the README file

Resolves bug-report http://bugs.openfoam.org/view.php?id=2098
This commit is contained in:
Henry Weller
2016-05-30 21:20:56 +01:00
parent 1cce60aa78
commit eba760a6d6
24640 changed files with 6366069 additions and 0 deletions

View File

@ -0,0 +1,283 @@
/**
* vtkWebLoader JavaScript Library.
*
* vtkWebLoader use the vtkWeb namespace to manage JavaScript dependency and more specifically
* vtkWeb dependencies.
*
* @class vtkWebLoader
*
* @singleton
*/
(function (GLOBAL) {
var vtkWebLibs = {
"core" : [
"ext/core/jquery-1.8.3.min.js",
"ext/core/autobahn.min.js",
"ext/core/gl-matrix.js",
"ext/core/jquery.hammer.js",
"ext/core/vgl.min.js",
"lib/core/vtkweb-all.js"
],
"core-min": [
"ext/core/jquery-1.8.3.min.js",
"ext/core/autobahn.min.js",
"ext/core/gl-matrix-min.js",
"ext/core/jquery.hammer.min.js",
"ext/core/vgl.min.js",
"lib/core/vtkweb-all.min.js"
],
"bootstrap": [
"ext/bootstrap/js/bootstrap.min.js",
"ext/bootstrap/css/bootstrap-responsive.min.css",
"ext/bootstrap/css/bootstrap.min.css"
],
"fontello": [
"ext/fontello/css/animation.css",
"ext/fontello/css/fontello.css"
],
"color": [
"ext/jscolor/jscolor.js"
],
"filebrowser": [
"ext/pure/pure.min.js",
"lib/widgets/FileBrowser/vtkweb-widget-filebrowser.js",
"lib/widgets/FileBrowser/vtkweb-widget-filebrowser.tpl",
"lib/widgets/FileBrowser/vtkweb-widget-filebrowser.css"
],
"pv-pipeline": [
"ext/jquery-ui/jquery-ui-1.10.0.css",
"ext/jquery-ui/jquery-ui-1.10.0.min.js",
"lib/css/paraview.ui.pipeline.css",
"lib/js/paraview.ui.pipeline.js",
],
"pv-toolbar": [
"lib/css/paraview.ui.toolbar.css",
"lib/css/paraview.ui.toolbar.vcr.css",
"lib/css/paraview.ui.toolbar.viewport.css",
"lib/css/paraview.ui.toolbar.connect.css",
"lib/js/paraview.ui.toolbar.js",
"lib/js/paraview.ui.toolbar.vcr.js",
"lib/js/paraview.ui.toolbar.viewport.js",
"lib/js/paraview.ui.toolbar.connect.js"
],
"jquery-ui": [
"ext/jquery-ui/jquery-ui-1.10.0.css",
"ext/jquery-ui/jquery-ui-1.10.0.min.js"
],
"d3":[
"ext/d3/d3.v2.js"
],
"nvd3":[
"ext/nvd3/nv.d3.css",
"ext/nvd3/nv.d3.js"
],
"rickshaw": [
"ext/rickshaw/rickshaw.min.css",
"ext/rickshaw/rickshaw.min.js"
],
"widgets": [
"ext/pure/pure.min.js",
"ext/d3/d3.v2.js",
"ext/rickshaw/rickshaw.min.css",
"ext/rickshaw/rickshaw.min.js",
"ext/fontello/css/animation.css",
"ext/fontello/css/fontello.css",
"lib/widgets/FileBrowser/vtkweb-widget-filebrowser.tpl",
"lib/widgets/TreeWidget/vtkweb-widget-tree.tpl",
"lib/widgets/vtkweb-widgets-min.css",
"lib/widgets/vtkweb-widgets-min.js"
],
"pv-visualizer": [
"ext/fontello/css/animation.css",
"ext/fontello/css/fontello.css",
"ext/bootstrap3/css/bootstrap-theme.min.css",
"ext/bootstrap3/css/bootstrap.min.css",
"ext/bootstrap3/js/bootstrap.min.js",
"lib/js/paraview.ui.action.list.js",
"lib/js/paraview.ui.files.js",
"lib/js/paraview.ui.data.js",
"lib/js/paraview.ui.proxy.editor.js",
"lib/css/paraview.ui.proxy.editor.css",
"lib/js/paraview.ui.svg.pipeline.js",
"lib/js/paraview.ui.opacity.editor.js",
"lib/css/paraview.ui.opacity.editor.css",
"lib/js/paraview.ui.color.editor.js",
"lib/css/paraview.ui.color.editor.css"
],
"pv-visualizer-main-js": [
"apps/Visualizer/main.js"
],
"pv-visualizer-main-all": [
"apps/Visualizer/main.js",
"apps/Visualizer/main.css"
],
"pv-visualizer-tpl": "apps/Visualizer/visualizer-tpl.html"
},
modules = [],
script = document.getElementsByTagName("script")[document.getElementsByTagName("script").length - 1],
basePath = "",
extraScripts = [];
// ---------------------------------------------------------------------
function loadCss(url) {
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = url;
head = document.getElementsByTagName("head")[0];
head.insertBefore(link, head.childNodes[0]);
}
// ---------------------------------------------------------------------
function loadJavaScript(url) {
document.write('<script src="' + url + '"></script>');
}
// ---------------------------------------------------------------------
function loadTemplate(url) {
var templates = document.getElementById("vtk-templates");
if(templates === null) {
templates = document.createElement("div");
templates.setAttribute("style", "display: none;");
templates.setAttribute("id", "vtk-templates");
document.getElementsByTagName("body")[0].appendChild(templates);
}
// Fetch template and append to vtk-templates
var request = makeHttpObject();
request.open("GET", url, true);
request.send(null);
request.onreadystatechange = function() {
if (request.readyState == 4) {
var content = templates.innerHTML;
content += request.responseText;
templates.innerHTML = content;
}
};
}
// ---------------------------------------------------------------------
function loadGlobalTemplate(url) {
var request = makeHttpObject();
request.open("GET", url, true);
request.send(null);
request.onreadystatechange = function() {
if (request.readyState == 4) {
var content = request.responseText,
body = document.getElementsByTagName("body")[0];
body.innerHTML += content;
}
};
}
// ---------------------------------------------------------------------
function makeHttpObject() {
try {
return new XMLHttpRequest();
}
catch (error) {}
try {
return new ActiveXObject("Msxml2.XMLHTTP");
}
catch (error) {}
try {
return new ActiveXObject("Microsoft.XMLHTTP");
}
catch (error) {}
throw new Error("Could not create HTTP request object.");
}
// ---------------------------------------------------------------------
function _endWith(string, end) {
return string.lastIndexOf(end) === (string.length - end.length);
}
// ---------------------------------------------------------------------
function loadFile(url) {
if(_endWith(url, ".js")) {
loadJavaScript(url);
} else if(_endWith(url, ".css")) {
loadCss(url);
} else if(_endWith(url, ".tpl")) {
loadTemplate(url);
}
}
// ---------------------------------------------------------------------
// Extract modules to load
// ---------------------------------------------------------------------
try {
modules = script.getAttribute("load").split(",");
for(var j in modules) {
modules[j] = modules[j].replace(/^\s+|\s+$/g, ''); // Trim
}
} catch(e) {
// 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 = [ "core-min" ];
}
// ---------------------------------------------------------------------
// Extract basePath
// ---------------------------------------------------------------------
var lastSlashIndex = script.getAttribute("src").lastIndexOf('lib/core/vtkweb-loader');
if(lastSlashIndex != -1) {
basePath = script.getAttribute("src").substr(0, lastSlashIndex);
}
// ---------------------------------------------------------------------
// See if we have a main application template to load
// ---------------------------------------------------------------------
try {
var templateKey = script.getAttribute("app-template");
if (templateKey !== null && templateKey !== '') {
loadGlobalTemplate(basePath + vtkWebLibs[templateKey]);
}
} catch (e) {
// This is fine, you may have defined your application inline
}
// ---------------------------------------------------------------------
// Add missing libs
// ---------------------------------------------------------------------
for(var i in modules) {
for(var j in vtkWebLibs[modules[i]]) {
var path = basePath + vtkWebLibs[modules[i]][j];
loadFile(path);
}
}
// ---------------------------------------------------------------------
// Add extra libs
// ---------------------------------------------------------------------
for(var i in extraScripts) {
loadFile(extraScripts[i]);
}
// ---------------------------------------------------------------------
// Remove loader
// ---------------------------------------------------------------------
script.parentNode.removeChild(script);
}(window));

View File

@ -0,0 +1,289 @@
/**
* vtkWeb JavaScript Library.
*
* This module allow the Web client to connect to a remote vtkWeb session.
* The session must already exist and waiting for connections.
*
* This module registers itself as: 'vtkweb-connect'
*
* @class vtkWeb.connect
*
* {@img paraview/ParaViewWeb-simple.png
* alt Focus on the communication between the client and the vtkWeb process}
*/
(function (GLOBAL, $) {
// Connections field used to store a map of the active sessions
var connections = {}, module = {};
/*
* Create a transport object appropriate to the protocol specified
* in the given url.
*/
function getTransportObject(url) {
var idx = url.indexOf(':'),
protocol = url.substring(0, idx);
if (protocol === 'ws' || protocol === 'wss') {
return {
'type': 'websocket',
'url': url,
};
} else if (protocol === 'http' || protocol === 'https') {
return {
'type': 'longpoll',
'url': url,
request_timeout: 300000
};
} else {
throw "Unknown protocol (" + protocol + ") for url (" + url + "). Unable to create transport object.";
}
}
/**
* @class vtkWeb.Session
* vtkWeb Session object on which RPC method calls can be made.
*
* session.call("viewport.image.render", request).then( function (reply) {
* // Do something with the reply
* });
*/
/**
* @member vtkWeb.Session
* @method call
* Returns a future of the RPC call.
* @param {String} method
* Full path method name.
* @param {Object|String|Number} args
* Arguments of the RPC call.
*
* @return {vtkWeb.Future} of the RPC call
*/
/**
* @class vtkWeb.Future
* Object on which can be attached a callback.
*/
/**
* @member vtkWeb.Future
* @method then
* @param {Function} callback
* Function to be called once the RPC called is done. The argument of the
* function is the response of the RPC method.
*/
/**
* Connect to a running vtkWeb session
*
* @member vtkWeb.connect
*
* @param {vtkWeb.Connection} connection
* A connection object that should have been generated by the Launcher
* part if any.
*
* connection = {
* sessionURL: "http://localhost:8080/ws"
* }
*
* get extended to once the readyCallback get called:
*
* connection = {
* sessionURL: "http://localhost:8080/ws",
* session: {vtkWeb.Session}
* }
*
*
* @param {Function} readyCallback
* Callback function called when a connection that has been extended with
* a valid {@link vtkWeb.Session}.
*
* @param {Function} closeCallback
* Callback function called when the session end.
*
* vtkWeb.connect(
* connection,
* function(connection) {
* // Now that we have a valid session let's add a viewport to
* // see the 3D view of our vtkWeb pipeline.
* var viewport = vtkWeb.createViewport(connection.session);
* viewport.bind(".viewport-3d");
* },
* function(code,reason) {
* if (code == ab.CONNECTION_UNSUPPORTED) {
* alert("Connection not supported");
* } else {
* console.log(reason);
* }
* }
* );
*/
function connect(connectionInfo, readyCallback, closeCallback) {
var uri = connectionInfo.sessionURL,
onReady = readyCallback,
onClose = closeCallback,
uriList = [].concat(uri),
transports = [];
if(!connectionInfo.hasOwnProperty("secret")) {
connectionInfo.secret = "vtkweb-secret"; // Default value
}
for (var i = 0; i < uriList.length; i+=1) {
var url = uriList[i],
transport = null;
try {
transport = getTransportObject(url);
transports.push(transport);
} catch (transportCreateError) {
console.error(transportCreateError);
}
}
connectionInfo.connection = new autobahn.Connection({
transports: transports,
realm: "vtkweb",
authmethods: ["wampcra"],
authid: "vtkweb",
onchallenge: function(session, method, extra) {
if (method === "wampcra") {
var secretKey = autobahn.auth_cra.derive_key(connectionInfo.secret, "salt123");
return autobahn.auth_cra.sign(secretKey, extra.challenge);
} else {
throw "don't know how to authenticate using '" + method + "'";
}
}
});
connectionInfo.connection.onopen = function(session, details) {
try {
connectionInfo.session = session;
connections[connectionInfo.sessionURL] = connectionInfo;
if (onReady) {
onReady(connectionInfo);
}
} catch(e) {
console.log(e);
}
}
connectionInfo.connection.onclose = function() {
delete connections[connectionInfo.sessionURL];
if (onClose) {
onClose(arguments[0], arguments[1].reason);
}
return false;
}
connectionInfo.connection.open();
}
/**
* Create a session that uses only http to make calls to the
* server-side protocols.
*
* @member {object} vtkWeb.connect
* A json object that only needs a 'sessionURL' string in to know where
* to send method requests.
*
* @param connetionInfo
*
* @param {Function} readyCallback
* Callback function called when a connection that has been extended with
* a valid session that can be used to make server-side method calls.
*
*/
function httpConnect(connectionInfo, readyCallback) {
connectionInfo.session = {
'call': function(methodName, args) {
var dfd = $.Deferred();
$.ajax({
type: 'POST',
url: connectionInfo.sessionURL + methodName,
dataType: 'json',
data: JSON.stringify({ 'args': args }),
success: function(result) {
dfd.resolve(result);
},
error: function(error) {
dfd.reject(error);
}
});
return dfd.promise();
}
};
readyCallback(connectionInfo);
}
/**
* Return any existing session for a given connection or Wamp URL
*
* @member vtkWeb.connect
*
* @param {String} sessionURL
* The sessionURL String.
*
* @return {vtkWeb.Connection} that contains a {@link vtkWeb.Session}
*/
function getConnection(sessionURL) {
return connections[sessionURL];
}
/**
* Return all the available connections stored in an Object like follow:
*
* {
* "ws://localhost:8080/proxy?sessionId=2345": connection
* }
*
* {@link vtkWeb.Connection}
*
* @member vtkWeb.connect
*/
function getConnections() {
return connections;
}
// ----------------------------------------------------------------------
// Init vtkWeb module if needed
// ----------------------------------------------------------------------
if (GLOBAL.hasOwnProperty("vtkWeb")) {
module = GLOBAL.vtkWeb || {};
} else {
GLOBAL.vtkWeb = module;
}
// ----------------------------------------------------------------------
// Export methods to the vtkWeb module
// ----------------------------------------------------------------------
module.connect = function (connection, ready, close) {
connect(connection, ready, close);
};
module.getConnection = function (connection) {
return getConnection(connection);
};
module.getConnections = function () {
return getConnections();
};
module.httpConnect = function(connection, readyCallback) {
return httpConnect(connection, readyCallback);
};
// ----------------------------------------------------------------------
// Local module registration
// ----------------------------------------------------------------------
try {
// Tests for presence of autobahn, then registers this module
if (GLOBAL.autobahn !== undefined) {
module.registerModule('vtkweb-connect');
} else {
console.error('Module failed to register, autobahn is missing');
}
} catch(err) {
console.error('Caught exception while registering module: ' + err.message);
}
}(window, jQuery));

View File

@ -0,0 +1,210 @@
/**
* vtkWeb JavaScript Library.
*
* This main module just gathers all the modules into a single namespace.
*
* This module registers itself as: 'vtkweb-base'
*
* @class vtkWeb
*
* @mixins vtkWeb.launcher
* @mixins vtkWeb.connect
* @mixins vtkWeb.viewport
* @mixins vtkWeb.viewport.image
* @mixins vtkWeb.viewport.webgl
*
* @singleton
*/
(function (GLOBAL, $) {
// VERSION field that store the current version of the library.
// WampSessions field used to store a map of the active sessions
// Default Viewport options values
var VERSION = "2.0.0",
fallBackStorage = {},
isMobile = (function(a){
return (/android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i).test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i.test(a.substr(0,4));
})(navigator.userAgent||navigator.vendor||window.opera), module = {}, registeredModules = [];
// ----------------------------------------------------------------------
extractURLParameters = function() {
var data = location.search.substr(1).split("&"), parameters = {};
for(var i=0; i<data.length; i++) {
var item = data[i].split("=");
parameters[item[0]] = item[1];
}
return parameters;
}
// ----------------------------------------------------------------------
isSecured = function() {
return location.protocol === 'https';
}
// ----------------------------------------------------------------------
udpateConnectionFromURL = function(connection) {
var params = extractURLParameters();
for(var key in params) {
connection[key] = decodeURIComponent(params[key]);
}
}
supportsLocalStorage = function() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch(e) {
return false;
}
}
storeApplicationDataObject = function(key, jsonObj) {
var storage = fallBackStorage;
if (supportsLocalStorage()) {
storage = localStorage;
}
storage[key] = JSON.stringify(jsonObj);
}
retrieveApplicationDataObject = function(key) {
var storage = fallBackStorage;
if (supportsLocalStorage()) {
storage = localStorage;
}
var storedData = storage[key];
if (storedData) {
try {
return JSON.parse(storedData);
} catch(e) {
console.error('Failed to parse stored data object as JSON: '+ storedData);
console.error(e);
}
}
return {}; // if not supported, we could store in a map in vtkweb
}
// ----------------------------------------------------------------------
// Init vtkWeb module if needed
// ----------------------------------------------------------------------
if (GLOBAL.hasOwnProperty("vtkWeb")) {
module = GLOBAL.vtkWeb || {};
} else {
GLOBAL.vtkWeb = module;
}
// ----------------------------------------------------------------------
// Export internal methods to the vtkWeb module
// ----------------------------------------------------------------------
module.version = VERSION;
module.isMobile = isMobile;
module.extractURLParameters = extractURLParameters;
module.udpateConnectionFromURL = udpateConnectionFromURL;
module.supportsLocalStorage = supportsLocalStorage;
module.storeApplicationDataObject = storeApplicationDataObject;
module.retrieveApplicationDataObject = retrieveApplicationDataObject;
module.properties = {
'sessionManagerURL': location.protocol + "//" + location.hostname + ":" + location.port + "/paraview/",
'sessionURL': (isSecured() ? "wss" : "ws") + "://" + location.hostname + ":" + location.port + "/ws"
};
module.NoOp = function() {};
module.errorCallback = function(code, reason) {
alert(reason);
GLOBAL.close();
}
// ----------------------------------------------------------------------
// Module registration hooks.
// ----------------------------------------------------------------------
/**
* Javascript libraries can call this function to register themselves.
*
* @member vtkWeb
* @method registerModule
*
* @param {String} The module name to use when registering a module.
*
*/
module.registerModule = function(moduleName) {
if (module.modulePresent(moduleName) === false) {
registeredModules.push(moduleName);
}
};
/**
* Javascript libraries call this function to determine whether a
* particular module is loaded.
*
* @member vtkWeb
* @method modulePresent
*
* @param {String} The name of the module name to check.
*
* @return {Boolean} True if the module specified by the given name
* is registered, false otherwise.
*/
module.modulePresent = function(moduleName) {
for (var i = 0; i < registeredModules.length; ++i) {
if (moduleName === registeredModules[i]) {
return true;
}
}
return false;
};
/**
* Javascript libraries call this function to determine whether an
* entire list of modules is loaded.
*
* @member vtkWeb
* @method allModulesPresent
*
* @param {Array} An array of module names to check.
*
* @return {Boolean} True if all modules names supplied are registered,
* false otherwise.
*/
module.allModulesPresent = function(moduleNameList) {
if (! moduleNameList instanceof Array) {
throw "allModulesPresent() takes an array, passed " + typeof(moduleNameList);
}
for (var i = 0; i < moduleNameList.length; ++i) {
if (module.modulePresent(moduleNameList[i]) === false) {
return false;
}
}
return true;
}
/**
* Javascript libraries call this function to get a list of the
* currently registered modules.
*
* @member vtkWeb
* @method getRegisteredModules
*
* @return {Array} An array of the registered module names.
*/
module.getRegisteredModules = function() {
return registeredModules;
};
// ----------------------------------------------------------------------
// Local module registration
// ----------------------------------------------------------------------
try {
// Tests for presence of jQuery, then registers this module
if ($ !== undefined) {
module.registerModule('vtkweb-base');
} else {
console.error('Module failed to register, jQuery is missing: ' + err.message);
}
} catch(err) {
console.error('Caught exception while registering module: ' + err.message);
}
}(window, jQuery));

View File

@ -0,0 +1,250 @@
/**
* vtkWeb JavaScript Library.
*
* This module allow the Web client to start a remote vtkWeb session and
* retreive all the connection information needed to properly connect to that
* newly created session.
*
* @class vtkWeb.launcher
*
* {@img paraview/ParaViewWeb-multiuser.png alt Focus on the communication between the client and the front-end that manage the vtkWeb processes}
*/
(function (GLOBAL, $) {
// Internal field used to store all connection objects
var Connections = [], module = {}, console = GLOBAL.console;
function generateSecretKey() {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 10; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
/**
* @class vtkWeb.Connection
* This class provides all the informations needed to connect to the session
* manager web service.
*/
/**
* @member vtkWeb.Connection
* @property {String} sessionManagerURL
* The service URL that will respond to the REST request to start or stop
* a visualization session.
*
* MANDATORY
*/
/**
* @member vtkWeb.Connection
* @property {String} name
* The name given for the visualization.
*
* RECOMMENDED/OPTIONAL
*/
/**
* @member vtkWeb.Connection
* @property {String} application
* The name of the application that should be started on the server side.
*
* MANDATORY
*/
/**
* @member vtkWeb.Connection
* @property {String|Number} __Any_Name__
* Any property that we want to provide to the session that will be created.
* Such property is not necessary used by the session manager but will be
* returned if a connection information is requested from a session.
*
* OPTIONAL
*/
/**
* @member vtkWeb.Connection
* @property {String} secret
* Password that should be used to protect remote session access.
*
* This property is used by the launcher to secure the process that it start
* but it is also used by the client to authenticate itself against
* the remote process.
*
* This can be provided by the client or by the server depending who
* generate it. In both case, the client will use it for its authentication.
* If missing, then the client will use the default secret key.
*
* OPTIONAL
*/
/**
* @member vtkWeb.Connection
* @property {Number} generate-secret
* Property used to specify where the generation of the secret key should be
* made.
* 0: We use the default secret key. (No dynamic one)
* 1: The JavaScript client generate the key and its the responsability of
* the server to provide the generated key to the vtkWeb process.
* 2: The launcher process generate that key when it start the vtkWeb
* process. That given secret key must be returned to the client within
* the connection object.
*
* OPTIONAL
*/
//=========================================================================
/**
* @member vtkWeb.Connection
* @property {String} sessionURL
* The websocket URL that should be used to connect to the running
* visualization session.
* This field is provided within the response.
*/
/**
* @member vtkWeb.Connection
* @property {String} id
* The session identifier.
* This field is provided within the response.
*/
/**
* @member vtkWeb.Connection
* @property {vtkWeb.Session} session
* The session object will be automatically added to the connection once the
* connection is properly established by calling:
*
* vtkWeb.connect(connection, success, error);
*
* This field is provided within the response.
*/
//=========================================================================
/**
* Start a new vtkWeb process on the server side.
* This method will make a JSON POST request to config.sessionManagerURL URL.
*
* @member vtkWeb.launcher
*
* @param {vtkWeb.ConnectionConfig} config
* Session creation parameters. (sessionManagerURL, name, application).
*
* @param {Function} successCallback
* The function will be called once the connection is successfully performed.
* The argument of the callback will be a {@link vtkWeb.Connection}.
*
* @param {Function} errorCallback
* The function will be called if anything bad happened and an explanation
* message will be provided as argument.
*/
function start(config, successFunction, errorFunction) {
if(!config.hasOwnProperty("secret") && config.hasOwnProperty("generate-secret") && config["generate-secret"] === 1) {
config.secret = generateSecretKey();
}
var okCallback = successFunction,
koCallback = errorFunction,
arg = {
url: config.sessionManagerURL,
type: "POST",
dataType: "json",
data: (JSON.stringify(config)),
success: function (reply) {
Connections.push(reply);
if (okCallback) {
okCallback(reply);
}
},
error: function (errMsg) {
if (koCallback) {
koCallback(errMsg);
}
}
};
return $.ajax(arg);
}
/**
* Query the Session Manager in order to retreive connection informations
* based on a session id.
*
* @member vtkWeb.launcher
*
* @param {String} sessionManagerURL
* Same as ConnectionConfig.sessionManagerURL value.
*
* @param {String} sessionId
* The unique identifier of a session.
*
* @return {vtkWeb.Connection} if the session is found.
*/
function fetchConnection(sessionManagerURL, sessionId) {
var config = {
url: sessionManagerURL + '/' + sessionId,
dataType: "json"
};
return $.ajax(config);
}
/**
* Stop a remote running visualization session.
*
* @member vtkWeb.launcher
*
* @param {vtkWeb.ConnectionConfig} connection
*/
function stop(connection) {
var config = {
url: connection.sessionManagerURL + "/" + connection.id,
type: "DELETE",
dataType: "json",
success: function (reply) {
console.log(reply);
},
error: function (errMsg) {
console.log("Error while trying to close service");
}
};
return $.ajax(config);
}
// ----------------------------------------------------------------------
// Init vtkWeb module if needed
// ----------------------------------------------------------------------
if (GLOBAL.hasOwnProperty("vtkWeb")) {
module = GLOBAL.vtkWeb || {};
} else {
GLOBAL.vtkWeb = module;
}
// ----------------------------------------------------------------------
// Export internal methods to the vtkWeb module
// ----------------------------------------------------------------------
module.start = function (config, successFunction, errorFunction) {
return start(config, successFunction, errorFunction);
};
module.stop = function (connection) {
return stop(connection);
};
module.fetchConnection = function (serviceUrl, sessionId) {
return fetchConnection(serviceUrl, sessionId);
};
/**
* Return all the session connections created in that JavaScript context.
* @member vtkWeb.launcher
* @return {vtkWeb.Connection[]}
*/
module.getConnections = function () {
return Connections;
};
// ----------------------------------------------------------------------
// Local module registration
// ----------------------------------------------------------------------
try {
// Tests for presence of jQuery, then registers this module
if ($ !== undefined) {
module.registerModule('vtkweb-launcher');
}
} catch(err) {
console.error('jQuery is missing: ' + err.message);
}
}(window, jQuery));

View File

@ -0,0 +1,109 @@
/**
* vtkWeb JavaScript Library.
*
* This module provide simpler connection paradigm for the Web client
* to connect to a remote vtkWeb session.
*
* This module registers itself as: 'vtkweb-simple'
*
* @class vtkWeb.simple
*/
(function (GLOBAL, $) {
// Connections field used to store a map of the active sessions
var module = {};
function autoConnect(connection, ready, close) {
if(connection.hasOwnProperty('sessionURL')) {
// Direct connect
vtkWeb.connect(connection, ready, close);
} else {
// Launch process
vtkWeb.start( connection, function(connection) {
if(connection.error) {
alert(connection.error);
close("error", connection.error);
} else {
// The process successfuly started
// => Just connect to the WebSocket
vtkWeb.connect(connection, ready, close);
}
}, function(msg) {
// No launcher found or error
try {
var launcherResponse = JSON.parse(msg.responseText);
close("launcher error", launcherResponse);
} catch (err) {
console.log("No launcher found. Attempting to connect using the direct WS url.");
connection.sessionURL = vtkWeb.properties.sessionURL;
vtkWeb.connect(connection, ready, close);
}
});
}
}
// ----------------------------------------------------------------------
// Init vtkWeb module if needed
// ----------------------------------------------------------------------
if (GLOBAL.hasOwnProperty("vtkWeb")) {
module = GLOBAL.vtkWeb || {};
} else {
GLOBAL.vtkWeb = module;
}
// ----------------------------------------------------------------------
// Export methods to the vtkWeb module
// ----------------------------------------------------------------------
/**
* Simple method that will automatically try to launch or connect
* to an existing session based on URL parameters and failures.
*
* @member vtkWeb.simple
* @method smartConnect
*
* @param {Object} base connection information
* @param {Function} callback when success which expect
* a full connection object as argument.
* @param {Function} callback when failure occurs with "code" and "reason"
* why the failure occured.
*/
module.smartConnect = function (connection, ready, close) {
if(close === undefined) {
close = module.errorCallback;
}
// Extend connection with URL parameters
module.udpateConnectionFromURL(connection);
// Check if we should connect to an existing session
if(connection.hasOwnProperty('session')) {
var url = connection['sessionManagerURL'] + connection['session'];
$.ajax({
url: url,
dataType: 'json'
}).done(function(realConnection){
autoConnect(realConnection, ready, close);
}).fail(function(arg){
alert("Unable to connect to the given session: " + connection['session']);
close("error", "Unable to connect to the given session: " + connection['session']);
});
} else {
autoConnect(connection, ready, close);
}
};
// ----------------------------------------------------------------------
// Local module registration
// ----------------------------------------------------------------------
try {
// Tests for presence of autobahn, then registers this module
if (module.allModulesPresent(['vtkweb-connect', 'vtkweb-launcher'])) {
module.registerModule('vtkweb-simple');
} else {
console.error('Module failed to register, vtkweb-connect and vtkweb-launcher are missing');
}
} catch(err) {
console.error('Caught exception while registering module: ' + err.message);
}
}(window, jQuery));

View File

@ -0,0 +1,347 @@
/**
* vtkWeb JavaScript Library.
*
* This testing module is made to test vtkWeb components
*
* This module registers itself as: 'vtkweb-testing'
*
* @class vtkWeb.testing
*
* @singleton
*/
(function (GLOBAL, $) {
var parentModule = {}, module = {}, testToRun = 0;
testSuite = {}, testOrder = [], outputLog = [], w = $(window),
testTotal = 0, testRunned = 0, success = 0, failure = 0,
testNameToRun = [],
testLineTemplate = '<tr id="NAME"><td><input type="checkbox" value="NAME" checked/></td><td width="100%">NAME</td><td align="center" class="status">STATUS</td></tr>',
tableHeader = '<thead><tr><th><input type="checkbox" value="select-all-test" title="Toggle all test selection" checked/></th><th width="100%">Test names</th><th>Status</th></tr>',
outputLogHTML = '<div class="row-fluid log" style="display: none;"><div class="span12"><textarea style="width: 98%;height: 150px;"></textarea></div></div></thead>',
controlHTML = '<table class="table table-striped"><thead><tr><th width="100%"></th><th><div class="run-tests vtk-icon-play"></div></th><th><div class="toggle-log vtk-icon-tools"></div></th><th class="summary" style="border-radius: 5px;">Results</th><th><span id="passedTestsSpan" class="badge badge-success">0</span></th><th><span id="failedTestsSpan" class="badge" style="background-color: red">0</span></th></tr></thead></table>',
statusSuccess = '<div class="vtk-icon-ok-circled" style="color: green;"></div>',
statusFailure = '<div class="vtk-icon-cancel-circled" style="color: red;"></div>',
statusNotRunned = '<div class="vtk-icon-help-circled"></div>';
// ----------------------------------------------------------------------
// Init vtkWeb.testing module if needed
// ----------------------------------------------------------------------
if (GLOBAL.hasOwnProperty("vtkWeb")) {
parentModule = GLOBAL.vtkWeb || {};
} else {
GLOBAL.vtkWeb = parentModule;
}
if (parentModule.hasOwnProperty("testing")) {
module = GLOBAL.vtkWeb.testing || {};
} else {
GLOBAL.vtkWeb.testing = module;
}
// ----------------------------------------------------------------------
// Internal methods
// ----------------------------------------------------------------------
function registerTest(name, func) {
testOrder.push(name);
testSuite[name] = {
'name': name,
'func': func,
'active': true,
'message': null,
'success': false,
'done': false
};
}
// ----------------------------------------------------------------------
function clearLog() {
outputLog = [];
}
// ----------------------------------------------------------------------
function logToString() {
return outputLog.join('\n');
}
// ----------------------------------------------------------------------
function clearResults() {
outputLog = [];
testTotal = 0;
testRunned = 0;
success = 0;
failure = 0;
for(var test in testSuite) {
test.success = false;
test.message = null;
test.done = false;
}
w.trigger('vtk-test-clear');
}
// ----------------------------------------------------------------------
function log(msg) {
outputLog.push(msg);
w.trigger('vtk-test-log-update');
}
// ----------------------------------------------------------------------
function resultCallback(result) {
var test = testSuite[result.name];
// Update test info
test.done = true;
test.message = result.message;
test.success = result.success;
// Less test remaining
++testRunned;
if(result.success) {
++success;
} else {
++failure;
}
// Update log
log((test.success?"+":"-") + result.name + ": " + test.message);
// Trigger event
result.count = testRunned;
result.total = testTotal;
result.coutSuccess = success;
result.countFailure = failure;
result.type = 'vtk-test-complete';
w.trigger(result);
if(testTotal === testRunned) {
w.trigger('vtk-test-done');
} else {
runAnotherTest();
}
}
// ----------------------------------------------------------------------
function testToString(testName) {
return testLineTemplate.replace(/ID/g, testName).replace(/NAME/g, testName).replace('STATUS', statusNotRunned);
}
// ----------------------------------------------------------------------
function runAnotherTest() {
var testName = testNameToRun.shift();
var testFunction = testSuite[testName].func;
testFunction(testName, resultCallback);
}
// ----------------------------------------------------------------------
// Export methods to the vtkWeb.testing module
// ----------------------------------------------------------------------
/**
* Register a set of methods as tests.
*
* @class vtkWeb.testing
* @method registerTests
* @param {Object} testSuiteToRegister
* Should contains a set of methods that will be
* executed.
*/
module.registerTests = function(testSuiteToRegister) {
for (var testName in testSuiteToRegister) {
registerTest(testName, testSuiteToRegister[testName]);
}
}
// ----------------------------------------------------------------------
/**
* Trigger the run of all the active test that have been
* previously registered.
*
* @class vtkWeb.testing
* @method runTests
*/
module.runTests = function() {
clearResults();
log("Start testings...");
// Extract active tests list
testNameToRun = [];
for(var idx in testOrder) {
if(testSuite[testOrder[idx]].active) {
testNameToRun.push(testOrder[idx]);
}
}
testTotal = testNameToRun.length;
// Start the tests running
runAnotherTest();
}
// ----------------------------------------------------------------------
/**
* Toggle test to be run.
*
* @class vtkWeb.testing
* @method enableTest
*/
module.enableTest = function(name, status) {
testSuite[name].active = status;
}
// ----------------------------------------------------------------------
/**
* Return test results summary.
*
* @class vtkWeb.testing
* @method getCurrentTestResults
*/
module.getCurrentTestResults = function() {
return {
'finished': (testTotal === testRunned),
'testCount': testTotal,
'successes': success,
'failures': failure
};
}
// ----------------------------------------------------------------------
/**
* Retrieve the test log, which contains all detailed error outputs from
* the tests.
*
* @class vtkWeb.testing
* @method getTestLog
*/
module.getTestLog = logToString;
// ----------------------------------------------------------------------
// Provide JQuery widgets
// ----------------------------------------------------------------------
/**
* Fill the selected container with test list and
* action buttons to toggle and run them.
*
* @class jquery
* @method vtkWebTestList
*/
$.fn.vtkWebTestList = function(options) {
return this.each(function() {
var me = $(this).empty(), buffer = [];
// Generate HTML
buffer.push(controlHTML);
buffer.push('<table class="table table-striped test-panel">');
buffer.push(tableHeader);
buffer.push('<tbody>');
for(var idx in testOrder) {
buffer.push(testToString(testOrder[idx]));
}
buffer.push('</tbody></table>');
buffer.push(outputLogHTML);
me[0].innerHTML = buffer.join('');
// Initialize listeners
$('input', me).change(function(){
var checkbox = $(this),
checked = checkbox.is(":checked"),
testName = checkbox.val();
if(testSuite.hasOwnProperty(testName)) {
testSuite[testName].active = checked;
} else {
$('input', me).prop('checked', checked);
for(var test in testSuite) {
testSuite[test].active = checked;
}
}
clearResults();
});
$('.toggle-log', me).click(function(){
var log = $('.log');
var results = $('.test-panel');
if(log.is(":visible")) {
log.hide();
results.show();
} else {
log.show();
results.hide();
}
});
$('.run-tests', me).click(function(){
module.runTests();
});
// Bind events to UI updates
w.bind('vtk-test-clear', function(){
updateTextArea();
updateResultSummary();
$(".status").html(statusNotRunned);
}).bind('vtk-test-complete', function(event){
updateTextArea();
updateResultSummary();
updateStatus(event.name);
});
});
};
// ----------------------------------------------------------------------
function updateTextArea() {
$('textarea')[0].innerHTML = logToString();
}
// ----------------------------------------------------------------------
function updateResultSummary() {
$('#passedTestsSpan').html(success);
$('#failedTestsSpan').html(failure);
if(testTotal !== 0 && testTotal === testRunned) {
if(success === testTotal) {
$('.summary').css('background', 'green');
} else {
$('.summary').css('background', 'red');
}
} else {
$('.summary').css('background', 'none');
}
}
// ----------------------------------------------------------------------
function updateStatus(testName) {
var test = testSuite[testName]
$("#" + testName + " .status").html(test.success ? statusSuccess : statusFailure).attr('title', test.message);
}
// ----------------------------------------------------------------------
// Local module registration
// ----------------------------------------------------------------------
try {
// Tests for presence of jQuery, then registers this module
if ($ !== undefined) {
parentModule.registerModule('vtkweb-testing');
} else {
console.error('Module failed to register, jQuery is missing.');
}
} catch(err) {
console.error('Caught exception while registering module: ' + err.message);
}
}(window, jQuery));

View File

@ -0,0 +1,346 @@
/**
* vtkWeb JavaScript Library.
*
* This module extend the vtkWeb viewport to add support for Image delivery
* mechanism for rendering.
*
* @class vtkWeb.viewports.image
*
* Viewport Factory description:
* - Key: image
* - Stats:
* - image-fps
* - image-round-trip
* - image-server-processing
*/
(function (GLOBAL, $) {
var module = {},
RENDERER_CSS = {
"position": "absolute",
"top": "0px",
"left": "0px",
"right": "0px",
"bottom": "0px",
"z-index" : "0"
},
DEFAULT_OPTIONS = {
interactiveQuality: 30,
stillQuality: 100
},
FACTORY_KEY = 'image',
FACTORY = {
'builder': createImageDeliveryRenderer,
'options': DEFAULT_OPTIONS,
'stats': {
'image-fps': {
label: 'Framerate',
type: 'time',
convert: function(value) {
if(value === 0) {
return 0;
}
return (1000 / value).toFixed(2);
}
},
'image-round-trip': {
label: 'Round&nbsp;trip&nbsp;(ms)',
type: 'value',
convert: NoOp
},
'image-server-processing': {
label: 'Processing&nbsp;Time&nbsp;(ms)',
type: 'value',
convert: NoOp
}
}
};
// ----------------------------------------------------------------------
function NoOp(a) {
return a;
}
// ----------------------------------------------------------------------
// Image Delivery renderer - factory method
// ----------------------------------------------------------------------
function createImageDeliveryRenderer(domElement) {
var container = $(domElement),
options = $.extend({}, DEFAULT_OPTIONS, container.data('config')),
bgImage = new Image(),
session = options.session,
canvas = GLOBAL.document.createElement('canvas'),
ctx2d = canvas.getContext('2d'),
renderer = $(canvas).addClass(FACTORY_KEY).css(RENDERER_CSS).append(bgImage),
force_render = false,
statistics = null,
lastMTime = 0,
render_onidle_timeout = null,
action_pending = false,
button_state = {
left : false,
right: false,
middle : false
},
quality = 100;
// ----
/// Internal method that returns true if the mouse interaction event should be
/// throttled.
function eatMouseEvent(event) {
var force_event = (button_state.left !== event.buttonLeft || button_state.right !== event.buttonRight || button_state.middle !== event.buttonMiddle);
if (!force_event && !event.buttonLeft && !event.buttonRight && !event.buttonMiddle && !event.scroll) {
return true;
}
if (!force_event && action_pending) {
return true;
}
button_state.left = event.buttonLeft;
button_state.right = event.buttonRight;
button_state.middle = event.buttonMiddle;
return false;
}
//-----
// Internal function that requests a render on idle. Calling this
// mutliple times will only result in the request being set once.
function renderOnIdle() {
if (render_onidle_timeout === null) {
render_onidle_timeout = GLOBAL.setTimeout(render, 250);
}
}
// Setup internal API
function render(fetch) {
if (force_render === false) {
if (render_onidle_timeout !== null) {
// clear any renderOnIdle requests that are pending since we
// are sending a render request.
GLOBAL.clearTimeout(render_onidle_timeout);
render_onidle_timeout = null;
}
force_render = true;
var renderCfg = {
size: [ container.innerWidth(), container.innerHeight() ],
view: Number(options.view),
mtime: fetch ? 0 : lastMTime,
quality: quality,
localTime : new Date().getTime()
};
container.trigger({
type: 'stats',
stat_id: 'image-fps',
stat_value: 0 // start
});
session.call("viewport.image.render", [renderCfg]).then(function (res) {
options.view = Number(res.global_id);
lastMTime = res.mtime;
if(res.hasOwnProperty("image") && res.image !== null) {
/**
* @member vtkWeb.Viewport
* @event start-loading
*/
$(container).parent().trigger("start-loading");
bgImage.width = res.size[0];
bgImage.height = res.size[1];
var previousSrc = bgImage.src;
bgImage.src = "data:image/" + res.format + "," + res.image;
container.trigger({
type: 'stats',
stat_id: 'image-fps',
stat_value: 1 // stop
});
container.trigger({
type: 'stats',
stat_id: 'image-round-trip',
stat_value: Number(new Date().getTime() - res.localTime) - res.workTime
});
container.trigger({
type: 'stats',
stat_id: 'image-server-processing',
stat_value: Number(res.workTime)
});
}
renderStatistics();
force_render = false;
container.trigger('done');
// the image we received is not the latest, we should
// request another render to try to get the latest image.
if (res.stale === true) {
renderOnIdle();
} else {
container.trigger({
type: 'renderer-ready'
});
}
});
}
}
// internal function to render stats.
function renderStatistics() {
if (statistics) {
ctx2d.font = "bold 12px sans-serif";
//ctx2d.fillStyle = "white";
ctx2d.fillStyle = "black";
ctx2d.fillRect(10, 10, 240, 100);
//ctx2d.fillStyle = "black";
ctx2d.fillStyle = "white";
ctx2d.fillText("Frame Rate: " + statistics.frameRate().toFixed(2), 15, 25);
ctx2d.fillText("Average Frame Rate: " + statistics.averageFrameRate().toFixed(2),
15, 40);
ctx2d.fillText("Round trip: " + statistics.roundTrip() + " ms - Max: " + statistics.maxRoundTrip() + " ms",
15, 55);
ctx2d.fillText("Server work time: " + statistics.serverWorkTime() + " ms - Max: " + statistics.maxServerWorkTime() + " ms",
15, 70);
ctx2d.fillText("Minimum Frame Rate: " + statistics.minFrameRate().toFixed(2),
15, 85);
ctx2d.fillText("Loading time: " + statistics.trueLoadTime(),
15, 100);
}
}
// Choose if rendering is happening in Canvas or image
bgImage.onload = function(){
paint();
};
// internal function used to draw update data on the canvas. When not
// using canvas, this has no effect.
function paint() {
/**
* @member vtkWeb.Viewport
* @event stop-loading
*/
$(container).parent().trigger("stop-loading");
ctx2d.canvas.width = $(container).width();
ctx2d.canvas.height = $(container).height();
ctx2d.drawImage(bgImage, 0, 0, bgImage.width, bgImage.height);
renderStatistics();
}
// Attach listener to container for mouse interaction and invalidateScene
container.bind('invalidateScene', function() {
if(renderer.hasClass('active')){
render(true);
}
}).bind('resetViewId', function(e){
options.view = -1;
}).bind('captureRenderedImage', function(e){
if(renderer.hasClass('active')){
$(container).parent().trigger({
type: 'captured-screenshot-ready',
imageData: bgImage.src
});
}
}).bind('render', function(e){
if(renderer.hasClass('active')){
var opts = e.options,
previousQuality = quality,
forceRender = false;
if(opts) {
quality = opts.hasOwnProperty('quality') ? opts.quality : quality;
options.view = opts.hasOwnProperty('view') ? opts.view : options.view;
forceRender = opts.hasOwnProperty('forceRender');
}
render(forceRender);
// Revert back to previous state
quality = previousQuality;
}
}).bind('mouse', function(evt){
if(renderer.hasClass('active')){
// stop default event handling by the browser.
evt.preventDefault();
// Update quality based on the type of the event
if(evt.action === 'up' || evt.action === 'dblclick' || evt.action === 'scroll') {
quality = options.stillQuality;
} else {
quality = options.interactiveQuality;
}
var vtkWeb_event = {
view: Number(options.view),
action: evt.action,
charCode: evt.charCode,
altKey: evt.altKey,
ctrlKey: evt.ctrlKey,
shiftKey: evt.shiftKey,
metaKey: evt.metaKey,
buttonLeft: (evt.current_button === 1 ? true : false),
buttonMiddle: (evt.current_button === 2 ? true : false),
buttonRight: (evt.current_button === 3 ? true : false)
},
elem_position = $(evt.delegateTarget).offset(),
pointer = {
x : (evt.pageX - elem_position.left),
y : (evt.pageY - elem_position.top)
};
if(evt.action === 'scroll') {
vtkWeb_event.scroll = evt.scroll;
} else {
vtkWeb_event.x = pointer.x / renderer.width();
vtkWeb_event.y = 1.0 - (pointer.y / renderer.height());
}
if (eatMouseEvent(vtkWeb_event)) {
return;
}
action_pending = true;
session.call("viewport.mouse.interaction", [vtkWeb_event]).then(function (res) {
if (res) {
action_pending = false;
render();
}
}, function(error) {
console.log("Call to viewport.mouse.interaction failed");
console.log(error);
});
}
}).append(renderer);
}
// ----------------------------------------------------------------------
// Init vtkWeb module if needed
// ----------------------------------------------------------------------
if (GLOBAL.hasOwnProperty("vtkWeb")) {
module = GLOBAL.vtkWeb || {};
} else {
GLOBAL.vtkWeb = module;
}
// ----------------------------------------------------------------------
// Extend the viewport factory
// ----------------------------------------------------------------------
if(!module.hasOwnProperty('ViewportFactory')) {
module['ViewportFactory'] = {};
}
module.ViewportFactory[FACTORY_KEY] = FACTORY;
// ----------------------------------------------------------------------
// Local module registration
// ----------------------------------------------------------------------
try {
// Tests for presence of jQuery, then registers this module
if ($ !== undefined) {
module.registerModule('vtkweb-viewport-image');
}
} catch(err) {
console.error('jQuery is missing: ' + err.message);
}
}(window, jQuery));

View File

@ -0,0 +1,892 @@
/**
* vtkWeb JavaScript Library.
*
* This module allow the Web client to create viewport to vtkWeb views.
* Those viewport are interactive windows that are used to render 2D/3D content
* and response to user mouse interactions.
*
* This module registers itself as: 'vtkweb-viewport'
*
* @class vtkWeb.Viewport
*/
(function (GLOBAL, $) {
// ----------------------------------------------------------------------
// Viewport constants
// ----------------------------------------------------------------------
var DEFAULT_RENDERERS_CONTAINER_HTML = "<div class='renderers'></div>",
DEFAULT_RENDERERS_CONTAINER_CSS = {
"position": "absolute",
"top": "0px",
"left": "0px",
"right": "0px",
"bottom": "0px",
"z-index" : "0",
"overflow": "hidden"
},
DEFAULT_MOUSE_LISTENER_HTML = "<div class='mouse-listener'></div>",
DEFAULT_MOUSE_LISTENER_CSS = {
"position": "absolute",
"top": "0px",
"left": "0px",
"right": "0px",
"bottom": "0px",
"z-index" : "3"
},
DEFAULT_STATISTIC_HTML = "<div class='statistics'></div>",
DEFAULT_STATISTIC_CSS = {
"position": "absolute",
"top": "0px",
"left": "0px",
"right": "0px",
"bottom": "0px",
"z-index" : "2",
"display" : "none"
},
DEFAULT_OVERLAY_HTML = "<canvas class='overlay'></canvas>",
DEFAULT_OVERLAY_CSS = {
"position": "absolute",
"top": "0px",
"left": "0px",
"right": "0px",
"bottom": "0px",
"z-index" : "1"
},
module = {},
DEFAULT_VIEWPORT_OPTIONS = {
session: null,
view: -1,
enableInteractions: true,
renderer: 'image'
};
// ----------------------------------------------------------------------
// Mouse interaction helper methods for viewport
// ----------------------------------------------------------------------
function preventDefault(event) {
event.preventDefault();
}
// ----------------------------------------------------------------------
function attachMouseListener(mouseListenerContainer, renderersContainer, overlayContainer, viewport) {
var current_button = null,
area = [0,0,0,0],
vp = viewport,
overlayCtx2D = overlayContainer[0].getContext('2d');
// Draw rectangle in overlay
function clearOverlay() {
if(overlayCtx2D !== null) {
overlayCtx2D.canvas.width = mouseListenerContainer.width();
overlayCtx2D.canvas.height = mouseListenerContainer.height();
overlayCtx2D.clearRect(0,0,overlayCtx2D.canvas.width,overlayCtx2D.canvas.height);
}
}
function redrawSelection() {
clearOverlay();
if(overlayCtx2D !== null) {
overlayCtx2D.strokeStyle="#FFFFFF";
var x1 = Math.min(area[0],area[2]);
var y1 = Math.min(area[1],area[3]);
var x2 = Math.max(area[0],area[2]);
var y2 = Math.max(area[1],area[3]);
var width = Math.abs(x2 - x1);
var height = Math.abs(y2 - y1);
overlayCtx2D.rect(x1, overlayContainer.height()-y2, width, height);
overlayCtx2D.stroke();
}
}
function extractCoordinates(event, start, reorder) {
var elem_position = $(event.delegateTarget).offset(),
height = $(event.delegateTarget).height(),
x = (event.pageX - elem_position.left),
y = (event.pageY - elem_position.top),
offset = start ? 0 : 2;
area[0 + offset] = x;
area[1 + offset] = height - y;
if(reorder) {
// Re-order area
var newArea = [
Math.min(area[0], area[2]),
Math.min(area[1], area[3]),
Math.max(area[0], area[2]),
Math.max(area[1], area[3])
];
area[0] = newArea[0];
area[1] = newArea[1];
area[2] = newArea[2];
area[3] = newArea[3];
}
}
// Internal method used to pre-process the interaction to standardise it
// for a vtkWeb usage.
function mouseInteraction(event) {
if(event.hasOwnProperty("type")) {
if(event.type === 'mouseup') {
current_button = null;
if (vp.getSelectionMode() === true) {
clearOverlay();
}
renderersContainer.trigger($.extend(event, {
type: 'mouse',
action: 'up',
current_button: current_button
}));
extractCoordinates(event, false, true);
renderersContainer.trigger({
type: 'endInteraction',
area: area
});
} else if(event.type === 'mousedown') {
current_button = event.which;
// Override button if modifier is used
// Middle: Alt - Right: Shift
if(event.shiftKey) {
current_button = 3;
event.shiftKey = false;
} else if(event.altKey) {
current_button = 2;
event.altKey = false;
}
extractCoordinates(event, true, false);
renderersContainer.trigger('startInteraction');
renderersContainer.trigger($.extend(event, {
type: 'mouse',
action: 'down',
current_button: current_button
}));
} else if(event.type === 'mousemove' && current_button != null) {
if (vp.getSelectionMode() === true) {
extractCoordinates(event, false, false);
redrawSelection();
}
renderersContainer.trigger($.extend(event, {
type: 'mouse',
action: 'move',
current_button: current_button
}));
}
}
}
// Bind listener to UI container
mouseListenerContainer.bind("contextmenu mouseover click", preventDefault);
mouseListenerContainer.bind('mousedown mouseup mousemove', mouseInteraction);
mouseListenerContainer.dblclick(function(event){
renderersContainer.trigger($.extend(event, {
type: 'mouse',
action: 'dblclick',
current_button: event.which
}));
});
mouseListenerContainer.bind("DOMMouseScroll mousewheel",function(event){
var scrollValue = (event.originalEvent.wheelDeltaY || -event.originalEvent.detail);
renderersContainer.trigger($.extend(event, {
type: 'mouse',
action: 'scroll',
current_button: current_button,
scroll: scrollValue
}));
});
}
// ----------------------------------------------------------------------
function attachTouchListener(mouseListenerContainer, renderersContainer, viewport) {
var current_button = null, posX, posY, defaultDragButton = 1,
isZooming = false, isDragging = false, mouseAction = 'up', target;
function mobileTouchInteraction(evt) {
evt.gesture.preventDefault();
switch(evt.type) {
case 'drag':
if(isZooming) {
return;
}
current_button = defaultDragButton;
if(mouseAction === 'up') {
mouseAction = "down";
target = evt.gesture.target;
isDragging = true;
} else {
mouseAction = "move";
}
posX = evt.gesture.touches[0].pageX;
posY = evt.gesture.touches[0].pageY;
break;
case 'hold':
if(defaultDragButton === 1) {
defaultDragButton = 2;
mouseListenerContainer.html("Pan mode").css('color','#FFFFFF');
} else {
defaultDragButton = 1;
mouseListenerContainer.html("Rotation mode").css('color','#FFFFFF');
}
break;
case 'release':
mouseListenerContainer.html('');
current_button = 0;
mouseAction = "up";
isZooming = false;
isDragging = false;
break;
case 'doubletap':
viewport.resetCamera();
return;
case 'pinch':
if(isDragging) {
return;
}
current_button = 3;
if(mouseAction === 'up') {
mouseAction = 'down';
posX = 0;
posY = mouseListenerContainer.height();
target = evt.gesture.target;
isZooming = true;
} else {
mouseAction = 'move';
posY = mouseListenerContainer.height() * (1+(evt.gesture.scale-1)/2);
}
break;
}
//mouseListenerContainer.html(mouseAction + ' (' + posX + ', ' + posY + ') b:' + current_button + ' z: ' + isZooming ).css('color','#FFFFFF');
// Trigger event
renderersContainer.trigger({
type: 'mouse',
action: mouseAction,
current_button: current_button,
charCode: '',
altKey: false,
ctrlKey: false,
shiftKey: false,
metaKey: false,
delegateTarget: target,
pageX: posX,
pageY: posY
});
}
// Bind listener to UI container
mouseListenerContainer.hammer({
prevent_default : true,
prevent_mouseevents : true,
transform : true,
transform_always_block : true,
transform_min_scale : 0.03,
transform_min_rotation : 2,
drag : true,
drag_max_touches : 1,
drag_min_distance : 10,
swipe : false,
hold : true // To switch from rotation to pan
}).on("doubletap pinch drag release hold", mobileTouchInteraction);
}
// ----------------------------------------------------------------------
// Viewport statistic manager
// ----------------------------------------------------------------------
function createStatisticManager() {
var statistics = {}, formatters = {};
// Fill stat formatters
for(var factoryKey in vtkWeb.ViewportFactory) {
var factory = vtkWeb.ViewportFactory[factoryKey];
if(factory.hasOwnProperty('stats')) {
for(var key in factory.stats) {
formatters[key] = factory.stats[key];
}
}
}
function handleEvent(event) {
var id = event.stat_id,
value = event.stat_value,
statObject = null;
if(!statistics.hasOwnProperty(id) && formatters.hasOwnProperty(id)) {
if(formatters[id].type === 'time') {
statObject = statistics[id] = createTimeValueRecord();
} else if (formatters[id].type === 'value') {
statObject = statistics[id] = createValueRecord();
}
} else {
statObject = statistics[id];
}
if(statObject != null) {
statObject.record(value);
}
}
// ------------------------------------------------------------------
function toHTML() {
var buffer = createBuffer(), hasContent = false, key, formater, stat,
min, max;
// Extract stat data
buffer.append("<table class='viewport-stat'>");
buffer.append("<tr class='stat-header'><td class='label'>Timed Quantity</td><td class='value'>Current</td><td class='min'>Min</td><td class='max'>Max</td><td class='avg'>Average</td></tr>");
for(key in statistics) {
if(formatters.hasOwnProperty(key) && statistics[key].valid) {
formater = formatters[key];
stat = statistics[key];
hasContent = true;
// The functiion may swap the order
min = formater.convert(stat.min);
max = formater.convert(stat.max);
buffer.append("<tr><td class='label'>");
buffer.append(formater.label);
buffer.append("</td><td class='value'>");
buffer.append(formater.convert(stat.value));
buffer.append("</td><td class='min'>");
buffer.append((min < max) ? min : max);
buffer.append("</td><td class='max'>");
buffer.append((min > max) ? min : max);
buffer.append("</td><td class='avg'>");
buffer.append(formater.convert(stat.getAverageValue()));
buffer.append("</td></tr>");
}
}
buffer.append("</table>");
return hasContent ? buffer.toString() : "";
}
// ------------------------------------------------------------------
return {
eventHandler: handleEvent,
toHTML: toHTML,
reset: function() {
statistics = {};
}
}
}
// ----------------------------------------------------------------------
function createBuffer() {
var idx = -1, buffer = [];
return {
clear: function(){
idx = -1;
buffer = [];
},
append: function(str) {
buffer[++idx] = str;
return this;
},
toString: function() {
return buffer.join('');
}
};
}
// ----------------------------------------------------------------------
function createTimeValueRecord() {
var lastTime, sum, count;
// Default values
lastTime = 0;
sum = 0;
count = 0;
return {
value: 0.0,
valid: false,
min: +1000000000.0,
max: -1000000000.0,
record: function(v) {
if(v === 0) {
this.start();
} else if (v === 1) {
this.stop();
}
},
start: function() {
lastTime = new Date().getTime();
},
stop: function() {
if(lastTime != 0) {
this.valid = true;
var time = new Date().getTime();
this.value = time - lastTime;
this.min = (this.min < this.value) ? this.min : this.value;
this.max = (this.max > this.value) ? this.max : this.value;
//
sum += this.value;
count++;
}
},
reset: function() {
count = 0;
sum = 0;
lastTime = 0;
this.value = 0;
this.min = +1000000000.0;
this.max = -1000000000.0;
this.valid = false;
},
getAverageValue: function() {
if(count == 0) {
return 0;
}
return Math.floor(sum / count);
}
}
}
// ----------------------------------------------------------------------
function createValueRecord() {
var sum = 0, count = 0;
return {
value: 0.0,
valid: false,
min: +1000000000.0,
max: -1000000000.0,
record: function(v) {
this.valid = true;
this.value = v;
this.min = (this.min < this.value) ? this.min : this.value;
this.max = (this.max > this.value) ? this.max : this.value;
//
sum += this.value;
count++;
},
reset: function() {
count = 0;
sum = 0;
this.value = 0;
this.min = +1000000000.0;
this.max = -1000000000.0;
this.valid = false;
},
getAverageValue: function() {
if(count === 0) {
return 0;
}
return Math.floor(sum / count);
}
}
}
// ----------------------------------------------------------------------
// Viewport container definition
// ----------------------------------------------------------------------
/**
* Create a new viewport for a vtkWeb View.
* The options are explained below.
*
* @member vtkWeb.Viewport
* @param {Object} options
* Configure the viewport to create the way we want.
*
* options = {
* session: sessionObject, // Object used to communicate with the remote server.
* view: -1, // -1 for active view or use the proper viewId
* enableInteractions: true, // True if mouse interaction should be forwarded to the server
* renderer: 'image' // Type of renderer to be used. Can only be 'image' 'webgl' or 'vgl'.
* // --- image renderer options
* interactiveQuality: 30, // StillRender quality when interacting
* stillQuality: 100, // StillRender quality when not interacting
* // --- webgl/vgl renderer options
* keepServerInSynch: false. // Push camera information to server if true
* }
*
* @return {vtkWeb.Viewport}
*/
function createViewport(options) {
// Make sure we have a valid autobahn session
if (options.session === null) {
throw "'session' must be provided within the option.";
}
function onReadyInternal() {
if(userReadyCallback.fn) {
// Providing just a short delay before triggering the "image-not-state"
// event provides a break in the action so that the browser can draw the
// image we just copied to the src attribute.
setTimeout(userReadyCallback.fn, 0);
if(userReadyCallback.once) {
userReadyCallback.fn = null;
}
}
}
// Create viewport
var config = $.extend({}, DEFAULT_VIEWPORT_OPTIONS, options),
session = options.session,
rendererContainer = $(DEFAULT_RENDERERS_CONTAINER_HTML).css(DEFAULT_RENDERERS_CONTAINER_CSS),
mouseListener = $(DEFAULT_MOUSE_LISTENER_HTML).css(DEFAULT_MOUSE_LISTENER_CSS),
statContainer = $(DEFAULT_STATISTIC_HTML).css(DEFAULT_STATISTIC_CSS),
overlayContainer = $(DEFAULT_OVERLAY_HTML).css(DEFAULT_OVERLAY_CSS),
onDoneQueue = [],
statisticManager = createStatisticManager(),
inSelectionMode = false,
userReadyCallback = { fn: null, once: false},
viewport = {
/**
* Update the active renderer to be something else.
* This allow the user to switch from Image Delivery to Geometry delivery
* or even any other available renderer type available.
*
* The available renderers are indexed inside the following object vtkWeb.ViewportFactory.
*
* @member vtkWeb.Viewport
* @param {String} rendererName
* Key used to ID the renderer type.
*/
setActiveRenderer: function(rendererName) {
$('.' + rendererName, rendererContainer).addClass('active').show().siblings().removeClass('active').hide();
rendererContainer.trigger('active');
statContainer[0].innerHTML = '';
statisticManager.reset();
},
/**
* Method that should be called each time something in the scene as changed
* and we want to update the viewport to reflect the latest state of the scene.
*
* @member vtkWeb.Viewport
* @param {Function} ondone Function to call after rendering is complete.
*/
invalidateScene: function(onDone) {
onDoneQueue.push(onDone);
rendererContainer.trigger('invalidateScene');
},
/**
* Method that should be called when nothing has changed in the scene
* but for some reason the viewport has been dirty.
* (i.e. Toggeling the statistic information within the viewport)
*
* @member vtkWeb.Viewport
* @param {Function} ondone Function to call after rendering is complete.
*/
render: function(onDone, args) {
onDoneQueue.push(onDone);
rendererContainer.trigger({type: 'render', options: args});
},
/**
* Reset the camera of the scene to make it fit in the screen as well
* as invalidating the scene automatically.
*
* @member vtkWeb.Viewport
* @param {Function} ondone Function to call after rendering is complete.
*/
resetCamera: function(onDone) {
onDoneQueue.push(onDone);
return session.call("viewport.camera.reset", [Number(config.view)]).then(function () {
rendererContainer.trigger('invalidateScene');
});
},
/**
* Update Orientation Axes Visibility for the given view
*
* @member vtkWeb.Viewport
* @param {Boolean} show
* Show: true / Hide: false
* @param {Function} ondone Function to call after rendering is complete.
*/
updateOrientationAxesVisibility: function (show, onDone) {
return session.call("viewport.axes.orientation.visibility.update", [Number(config.view), show]).then(function () {
onDoneQueue.push(onDone);
rendererContainer.trigger('invalidateScene');
});
},
/**
* Update the Center Axes Visibility for the given view
*
* @member vtkWeb.Viewport
* @param {Boolean} show
* Show: true / Hide: false
* @param {Function} ondone Function to call after rendering is complete.
*/
updateCenterAxesVisibility: function (show, onDone) {
return session.call("viewport.axes.center.visibility.update", [Number(config.view), show]).then(function () {
onDoneQueue.push(onDone);
rendererContainer.trigger('invalidateScene');
});
},
/**
* Reset view id.
* This allow to invalidate the viewport and use the new active view
*
* @member vtkWeb.Viewport
*/
resetViewId: function () {
rendererContainer.trigger('resetViewId');
},
/**
* Ask the concrete renderer to generate an image and trigger its own
* event when the image is ready.
*
* @member vtkWeb.Viewport
*/
captureScreenImage: function () {
rendererContainer.trigger('captureRenderedImage');
},
/**
*
*/
downloadTimestepData: function(shaList) {
rendererContainer.trigger('downloadAllTimesteps');
},
/*
*
*/
clearGeometryCache: function() {
rendererContainer.trigger('clearCache');
},
/*
* Provide a function that will be called when
*/
onReady: function(readyCallback, once) {
userReadyCallback.fn = readyCallback;
userReadyCallback.once = once;
},
/**
* Attach viewport to a DOM element
*
* @member vtkWeb.Viewport
* @param {String} selector
* The will be used internally to get the jQuery associated element
*
* <div class="renderer"></div>
* viewport.bind(".renderer");
*
* <div id="renderer"></div>
* viewport.bind("#renderer");
*
* <html>
* <body>
* <!-- renderer -->
* <div></div>
* </body>
* </html>
* viewport.bind("body > div");
*/
bind: function (selector) {
var container = $(selector);
if (container.attr("__vtkWeb_viewport__") !== "true") {
container.attr("__vtkWeb_viewport__", "true");
container.append(rendererContainer).append(mouseListener).append(statContainer).append(overlayContainer);
rendererContainer.trigger('invalidateScene');
}
},
/**
* Remove viewport from DOM element
*
* @member vtkWeb.Viewport
*/
unbind: function () {
var parentElement = rendererContainer.parent();
if (parentElement) {
parentElement.attr("__vtkWeb_viewport__", "false");
rendererContainer.remove();
mouseListener.remove();
statContainer.remove();
overlayContainer.remove();
}
},
/**
* Update statistic visibility
*
* @member vtkWeb.Viewport
* @param {Boolean} visible
*/
showStatistics: function(isVisible) {
if(isVisible) {
statContainer.show();
} else {
statContainer.hide();
}
},
/**
* Clear current statistic values
*
* @member vtkWeb.Viewport
*/
resetStatistics: function() {
statisticManager.reset();
statContainer.empty();
},
/**
* Event triggered before a mouse down.
*
* @member vtkWeb.Viewport
* @event startInteraction
*/
/**
* Event triggered after a mouse up.
*
* @member vtkWeb.Viewport
* @event endInteraction
* @param area
* Provide the area in pixel between the startInteraction and endInteraction.
* This can be used for selection or zoom to box... [minX, minY, maxX, maxY]
*/
/**
* Toggle selection mode.
*
* When selection mode is active, the next interaction will
* create a 2D rectangle that will be used to define an area to select.
* This won't perform any selection but will provide another interaction
* mode on the client side which will then trigger an event with
* the given area. It is the responsibility of the user to properly
* handle that event and eventually trigger the appropriate server
* code to perform a selection or a zoom-to-box type of action.
*
* @member vtkWeb.Viewport
* @return {Boolean} inSelectionMode state
*/
toggleSelectionMode: function() {
inSelectionMode = !inSelectionMode;
return inSelectionMode;
},
/**
* Return whether or not viewport is in selection mode.
*
* @return {Boolean} true if viewport is in selection mode, false otherwise.
*/
getSelectionMode: function() {
return inSelectionMode;
}
};
// Attach config object to renderer parent
rendererContainer.data('config', config);
// Attach onDone listener
rendererContainer.bind('done', function(){
while(onDoneQueue.length > 0) {
var callback = onDoneQueue.pop();
try {
if(callback) {
callback();
}
} catch(error) {
console.log("On Done callback error:");
console.log(error);
}
}
});
// Attach the on ready internal
rendererContainer.bind('renderer-ready', onReadyInternal);
// Create any renderer type that is available
for(var key in vtkWeb.ViewportFactory) {
try {
vtkWeb.ViewportFactory[key].builder(rendererContainer);
} catch(error) {
console.log("Error while trying to load renderer: " + key);
console.log(error);
}
}
// Set default renderer
viewport.setActiveRenderer(config.renderer);
// Attach mouse listener if requested
if (config.enableInteractions) {
attachMouseListener(mouseListener, rendererContainer, overlayContainer, viewport);
try {
attachTouchListener(mouseListener, rendererContainer, viewport);
} catch(error) {
console.log('Hammer is not properly initialized');
console.log(error);
}
}
// Attach stat listener
rendererContainer.bind('stats', function(event){
statisticManager.eventHandler(event);
statContainer[0].innerHTML = statisticManager.toHTML();
});
return viewport;
}
// ----------------------------------------------------------------------
// Init vtkWeb module if needed
// ----------------------------------------------------------------------
if (GLOBAL.hasOwnProperty("vtkWeb")) {
module = GLOBAL.vtkWeb || {};
} else {
GLOBAL.vtkWeb = module;
}
// ----------------------------------------------------------------------
// Export internal methods to the vtkWeb module
// ----------------------------------------------------------------------
module.createViewport = function (option) {
return createViewport(option);
};
// ----------------------------------------------------------------------
// Local module registration
// ----------------------------------------------------------------------
try {
// Tests for presence of jQuery, then registers this module
if ($ !== undefined) {
module.registerModule('vtkweb-viewport');
} else {
console.error('Module failed to register, jQuery is missing');
}
} catch(err) {
console.error('Caught exception while registering module: ' + err.message);
}
}(window, jQuery));

View File

@ -0,0 +1,625 @@
/**
* vtkWeb JavaScript Library.
*
* This module extend the vtkWeb viewport to add support for WebGL rendering.
*
* @class vtkWeb.viewports.vgl
*
* Viewport Factory description:
* - Key: vgl
* - Stats:
* - vgl-fps
* - vgl-nb-objects
* - vgl-fetch-scene
* - vgl-fetch-object
*/
(function (GLOBAL, $) {
var module = {},
RENDERER_CSS = {
"position": "absolute",
"top" : "0px",
"left" : "0px",
"right" : "0px",
"bottom" : "0px",
"z-index" : "0"
},
RENDERER_CSS_2D = {
"z-index" : "1"
},
RENDERER_CSS_3D = {
"z-index" : "0"
},
DEFAULT_OPTIONS = {
keepServerInSynch: false
},
FACTORY_KEY = 'vgl',
FACTORY = {
'builder': createVGLGeometryDeliveryRenderer,
'options': DEFAULT_OPTIONS,
'stats': {
'vgl-fps': {
label: 'Framerate',
type: 'time',
convert: function(value) {
if(value === 0) {
return 0;
}
return (1000 / value).toFixed(2);
}
},
'vgl-nb-objects': {
label: 'Number&nbsp;of&nbsp;3D&nbsp;objects',
type: 'value',
convert: NoOp
},
'vgl-fetch-scene': {
label: 'Fetch&nbsp;scene&nbsp;(ms)',
type: 'time',
convert: NoOp
},
'vgl-fetch-object': {
label: 'Fetch&nbsp;object&nbsp;(ms)',
type: 'time',
convert: NoOp
}
}
},
DEFAULT_SHADERS = {},
mvMatrixStack = [],
PROGRESS_BAR_TEMPLATE =
'<div class="download-progressbar-container">' +
' <span class="progressbar-title">Download Progress</span>' +
' <div class="progressbar-content-container">' +
' <span class="progress-message-span">MESSAGE</span>' +
' <div class="progress progress-meter-container">' +
' <div class="progress-bar progress-bar-striped active progress-meter" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%;">' +
' </div>' +
' </div>' +
' </div>' +
'</div>';
// ----------------------------------------------------------------------
function NoOp(a) {
return a;
}
function getKey(object) {
return object.id + '_' + object.md5 + '_' + object.part;
}
// ----------------------------------------------------------------------
// Geometry Delivery renderer - factory method
// ----------------------------------------------------------------------
function createVGLGeometryDeliveryRenderer(domElement) {
var m_container = $(domElement),
m_options = $.extend({}, DEFAULT_OPTIONS, m_container.data('config')),
m_session = m_options.session,
m_divContainer = GLOBAL.document.createElement('div'),
m_canvas2D = GLOBAL.document.createElement('canvas'),
m_canvas3D = GLOBAL.document.createElement('canvas'),
m_ctx2d = m_canvas2D.getContext('2d'),
gl = m_canvas3D.getContext("webgl") || m_canvas3D.getContext("experimental-webgl"),
m_rendererAttrs = $(m_divContainer).addClass(FACTORY_KEY).css(RENDERER_CSS).append($(m_canvas2D).css(RENDERER_CSS).css(RENDERER_CSS_2D)).append($(m_canvas3D).css(RENDERER_CSS).css(RENDERER_CSS_3D)),
m_sceneJSON = null,
m_sceneData = null,
m_vglVtkReader = vgl.vtkReader(),
m_viewer = null,
m_interactorStyle,
originalMouseDown = document.onmousedown,
originalMouseUp = document.onmouseup,
originalMouseMove = document.onmousemove,
originalContextMenu = document.oncontextmenu,
m_background = null;
screenImage = null,
m_vglActors = {},
m_objectIndex = {},
m_numberOfPartsDownloaded = 0,
m_numberOfPartsToDownload = 0;
// Helper functions -------------------------------------------------
function fetchMissingObjects(fetchMethod, sceneJSON) {
for(var idx in sceneJSON.Objects) {
var currentObject = sceneJSON.Objects[idx];
for(var part = 1; part <= currentObject.parts; part++) {
var key = getKey({
'id': currentObject.id,
'md5': currentObject.md5,
'part': part
});
if(!m_objectIndex.hasOwnProperty(key)) {
fetchMethod(currentObject, part);
} else {
handleCompleteSceneObject(m_objectIndex[key]);
}
}
}
}
// ------------------------------------------------------------------
function fetchScene() {
m_container.trigger({
type: 'stats',
stat_id: 'vgl-fetch-scene',
stat_value: 0
});
m_session.call("viewport.webgl.metadata", [Number(m_options.view)]).then(function(data) {
if (m_sceneData === data) {
updateScene();
return;
}
m_sceneData = data;
m_sceneJSON = JSON.parse(data);
m_vglVtkReader.setVtkScene(m_sceneJSON);
m_container.trigger({
type: 'stats',
stat_id: 'vgl-fetch-scene',
stat_value: 1
});
updateScene();
});
}
// ------------------------------------------------------------------
function handleDownloadProgressEvent(event) {
var status = event.status;
function updateProgressBar(element, percentProgress) {
var progressString = percentProgress + '%';
element.attr('aria-valuenow', percentProgress)
.css('width', progressString);
//.html(progressString);
}
if (status === 'create') {
var initialMessage = "Downloading metadata for all timesteps",
html = PROGRESS_BAR_TEMPLATE.replace(/MESSAGE/, initialMessage),
progressElt = $(html);
m_container.append(progressElt);
} else if (status === 'update') {
var progressType = event.progressType,
pbElt = $('.progress-meter');
if (progressType === 'retrieved-metadata') {
$('.progress-message-span').text('Metadata retrieved, downloading objects');
pbElt.removeClass('progress-bar-striped active');
updateProgressBar(pbElt, 0);
} else {
var numPartsThisSha = event.numParts;
m_numberOfPartsDownloaded += numPartsThisSha;
var numberRemaining = m_numberOfPartsToDownload - m_numberOfPartsDownloaded;
var percent = ((m_numberOfPartsDownloaded / m_numberOfPartsToDownload) * 100).toFixed(0);
if (numberRemaining <= 0) {
$('.download-progressbar-container').remove();
} else {
$('.progress-message-span').text('Downloading objects');
updateProgressBar(pbElt, percent);
}
}
}
}
// ------------------------------------------------------------------
function downloadAllTimesteps() {
m_container.trigger({
type: 'downloadProgress',
status: 'create'
});
m_session.call('viewport.webgl.metadata.alltimesteps', []).then(function(result){
if (result.hasOwnProperty('success') && result.success === true) {
var metaDataList = result.metaDataList;
m_container.trigger({
type: 'downloadProgress',
status: 'update',
progressType: 'retrieved-metadata'
});
// For progress events, I want to first know how many items to retrieve
m_numberOfPartsToDownload = 0;
for (var sha in metaDataList) {
if (metaDataList.hasOwnProperty(sha)) {
m_numberOfPartsToDownload += metaDataList[sha].numParts;
}
}
m_numberOfPartsDownloaded = 0;
setTimeout(function() {
// Now go through and download the heavy data for anythin we don't already have
for (var sha in metaDataList) {
if (metaDataList.hasOwnProperty(sha)) {
var numParts = metaDataList[sha].numParts,
objId = metaDataList[sha].id,
alreadyCached = true;
// Before I go and fetch all the parts for this object, make sure
// I don't already have them cached
for (var i = 0; i < numParts; i+=1) {
var key = getKey({
'id': objId,
'md5': sha,
'part': i + 1
});
if(!m_objectIndex.hasOwnProperty(key)) {
alreadyCached = false;
break;
}
}
if (alreadyCached === false) {
fetchCachedObject(sha);
} else {
m_container.trigger({
type: 'downloadProgress',
status: 'update',
numParts: numParts
});
}
}
}
}, 500);
}
}, function(metaDataError) {
console.log("Error retrieving metadata for all timesteps");
console.log(metaDataError);
});
}
// ------------------------------------------------------------------
function fetchCachedObject(sha) {
var viewId = Number(m_options.view);
m_session.call('viewport.webgl.cached.data', [sha]).then(function(result) {
if (result.success === false) {
console.log("Fetching cached data for " + sha + " failed, reason:");
consolelog(result.reason);
return;
}
var dataObject = result.data;
if (dataObject.hasOwnProperty('partsList')) {
for (var dIdx = 0; dIdx < dataObject.partsList.length; dIdx += 1) {
// Create a complete scene part object and cache it
var newObject = {
md5: dataObject.md5,
part: dIdx + 1,
vid: viewId,
id: dataObject.id,
data: dataObject.partsList[dIdx],
hasTransparency: dataObject.transparency,
layer: dataObject.layer
};
var key = getKey(newObject);
m_objectIndex[key] = newObject;
var actors = m_vglVtkReader.parseObject(newObject);
m_vglActors[key] = actors;
}
m_container.trigger({
type: 'downloadProgress',
status: 'update',
numParts: dataObject.partsList.length
});
}
}, function(err) {
console.log('viewport.webgl.cached.data rpc method failed');
console.log(err);
});
}
// ------------------------------------------------------------------
function handleCompleteSceneObject(sceneObject) {
var renderer = m_vglVtkReader.getRenderer(sceneObject.layer),
key = getKey(sceneObject),
actors = {};
// Parse the new object if its not parsed already
// if parsed already then check if exists in current renderer
if (key in m_vglActors) {
actors = m_vglActors[key];
// if exists in current renderer do nothing
for (i = 0; i < actors.length; i++) {
var actor = actors[i];
if (!renderer.hasActor(actor)) {
renderer.addActor(actor);
}
}
} else {
// Object was not parsed so parse it, create actors, and add them to the renderer.
actors = m_vglVtkReader.parseObject(sceneObject);
m_vglActors[key] = actors;
for (i = 0; i < actors.length; i++) {
renderer.addActor(actors[i]);
}
}
// Mark the actor as valid
actors.invalid = false;
}
// ------------------------------------------------------------------
function fetchObject(sceneObject, part) {
try {
var viewId = Number(m_options.view),
newObject, renderer, actor, actors, key, i;
m_container.trigger({
type: 'stats',
stat_id: 'vgl-fetch-object',
stat_value: 0
});
m_session.call("viewport.webgl.data", [viewId, sceneObject.id, part]).then(function(data) {
try {
m_container.trigger({
type: 'stats',
stat_id: 'vgl-fetch-object',
stat_value: 1
});
//add object to the reader
newObject = {
md5: sceneObject.md5,
part: part,
vid: viewId,
id: sceneObject.id,
data: data,
hasTransparency: sceneObject.transparency,
layer: sceneObject.layer
};
// First, actually cache the object, because that was not happening
var key = getKey(newObject);
m_objectIndex[key] = newObject;
// Now add the object to the reader, etc...
handleCompleteSceneObject(newObject);
// Redraw the scene
drawScene(false);
} catch(error) {
console.log(error);
}
});
} catch(error) {
console.log(error);
}
}
// ------------------------------------------------------------------
function render(saveScreenOnRender) {
m_canvas3D.width = m_rendererAttrs.width();
m_canvas3D.height = m_rendererAttrs.height();
m_viewer = m_vglVtkReader.updateCanvas(m_canvas3D);
drawScene(saveScreenOnRender);
}
// ------------------------------------------------------------------
function drawScene(saveScreenOnRender) {
var layer;
try {
if (m_sceneJSON === null || typeof m_sceneJSON === 'undefined') {
return;
}
var width = m_rendererAttrs.width(),
height = m_rendererAttrs.height(),
nbObjects;
// Update frame rate
m_container.trigger({
type: 'stats',
stat_id: 'vgl-fps',
stat_value: 0
});
m_viewer.render();
if (saveScreenOnRender === true) {
screenImage = m_canvas3D.toDataURL();
}
numObjects = m_vglVtkReader.numObjects();
// Update frame rate
m_container.trigger({
type: 'stats',
stat_id: 'vgl-fps',
stat_value: 1
});
m_container.trigger({
type: 'stats',
stat_id: 'vgl-nb-objects',
stat_value: numObjects
});
} catch(error) {
console.log(error);
}
m_container.trigger('done');
}
// ------------------------------------------------------------------
function pushCameraState() {
if(m_viewer !== null) {
var cam = m_viewer.renderWindow().activeRenderer().camera(),
fp_ = cam.focalPoint(),
up_ = cam.viewUpDirection(),
pos_ = cam.position(),
fp = [fp_[0], fp_[1], fp_[2]],
up = [up_[0], up_[1], up_[2]],
pos = [pos_[0], pos_[1], pos_[2]];
m_session.call("viewport.camera.update", [Number(m_options.view), fp, up, pos]);
}
}
// ------------------------------------------------------------------
function updateScene() {
var key;
try{
if(m_sceneJSON === null || typeof(m_sceneJSON) === "undefined") {
return;
}
m_vglVtkReader.initScene();
// Mark all actors as invalid
for (key in m_vglActors) {
m_vglActors[key].invalid = true;
}
// Fetch the object that we are missing
fetchMissingObjects(fetchObject, m_sceneJSON);
// Draw scene
drawScene(false);
} catch(error) {
console.log(error);
}
}
// ------------------------------------------------------------------
function clearCache() {
m_objectIndex = {};
m_vglActors = {};
m_container.trigger('invalidateScene');
}
// ------------------------------------------------------------------
// Add rendererAttrs into the DOM
m_container.append(m_rendererAttrs);
// ------------------------------------------------------------------
// Add viewport listener
m_container.bind('invalidateScene', function() {
if(m_rendererAttrs.hasClass('active')){
if (m_vglVtkReader === null) {
m_vglVtkReader = vgl.vtkReader();
} else {
m_vglVtkReader.deleteViewer();
}
m_canvas3D.width = m_rendererAttrs.width();
m_canvas3D.height = m_rendererAttrs.height();
m_viewer = m_vglVtkReader.createViewer(m_canvas3D);
m_viewer.renderWindow().activeRenderer().setResetScene(false);
m_viewer.renderWindow().activeRenderer().setResetClippingRange(false);
// Bind mouse event handlers
m_container.on('mouse', function(event) {
if (m_viewer) {
if (event.action === 'move') {
m_viewer.handleMouseMove(event.originalEvent);
}
else if (event.action === 'up') {
m_viewer.handleMouseUp(event.originalEvent);
}
else if (event.action === 'down') {
m_viewer.handleMouseDown(event.originalEvent);
}
}
});
fetchScene();
}
}).bind('render', function(){
if(m_rendererAttrs.hasClass('active')){
render(false);
}
}).bind('downloadAllTimesteps', function(event){
if(m_rendererAttrs.hasClass('active')){
downloadAllTimesteps();
}
}).bind('clearCache', function(event){
if(m_rendererAttrs.hasClass('active')){
clearCache();
}
}).bind('downloadProgress', function(event){
if(m_rendererAttrs.hasClass('active')){
handleDownloadProgressEvent(event);
}
}).bind('captureRenderedImage', function(e){
if (m_rendererAttrs.hasClass('active')) {
render(true);
$(m_container).parent().trigger({
type: 'captured-screenshot-ready',
imageData: screenImage
});
}
}).bind('resetViewId', function(e){
m_options.view = -1;
}).bind('mouse', function(event){
if(m_rendererAttrs.hasClass('active')){
event.preventDefault();
pushCameraState();
}
}).bind('active', function(){
if(m_rendererAttrs.hasClass('active')){
// Ready to render data
fetchScene();
}
});
}
// ----------------------------------------------------------------------
// Init vtkWeb module if needed
// ----------------------------------------------------------------------
if (GLOBAL.hasOwnProperty("vtkWeb")) {
module = GLOBAL.vtkWeb || {};
} else {
GLOBAL.vtkWeb = module;
}
// ----------------------------------------------------------------------
// Extend the viewport factory - ONLY IF WEBGL IS SUPPORTED
// ----------------------------------------------------------------------
try {
if (GLOBAL.WebGLRenderingContext && typeof(vec3) != "undefined" && typeof(mat4) != "undefined") {
var canvas = GLOBAL.document.createElement('canvas'),
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
if(gl) {
// WebGL is supported
if(!module.hasOwnProperty('ViewportFactory')) {
module['ViewportFactory'] = {};
}
module.ViewportFactory[FACTORY_KEY] = FACTORY;
}
}
} catch(exception) {
// nothing to do
}
// ----------------------------------------------------------------------
// Local module registration
// ----------------------------------------------------------------------
try {
// Tests for presence of jQuery and glMatrix, then registers this module
if ($ !== undefined && module.ViewportFactory[FACTORY_KEY] !== undefined) {
module.registerModule('vtkweb-viewport-vgl');
}
} catch(err) {
console.error('jQuery or glMatrix is missing or browser does not support WebGL: ' + err.message);
}
}(window, jQuery));

File diff suppressed because it is too large Load Diff