UNPKG

vitessce

Version:

Vitessce app and React component library

166 lines (157 loc) 4.97 kB
import GL from '@luma.gl/constants'; // eslint-disable-line import/no-extraneous-dependencies import { project32, picking } from '@deck.gl/core'; // eslint-disable-line import/no-extraneous-dependencies import { Texture2D, isWebGL2 } from '@luma.gl/core'; import { XRLayer } from '@hms-dbmi/viv'; import { fs, vs } from './bitmask-layer-shaders'; import { GLSL_COLORMAPS, GLSL_COLORMAP_DEFAULT, COLORMAP_SHADER_PLACEHOLDER, } from './constants'; function padWithDefault(arr, defaultValue, padWidth) { const newArr = [...arr]; for (let i = 0; i < padWidth; i += 1) { newArr.push(defaultValue); } return newArr; } const defaultProps = { hoveredCell: { type: 'number', value: null, compare: true }, cellColorData: { type: 'object', value: null, compare: true }, colormap: { type: 'string', value: GLSL_COLORMAP_DEFAULT, compare: true }, expressionData: { type: 'object', value: null, compare: true }, }; /** * A BitmapLayer that performs aggregation in the fragment shader, * and renders its texture from a Uint8Array rather than an ImageData. */ export default class BitmaskLayer extends XRLayer { // eslint-disable-next-line class-methods-use-this getShaders() { const { colormap } = this.props; return { fs, vs, modules: [project32, picking], defines: { [COLORMAP_SHADER_PLACEHOLDER]: GLSL_COLORMAPS.includes(colormap) ? colormap : GLSL_COLORMAP_DEFAULT, }, }; } updateState({ props, oldProps, changeFlags }) { super.updateState({ props, oldProps, changeFlags }); if (props.cellColorData !== oldProps.cellColorData) { this.setColorTexture(); } if (props.expressionData !== oldProps.expressionData) { const { expressionData, cellTexHeight, cellTexWidth } = this.props; const expressionTex = this.dataToTexture( expressionData, cellTexWidth, cellTexHeight, ); this.setState({ expressionTex }); } if (props.colormap !== oldProps.colormap) { const { gl } = this.context; if (this.state.model) { this.state.model.delete(); } // eslint-disable-next-line no-underscore-dangle this.setState({ model: this._getModel(gl) }); this.getAttributeManager().invalidateAll(); } } setColorTexture() { const { cellColorData: data, cellTexHeight: height, cellTexWidth: width, } = this.props; const colorTex = new Texture2D(this.context.gl, { width, height, // Only use Float32 so we don't have to write two shaders data, // we don't want or need mimaps mipmaps: false, parameters: { // NEAREST for integer data [GL.TEXTURE_MIN_FILTER]: GL.NEAREST, [GL.TEXTURE_MAG_FILTER]: GL.NEAREST, // CLAMP_TO_EDGE to remove tile artifacts [GL.TEXTURE_WRAP_S]: GL.CLAMP_TO_EDGE, [GL.TEXTURE_WRAP_T]: GL.CLAMP_TO_EDGE, }, format: GL.RGB, dataFormat: GL.RGB, type: GL.UNSIGNED_BYTE, }); this.setState({ colorTex }); } draw(opts) { const { uniforms } = opts; const { channelsVisible, hoveredCell, colorScaleLo, colorScaleHi, isExpressionMode, } = this.props; const { textures, model, colorTex, expressionTex, } = this.state; // Render the image if (textures && model && colorTex) { model .setUniforms( Object.assign({}, uniforms, { hovered: hoveredCell || 0, colorTex, expressionTex, colorTexHeight: colorTex.height, colorTexWidth: colorTex.width, channelsVisible: padWithDefault( channelsVisible, false, // There are six texture entries on the shaders 6 - channelsVisible.length, ), uColorScaleRange: [colorScaleLo, colorScaleHi], uIsExpressionMode: isExpressionMode, ...textures, }), ) .draw(); } } /** * This function creates textures from the data */ dataToTexture(data, width, height) { const isWebGL2On = isWebGL2(this.context.gl); return new Texture2D(this.context.gl, { width, height, // Only use Float32 so we don't have to write two shaders data: new Float32Array(data), // we don't want or need mimaps mipmaps: false, parameters: { // NEAREST for integer data [GL.TEXTURE_MIN_FILTER]: GL.NEAREST, [GL.TEXTURE_MAG_FILTER]: GL.NEAREST, // CLAMP_TO_EDGE to remove tile artifacts [GL.TEXTURE_WRAP_S]: GL.CLAMP_TO_EDGE, [GL.TEXTURE_WRAP_T]: GL.CLAMP_TO_EDGE, }, format: isWebGL2On ? GL.R32F : GL.LUMINANCE, dataFormat: isWebGL2On ? GL.RED : GL.LUMINANCE, type: GL.FLOAT, }); } } BitmaskLayer.layerName = 'BitmaskLayer'; BitmaskLayer.defaultProps = defaultProps;