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
@@ -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,235 @@ 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
+ uniforms[`colorscale${suffix}`] = regl.prop(`colorscale_${qk}`)
246
+ uniforms[`color_range${suffix}`] = regl.prop(`color_range_${qk}`)
247
+ uniforms[`color_scale_type${suffix}`] = regl.prop(`color_scale_type_${qk}`)
248
+ uniforms[`alpha_blend${suffix}`] = regl.prop(`alpha_blend_${qk}`)
185
249
  }
186
250
 
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}`))
251
+ for (const [suffix, qk] of Object.entries(layer.filterAxes)) {
252
+ uniforms[`filter_range${suffix}`] = regl.prop(`filter_range_${qk}`)
253
+ uniforms[`filter_scale_type${suffix}`] = regl.prop(`filter_scale_type_${qk}`)
190
254
  }
191
255
 
192
- // Inject GLSL helpers before the layer shader body.
256
+ // Strip spatial uniforms from vert (re-declared in buildSpatialGlsl)
257
+ vertSrc = removeUniformDecl(vertSrc, 'xDomain')
258
+ vertSrc = removeUniformDecl(vertSrc, 'yDomain')
259
+ vertSrc = removeUniformDecl(vertSrc, 'zDomain')
260
+ vertSrc = removeUniformDecl(vertSrc, 'xScaleType')
261
+ vertSrc = removeUniformDecl(vertSrc, 'yScaleType')
262
+ vertSrc = removeUniformDecl(vertSrc, 'zScaleType')
263
+ vertSrc = removeUniformDecl(vertSrc, 'u_is3D')
264
+ vertSrc = removeUniformDecl(vertSrc, 'u_mvp')
265
+
193
266
  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;`
267
+ const colorGlsl = (Object.keys(layer.colorAxes).length > 0 || Object.keys(layer.colorAxes2d).length > 0) ? buildColorGlsl() : ''
268
+ const filterGlsl = Object.keys(layer.filterAxes).length > 0 ? buildFilterGlsl() : ''
269
+ const pickVertDecls = `in float a_pickId;\nout float v_pickId;`
270
+
271
+ const hasNdColumns = ndColumnHelperLines.length > 0
272
+ const ndColumnHelpersStr = ndColumnHelperLines.join('\n')
273
+
274
+ // Column helpers for vert: precision + all samplers + sampleColumn + sampleColumnND + wrappers
275
+ const columnHelpers = allDataColumns.length > 0
276
+ ? [
277
+ 'precision highp sampler2D;',
278
+ samplerDecls,
279
+ SAMPLE_COLUMN_GLSL,
280
+ hasNdColumns ? SAMPLE_COLUMN_ND_GLSL : '',
281
+ ndColumnHelpersStr,
282
+ ].filter(Boolean).join('\n')
283
+ : ''
284
+
285
+ // Column helpers for frag: only nD column samplers + sampleColumnND + wrappers
286
+ // (1D columns stay in vert; their a_pickId is a vertex attribute)
287
+ const ndFragHelpers = hasNdColumns
288
+ ? [
289
+ 'precision highp sampler2D;',
290
+ ...Object.keys(ndTextures).map(n => `uniform sampler2D ${n};`),
291
+ SAMPLE_COLUMN_ND_GLSL,
292
+ ndColumnHelpersStr,
293
+ ].join('\n')
294
+ : ''
295
+
296
+ const fnSuffix = (s) => s.replace(/^_+/, '')
297
+
298
+ const colorHelperLines = []
299
+ let fragSrc = this.frag
300
+ for (const [suffix] of Object.entries(layer.colorAxes)) {
301
+ colorHelperLines.push(
302
+ `uniform int colorscale${suffix};`,
303
+ `uniform vec2 color_range${suffix};`,
304
+ `uniform float color_scale_type${suffix};`,
305
+ `uniform float alpha_blend${suffix};`,
306
+ `vec4 map_color_${fnSuffix(suffix)}(float value) {`,
307
+ ` return map_color_s(colorscale${suffix}, color_range${suffix}, value, color_scale_type${suffix}, alpha_blend${suffix});`,
308
+ `}`,
309
+ `vec4 map_color_2d_x_${fnSuffix(suffix)}(float value) {`,
310
+ ` return map_color_s_2d(colorscale${suffix}, color_range${suffix}, value, color_scale_type${suffix}, alpha_blend${suffix},`,
311
+ ` colorscale${suffix}, color_range${suffix}, 0.0/0.0, color_scale_type${suffix}, 0.0);`,
312
+ `}`,
313
+ `vec4 map_color_2d_y_${fnSuffix(suffix)}(float value) {`,
314
+ ` return map_color_s_2d(colorscale${suffix}, color_range${suffix}, 0.0/0.0, color_scale_type${suffix}, 0.0,`,
315
+ ` colorscale${suffix}, color_range${suffix}, value, color_scale_type${suffix}, alpha_blend${suffix});`,
316
+ `}`
317
+ )
318
+ fragSrc = removeUniformDecl(fragSrc, `colorscale${suffix}`)
319
+ fragSrc = removeUniformDecl(fragSrc, `color_range${suffix}`)
320
+ fragSrc = removeUniformDecl(fragSrc, `color_scale_type${suffix}`)
321
+ fragSrc = removeUniformDecl(fragSrc, `alpha_blend${suffix}`)
322
+ }
323
+ const colorHelpers = colorHelperLines.join('\n')
324
+
325
+ const color2dHelperLines = []
326
+ for (const [suffix2d, [s1, s2]] of Object.entries(layer.colorAxes2d)) {
327
+ color2dHelperLines.push(
328
+ `vec4 map_color_2d_${fnSuffix(suffix2d)}(vec2 values) {`,
329
+ ` return map_color_s_2d(colorscale${s1}, color_range${s1}, values.x, color_scale_type${s1}, alpha_blend${s1},`,
330
+ ` colorscale${s2}, color_range${s2}, values.y, color_scale_type${s2}, alpha_blend${s2});`,
331
+ `}`
332
+ )
333
+ }
334
+ const color2dHelpers = color2dHelperLines.join('\n')
335
+
336
+ const filterHelperLines = []
337
+ for (const [suffix] of Object.entries(layer.filterAxes)) {
338
+ filterHelperLines.push(
339
+ `uniform vec4 filter_range${suffix};`,
340
+ `bool filter_${fnSuffix(suffix)}(float value) {`,
341
+ ` return filter_in_range(filter_range${suffix}, value);`,
342
+ `}`
343
+ )
344
+ vertSrc = removeUniformDecl(vertSrc, `filter_range${suffix}`)
345
+ }
346
+ const filterHelpers = filterHelperLines.join('\n')
197
347
 
