gladly-plot 0.0.5 → 0.0.6

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.
Files changed (44) hide show
  1. package/README.md +9 -2
  2. package/package.json +10 -11
  3. package/src/axes/Axis.js +320 -172
  4. package/src/axes/AxisLink.js +6 -2
  5. package/src/axes/AxisRegistry.js +116 -39
  6. package/src/axes/Camera.js +47 -0
  7. package/src/axes/ColorAxisRegistry.js +10 -2
  8. package/src/axes/FilterAxisRegistry.js +1 -1
  9. package/src/axes/TickLabelAtlas.js +99 -0
  10. package/src/axes/ZoomController.js +446 -124
  11. package/src/colorscales/ColorscaleRegistry.js +30 -10
  12. package/src/compute/ComputationRegistry.js +126 -184
  13. package/src/compute/axisFilter.js +21 -9
  14. package/src/compute/conv.js +64 -8
  15. package/src/compute/elementwise.js +72 -0
  16. package/src/compute/fft.js +106 -20
  17. package/src/compute/filter.js +105 -103
  18. package/src/compute/hist.js +247 -142
  19. package/src/compute/kde.js +64 -46
  20. package/src/compute/scatter2dInterpolate.js +277 -0
  21. package/src/compute/util.js +196 -0
  22. package/src/core/ComputePipeline.js +153 -0
  23. package/src/core/GlBase.js +141 -0
  24. package/src/core/Layer.js +22 -8
  25. package/src/core/LayerType.js +251 -92
  26. package/src/core/Plot.js +630 -152
  27. package/src/core/PlotGroup.js +204 -0
  28. package/src/core/ShaderQueue.js +73 -0
  29. package/src/data/ColumnData.js +269 -0
  30. package/src/data/Computation.js +95 -0
  31. package/src/data/Data.js +270 -0
  32. package/src/floats/Float.js +56 -0
  33. package/src/index.js +16 -4
  34. package/src/layers/BarsLayer.js +168 -0
  35. package/src/layers/ColorbarLayer.js +10 -14
  36. package/src/layers/ColorbarLayer2d.js +13 -24
  37. package/src/layers/FilterbarLayer.js +4 -3
  38. package/src/layers/LinesLayer.js +108 -122
  39. package/src/layers/PointsLayer.js +73 -69
  40. package/src/layers/ScatterShared.js +62 -106
  41. package/src/layers/TileLayer.js +20 -16
  42. package/src/math/mat4.js +100 -0
  43. package/src/core/Data.js +0 -67
  44. package/src/layers/HistogramLayer.js +0 -212
@@ -1,4 +1,6 @@
1
- import { registerTextureComputation, TextureComputation, EXPRESSION_REF } from "./ComputationRegistry.js"
1
+ import { registerTextureComputation, registerComputedData, EXPRESSION_REF, resolveQuantityKind } from "./ComputationRegistry.js"
2
+ import { TextureComputation, ComputedData } from "../data/Computation.js"
3
+ import { ArrayColumn } from "../data/ColumnData.js"
2
4
 
3
5
  /* ============================================================
4
6
  Utilities
@@ -8,6 +10,8 @@ function nextPow2(n) {
8
10
  return 1 << Math.ceil(Math.log2(n));
9
11
  }
10
12
 
13
+ // Internal: complex texture (R=real, G=imag per frequency bin), 1 element per texel.
14
+ // Not exposed via sampleColumn — only used as intermediate within this module.
11
15
  function makeComplexTexture(regl, data, N) {
12
16
  // data: Float32Array (real), imag assumed 0
13
17
  const texData = new Float32Array(N * 4);
@@ -39,7 +43,7 @@ function makeEmptyComplexTexture(regl, N) {
39
43
  }
40
44
 
41
45
  function makeFBO(regl, tex) {
42
- return regl.framebuffer({ color: tex });
46
+ return regl.framebuffer({ color: tex, depth: false, stencil: false });
43
47
  }
44
48
 
45
49
  /* ============================================================
@@ -166,11 +170,15 @@ function scalePass(regl, N) {
166
170
  });
167
171
  }
168
172
 
173
+ // If a single batch of FFT stages takes longer than this, yield before the next batch.
174
+ const TDR_STEP_MS = 500
175
+
169
176
  /* ============================================================
170
- Public: GPU FFT (top-level API)
177
+ Internal GPU FFT — returns a complex texture (R=real, G=imag)
178
+ with 1 frequency bin per texel. NOT for direct use with sampleColumn.
171
179
  ============================================================ */
