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
package/src/Float.js DELETED
@@ -1,159 +0,0 @@
1
- import { Colorbar } from "./Colorbar.js"
2
- import { Plot } from "./Plot.js"
3
-
4
- const DRAG_BAR_HEIGHT = 12
5
- const MIN_WIDTH = 80
6
- const MIN_HEIGHT = DRAG_BAR_HEIGHT + 30
7
-
8
- // Default sizes include the drag bar so the colorbar content area is unchanged.
9
- const DEFAULT_SIZE = {
10
- horizontal: { width: 220, height: 70 + DRAG_BAR_HEIGHT },
11
- vertical: { width: 70, height: 220 + DRAG_BAR_HEIGHT }
12
- }
13
-
14
- export class Float {
15
- constructor(parentPlot, colorAxisName, {
16
- orientation = "horizontal",
17
- x = 10,
18
- y = 10,
19
- width,
20
- height,
21
- margin
22
- } = {}) {
23
- const defaults = DEFAULT_SIZE[orientation]
24
- const w = width ?? defaults.width
25
- const h = height ?? defaults.height
26
-
27
- // Outer floating container
28
- this._el = document.createElement('div')
29
- Object.assign(this._el.style, {
30
- position: 'absolute',
31
- left: x + 'px',
32
- top: y + 'px',
33
- width: w + 'px',
34
- height: h + 'px',
35
- zIndex: '10',
36
- boxSizing: 'border-box',
37
- background: 'rgba(255,255,255,0.88)',
38
- border: '1px solid #aaa',
39
- borderRadius: '4px',
40
- boxShadow: '0 2px 8px rgba(0,0,0,0.25)',
41
- overflow: 'hidden'
42
- })
43
-
44
- // Ensure parent is positioned so our absolute child is contained within it
45
- const parentEl = parentPlot.container
46
- if (getComputedStyle(parentEl).position === 'static') {
47
- parentEl.style.position = 'relative'
48
- }
49
- parentEl.appendChild(this._el)
50
-
51
- // Drag bar — thin strip at the top; dragging here moves the float
52
- this._dragBar = document.createElement('div')
53
- Object.assign(this._dragBar.style, {
54
- position: 'absolute',
55
- top: '0',
56
- left: '0',
57
- right: '0',
58
- height: DRAG_BAR_HEIGHT + 'px',
59
- cursor: 'grab',
60
- background: 'rgba(0,0,0,0.07)',
61
- borderBottom: '1px solid rgba(0,0,0,0.12)',
62
- zIndex: '1'
63
- })
64
- this._el.appendChild(this._dragBar)
65
-
66
- // Resize handle — bottom-right corner
67
- this._resizeHandle = document.createElement('div')
68
- Object.assign(this._resizeHandle.style, {
69
- position: 'absolute',
70
- right: '0',
71
- bottom: '0',
72
- width: '12px',
73
- height: '12px',
74
- cursor: 'se-resize',
75
- background: 'rgba(0,0,0,0.18)',
76
- borderTopLeftRadius: '3px',
77
- zIndex: '3'
78
- })
79
- this._el.appendChild(this._resizeHandle)
80
-
81
- // Sub-container for the colorbar — sits below the drag bar
82
- this._colorbarEl = document.createElement('div')
83
- Object.assign(this._colorbarEl.style, {
84
- position: 'absolute',
85
- top: DRAG_BAR_HEIGHT + 'px',
86
- left: '0',
87
- right: '0',
88
- bottom: '0'
89
- })
90
- this._el.appendChild(this._colorbarEl)
91
-
92
- this._colorbar = new Colorbar(this._colorbarEl, parentPlot, colorAxisName, { orientation, margin })
93
-
94
- this._setupInteraction()
95
- }
96
-
97
- _setupInteraction() {
98
- let mode = null // 'drag' | 'resize'
99
- let startX, startY, startLeft, startTop, startW, startH
100
-
101
- const onDragBarMouseDown = (e) => {
102
- mode = 'drag'
103
- startX = e.clientX
104
- startY = e.clientY
105
- startLeft = parseInt(this._el.style.left, 10)
106
- startTop = parseInt(this._el.style.top, 10)
107
- this._dragBar.style.cursor = 'grabbing'
108
- e.preventDefault()
109
- }
110
-
111
- const onResizeMouseDown = (e) => {
112
- mode = 'resize'
113
- startX = e.clientX
114
- startY = e.clientY
115
- startW = this._el.offsetWidth
116
- startH = this._el.offsetHeight
117
- e.preventDefault()
118
- e.stopPropagation()
119
- }
120
-
121
- const onMouseMove = (e) => {
122
- if (!mode) return
123
- const dx = e.clientX - startX
124
- const dy = e.clientY - startY
125
- if (mode === 'drag') {
126
- this._el.style.left = (startLeft + dx) + 'px'
127
- this._el.style.top = (startTop + dy) + 'px'
128
- } else {
129
- this._el.style.width = Math.max(MIN_WIDTH, startW + dx) + 'px'
130
- this._el.style.height = Math.max(MIN_HEIGHT, startH + dy) + 'px'
131
- }
132
- }
133
-
134
- const onMouseUp = () => {
135
- if (mode === 'drag') this._dragBar.style.cursor = 'grab'
136
- mode = null
137
- }
138
-
139
- this._dragBar.addEventListener('mousedown', onDragBarMouseDown)
140
- this._resizeHandle.addEventListener('mousedown', onResizeMouseDown)
141
- document.addEventListener('mousemove', onMouseMove)
142
- document.addEventListener('mouseup', onMouseUp)
143
-
144
- this._cleanupInteraction = () => {
145
- this._dragBar.removeEventListener('mousedown', onDragBarMouseDown)
146
- this._resizeHandle.removeEventListener('mousedown', onResizeMouseDown)
147
- document.removeEventListener('mousemove', onMouseMove)
148
- document.removeEventListener('mouseup', onMouseUp)
149
- }
150
- }
151
-
152
- destroy() {
153
- this._cleanupInteraction()
154
- this._colorbar.destroy()
155
- this._el.remove()
156
- }
157
- }
158
-
159
- Plot._FloatClass = Float
package/src/Layer.js DELETED
@@ -1,44 +0,0 @@
1
- export class Layer {
2
- constructor({ type, attributes, uniforms, nameMap = {}, domains = {}, lineWidth = 1, primitive = "points", xAxis = "xaxis_bottom", yAxis = "yaxis_left", xAxisQuantityKind, yAxisQuantityKind, colorAxes = [], filterAxes = [], vertexCount = null, instanceCount = null, attributeDivisors = {}, blend = null }) {
3
- // Validate that all attributes are typed arrays
4
- for (const [key, value] of Object.entries(attributes)) {
5
- if (!(value instanceof Float32Array)) {
6
- throw new Error(`Attribute '${key}' must be Float32Array`)
7
- }
8
- }
9
-
10
- // Validate colorAxes: must be an array of quantity kind strings
11
- for (const quantityKind of colorAxes) {
12
- if (typeof quantityKind !== 'string') {
13
- throw new Error(`Color axis quantity kind must be a string, got ${typeof quantityKind}`)
14
- }
15
- }
16
-
17
- // Validate filterAxes: must be an array of quantity kind strings
18
- for (const quantityKind of filterAxes) {
19
- if (typeof quantityKind !== 'string') {
20
- throw new Error(`Filter axis quantity kind must be a string, got ${typeof quantityKind}`)
21
- }
22
- }
23
-
24
- this.type = type
25
- this.attributes = attributes
26
- this.uniforms = uniforms
27
- this.nameMap = nameMap
28
- this.domains = domains
29
- this.lineWidth = lineWidth
30
- this.primitive = primitive
31
- this.xAxis = xAxis
32
- this.yAxis = yAxis
33
- this.xAxisQuantityKind = xAxisQuantityKind
34
- this.yAxisQuantityKind = yAxisQuantityKind
35
- // colorAxes: string[] — quantity kinds of color axes; attribute named by quantityKind holds the data
36
- this.colorAxes = colorAxes
37
- // filterAxes: string[] — quantity kinds of filter axes; attribute named by quantityKind holds the data
38
- this.filterAxes = filterAxes
39
- this.vertexCount = vertexCount
40
- this.instanceCount = instanceCount
41
- this.attributeDivisors = attributeDivisors
42
- this.blend = blend
43
- }
44
- }
package/src/LayerType.js DELETED
@@ -1,209 +0,0 @@
1
- import { Layer } from "./Layer.js"
2
- import { buildColorGlsl } from "./ColorscaleRegistry.js"
3
- import { buildFilterGlsl } from "./FilterAxisRegistry.js"
4
-
5
- function buildSpatialGlsl() {
6
- return `float normalize_axis(float v, vec2 domain, float scaleType) {
7
- float vt = scaleType > 0.5 ? log(v) : v;
8
- float d0 = scaleType > 0.5 ? log(domain.x) : domain.x;
9
- float d1 = scaleType > 0.5 ? log(domain.y) : domain.y;
10
- return (vt - d0) / (d1 - d0);
11
- }`
12
- }
13
-
14
- function buildApplyColorGlsl() {
15
- return `uniform float u_pickingMode;
16
- uniform float u_pickLayerIndex;
17
- varying float v_pickId;
18
- vec4 gladly_apply_color(vec4 color) {
19
- if (u_pickingMode > 0.5) {
20
- float layerIdx = u_pickLayerIndex + 1.0;
21
- float dataIdx = floor(v_pickId + 0.5);
22
- return vec4(
23
- layerIdx / 255.0,
24
- floor(dataIdx / 65536.0) / 255.0,
25
- floor(mod(dataIdx, 65536.0) / 256.0) / 255.0,
26
- mod(dataIdx, 256.0) / 255.0
27
- );
28
- }
29
- return color;
30
- }`
31
- }
32
-
33
- function injectPickIdAssignment(src) {
34
- const lastBrace = src.lastIndexOf('}')
35
- if (lastBrace === -1) return src
36
- return src.slice(0, lastBrace) + ' v_pickId = a_pickId;\n}'
37
- }
38
-
39
- function injectInto(src, helpers) {
40
- const injected = helpers.filter(Boolean).join('\n')
41
- if (!injected) return src
42
- const versionRe = /^[ \t]*#version[^\n]*\n?/
43
- const versionMatch = src.match(versionRe)
44
- const version = versionMatch ? versionMatch[0] : ''
45
- const rest = version ? src.slice(version.length) : src
46
- const precisionRe = /^\s*precision\s+\S+\s+\S+\s*;\s*$/mg
47
- const precisions = rest.match(precisionRe) ?? []
48
- const body = rest.replace(precisionRe, '')
49
- return version + precisions.join('\n') + '\n' + injected + '\n' + body
50
- }
51
-
52
- export class LayerType {
53
- constructor({
54
- name,
55
- // Optional static axis declarations (for schema/introspection — no function call needed)
56
- xAxis, xAxisQuantityKind,
57
- yAxis, yAxisQuantityKind,
58
- colorAxisQuantityKinds,
59
- filterAxisQuantityKinds,
60
- // Optional dynamic resolver — overrides statics wherever it returns a non-undefined value
61
- getAxisConfig,
62
- // GPU rendering
63
- vert, frag, schema, createLayer, createDrawCommand
64
- }) {
65
- this.name = name
66
- // Static declarations stored as-is (undefined = not declared)
67
- this.xAxis = xAxis
68
- this.xAxisQuantityKind = xAxisQuantityKind
69
- this.yAxis = yAxis
70
- this.yAxisQuantityKind = yAxisQuantityKind
71
- this.colorAxisQuantityKinds = colorAxisQuantityKinds ?? []
72
- this.filterAxisQuantityKinds = filterAxisQuantityKinds ?? []
73
- this.vert = vert
74
- this.frag = frag
75
-
76
- if (schema) this._schema = schema
77
- if (createLayer) this._createLayer = createLayer
78
- if (getAxisConfig) this._getAxisConfig = getAxisConfig
79
- if (createDrawCommand) this.createDrawCommand = createDrawCommand
80
- }
81
-
82
- createDrawCommand(regl, layer) {
83
- const nm = layer.nameMap
84
- // Rename an internal name to the shader-visible name via nameMap (identity if absent).
85
- const shaderName = (internalName) => nm[internalName] ?? internalName
86
- // Build a single-entry uniform object with renamed key reading from the internal prop name.
87
- const u = (internalName) => ({ [shaderName(internalName)]: regl.prop(internalName) })
88
-
89
- const isInstanced = layer.instanceCount !== null
90
- const pickCount = isInstanced ? layer.instanceCount : (layer.vertexCount ?? layer.attributes.x?.length ?? 0)
91
- const pickIds = new Float32Array(pickCount)
92
- for (let i = 0; i < pickCount; i++) pickIds[i] = i
93
-
94
- const attributes = {
95
- ...Object.fromEntries(
96
- Object.entries(layer.attributes).map(([key, buffer]) => {
97
- const divisor = layer.attributeDivisors[key]
98
- const attrObj = divisor !== undefined ? { buffer, divisor } : { buffer }
99
- return [shaderName(key), attrObj]
100
- })
101
- ),
102
- a_pickId: isInstanced ? { buffer: regl.buffer(pickIds), divisor: 1 } : regl.buffer(pickIds)
103
- }
104
-
105
- const uniforms = {
106
- ...u("xDomain"),
107
- ...u("yDomain"),
108
- ...u("xScaleType"),
109
- ...u("yScaleType"),
110
- u_pickingMode: regl.prop('u_pickingMode'),
111
- u_pickLayerIndex: regl.prop('u_pickLayerIndex'),
112
- ...Object.fromEntries(
113
- Object.entries(layer.uniforms).map(([key, value]) => [shaderName(key), value])
114
- )
115
- }
116
-
117
- // Add per-color-axis uniforms (colorscale index + range + scale type), keyed by quantity kind
118
- for (const qk of layer.colorAxes) {
119
- Object.assign(uniforms, u(`colorscale_${qk}`), u(`color_range_${qk}`), u(`color_scale_type_${qk}`))
120
- }
121
-
122
- // Add per-filter-axis uniforms (vec4: [min, max, hasMin, hasMax] + scale type), keyed by quantity kind
123
- for (const qk of layer.filterAxes) {
124
- Object.assign(uniforms, u(`filter_range_${qk}`), u(`filter_scale_type_${qk}`))
125
- }
126
-
127
- // Inject GLSL helpers before the layer shader body.
128
- const spatialGlsl = buildSpatialGlsl()
129
- const colorGlsl = layer.colorAxes.length > 0 ? buildColorGlsl() : ''
130
- const filterGlsl = layer.filterAxes.length > 0 ? buildFilterGlsl() : ''
131
- const pickVertDecls = `attribute float a_pickId;\nvarying float v_pickId;`
132
-
133
- const drawConfig = {
134
- vert: injectPickIdAssignment(injectInto(this.vert, [spatialGlsl, filterGlsl, pickVertDecls])),
135
- frag: injectInto(this.frag, [buildApplyColorGlsl(), colorGlsl, filterGlsl]),
136
- attributes,
137
- uniforms,
138
- viewport: regl.prop("viewport"),
139
- primitive: layer.primitive,
140
- lineWidth: layer.lineWidth,
141
- count: regl.prop("count"),
142
- ...(layer.blend ? { blend: layer.blend } : {})
143
- }
144
-
145
- if (layer.instanceCount !== null) {
146
- drawConfig.instances = regl.prop("instances")
147
- }
148
-
149
- return regl(drawConfig)
150
- }
151
-
152
- schema(data) {
153
- if (this._schema) return this._schema(data)
154
- throw new Error(`LayerType '${this.name}' does not implement schema()`)
155
- }
156
-
157
- // Resolves axis config by merging static declarations with dynamic getAxisConfig output.
158
- // Dynamic values (non-undefined) override static values.
159
- resolveAxisConfig(parameters, data) {
160
- const resolved = {
161
- xAxis: this.xAxis,
162
- xAxisQuantityKind: this.xAxisQuantityKind,
163
- yAxis: this.yAxis,
164
- yAxisQuantityKind: this.yAxisQuantityKind,
165
- colorAxisQuantityKinds: [...this.colorAxisQuantityKinds],
166
- filterAxisQuantityKinds: [...this.filterAxisQuantityKinds],
167
- }
168
-
169
- if (this._getAxisConfig) {
170
- const dynamic = this._getAxisConfig.call(this, parameters, data)
171
- if (dynamic.xAxis !== undefined) resolved.xAxis = dynamic.xAxis
172
- if (dynamic.xAxisQuantityKind !== undefined) resolved.xAxisQuantityKind = dynamic.xAxisQuantityKind
173
- if (dynamic.yAxis !== undefined) resolved.yAxis = dynamic.yAxis
174
- if (dynamic.yAxisQuantityKind !== undefined) resolved.yAxisQuantityKind = dynamic.yAxisQuantityKind
175
- if (dynamic.colorAxisQuantityKinds !== undefined) resolved.colorAxisQuantityKinds = dynamic.colorAxisQuantityKinds
176
- if (dynamic.filterAxisQuantityKinds !== undefined) resolved.filterAxisQuantityKinds = dynamic.filterAxisQuantityKinds
177
- }
178
-
179
- return resolved
180
- }
181
-
182
- createLayer(parameters, data) {
183
- if (!this._createLayer) {
184
- throw new Error(`LayerType '${this.name}' does not implement createLayer()`)
185
- }
186
- const gpuConfigs = this._createLayer.call(this, parameters, data)
187
- const axisConfig = this.resolveAxisConfig(parameters, data)
188
-
189
- return gpuConfigs.map(gpuConfig => new Layer({
190
- type: this,
191
- attributes: gpuConfig.attributes ?? {},
192
- uniforms: gpuConfig.uniforms ?? {},
193
- nameMap: gpuConfig.nameMap ?? {},
194
- domains: gpuConfig.domains ?? {},
195
- lineWidth: gpuConfig.lineWidth ?? 1,
196
- primitive: gpuConfig.primitive ?? "points",
197
- vertexCount: gpuConfig.vertexCount ?? null,
198
- instanceCount: gpuConfig.instanceCount ?? null,
199
- attributeDivisors: gpuConfig.attributeDivisors ?? {},
200
- blend: gpuConfig.blend ?? null,
201
- xAxis: axisConfig.xAxis,
202
- yAxis: axisConfig.yAxis,
203
- xAxisQuantityKind: axisConfig.xAxisQuantityKind,
204
- yAxisQuantityKind: axisConfig.yAxisQuantityKind,
205
- colorAxes: axisConfig.colorAxisQuantityKinds,
206
- filterAxes: axisConfig.filterAxisQuantityKinds,
207
- }))
208
- }
209
- }