348
+ 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
349
  const drawConfig = {
199
- vert: injectPickIdAssignment(injectInto(vertSrc, [spatialGlsl, filterGlsl, pickVertDecls, ...allGlobalDecls])),
200
- frag: injectInto(this.frag, [buildApplyColorGlsl(), colorGlsl, filterGlsl]),
350
+ vert: injectPickIdAssignment(injectInto(vertSrc, [spatialGlsl, filterGlsl, filterHelpers, columnHelpers, pickVertDecls])),
351
+ frag: injectIntoMainStart(injectInto(fragSrc, [buildApplyColorGlsl(), buildClipFragGlsl(), colorGlsl, colorHelpers, color2dHelpers, filterGlsl, filterHelpers, ndFragHelpers]), clipFragDiscard),
201
352
  attributes,
202
353
  uniforms,
203
354
  viewport: regl.prop("viewport"),
204
355
  primitive: layer.primitive,
205
356
  lineWidth: layer.lineWidth,
206
357
  count: regl.prop("count"),
207
- ...(layer.blend ? { blend: layer.blend } : {})
358
+ ...(Object.keys(layer.colorAxes).length > 0 || Object.keys(layer.colorAxes2d).length > 0
359
+ ? { blend: { enable: true, func: { srcRGB: 'src alpha', dstRGB: 'one minus src alpha', srcAlpha: 0, dstAlpha: 1 } } }
360
+ : layer.blend ? { blend: layer.blend } : {})
208
361
  }
209
362
 
210
363
  if (layer.instanceCount !== null) {
211
364
  drawConfig.instances = regl.prop("instances")
212
365
  }
213
366
 
214
- return regl(drawConfig)
367
+ return drawConfig
215
368
  }
216
369
 
