@hema-to/regl-scatterplot 1.14.1-hemato.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 +21 -0
- package/README.md +1110 -0
- package/dist/regl-scatterplot.esm.d.ts +126 -0
- package/dist/regl-scatterplot.esm.js +9962 -0
- package/dist/regl-scatterplot.js +9385 -0
- package/dist/regl-scatterplot.min.js +8 -0
- package/dist/types.d.ts +306 -0
- package/package.json +100 -0
- package/src/bg.fs +13 -0
- package/src/bg.vs +16 -0
- package/src/constants.js +189 -0
- package/src/index.js +4654 -0
- package/src/kdbush-class.js +369 -0
- package/src/kdbush-worker.js +20 -0
- package/src/kdbush.js +69 -0
- package/src/lasso-manager/constants.js +7 -0
- package/src/lasso-manager/create-long-press-animations.js +257 -0
- package/src/lasso-manager/create-long-press-elements.js +68 -0
- package/src/lasso-manager/index.js +781 -0
- package/src/lasso-manager/utils.js +40 -0
- package/src/point-simple.fs +10 -0
- package/src/point-update.fs +21 -0
- package/src/point-update.vs +13 -0
- package/src/point.fs +22 -0
- package/src/point.vs +124 -0
- package/src/renderer.js +252 -0
- package/src/spline-curve-worker.js +271 -0
- package/src/spline-curve.js +23 -0
- package/src/types.d.ts +306 -0
- package/src/utils.js +598 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculates exponential moving average of 2D points
|
|
3
|
+
* @param {[number, number][]} values - Array of numbers to average
|
|
4
|
+
* @param {number} halfLife - Number of steps after which weight becomes half
|
|
5
|
+
* @param {number} windowSize - Maximum number of previous values to consider
|
|
6
|
+
* @returns {number} The exponential moving average
|
|
7
|
+
*/
|
|
8
|
+
export const exponentialMovingAverage = (values, halfLife, windowSize) => {
|
|
9
|
+
if (values.length === 0) {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (values.length === 1) {
|
|
14
|
+
return values[0];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Calculate decay factor from `halfLife` such that weight = 0.5 when the
|
|
18
|
+
// step is `halfLife`
|
|
19
|
+
const decayBase = 2 ** (-1 / halfLife);
|
|
20
|
+
|
|
21
|
+
// Limit to window size
|
|
22
|
+
const startIdx = Math.max(0, values.length - windowSize);
|
|
23
|
+
const relevantValues = values.slice(startIdx);
|
|
24
|
+
|
|
25
|
+
let weightedSumX = 0;
|
|
26
|
+
let weightedSumY = 0;
|
|
27
|
+
let weightSum = 0;
|
|
28
|
+
|
|
29
|
+
// Calculate weighted sum starting from most recent value
|
|
30
|
+
for (let i = relevantValues.length - 1; i >= 0; i--) {
|
|
31
|
+
const steps = relevantValues.length - 1 - i;
|
|
32
|
+
const weight = decayBase ** steps;
|
|
33
|
+
|
|
34
|
+
weightedSumX += relevantValues[i][0] * weight;
|
|
35
|
+
weightedSumY += relevantValues[i][1] * weight;
|
|
36
|
+
weightSum += weight;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return [weightedSumX / weightSum, weightedSumY / weightSum];
|
|
40
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const SHADER = `precision highp float;
|
|
2
|
+
|
|
3
|
+
uniform sampler2D startStateTex;
|
|
4
|
+
uniform sampler2D endStateTex;
|
|
5
|
+
uniform float t;
|
|
6
|
+
|
|
7
|
+
varying vec2 particleTextureIndex;
|
|
8
|
+
|
|
9
|
+
void main() {
|
|
10
|
+
// Interpolate x, y, and value
|
|
11
|
+
vec3 start = texture2D(startStateTex, particleTextureIndex).xyw;
|
|
12
|
+
vec3 end = texture2D(endStateTex, particleTextureIndex).xyw;
|
|
13
|
+
vec3 curr = start * (1.0 - t) + end * t;
|
|
14
|
+
|
|
15
|
+
// The category cannot be interpolated
|
|
16
|
+
float endCategory = texture2D(endStateTex, particleTextureIndex).z;
|
|
17
|
+
|
|
18
|
+
gl_FragColor = vec4(curr.xy, endCategory, curr.z);
|
|
19
|
+
}`;
|
|
20
|
+
|
|
21
|
+
export default SHADER;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const SHADER = `precision highp float;
|
|
2
|
+
|
|
3
|
+
attribute vec2 position;
|
|
4
|
+
varying vec2 particleTextureIndex;
|
|
5
|
+
|
|
6
|
+
void main() {
|
|
7
|
+
// map normalized device coords to texture coords
|
|
8
|
+
particleTextureIndex = 0.5 * (1.0 + position);
|
|
9
|
+
|
|
10
|
+
gl_Position = vec4(position, 0, 1);
|
|
11
|
+
}`;
|
|
12
|
+
|
|
13
|
+
export default SHADER;
|
package/src/point.fs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const FRAGMENT_SHADER = `
|
|
2
|
+
precision highp float;
|
|
3
|
+
|
|
4
|
+
uniform float antiAliasing;
|
|
5
|
+
|
|
6
|
+
varying vec4 color;
|
|
7
|
+
varying float finalPointSize;
|
|
8
|
+
|
|
9
|
+
float linearstep(float edge0, float edge1, float x) {
|
|
10
|
+
return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
void main() {
|
|
14
|
+
vec2 c = gl_PointCoord * 2.0 - 1.0;
|
|
15
|
+
float sdf = length(c) * finalPointSize;
|
|
16
|
+
float alpha = linearstep(finalPointSize + antiAliasing, finalPointSize - antiAliasing, sdf);
|
|
17
|
+
|
|
18
|
+
gl_FragColor = vec4(color.rgb, alpha * color.a);
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
export default FRAGMENT_SHADER;
|
package/src/point.vs
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
const createVertexShader = (globalState) => `
|
|
2
|
+
precision highp float;
|
|
3
|
+
|
|
4
|
+
uniform sampler2D colorTex;
|
|
5
|
+
uniform float colorTexRes;
|
|
6
|
+
uniform float colorTexEps;
|
|
7
|
+
uniform sampler2D stateTex;
|
|
8
|
+
uniform float stateTexRes;
|
|
9
|
+
uniform float stateTexEps;
|
|
10
|
+
uniform float devicePixelRatio;
|
|
11
|
+
uniform sampler2D encodingTex;
|
|
12
|
+
uniform float encodingTexRes;
|
|
13
|
+
uniform float encodingTexEps;
|
|
14
|
+
uniform float pointSizeExtra;
|
|
15
|
+
uniform float pointOpacityMax;
|
|
16
|
+
uniform float pointOpacityScale;
|
|
17
|
+
uniform float numPoints;
|
|
18
|
+
uniform float globalState;
|
|
19
|
+
uniform float isColoredByZ;
|
|
20
|
+
uniform float isColoredByW;
|
|
21
|
+
uniform float isOpacityByZ;
|
|
22
|
+
uniform float isOpacityByW;
|
|
23
|
+
uniform float isOpacityByDensity;
|
|
24
|
+
uniform float isSizedByZ;
|
|
25
|
+
uniform float isSizedByW;
|
|
26
|
+
uniform float isPixelAligned;
|
|
27
|
+
uniform float colorMultiplicator;
|
|
28
|
+
uniform float opacityMultiplicator;
|
|
29
|
+
uniform float opacityDensity;
|
|
30
|
+
uniform float sizeMultiplicator;
|
|
31
|
+
uniform float numColorStates;
|
|
32
|
+
uniform float pointScale;
|
|
33
|
+
uniform float drawingBufferWidth;
|
|
34
|
+
uniform float drawingBufferHeight;
|
|
35
|
+
uniform mat4 modelViewProjection;
|
|
36
|
+
|
|
37
|
+
attribute vec2 stateIndex;
|
|
38
|
+
|
|
39
|
+
varying vec4 color;
|
|
40
|
+
varying float finalPointSize;
|
|
41
|
+
|
|
42
|
+
void main() {
|
|
43
|
+
vec4 state = texture2D(stateTex, stateIndex);
|
|
44
|
+
|
|
45
|
+
if (isPixelAligned < 0.5) {
|
|
46
|
+
gl_Position = modelViewProjection * vec4(state.x, state.y, 0.0, 1.0);
|
|
47
|
+
} else {
|
|
48
|
+
vec4 clipSpacePosition = modelViewProjection * vec4(state.x, state.y, 0.0, 1.0);
|
|
49
|
+
vec2 ndcPosition = clipSpacePosition.xy / clipSpacePosition.w;
|
|
50
|
+
vec2 pixelPos = 0.5 * (ndcPosition + 1.0) * vec2(drawingBufferWidth, drawingBufferHeight);
|
|
51
|
+
pixelPos = floor(pixelPos + 0.5); // Snap to nearest pixel
|
|
52
|
+
vec2 snappedPosition = (pixelPos / vec2(drawingBufferWidth, drawingBufferHeight)) * 2.0 - 1.0;
|
|
53
|
+
gl_Position = vec4(snappedPosition, 0.0, 1.0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
// Determine color index
|
|
58
|
+
float colorIndexZ = isColoredByZ * floor(state.z * colorMultiplicator);
|
|
59
|
+
float colorIndexW = isColoredByW * floor(state.w * colorMultiplicator);
|
|
60
|
+
|
|
61
|
+
// Multiply by the number of color states per color
|
|
62
|
+
// I.e., normal, active, hover, background, etc.
|
|
63
|
+
float colorIndex = (colorIndexZ + colorIndexW) * numColorStates;
|
|
64
|
+
|
|
65
|
+
// Half a "pixel" or "texel" in texture coordinates
|
|
66
|
+
float colorLinearIndex = colorIndex + globalState;
|
|
67
|
+
|
|
68
|
+
// Need to add cEps here to avoid floating point issue that can lead to
|
|
69
|
+
// dramatic changes in which color is loaded as floor(3/2.9999) = 1 but
|
|
70
|
+
// floor(3/3.0001) = 0!
|
|
71
|
+
float colorRowIndex = floor((colorLinearIndex + colorTexEps) / colorTexRes);
|
|
72
|
+
|
|
73
|
+
vec2 colorTexIndex = vec2(
|
|
74
|
+
(colorLinearIndex / colorTexRes) - colorRowIndex + colorTexEps,
|
|
75
|
+
colorRowIndex / colorTexRes + colorTexEps
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
color = texture2D(colorTex, colorTexIndex);
|
|
79
|
+
|
|
80
|
+
// Retrieve point size
|
|
81
|
+
float pointSizeIndexZ = isSizedByZ * floor(state.z * sizeMultiplicator);
|
|
82
|
+
float pointSizeIndexW = isSizedByW * floor(state.w * sizeMultiplicator);
|
|
83
|
+
float pointSizeIndex = pointSizeIndexZ + pointSizeIndexW;
|
|
84
|
+
|
|
85
|
+
float pointSizeRowIndex = floor((pointSizeIndex + encodingTexEps) / encodingTexRes);
|
|
86
|
+
vec2 pointSizeTexIndex = vec2(
|
|
87
|
+
(pointSizeIndex / encodingTexRes) - pointSizeRowIndex + encodingTexEps,
|
|
88
|
+
pointSizeRowIndex / encodingTexRes + encodingTexEps
|
|
89
|
+
);
|
|
90
|
+
float pointSize = texture2D(encodingTex, pointSizeTexIndex).x;
|
|
91
|
+
|
|
92
|
+
// Retrieve opacity
|
|
93
|
+
${
|
|
94
|
+
(() => {
|
|
95
|
+
// Drawing the inner border of selected points
|
|
96
|
+
if (globalState === 3) return '';
|
|
97
|
+
|
|
98
|
+
// Draw points with opacity encoding or dynamic opacity
|
|
99
|
+
return `
|
|
100
|
+
if (isOpacityByDensity < 0.5) {
|
|
101
|
+
float opacityIndexZ = isOpacityByZ * floor(state.z * opacityMultiplicator);
|
|
102
|
+
float opacityIndexW = isOpacityByW * floor(state.w * opacityMultiplicator);
|
|
103
|
+
float opacityIndex = opacityIndexZ + opacityIndexW;
|
|
104
|
+
|
|
105
|
+
float opacityRowIndex = floor((opacityIndex + encodingTexEps) / encodingTexRes);
|
|
106
|
+
vec2 opacityTexIndex = vec2(
|
|
107
|
+
(opacityIndex / encodingTexRes) - opacityRowIndex + encodingTexEps,
|
|
108
|
+
opacityRowIndex / encodingTexRes + encodingTexEps
|
|
109
|
+
);
|
|
110
|
+
color.a = texture2D(encodingTex, opacityTexIndex)[${1 + globalState}];
|
|
111
|
+
} else {
|
|
112
|
+
color.a = min(1.0, opacityDensity + globalState);
|
|
113
|
+
}
|
|
114
|
+
`;
|
|
115
|
+
})()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
color.a = min(pointOpacityMax, color.a) * pointOpacityScale;
|
|
119
|
+
finalPointSize = (pointSize * pointScale) + pointSizeExtra;
|
|
120
|
+
gl_PointSize = finalPointSize;
|
|
121
|
+
}
|
|
122
|
+
`;
|
|
123
|
+
|
|
124
|
+
export default createVertexShader;
|
package/src/renderer.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { CLEAR_OPTIONS, DEFAULT_GAMMA } from './constants.js';
|
|
2
|
+
import { checkReglExtensions, createRegl } from './utils.js';
|
|
3
|
+
|
|
4
|
+
export const createRenderer = (
|
|
5
|
+
/** @type {Partial<import('./types').RendererOptions>} */ options = {},
|
|
6
|
+
) => {
|
|
7
|
+
let {
|
|
8
|
+
regl,
|
|
9
|
+
canvas = document.createElement('canvas'),
|
|
10
|
+
gamma = DEFAULT_GAMMA,
|
|
11
|
+
} = options;
|
|
12
|
+
|
|
13
|
+
let isDestroyed = false;
|
|
14
|
+
|
|
15
|
+
if (!regl) {
|
|
16
|
+
regl = createRegl(canvas);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const isSupportingAllGlExtensions = checkReglExtensions(regl);
|
|
20
|
+
|
|
21
|
+
const fboRes = [canvas.width, canvas.height];
|
|
22
|
+
const fbo = regl.framebuffer({
|
|
23
|
+
width: fboRes[0],
|
|
24
|
+
height: fboRes[1],
|
|
25
|
+
colorFormat: 'rgba',
|
|
26
|
+
colorType: 'float',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Render the float32 framebuffer to the internal canvas
|
|
31
|
+
*
|
|
32
|
+
* From https://observablehq.com/@rreusser/selecting-the-right-opacity-for-2d-point-clouds
|
|
33
|
+
*/
|
|
34
|
+
const renderToCanvas = regl({
|
|
35
|
+
vert: `
|
|
36
|
+
precision highp float;
|
|
37
|
+
attribute vec2 xy;
|
|
38
|
+
void main () {
|
|
39
|
+
gl_Position = vec4(xy, 0, 1);
|
|
40
|
+
}`,
|
|
41
|
+
frag: `
|
|
42
|
+
precision highp float;
|
|
43
|
+
uniform vec2 srcRes;
|
|
44
|
+
uniform sampler2D src;
|
|
45
|
+
uniform float gamma;
|
|
46
|
+
|
|
47
|
+
vec3 approxLinearToSRGB (vec3 rgb, float gamma) {
|
|
48
|
+
return pow(clamp(rgb, vec3(0), vec3(1)), vec3(1.0 / gamma));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
void main () {
|
|
52
|
+
vec4 color = texture2D(src, gl_FragCoord.xy / srcRes);
|
|
53
|
+
gl_FragColor = vec4(approxLinearToSRGB(color.rgb, gamma), color.a);
|
|
54
|
+
}`,
|
|
55
|
+
attributes: {
|
|
56
|
+
xy: [-4, -4, 4, -4, 0, 4],
|
|
57
|
+
},
|
|
58
|
+
uniforms: {
|
|
59
|
+
src: () => fbo,
|
|
60
|
+
srcRes: () => fboRes,
|
|
61
|
+
gamma: () => gamma,
|
|
62
|
+
},
|
|
63
|
+
count: 3,
|
|
64
|
+
depth: { enable: false },
|
|
65
|
+
blend: {
|
|
66
|
+
enable: true,
|
|
67
|
+
func: {
|
|
68
|
+
// biome-ignore lint/style/useNamingConvention: Regl internal
|
|
69
|
+
srcRGB: 'one',
|
|
70
|
+
srcAlpha: 'one',
|
|
71
|
+
// biome-ignore lint/style/useNamingConvention: Regl internal
|
|
72
|
+
dstRGB: 'one minus src alpha',
|
|
73
|
+
dstAlpha: 'one minus src alpha',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Copy the pixels from the internal canvas onto the target canvas
|
|
80
|
+
*/
|
|
81
|
+
const copyTo = (targetCanvas) => {
|
|
82
|
+
const ctx = targetCanvas.getContext('2d');
|
|
83
|
+
ctx.clearRect(0, 0, targetCanvas.width, targetCanvas.height);
|
|
84
|
+
ctx.drawImage(
|
|
85
|
+
canvas,
|
|
86
|
+
(canvas.width - targetCanvas.width) / 2,
|
|
87
|
+
(canvas.height - targetCanvas.height) / 2,
|
|
88
|
+
targetCanvas.width,
|
|
89
|
+
targetCanvas.height,
|
|
90
|
+
0,
|
|
91
|
+
0,
|
|
92
|
+
targetCanvas.width,
|
|
93
|
+
targetCanvas.height,
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The render function
|
|
99
|
+
*/
|
|
100
|
+
const render = (
|
|
101
|
+
/** @type {(): void} */ draw,
|
|
102
|
+
/** @type {HTMLCanvasElement} */ targetCanvas,
|
|
103
|
+
) => {
|
|
104
|
+
// Clear internal canvas
|
|
105
|
+
regl.clear(CLEAR_OPTIONS);
|
|
106
|
+
fbo.use(() => {
|
|
107
|
+
// Clear framebuffer
|
|
108
|
+
regl.clear(CLEAR_OPTIONS);
|
|
109
|
+
draw();
|
|
110
|
+
});
|
|
111
|
+
renderToCanvas();
|
|
112
|
+
copyTo(targetCanvas);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Update Regl's viewport, drawingBufferWidth, and drawingBufferHeight
|
|
117
|
+
*
|
|
118
|
+
* @description Call this method after the viewport has changed, e.g., width
|
|
119
|
+
* or height have been altered
|
|
120
|
+
*/
|
|
121
|
+
const refresh = () => {
|
|
122
|
+
regl.poll();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const drawFns = new Set();
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Register an draw function that is going to be invoked on every animation
|
|
129
|
+
* frame.
|
|
130
|
+
*/
|
|
131
|
+
const onFrame = (/** @type {(): void} */ draw) => {
|
|
132
|
+
drawFns.add(draw);
|
|
133
|
+
return () => {
|
|
134
|
+
drawFns.delete(draw);
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const frame = regl.frame(() => {
|
|
139
|
+
const iterator = drawFns.values();
|
|
140
|
+
let result = iterator.next();
|
|
141
|
+
while (!result.done) {
|
|
142
|
+
result.value(); // The draw function
|
|
143
|
+
result = iterator.next();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const resize = (
|
|
148
|
+
/** @type {number} */ customWidth,
|
|
149
|
+
/** @type {number} */ customHeight,
|
|
150
|
+
) => {
|
|
151
|
+
// We need to limit the width and height by the screen size to prevent
|
|
152
|
+
// a bug in VSCode where the window height is said to be taller than the
|
|
153
|
+
// screen height. The problem with too large dimensions is that at some
|
|
154
|
+
// point WebGL will break down because there's an upper limit on how large
|
|
155
|
+
// any buffer and texture can be. It also harms the performance quite a bit.
|
|
156
|
+
//
|
|
157
|
+
// By restricting the widht/height to the screen size we should have a safe
|
|
158
|
+
// upper limit for the canvas size.
|
|
159
|
+
//
|
|
160
|
+
// @see
|
|
161
|
+
// https://github.com/microsoft/vscode/issues/225808
|
|
162
|
+
// https://github.com/flekschas/jupyter-scatter/issues/37
|
|
163
|
+
const width =
|
|
164
|
+
customWidth === undefined
|
|
165
|
+
? Math.min(window.innerWidth, window.screen.availWidth)
|
|
166
|
+
: customWidth;
|
|
167
|
+
const height =
|
|
168
|
+
customHeight === undefined
|
|
169
|
+
? Math.min(window.innerHeight, window.screen.availHeight)
|
|
170
|
+
: customHeight;
|
|
171
|
+
canvas.width = width * window.devicePixelRatio;
|
|
172
|
+
canvas.height = height * window.devicePixelRatio;
|
|
173
|
+
fboRes[0] = canvas.width;
|
|
174
|
+
fboRes[1] = canvas.height;
|
|
175
|
+
fbo.resize(...fboRes);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const resizeHandler = () => {
|
|
179
|
+
resize();
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
if (!options.canvas) {
|
|
183
|
+
window.addEventListener('resize', resizeHandler);
|
|
184
|
+
window.addEventListener('orientationchange', resizeHandler);
|
|
185
|
+
resize();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Destroy the renderer to free resources and cancel animation frames
|
|
190
|
+
*/
|
|
191
|
+
const destroy = () => {
|
|
192
|
+
isDestroyed = true;
|
|
193
|
+
window.removeEventListener('resize', resizeHandler);
|
|
194
|
+
window.removeEventListener('orientationchange', resizeHandler);
|
|
195
|
+
frame.cancel();
|
|
196
|
+
canvas = undefined;
|
|
197
|
+
regl.destroy();
|
|
198
|
+
regl = undefined;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
/**
|
|
203
|
+
* Get the associated canvas element
|
|
204
|
+
* @return {HTMLCanvasElement} The associated canvas element
|
|
205
|
+
*/
|
|
206
|
+
get canvas() {
|
|
207
|
+
return canvas;
|
|
208
|
+
},
|
|
209
|
+
/**
|
|
210
|
+
* Get the associated Regl instance
|
|
211
|
+
* @return {import('regl').Regl} The associated Regl instance
|
|
212
|
+
*/
|
|
213
|
+
get regl() {
|
|
214
|
+
return regl;
|
|
215
|
+
},
|
|
216
|
+
/**
|
|
217
|
+
* Get the gamma value
|
|
218
|
+
* @return {number} The gamma value
|
|
219
|
+
*/
|
|
220
|
+
get gamma() {
|
|
221
|
+
return gamma;
|
|
222
|
+
},
|
|
223
|
+
/**
|
|
224
|
+
* Set gamma to a new value
|
|
225
|
+
* @param {number} newGamma - The new gamma value
|
|
226
|
+
*/
|
|
227
|
+
set gamma(newGamma) {
|
|
228
|
+
gamma = +newGamma;
|
|
229
|
+
},
|
|
230
|
+
/**
|
|
231
|
+
* Get whether the browser supports all necessary WebGL features
|
|
232
|
+
* @return {boolean} If `true` the browser supports all necessary WebGL features
|
|
233
|
+
*/
|
|
234
|
+
get isSupported() {
|
|
235
|
+
return isSupportingAllGlExtensions;
|
|
236
|
+
},
|
|
237
|
+
/**
|
|
238
|
+
* Get whether the renderer (and its Regl instance) is destroyed
|
|
239
|
+
* @return {boolean} If `true` the renderer is destroyed
|
|
240
|
+
*/
|
|
241
|
+
get isDestroyed() {
|
|
242
|
+
return isDestroyed;
|
|
243
|
+
},
|
|
244
|
+
render,
|
|
245
|
+
resize,
|
|
246
|
+
onFrame,
|
|
247
|
+
refresh,
|
|
248
|
+
destroy,
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export default createRenderer;
|