Files
ThirdParty-6/ParaView-5.0.1/VTK/Web/JavaScript/Lib/vtkweb.viewport.image.js

347 lines
13 KiB
JavaScript

/**
* 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 trip (ms)',
type: 'value',
convert: NoOp
},
'image-server-processing': {
label: 'Processing Time (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));