@vitessce/gl 2.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,132 @@
1
+ import GL from '@luma.gl/constants'; // eslint-disable-line import/no-extraneous-dependencies
2
+ import { project32, picking } from '@deck.gl/core'; // eslint-disable-line import/no-extraneous-dependencies
3
+ import { Texture2D, isWebGL2 } from '@luma.gl/core';
4
+ import { XRLayer } from '@hms-dbmi/viv';
5
+ import { fs, vs } from './bitmask-layer-shaders';
6
+ import { GLSL_COLORMAPS, GLSL_COLORMAP_DEFAULT, COLORMAP_SHADER_PLACEHOLDER, } from './constants';
7
+ function padWithDefault(arr, defaultValue, padWidth) {
8
+ const newArr = [...arr];
9
+ for (let i = 0; i < padWidth; i += 1) {
10
+ newArr.push(defaultValue);
11
+ }
12
+ return newArr;
13
+ }
14
+ const defaultProps = {
15
+ hoveredCell: { type: 'number', value: null, compare: true },
16
+ cellColorData: { type: 'object', value: null, compare: true },
17
+ colormap: { type: 'string', value: GLSL_COLORMAP_DEFAULT, compare: true },
18
+ expressionData: { type: 'object', value: null, compare: true },
19
+ };
20
+ /**
21
+ * A BitmapLayer that performs aggregation in the fragment shader,
22
+ * and renders its texture from a Uint8Array rather than an ImageData.
23
+ */
24
+ export default class BitmaskLayer extends XRLayer {
25
+ // eslint-disable-next-line class-methods-use-this
26
+ getShaders() {
27
+ const { colormap } = this.props;
28
+ return {
29
+ fs,
30
+ vs,
31
+ modules: [project32, picking],
32
+ defines: {
33
+ [COLORMAP_SHADER_PLACEHOLDER]: GLSL_COLORMAPS.includes(colormap)
34
+ ? colormap
35
+ : GLSL_COLORMAP_DEFAULT,
36
+ },
37
+ };
38
+ }
39
+ updateState({ props, oldProps, changeFlags }) {
40
+ super.updateState({ props, oldProps, changeFlags });
41
+ if (props.cellColorData !== oldProps.cellColorData) {
42
+ this.setColorTexture();
43
+ }
44
+ if (props.expressionData !== oldProps.expressionData) {
45
+ const { expressionData, cellTexHeight, cellTexWidth } = this.props;
46
+ const expressionTex = this.dataToTexture(expressionData, cellTexWidth, cellTexHeight);
47
+ this.setState({ expressionTex });
48
+ }
49
+ if (props.colormap !== oldProps.colormap) {
50
+ const { gl } = this.context;
51
+ if (this.state.model) {
52
+ this.state.model.delete();
53
+ }
54
+ // eslint-disable-next-line no-underscore-dangle
55
+ this.setState({ model: this._getModel(gl) });
56
+ this.getAttributeManager().invalidateAll();
57
+ }
58
+ }
59
+ setColorTexture() {
60
+ const { cellColorData: data, cellTexHeight: height, cellTexWidth: width, } = this.props;
61
+ const colorTex = new Texture2D(this.context.gl, {
62
+ width,
63
+ height,
64
+ // Only use Float32 so we don't have to write two shaders
65
+ data,
66
+ // we don't want or need mimaps
67
+ mipmaps: false,
68
+ parameters: {
69
+ // NEAREST for integer data
70
+ [GL.TEXTURE_MIN_FILTER]: GL.NEAREST,
71
+ [GL.TEXTURE_MAG_FILTER]: GL.NEAREST,
72
+ // CLAMP_TO_EDGE to remove tile artifacts
73
+ [GL.TEXTURE_WRAP_S]: GL.CLAMP_TO_EDGE,
74
+ [GL.TEXTURE_WRAP_T]: GL.CLAMP_TO_EDGE,
75
+ },
76
+ format: GL.RGB,
77
+ dataFormat: GL.RGB,
78
+ type: GL.UNSIGNED_BYTE,
79
+ });
80
+ this.setState({ colorTex });
81
+ }
82
+ draw(opts) {
83
+ const { uniforms } = opts;
84
+ const { channelsVisible, hoveredCell, colorScaleLo, colorScaleHi, isExpressionMode, } = this.props;
85
+ const { textures, model, colorTex, expressionTex, } = this.state;
86
+ // Render the image
87
+ if (textures && model && colorTex) {
88
+ model
89
+ .setUniforms(Object.assign({}, uniforms, {
90
+ hovered: hoveredCell || 0,
91
+ colorTex,
92
+ expressionTex,
93
+ colorTexHeight: colorTex.height,
94
+ colorTexWidth: colorTex.width,
95
+ channelsVisible: padWithDefault(channelsVisible, false,
96
+ // There are six texture entries on the shaders
97
+ 6 - channelsVisible.length),
98
+ uColorScaleRange: [colorScaleLo, colorScaleHi],
99
+ uIsExpressionMode: isExpressionMode,
100
+ ...textures,
101
+ }))
102
+ .draw();
103
+ }
104
+ }
105
+ /**
106
+ * This function creates textures from the data
107
+ */
108
+ dataToTexture(data, width, height) {
109
+ const isWebGL2On = isWebGL2(this.context.gl);
110
+ return new Texture2D(this.context.gl, {
111
+ width,
112
+ height,
113
+ // Only use Float32 so we don't have to write two shaders
114
+ data: new Float32Array(data),
115
+ // we don't want or need mimaps
116
+ mipmaps: false,
117
+ parameters: {
118
+ // NEAREST for integer data
119
+ [GL.TEXTURE_MIN_FILTER]: GL.NEAREST,
120
+ [GL.TEXTURE_MAG_FILTER]: GL.NEAREST,
121
+ // CLAMP_TO_EDGE to remove tile artifacts
122
+ [GL.TEXTURE_WRAP_S]: GL.CLAMP_TO_EDGE,
123
+ [GL.TEXTURE_WRAP_T]: GL.CLAMP_TO_EDGE,
124
+ },
125
+ format: isWebGL2On ? GL.R32F : GL.LUMINANCE,
126
+ dataFormat: isWebGL2On ? GL.RED : GL.LUMINANCE,
127
+ type: GL.FLOAT,
128
+ });
129
+ }
130
+ }
131
+ BitmaskLayer.layerName = 'BitmaskLayer';
132
+ BitmaskLayer.defaultProps = defaultProps;
@@ -0,0 +1,123 @@
1
+ import GL from '@luma.gl/constants'; // eslint-disable-line import/no-extraneous-dependencies
2
+ import { _mergeShaders, project32, picking } from '@deck.gl/core'; // eslint-disable-line import/no-extraneous-dependencies
3
+ import { BitmapLayer } from '@deck.gl/layers'; // eslint-disable-line import/no-extraneous-dependencies
4
+ import { Texture2D } from '@luma.gl/core';
5
+ import { PIXELATED_TEXTURE_PARAMETERS, TILE_SIZE } from './heatmap-constants';
6
+ import { GLSL_COLORMAPS, GLSL_COLORMAP_DEFAULT, COLORMAP_SHADER_PLACEHOLDER, } from './constants';
7
+ import { vertexShader, fragmentShader } from './heatmap-bitmap-layer-shaders';
8
+ const defaultProps = {
9
+ image: { type: 'object', value: null, async: true },
10
+ colormap: { type: 'string', value: GLSL_COLORMAP_DEFAULT, compare: true },
11
+ bounds: { type: 'array', value: [1, 0, 0, 1], compare: true },
12
+ aggSizeX: { type: 'number', value: 8.0, compare: true },
13
+ aggSizeY: { type: 'number', value: 8.0, compare: true },
14
+ colorScaleLo: { type: 'number', value: 0.0, compare: true },
15
+ colorScaleHi: { type: 'number', value: 1.0, compare: true },
16
+ };
17
+ /**
18
+ * A BitmapLayer that performs aggregation in the fragment shader,
19
+ * and renders its texture from a Uint8Array rather than an ImageData.
20
+ */
21
+ export default class HeatmapBitmapLayer extends BitmapLayer {
22
+ /**
23
+ * Copy of getShaders from Layer (grandparent, parent of BitmapLayer).
24
+ * Reference: https://github.com/visgl/deck.gl/blob/0afd4e99a6199aeec979989e0c361c97e6c17a16/modules/core/src/lib/layer.js#L302
25
+ * @param {object} shaders
26
+ * @returns {object} Merged shaders.
27
+ */
28
+ // eslint-disable-next-line no-underscore-dangle
29
+ _getShaders(shaders) {
30
+ this.props.extensions.forEach((extension) => {
31
+ // eslint-disable-next-line no-param-reassign
32
+ shaders = _mergeShaders(shaders, extension.getShaders.call(this, extension));
33
+ });
34
+ return shaders;
35
+ }
36
+ /**
37
+ * Need to override to provide custom shaders.
38
+ */
39
+ getShaders() {
40
+ const { colormap } = this.props;
41
+ const fragmentShaderWithColormap = GLSL_COLORMAPS.includes(colormap)
42
+ ? fragmentShader.replace(COLORMAP_SHADER_PLACEHOLDER, colormap)
43
+ : fragmentShader.replace(COLORMAP_SHADER_PLACEHOLDER, GLSL_COLORMAP_DEFAULT);
44
+ // eslint-disable-next-line no-underscore-dangle
45
+ return this._getShaders({
46
+ vs: vertexShader,
47
+ fs: fragmentShaderWithColormap,
48
+ modules: [project32, picking],
49
+ });
50
+ }
51
+ updateState(args) {
52
+ super.updateState(args);
53
+ this.loadTexture(this.props.image);
54
+ const { props, oldProps } = args;
55
+ if (props.colormap !== oldProps.colormap) {
56
+ const { gl } = this.context;
57
+ // eslint-disable-next-line no-unused-expressions
58
+ this.state.model?.delete();
59
+ // eslint-disable-next-line no-underscore-dangle
60
+ this.state.model = this._getModel(gl);
61
+ this.getAttributeManager().invalidateAll();
62
+ }
63
+ }
64
+ /**
65
+ * Need to override to provide additional uniform values.
66
+ * Simplified by removing video-related code.
67
+ * Reference: https://github.com/visgl/deck.gl/blob/0afd4e99a6199aeec979989e0c361c97e6c17a16/modules/layers/src/bitmap-layer/bitmap-layer.js#L173
68
+ * @param {*} opts
69
+ */
70
+ draw(opts) {
71
+ const { uniforms } = opts;
72
+ const { bitmapTexture, model } = this.state;
73
+ const { aggSizeX, aggSizeY, colorScaleLo, colorScaleHi, } = this.props;
74
+ // Render the image
75
+ if (bitmapTexture && model) {
76
+ model
77
+ .setUniforms(Object.assign({}, uniforms, {
78
+ uBitmapTexture: bitmapTexture,
79
+ uTextureSize: [TILE_SIZE, TILE_SIZE],
80
+ uAggSize: [aggSizeX, aggSizeY],
81
+ uColorScaleRange: [colorScaleLo, colorScaleHi],
82
+ }))
83
+ .draw();
84
+ }
85
+ }
86
+ /**
87
+ * Need to override to provide the custom DEFAULT_TEXTURE_PARAMETERS
88
+ * object.
89
+ * Simplified by removing video-related code.
90
+ * Reference: https://github.com/visgl/deck.gl/blob/0afd4e99a6199aeec979989e0c361c97e6c17a16/modules/layers/src/bitmap-layer/bitmap-layer.js#L218
91
+ * @param {Uint8Array} image
92
+ */
93
+ loadTexture(image) {
94
+ const { gl } = this.context;
95
+ if (this.state.bitmapTexture) {
96
+ this.state.bitmapTexture.delete();
97
+ }
98
+ if (image instanceof Texture2D) {
99
+ this.setState({
100
+ bitmapTexture: image,
101
+ });
102
+ }
103
+ else if (image) {
104
+ this.setState({
105
+ bitmapTexture: new Texture2D(gl, {
106
+ data: image,
107
+ mipmaps: false,
108
+ parameters: PIXELATED_TEXTURE_PARAMETERS,
109
+ // Each color contains a single luminance value.
110
+ // When sampled, rgb are all set to this luminance, alpha is 1.0.
111
+ // Reference: https://luma.gl/docs/api-reference/webgl/texture#texture-formats
112
+ format: GL.LUMINANCE,
113
+ dataFormat: GL.LUMINANCE,
114
+ type: GL.UNSIGNED_BYTE,
115
+ width: TILE_SIZE,
116
+ height: TILE_SIZE,
117
+ }),
118
+ });
119
+ }
120
+ }
121
+ }
122
+ HeatmapBitmapLayer.layerName = 'HeatmapBitmapLayer';
123
+ HeatmapBitmapLayer.defaultProps = defaultProps;
@@ -0,0 +1,129 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+ import { COORDINATE_SYSTEM, CompositeLayer } from '@deck.gl/core'; // eslint-disable-line import/no-extraneous-dependencies
3
+ import { TextLayer } from '@deck.gl/layers'; // eslint-disable-line import/no-extraneous-dependencies
4
+ import { AXIS_LABEL_TEXT_SIZE, AXIS_TITLE_TEXT_SIZE, AXIS_MARGIN, THEME_TO_TEXT_COLOR, AXIS_FONT_FAMILY, COLOR_BAR_SIZE, } from './heatmap-constants';
5
+ export default class HeatmapCompositeTextLayer extends CompositeLayer {
6
+ _renderAxisTopLayers() {
7
+ const { axisTopLabelData, matrixLeft, width, matrixWidth, viewWidth, theme, targetX, targetY, axisTopTitle, cellWidth, axisOffsetTop, scaleFactor, hideTopLabels, } = this.props;
8
+ const showAxisTopLabels = cellWidth >= AXIS_LABEL_TEXT_SIZE;
9
+ const axisLabelTop = targetY + (axisOffsetTop - AXIS_MARGIN) / 2 / scaleFactor;
10
+ return hideTopLabels ? [] : [
11
+ new TextLayer({
12
+ id: 'axisTopLabels',
13
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
14
+ data: axisTopLabelData,
15
+ getText: d => d[1],
16
+ getPosition: d => [matrixLeft + ((d[0] + 0.5) / width) * matrixWidth, axisLabelTop],
17
+ getTextAnchor: 'start',
18
+ getColor: () => THEME_TO_TEXT_COLOR[theme],
19
+ getSize: (showAxisTopLabels ? AXIS_LABEL_TEXT_SIZE : 0),
20
+ getAngle: 75,
21
+ fontFamily: AXIS_FONT_FAMILY,
22
+ updateTriggers: {
23
+ getPosition: [axisLabelTop, matrixLeft, matrixWidth, viewWidth],
24
+ getSize: [showAxisTopLabels],
25
+ getColor: [theme],
26
+ },
27
+ }),
28
+ new TextLayer({
29
+ id: 'axisTopTitle',
30
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
31
+ data: [{ title: axisTopTitle }],
32
+ getText: d => d.title,
33
+ getPosition: [targetX, targetY],
34
+ getTextAnchor: 'middle',
35
+ getColor: () => THEME_TO_TEXT_COLOR[theme],
36
+ getSize: (!showAxisTopLabels ? AXIS_TITLE_TEXT_SIZE : 0),
37
+ getAngle: 0,
38
+ fontFamily: AXIS_FONT_FAMILY,
39
+ updateTriggers: {
40
+ getSize: [showAxisTopLabels],
41
+ getColor: [theme],
42
+ },
43
+ }),
44
+ ];
45
+ }
46
+ _renderCornerLayers() {
47
+ const { theme, targetX, targetY, axisOffsetTop, scaleFactor, cellColorLabelsData, axisOffsetLeft, transpose, } = this.props;
48
+ const axisLabelTop = targetY + (axisOffsetTop - AXIS_MARGIN) / 2 / scaleFactor;
49
+ const axisLabelLeft = targetX + (axisOffsetLeft - AXIS_MARGIN) / 2 / scaleFactor;
50
+ return cellColorLabelsData.map(data => (new TextLayer({
51
+ id: `cellColorLabel-${data[0]}`,
52
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
53
+ data: [data],
54
+ getText: d => d[1],
55
+ getTextAnchor: (transpose ? 'end' : 'start'),
56
+ getAlignmentBaseline: 'top',
57
+ getColor: () => THEME_TO_TEXT_COLOR[theme],
58
+ getSize: AXIS_LABEL_TEXT_SIZE,
59
+ getPosition: d => [
60
+ (transpose
61
+ ? axisLabelLeft
62
+ : targetX
63
+ + ((-cellColorLabelsData.length + d[0] * 2) * COLOR_BAR_SIZE + AXIS_MARGIN)
64
+ / 2 / scaleFactor),
65
+ (transpose
66
+ ? targetY
67
+ + ((-cellColorLabelsData.length + d[0] * 2) * COLOR_BAR_SIZE + AXIS_MARGIN)
68
+ / 2 / scaleFactor
69
+ : axisLabelTop),
70
+ ],
71
+ getAngle: (transpose ? 0 : 90),
72
+ fontFamily: AXIS_FONT_FAMILY,
73
+ })));
74
+ }
75
+ _renderAxisLeftLayers() {
76
+ const { axisLeftLabelData, matrixTop, height, matrixHeight, viewHeight, theme, axisLeftTitle, targetX, targetY, cellHeight, axisOffsetLeft, scaleFactor, hideLeftLabels, } = this.props;
77
+ const showAxisLeftLabels = cellHeight >= AXIS_LABEL_TEXT_SIZE;
78
+ const axisLabelLeft = targetX + (axisOffsetLeft - AXIS_MARGIN) / 2 / scaleFactor;
79
+ return hideLeftLabels ? [] : [
80
+ new TextLayer({
81
+ id: 'axisLeftLabels',
82
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
83
+ data: axisLeftLabelData,
84
+ getText: d => d[1],
85
+ getPosition: d => [axisLabelLeft, matrixTop + ((d[0] + 0.5) / height) * matrixHeight],
86
+ getTextAnchor: 'end',
87
+ getColor: () => THEME_TO_TEXT_COLOR[theme],
88
+ getSize: (showAxisLeftLabels ? AXIS_LABEL_TEXT_SIZE : 0),
89
+ getAngle: 0,
90
+ fontFamily: AXIS_FONT_FAMILY,
91
+ updateTriggers: {
92
+ getPosition: [axisLabelLeft, matrixTop, matrixHeight, viewHeight],
93
+ getSize: [showAxisLeftLabels],
94
+ getColor: [theme],
95
+ },
96
+ }),
97
+ new TextLayer({
98
+ id: 'axisLeftTitle',
99
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
100
+ data: [{ title: axisLeftTitle }],
101
+ getText: d => d.title,
102
+ getPosition: [targetX, targetY],
103
+ getTextAnchor: 'middle',
104
+ getColor: () => THEME_TO_TEXT_COLOR[theme],
105
+ getSize: (!showAxisLeftLabels ? AXIS_TITLE_TEXT_SIZE : 0),
106
+ getAngle: 90,
107
+ fontFamily: AXIS_FONT_FAMILY,
108
+ updateTriggers: {
109
+ getSize: [showAxisLeftLabels],
110
+ getColor: [theme],
111
+ },
112
+ }),
113
+ ];
114
+ }
115
+ renderLayers() {
116
+ const { axis } = this.props;
117
+ if (axis === 'left') {
118
+ return this._renderAxisLeftLayers();
119
+ }
120
+ if (axis === 'top') {
121
+ return this._renderAxisTopLayers();
122
+ }
123
+ if (axis === 'corner') {
124
+ return this._renderCornerLayers();
125
+ }
126
+ return [];
127
+ }
128
+ }
129
+ HeatmapCompositeTextLayer.layerName = 'HeatmapCompositeTextLayer';
@@ -0,0 +1,129 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+ import GL from '@luma.gl/constants'; // eslint-disable-line import/no-extraneous-dependencies
3
+ import { _mergeShaders, project32, picking } from '@deck.gl/core'; // eslint-disable-line import/no-extraneous-dependencies
4
+ import { BitmapLayer } from '@deck.gl/layers'; // eslint-disable-line import/no-extraneous-dependencies
5
+ import { Texture2D } from '@luma.gl/core';
6
+ import { PIXELATED_TEXTURE_PARAMETERS, TILE_SIZE, DATA_TEXTURE_SIZE } from './heatmap-constants';
7
+ import { GLSL_COLORMAPS, GLSL_COLORMAP_DEFAULT, COLORMAP_SHADER_PLACEHOLDER } from './constants';
8
+ import { vertexShader, fragmentShader } from './padded-expression-heatmap-bitmap-layer-shaders';
9
+ const defaultProps = {
10
+ image: { type: 'object', value: null, async: true },
11
+ colormap: { type: 'string', value: GLSL_COLORMAP_DEFAULT, compare: true },
12
+ bounds: { type: 'array', value: [1, 0, 0, 1], compare: true },
13
+ aggSizeX: { type: 'number', value: 8.0, compare: true },
14
+ aggSizeY: { type: 'number', value: 8.0, compare: true },
15
+ colorScaleLo: { type: 'number', value: 0.0, compare: true },
16
+ colorScaleHi: { type: 'number', value: 1.0, compare: true },
17
+ };
18
+ /**
19
+ * A BitmapLayer that performs aggregation in the fragment shader,
20
+ * and renders its texture from a Uint8Array rather than an ImageData.
21
+ */
22
+ export default class PaddedExpressionHeatmapBitmapLayer extends BitmapLayer {
23
+ /**
24
+ * Copy of getShaders from Layer (grandparent, parent of BitmapLayer).
25
+ * Reference: https://github.com/visgl/deck.gl/blob/0afd4e99a6199aeec979989e0c361c97e6c17a16/modules/core/src/lib/layer.js#L302
26
+ * @param {object} shaders
27
+ * @returns {object} Merged shaders.
28
+ */
29
+ _getShaders(shaders) {
30
+ this.props.extensions.forEach((extension) => {
31
+ // eslint-disable-next-line no-param-reassign
32
+ shaders = _mergeShaders(shaders, extension.getShaders.call(this, extension));
33
+ });
34
+ return shaders;
35
+ }
36
+ /**
37
+ * Need to override to provide custom shaders.
38
+ */
39
+ getShaders() {
40
+ const { colormap } = this.props;
41
+ const fragmentShaderWithColormap = GLSL_COLORMAPS.includes(colormap)
42
+ ? fragmentShader.replace(COLORMAP_SHADER_PLACEHOLDER, colormap)
43
+ : fragmentShader.replace(COLORMAP_SHADER_PLACEHOLDER, GLSL_COLORMAP_DEFAULT);
44
+ return this._getShaders({
45
+ vs: vertexShader,
46
+ fs: fragmentShaderWithColormap,
47
+ modules: [project32, picking],
48
+ });
49
+ }
50
+ updateState(args) {
51
+ super.updateState(args);
52
+ const { props, oldProps } = args;
53
+ if (props.colormap !== oldProps.colormap) {
54
+ const { gl } = this.context;
55
+ // eslint-disable-next-line no-unused-expressions
56
+ this.state.model?.delete();
57
+ this.state.model = this._getModel(gl);
58
+ this.getAttributeManager().invalidateAll();
59
+ }
60
+ if (props.image !== oldProps.image) {
61
+ this.loadTexture(this.props.image);
62
+ }
63
+ }
64
+ /**
65
+ * Need to override to provide additional uniform values.
66
+ * Simplified by removing video-related code.
67
+ * Reference: https://github.com/visgl/deck.gl/blob/0afd4e99a6199aeec979989e0c361c97e6c17a16/modules/layers/src/bitmap-layer/bitmap-layer.js#L173
68
+ * @param {*} opts
69
+ */
70
+ draw(opts) {
71
+ const { uniforms } = opts;
72
+ const { bitmapTexture, model } = this.state;
73
+ const { aggSizeX, aggSizeY, colorScaleLo, colorScaleHi, origDataSize, tileI, tileJ, numXTiles, numYTiles, } = this.props;
74
+ // Render the image
75
+ if (bitmapTexture && model) {
76
+ model
77
+ .setUniforms(Object.assign({}, uniforms, {
78
+ uBitmapTexture: bitmapTexture,
79
+ uOrigDataSize: origDataSize,
80
+ uReshapedDataSize: [DATA_TEXTURE_SIZE, DATA_TEXTURE_SIZE],
81
+ uTextureSize: [TILE_SIZE, TILE_SIZE],
82
+ uAggSize: [aggSizeX, aggSizeY],
83
+ uColorScaleRange: [colorScaleLo, colorScaleHi],
84
+ tileIJ: [tileI, tileJ],
85
+ dataIJ: [0, 0],
86
+ numTiles: [numXTiles, numYTiles],
87
+ numData: [1, 1],
88
+ }))
89
+ .draw();
90
+ }
91
+ }
92
+ /**
93
+ * Need to override to provide the custom DEFAULT_TEXTURE_PARAMETERS
94
+ * object.
95
+ * Simplified by removing video-related code.
96
+ * Reference: https://github.com/visgl/deck.gl/blob/0afd4e99a6199aeec979989e0c361c97e6c17a16/modules/layers/src/bitmap-layer/bitmap-layer.js#L218
97
+ * @param {Array<Uint8Array>} images
98
+ */
99
+ loadTexture(image) {
100
+ const { gl } = this.context;
101
+ if (this.state.bitmapTexture) {
102
+ this.state.bitmapTexture.delete();
103
+ }
104
+ if (image && image instanceof Texture2D) {
105
+ this.setState({
106
+ bitmapTexture: image,
107
+ });
108
+ }
109
+ else if (image) {
110
+ this.setState({
111
+ bitmapTexture: new Texture2D(gl, {
112
+ data: image,
113
+ mipmaps: false,
114
+ parameters: PIXELATED_TEXTURE_PARAMETERS,
115
+ // Each color contains a single luminance value.
116
+ // When sampled, rgb are all set to this luminance, alpha is 1.0.
117
+ // Reference: https://luma.gl/docs/api-reference/webgl/texture#texture-formats
118
+ format: GL.LUMINANCE,
119
+ dataFormat: GL.LUMINANCE,
120
+ type: GL.UNSIGNED_BYTE,
121
+ width: DATA_TEXTURE_SIZE,
122
+ height: DATA_TEXTURE_SIZE,
123
+ }),
124
+ });
125
+ }
126
+ }
127
+ }
128
+ PaddedExpressionHeatmapBitmapLayer.layerName = 'PaddedExpressionHeatmapBitmapLayer';
129
+ PaddedExpressionHeatmapBitmapLayer.defaultProps = defaultProps;
@@ -0,0 +1,26 @@
1
+ import { BitmapLayer } from '@deck.gl/layers'; // eslint-disable-line import/no-extraneous-dependencies
2
+ import { CompositeLayer } from '@deck.gl/core'; // eslint-disable-line import/no-extraneous-dependencies
3
+ import { PIXELATED_TEXTURE_PARAMETERS } from './heatmap-constants';
4
+ // These are the same defaultProps as for BitmapLayer.
5
+ const defaultProps = {
6
+ ...BitmapLayer.defaultProps,
7
+ image: { type: 'object', value: null, async: true },
8
+ bounds: { type: 'array', value: [1, 0, 0, 1], compare: true },
9
+ desaturate: {
10
+ type: 'number', min: 0, max: 1, value: 0,
11
+ },
12
+ transparentColor: { type: 'color', value: [0, 0, 0, 0] },
13
+ tintColor: { type: 'color', value: [255, 255, 255] },
14
+ };
15
+ export default class PixelatedBitmapLayer extends CompositeLayer {
16
+ renderLayers() {
17
+ const { image } = this.props;
18
+ return new BitmapLayer(this.props, {
19
+ id: `${this.props.id}-wrapped`,
20
+ image,
21
+ textureParameters: PIXELATED_TEXTURE_PARAMETERS,
22
+ });
23
+ }
24
+ }
25
+ PixelatedBitmapLayer.layerName = 'PixelatedBitmapLayer';
26
+ PixelatedBitmapLayer.defaultProps = defaultProps;
@@ -0,0 +1,105 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+ /* eslint-disable import/no-extraneous-dependencies */
3
+ import GL from '@luma.gl/constants';
4
+ import { LayerExtension } from '@deck.gl/core';
5
+ import { GLSL_COLORMAPS, GLSL_COLORMAP_DEFAULT, COLORMAP_SHADER_PLACEHOLDER } from '../constants';
6
+ import module from './shader-module';
7
+ const defaultProps = {
8
+ colormap: { type: 'string', value: GLSL_COLORMAP_DEFAULT, compare: true },
9
+ colorScaleLo: { type: 'number', value: 0.0, compare: true },
10
+ colorScaleHi: { type: 'number', value: 1.0, compare: true },
11
+ isExpressionMode: false,
12
+ getExpressionValue: { type: 'accessor', value: 0 },
13
+ getSelectionState: { type: 'accessor', value: 0.0 },
14
+ };
15
+ export default class ScaledExpressionExtension extends LayerExtension {
16
+ getShaders() {
17
+ const { colormap } = this.props;
18
+ return {
19
+ modules: [module],
20
+ defines: {
21
+ [COLORMAP_SHADER_PLACEHOLDER]: GLSL_COLORMAPS.includes(colormap)
22
+ ? colormap
23
+ : GLSL_COLORMAP_DEFAULT,
24
+ },
25
+ };
26
+ }
27
+ updateState({ props, oldProps }) {
28
+ if (props.colormap !== oldProps.colormap) {
29
+ const { gl } = this.context;
30
+ // Normal single model layers, like ScatterplotLayer
31
+ if (this.state.model) {
32
+ // eslint-disable-next-line no-unused-expressions
33
+ this.state.model?.delete();
34
+ this.state.model = this._getModel(gl);
35
+ }
36
+ else {
37
+ // Special handling for PolygonLayer sublayer models.
38
+ if (this.state.models) {
39
+ // eslint-disable-next-line no-unused-expressions
40
+ this.state.models?.forEach(model => model?.delete());
41
+ }
42
+ if (this.state.topModel) {
43
+ // eslint-disable-next-line no-unused-expressions
44
+ this.state.topModel?.delete();
45
+ }
46
+ if (this.state.sideModel) {
47
+ // eslint-disable-next-line no-unused-expressions
48
+ this.state.sideModel?.delete();
49
+ }
50
+ if (this._getModels) {
51
+ this.setState(this._getModels(this.context.gl));
52
+ }
53
+ }
54
+ const attributeManager = this.getAttributeManager();
55
+ if (attributeManager) {
56
+ this.getAttributeManager().invalidateAll();
57
+ }
58
+ }
59
+ }
60
+ initializeState() {
61
+ const layer = this.getCurrentLayer();
62
+ // No need to run this on layers that don't have a `draw` call.
63
+ if (layer.isComposite) {
64
+ return;
65
+ }
66
+ const attributeManager = this.getAttributeManager();
67
+ if (attributeManager) {
68
+ // This attributes is the array of expression data needed for
69
+ // coloring cells against the colormap.
70
+ attributeManager.add({
71
+ expressionValue: {
72
+ type: GL.FLOAT,
73
+ size: 1,
74
+ transition: true,
75
+ accessor: 'getExpressionValue',
76
+ defaultValue: 1,
77
+ // PolygonLayer fill needs not-intsanced attribute but
78
+ // ScatterplotLayer and the PolygonLayer stroke needs instanced.
79
+ // So use another attribute's divisor property as a proxy for this divisor.
80
+ divisor: Object.values(attributeManager.attributes)[0].settings.divisor,
81
+ },
82
+ });
83
+ }
84
+ }
85
+ draw() {
86
+ const { colorScaleLo, colorScaleHi, isExpressionMode, } = this.props;
87
+ const { topModel, sideModel, models, model, } = this.state;
88
+ const uniforms = {
89
+ uColorScaleRange: [colorScaleLo, colorScaleHi],
90
+ uIsExpressionMode: isExpressionMode,
91
+ };
92
+ // ScatterplotLayer model
93
+ // eslint-disable-next-line no-unused-expressions
94
+ model?.setUniforms(uniforms);
95
+ // PolygonLayer models from sublayers
96
+ // eslint-disable-next-line no-unused-expressions
97
+ models?.forEach(m => m.setUniforms(uniforms));
98
+ // eslint-disable-next-line no-unused-expressions
99
+ topModel?.setUniforms(uniforms);
100
+ // eslint-disable-next-line no-unused-expressions
101
+ sideModel?.setUniforms(uniforms);
102
+ }
103
+ }
104
+ ScaledExpressionExtension.extensionName = 'ScaledExpressionExtension';
105
+ ScaledExpressionExtension.defaultProps = defaultProps;
@@ -0,0 +1,2 @@
1
+ import ScaledExpressionExtension from './ScaledExpressionExtension';
2
+ export default ScaledExpressionExtension;