gladly-plot 0.0.5 → 0.0.7

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 +253 -92
  26. package/src/core/Plot.js +644 -162
  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
@@ -2,20 +2,52 @@ import { Layer } from "./Layer.js"
2
2
  import { buildColorGlsl } from "../colorscales/ColorscaleRegistry.js"
3
3
  import { buildFilterGlsl } from "../axes/FilterAxisRegistry.js"
4
4
  import { resolveAttributeExpr } from "../compute/ComputationRegistry.js"
5
+ import { SAMPLE_COLUMN_GLSL, SAMPLE_COLUMN_ND_GLSL } from "../data/ColumnData.js"
5
6
 
6
7
  function buildSpatialGlsl() {
7
- return `float normalize_axis(float v, vec2 domain, float scaleType) {
8
+ return `uniform vec2 xDomain;
9
+ uniform vec2 yDomain;
10
+ uniform vec2 zDomain;
11
+ uniform float xScaleType;
12
+ uniform float yScaleType;
13
+ uniform float zScaleType;
14
+ uniform float u_is3D;
15
+ uniform mat4 u_mvp;
16
+ out vec3 v_clip_pos;
17
+ float normalize_axis(float v, vec2 domain, float scaleType) {
8
18
  float vt = scaleType > 0.5 ? log(v) : v;
9
19
  float d0 = scaleType > 0.5 ? log(domain.x) : domain.x;
10
20
  float d1 = scaleType > 0.5 ? log(domain.y) : domain.y;
11
21
  return (vt - d0) / (d1 - d0);
22
+ }
23
+ vec4 plot_pos_3d(vec3 pos) {
24
+ float nx = normalize_axis(pos.x, xDomain, xScaleType);
25
+ float ny = normalize_axis(pos.y, yDomain, yScaleType);
26
+ float nz = normalize_axis(pos.z, zDomain, zScaleType);
27
+ v_clip_pos = vec3(nx, ny, nz);
28
+ return u_mvp * vec4(nx*2.0-1.0, ny*2.0-1.0, nz*2.0-1.0, 1.0);
29
+ }
30
+ vec4 plot_pos(vec2 pos) {
31
+ float nx = normalize_axis(pos.x, xDomain, xScaleType);
32
+ float ny = normalize_axis(pos.y, yDomain, yScaleType);
33
+ if (u_is3D > 0.5) {
34
+ return plot_pos_3d(vec3(pos, zDomain.x));
35
+ }
36
+ v_clip_pos = vec3(nx, ny, 0.5);
37
+ return u_mvp * vec4(nx*2.0-1.0, ny*2.0-1.0, 0.0, 1.0);
12
38
  }`
13
39
  }
14
40
 
41
+ function buildClipFragGlsl() {
42
+ return `in vec3 v_clip_pos;
43
+ uniform float u_is3D;`
44
+ }
45
+
15
46
  function buildApplyColorGlsl() {
16
- return `uniform float u_pickingMode;
47
+ return `out vec4 fragColor;
48
+ uniform float u_pickingMode;
17
49
  uniform float u_pickLayerIndex;
18
- varying float v_pickId;
50
+ in float v_pickId;
19
51
  vec4 gladly_apply_color(vec4 color) {
20
52
  if (u_pickingMode > 0.5) {
21
53
  float layerIdx = u_pickLayerIndex + 1.0;
@@ -31,15 +63,20 @@ vec4 gladly_apply_color(vec4 color) {
31
63
  }`
32
64
  }
33
65
 
34
- // Removes `attribute [precision] type varName;` from GLSL source.
35
66
  function removeAttributeDecl(src, varName) {
36
67
  return src.replace(
37
- new RegExp(`[ \\t]*attribute\\s+(?:(?:lowp|mediump|highp)\\s+)?\\w+\\s+${varName}\\s*;[ \\t]*\\n?`, 'g'),
68
+ new RegExp(`[ \\t]*in\\s+(?:(?:lowp|mediump|highp)\\s+)?\\w+\\s+${varName}\\s*;[ \\t]*\\n?`, 'g'),
69
+ ''
70
+ )
71
+ }
72
+
73
+ function removeUniformDecl(src, varName) {
74
+ return src.replace(
75
+ new RegExp(`[ \\t]*uniform\\s+(?:(?:lowp|mediump|highp)\\s+)?\\w+\\s+${varName}\\s*;[ \\t]*\\n?`, 'g'),
38
76
  ''
39
77
  )
40
78
  }