172
180
 
173
- export function fft1d(regl, realArray, inverse = false) {
181
+ export async function fft1d(regl, realArray, inverse = false) {
174
182
  const N = nextPow2(realArray.length);
175
183
 
176
184
  let texA = makeComplexTexture(regl, realArray, N);
@@ -185,8 +193,9 @@ export function fft1d(regl, realArray, inverse = false) {
185
193
  });
186
194
  [fboA, fboB] = [fboB, fboA];
187
195
 
188
- // FFT stages
196
+ // FFT stages — yield between batches to avoid triggering the Windows TDR watchdog.
189
197
  const stages = Math.log2(N);
198
+ let batchStart = performance.now();
190
199
  for (let s = 1; s <= stages; s++) {
191
200
  fftStagePass(regl, 1 << s, inverse)({
192
201
  input: fboA,
@@ -194,6 +203,10 @@ export function fft1d(regl, realArray, inverse = false) {
194
203
  fbo: fboB
195
204
  });
196
205
  [fboA, fboB] = [fboB, fboA];
206
+ if (performance.now() - batchStart > TDR_STEP_MS) {
207
+ await new Promise(r => requestAnimationFrame(r));
208
+ batchStart = performance.now();
209
+ }
197
210
  }
198
211
 
199
212
  // scale for inverse
@@ -209,14 +222,14 @@ export function fft1d(regl, realArray, inverse = false) {
209
222
  }
210
223
 
211
224
  /* ============================================================
212
- FFT-based convolution
225
+ FFT-based convolution (internal)
213
226
  ============================================================ */
214
227
 
215
- export function fftConvolution(regl, signal, kernel) {
228
+ export async function fftConvolution(regl, signal, kernel) {
216
229
  const N = nextPow2(signal.length + kernel.length);
217
230
 
218
- const sigTex = fft1d(regl, signal, false);
219
- const kerTex = fft1d(regl, kernel, false);
231
+ const sigTex = await fft1d(regl, signal, false);
232
+ const kerTex = await fft1d(regl, kernel, false);
220
233
 
221
234
  const outTex = makeEmptyComplexTexture(regl, N);
222
235
  const outFBO = makeFBO(regl, outTex);
@@ -251,32 +264,108 @@ export function fftConvolution(regl, signal, kernel) {
251
264
  })();
252
265
 
253
266
  // inverse FFT
254
- return fft1d(regl, new Float32Array(N), true);
267
+ return await fft1d(regl, new Float32Array(N), true);
268
+ }
269
+
270
+ /* ============================================================
271
+ extractAndRepack: extract one channel from a complex texture
272
+ (1 element per texel, R=real/G=imag) into a 4-packed RGBA texture.
273
+ channelSwizzle: 'r' for real part, 'g' for imaginary part.
274
+ ============================================================ */
275
+
276
+ function extractAndRepack(regl, complexTex, channelSwizzle, N) {
277
+ const nTexels = Math.ceil(N / 4)
278
+ const w = Math.min(nTexels, regl.limits.maxTextureSize)
279
+ const h = Math.ceil(nTexels / w)
280
+ const outputTex = regl.texture({ width: w, height: h, type: 'float', format: 'rgba' })
281
+ const outputFBO = regl.framebuffer({ color: outputTex, depth: false, stencil: false })
282
+
283
+ regl({
284
+ framebuffer: outputFBO,
285
+ vert: `#version 300 es
286
+ in vec2 position;
287
+ void main() { gl_Position = vec4(position, 0.0, 1.0); }`,
288
+ frag: `#version 300 es
289
+ precision highp float;
290
+ uniform sampler2D complexTex;
291
+ uniform int totalLength;
292
+ out vec4 fragColor;
293
+ void main() {
294
+ int texelI = int(gl_FragCoord.y) * ${w} + int(gl_FragCoord.x);
295
+ int base = texelI * 4;
296
+ float v0 = base + 0 < totalLength ? texelFetch(complexTex, ivec2(base+0, 0), 0).${channelSwizzle} : 0.0;
297
+ float v1 = base + 1 < totalLength ? texelFetch(complexTex, ivec2(base+1, 0), 0).${channelSwizzle} : 0.0;
298
+ float v2 = base + 2 < totalLength ? texelFetch(complexTex, ivec2(base+2, 0), 0).${channelSwizzle} : 0.0;
299
+ float v3 = base + 3 < totalLength ? texelFetch(complexTex, ivec2(base+3, 0), 0).${channelSwizzle} : 0.0;
300
+ fragColor = vec4(v0, v1, v2, v3);
301
+ }`,
302
+ attributes: { position: [[-1,-1],[1,-1],[-1,1],[1,1]] },
303
+ uniforms: { complexTex, totalLength: N },
304
+ count: 4,
305
+ primitive: 'triangle strip'
306
+ })()
307
+
308
+ outputTex._dataLength = N
309
+ return outputTex
255
310
  }
256
311
 
257
- class Fft1dComputation extends TextureComputation {
258
- compute(regl, params) {
259
- return fft1d(regl, params.input, params.inverse ?? false)
312
+ /* ============================================================
313
+ FftData ComputedData producing 'real' and 'imag' columns
314
+ ============================================================ */
315
+
316
+ class FftData extends ComputedData {
317
+ columns() { return ['real', 'imag'] }
318
+
319
+ async compute(regl, params, data, getAxisDomain) {
320
+ const inputCol = data.getData(params.input)
321
+ if (!(inputCol instanceof ArrayColumn)) {
322
+ throw new Error(`FftData: input '${params.input}' must be a plain data column`)
323
+ }
324
+ const N = nextPow2(inputCol.array.length)
325
+ const complexTex = await fft1d(regl, inputCol.array, params.inverse ?? false)
326
+ return {
327
+ real: extractAndRepack(regl, complexTex, 'r', N),
328
+ imag: extractAndRepack(regl, complexTex, 'g', N),
329
+ _meta: {
330
+ domains: { real: null, imag: null },
331
+ quantityKinds: { real: null, imag: null }
332
+ }
333
+ }
260
334
  }
335
+
261
336
  schema(data) {
337
+ const cols = data ? data.columns() : []
262
338
  return {
263
339
  type: 'object',
340
+ title: 'FftData',
264
341
  properties: {
265
- input: EXPRESSION_REF,
266
- inverse: { type: 'boolean' }
342
+ input: { type: 'string', enum: cols, description: 'Input signal column' },
343
+ inverse: { type: 'boolean', default: false, description: 'Inverse FFT' }
267
344
  },
268
345
  required: ['input']
269
346
  }
270
347
  }
271
348
  }
272
349
 
350
+ registerComputedData('FftData', new FftData())
351
+
352
+ /* ============================================================
353
+ FftConvolutionComputation — TextureComputation (real output)
354
+ ============================================================ */
355
+
273
356
  class FftConvolutionComputation extends TextureComputation {
274
- compute(regl, params) {
275
- return fftConvolution(regl, params.signal, params.kernel)
357
+ getQuantityKind(params, data) { return resolveQuantityKind(params.signal, data) }
358
+ async compute(regl, params, data, getAxisDomain) {
359
+ const signal = params.signal instanceof ArrayColumn ? params.signal.array : params.signal
360
+ const kernel = params.kernel instanceof ArrayColumn ? params.kernel.array : params.kernel
361
+ const N = nextPow2(signal.length + kernel.length)
362
+ const complexResult = await fftConvolution(regl, signal, kernel)
363
+ return extractAndRepack(regl, complexResult, 'r', N)
276
364
  }
277
365
  schema(data) {
278
366
  return {
279
367
  type: 'object',
368
+ title: 'fftConvolution',
280
369
  properties: {
281
370
  signal: EXPRESSION_REF,
282
371
  kernel: EXPRESSION_REF
@@ -286,7 +375,4 @@ class FftConvolutionComputation extends TextureComputation {
286
375
  }
287
376
  }
288
377
 
289
- // fft1d: output is a complex texture — R = real part, G = imaginary part.
290
- // Use a downstream computation (e.g. magnitude) to get a single scalar per bin.
291
- registerTextureComputation('fft1d', new Fft1dComputation())
292
378
  registerTextureComputation('fftConvolution', new FftConvolutionComputation())
@@ -1,166 +1,162 @@
1
- import { registerTextureComputation, TextureComputation, EXPRESSION_REF } from "./ComputationRegistry.js"
2
-
3
- function toTexture(regl, input, length) {
4
- if (input instanceof Float32Array) {
5
- return regl.texture({ data: input, shape: [length, 1], type: 'float', format: 'rgba' });
6
- }
7
- return input; // already a texture
8
- }
1
+ import { registerTextureComputation, EXPRESSION_REF, resolveQuantityKind } from "./ComputationRegistry.js"
2
+ import { TextureComputation } from "../data/Computation.js"
3
+ import { ArrayColumn, SAMPLE_COLUMN_GLSL } from "../data/ColumnData.js"
9
4
 
10
5
  function subtractTextures(regl, texA, texB) {
11
- const length = texA.width;
12
- const outputTex = regl.texture({ width: length, height: 1, type: 'float', format: 'rgba' });
13
- const outputFBO = regl.framebuffer({ color: outputTex });
6
+ const w = texA.width
7
+ const h = texA.height
8
+ const outputTex = regl.texture({ width: w, height: h, type: 'float', format: 'rgba' })
9
+ const outputFBO = regl.framebuffer({ color: outputTex, depth: false, stencil: false })
14
10
 
15
- const drawSub = regl({
11
+ regl({
16
12
  framebuffer: outputFBO,
17
- vert: `
13
+ vert: `#version 300 es
18
14
  precision highp float;
19
- attribute float bin;
20
- void main() {
21
- float x = (bin + 0.5)/${length}.0*2.0 - 1.0;
22
- gl_Position = vec4(x,0.0,0.0,1.0);
23
- }
15
+ in vec2 position;
16
+ void main() { gl_Position = vec4(position, 0.0, 1.0); }
24
17
  `,
25
- frag: `
18
+ frag: `#version 300 es
26
19
  precision highp float;
27
20
  uniform sampler2D texA;
28
21
  uniform sampler2D texB;
29
22
  out vec4 fragColor;
30
23
  void main() {
31
- int idx = int(gl_FragCoord.x-0.5);
32
- float a = texelFetch(texA, ivec2(idx,0),0).r;
33
- float b = texelFetch(texB, ivec2(idx,0),0).r;
34
- fragColor = vec4(a-b,0.0,0.0,1.0);
24
+ ivec2 coord = ivec2(gl_FragCoord.xy);
25
+ fragColor = texelFetch(texA, coord, 0) - texelFetch(texB, coord, 0);
35
26
  }
36
27
  `,
37
- attributes: { bin: Array.from({ length }, (_, i) => i) },
28
+ attributes: { position: [[-1, -1], [1, -1], [-1, 1], [1, 1]] },
38
29
  uniforms: { texA, texB },
39
- count: length,
40
- primitive: 'points'
41
- });
30
+ count: 4,
31
+ primitive: 'triangle strip'
32
+ })()
42
33
 
43
- drawSub();
44
- return outputTex;
34
+ if (texA._dataLength !== undefined) outputTex._dataLength = texA._dataLength
35
+ return outputTex
45
36
  }
46
37
 
47
38
  /**
48
39
  * Generic 1D convolution filter
49
40
  * @param {regl} regl - regl context
50
- * @param {Float32Array | Texture} input - CPU array or GPU texture
51
- * @param {Float32Array} kernel - 1D kernel
52
- * @returns {Texture} - filtered output texture
41
+ * @param {Texture} inputTex - 4-packed GPU texture, _dataLength set
42
+ * @param {Float32Array} kernel - 1D kernel weights
43
+ * @returns {Texture} - filtered output texture (4-packed, same dimensions as input)
53
44
  */
54
- function filter1D(regl, input, kernel) {
55
- const length = input instanceof Float32Array ? input.length : input.width;
56
- const inputTex = toTexture(regl, input, length);
45
+ function filter1D(regl, inputTex, kernel) {
46
+ const length = inputTex._dataLength ?? inputTex.width * inputTex.height * 4
47
+ const w = inputTex.width
48
+ const h = inputTex.height
57
49
 
58
- const kernelTex = regl.texture({ data: kernel, shape: [kernel.length, 1], type: 'float' });
50
+ // Kernel texture stays R-channel (internal, not exposed via sampleColumn)
51
+ const kernelData = new Float32Array(kernel.length * 4)
52
+ for (let i = 0; i < kernel.length; i++) kernelData[i * 4] = kernel[i]
53
+ const kernelTex = regl.texture({ data: kernelData, shape: [kernel.length, 1], type: 'float', format: 'rgba' })
54
+ const outputTex = regl.texture({ width: w, height: h, type: 'float', format: 'rgba' })
55
+ const outputFBO = regl.framebuffer({ color: outputTex, depth: false, stencil: false })
59
56
 
60
- const outputTex = regl.texture({ width: length, height: 1, type: 'float', format: 'rgba' });
61
- const outputFBO = regl.framebuffer({ color: outputTex });
57
+ const radius = Math.floor(kernel.length / 2)
62
58
 
63
- const radius = Math.floor(kernel.length / 2);
64
-
65
- const drawFilter = regl({
59
+ regl({
66
60
  framebuffer: outputFBO,
67
- vert: `
61
+ vert: `#version 300 es
68
62
  precision highp float;
69
- attribute float bin;
70
- void main() {
71
- float x = (bin + 0.5)/${length}.0*2.0 - 1.0;
72
- gl_Position = vec4(x, 0.0, 0.0, 1.0);
73
- }
63
+ in vec2 position;
64
+ void main() { gl_Position = vec4(position, 0.0, 1.0); }
74
65
  `,
75
- frag: `
76
- #version 300 es
66
+ frag: `#version 300 es
77
67
  precision highp float;
68
+ precision highp sampler2D;
78
69
  uniform sampler2D inputTex;
79
70
  uniform sampler2D kernelTex;
80
71
  uniform int radius;
81
- uniform int length;
72
+ uniform int totalLength;
82
73
  out vec4 fragColor;
83
-
74
+ ${SAMPLE_COLUMN_GLSL}
84
75
  void main() {
85
- float idx = gl_FragCoord.x - 0.5;
86
- float sum = 0.0;
87
- for (int i=-16; i<=16; i++) { // max kernel radius 16
88
- if (i+16 >= radius*2+1) break;
89
- int sampleIdx = int(clamp(idx + float(i), 0.0, float(length-1)));
90
- float val = texelFetch(inputTex, ivec2(sampleIdx,0),0).r;
91
- float w = texelFetch(kernelTex, ivec2(i+radius,0),0).r;
92
- sum += val * w;
76
+ ivec2 sz = textureSize(inputTex, 0);
77
+ int texelI = int(gl_FragCoord.y) * sz.x + int(gl_FragCoord.x);
78
+ int base = texelI * 4;
79
+ float s0 = 0.0, s1 = 0.0, s2 = 0.0, s3 = 0.0;
80
+ for (int i = -16; i <= 16; i++) {
81
+ if (i + 16 >= radius * 2 + 1) break;
82
+ float kw = texelFetch(kernelTex, ivec2(i + radius, 0), 0).r;
83
+ s0 += sampleColumn(inputTex, float(clamp(base + 0 + i, 0, totalLength - 1))) * kw;
84
+ s1 += sampleColumn(inputTex, float(clamp(base + 1 + i, 0, totalLength - 1))) * kw;
85
+ s2 += sampleColumn(inputTex, float(clamp(base + 2 + i, 0, totalLength - 1))) * kw;
86
+ s3 += sampleColumn(inputTex, float(clamp(base + 3 + i, 0, totalLength - 1))) * kw;
93
87
  }
94
- fragColor = vec4(sum,0.0,0.0,1.0);
88
+ fragColor = vec4(s0, s1, s2, s3);
95
89
  }
96
90
  `,
97
- attributes: {
98
- bin: Array.from({ length }, (_, i) => i)
99
- },
100
- uniforms: {
101
- inputTex,
102
- kernelTex,
103
- radius,
104
- length
105
- },
106
- count: length,
107
- primitive: 'points'
108
- });
109
-
110
- drawFilter();
111
- return outputTex;
91
+ attributes: { position: [[-1, -1], [1, -1], [-1, 1], [1, 1]] },
92
+ uniforms: { inputTex, kernelTex, radius, totalLength: length },
93
+ count: 4,
94
+ primitive: 'triangle strip'
95
+ })()
96
+
97
+ outputTex._dataLength = length
98
+ return outputTex
112
99
  }
113
100
 
114
101
  // Gaussian kernel helper
115
102
  function gaussianKernel(size, sigma) {
116
- const radius = Math.floor(size / 2);
117
- const kernel = new Float32Array(size);
118
- let sum = 0;
103
+ const radius = Math.floor(size / 2)
104
+ const kernel = new Float32Array(size)
105
+ let sum = 0
119
106
  for (let i = -radius; i <= radius; i++) {
120
- const v = Math.exp(-0.5 * (i / sigma) ** 2);
121
- kernel[i + radius] = v;
122
- sum += v;
107
+ const v = Math.exp(-0.5 * (i / sigma) ** 2)
108
+ kernel[i + radius] = v
109
+ sum += v
123
110
  }
124
- for (let i = 0; i < size; i++) kernel[i] /= sum;
125
- return kernel;
111
+ for (let i = 0; i < size; i++) kernel[i] /= sum
112
+ return kernel
126
113
  }
127
114
 
128
115
  /**
129
116
  * Low-pass filter
117
+ * @param {regl} regl
118
+ * @param {Texture} inputTex - 4-packed GPU texture
130
119
  */
131
- function lowPass(regl, input, sigma = 3, kernelSize = null) {
132
- const size = kernelSize || Math.ceil(sigma*6)|1; // ensure odd
133
- const kernel = gaussianKernel(size, sigma);
134
- return filter1D(regl, input, kernel);
120
+ function lowPass(regl, inputTex, sigma = 3, kernelSize = null) {
121
+ const size = kernelSize || (Math.ceil(sigma * 6) | 1) // ensure odd
122
+ const kernel = gaussianKernel(size, sigma)
123
+ return filter1D(regl, inputTex, kernel)
135
124
  }
136
125
 
137
126
  /**
138
127
  * High-pass filter: subtract low-pass
128
+ * @param {regl} regl
129
+ * @param {Texture} inputTex - 4-packed GPU texture
139
130
  */
140
- function highPass(regl, input, sigma = 3, kernelSize = null) {
141
- const low = lowPass(regl, input, sigma, kernelSize);
142
- // high = input - low (using a shader)
143
- return subtractTextures(regl, input, low);
131
+ function highPass(regl, inputTex, sigma = 3, kernelSize = null) {
132
+ const low = lowPass(regl, inputTex, sigma, kernelSize)
133
+ return subtractTextures(regl, inputTex, low)
144
134
  }
145
135
 
146
136
  /**
147
137
  * Band-pass filter: difference of low-pass filters
138
+ * @param {regl} regl
139
+ * @param {Texture} inputTex - 4-packed GPU texture
148
140
  */
149
- function bandPass(regl, input, sigmaLow, sigmaHigh) {
150
- const lowHigh = lowPass(regl, input, sigmaHigh);
151
- const lowLow = lowPass(regl, input, sigmaLow);
152
- return subtractTextures(regl, lowHigh, lowLow);
141
+ function bandPass(regl, inputTex, sigmaLow, sigmaHigh) {
142
+ const lowHigh = lowPass(regl, inputTex, sigmaHigh)
143
+ const lowLow = lowPass(regl, inputTex, sigmaLow)
144
+ return subtractTextures(regl, lowHigh, lowLow)
153
145
  }
154
146
 
155
147
  export { filter1D, gaussianKernel, lowPass, highPass, bandPass }
156
148
 
157
149
  class Filter1DComputation extends TextureComputation {
158
- compute(regl, params) {
159
- return filter1D(regl, params.input, params.kernel)
150
+ getQuantityKind(params, data) { return resolveQuantityKind(params.input, data) }
151
+ compute(regl, inputs, getAxisDomain) {
152
+ const inputTex = inputs.input.toTexture(regl)
153
+ const kernelArr = inputs.kernel instanceof ArrayColumn ? inputs.kernel.array : inputs.kernel
154
+ return filter1D(regl, inputTex, kernelArr)
160
155
  }
161
156
  schema(data) {
162
157
  return {
163
158
  type: 'object',
159
+ title: 'filter1D',
164
160
  properties: {
165
161
  input: EXPRESSION_REF,
166
162
  kernel: EXPRESSION_REF
@@ -171,12 +167,14 @@ class Filter1DComputation extends TextureComputation {
171
167
  }
172
168
 
173
169
  class LowPassComputation extends TextureComputation {
174
- compute(regl, params) {
175
- return lowPass(regl, params.input, params.sigma, params.kernelSize)
170
+ getQuantityKind(params, data) { return resolveQuantityKind(params.input, data) }
171
+ compute(regl, inputs, getAxisDomain) {
172
+ return lowPass(regl, inputs.input.toTexture(regl), inputs.sigma, inputs.kernelSize)
176
173
  }
177
174
  schema(data) {
178
175
  return {
179
176
  type: 'object',
177
+ title: 'lowPass',
180
178
  properties: {
181
179
  input: EXPRESSION_REF,
182
180
  sigma: { type: 'number' },
@@ -188,12 +186,14 @@ class LowPassComputation extends TextureComputation {
188
186
  }
189
187
 
190
188
  class HighPassComputation extends TextureComputation {
191
- compute(regl, params) {
192
- return highPass(regl, params.input, params.sigma, params.kernelSize)
189
+ getQuantityKind(params, data) { return resolveQuantityKind(params.input, data) }
190
+ compute(regl, inputs, getAxisDomain) {
191
+ return highPass(regl, inputs.input.toTexture(regl), inputs.sigma, inputs.kernelSize)
193
192
  }
194
193
  schema(data) {
195
194
  return {
196
195
  type: 'object',
196
+ title: 'highPass',
197
197
  properties: {
198
198
  input: EXPRESSION_REF,
199
199
  sigma: { type: 'number' },
@@ -205,12 +205,14 @@ class HighPassComputation extends TextureComputation {
205
205
  }
206
206
 
207
207
  class BandPassComputation extends TextureComputation {
208
- compute(regl, params) {
209
- return bandPass(regl, params.input, params.sigmaLow, params.sigmaHigh)
208
+ getQuantityKind(params, data) { return resolveQuantityKind(params.input, data) }
209
+ compute(regl, inputs, getAxisDomain) {
210
+ return bandPass(regl, inputs.input.toTexture(regl), inputs.sigmaLow, inputs.sigmaHigh)
210
211
  }
211
212
  schema(data) {
212
213
  return {
213
214
  type: 'object',
215
+ title: 'bandPass',
214
216
  properties: {
215
217
  input: EXPRESSION_REF,
216
218
  sigmaLow: { type: 'number' },