217
370
  schema(data) {
@@ -219,43 +372,46 @@ export class LayerType {
219
372
  throw new Error(`LayerType '${this.name}' does not implement schema()`)
220
373
  }
221
374
 
222
- // Resolves axis config by merging static declarations with dynamic getAxisConfig output.
223
- // Dynamic values (non-undefined) override static values.
224
375
  resolveAxisConfig(parameters, data) {
225
376
  const resolved = {
226
377
  xAxis: this.xAxis,
227
378
  xAxisQuantityKind: this.xAxisQuantityKind,
228
379
  yAxis: this.yAxis,
229
380
  yAxisQuantityKind: this.yAxisQuantityKind,
230
- colorAxisQuantityKinds: [...this.colorAxisQuantityKinds],
231
- filterAxisQuantityKinds: [...this.filterAxisQuantityKinds],
381
+ zAxis: this.zAxis,
382
+ zAxisQuantityKind: this.zAxisQuantityKind,
383
+ colorAxisQuantityKinds: { ...this.colorAxisQuantityKinds },
384
+ colorAxis2dQuantityKinds: { ...this.colorAxis2dQuantityKinds },
385
+ filterAxisQuantityKinds: { ...this.filterAxisQuantityKinds },
232
386
  }
233
387
 
234
388
  if (this._getAxisConfig) {
235
389
  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
390
+ if (dynamic.xAxis !== undefined) resolved.xAxis = dynamic.xAxis
391
+ if (dynamic.xAxisQuantityKind !== undefined) resolved.xAxisQuantityKind = dynamic.xAxisQuantityKind
392
+ if (dynamic.yAxis !== undefined) resolved.yAxis = dynamic.yAxis
393
+ if (dynamic.yAxisQuantityKind !== undefined) resolved.yAxisQuantityKind = dynamic.yAxisQuantityKind
394
+ if (dynamic.zAxis !== undefined) resolved.zAxis = dynamic.zAxis
395
+ if (dynamic.zAxisQuantityKind !== undefined) resolved.zAxisQuantityKind = dynamic.zAxisQuantityKind
396
+ if (dynamic.colorAxisQuantityKinds !== undefined) resolved.colorAxisQuantityKinds = dynamic.colorAxisQuantityKinds
397
+ if (dynamic.colorAxis2dQuantityKinds !== undefined) resolved.colorAxis2dQuantityKinds = dynamic.colorAxis2dQuantityKinds
398
+ if (dynamic.filterAxisQuantityKinds !== undefined) resolved.filterAxisQuantityKinds = dynamic.filterAxisQuantityKinds
242
399
  }
243
400
 
244
401
  return resolved
245
402
  }
246
403
 
247
- createLayer(parameters, data) {
404
+ async createLayer(regl, parameters, data, plot) {
248
405
  if (!this._createLayer) {
249
406
  throw new Error(`LayerType '${this.name}' does not implement createLayer()`)
250
407
  }
251
- const gpuConfigs = this._createLayer.call(this, parameters, data)
408
+ const gpuConfigs = await this._createLayer.call(this, regl, parameters, data, plot)
252
409
  const axisConfig = this.resolveAxisConfig(parameters, data)
253
410
 
254
411
  return gpuConfigs.map(gpuConfig => new Layer({
255
412
  type: this,
256
413
  attributes: gpuConfig.attributes ?? {},
257
414
  uniforms: gpuConfig.uniforms ?? {},
258
- nameMap: gpuConfig.nameMap ?? {},
259
415
  domains: gpuConfig.domains ?? {},
260
416
  lineWidth: gpuConfig.lineWidth ?? 1,
261
417
  primitive: gpuConfig.primitive ?? "points",
@@ -265,9 +421,12 @@ export class LayerType {
265
421
  blend: gpuConfig.blend ?? null,
266
422
  xAxis: axisConfig.xAxis,
267
423
  yAxis: axisConfig.yAxis,
424
+ zAxis: axisConfig.zAxis,
268
425
  xAxisQuantityKind: axisConfig.xAxisQuantityKind,
269
426
  yAxisQuantityKind: axisConfig.yAxisQuantityKind,
427
+ zAxisQuantityKind: axisConfig.zAxisQuantityKind,
270
428
  colorAxes: axisConfig.colorAxisQuantityKinds,
429
+ colorAxes2d: axisConfig.colorAxis2dQuantityKinds,
271
430
  filterAxes: axisConfig.filterAxisQuantityKinds,
272
431
  }))
273
432
  }