gladly-plot 0.0.3 → 0.0.5

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 +1 -0
  2. package/package.json +16 -8
  3. package/src/axes/Axis.js +253 -0
  4. package/src/{AxisQuantityKindRegistry.js → axes/AxisQuantityKindRegistry.js} +7 -0
  5. package/src/{AxisRegistry.js → axes/AxisRegistry.js} +48 -0
  6. package/src/axes/ColorAxisRegistry.js +93 -0
  7. package/src/{FilterAxisRegistry.js → axes/FilterAxisRegistry.js} +63 -0
  8. package/src/axes/ZoomController.js +141 -0
  9. package/src/colorscales/BivariateColorscales.js +205 -0
  10. package/src/colorscales/ColorscaleRegistry.js +124 -0
  11. package/src/compute/ComputationRegistry.js +237 -0
  12. package/src/compute/axisFilter.js +47 -0
  13. package/src/compute/conv.js +230 -0
  14. package/src/compute/fft.js +292 -0
  15. package/src/compute/filter.js +227 -0
  16. package/src/compute/hist.js +180 -0
  17. package/src/compute/kde.js +102 -0
  18. package/src/{Layer.js → core/Layer.js} +4 -3
  19. package/src/{LayerType.js → core/LayerType.js} +72 -7
  20. package/src/core/Plot.js +735 -0
  21. package/src/{Colorbar.js → floats/Colorbar.js} +19 -5
  22. package/src/floats/Colorbar2d.js +77 -0
  23. package/src/{Filterbar.js → floats/Filterbar.js} +18 -4
  24. package/src/{FilterbarFloat.js → floats/Float.js} +17 -30
  25. package/src/{EpsgUtils.js → geo/EpsgUtils.js} +1 -1
  26. package/src/index.js +35 -22
  27. package/src/{ColorbarLayer.js → layers/ColorbarLayer.js} +2 -2
  28. package/src/layers/ColorbarLayer2d.js +97 -0
  29. package/src/{FilterbarLayer.js → layers/FilterbarLayer.js} +2 -2
  30. package/src/layers/HistogramLayer.js +212 -0
  31. package/src/layers/LinesLayer.js +199 -0
  32. package/src/layers/PointsLayer.js +114 -0
  33. package/src/layers/ScatterShared.js +142 -0
  34. package/src/{TileLayer.js → layers/TileLayer.js} +4 -4
  35. package/src/Axis.js +0 -48
  36. package/src/ColorAxisRegistry.js +0 -49
  37. package/src/ColorscaleRegistry.js +0 -52
  38. package/src/Float.js +0 -159
  39. package/src/Plot.js +0 -1068
  40. package/src/ScatterLayer.js +0 -133
  41. /package/src/{AxisLink.js → axes/AxisLink.js} +0 -0
  42. /package/src/{MatplotlibColorscales.js → colorscales/MatplotlibColorscales.js} +0 -0
  43. /package/src/{Data.js → core/Data.js} +0 -0
  44. /package/src/{LayerTypeRegistry.js → core/LayerTypeRegistry.js} +0 -0
