gladly-plot 0.0.1
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.
- package/LICENSE +21 -0
- package/README.md +34 -0
- package/package.json +28 -0
- package/src/Axis.js +48 -0
- package/src/AxisLink.js +31 -0
- package/src/AxisQuantityKindRegistry.js +23 -0
- package/src/AxisRegistry.js +54 -0
- package/src/ColorAxisRegistry.js +49 -0
- package/src/Colorbar.js +64 -0
- package/src/ColorbarLayer.js +74 -0
- package/src/ColorscaleRegistry.js +49 -0
- package/src/FilterAxisRegistry.js +76 -0
- package/src/Filterbar.js +138 -0
- package/src/FilterbarFloat.js +157 -0
- package/src/FilterbarLayer.js +49 -0
- package/src/Float.js +159 -0
- package/src/Layer.js +43 -0
- package/src/LayerType.js +169 -0
- package/src/LayerTypeRegistry.js +19 -0
- package/src/MatplotlibColorscales.js +564 -0
- package/src/Plot.js +976 -0
- package/src/ScatterLayer.js +107 -0
- package/src/index.js +21 -0
package/src/LayerType.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
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
|
+
export class LayerType {
|
|
15
|
+
constructor({
|
|
16
|
+
name,
|
|
17
|
+
// Optional static axis declarations (for schema/introspection — no function call needed)
|
|
18
|
+
xAxis, xAxisQuantityKind,
|
|
19
|
+
yAxis, yAxisQuantityKind,
|
|
20
|
+
colorAxisQuantityKinds,
|
|
21
|
+
filterAxisQuantityKinds,
|
|
22
|
+
// Optional dynamic resolver — overrides statics wherever it returns a non-undefined value
|
|
23
|
+
getAxisConfig,
|
|
24
|
+
// GPU rendering
|
|
25
|
+
vert, frag, schema, createLayer
|
|
26
|
+
}) {
|
|
27
|
+
this.name = name
|
|
28
|
+
// Static declarations stored as-is (undefined = not declared)
|
|
29
|
+
this.xAxis = xAxis
|
|
30
|
+
this.xAxisQuantityKind = xAxisQuantityKind
|
|
31
|
+
this.yAxis = yAxis
|
|
32
|
+
this.yAxisQuantityKind = yAxisQuantityKind
|
|
33
|
+
this.colorAxisQuantityKinds = colorAxisQuantityKinds ?? []
|
|
34
|
+
this.filterAxisQuantityKinds = filterAxisQuantityKinds ?? []
|
|
35
|
+
this.vert = vert
|
|
36
|
+
this.frag = frag
|
|
37
|
+
|
|
38
|
+
if (schema) this._schema = schema
|
|
39
|
+
if (createLayer) this._createLayer = createLayer
|
|
40
|
+
if (getAxisConfig) this._getAxisConfig = getAxisConfig
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
createDrawCommand(regl, layer) {
|
|
44
|
+
const nm = layer.nameMap
|
|
45
|
+
// Rename an internal name to the shader-visible name via nameMap (identity if absent).
|
|
46
|
+
const shaderName = (internalName) => nm[internalName] ?? internalName
|
|
47
|
+
// Build a single-entry uniform object with renamed key reading from the internal prop name.
|
|
48
|
+
const u = (internalName) => ({ [shaderName(internalName)]: regl.prop(internalName) })
|
|
49
|
+
|
|
50
|
+
const attributes = Object.fromEntries(
|
|
51
|
+
Object.entries(layer.attributes).map(([key, buffer]) => {
|
|
52
|
+
const divisor = layer.attributeDivisors[key]
|
|
53
|
+
const attrObj = divisor !== undefined ? { buffer, divisor } : { buffer }
|
|
54
|
+
return [shaderName(key), attrObj]
|
|
55
|
+
})
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const uniforms = {
|
|
59
|
+
...u("xDomain"),
|
|
60
|
+
...u("yDomain"),
|
|
61
|
+
...u("xScaleType"),
|
|
62
|
+
...u("yScaleType"),
|
|
63
|
+
...Object.fromEntries(
|
|
64
|
+
Object.entries(layer.uniforms).map(([key, value]) => [shaderName(key), value])
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add per-color-axis uniforms (colorscale index + range + scale type), keyed by quantity kind
|
|
69
|
+
for (const qk of layer.colorAxes) {
|
|
70
|
+
Object.assign(uniforms, u(`colorscale_${qk}`), u(`color_range_${qk}`), u(`color_scale_type_${qk}`))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Add per-filter-axis uniforms (vec4: [min, max, hasMin, hasMax] + scale type), keyed by quantity kind
|
|
74
|
+
for (const qk of layer.filterAxes) {
|
|
75
|
+
Object.assign(uniforms, u(`filter_range_${qk}`), u(`filter_scale_type_${qk}`))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Inject GLSL helpers before the layer shader body.
|
|
79
|
+
const spatialGlsl = buildSpatialGlsl()
|
|
80
|
+
const colorGlsl = layer.colorAxes.length > 0 ? buildColorGlsl() : ''
|
|
81
|
+
const filterGlsl = layer.filterAxes.length > 0 ? buildFilterGlsl() : ''
|
|
82
|
+
const injectInto = (src, helpers) => {
|
|
83
|
+
const injected = helpers.filter(Boolean).join('\n')
|
|
84
|
+
if (!injected) return src
|
|
85
|
+
const versionRe = /^[ \t]*#version[^\n]*\n?/
|
|
86
|
+
const versionMatch = src.match(versionRe)
|
|
87
|
+
const version = versionMatch ? versionMatch[0] : ''
|
|
88
|
+
const rest = version ? src.slice(version.length) : src
|
|
89
|
+
const precisionRe = /^\s*precision\s+\S+\s+\S+\s*;\s*$/mg
|
|
90
|
+
const precisions = rest.match(precisionRe) ?? []
|
|
91
|
+
const body = rest.replace(precisionRe, '')
|
|
92
|
+
return version + precisions.join('\n') + '\n' + injected + '\n' + body
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const drawConfig = {
|
|
96
|
+
vert: injectInto(this.vert, [spatialGlsl, colorGlsl, filterGlsl]),
|
|
97
|
+
frag: injectInto(this.frag, [colorGlsl, filterGlsl]),
|
|
98
|
+
attributes,
|
|
99
|
+
uniforms,
|
|
100
|
+
viewport: regl.prop("viewport"),
|
|
101
|
+
primitive: layer.primitive,
|
|
102
|
+
lineWidth: layer.lineWidth,
|
|
103
|
+
count: regl.prop("count")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (layer.instanceCount !== null) {
|
|
107
|
+
drawConfig.instances = regl.prop("instances")
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return regl(drawConfig)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
schema(data) {
|
|
114
|
+
if (this._schema) return this._schema(data)
|
|
115
|
+
throw new Error(`LayerType '${this.name}' does not implement schema()`)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Resolves axis config by merging static declarations with dynamic getAxisConfig output.
|
|
119
|
+
// Dynamic values (non-undefined) override static values.
|
|
120
|
+
resolveAxisConfig(parameters, data) {
|
|
121
|
+
const resolved = {
|
|
122
|
+
xAxis: this.xAxis,
|
|
123
|
+
xAxisQuantityKind: this.xAxisQuantityKind,
|
|
124
|
+
yAxis: this.yAxis,
|
|
125
|
+
yAxisQuantityKind: this.yAxisQuantityKind,
|
|
126
|
+
colorAxisQuantityKinds: [...this.colorAxisQuantityKinds],
|
|
127
|
+
filterAxisQuantityKinds: [...this.filterAxisQuantityKinds],
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (this._getAxisConfig) {
|
|
131
|
+
const dynamic = this._getAxisConfig.call(this, parameters, data)
|
|
132
|
+
if (dynamic.xAxis !== undefined) resolved.xAxis = dynamic.xAxis
|
|
133
|
+
if (dynamic.xAxisQuantityKind !== undefined) resolved.xAxisQuantityKind = dynamic.xAxisQuantityKind
|
|
134
|
+
if (dynamic.yAxis !== undefined) resolved.yAxis = dynamic.yAxis
|
|
135
|
+
if (dynamic.yAxisQuantityKind !== undefined) resolved.yAxisQuantityKind = dynamic.yAxisQuantityKind
|
|
136
|
+
if (dynamic.colorAxisQuantityKinds !== undefined) resolved.colorAxisQuantityKinds = dynamic.colorAxisQuantityKinds
|
|
137
|
+
if (dynamic.filterAxisQuantityKinds !== undefined) resolved.filterAxisQuantityKinds = dynamic.filterAxisQuantityKinds
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return resolved
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
createLayer(parameters, data) {
|
|
144
|
+
if (!this._createLayer) {
|
|
145
|
+
throw new Error(`LayerType '${this.name}' does not implement createLayer()`)
|
|
146
|
+
}
|
|
147
|
+
const gpuConfigs = this._createLayer.call(this, parameters, data)
|
|
148
|
+
const axisConfig = this.resolveAxisConfig(parameters, data)
|
|
149
|
+
|
|
150
|
+
return gpuConfigs.map(gpuConfig => new Layer({
|
|
151
|
+
type: this,
|
|
152
|
+
attributes: gpuConfig.attributes ?? {},
|
|
153
|
+
uniforms: gpuConfig.uniforms ?? {},
|
|
154
|
+
nameMap: gpuConfig.nameMap ?? {},
|
|
155
|
+
domains: gpuConfig.domains ?? {},
|
|
156
|
+
lineWidth: gpuConfig.lineWidth ?? 1,
|
|
157
|
+
primitive: gpuConfig.primitive ?? "points",
|
|
158
|
+
vertexCount: gpuConfig.vertexCount ?? null,
|
|
159
|
+
instanceCount: gpuConfig.instanceCount ?? null,
|
|
160
|
+
attributeDivisors: gpuConfig.attributeDivisors ?? {},
|
|
161
|
+
xAxis: axisConfig.xAxis,
|
|
162
|
+
yAxis: axisConfig.yAxis,
|
|
163
|
+
xAxisQuantityKind: axisConfig.xAxisQuantityKind,
|
|
164
|
+
yAxisQuantityKind: axisConfig.yAxisQuantityKind,
|
|
165
|
+
colorAxes: axisConfig.colorAxisQuantityKinds,
|
|
166
|
+
filterAxes: axisConfig.filterAxisQuantityKinds,
|
|
167
|
+
}))
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const registry = new Map()
|
|
2
|
+
|
|
3
|
+
export function registerLayerType(name, layerType) {
|
|
4
|
+
if (registry.has(name)) {
|
|
5
|
+
throw new Error(`Layer type '${name}' is already registered`)
|
|
6
|
+
}
|
|
7
|
+
registry.set(name, layerType)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getLayerType(name) {
|
|
11
|
+
if (!registry.has(name)) {
|
|
12
|
+
throw new Error(`Layer type '${name}' not registered. Available types: ${Array.from(registry.keys()).join(', ')}`)
|
|
13
|
+
}
|
|
14
|
+
return registry.get(name)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getRegisteredLayerTypes() {
|
|
18
|
+
return Array.from(registry.keys())
|
|
19
|
+
}
|