gladly-plot 0.0.4 → 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 (60) hide show
  1. package/README.md +9 -2
  2. package/package.json +10 -11
  3. package/src/axes/Axis.js +401 -0
  4. package/src/{AxisLink.js → axes/AxisLink.js} +6 -2
  5. package/src/{AxisQuantityKindRegistry.js → axes/AxisQuantityKindRegistry.js} +7 -0
  6. package/src/axes/AxisRegistry.js +179 -0
  7. package/src/axes/Camera.js +47 -0
  8. package/src/axes/ColorAxisRegistry.js +101 -0
  9. package/src/{FilterAxisRegistry.js → axes/FilterAxisRegistry.js} +63 -0
  10. package/src/axes/TickLabelAtlas.js +99 -0
  11. package/src/axes/ZoomController.js +463 -0
  12. package/src/colorscales/BivariateColorscales.js +205 -0
  13. package/src/colorscales/ColorscaleRegistry.js +144 -0
  14. package/src/compute/ComputationRegistry.js +179 -0
  15. package/src/compute/axisFilter.js +59 -0
  16. package/src/compute/conv.js +286 -0
  17. package/src/compute/elementwise.js +72 -0
  18. package/src/compute/fft.js +378 -0
  19. package/src/compute/filter.js +229 -0
  20. package/src/compute/hist.js +285 -0
  21. package/src/compute/kde.js +120 -0
  22. package/src/compute/scatter2dInterpolate.js +277 -0
  23. package/src/compute/util.js +196 -0
  24. package/src/core/ComputePipeline.js +153 -0
  25. package/src/core/GlBase.js +141 -0
  26. package/src/core/Layer.js +59 -0
  27. package/src/core/LayerType.js +433 -0
  28. package/src/core/Plot.js +1213 -0
  29. package/src/core/PlotGroup.js +204 -0
  30. package/src/core/ShaderQueue.js +73 -0
  31. package/src/data/ColumnData.js +269 -0
  32. package/src/data/Computation.js +95 -0
  33. package/src/data/Data.js +270 -0
  34. package/src/{Colorbar.js → floats/Colorbar.js} +19 -5
  35. package/src/floats/Colorbar2d.js +77 -0
  36. package/src/{Filterbar.js → floats/Filterbar.js} +18 -4
  37. package/src/{FilterbarFloat.js → floats/Float.js} +73 -30
  38. package/src/{EpsgUtils.js → geo/EpsgUtils.js} +1 -1
  39. package/src/index.js +47 -22
  40. package/src/layers/BarsLayer.js +168 -0
  41. package/src/{ColorbarLayer.js → layers/ColorbarLayer.js} +12 -16
  42. package/src/layers/ColorbarLayer2d.js +86 -0
  43. package/src/{FilterbarLayer.js → layers/FilterbarLayer.js} +6 -5
  44. package/src/layers/LinesLayer.js +185 -0
  45. package/src/layers/PointsLayer.js +118 -0
  46. package/src/layers/ScatterShared.js +98 -0
  47. package/src/{TileLayer.js → layers/TileLayer.js} +24 -20
  48. package/src/math/mat4.js +100 -0
  49. package/src/Axis.js +0 -48
  50. package/src/AxisRegistry.js +0 -54
  51. package/src/ColorAxisRegistry.js +0 -49
  52. package/src/ColorscaleRegistry.js +0 -52
  53. package/src/Data.js +0 -67
  54. package/src/Float.js +0 -159
  55. package/src/Layer.js +0 -44
  56. package/src/LayerType.js +0 -209
  57. package/src/Plot.js +0 -1073
  58. package/src/ScatterLayer.js +0 -287
  59. /package/src/{MatplotlibColorscales.js → colorscales/MatplotlibColorscales.js} +0 -0
  60. /package/src/{LayerTypeRegistry.js → core/LayerTypeRegistry.js} +0 -0