@@ -0,0 +1,230 @@
1
+ import { fftConvolution } from "./fft.js"
2
+ import { registerTextureComputation, TextureComputation, EXPRESSION_REF } from "./ComputationRegistry.js"
3
+
4
+ /*
5
+ ============================================================
6
+ Utilities
7
+ ============================================================
8
+ */
9
+
10
+ const MAX_KERNEL_LOOP = 1024;
11
+
12
+ function nextPow2(n) {
13
+ return 1 << Math.ceil(Math.log2(n));
14
+ }
15
+
16
+ function make1DTexture(regl, data, width) {
17
+ return regl.texture({
18
+ data,
19
+ width,
20
+ height: 1,
21
+ format: "rgba",
22
+ type: "float",
23
+ wrap: "clamp",
24
+ min: "nearest",
25
+ mag: "nearest"
26
+ });
27
+ }
28
+
29
+ function makeFBO(regl, width) {
30
+ return regl.framebuffer({
31
+ color: regl.texture({
32
+ width,
33
+ height: 1,
34
+ format: "rgba",
35
+ type: "float"
36
+ })
37
+ });
38
+ }
39
+
40
+ /*
41
+ ============================================================
42
+ 1) Single-pass convolution (kernel ≤ 1024)
43
+ ============================================================
44
+ */
45
+
46
+ function singlePassConvolution(regl) {
47
+ return regl({
48
+ frag: `#version 300 es
49
+ precision highp float;
50
+ uniform sampler2D signal, kernel;
51
+ uniform int N, K;
52
+ out vec4 outColor;
53
+
54
+ void main() {
55
+ int x = int(gl_FragCoord.x);
56
+ float sum = 0.0;
57
+
58
+ for (int i = 0; i < ${MAX_KERNEL_LOOP}; i++) {
59
+ if (i >= K) break;
60
+ int xi = x - i;
61
+ if (xi < 0 || xi >= N) continue;
62
+
63
+ float s = texelFetch(signal, ivec2(xi, 0), 0).r;
64
+ float k = texelFetch(kernel, ivec2(i, 0), 0).r;
65
+ sum += s * k;
66
+ }
67
+
68
+ outColor = vec4(sum, 0, 0, 1);
69
+ }`,
70
+ vert: `#version 300 es
71
+ in vec2 position;
72
+ void main() {
73
+ gl_Position = vec4(position, 0, 1);
74
+ }`,
75
+ attributes: {
76
+ position: [[-1,-1],[1,-1],[-1,1],[1,1]]
77
+ },
78
+ uniforms: {
79
+ signal: regl.prop("signal"),
80
+ kernel: regl.prop("kernel"),
81
+ N: regl.prop("N"),
82
+ K: regl.prop("K")
83
+ },
84
+ framebuffer: regl.prop("fbo"),
85
+ count: 4,
86
+ primitive: "triangle strip"
87
+ });
88
+ }
89
+
90
+ /*
91
+ ============================================================
92
+ 2) Two-pass chunked convolution (arbitrary kernel size)
93
+ ============================================================
94
+ */
95
+
96
+ function chunkedConvolution(regl) {
97
+ const partialPass = regl({
98
+ frag: `#version 300 es
99
+ precision highp float;
100
+ uniform sampler2D signal, kernel2D;
101
+ uniform int N, chunkOffset;
102
+ out vec4 outColor;
103
+
104
+ void main() {
105
+ int x = int(gl_FragCoord.x);
106
+ float sum = 0.0;
107
+
108
+ for (int i = 0; i < ${MAX_KERNEL_LOOP}; i++) {
109
+ int kIndex = chunkOffset + i;
110
+ int xi = x - kIndex;
111
+ if (xi < 0 || xi >= N) continue;
112
+
113
+ float s = texelFetch(signal, ivec2(xi, 0), 0).r;
114
+ float k = texelFetch(kernel2D, ivec2(i, 0), 0).r;
115
+ sum += s * k;
116
+ }
117
+
118
+ outColor = vec4(sum, 0, 0, 1);
119
+ }`,
120
+ vert: `#version 300 es
121
+ in vec2 position;
122
+ void main() {
123
+ gl_Position = vec4(position, 0, 1);
124
+ }`,
125
+ attributes: {
126
+ position: [[-1,-1],[1,-1],[-1,1],[1,1]]
127
+ },
128
+ uniforms: {
129
+ signal: regl.prop("signal"),
130
+ kernel2D: regl.prop("kernel"),
131
+ N: regl.prop("N"),
132
+ chunkOffset: regl.prop("offset")
133
+ },
134
+ framebuffer: regl.prop("fbo"),
135
+ blend: {
136
+ enable: true,
137
+ func: { src: "one", dst: "one" }
138
+ },
139
+ count: 4,
140
+ primitive: "triangle strip"
141
+ });
142
+
143
+ return function run({ signalTex, kernel, N }) {
144
+ const chunks = Math.ceil(kernel.length / MAX_KERNEL_LOOP);
145
+ const fbo = makeFBO(regl, N);
146
+
147
+ regl.clear({ framebuffer: fbo, color: [0,0,0,0] });
148
+
149
+ for (let c = 0; c < chunks; c++) {
150
+ const slice = kernel.slice(
151
+ c * MAX_KERNEL_LOOP,
152
+ (c + 1) * MAX_KERNEL_LOOP
153
+ );
154
+
155
+ const kernelTex = make1DTexture(regl, slice, MAX_KERNEL_LOOP);
156
+
157
+ partialPass({
158
+ signal: signalTex,
159
+ kernel: kernelTex,
160
+ N,
161
+ offset: c * MAX_KERNEL_LOOP,
162
+ fbo
163
+ });
164
+ }
165
+
166
+ return fbo.color[0];
167
+ };
168
+ }
169
+
170
+ /*
171
+ ============================================================
172
+ Adaptive wrapper
173
+ ============================================================
174
+ */
175
+
176
+ export default function adaptiveConvolution(regl, signalArray, kernelArray) {
177
+ const single = singlePassConvolution(regl);
178
+ const chunked = chunkedConvolution(regl);
179
+
180
+ const N = signalArray.length;
181
+ const K = kernelArray.length;
182
+
183
+ const signalTex = make1DTexture(regl, signalArray, N);
184
+
185
+ // Case 1: single pass
186
+ if (K <= MAX_KERNEL_LOOP) {
187
+ const kernelTex = make1DTexture(regl, kernelArray, K);
188
+ const fbo = makeFBO(regl, N);
189
+
190
+ single({
191
+ signal: signalTex,
192
+ kernel: kernelTex,
193
+ N,
194
+ K,
195
+ fbo
196
+ });
197
+
198
+ return fbo.color[0];
199
+ }
200
+
201
+ // Case 2: chunked
202
+ if (K <= 8192) {
203
+ return chunked({
204
+ signalTex,
205
+ kernel: kernelArray,
206
+ N
207
+ });
208
+ }
209
+
210
+ // Case 3: FFT
211
+ return fftConvolution(regl, signalArray, kernelArray);
212
+ }
213
+
214
+ class ConvolutionComputation extends TextureComputation {
215
+ compute(regl, params) {
216
+ return adaptiveConvolution(regl, params.signal, params.kernel)
217
+ }
218
+ schema(data) {
219
+ return {
220
+ type: 'object',
221
+ properties: {
222
+ signal: EXPRESSION_REF,
223
+ kernel: EXPRESSION_REF
224
+ },
225
+ required: ['signal', 'kernel']
226
+ }
227
+ }
228
+ }
229
+
230
+ registerTextureComputation('convolution', new ConvolutionComputation())
@@ -0,0 +1,292 @@
1
+ import { registerTextureComputation, TextureComputation, EXPRESSION_REF } from "./ComputationRegistry.js"
2
+
3
+ /* ============================================================
4
+ Utilities
5
+ ============================================================ */
6
+
7
+ function nextPow2(n) {
8
+ return 1 << Math.ceil(Math.log2(n));
9
+ }
10
+
11
+ function makeComplexTexture(regl, data, N) {
12
+ // data: Float32Array (real), imag assumed 0
13
+ const texData = new Float32Array(N * 4);
14
+ for (let i = 0; i < data.length; i++) {
15
+ texData[i * 4] = data[i];
16
+ }
17
+ return regl.texture({
18
+ data: texData,
19
+ width: N,
20
+ height: 1,
21
+ format: "rgba",
22
+ type: "float",
23
+ min: "nearest",
24
+ mag: "nearest",
25
+ wrap: "clamp"
26
+ });
27
+ }
28
+
29
+ function makeEmptyComplexTexture(regl, N) {
30
+ return regl.texture({
31
+ width: N,
32
+ height: 1,
33
+ format: "rgba",
34
+ type: "float",
35
+ min: "nearest",
36
+ mag: "nearest",
37
+ wrap: "clamp"
38
+ });
39
+ }
40
+
41
+ function makeFBO(regl, tex) {
42
+ return regl.framebuffer({ color: tex });
43
+ }
44
+
45
+ /* ============================================================
46
+ Fullscreen quad
47
+ ============================================================ */
48
+
49
+ const quad = {
50
+ attributes: {
51
+ position: [[-1, -1], [1, -1], [-1, 1], [1, 1]]
52
+ },
53
+ count: 4,
54
+ primitive: "triangle strip"
55
+ };
56
+
57
+ /* ============================================================
58
+ FFT shaders
59
+ ============================================================ */
60
+
61
+ function bitReversePass(regl, N) {
62
+ return regl({
63
+ frag: `#version 300 es
64
+ precision highp float;
65
+ uniform sampler2D inputTex;
66
+ uniform int N;
67
+ out vec4 outColor;
68
+
69
+ int bitReverse(int x, int bits) {
70
+ int y = 0;
71
+ for (int i = 0; i < 16; i++) {
72
+ if (i >= bits) break;
73
+ y = (y << 1) | (x & 1);
74
+ x >>= 1;
75
+ }
76
+ return y;
77
+ }
78
+
79
+ void main() {
80
+ int x = int(gl_FragCoord.x);
81
+ int bits = int(log2(float(N)));
82
+ int rx = bitReverse(x, bits);
83
+ vec2 v = texelFetch(inputTex, ivec2(rx, 0), 0).rg;
84
+ outColor = vec4(v, 0.0, 1.0);
85
+ }`,
86
+ vert: `#version 300 es
87
+ in vec2 position;
88
+ void main() {
89
+ gl_Position = vec4(position, 0, 1);
90
+ }`,
91
+ uniforms: {
92
+ inputTex: regl.prop("input"),
93
+ N
94
+ },
95
+ framebuffer: regl.prop("fbo"),
96
+ ...quad
97
+ });
98
+ }
99
+
100
+ function fftStagePass(regl, stage, inverse) {
101
+ return regl({
102
+ frag: `#version 300 es
103
+ precision highp float;
104
+ uniform sampler2D inputTex;
105
+ uniform int stage;
106
+ uniform int N;
107
+ out vec4 outColor;
108
+
109
+ void main() {
110
+ int x = int(gl_FragCoord.x);
111
+ int half = stage >> 1;
112
+ int block = (x / stage) * stage;
113
+ int i = block + (x % half);
114
+ int j = i + half;
115
+
116
+ vec2 a = texelFetch(inputTex, ivec2(i, 0), 0).rg;
117
+ vec2 b = texelFetch(inputTex, ivec2(j, 0), 0).rg;
118
+
119
+ float sign = ${inverse ? "1.0" : "-1.0"};
120
+ float angle = sign * 6.28318530718 * float(x % stage) / float(stage);
121
+ vec2 w = vec2(cos(angle), sin(angle));
122
+
123
+ vec2 t = vec2(
124
+ b.x * w.x - b.y * w.y,
125
+ b.x * w.y + b.y * w.x
126
+ );
127
+
128
+ vec2 outv = (x < j) ? a + t : a - t;
129
+ outColor = vec4(outv, 0.0, 1.0);
130
+ }`,
131
+ vert: `#version 300 es
132
+ in vec2 position;
133
+ void main() {
134
+ gl_Position = vec4(position, 0, 1);
135
+ }`,
136
+ uniforms: {
137
+ inputTex: regl.prop("input"),
138
+ stage,
139
+ N: regl.prop("N")
140
+ },
141
+ framebuffer: regl.prop("fbo"),
142
+ ...quad
143
+ });
144
+ }
145
+
146
+ function scalePass(regl, N) {
147
+ return regl({
148
+ frag: `#version 300 es
149
+ precision highp float;
150
+ uniform sampler2D inputTex;
151
+ out vec4 outColor;
152
+ void main() {
153
+ vec2 v = texelFetch(inputTex, ivec2(int(gl_FragCoord.x),0),0).rg;
154
+ outColor = vec4(v / float(${N}), 0.0, 1.0);
155
+ }`,
156
+ vert: `#version 300 es
157
+ in vec2 position;
158
+ void main() {
159
+ gl_Position = vec4(position, 0, 1);
160
+ }`,
161
+ uniforms: {
162
+ inputTex: regl.prop("input")
163
+ },
164
+ framebuffer: regl.prop("fbo"),
165
+ ...quad
166
+ });
167
+ }
168
+
169
+ /* ============================================================
170
+ Public: GPU FFT (top-level API)
171
+ ============================================================ */
172
+
173
+ export function fft1d(regl, realArray, inverse = false) {
174
+ const N = nextPow2(realArray.length);
175
+
176
+ let texA = makeComplexTexture(regl, realArray, N);
177
+ let texB = makeEmptyComplexTexture(regl, N);
178
+ let fboA = makeFBO(regl, texA);
179
+ let fboB = makeFBO(regl, texB);
180
+
181
+ // bit reversal
182
+ bitReversePass(regl, N)({
183
+ input: texA,
184
+ fbo: fboB
185
+ });
186
+ [fboA, fboB] = [fboB, fboA];
187
+
188
+ // FFT stages
189
+ const stages = Math.log2(N);
190
+ for (let s = 1; s <= stages; s++) {
191
+ fftStagePass(regl, 1 << s, inverse)({
192
+ input: fboA,
193
+ N,
194
+ fbo: fboB
195
+ });
196
+ [fboA, fboB] = [fboB, fboA];
197
+ }
198
+
199
+ // scale for inverse
200
+ if (inverse) {
201
+ scalePass(regl, N)({
202
+ input: fboA,
203
+ fbo: fboB
204
+ });
205
+ return fboB.color[0];
206
+ }
207
+
208
+ return fboA.color[0];
209
+ }
210
+
211
+ /* ============================================================
212
+ FFT-based convolution
213
+ ============================================================ */
214
+
215
+ export function fftConvolution(regl, signal, kernel) {
216
+ const N = nextPow2(signal.length + kernel.length);
217
+
218
+ const sigTex = fft1d(regl, signal, false);
219
+ const kerTex = fft1d(regl, kernel, false);
220
+
221
+ const outTex = makeEmptyComplexTexture(regl, N);
222
+ const outFBO = makeFBO(regl, outTex);
223
+
224
+ // pointwise complex multiply
225
+ regl({
226
+ frag: `#version 300 es
227
+ precision highp float;
228
+ uniform sampler2D A, B;
229
+ out vec4 outColor;
230
+ void main() {
231
+ int x = int(gl_FragCoord.x);
232
+ vec2 a = texelFetch(A, ivec2(x,0),0).rg;
233
+ vec2 b = texelFetch(B, ivec2(x,0),0).rg;
234
+ outColor = vec4(
235
+ a.x*b.x - a.y*b.y,
236
+ a.x*b.y + a.y*b.x,
237
+ 0, 1
238
+ );
239
+ }`,
240
+ vert: `#version 300 es
241
+ in vec2 position;
242
+ void main() {
243
+ gl_Position = vec4(position,0,1);
244
+ }`,
245
+ uniforms: {
246
+ A: sigTex,
247
+ B: kerTex
248
+ },
249
+ framebuffer: outFBO,
250
+ ...quad
251
+ })();
252
+
253
+ // inverse FFT
254
+ return fft1d(regl, new Float32Array(N), true);
255
+ }
256
+
257
+ class Fft1dComputation extends TextureComputation {
258
+ compute(regl, params) {
259
+ return fft1d(regl, params.input, params.inverse ?? false)
260
+ }
261
+ schema(data) {
262
+ return {
263
+ type: 'object',
264
+ properties: {
265
+ input: EXPRESSION_REF,
266
+ inverse: { type: 'boolean' }
267
+ },
268
+ required: ['input']
269
+ }
270
+ }
271
+ }
272
+
273
+ class FftConvolutionComputation extends TextureComputation {
274
+ compute(regl, params) {
275
+ return fftConvolution(regl, params.signal, params.kernel)
276
+ }
277
+ schema(data) {
278
+ return {
279
+ type: 'object',
280
+ properties: {
281
+ signal: EXPRESSION_REF,
282
+ kernel: EXPRESSION_REF
283
+ },
284
+ required: ['signal', 'kernel']
285
+ }
286
+ }
287
+ }
288
+
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
+ registerTextureComputation('fftConvolution', new FftConvolutionComputation())