diff --git a/Sources/Proxy/Core/AbstractRepresentationProxy/index.js b/Sources/Proxy/Core/AbstractRepresentationProxy/index.js index 6f25cc9c3c0..dd7e5d132cc 100644 --- a/Sources/Proxy/Core/AbstractRepresentationProxy/index.js +++ b/Sources/Proxy/Core/AbstractRepresentationProxy/index.js @@ -2,17 +2,6 @@ import macro from 'vtk.js/Sources/macro'; import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; import vtkScalarsToColors from 'vtk.js/Sources/Common/Core/ScalarsToColors'; -// ---------------------------------------------------------------------------- - -function connectMapper(mapper, input) { - const algo = input.getAlgo(); - if (algo) { - mapper.setInputConnection(algo.getOutputPort()); - } else { - mapper.setInputData(input.getDataset()); - } -} - // ---------------------------------------------------------------------------- // vtkAbstractRepresentationProxy methods // ---------------------------------------------------------------------------- @@ -21,10 +10,30 @@ function vtkAbstractRepresentationProxy(publicAPI, model) { // Set our className model.classHierarchy.push('vtkAbstractRepresentationProxy'); + function updateConnectivity() { + if (model.input) { + let count = model.sourceDependencies.length; + while (count--) { + model.sourceDependencies[count].setInputData(model.input.getDataset()); + } + } + } + publicAPI.setInput = (source) => { + if (model.sourceSubscription) { + model.sourceSubscription.unsubscribe(); + model.sourceSubscription = null; + } publicAPI.gcPropertyLinks(); model.input = source; publicAPI.updateColorByDomain(); + + if (model.input) { + updateConnectivity(); + model.sourceSubscription = model.input.onDatasetChange( + updateConnectivity + ); + } }; publicAPI.getInputDataSet = () => @@ -268,6 +277,13 @@ function vtkAbstractRepresentationProxy(publicAPI, model) { }, }); }; + + publicAPI.delete = macro.chain(() => { + if (model.sourceSubscription) { + model.sourceSubscription.unsubscribe(); + model.sourceSubscription = null; + } + }, publicAPI.delete); } // ---------------------------------------------------------------------------- @@ -277,6 +293,7 @@ function vtkAbstractRepresentationProxy(publicAPI, model) { const DEFAULT_VALUES = { actors: [], volumes: [], + sourceDependencies: [], }; // ---------------------------------------------------------------------------- @@ -292,4 +309,4 @@ function extend(publicAPI, model, initialValues = {}) { macro.proxy(publicAPI, model); } -export default { extend, connectMapper }; +export default { extend }; diff --git a/Sources/Proxy/Core/ProxyManager/core.js b/Sources/Proxy/Core/ProxyManager/core.js index bd3fa772f63..e809611f921 100644 --- a/Sources/Proxy/Core/ProxyManager/core.js +++ b/Sources/Proxy/Core/ProxyManager/core.js @@ -150,7 +150,7 @@ export default function addRegistrationAPI(publicAPI, model) { const viewToUse = view || publicAPI.getActiveView(); // Can only get a representation for a source and a view - if (!sourceToUse || !viewToUse) { + if (!sourceToUse || !viewToUse || !sourceToUse.getType()) { return null; } diff --git a/Sources/Proxy/Core/SourceProxy/index.js b/Sources/Proxy/Core/SourceProxy/index.js index 8530b7b0688..71f325dbbdf 100644 --- a/Sources/Proxy/Core/SourceProxy/index.js +++ b/Sources/Proxy/Core/SourceProxy/index.js @@ -8,46 +8,98 @@ function vtkSourceProxy(publicAPI, model) { // Set our className model.classHierarchy.push('vtkSourceProxy'); - function updateDataset() { - if (model.algo) { - publicAPI.setDataset(model.algo.getOutputData(), model.type); + // API ---------------------------------------------------------------------- + + publicAPI.setInputProxy = (source) => { + if (model.inputSubscription) { + model.inputSubscription(); + model.inputSubscription = null; } - } + model.inputProxy = source; + if (model.inputProxy) { + model.inputSubscription = source.onModified( + publicAPI.update, + -1 + ).unsubscribe; // Trigger at next cycle + } + publicAPI.update(); + }; - // API ---------------------------------------------------------------------- + // -------------------------------------------------------------------------- publicAPI.setInputData = (ds, type) => { if (model.dataset !== ds) { model.dataset = ds; model.type = type || ds.getClassName(); publicAPI.modified(); + publicAPI.invokeDatasetChange(); } }; // -------------------------------------------------------------------------- - publicAPI.setInputAlgorithm = (algo, type) => { + publicAPI.setInputAlgorithm = (algo, type, autoUpdate = true) => { if (model.algo !== algo) { model.algo = algo; if (model.algoSubscription) { model.algoSubscription(); model.algoSubscription = null; } - if (algo) { - publicAPI.setInputData(algo.getOutputData(), type); - model.algoSubscription = algo.onModified(updateDataset, -1).unsubscribe; // Trigger at next cycle + if (algo && autoUpdate) { + model.algoSubscription = algo.onModified(() => { + publicAPI.update(); + }, -1).unsubscribe; // Trigger at next cycle + publicAPI.update(); } } }; // -------------------------------------------------------------------------- + publicAPI.update = () => { + if (model.algo && model.inputProxy) { + model.algo.setInputData(model.inputProxy.getDataset()); + } + if (model.updateDomain && model.inputProxy) { + model.updateDomain(publicAPI, model.inputProxy.getDataset()); + } + if (model.algo) { + publicAPI.setInputData(model.algo.getOutputData(), model.type); + } + }; + + publicAPI.getUpdate = () => model.algo.getMTime() > model.dataset.getMTime(); + + // -------------------------------------------------------------------------- + publicAPI.delete = macro.chain(() => { if (model.algoSubscription) { model.algoSubscription(); model.algoSubscription = null; } + if (model.inputSubscription) { + model.inputSubscription(); + model.inputSubscription = null; + } }, publicAPI.delete); + + // -------------------------------------------------------------------------- + // Initialisation + // -------------------------------------------------------------------------- + + if (model.inputProxy) { + model.inputSubscription = model.inputProxy.onModified(() => { + publicAPI.update(); + }, -1).unsubscribe; // Trigger at next cycle + } + if (model.algoFactory) { + publicAPI.setInputAlgorithm( + model.algoFactory.newInstance(), + null, + model.autoUpdate + ); + } + publicAPI.update(); } // ---------------------------------------------------------------------------- @@ -71,11 +123,16 @@ export function extend(publicAPI, model, initialValues = {}) { 'algo', 'inputProxy', ]); - macro.set(publicAPI, model, ['name', 'inputProxy']); + macro.set(publicAPI, model, ['name']); + macro.event(publicAPI, model, 'DatasetChange'); + macro.proxy(publicAPI, model); // Object specific methods vtkSourceProxy(publicAPI, model); - macro.proxy(publicAPI, model); + + if (model.proxyPropertyMapping) { + macro.proxyPropertyMapping(publicAPI, model, model.proxyPropertyMapping); + } } // ---------------------------------------------------------------------------- @@ -84,4 +141,7 @@ export const newInstance = macro.newInstance(extend, 'vtkSourceProxy'); // ---------------------------------------------------------------------------- -export default { newInstance, extend }; +export default { + newInstance, + extend, +}; diff --git a/Sources/Proxy/Representations/GeometryRepresentationProxy/index.js b/Sources/Proxy/Representations/GeometryRepresentationProxy/index.js index a2cf0b9aba4..28c71a766a0 100644 --- a/Sources/Proxy/Representations/GeometryRepresentationProxy/index.js +++ b/Sources/Proxy/Representations/GeometryRepresentationProxy/index.js @@ -26,10 +26,6 @@ const PROPERTIES_DEFAULT = { function vtkGeometryRepresentationProxy(publicAPI, model) { // Set our className model.classHierarchy.push('vtkGeometryRepresentationProxy'); - const superSetInput = publicAPI.setInput; - - // parent methods - model.selectedArray = null; // Default use solid color // Internals model.mapper = vtkMapper.newInstance({ @@ -40,21 +36,11 @@ function vtkGeometryRepresentationProxy(publicAPI, model) { model.actor = vtkActor.newInstance(); model.property = model.actor.getProperty(); - // API ---------------------------------------------------------------------- - - publicAPI.setInput = (source) => { - superSetInput(source); - - if (!source) { - return; - } - - vtkAbstractRepresentationProxy.connectMapper(model.mapper, source); - - // connect rendering pipeline - model.actor.setMapper(model.mapper); - model.actors.push(model.actor); - }; + // Auto connect mappers + model.sourceDependencies.push(model.mapper); + // connect rendering pipeline + model.actor.setMapper(model.mapper); + model.actors.push(model.actor); } // ---------------------------------------------------------------------------- diff --git a/Sources/Proxy/Representations/MoleculeRepresentationProxy/index.js b/Sources/Proxy/Representations/MoleculeRepresentationProxy/index.js index 26fe2796348..44428c13e26 100644 --- a/Sources/Proxy/Representations/MoleculeRepresentationProxy/index.js +++ b/Sources/Proxy/Representations/MoleculeRepresentationProxy/index.js @@ -13,7 +13,6 @@ import vtkAbstractRepresentationProxy from 'vtk.js/Sources/Proxy/Core/AbstractRe function vtkMoleculeRepresentationProxy(publicAPI, model) { // Set our className model.classHierarchy.push('vtkMoleculeRepresentationProxy'); - const superSetInput = publicAPI.setInput; // Internals model.filter = vtkMoleculeToRepresentation.newInstance(); @@ -22,32 +21,24 @@ function vtkMoleculeRepresentationProxy(publicAPI, model) { model.sphereActor = vtkActor.newInstance(); model.stickActor = vtkActor.newInstance(); - // API ---------------------------------------------------------------------- - - publicAPI.setInput = (source) => { - superSetInput(source); + model.sourceDependencies.push(model.filter); - if (!source) { - return; - } + // render sphere + model.sphereMapper.setInputConnection(model.filter.getOutputPort(0)); + model.sphereMapper.setScaleArray(model.filter.getSphereScaleArrayName()); + model.sphereActor.setMapper(model.sphereMapper); - vtkAbstractRepresentationProxy.connectMapper(model.filter, source); + // render sticks + model.stickMapper.setInputConnection(model.filter.getOutputPort(1)); + model.stickMapper.setScaleArray('stickScales'); + model.stickMapper.setOrientationArray('orientation'); + model.stickActor.setMapper(model.stickMapper); - // render sphere - model.sphereMapper.setInputConnection(model.filter.getOutputPort(0)); - model.sphereMapper.setScaleArray(model.filter.getSphereScaleArrayName()); - model.sphereActor.setMapper(model.sphereMapper); + // Add actors + model.actors.push(model.sphereActor); + model.actors.push(model.stickActor); - // render sticks - model.stickMapper.setInputConnection(model.filter.getOutputPort(1)); - model.stickMapper.setScaleArray('stickScales'); - model.stickMapper.setOrientationArray('orientation'); - model.stickActor.setMapper(model.stickMapper); - - // Add actors - model.actors.push(model.sphereActor); - model.actors.push(model.stickActor); - }; + // API ---------------------------------------------------------------------- publicAPI.setColorBy = () => {}; publicAPI.getColorBy = () => []; diff --git a/Sources/Proxy/Representations/SliceRepresentationProxy/index.js b/Sources/Proxy/Representations/SliceRepresentationProxy/index.js index 95333bd0753..cff2e61eb85 100644 --- a/Sources/Proxy/Representations/SliceRepresentationProxy/index.js +++ b/Sources/Proxy/Representations/SliceRepresentationProxy/index.js @@ -75,18 +75,13 @@ function vtkSliceRepresentationProxy(publicAPI, model) { model.actor = vtkImageSlice.newInstance(); model.property = model.actor.getProperty(); - // API ---------------------------------------------------------------------- + // connect rendering pipeline + model.actor.setMapper(model.mapper); + model.actors.push(model.actor); - publicAPI.setInput = (source) => { - superSetInput(source); - - if (!source) { - return; - } - - vtkAbstractRepresentationProxy.connectMapper(model.mapper, source); + function setInputData(inputDataset) { const state = updateDomains( - publicAPI.getInputDataSet(), + inputDataset, publicAPI.getDataArray(), model, publicAPI.updateProxyProperty @@ -94,17 +89,26 @@ function vtkSliceRepresentationProxy(publicAPI, model) { publicAPI.set(state); // Init slice location - const extent = publicAPI.getInputDataSet().getExtent(); + const extent = inputDataset.getExtent(); publicAPI.setXSlice(Math.floor(mean(extent[0], extent[1]))); publicAPI.setYSlice(Math.floor(mean(extent[2], extent[3]))); publicAPI.setZSlice(Math.floor(mean(extent[4], extent[5]))); + } - // connect rendering pipeline - model.actor.setMapper(model.mapper); - model.actors.push(model.actor); + // Keep things updated + model.sourceDependencies.push(model.mapper); + model.sourceDependencies.push({ setInputData }); - // Create a link handler on source + // API ---------------------------------------------------------------------- + publicAPI.setInput = (source) => { + superSetInput(source); + + if (!source) { + return; + } + + // Create a link handler on source // Ensure the delete will clear all possible conbinaison ['SliceX', 'SliceY', 'SliceZ', 'ColorWindow', 'ColorLevel'].forEach( (linkName) => { diff --git a/Sources/Proxy/Representations/VolumeRepresentationProxy/index.js b/Sources/Proxy/Representations/VolumeRepresentationProxy/index.js index 6eff8fa6c5c..8647ac75220 100644 --- a/Sources/Proxy/Representations/VolumeRepresentationProxy/index.js +++ b/Sources/Proxy/Representations/VolumeRepresentationProxy/index.js @@ -151,6 +151,8 @@ function vtkVolumeRepresentationProxy(publicAPI, model) { model.volume = vtkVolume.newInstance(); model.property = model.volume.getProperty(); + model.sourceDependencies.push(model.mapper); + // Slices model.mapperX = vtkImageMapper.newInstance({ currentSlicingMode: vtkImageMapper.SlicingMode.X, @@ -172,17 +174,23 @@ function vtkVolumeRepresentationProxy(publicAPI, model) { property: model.propertySlices, }); - // API ---------------------------------------------------------------------- - - publicAPI.setInput = (source) => { - superSetInput(source); + model.sourceDependencies.push(model.mapperX); + model.sourceDependencies.push(model.mapperY); + model.sourceDependencies.push(model.mapperZ); - if (!source) { - return; - } + // connect rendering pipeline + model.volume.setMapper(model.mapper); + model.volumes.push(model.volume); - vtkAbstractRepresentationProxy.connectMapper(model.mapper, source); + // Connect slice pipeline + model.actorX.setMapper(model.mapperX); + model.actors.push(model.actorX); + model.actorY.setMapper(model.mapperY); + model.actors.push(model.actorY); + model.actorZ.setMapper(model.mapperZ); + model.actors.push(model.actorZ); + function setInputData(inputDataset) { const [name, location] = publicAPI.getColorBy(); publicAPI.rescaleTransferFunctionToDataRange(name, location); @@ -197,36 +205,29 @@ function vtkVolumeRepresentationProxy(publicAPI, model) { model.property.setRGBTransferFunction(0, lutProxy.getLookupTable()); model.property.setScalarOpacity(0, pwfProxy.getPiecewiseFunction()); - updateConfiguration( - publicAPI.getInputDataSet(), - publicAPI.getDataArray(), - model - ); + updateConfiguration(inputDataset, publicAPI.getDataArray(), model); publicAPI.setSampleDistance(); publicAPI.setEdgeGradient(); // Update domains const state = updateDomains( - publicAPI.getInputDataSet(), + inputDataset, publicAPI.getDataArray(), publicAPI.updateProxyProperty ); publicAPI.set(state); + } - // connect rendering pipeline - model.volume.setMapper(model.mapper); - model.volumes.push(model.volume); - - // Connect slice pipeline - vtkAbstractRepresentationProxy.connectMapper(model.mapperX, source); - model.actorX.setMapper(model.mapperX); - model.actors.push(model.actorX); - vtkAbstractRepresentationProxy.connectMapper(model.mapperY, source); - model.actorY.setMapper(model.mapperY); - model.actors.push(model.actorY); - vtkAbstractRepresentationProxy.connectMapper(model.mapperZ, source); - model.actorZ.setMapper(model.mapperZ); - model.actors.push(model.actorZ); + model.sourceDependencies.push({ setInputData }); + + // API ---------------------------------------------------------------------- + + publicAPI.setInput = (source) => { + superSetInput(source); + + if (!source) { + return; + } // Create a link handler on source ['SliceX', 'SliceY', 'SliceZ', 'ColorWindow', 'ColorLevel'].forEach(