41
79
 
42
- // Injects code immediately after `void main() {`.
43
80
  function injectIntoMainStart(src, code) {
44
81
  return src.replace(
45
82
  /void\s+main\s*\(\s*(?:void\s*)?\)\s*\{/,
@@ -69,24 +106,27 @@ function injectInto(src, helpers) {
69
106
  export class LayerType {
70
107
  constructor({
71
108
  name,
72
- // Optional static axis declarations (for schema/introspection — no function call needed)
73
109
  xAxis, xAxisQuantityKind,
74
110
  yAxis, yAxisQuantityKind,
111
+ zAxis, zAxisQuantityKind,
75
112
  colorAxisQuantityKinds,
113
+ colorAxis2dQuantityKinds,
76
114
  filterAxisQuantityKinds,
77
- // Optional dynamic resolver — overrides statics wherever it returns a non-undefined value
78
115
  getAxisConfig,
79
- // GPU rendering
80
- vert, frag, schema, createLayer, createDrawCommand
116
+ vert, frag, schema, createLayer, createDrawCommand,
117
+ suppressWarnings = false
81
118
  }) {
82
119
  this.name = name
83
- // Static declarations stored as-is (undefined = not declared)
120
+ this.suppressWarnings = suppressWarnings
84
121
  this.xAxis = xAxis
85
122
  this.xAxisQuantityKind = xAxisQuantityKind
86
123
  this.yAxis = yAxis
87
124
  this.yAxisQuantityKind = yAxisQuantityKind
88
- this.colorAxisQuantityKinds = colorAxisQuantityKinds ?? []
89
- this.filterAxisQuantityKinds = filterAxisQuantityKinds ?? []
125
+ this.zAxis = zAxis ?? null
126
+ this.zAxisQuantityKind = zAxisQuantityKind ?? null
127
+ this.colorAxisQuantityKinds = colorAxisQuantityKinds ?? {}
128
+ this.colorAxis2dQuantityKinds = colorAxis2dQuantityKinds ?? {}
129
+ this.filterAxisQuantityKinds = filterAxisQuantityKinds ?? {}
90
130
  this.vert = vert
91
131
  this.frag = frag
92
132
 
@@ -96,122 +136,237 @@ export class LayerType {
96
136
  if (createDrawCommand) this.createDrawCommand = createDrawCommand
97
137
  }
98
138
 
99
- createDrawCommand(regl, layer, plot) {
100
- const nm = layer.nameMap
101
- // Rename an internal name to the shader-visible name via nameMap (identity if absent).
102
- const shaderName = (internalName) => nm[internalName] ?? internalName
103
- // Build a single-entry uniform object with renamed key reading from the internal prop name.
104
- const u = (internalName) => ({ [shaderName(internalName)]: regl.prop(internalName) })
105
-
106
- // --- Resolve computed attributes ---
139
+ async createDrawCommand(regl, layer, plot) {
140
+ // --- Resolve attributes ---
107
141
  let vertSrc = this.vert
108
- const originalBufferAttrs = {} // internal_name → Float32Array
109
- const computedBufferAttrs = {} // shader_name Float32Array (from context.bufferAttrs)
110
- const allTextureUniforms = {}
111
- const allScalarUniforms = {}
112
- const allGlobalDecls = []
113
- const mainInjections = []
114
- const allAxisUpdaters = []
142
+ const bufferAttrs = {} // name → Float32Array (fixed geometry)
143
+ const allTextures = {} // uniformName → () => texture
144
+ const allDataColumns = [] // ColumnData instances for refresh()
145
+ const mainInjections = [] // float name = expr; injected inside main()
146
+
147
+ const ndTextures = {} // sampler2D textures for nD columns only (also in allTextures)
148
+ const ndColumnHelperLines = [] // shape uniform decls + typed wrapper fns
149
+ const ndShapeUniforms = {} // { u_col_name_shape: [x, y, z, w] }
115
150
 
116
151
  for (const [key, expr] of Object.entries(layer.attributes)) {
117
- const result = resolveAttributeExpr(regl, expr, shaderName(key), plot)
152
+ const result = await resolveAttributeExpr(regl, expr, key, plot)
118
153
  if (result.kind === 'buffer') {
119
- originalBufferAttrs[key] = result.value
154
+ bufferAttrs[key] = result.value
120
155
  } else {
121
- vertSrc = removeAttributeDecl(vertSrc, shaderName(key))
122
- Object.assign(computedBufferAttrs, result.context.bufferAttrs)
123
- Object.assign(allTextureUniforms, result.context.textureUniforms)
124
- Object.assign(allScalarUniforms, result.context.scalarUniforms)
125
- allGlobalDecls.push(...result.context.globalDecls)
126
- mainInjections.push(`float ${shaderName(key)} = ${result.glslExpr};`)
127
- allAxisUpdaters.push(...result.context.axisUpdaters)
156
+ vertSrc = removeAttributeDecl(vertSrc, key)
157
+ Object.assign(allTextures, result.textures)
158
+ allDataColumns.push(result.col)
159
+
160
+ if (result.col.ndim === 1) {
161
+ mainInjections.push(`float ${key} = ${result.glslExpr};`)
162
+ } else {
163
+ // nD column: inject shape uniform + typed wrapper; skip auto-assignment
164
+ const uName = `u_col_${key}`
165
+ const s = result.col.shape
166
+ ndShapeUniforms[`${uName}_shape`] = [s[0] ?? 1, s[1] ?? 1, s[2] ?? 1, s[3] ?? 1]
167
+ Object.assign(ndTextures, result.textures)
168
+ const ndim = result.col.ndim
169
+ const ivecType = ndim <= 2 ? 'ivec2' : ndim === 3 ? 'ivec3' : 'ivec4'
170
+ const idxExpand = ndim <= 2 ? `ivec4(idx, 0, 0)` : ndim === 3 ? `ivec4(idx, 0)` : `idx`
171
+ ndColumnHelperLines.push(
172
+ `uniform ivec4 ${uName}_shape;`,
173
+ `float sample_${key}(${ivecType} idx) {`,
174
+ ` return sampleColumnND(${uName}, ${uName}_shape, ${idxExpand});`,
175
+ `}`
176
+ )
177
+ }
128
178
  }
129
179
  }
130
180
 
131
- layer._axisUpdaters = allAxisUpdaters
181
+ layer._dataColumns = allDataColumns
132
182
 
133
183
  if (mainInjections.length > 0) {
134
184
  vertSrc = injectIntoMainStart(vertSrc, mainInjections.join('\n '))
135
185
  }
136
186
 
137
- // Merged buffer attrs by shader name — used for vertex count fallback.
138
- const allShaderBuffers = {
139
- ...Object.fromEntries(Object.entries(originalBufferAttrs).map(([k, v]) => [shaderName(k), v])),
140
- ...computedBufferAttrs
187
+ // Validate buffer attributes
188
+ if (!this.suppressWarnings) {
189
+ for (const [key, buf] of Object.entries(bufferAttrs)) {
190
+ if (buf instanceof Float32Array && buf.length === 0) {
191
+ console.warn(`[gladly] Layer '${this.name}': buffer attribute '${key}' is an empty Float32Array — this layer may draw nothing`)
192
+ }
193
+ }
141
194
  }
142
195
 
196
+ // Pick IDs
143
197
  const isInstanced = layer.instanceCount !== null
144
198
  const pickCount = isInstanced ? layer.instanceCount :
145
- (layer.vertexCount ?? allShaderBuffers[shaderName('x')]?.length
146
- ?? Object.values(layer.attributes).find(v => v instanceof Float32Array)?.length ?? 0)
199
+ (layer.vertexCount ??
200
+ Object.values(bufferAttrs).find(v => v instanceof Float32Array)?.length ?? 0)
201
+ if (pickCount === 0 && !this.suppressWarnings) {
202
+ console.warn(
203
+ `[gladly] Layer '${this.name}': ` +
204
+ `${isInstanced ? 'instanceCount' : 'vertex count'} resolved to 0 — ` +
205
+ `this layer will draw nothing. Check that data columns are non-empty.`
206
+ )
207
+ }
147
208
  const pickIds = new Float32Array(pickCount)
148
209
  for (let i = 0; i < pickCount; i++) pickIds[i] = i
149
210
 
150
- // --- Build regl attributes ---
211
+ // Sampler declarations for column textures
212
+ const samplerDecls = Object.keys(allTextures)
213
+ .map(n => `uniform sampler2D ${n};`)
214
+ .join('\n')
215
+
216
+ // Build regl attributes
151
217
  const attributes = {
152
- // Original buffer attrs with possible divisors
153
218
  ...Object.fromEntries(
154
- Object.entries(originalBufferAttrs).map(([key, buffer]) => {
219
+ Object.entries(bufferAttrs).map(([key, buffer]) => {
155
220
  const divisor = layer.attributeDivisors[key]
156
- const attrObj = divisor !== undefined ? { buffer, divisor } : { buffer }
157
- return [shaderName(key), attrObj]
221
+ return [key, divisor !== undefined ? { buffer, divisor } : { buffer }]
158
222
  })
159
223
  ),
160
- // Computed buffer attrs (no divisors; already shader-named)
161
- ...Object.fromEntries(
162
- Object.entries(computedBufferAttrs).map(([shaderKey, buffer]) => [shaderKey, { buffer }])
163
- ),
164
- a_pickId: isInstanced ? { buffer: regl.buffer(pickIds), divisor: 1 } : regl.buffer(pickIds)
224
+ a_pickId: isInstanced ? { buffer: pickIds, divisor: 1 } : { buffer: pickIds }
165
225
  }
166
226
 
167
- // --- Build uniforms ---
227
+ // Build uniforms
168
228
  const uniforms = {
169
- ...u("xDomain"),
170
- ...u("yDomain"),
171
- ...u("xScaleType"),
172
- ...u("yScaleType"),
173
- u_pickingMode: regl.prop('u_pickingMode'),
229
+ ...ndShapeUniforms,
230
+ xDomain: regl.prop("xDomain"),
231
+ yDomain: regl.prop("yDomain"),
232
+ zDomain: regl.prop("zDomain"),
233
+ xScaleType: regl.prop("xScaleType"),
234
+ yScaleType: regl.prop("yScaleType"),
235
+ zScaleType: regl.prop("zScaleType"),
236
+ u_is3D: regl.prop("u_is3D"),
237
+ u_mvp: regl.prop("u_mvp"),
238
+ u_pickingMode: regl.prop('u_pickingMode'),
174
239
  u_pickLayerIndex: regl.prop('u_pickLayerIndex'),
175
- ...Object.fromEntries(
176
- Object.entries(layer.uniforms).map(([key, value]) => [shaderName(key), value])
177
- ),
178
- ...allTextureUniforms,
179
- ...allScalarUniforms
240
+ ...layer.uniforms,
241
+ ...Object.fromEntries(Object.entries(allTextures).map(([k, fn]) => [k, fn]))
180
242
  }
181
243
 
182
- // Add per-color-axis uniforms (colorscale index + range + scale type), keyed by quantity kind
183
- for (const qk of layer.colorAxes) {
184
- Object.assign(uniforms, u(`colorscale_${qk}`), u(`color_range_${qk}`), u(`color_scale_type_${qk}`))
244
+ for (const [suffix, qk] of Object.entries(layer.colorAxes)) {
245
+ const pk = qk.replace(/\./g, '_')
246
+ uniforms[`colorscale${suffix}`] = regl.prop(`colorscale_${pk}`)
247
+ uniforms[`color_range${suffix}`] = regl.prop(`color_range_${pk}`)
248
+ uniforms[`color_scale_type${suffix}`] = regl.prop(`color_scale_type_${pk}`)
249
+ uniforms[`alpha_blend${suffix}`] = regl.prop(`alpha_blend_${pk}`)
185
250
  }
186
251
 
187
- // Add per-filter-axis uniforms (vec4: [min, max, hasMin, hasMax] + scale type), keyed by quantity kind
188
- for (const qk of layer.filterAxes) {
189
- Object.assign(uniforms, u(`filter_range_${qk}`), u(`filter_scale_type_${qk}`))
252
+ for (const [suffix, qk] of Object.entries(layer.filterAxes)) {
253
+ const pk = qk.replace(/\./g, '_')
254
+ uniforms[`filter_range${suffix}`] = regl.prop(`filter_range_${pk}`)
255
+ uniforms[`filter_scale_type${suffix}`] = regl.prop(`filter_scale_type_${pk}`)
190
256
  }
191
257
 
192
- // Inject GLSL helpers before the layer shader body.
258
+ // Strip spatial uniforms from vert (re-declared in buildSpatialGlsl)
259
+ vertSrc = removeUniformDecl(vertSrc, 'xDomain')
260
+ vertSrc = removeUniformDecl(vertSrc, 'yDomain')
261
+ vertSrc = removeUniformDecl(vertSrc, 'zDomain')
262
+ vertSrc = removeUniformDecl(vertSrc, 'xScaleType')
263
+ vertSrc = removeUniformDecl(vertSrc, 'yScaleType')
264
+ vertSrc = removeUniformDecl(vertSrc, 'zScaleType')
265
+ vertSrc = removeUniformDecl(vertSrc, 'u_is3D')
266
+ vertSrc = removeUniformDecl(vertSrc, 'u_mvp')
267
+
193
268
  const spatialGlsl = buildSpatialGlsl()
194
- const colorGlsl = layer.colorAxes.length > 0 ? buildColorGlsl() : ''
195
- const filterGlsl = layer.filterAxes.length > 0 ? buildFilterGlsl() : ''
196
- const pickVertDecls = `attribute float a_pickId;\nvarying float v_pickId;`
269
+ const colorGlsl = (Object.keys(layer.colorAxes).length > 0 || Object.keys(layer.colorAxes2d).length > 0) ? buildColorGlsl() : ''
270
+ const filterGlsl = Object.keys(layer.filterAxes).length > 0 ? buildFilterGlsl() : ''
271
+ const pickVertDecls = `in float a_pickId;\nout float v_pickId;`
272
+
273
+ const hasNdColumns = ndColumnHelperLines.length > 0
274
+ const ndColumnHelpersStr = ndColumnHelperLines.join('\n')
275
+
276
+ // Column helpers for vert: precision + all samplers + sampleColumn + sampleColumnND + wrappers
277
+ const columnHelpers = allDataColumns.length > 0
278
+ ? [
279
+ 'precision highp sampler2D;',
280
+ samplerDecls,
281
+ SAMPLE_COLUMN_GLSL,
282
+ hasNdColumns ? SAMPLE_COLUMN_ND_GLSL : '',
283
+ ndColumnHelpersStr,
284
+ ].filter(Boolean).join('\n')
285
+ : ''
286
+
287
+ // Column helpers for frag: only nD column samplers + sampleColumnND + wrappers
288
+ // (1D columns stay in vert; their a_pickId is a vertex attribute)
289
+ const ndFragHelpers = hasNdColumns
290
+ ? [
291
+ 'precision highp sampler2D;',
292
+ ...Object.keys(ndTextures).map(n => `uniform sampler2D ${n};`),
293
+ SAMPLE_COLUMN_ND_GLSL,
294
+ ndColumnHelpersStr,
295
+ ].join('\n')
296
+ : ''
297
+
298
+ const fnSuffix = (s) => s.replace(/^_+/, '')
299
+
300
+ const colorHelperLines = []
301
+ let fragSrc = this.frag
302
+ for (const [suffix] of Object.entries(layer.colorAxes)) {
303
+ colorHelperLines.push(
304
+ `uniform int colorscale${suffix};`,
305
+ `uniform vec2 color_range${suffix};`,
306
+ `uniform float color_scale_type${suffix};`,
307
+ `uniform float alpha_blend${suffix};`,
308
+ `vec4 map_color_${fnSuffix(suffix)}(float value) {`,
309
+ ` return map_color_s(colorscale${suffix}, color_range${suffix}, value, color_scale_type${suffix}, alpha_blend${suffix});`,
310
+ `}`,
311
+ `vec4 map_color_2d_x_${fnSuffix(suffix)}(float value) {`,
312
+ ` return map_color_s_2d(colorscale${suffix}, color_range${suffix}, value, color_scale_type${suffix}, alpha_blend${suffix},`,
313
+ ` colorscale${suffix}, color_range${suffix}, 0.0/0.0, color_scale_type${suffix}, 0.0);`,
314
+ `}`,
315
+ `vec4 map_color_2d_y_${fnSuffix(suffix)}(float value) {`,
316
+ ` return map_color_s_2d(colorscale${suffix}, color_range${suffix}, 0.0/0.0, color_scale_type${suffix}, 0.0,`,
317
+ ` colorscale${suffix}, color_range${suffix}, value, color_scale_type${suffix}, alpha_blend${suffix});`,
318
+ `}`
319
+ )
320
+ fragSrc = removeUniformDecl(fragSrc, `colorscale${suffix}`)
321
+ fragSrc = removeUniformDecl(fragSrc, `color_range${suffix}`)
322
+ fragSrc = removeUniformDecl(fragSrc, `color_scale_type${suffix}`)
323
+ fragSrc = removeUniformDecl(fragSrc, `alpha_blend${suffix}`)
324
+ }
325
+ const colorHelpers = colorHelperLines.join('\n')
326
+
327
+ const color2dHelperLines = []
328
+ for (const [suffix2d, [s1, s2]] of Object.entries(layer.colorAxes2d)) {
329
+ color2dHelperLines.push(
330
+ `vec4 map_color_2d_${fnSuffix(suffix2d)}(vec2 values) {`,
331
+ ` return map_color_s_2d(colorscale${s1}, color_range${s1}, values.x, color_scale_type${s1}, alpha_blend${s1},`,
332
+ ` colorscale${s2}, color_range${s2}, values.y, color_scale_type${s2}, alpha_blend${s2});`,
333
+ `}`
334
+ )
335
+ }
336
+ const color2dHelpers = color2dHelperLines.join('\n')
337
+
338
+ const filterHelperLines = []
339
+ for (const [suffix] of Object.entries(layer.filterAxes)) {
340
+ filterHelperLines.push(
341
+ `uniform vec4 filter_range${suffix};`,
342
+ `bool filter_${fnSuffix(suffix)}(float value) {`,
343
+ ` return filter_in_range(filter_range${suffix}, value);`,
344
+ `}`
345
+ )
346
+ vertSrc = removeUniformDecl(vertSrc, `filter_range${suffix}`)
347
+ }
348
+ const filterHelpers = filterHelperLines.join('\n')
197
349
 
350
+ const clipFragDiscard = `if (u_is3D > 0.5 && (v_clip_pos.x < 0.0 || v_clip_pos.x > 1.0 || v_clip_pos.y < 0.0 || v_clip_pos.y > 1.0 || v_clip_pos.z < 0.0 || v_clip_pos.z > 1.0)) discard;`
198
351
  const drawConfig = {
199
- vert: injectPickIdAssignment(injectInto(vertSrc, [spatialGlsl, filterGlsl, pickVertDecls, ...allGlobalDecls])),
200
- frag: injectInto(this.frag, [buildApplyColorGlsl(), colorGlsl, filterGlsl]),
352
+ vert: injectPickIdAssignment(injectInto(vertSrc, [spatialGlsl, filterGlsl, filterHelpers, columnHelpers, pickVertDecls])),
353
+ frag: injectIntoMainStart(injectInto(fragSrc, [buildApplyColorGlsl(), buildClipFragGlsl(), colorGlsl, colorHelpers, color2dHelpers, filterGlsl, filterHelpers, ndFragHelpers]), clipFragDiscard),
201
354
  attributes,
202
355
  uniforms,
203
356
  viewport: regl.prop("viewport"),
204
357
  primitive: layer.primitive,
205
358
  lineWidth: layer.lineWidth,
206
359
  count: regl.prop("count"),
207
- ...(layer.blend ? { blend: layer.blend } : {})
360
+ ...(Object.keys(layer.colorAxes).length > 0 || Object.keys(layer.colorAxes2d).length > 0
361
+ ? { blend: { enable: true, func: { srcRGB: 'src alpha', dstRGB: 'one minus src alpha', srcAlpha: 0, dstAlpha: 1 } } }
362
+ : layer.blend ? { blend: layer.blend } : {})
208
363
  }
209
364
 
210
365
  if (layer.instanceCount !== null) {
211
366
  drawConfig.instances = regl.prop("instances")
212
367
  }
213
368
 
214
- return regl(drawConfig)
369
+ return drawConfig
215
370
  }
216
371
 
217
372
  schema(data) {
@@ -219,43 +374,46 @@ export class LayerType {
219
374
  throw new Error(`LayerType '${this.name}' does not implement schema()`)
220
375
  }
221
376
 
222
- // Resolves axis config by merging static declarations with dynamic getAxisConfig output.
223
- // Dynamic values (non-undefined) override static values.
224
377
  resolveAxisConfig(parameters, data) {
225
378
  const resolved = {
226
379
  xAxis: this.xAxis,
227
380
  xAxisQuantityKind: this.xAxisQuantityKind,
228
381
  yAxis: this.yAxis,
229
382
  yAxisQuantityKind: this.yAxisQuantityKind,
230
- colorAxisQuantityKinds: [...this.colorAxisQuantityKinds],
231
- filterAxisQuantityKinds: [...this.filterAxisQuantityKinds],
383
+ zAxis: this.zAxis,
384
+ zAxisQuantityKind: this.zAxisQuantityKind,
385
+ colorAxisQuantityKinds: { ...this.colorAxisQuantityKinds },
386
+ colorAxis2dQuantityKinds: { ...this.colorAxis2dQuantityKinds },
387
+ filterAxisQuantityKinds: { ...this.filterAxisQuantityKinds },
232
388
  }
233
389
 
234
390
  if (this._getAxisConfig) {
235
391
  const dynamic = this._getAxisConfig.call(this, parameters, data)
236
- if (dynamic.xAxis !== undefined) resolved.xAxis = dynamic.xAxis
237
- if (dynamic.xAxisQuantityKind !== undefined) resolved.xAxisQuantityKind = dynamic.xAxisQuantityKind
238
- if (dynamic.yAxis !== undefined) resolved.yAxis = dynamic.yAxis
239
- if (dynamic.yAxisQuantityKind !== undefined) resolved.yAxisQuantityKind = dynamic.yAxisQuantityKind
240
- if (dynamic.colorAxisQuantityKinds !== undefined) resolved.colorAxisQuantityKinds = dynamic.colorAxisQuantityKinds
241
- if (dynamic.filterAxisQuantityKinds !== undefined) resolved.filterAxisQuantityKinds = dynamic.filterAxisQuantityKinds
392
+ if (dynamic.xAxis !== undefined) resolved.xAxis = dynamic.xAxis
393
+ if (dynamic.xAxisQuantityKind !== undefined) resolved.xAxisQuantityKind = dynamic.xAxisQuantityKind
394
+ if (dynamic.yAxis !== undefined) resolved.yAxis = dynamic.yAxis
395
+ if (dynamic.yAxisQuantityKind !== undefined) resolved.yAxisQuantityKind = dynamic.yAxisQuantityKind
396
+ if (dynamic.zAxis !== undefined) resolved.zAxis = dynamic.zAxis
397
+ if (dynamic.zAxisQuantityKind !== undefined) resolved.zAxisQuantityKind = dynamic.zAxisQuantityKind
398
+ if (dynamic.colorAxisQuantityKinds !== undefined) resolved.colorAxisQuantityKinds = dynamic.colorAxisQuantityKinds
399
+ if (dynamic.colorAxis2dQuantityKinds !== undefined) resolved.colorAxis2dQuantityKinds = dynamic.colorAxis2dQuantityKinds
400
+ if (dynamic.filterAxisQuantityKinds !== undefined) resolved.filterAxisQuantityKinds = dynamic.filterAxisQuantityKinds
242
401
  }
243
402
 
244
403
  return resolved
245
404
  }
246
405
 
247
- createLayer(parameters, data) {
406
+ async createLayer(regl, parameters, data, plot) {
248
407
  if (!this._createLayer) {
249
408
  throw new Error(`LayerType '${this.name}' does not implement createLayer()`)
250
409
  }
251
- const gpuConfigs = this._createLayer.call(this, parameters, data)
410
+ const gpuConfigs = await this._createLayer.call(this, regl, parameters, data, plot)
252
411
  const axisConfig = this.resolveAxisConfig(parameters, data)
253
412
 
254
413
  return gpuConfigs.map(gpuConfig => new Layer({
255
414
  type: this,
256
415
  attributes: gpuConfig.attributes ?? {},
257
416
  uniforms: gpuConfig.uniforms ?? {},
258
- nameMap: gpuConfig.nameMap ?? {},
259
417
  domains: gpuConfig.domains ?? {},
260
418
  lineWidth: gpuConfig.lineWidth ?? 1,
261
419
  primitive: gpuConfig.primitive ?? "points",
@@ -265,9 +423,12 @@ export class LayerType {
265
423
  blend: gpuConfig.blend ?? null,
266
424
  xAxis: axisConfig.xAxis,
267
425
  yAxis: axisConfig.yAxis,
426
+ zAxis: axisConfig.zAxis,
268
427
  xAxisQuantityKind: axisConfig.xAxisQuantityKind,
269
428
  yAxisQuantityKind: axisConfig.yAxisQuantityKind,
429
+ zAxisQuantityKind: axisConfig.zAxisQuantityKind,
270
430
  colorAxes: axisConfig.colorAxisQuantityKinds,
431
+ colorAxes2d: axisConfig.colorAxis2dQuantityKinds,
271
432
  filterAxes: axisConfig.filterAxisQuantityKinds,
272
433
  }))
273
434
  }