@@ -0,0 +1,98 @@
1
+ import { LayerType } from "../core/LayerType.js"
2
+ import { AXIS_GEOMETRY } from "../axes/AxisRegistry.js"
3
+ import { Data } from "../data/Data.js"
4
+ import { computationSchema, EXPRESSION_REF, EXPRESSION_REF_OPT, resolveQuantityKind } from "../compute/ComputationRegistry.js"
5
+
6
+ const X_AXES = Object.keys(AXIS_GEOMETRY).filter(a => AXIS_GEOMETRY[a].dir === 'x')
7
+ const Y_AXES = Object.keys(AXIS_GEOMETRY).filter(a => AXIS_GEOMETRY[a].dir === 'y')
8
+ const Z_AXES = Object.keys(AXIS_GEOMETRY).filter(a => AXIS_GEOMETRY[a].dir === 'z')
9
+
10
+ export class ScatterLayerTypeBase extends LayerType {
11
+ _getAxisConfig(parameters, data) {
12
+ const d = Data.wrap(data)
13
+ const {
14
+ xData, yData, zData: zDataRaw,
15
+ vData: vDataRaw, vData2: vData2Raw, fData: fDataRaw,
16
+ xAxis = "xaxis_bottom", yAxis = "yaxis_left", zAxis = "none",
17
+ } = parameters
18
+ const vDataIn = (vDataRaw == null || vDataRaw === "none") ? null : vDataRaw
19
+ const vData2In = (vData2Raw == null || vData2Raw === "none") ? null : vData2Raw
20
+ const fData = (fDataRaw == null || fDataRaw === "none") ? null : fDataRaw
21
+ const zData = (zDataRaw == null || zDataRaw === "none") ? null : zDataRaw
22
+ const zAxisResolved = (zAxis == null || zAxis === "none") ? null : zAxis
23
+ const vData = vDataIn
24
+ const vData2 = vData2In
25
+ const colorAxisQuantityKinds = {}
26
+ const vQK = vData ? resolveQuantityKind(vData, d) : null
27
+ const vQK2 = vData2 ? resolveQuantityKind(vData2, d) : null
28
+ if (vQK) colorAxisQuantityKinds[''] = vQK
29
+ if (vQK2) colorAxisQuantityKinds['2'] = vQK2
30
+ const colorAxis2dQuantityKinds = vData && vData2 ? { '': ['', '2'] } : {}
31
+ const filterAxisQuantityKinds = fData ? { '': resolveQuantityKind(fData, d) } : {}
32
+ return {
33
+ xAxis,
34
+ xAxisQuantityKind: resolveQuantityKind(xData, d) ?? undefined,
35
+ yAxis,
36
+ yAxisQuantityKind: resolveQuantityKind(yData, d) ?? undefined,
37
+ zAxis: zData ? (zAxisResolved ?? "zaxis_bottom_left") : null,
38
+ zAxisQuantityKind: zData ? (resolveQuantityKind(zData, d) ?? undefined) : undefined,
39
+ colorAxisQuantityKinds,
40
+ colorAxis2dQuantityKinds,
41
+ filterAxisQuantityKinds,
42
+ }
43
+ }
44
+
45
+ _commonSchemaProperties(data) {
46
+ return {
47
+ xData: EXPRESSION_REF,
48
+ yData: EXPRESSION_REF,
49
+ zData: EXPRESSION_REF_OPT,
50
+ vData: EXPRESSION_REF_OPT,
51
+ vData2: EXPRESSION_REF_OPT,
52
+ fData: EXPRESSION_REF_OPT,
53
+ xAxis: {
54
+ type: "string",
55
+ enum: X_AXES,
56
+ default: "xaxis_bottom",
57
+ description: "Which x-axis to use for this layer",
58
+ },
59
+ yAxis: {
60
+ type: "string",
61
+ enum: Y_AXES,
62
+ default: "yaxis_left",
63
+ description: "Which y-axis to use for this layer",
64
+ },
65
+ zAxis: {
66
+ type: "string",
67
+ enum: ["none", ...Z_AXES],
68
+ default: "none",
69
+ description: "Which z-axis to use for this layer (enables 3D mode)",
70
+ },
71
+ }
72
+ }
73
+
74
+ _buildDomains(d, xData, yData, zData, vData, vData2, xQK, yQK, zQK, vQK, vQK2) {
75
+ const domains = {}
76
+ if (xQK && typeof xData === 'string') {
77
+ const xDomain = d.getDomain(xData)
78
+ if (xDomain) domains[xQK] = xDomain
79
+ }
80
+ if (yQK && typeof yData === 'string') {
81
+ const yDomain = d.getDomain(yData)
82
+ if (yDomain) domains[yQK] = yDomain
83
+ }
84
+ if (zData && zQK && typeof zData === 'string') {
85
+ const zDomain = d.getDomain(zData)
86
+ if (zDomain) domains[zQK] = zDomain
87
+ }
88
+ if (vData && vQK && typeof vData === 'string') {
89
+ const vDomain = d.getDomain(vData)
90
+ if (vDomain) domains[vQK] = vDomain
91
+ }
92
+ if (vData2 && vQK2 && typeof vData2 === 'string') {
93
+ const vDomain2 = d.getDomain(vData2)
94
+ if (vDomain2) domains[vQK2] = vDomain2
95
+ }
96
+ return domains
97
+ }
98
+ }
@@ -1,8 +1,8 @@
1
1
  import proj4 from 'proj4'
