@thewtex/vtk.js-esm
Version:
Visualization Toolkit for the Web
604 lines (515 loc) • 21.4 kB
JavaScript
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import macro from '../../../macro.js';
import vtkOpenGLTexture from '../Texture.js';
import vtkDataArray from '../../../Common/Core/DataArray.js';
import vtkHelper from '../Helper.js';
import vtkProperty from '../../Core/Property.js';
import vtkVertexArrayObject from '../VertexArrayObject.js';
import vtkOpenGLFramebuffer from '../Framebuffer.js';
import vtkLineIntegralConvolution2D from './LineIntegralConvolution2D.js';
import { v as vtkLineIntegralConvolution2D_quadVS } from './glsl/vtkLineIntegralConvolution2D_quadVS.glsl.js';
import { v as vtkLineIntegralConvolution2D_SC } from './glsl/vtkLineIntegralConvolution2D_SC.glsl.js';
import { v as vtkSurfaceLICInterface_DCpy } from './glsl/vtkSurfaceLICInterface_DCpy.glsl.js';
import { v as vtkSurfaceLICInterface_CE } from './glsl/vtkSurfaceLICInterface_CE.glsl.js';
import { s as seedrandom } from '../../../vendor/seedrandom/index.js';
import { NoiseType, ContrastEnhanceMode } from '../../Core/SurfaceLICInterface/Constants.js';
import vtkSurfaceLICInterface from '../../Core/SurfaceLICInterface.js';
var Representation = vtkProperty.Representation; // ----------------------------------------------------------------------------
// vtkLICInterface methods
// ----------------------------------------------------------------------------
function getQuadPoly(openGLRenderWindow) {
var quad = vtkHelper.newInstance();
quad.setOpenGLRenderWindow(openGLRenderWindow); // build the CABO
var ptsArray = new Float32Array(12);
for (var i = 0; i < 4; i++) {
ptsArray[i * 3] = i % 2 * 2 - 1.0;
ptsArray[i * 3 + 1] = i > 1 ? 1.0 : -1.0;
ptsArray[i * 3 + 2] = 0.0;
}
var tCoord = new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0]);
var cellArray = new Uint16Array(8);
cellArray[0] = 3;
cellArray[1] = 0;
cellArray[2] = 1;
cellArray[3] = 3;
cellArray[4] = 3;
cellArray[5] = 0;
cellArray[6] = 3;
cellArray[7] = 2;
var points = vtkDataArray.newInstance({
numberOfComponents: 3,
values: ptsArray
});
points.setName('points');
var cells = vtkDataArray.newInstance({
numberOfComponents: 1,
values: cellArray
});
var tArray = vtkDataArray.newInstance({
numberOfComponents: 2,
values: tCoord
});
quad.getCABO().createVBO(cells, 'polys', Representation.SURFACE, {
points: points,
cellOffset: 0,
tcoords: tArray
});
return quad;
}
function vtkOpenGLSurfaceLICInterface(publicAPI, model) {
model.classHierarchy.push('vtkOpenGLSurfaceLICInterface');
publicAPI.renderQuad = function (bounds, program) {
var poly = model.licQuad;
var gl = model.context;
var VAO = model.licQuadVAO;
if (!VAO) {
VAO = vtkVertexArrayObject.newInstance();
VAO.setOpenGLRenderWindow(model.openGLRenderWindow);
model.licQuadVAO = VAO;
}
if (model.previousProgramHash !== program.getMd5Hash()) {
VAO.shaderProgramChanged();
poly.getCABO().bind();
VAO.addAttributeArray(program, poly.getCABO(), 'vertexDC', poly.getCABO().getVertexOffset(), poly.getCABO().getStride(), model.context.FLOAT, 3, model.context.FALSE);
VAO.addAttributeArray(program, poly.getCABO(), 'tcoordDC', poly.getCABO().getTCoordOffset(), poly.getCABO().getStride(), model.context.FLOAT, 2, model.context.FALSE);
model.previousProgramHash = program.getMd5Hash();
}
gl.drawArrays(gl.TRIANGLES, 0, poly.getCABO().getElementCount());
VAO.release();
};
function generateGaussianNoise(length, numberOfNoiseLevels, noiseImpulseProbability, noiseImpulseBackgroundValue, min, max) {
var N = 2048;
var impulseProb = Math.max(0.0, Math.min(1.0, noiseImpulseProbability));
var noise = Float32Array.from({
length: length * length
}, function () {
var val = 0;
if (impulseProb === 1.0 || Math.random() > 1.0 - impulseProb) {
for (var i = 0; i < N; ++i) {
val += Math.random();
}
}
return val;
}); // Normalize
var maxVal = 0.0;
var minVal = N + 1;
noise.forEach(function (val) {
// Don't count 0s for minVal if impulseProb < 1.0
if (impulseProb === 1.0) {
minVal = val < minVal ? val : minVal;
} else {
minVal = val < minVal && val > 0.0 ? val : minVal;
}
maxVal = val > maxVal ? val : maxVal;
});
var diff = maxVal - minVal;
if (diff === 0.0) {
minVal = 0.0;
if (maxVal === 0.0) {
diff = 1.0;
} else {
diff = maxVal;
}
}
var maxLevel = numberOfNoiseLevels - 1;
var delta = maxLevel !== 0 ? 1.0 / maxLevel : 0.0;
var noiseRange = max - min;
return noise.map(function (val) {
var normalized = val < minVal ? val : (val - minVal) / diff;
var l = Math.floor(normalized * numberOfNoiseLevels);
var quantized = l > maxLevel ? maxLevel : l;
if (val >= minVal) {
if (numberOfNoiseLevels === 1) {
return max;
}
return min + quantized * delta * noiseRange;
}
return noiseImpulseBackgroundValue;
});
}
function generateUniformNoise(_ref, numberOfNoiseLevels, min, max) {
var _ref2 = _slicedToArray(_ref, 2),
width = _ref2[0],
height = _ref2[1];
var diff = max - min;
return Float32Array.from({
length: width * height
}, function () {
var r = Math.random();
r = Math.floor(r * numberOfNoiseLevels) / numberOfNoiseLevels;
r = r * diff + min;
if (r > 1.0) {
return 1.0;
}
if (r < 0.0) {
return 0.0;
}
return r;
});
}
publicAPI.generateNoiseTexture = function (length) {
if (!model.noiseTexture || model.licInterface.getRebuildNoiseTexture()) {
model.licInterface.setRebuildNoiseTexture(false);
if (model.noiseTexture) {
model.noiseTexture.releaseGraphicsResources();
} // Reseed RNG
seedrandom(model.noiseGeneratorSeed, {
global: true
});
var base = [];
var _model$licInterface$g = model.licInterface.get('noiseTextureType', 'noiseGrainSize', 'numberOfNoiseLevels', 'noiseImpulseProbability', 'noiseImpulseBackgroundValue', 'minNoiseValue', 'maxNoiseValue'),
noiseTextureType = _model$licInterface$g.noiseTextureType,
noiseGrainSize = _model$licInterface$g.noiseGrainSize,
numberOfNoiseLevels = _model$licInterface$g.numberOfNoiseLevels,
noiseImpulseProbability = _model$licInterface$g.noiseImpulseProbability,
noiseImpulseBackgroundValue = _model$licInterface$g.noiseImpulseBackgroundValue,
minNoiseValue = _model$licInterface$g.minNoiseValue,
maxNoiseValue = _model$licInterface$g.maxNoiseValue;
switch (noiseTextureType) {
case NoiseType.GAUSSIAN:
base = generateGaussianNoise(Math.floor(length / noiseGrainSize), numberOfNoiseLevels, noiseImpulseProbability, noiseImpulseBackgroundValue, minNoiseValue, maxNoiseValue);
break;
case NoiseType.UNIFORM:
default:
base = generateUniformNoise([Math.ceil(length / noiseGrainSize), Math.ceil(length / noiseGrainSize)], numberOfNoiseLevels, minNoiseValue, maxNoiseValue);
}
var invGrainSize = 1.0 / noiseGrainSize;
var values = Float32Array.from({
length: length * length * 4
}, function (val, index) {
var baseIndex = index / 4;
if (index % 4 === 0) {
var x = Math.floor(baseIndex % length * invGrainSize);
var y = Math.floor(baseIndex / length * invGrainSize);
return base[y * (length / noiseGrainSize) + x];
}
if (index % 4 === 1 || index % 4 === 3) {
return 1.0;
}
return 0.0;
});
var texture = vtkOpenGLTexture.newInstance({
wrapS: vtkOpenGLTexture.Wrap.REPEAT,
wrapT: vtkOpenGLTexture.Wrap.REPEAT,
minificationFilter: vtkOpenGLTexture.Filter.NEAREST,
magnificationFilter: vtkOpenGLTexture.Filter.NEAREST,
generateMipMap: false,
openGLDataType: model.context.FLOAT,
baseLevel: 0,
maxLevel: 0,
autoParameters: false
});
texture.setOpenGLRenderWindow(model.openGLRenderWindow);
texture.create2DFromRaw(length, length, 4, 'Float32Array', values);
texture.activate();
texture.sendParameters();
texture.deactivate();
model.noiseTexture = texture;
}
};
publicAPI.buildAShader = function (fSource) {
return model.openGLRenderWindow.getShaderCache().readyShaderProgramArray(vtkLineIntegralConvolution2D_quadVS, fSource, '');
};
publicAPI.allocateTextures = function () {
var nearest = vtkOpenGLTexture.Filter.NEAREST;
var linear = vtkOpenGLTexture.Filter.LINEAR;
var rw = model.openGLRenderWindow;
if (!model.geometryImage) {
model.geometryImage = publicAPI.allocateTexture(rw, nearest);
}
if (!model.vectorImage) {
model.vectorImage = publicAPI.allocateTexture(rw, linear);
}
if (!model.maskVectorImage) {
model.maskVectorImage = publicAPI.allocateTexture(rw, linear);
}
if (!model.LICImage) {
model.LICImage = publicAPI.allocateTexture(rw, nearest);
}
if (!model.RGBColorImage) {
model.RGBColorImage = publicAPI.allocateTexture(rw, nearest);
}
if (!model.HSLColorImage) {
model.HSLColorImage = publicAPI.allocateTexture(rw, nearest);
}
if (!model.depthTexture) {
model.depthTexture = publicAPI.allocateDepthTexture(rw);
}
};
publicAPI.allocateTexture = function (openGLRenderWindow, filter) {
var gl = model.context;
var texture = vtkOpenGLTexture.newInstance({
wrapS: vtkOpenGLTexture.Wrap.CLAMP_TO_EDGE,
wrapT: vtkOpenGLTexture.Wrap.CLAMP_TO_EDGE,
minificationFilter: filter,
magnificationFilter: filter,
generateMipmap: false,
openGLDataType: gl.FLOAT,
baseLevel: 0,
maxLevel: 0,
autoParameters: false
});
texture.setOpenGLRenderWindow(openGLRenderWindow);
texture.setInternalFormat(gl.RGBA32F);
texture.create2DFromRaw.apply(texture, _toConsumableArray(model.size).concat([4, 'Float32Array', null]));
texture.activate();
texture.sendParameters();
texture.deactivate();
return texture;
};
publicAPI.allocateDepthTexture = function (openGLRenderWindow) {
var gl = model.context;
var texture = vtkOpenGLTexture.newInstance({
generateMipmap: false,
openGLDataType: gl.FLOAT,
autoParameters: false
});
texture.setOpenGLRenderWindow(openGLRenderWindow);
texture.createDepthFromRaw.apply(texture, _toConsumableArray(model.size).concat(['Float32Array', null]));
texture.activate();
texture.sendParameters();
texture.deactivate();
return texture;
};
publicAPI.createFBO = function () {
if (!model.framebuffer) {
model.licHelper = null; // All buffers need rebuilding
var fb = vtkOpenGLFramebuffer.newInstance();
fb.setOpenGLRenderWindow(model.openGLRenderWindow);
fb.saveCurrentBindingsAndBuffers();
fb.create.apply(fb, _toConsumableArray(model.size));
fb.populateFramebuffer();
model.framebuffer = fb;
fb.restorePreviousBindingsAndBuffers();
}
};
publicAPI.completedGeometry = function () {
var gl = model.context;
var fb = model.framebuffer;
fb.removeColorBuffer(0);
fb.removeColorBuffer(1);
fb.removeColorBuffer(2);
fb.removeDepthBuffer();
gl.drawBuffers([gl.NONE]);
fb.restorePreviousBindingsAndBuffers();
};
publicAPI.buildAllShaders = function () {
if (model.shadersNeedBuilding) {
model.licColorPass = publicAPI.buildAShader(vtkLineIntegralConvolution2D_SC);
model.licCopyPass = publicAPI.buildAShader(vtkSurfaceLICInterface_DCpy);
model.enhanceContrastPass = publicAPI.buildAShader(vtkSurfaceLICInterface_CE);
model.shadersNeedBuilding = false;
}
};
publicAPI.initializeResources = function () {
publicAPI.createFBO();
publicAPI.generateNoiseTexture(model.licInterface.getNoiseTextureSize());
publicAPI.allocateTextures();
publicAPI.buildAllShaders();
if (!model.licQuad) {
model.licQuad = getQuadPoly(model.openGLRenderWindow);
}
if (!model.licHelper) {
model.licHelper = vtkLineIntegralConvolution2D.newInstance();
}
};
publicAPI.prepareForGeometry = function () {
var fb = model.framebuffer;
fb.saveCurrentBindingsAndBuffers();
fb.bind();
model.geometryImage.activate();
model.vectorImage.activate();
model.maskVectorImage.activate();
fb.removeColorBuffer(0);
fb.removeColorBuffer(1);
fb.removeColorBuffer(2);
fb.setColorBuffer(model.geometryImage, 0);
fb.setColorBuffer(model.vectorImage, 1);
fb.setColorBuffer(model.maskVectorImage, 2);
fb.setDepthBuffer(model.depthTexture);
var gl = model.context;
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]);
gl.viewport.apply(gl, [0, 0].concat(_toConsumableArray(model.size)));
gl.scissor.apply(gl, [0, 0].concat(_toConsumableArray(model.size)));
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.SCISSOR_TEST);
gl.clearColor(0.0, 0.0, 0.0, 0.0); // eslint-disable-next-line no-bitwise
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
}; // Copies the LIC image to the renderWindow. Will try to upscale the image to match the rw's size.
publicAPI.copyToScreen = function (windowSize) {
model.RGBColorImage.activate();
model.depthTexture.activate();
if (!model.licCopyPass) {
publicAPI.initializeResources();
}
var copyPass = model.licCopyPass;
model.openGLRenderWindow.getShaderCache().readyShaderProgram(copyPass);
var gl = model.context;
gl.viewport.apply(gl, [0, 0].concat(_toConsumableArray(windowSize)));
gl.scissor.apply(gl, [0, 0].concat(_toConsumableArray(windowSize)));
gl.disable(gl.BLEND);
gl.enable(gl.DEPTH_TEST);
gl.disable(gl.SCISSOR_TEST);
copyPass.setUniformi('texDepth', model.depthTexture.getTextureUnit());
copyPass.setUniformi('texRGBColors', model.RGBColorImage.getTextureUnit());
publicAPI.renderQuad(windowSize, copyPass);
model.RGBColorImage.deactivate();
model.depthTexture.deactivate();
};
publicAPI.combineColorsAndLIC = function () {
var gl = model.context;
var fb = model.framebuffer;
fb.saveCurrentBindingsAndBuffers();
fb.bind();
fb.create.apply(fb, _toConsumableArray(model.size));
fb.removeColorBuffer(0);
fb.removeColorBuffer(1);
fb.setColorBuffer(model.RGBColorImage, 0);
fb.setColorBuffer(model.HSLColorImage, 1);
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
gl.disable(gl.DEPTH_TEST);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
model.vectorImage.activate();
model.geometryImage.activate();
model.LICImage.activate();
if (!model.licColorPass) {
publicAPI.initializeResources();
}
var colorPass = model.licColorPass;
model.openGLRenderWindow.getShaderCache().readyShaderProgram(colorPass);
colorPass.setUniformi('texVectors', model.vectorImage.getTextureUnit());
colorPass.setUniformi('texGeomColors', model.geometryImage.getTextureUnit());
var _model$licInterface$g2 = model.licInterface.get('colorMode', 'LICIntensity', 'mapModeBias', 'maskIntensity', 'maskColor', 'enhanceContrast', 'lowColorContrastEnhancementFactor', 'highColorContrastEnhancementFactor'),
colorMode = _model$licInterface$g2.colorMode,
LICIntensity = _model$licInterface$g2.LICIntensity,
mapModeBias = _model$licInterface$g2.mapModeBias,
maskIntensity = _model$licInterface$g2.maskIntensity,
maskColor = _model$licInterface$g2.maskColor,
enhanceContrast = _model$licInterface$g2.enhanceContrast,
lowColorContrastEnhancementFactor = _model$licInterface$g2.lowColorContrastEnhancementFactor,
highColorContrastEnhancementFactor = _model$licInterface$g2.highColorContrastEnhancementFactor;
colorPass.setUniformi('texLIC', model.LICImage.getTextureUnit());
colorPass.setUniformi('uScalarColorMode', colorMode);
colorPass.setUniformf('uLICIntensity', LICIntensity);
colorPass.setUniformf('uMapBias', mapModeBias);
colorPass.setUniformf('uMaskIntensity', maskIntensity);
colorPass.setUniform3f.apply(colorPass, ['uMaskColor'].concat(_toConsumableArray(maskColor)));
publicAPI.renderQuad(model.size, colorPass);
model.vectorImage.deactivate();
model.geometryImage.deactivate();
model.LICImage.deactivate();
fb.removeColorBuffer(0);
fb.removeColorBuffer(1);
gl.drawBuffers([gl.NONE]);
if (enhanceContrast === ContrastEnhanceMode.COLOR || enhanceContrast === ContrastEnhanceMode.BOTH) {
// min and max luminance values. Most of the time close to 0 and 1
var min = 0.0;
var max = 1.0;
var lDiff = max - min;
min += lDiff * lowColorContrastEnhancementFactor;
max -= lDiff * highColorContrastEnhancementFactor;
lDiff = max - min;
fb.setColorBuffer(model.RGBColorImage);
gl.drawBuffers([gl.COLOR_ATTACHMENT0]);
model.geometryImage.activate();
model.HSLColorImage.activate();
model.LICImage.activate();
if (!model.enhanceContrastPass) {
publicAPI.initializeResources();
}
var enhanceContrastPass = model.enhanceContrastPass;
model.openGLRenderWindow.getShaderCache().readyShaderProgram(enhanceContrastPass);
enhanceContrastPass.setUniformi('texGeomColors', model.geometryImage.getTextureUnit());
enhanceContrastPass.setUniformi('texHSLColors', model.HSLColorImage.getTextureUnit());
enhanceContrastPass.setUniformi('texLIC', model.LICImage.getTextureUnit());
enhanceContrastPass.setUniformf('uLMin', min);
enhanceContrastPass.setUniformf('uLMaxMinDiff', lDiff);
publicAPI.renderQuad(model.size, enhanceContrastPass);
model.geometryImage.deactivate();
model.HSLColorImage.deactivate();
model.LICImage.deactivate();
fb.removeColorBuffer(0);
gl.drawBuffers([gl.NONE]);
}
fb.restorePreviousBindingsAndBuffers();
};
publicAPI.applyLIC = function () {
var options = model.licInterface.get('stepSize', 'numberOfSteps', 'enhancedLIC', 'enhanceContrast', 'lowLICContrastEnhancementFactor', 'highLICContrastEnhancementFactor', 'antiAlias', 'normalizeVectors', 'maskThreshold', 'transformVectors');
var resultTexture = model.licHelper.executeLIC(model.size, model.vectorImage, model.maskVectorImage, model.noiseTexture, model.openGLRenderWindow, options);
if (!resultTexture) {
console.error('Failed to compute image LIC');
model.LICImage = null;
return;
}
model.LICImage = resultTexture;
};
publicAPI.setSize = function (size) {
// If size changed, reallocate fb and textures
if (Array.isArray(size) && size.length === 2) {
if (!model.size || model.size[0] !== size[0] || model.size[1] !== size[1]) {
model.size = size;
publicAPI.releaseGraphicsResources();
}
}
};
publicAPI.releaseGraphicsResources = function () {
if (model.geometryImage) {
model.geometryImage.releaseGraphicsResources();
model.geometryImage = null;
}
if (model.vectorImage) {
model.vectorImage.releaseGraphicsResources();
model.vectorImage = null;
}
if (model.maskVectorImage) {
model.maskVectorImage.releaseGraphicsResources();
model.maskVectorImage = null;
}
if (model.LICImage) {
model.LICImage.releaseGraphicsResources();
model.LICImage = null;
}
if (model.RGBColorImage) {
model.RGBColorImage.releaseGraphicsResources();
model.RGBColorImage = null;
}
if (model.HSLColorImage) {
model.HSLColorImage.releaseGraphicsResources();
model.HSLColorImage = null;
}
if (model.depthTexture) {
model.depthTexture.releaseGraphicsResources();
model.depthTexture = null;
}
if (model.framebuffer) {
model.framebuffer.releaseGraphicsResources();
model.framebuffer = null;
}
};
}
var DEFAULT_VALUES = {
context: null,
openGLRenderWindow: null,
shadersNeedBuilding: true,
reallocateTextures: true,
size: null,
licInterface: null
};
function extend(publicAPI, model) {
var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
Object.assign(model, DEFAULT_VALUES, initialValues); // Inherit
vtkSurfaceLICInterface.extend(publicAPI, model, initialValues);
macro.obj(publicAPI, model);
macro.setGet(publicAPI, model, ['context', 'openGLRenderWindow', 'reallocateTextures', 'licInterface', 'size']); // Object methods
vtkOpenGLSurfaceLICInterface(publicAPI, model);
} // ----------------------------------------------------------------------------
var newInstance = macro.newInstance(extend, 'vtkSurfaceLICInterface'); // ----------------------------------------------------------------------------
var vtkOpenGLSurfaceLICInterface$1 = {
newInstance: newInstance,
extend: extend
};
export default vtkOpenGLSurfaceLICInterface$1;
export { extend, newInstance };