@kitware/vtk.js
Version:
Visualization Toolkit for the Web
445 lines (412 loc) • 15.6 kB
JavaScript
import { m as macro } from '../../macros2.js';
import vtkActor from '../../Rendering/Core/Actor.js';
import vtkVolume from '../../Rendering/Core/Volume.js';
import vtkHttpDataSetReader from './HttpDataSetReader.js';
import vtkHttpDataSetSeriesReader from './HttpDataSetSeriesReader.js';
import vtkMapper from '../../Rendering/Core/Mapper.js';
import vtkTexture from '../../Rendering/Core/Texture.js';
import vtkTextureLODsDownloader from '../../Rendering/Misc/TextureLODsDownloader.js';
import vtkHttpDataSetLODsLoader from '../Misc/HttpDataSetLODsLoader.js';
import vtkColorTransferFunction from '../../Rendering/Core/ColorTransferFunction.js';
import vtkPiecewiseFunction from '../../Common/DataModel/PiecewiseFunction.js';
import vtkVolumeMapper from '../../Rendering/Core/VolumeMapper.js';
import vtkTimeStepBasedAnimationHandler from '../../Interaction/Animations/TimeStepBasedAnimationHandler.js';
import DataAccessHelper from './DataAccessHelper.js';
import './DataAccessHelper/LiteHttpDataAccessHelper.js';
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // HTTP + zip
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HtmlDataAccessHelper'; // html + base64 + zip
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/JSZipDataAccessHelper'; // zip
const {
vtkErrorMacro
} = macro;
let itemCount = 1;
function applySettings(sceneItem, settings) {
if (settings.actor) {
sceneItem.actor.set(settings.actor);
}
if (settings.actorRotation) {
sceneItem.actor.rotateWXYZ(settings.actorRotation[0], settings.actorRotation[1], settings.actorRotation[2], settings.actorRotation[3]);
}
if (settings.volumeRotation) {
sceneItem.volume.rotateWXYZ(settings.volumeRotation[0], settings.volumeRotation[1], settings.volumeRotation[2], settings.volumeRotation[3]);
}
if (settings.property) {
if (settings.volume) {
const volumePropertySettings = {
...settings.property
};
delete volumePropertySettings.components;
sceneItem.volume.getProperty().set(volumePropertySettings);
if (settings.property.components) {
const volumeProperty = sceneItem.volume.getProperty();
sceneItem.volumeComponents.forEach((component, componentIndex) => {
volumeProperty.setScalarOpacityUnitDistance(componentIndex, settings.property.components[componentIndex].scalarOpacityUnitDistance);
if (component.rgbTransferFunction) {
volumeProperty.setRGBTransferFunction(componentIndex, component.rgbTransferFunction);
}
if (component.grayTransferFunction) {
volumeProperty.setGrayTransferFunction(componentIndex, component.grayTransferFunction);
}
if (component.scalarOpacity) {
volumeProperty.setScalarOpacity(componentIndex, component.scalarOpacity);
}
});
}
} else {
sceneItem.actor.getProperty().set(settings.property);
}
}
if (settings.mapper) {
if (settings.mapper.colorByArrayName) {
sceneItem.source.enableArray(settings.mapper.colorByArrayName, settings.mapper.colorByArrayName);
sceneItem.source.loadData();
}
sceneItem.mapper.set(settings.mapper);
if (settings.mapper.colorByArrayName && settings.luts[settings.mapper.colorByArrayName]) {
sceneItem.mapper.setLookupTable(settings.luts[settings.mapper.colorByArrayName]);
sceneItem.mapper.setUseLookupTableScalarRange(true);
}
}
if (settings.lookupTable) {
sceneItem.mapper.getLookupTable().set(settings.lookupTable);
sceneItem.mapper.getLookupTable().build();
}
if (settings.textureLODs) {
sceneItem.textureLODs = settings.textureLODs;
}
if (settings.sourceLODs) {
sceneItem.sourceLODs = settings.sourceLODs;
}
}
// ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------
function isImage(str) {
const ext = str.split('.').pop().toLowerCase();
return ['jpg', 'png', 'jpeg'].indexOf(ext) !== -1;
}
function loadColorTransferFunction(item) {
const tf = vtkColorTransferFunction.newInstance(item);
if (item.nodes) {
tf.removeAllPoints();
item.nodes.forEach(_ref => {
let [x, r, g, b, midpoint, sharpness] = _ref;
tf.addRGBPointLong(x, r, g, b, midpoint, sharpness);
});
}
return tf;
}
function loadPiecewiseFunction(item) {
const pwf = vtkPiecewiseFunction.newInstance(item);
if (item.points) {
pwf.removeAllPoints();
item.points.forEach(_ref2 => {
let [x, y, midpoint, sharpness] = _ref2;
return pwf.addPointLong(x, y, midpoint, sharpness);
});
}
return pwf;
}
function initializeVolumeComponents(components) {
return components.map(component => {
const ret = {};
if (component.rgbTransferFunction) {
ret.rgbTransferFunction = loadColorTransferFunction(component.rgbTransferFunction);
} else if (component.grayTransferFunction) {
ret.grayTransferFunction = loadPiecewiseFunction(component.grayTransferFunction);
}
if (component.scalarOpacity) {
ret.scalarOpacity = loadPiecewiseFunction(component.scalarOpacity);
}
return ret;
});
}
// ----------------------------------------------------------------------------
function defineLoadFuctionForReader(type) {
return (item, model, publicAPI) => {
const source = type.newInstance({
fetchGzip: model.fetchGzip,
dataAccessHelper: model.dataAccessHelper
});
let mapper;
if (item.volume) {
mapper = vtkVolumeMapper.newInstance();
} else {
mapper = vtkMapper.newInstance();
}
const sceneItem = {
name: item.name || `Item ${itemCount++}`,
source,
mapper,
defaultSettings: item
};
if (item.volume) {
const volume = vtkVolume.newInstance();
sceneItem.volume = volume;
if (model.renderer) {
model.renderer.addVolume(volume);
}
if (item.property && item.property.components) {
// initialize transfer functions
sceneItem.volumeComponents = initializeVolumeComponents(item.property.components);
}
volume.setMapper(mapper);
} else {
const actor = vtkActor.newInstance();
sceneItem.actor = actor;
if (item.texture && item.texture in model.usedTextures) {
// If this texture has already been used, re-use it
actor.addTexture(model.usedTextures[item.texture]);
} else if (item.texture) {
const url = [model.baseURL, item.texture].join('/');
const texture = vtkTexture.newInstance();
texture.setInterpolate(true);
texture.setRepeat(true);
actor.addTexture(texture);
sceneItem.texture = texture;
model.usedTextures[item.texture] = texture;
if (isImage(item.texture)) {
// It's an image file
model.dataAccessHelper.fetchImage({}, url, {
crossOrigin: 'anonymous'
}).then(img => {
texture.setImage(img);
});
} else {
// Assume it's a dataset file
const textureSource = type.newInstance({
fetchGzip: model.fetchGzip,
dataAccessHelper: model.dataAccessHelper
});
textureSource.setUrl(url, {
loadData: true
}).then(() => {
texture.setInputData(textureSource.getOutputData());
});
}
}
const {
textureLODs
} = item;
if (textureLODs && textureLODs.files && textureLODs.files.length !== 0) {
// If this texture LOD has already been used, re-use it
const textureLODsStr = JSON.stringify(textureLODs);
if (textureLODsStr in model.usedTextureLODs) {
actor.addTexture(model.usedTextureLODs[textureLODsStr]);
} else {
// Set it on the scene item so it can be accessed later, for
// doing things like setting a callback function.
sceneItem.textureLODsDownloader = vtkTextureLODsDownloader.newInstance();
const textureDownloader = sceneItem.textureLODsDownloader;
const texture = vtkTexture.newInstance();
texture.setInterpolate(true);
actor.addTexture(texture);
model.usedTextureLODs[textureLODsStr] = texture;
textureDownloader.setTexture(texture);
textureDownloader.setCrossOrigin('anonymous');
textureDownloader.setBaseUrl(textureLODs.baseUrl);
textureDownloader.setFiles(textureLODs.files);
if (model.startLODLoaders) {
textureDownloader.startDownloads();
}
}
}
if (model.renderer) {
model.renderer.addActor(actor);
}
actor.setMapper(mapper);
}
mapper.setInputConnection(source.getOutputPort());
source.setUrl([model.baseURL, item[item.type].url].join('/'), {
loadData: true
}).then(() => publicAPI.invokeReady());
applySettings(sceneItem, item);
model.scene.push(sceneItem);
const {
sourceLODs
} = item;
if (sourceLODs && sourceLODs.files && sourceLODs.files.length !== 0) {
// Set it on the scene item so it can be accessed later, for
// doing things like setting a callback function.
sceneItem.dataSetLODsLoader = vtkHttpDataSetLODsLoader.newInstance();
const {
dataSetLODsLoader
} = sceneItem;
dataSetLODsLoader.setMapper(mapper);
dataSetLODsLoader.setSceneItem(sceneItem);
dataSetLODsLoader.setBaseUrl(sourceLODs.baseUrl);
dataSetLODsLoader.setFiles(sourceLODs.files);
if (model.startLODLoaders) {
dataSetLODsLoader.startDownloads();
}
}
return sceneItem;
};
}
function loadTimeStepBasedAnimationHandler(data, model) {
model.animationHandler = vtkTimeStepBasedAnimationHandler.newInstance({
scene: model.scene,
originalMetadata: model.metadata,
applySettings
});
if (model.animationHandler && model.renderer) {
model.animationHandler.addRenderer(model.renderer);
}
model.animationHandler.setData(data);
}
// ----------------------------------------------------------------------------
// Note: keeping both types (with and without 'vtk' prefix) for backwards compatibility
// see https://gitlab.kitware.com/paraview/paraview/-/merge_requests/4628#note_876772
const TYPE_MAPPING = {
httpDataSetReader: defineLoadFuctionForReader(vtkHttpDataSetReader),
vtkHttpDataSetReader: defineLoadFuctionForReader(vtkHttpDataSetReader),
httpDataSetSeriesReader: defineLoadFuctionForReader(vtkHttpDataSetSeriesReader),
vtkHttpDataSetSeriesReader: defineLoadFuctionForReader(vtkHttpDataSetSeriesReader)
};
const ANIMATION_TYPE_MAPPING = {
timeStepBasedAnimationHandler: loadTimeStepBasedAnimationHandler,
vtkTimeStepBasedAnimationHandler: loadTimeStepBasedAnimationHandler
};
// ----------------------------------------------------------------------------
function updateDatasetTypeMapping(typeName, handler) {
TYPE_MAPPING[typeName] = handler;
}
// ----------------------------------------------------------------------------
// vtkHttpSceneLoader methods
// ----------------------------------------------------------------------------
function vtkHttpSceneLoader(publicAPI, model) {
const originalSceneParameters = {};
// These are here to re-use the same textures when possible
if (!model.usedTextures) {
model.usedTextures = {};
}
if (!model.usedTextureLODs) {
model.usedTextureLODs = {};
}
// Set our className
model.classHierarchy.push('vtkHttpSceneLoader');
// Create scene container
if (!model.scene) {
model.scene = [];
}
function setCameraParameters(params) {
if (model.renderer) {
const camera = model.renderer.getActiveCamera();
if (camera) {
camera.set(params);
} else {
vtkErrorMacro('No active camera to update');
}
}
}
function setBackground(color) {
if (model.renderer) {
model.renderer.setBackground(color);
}
}
// Create default dataAccessHelper if not available
if (!model.dataAccessHelper) {
model.dataAccessHelper = DataAccessHelper.get('http');
}
publicAPI.update = () => {
model.dataAccessHelper.fetchJSON(publicAPI, model.url).then(data => {
if (data.fetchGzip !== undefined) {
model.fetchGzip = data.fetchGzip;
}
if (data.background && model.renderer) {
setBackground(data.background);
}
if (data.camera) {
originalSceneParameters.camera = data.camera;
setCameraParameters(data.camera);
}
const luts = {};
if (data.lookupTables) {
Object.keys(data.lookupTables).forEach(fieldName => {
const config = data.lookupTables[fieldName];
const lookupTable = loadColorTransferFunction(config);
luts[fieldName] = lookupTable;
});
}
if (data.scene) {
data.scene.forEach(item => {
const builder = TYPE_MAPPING[item.type];
if (builder) {
builder({
luts,
...item
}, model, publicAPI);
}
});
global.scene = model.scene;
// Clear these
model.usedTextures = {};
model.usedTextureLODs = {};
}
// Capture index.json into meta
model.metadata = data;
if (data.animation) {
const animationLoader = ANIMATION_TYPE_MAPPING[data.animation.type];
animationLoader({
...data.animation
}, model, publicAPI, setCameraParameters, setBackground);
}
}, error => {
vtkErrorMacro(`Error fetching scene ${error}`);
});
};
publicAPI.resetScene = () => {
if (originalSceneParameters.camera) {
setCameraParameters(originalSceneParameters.camera);
}
};
// Set DataSet url
publicAPI.setUrl = url => {
if (url.indexOf('index.json') === -1) {
model.baseURL = url;
model.url = `${url}/index.json`;
} else {
model.url = url;
// Remove the file in the URL
const path = url.split('/');
path.pop();
model.baseURL = path.join('/');
}
// Fetch data
return publicAPI.update();
};
}
// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
const DEFAULT_VALUES = {
fetchGzip: false,
url: null,
baseURL: null,
animationHandler: null,
// Whether or not to automatically start texture LOD and poly LOD
// downloads when they are read.
startLODLoaders: true
};
// ----------------------------------------------------------------------------
function extend(publicAPI, model) {
let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
Object.assign(model, DEFAULT_VALUES, initialValues);
// Build VTK API
macro.obj(publicAPI, model);
macro.get(publicAPI, model, ['fetchGzip', 'url', 'baseURL', 'scene', 'metadata', 'animationHandler']);
macro.setGet(publicAPI, model, ['renderer']);
macro.event(publicAPI, model, 'ready');
// Object methods
vtkHttpSceneLoader(publicAPI, model);
}
// ----------------------------------------------------------------------------
const newInstance = macro.newInstance(extend, 'vtkHttpSceneLoader');
// ----------------------------------------------------------------------------
var vtkHttpSceneLoader$1 = {
newInstance,
extend,
applySettings,
updateDatasetTypeMapping
};
export { vtkHttpSceneLoader$1 as default, extend, newInstance };