2
- import { LayerType } from './LayerType.js'
3
- import { AXES } from './AxisRegistry.js'
4
- import { registerLayerType } from './LayerTypeRegistry.js'
5
- import { parseCrsCode, crsToQkX, crsToQkY, ensureCrsDefined } from './EpsgUtils.js'
2
+ import { LayerType } from '../core/LayerType.js'
3
+ import { AXES } from '../axes/AxisRegistry.js'
4
+ import { registerLayerType } from '../core/LayerTypeRegistry.js'
5
+ import { parseCrsCode, crsToQkX, crsToQkY, ensureCrsDefined } from '../geo/EpsgUtils.js'
6
6
 
7
7
  // ─── Tile math (standard Web Mercator / "slippy map" grid) ────────────────────
8
8
 
@@ -410,15 +410,15 @@ class TileManager {
410
410
 
411
411
  // ─── GLSL ─────────────────────────────────────────────────────────────────────
412
412
 
413
- const TILE_VERT = `
413
+ const TILE_VERT = `#version 300 es
414
414
  precision mediump float;
415
- attribute vec2 position;
416
- attribute vec2 uv;
415
+ in vec2 position;
416
+ in vec2 uv;
417
417
  uniform vec2 xDomain;
418
418
  uniform vec2 yDomain;
419
419
  uniform float xScaleType;
420
420
  uniform float yScaleType;
421
- varying vec2 vUv;
421
+ out vec2 vUv;
422
422
 
423
423
  float normalize_axis(float v, vec2 domain, float scaleType) {
424
424
  float vt = scaleType > 0.5 ? log(v) : v;
@@ -426,24 +426,28 @@ const TILE_VERT = `
426
426
  float d1 = scaleType > 0.5 ? log(domain.y) : domain.y;
427
427
  return (vt - d0) / (d1 - d0);
428
428
  }
429
+ vec4 plot_pos(vec2 pos) {
430
+ float nx = normalize_axis(pos.x, xDomain, xScaleType);
431
+ float ny = normalize_axis(pos.y, yDomain, yScaleType);
432
+ return vec4(nx * 2.0 - 1.0, ny * 2.0 - 1.0, 0.0, 1.0);
433
+ }
429
434
 
430
435
  void main() {
431
- float nx = normalize_axis(position.x, xDomain, xScaleType);
432
- float ny = normalize_axis(position.y, yDomain, yScaleType);
433
- gl_Position = vec4(nx * 2.0 - 1.0, ny * 2.0 - 1.0, 0.0, 1.0);
436
+ gl_Position = plot_pos(position);
434
437
  vUv = uv;
435
438
  }
436
439
  `
437
440
 
438
- const TILE_FRAG = `
441
+ const TILE_FRAG = `#version 300 es
439
442
  precision mediump float;
440
443
  uniform sampler2D tileTexture;
441
444
  uniform float opacity;
442
- varying vec2 vUv;
445
+ in vec2 vUv;
446
+ out vec4 fragColor;
443
447
 
444
448
  void main() {
445
- vec4 color = texture2D(tileTexture, vUv);
446
- gl_FragColor = vec4(color.rgb, color.a * opacity);
449
+ vec4 color = texture(tileTexture, vUv);
450
+ fragColor = vec4(color.rgb, color.a * opacity);
447
451
  }
448
452
  `
449
453
 
@@ -587,12 +591,12 @@ class TileLayerType extends LayerType {
587
591
  xAxisQuantityKind: crsToQkX(effectivePlotCrs),
588
592
  yAxis,
589
593
  yAxisQuantityKind: crsToQkY(effectivePlotCrs),
590
- colorAxisQuantityKinds: [],
591
- filterAxisQuantityKinds: [],
594
+ colorAxisQuantityKinds: {},
595
+ filterAxisQuantityKinds: {},
592
596
  }
593
597
  }
594
598
 
595
- createLayer(parameters, _data) {
599
+ createLayer(regl, parameters, _data) {
596
600
  const {
597
601
  xAxis = 'xaxis_bottom',
598
602
  yAxis = 'yaxis_left',
@@ -609,8 +613,8 @@ class TileLayerType extends LayerType {
609
613
  yAxis,
610
614
  xAxisQuantityKind: crsToQkX(effectivePlotCrs),
611
615
  yAxisQuantityKind: crsToQkY(effectivePlotCrs),
612
- colorAxes: [],
613
- filterAxes: [],
616
+ colorAxes: {},
617
+ filterAxes: {},
614
618
  vertexCount: 0,
615
619
  instanceCount: null,
616
620
  attributes: {},
@@ -0,0 +1,100 @@
1
+ // Column-major 4×4 matrix utilities matching WebGL/GLSL convention.
2
+ // Element at row r, column c is stored at index c*4+r.
3
+
4
+ export function mat4Identity() {
5
+ return new Float32Array([
6
+ 1, 0, 0, 0,
7
+ 0, 1, 0, 0,
8
+ 0, 0, 1, 0,
9
+ 0, 0, 0, 1,
10
+ ])
11
+ }
12
+
13
+ // a * b (left-multiplies a by b).
14
+ export function mat4Multiply(a, b) {
15
+ const out = new Float32Array(16)
16
+ for (let col = 0; col < 4; col++) {
17
+ for (let row = 0; row < 4; row++) {
18
+ let s = 0
19
+ for (let k = 0; k < 4; k++) s += a[k * 4 + row] * b[col * 4 + k]
20
+ out[col * 4 + row] = s
21
+ }
22
+ }
23
+ return out
24
+ }
25
+
26
+ // Perspective projection. fovY in radians, aspect = width/height.
27
+ export function mat4Perspective(fovY, aspect, near, far) {
28
+ const f = 1 / Math.tan(fovY / 2)
29
+ const nf = 1 / (near - far)
30
+ return new Float32Array([
31
+ f / aspect, 0, 0, 0,
32
+ 0, f, 0, 0,
33
+ 0, 0, (far + near) * nf, -1,
34
+ 0, 0, 2 * far * near * nf, 0,
35
+ ])
36
+ }
37
+
38
+ // View matrix looking from eye toward center with the given up vector.
39
+ export function mat4LookAt(eye, center, up) {
40
+ const f = vec3Normalize([center[0] - eye[0], center[1] - eye[1], center[2] - eye[2]])
41
+ const r = vec3Normalize(vec3Cross(f, up))
42
+ const u = vec3Cross(r, f)
43
+ return new Float32Array([
44
+ r[0], u[0], -f[0], 0,
45
+ r[1], u[1], -f[1], 0,
46
+ r[2], u[2], -f[2], 0,
47
+ -vec3Dot(r, eye), -vec3Dot(u, eye), vec3Dot(f, eye), 1,
48
+ ])
49
+ }
50
+
51
+ // Multiply a 4×4 column-major matrix by a vec4.
52
+ export function mat4MulVec4(m, v) {
53
+ return [
54
+ m[0]*v[0] + m[4]*v[1] + m[8] *v[2] + m[12]*v[3],
55
+ m[1]*v[0] + m[5]*v[1] + m[9] *v[2] + m[13]*v[3],
56
+ m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3],
57
+ m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3],
58
+ ]
59
+ }
60
+
61
+ export function vec3Normalize(v) {
62
+ const len = Math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])
63
+ return len > 1e-10 ? [v[0]/len, v[1]/len, v[2]/len] : [0, 0, 0]
64
+ }
65
+
66
+ export function vec3Cross(a, b) {
67
+ return [
68
+ a[1]*b[2] - a[2]*b[1],
69
+ a[2]*b[0] - a[0]*b[2],
70
+ a[0]*b[1] - a[1]*b[0],
71
+ ]
72
+ }
73
+
74
+ export function vec3Dot(a, b) {
75
+ return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
76
+ }
77
+
78
+ // Convert spherical coords (azimuth theta around y-axis, elevation phi from equator)
79
+ // to Cartesian.
80
+ export function sphericalToCartesian(theta, phi, r) {
81
+ return [
82
+ r * Math.cos(phi) * Math.sin(theta),
83
+ r * Math.sin(phi),
84
+ r * Math.cos(phi) * Math.cos(theta),
85
+ ]
86
+ }
87
+
88
+ // Project a 3D model-space point to HTML screen coordinates (y from top) using an MVP
89
+ // matrix that maps model space to full-canvas NDC, and the canvas dimensions.
90
+ // Returns [htmlX, htmlY] or null if the point is behind the camera (w ≤ 0).
91
+ export function projectToScreen(point, mvp, canvasWidth, canvasHeight) {
92
+ const clip = mat4MulVec4(mvp, [point[0], point[1], point[2], 1.0])
93
+ if (clip[3] <= 0) return null
94
+ const ndcX = clip[0] / clip[3]
95
+ const ndcY = clip[1] / clip[3]
96
+ return [
97
+ (ndcX + 1) * 0.5 * canvasWidth,
98
+ (1 - (ndcY + 1) * 0.5) * canvasHeight,
99
+ ]
100
+ }
package/src/Axis.js DELETED
@@ -1,48 +0,0 @@
1
- /**
2
- * An Axis represents a single data axis on a plot. Axis instances are stable across
3
- * plot.update() calls and can be linked together with linkAxes().
4
- *
5
- * Public interface (duck-typing compatible):
6
- * - axis.quantityKind — string | null
7
- * - axis.getDomain() — [min, max] | null
8
- * - axis.setDomain(domain) — update domain, schedule render, notify subscribers
9
- * - axis.subscribe(callback) — callback([min, max]) called on domain changes
10
- * - axis.unsubscribe(callback) — remove a previously added callback
11
- */
12
- export class Axis {
13
- constructor(plot, name) {
14
- this._plot = plot
15
- this._name = name
16
- this._listeners = new Set()
17
- this._propagating = false
18
- }
19
-
20
- /** The quantity kind for this axis, or null if the plot hasn't been initialized yet. */
21
- get quantityKind() { return this._plot.getAxisQuantityKind(this._name) }
22
-
23
- /** Returns [min, max], or null if the axis has no domain yet. */
24
- getDomain() { return this._plot.getAxisDomain(this._name) }
25
-
26
- /**
27
- * Sets the axis domain, schedules a render on the owning plot, and notifies all
28
- * subscribers (e.g. linked axes). A _propagating guard prevents infinite loops
29
- * when axes are linked bidirectionally.
30
- */
31
- setDomain(domain) {
32
- if (this._propagating) return
33
- this._propagating = true
34
- try {
35
- this._plot.setAxisDomain(this._name, domain)
36
- this._plot.scheduleRender()
37
- for (const cb of this._listeners) cb(domain)
38
- } finally {
39
- this._propagating = false
40
- }
41
- }
42
-
43
- /** Add a subscriber. callback([min, max]) is called after every setDomain(). */
44
- subscribe(callback) { this._listeners.add(callback) }
45
-
46
- /** Remove a previously added subscriber. */
47
- unsubscribe(callback) { this._listeners.delete(callback) }
48
- }
@@ -1,54 +0,0 @@
1
- import * as d3 from "d3-scale"
2
- import { getAxisQuantityKind } from "./AxisQuantityKindRegistry.js"
3
-
4
- export const AXES = ["xaxis_bottom","xaxis_top","yaxis_left","yaxis_right"]
5
-
6
- export class AxisRegistry {
7
- constructor(width, height) {
8
- this.scales = {}
9
- this.axisQuantityKinds = {}
10
- this.width = width
11
- this.height = height
12
- AXES.forEach(a => {
13
- this.scales[a] = null
14
- this.axisQuantityKinds[a] = null
15
- })
16
- }
17
-
18
- ensureAxis(axisName, axisQuantityKind, scaleOverride) {
19
- if (!AXES.includes(axisName)) throw new Error(`Unknown axis ${axisName}`)
20
- if (this.axisQuantityKinds[axisName] && this.axisQuantityKinds[axisName] !== axisQuantityKind)
21
- throw new Error(`Axis quantity kind mismatch on axis ${axisName}: ${this.axisQuantityKinds[axisName]} vs ${axisQuantityKind}`)
22
-
23
- if (!this.scales[axisName]) {
24
- const quantityKindDef = getAxisQuantityKind(axisQuantityKind)
25
- const scaleType = scaleOverride ?? quantityKindDef.scale
26
- this.scales[axisName] = scaleType === "log"
27
- ? d3.scaleLog().range(axisName.includes("y") ? [this.height,0] : [0,this.width])
28
- : d3.scaleLinear().range(axisName.includes("y") ? [this.height,0] : [0,this.width])
29
- this.axisQuantityKinds[axisName] = axisQuantityKind
30
- }
31
- return this.scales[axisName]
32
- }
33
-
34
- getScale(axisName) { return this.scales[axisName] }
35
-
36
- isLogScale(axisName) {
37
- const scale = this.scales[axisName]
38
- return !!scale && typeof scale.base === 'function'
39
- }
40
-
41
- setScaleType(axisName, scaleType) {
42
- const scale = this.scales[axisName]
43
- if (!scale) return
44
- const currentIsLog = typeof scale.base === 'function'
45
- const wantLog = scaleType === "log"
46
- if (currentIsLog === wantLog) return
47
- const currentDomain = scale.domain()
48
- const newScale = wantLog
49
- ? d3.scaleLog().range(axisName.includes("y") ? [this.height, 0] : [0, this.width])
50
- : d3.scaleLinear().range(axisName.includes("y") ? [this.height, 0] : [0, this.width])
51
- newScale.domain(currentDomain)
52
- this.scales[axisName] = newScale
53
- }
54
- }
@@ -1,49 +0,0 @@
1
- import { getAxisQuantityKind } from './AxisQuantityKindRegistry.js'
2
- import { getColorscaleIndex } from './ColorscaleRegistry.js'
3
-
4
- export class ColorAxisRegistry {
5
- constructor() {
6
- this._axes = new Map()
7
- }
8
-
9
- ensureColorAxis(quantityKind, colorscaleOverride = null) {
10
- if (!this._axes.has(quantityKind)) {
11
- this._axes.set(quantityKind, { colorscaleOverride, range: null })
12
- } else if (colorscaleOverride !== null) {
13
- this._axes.get(quantityKind).colorscaleOverride = colorscaleOverride
14
- }
15
- }
16
-
17
- setRange(quantityKind, min, max) {
18
- if (!this._axes.has(quantityKind)) {
19
- throw new Error(`Color axis '${quantityKind}' not found in registry`)
20
- }
21
- this._axes.get(quantityKind).range = [min, max]
22
- }
23
-
24
- getRange(quantityKind) {
25
- return this._axes.get(quantityKind)?.range ?? null
26
- }
27
-
28
- getColorscale(quantityKind) {
29
- const entry = this._axes.get(quantityKind)
30
- if (!entry) return null
31
- if (entry.colorscaleOverride) return entry.colorscaleOverride
32
- const unitDef = getAxisQuantityKind(quantityKind)
33
- return unitDef.colorscale ?? null
34
- }
35
-
36
- getColorscaleIndex(quantityKind) {
37
- const colorscale = this.getColorscale(quantityKind)
38
- if (colorscale === null) return 0
39
- return getColorscaleIndex(colorscale)
40
- }
41
-
42
- hasAxis(quantityKind) {
43
- return this._axes.has(quantityKind)
44
- }
45
-
46
- getQuantityKinds() {
47
- return Array.from(this._axes.keys())
48
- }
49
- }
@@ -1,52 +0,0 @@
1
- const colorscales = new Map()
2
-
3
- export function registerColorscale(name, glslFn) {
4
- colorscales.set(name, glslFn)
5
- }
6
-
7
- export function getRegisteredColorscales() {
8
- return colorscales
9
- }
10
-
11
- export function getColorscaleIndex(name) {
12
- let idx = 0
13
- for (const key of colorscales.keys()) {
14
- if (key === name) return idx
15
- idx++
16
- }
17
- return 0
18
- }
19
-
20
- export function buildColorGlsl() {
21
- if (colorscales.size === 0) return ''
22
-
23
- const parts = []
24
-
25
- for (const glslFn of colorscales.values()) {
26
- parts.push(glslFn)
27
- }
28
-
29
- parts.push('vec4 map_color(int cs, vec2 range, float value) {')
30
- parts.push(' float t = clamp((value - range.x) / (range.y - range.x), 0.0, 1.0);')
31
-
32
- let idx = 0
33
- for (const name of colorscales.keys()) {
34
- parts.push(` if (cs == ${idx}) return colorscale_${name}(t);`)
35
- idx++
36
- }
37
-
38
- parts.push(' return vec4(0.5, 0.5, 0.5, 1.0);')
39
- parts.push('}')
40
-
41
- parts.push('vec4 map_color_s(int cs, vec2 range, float v, float scaleType, float useAlpha) {')
42
- parts.push(' float vt = scaleType > 0.5 ? log(v) : v;')
43
- parts.push(' float r0 = scaleType > 0.5 ? log(range.x) : range.x;')
44
- parts.push(' float r1 = scaleType > 0.5 ? log(range.y) : range.y;')
45
- parts.push(' float t = clamp((vt - r0) / (r1 - r0), 0.0, 1.0);')
46
- parts.push(' vec4 color = map_color(cs, vec2(r0, r1), vt);')
47
- parts.push(' if (useAlpha > 0.5) color.a = t;')
48
- parts.push(' return gladly_apply_color(color);')
49
- parts.push('}')
50
-
51
- return parts.join('\n')
52
- }
package/src/Data.js DELETED
@@ -1,67 +0,0 @@
1
- export class Data {
2
- constructor(raw) {
3
- raw = raw ?? {}
4
- // Columnar format: { data: {col: Float32Array}, quantity_kinds?: {...}, domains?: {...} }
5
- // Detected by the presence of a top-level `data` property that is a plain object.
6
- if (raw.data != null && typeof raw.data === 'object' && !(raw.data instanceof Float32Array)) {
7
- this._columnar = true
8
- this._data = raw.data
9
- this._quantityKinds = raw.quantity_kinds ?? {}
10
- this._rawDomains = raw.domains ?? {}
11
- } else {
12
- this._columnar = false
13
- this._raw = raw
14
- }
15
- }
16
-
17
- static wrap(data) {
18
- if (data != null && typeof data.columns === 'function' && typeof data.getData === 'function') {
19
- return data
20
- }
21
- return new Data(data)
22
- }
23
-
24
- _entry(col) {
25
- if (this._columnar) {
26
- const rawDomain = this._rawDomains[col]
27
- let domain
28
- if (Array.isArray(rawDomain)) {
29
- domain = [rawDomain[0], rawDomain[1]]
30
- } else if (rawDomain && typeof rawDomain === 'object') {
31
- domain = [rawDomain.min, rawDomain.max]
32
- }
33
- return { data: this._data[col], quantityKind: this._quantityKinds[col], domain }
34
- }
35
-
36
- const v = this._raw[col]
37
- if (v instanceof Float32Array) {
38
- return { data: v, quantityKind: undefined, domain: undefined }
39
- }
40
- if (v && typeof v === 'object') {
41
- let domain
42
- if (Array.isArray(v.domain)) {
43
- domain = [v.domain[0], v.domain[1]]
44
- } else if (v.domain && typeof v.domain === 'object') {
45
- domain = [v.domain.min, v.domain.max]
46
- }
47
- return { data: v.data, quantityKind: v.quantity_kind, domain }
48
- }
49
- return { data: undefined, quantityKind: undefined, domain: undefined }
50
- }
51
-
52
- columns() {
53
- return this._columnar ? Object.keys(this._data) : Object.keys(this._raw)
54
- }
55
-
56
- getData(col) {
57
- return this._entry(col).data
58
- }
59
-
60
- getQuantityKind(col) {
61
- return this._entry(col).quantityKind
62
- }
63
-
64
- getDomain(col) {
65
- return this._entry(col).domain
66
- }
67
- }