Skip to content

Commit

Permalink
feat(VtkGeometryRenderer): Add local rendering capability
Browse files Browse the repository at this point in the history
Add an option to render geometry locally using vtk.js, fix bugs uncovered
as a result of allowing the swapping of the two renderers.
  • Loading branch information
scottwittenburg committed May 12, 2017
1 parent fb421e5 commit f3d66c4
Show file tree
Hide file tree
Showing 15 changed files with 324 additions and 74,519 deletions.
74,578 changes: 95 additions & 74,483 deletions dist/Visualizer.js

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"kw-web-suite": "2.2.1",
"kw-doc": "1.0.15",

"paraviewweb": "2.2.64"
"paraviewweb": "2.2.101",
"vtk.js": "2.18.17"
},
"scripts": {
"build": "fix-autobahn && webpack",
Expand Down
2 changes: 1 addition & 1 deletion server/pvw-visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def initialize(self):
self.registerVtkWebProtocol(pv_protocols.ParaViewWebMouseHandler())
self.registerVtkWebProtocol(pv_protocols.ParaViewWebViewPort(_VisualizerServer.viewportScale, _VisualizerServer.viewportMaxWidth, _VisualizerServer.viewportMaxHeight))
self.registerVtkWebProtocol(pv_protocols.ParaViewWebViewPortImageDelivery())
self.registerVtkWebProtocol(pv_protocols.ParaViewWebViewPortGeometryDelivery())
self.registerVtkWebProtocol(pv_protocols.ParaViewWebLocalRendering())
self.registerVtkWebProtocol(pv_protocols.ParaViewWebTimeHandler())
self.registerVtkWebProtocol(pv_protocols.ParaViewWebSelectionHandler())
self.registerVtkWebProtocol(pv_protocols.ParaViewWebWidgetManager())
Expand Down
34 changes: 23 additions & 11 deletions src/ImageProviders.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,52 @@ const listeners = {};
function setImageProvider(provider, key) {
providers[key || DEFAULT_IMAGE_PROVIDER] = provider;
const listenersToNotify = listeners[key || DEFAULT_IMAGE_PROVIDER] || [];
listenersToNotify.forEach((cb) => {
if (cb) {
cb(provider);
listenersToNotify.forEach((listener) => {
if (listener && !listener.called) {
listener.callback(provider);
listener.called = true;
}
});
delete listeners[key || DEFAULT_IMAGE_PROVIDER];
}

function getImageProvider(key) {
return providers[key || DEFAULT_IMAGE_PROVIDER];
}

function onImageProvider(callback, key = DEFAULT_IMAGE_PROVIDER) {
if (providers[key]) {
callback(providers[key]);
return -1;
}

function addListener(callback, key, called = false) {
if (!listeners[key]) {
listeners[key] = [];
}
const id = listeners[key].length;
listeners[key].push(callback);
listeners[key].push({ called, callback });

return id;
}

function onImageProvider(callback, key = DEFAULT_IMAGE_PROVIDER) {
if (providers[key]) {
callback(providers[key]);
return addListener(callback, key, true);
}

return addListener(callback, key);
}

function unsubscribe(id, key = DEFAULT_IMAGE_PROVIDER) {
listeners[key][id] = null;
}

function reset(key) {
const listenersToReset = listeners[key || DEFAULT_IMAGE_PROVIDER] || [];
listenersToReset.forEach((listener) => {
listener.called = false;
});
}

export default {
setImageProvider,
getImageProvider,
onImageProvider,
unsubscribe,
reset,
};
20 changes: 20 additions & 0 deletions src/LocalRenderingImageProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Monologue from 'monologue.js';

const IMAGE_READY = 'local.image.ready';

export default class LocalRenderingImageProvider {
fireImageReady(img) {
this.emit(IMAGE_READY, { url: img });
}

onImageReady(callback) {
return this.on(IMAGE_READY, callback);
}

/* eslint-disable class-methods-use-this */
getLastImageReadyEvent() {
return null;
}
}

Monologue.mixInto(LocalRenderingImageProvider);
99 changes: 85 additions & 14 deletions src/MainView.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import React from 'react';
import VtkRenderer from 'paraviewweb/src/React/Renderers/VtkRenderer';
import SvgIconWidget from 'paraviewweb/src/React/Widgets/SvgIconWidget';
import { connect } from 'react-redux';
import React from 'react';

import style from 'VisualizerStyle/MainView.mcss';
import VtkRenderer from 'paraviewweb/src/React/Renderers/VtkRenderer';
import VtkGeometryRenderer from 'paraviewweb/src/React/Renderers/VtkGeometryRenderer';
import SvgIconWidget from 'paraviewweb/src/React/Widgets/SvgIconWidget';
import { connect } from 'react-redux';

import ControlPanel from './panels/ControlPanel';
import TimeController from './panels/TimeController';
import logo from './logo.svg';
import style from 'VisualizerStyle/MainView.mcss';

import ControlPanel from './panels/ControlPanel';
import TimeController from './panels/TimeController';
import logo from './logo.svg';

import network from './network';
import ImageProviders from './ImageProviders';
import LocalRenderingImageProvider from './LocalRenderingImageProvider';
import { selectors, actions, dispatch } from './redux';

export const Visualizer = React.createClass({
Expand All @@ -19,10 +22,15 @@ export const Visualizer = React.createClass({

propTypes: {
resetCamera: React.PropTypes.func,
updateCamera: React.PropTypes.func,
client: React.PropTypes.object,
connection: React.PropTypes.object,
session: React.PropTypes.object,
pendingCount: React.PropTypes.number,
remoteRendering: React.PropTypes.bool,
viewId: React.PropTypes.string,
provideOnImageReady: React.PropTypes.bool,
updateActiveViewId: React.PropTypes.func,
},

getInitialState() {
Expand All @@ -31,15 +39,63 @@ export const Visualizer = React.createClass({
};
},

componentWillMount() {
this.needsSetImageProvider = true;
},

componentDidMount() {
ImageProviders.setImageProvider(this.renderer.binaryImageStream);
this.setImageProvider();
},

componentWillReceiveProps(nextProps) {
if (nextProps.remoteRendering !== this.props.remoteRendering) {
if (nextProps.remoteRendering) {
// Changing back to remote rendering
const params = this.renderer.getCameraParameters();
this.props.updateCamera(this.props.viewId, params.focalPoint, params.viewUp, params.position);
}
ImageProviders.reset();
this.needsSetImageProvider = true;
}
},

componentDidUpdate() {
this.setImageProvider();
},

setImageProvider() {
if (this.needsSetImageProvider) {
if (this.renderer.binaryImageStream) {
ImageProviders.setImageProvider(this.renderer.binaryImageStream);
} else {
if (!this.localRenderingImageProvider) {
this.localRenderingImageProvider = new LocalRenderingImageProvider();
}
ImageProviders.setImageProvider(this.localRenderingImageProvider);
}
}
this.needsSetImageProvider = false;
},

resetCamera() {
this.props.resetCamera();
if (this.renderer && this.renderer.resetCamera) {
this.renderer.resetCamera();
}
},

toggleMenu() {
this.setState({ menuVisible: !this.state.menuVisible });
},

localImageReady(img) {
if (this.localRenderingImageProvider) {
this.localRenderingImageProvider.fireImageReady(img);
}
},

render() {
const Renderer = this.props.remoteRendering ? VtkRenderer : VtkGeometryRenderer;
return (
<div className={style.container}>
<div className={style.topBar}>
Expand All @@ -54,22 +110,32 @@ export const Visualizer = React.createClass({
/>
Visualizer
</div>
<ControlPanel className={this.state.menuVisible ? style.menu : style.hiddenMenu} />
<ControlPanel
className={this.state.menuVisible ? style.menu : style.hiddenMenu}
resetCamera={this.resetCamera}
/>
</div>
<div className={style.buttons}>
<TimeController />
<i
className={style.resetCameraButton}
onClick={this.props.resetCamera}
onClick={this.resetCamera}
/>
</div>
</div>
<VtkRenderer
ref={c => (this.renderer = c)}
<Renderer
ref={(c) => { this.renderer = c; }}
client={this.props.client}
viewId={this.props.viewId}
connection={this.props.connection}
session={this.props.session}
className={style.viewport}
onImageReady={this.props.provideOnImageReady ? this.localImageReady : null}
viewIdUpdated={this.props.updateActiveViewId}
resizeOnWindowResize
clearOneTimeUpdatersOnUnmount
clearInstanceCacheOnUnMount
clearArrayCacheOnUnMount
/>
</div>);
},
Expand All @@ -83,11 +149,16 @@ export default connect(
const client = network.getClient();
const connection = network.getConnection();
const session = connection.session;
const remoteRendering = selectors.view.getRemoteRenderingState(state);
const viewId = selectors.active.getActiveView(state);
const provideOnImageReady = selectors.ui.getVisiblePanel(state) === 3; // SavePanel visible

return { client, connection, session, pendingCount };
return { client, connection, session, pendingCount, remoteRendering, viewId, provideOnImageReady };
},
() => ({
resetCamera: () => dispatch(actions.view.resetCamera()),
updateCamera: (viewId, focalPoint, viewUp, position) => dispatch(actions.view.updateCamera(viewId, focalPoint, viewUp, position)),
updateActiveViewId: viewId => dispatch(actions.active.activate(viewId, actions.active.TYPE_VIEW)),
})
)(Visualizer);

1 change: 1 addition & 0 deletions src/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function start(conn) {
'ProxyManager',
'TimeHandler',
'ViewPort',
'VtkGeometryDelivery',
], customProtocols);

if (readyCallback) {
Expand Down
26 changes: 26 additions & 0 deletions src/panels/ControlPanel/SettingPanel/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import React from 'react';
import ProxyEditorWidget from 'paraviewweb/src/React/Widgets/ProxyEditorWidget';
import CheckboxProperty from 'paraviewweb/src/React/Properties/CheckboxProperty';
import style from 'VisualizerStyle/SettingPanel.mcss';

import { connect } from 'react-redux';
import { selectors, actions, dispatch } from '../../../redux';

// ----------------------------------------------------------------------------

const LOCAL_RENDERING_PROPS = {
data: { value: true, id: 'remoteRenderingCheckbox' },
show: () => true,
ui: {
label: 'Remote Rendering',
componentLabels: [''],
help: 'Uncheck for local rendering. If doing local rendering, all the geometry and the used data arrays will be sent to the browser.' },
};

export const SettingPanel = React.createClass({

displayName: 'ParaViewWeb/SettingPanel',
Expand All @@ -19,6 +29,8 @@ export const SettingPanel = React.createClass({
fetchSettingProxy: React.PropTypes.func,
applyChangeSet: React.PropTypes.func,
updateCollapsableState: React.PropTypes.func,
isRemoteRenderingEnabled: React.PropTypes.bool,
updateRemoteRendering: React.PropTypes.func,
},

getDefaultProps() {
Expand All @@ -41,13 +53,23 @@ export const SettingPanel = React.createClass({
this.props.fetchSettingProxy();
},

remoteRenderingBoxChecked() {
this.props.updateRemoteRendering(!this.props.isRemoteRenderingEnabled);
},

render() {
if (!this.props.visible) {
return null;
}

const checkboxProps = Object.assign({}, LOCAL_RENDERING_PROPS, {
onChange: this.remoteRenderingBoxChecked,
});
checkboxProps.data.value = this.props.isRemoteRenderingEnabled;

return (
<div className={style.container}>
<CheckboxProperty {...checkboxProps} />
<ProxyEditorWidget
sections={this.props.sections}
onApply={this.applyChanges}
Expand All @@ -73,6 +95,10 @@ export default connect(
updateCollapsableState(name, isOpen) {
dispatch(actions.ui.updateCollapsableState(name, isOpen));
},
isRemoteRenderingEnabled: selectors.view.getRemoteRenderingState(state),
updateRemoteRendering(isRemote) {
dispatch(actions.view.setRemoteRendering(isRemote));
},
};
}
)(SettingPanel);
3 changes: 0 additions & 3 deletions src/panels/ControlPanel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,6 @@ export default connect(
(state) => {
return {
activeIdx: selectors.ui.getVisiblePanel(state),
resetCamera() {
dispatch(actions.view.resetCamera());
},
updateActivePanel(idx) {
dispatch(actions.ui.updateVisiblePanel(idx));
},
Expand Down
5 changes: 3 additions & 2 deletions src/redux/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import * as network from './ducks/network';
import * as proxies from './ducks/proxies';
import * as save from './ducks/save';
import * as time from './ducks/time';
import * as view from './ducks/view';
import * as ui from './ducks/ui';
import * as view from './ducks/view';


function resetVisualizerState() {
return { type: 'RESET_VISUALIZER_STATE' };
Expand All @@ -21,6 +22,6 @@ export default {
resetVisualizerState,
save,
time,
view,
ui,
view,
};
Loading

0 comments on commit f3d66c4

Please sign in to comment.