gladly-plot 0.0.2 → 0.0.3
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/package.json +1 -1
- package/src/ColorbarLayer.js +1 -1
- package/src/ColorscaleRegistry.js +5 -2
- package/src/FilterbarLayer.js +1 -1
- package/src/LayerType.js +58 -21
- package/src/Plot.js +94 -2
- package/src/ScatterLayer.js +3 -4
package/package.json
CHANGED
package/src/ColorbarLayer.js
CHANGED
|
@@ -41,7 +41,7 @@ export const colorbarLayerType = new LayerType({
|
|
|
41
41
|
float r0 = color_scale_type > 0.5 ? log(color_range.x) : color_range.x;
|
|
42
42
|
float r1 = color_scale_type > 0.5 ? log(color_range.y) : color_range.y;
|
|
43
43
|
float v = r0 + tval * (r1 - r0);
|
|
44
|
-
gl_FragColor = map_color(colorscale, vec2(r0, r1), v);
|
|
44
|
+
gl_FragColor = gladly_apply_color(map_color(colorscale, vec2(r0, r1), v));
|
|
45
45
|
}
|
|
46
46
|
`,
|
|
47
47
|
|
|
@@ -38,11 +38,14 @@ export function buildColorGlsl() {
|
|
|
38
38
|
parts.push(' return vec4(0.5, 0.5, 0.5, 1.0);')
|
|
39
39
|
parts.push('}')
|
|
40
40
|
|
|
41
|
-
parts.push('vec4 map_color_s(int cs, vec2 range, float v, float scaleType) {')
|
|
41
|
+
parts.push('vec4 map_color_s(int cs, vec2 range, float v, float scaleType, float useAlpha) {')
|
|
42
42
|
parts.push(' float vt = scaleType > 0.5 ? log(v) : v;')
|
|
43
43
|
parts.push(' float r0 = scaleType > 0.5 ? log(range.x) : range.x;')
|
|
44
44
|
parts.push(' float r1 = scaleType > 0.5 ? log(range.y) : range.y;')
|
|
45
|
-
parts.push('
|
|
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);')
|
|
46
49
|
parts.push('}')
|
|
47
50
|
|
|
48
51
|
return parts.join('\n')
|
package/src/FilterbarLayer.js
CHANGED
|
@@ -24,7 +24,7 @@ export const filterbarLayerType = new LayerType({
|
|
|
24
24
|
`,
|
|
25
25
|
frag: `
|
|
26
26
|
precision mediump float;
|
|
27
|
-
void main() { gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); }
|
|
27
|
+
void main() { gl_FragColor = gladly_apply_color(vec4(0.0, 0.0, 0.0, 0.0)); }
|
|
28
28
|
`,
|
|
29
29
|
|
|
30
30
|
schema: () => ({
|
package/src/LayerType.js
CHANGED
|
@@ -11,6 +11,44 @@ function buildSpatialGlsl() {
|
|
|
11
11
|
}`
|
|
12
12
|
}
|
|
13
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
|
+
|
|
14
52
|
export class LayerType {
|
|
15
53
|
constructor({
|
|
16
54
|
name,
|
|
@@ -48,19 +86,29 @@ export class LayerType {
|
|
|
48
86
|
// Build a single-entry uniform object with renamed key reading from the internal prop name.
|
|
49
87
|
const u = (internalName) => ({ [shaderName(internalName)]: regl.prop(internalName) })
|
|
50
88
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
+
}
|
|
58
104
|
|
|
59
105
|
const uniforms = {
|
|
60
106
|
...u("xDomain"),
|
|
61
107
|
...u("yDomain"),
|
|
62
108
|
...u("xScaleType"),
|
|
63
109
|
...u("yScaleType"),
|
|
110
|
+
u_pickingMode: regl.prop('u_pickingMode'),
|
|
111
|
+
u_pickLayerIndex: regl.prop('u_pickLayerIndex'),
|
|
64
112
|
...Object.fromEntries(
|
|
65
113
|
Object.entries(layer.uniforms).map(([key, value]) => [shaderName(key), value])
|
|
66
114
|
)
|
|
@@ -80,22 +128,11 @@ export class LayerType {
|
|
|
80
128
|
const spatialGlsl = buildSpatialGlsl()
|
|
81
129
|
const colorGlsl = layer.colorAxes.length > 0 ? buildColorGlsl() : ''
|
|
82
130
|
const filterGlsl = layer.filterAxes.length > 0 ? buildFilterGlsl() : ''
|
|
83
|
-
const
|
|
84
|
-
const injected = helpers.filter(Boolean).join('\n')
|
|
85
|
-
if (!injected) return src
|
|
86
|
-
const versionRe = /^[ \t]*#version[^\n]*\n?/
|
|
87
|
-
const versionMatch = src.match(versionRe)
|
|
88
|
-
const version = versionMatch ? versionMatch[0] : ''
|
|
89
|
-
const rest = version ? src.slice(version.length) : src
|
|
90
|
-
const precisionRe = /^\s*precision\s+\S+\s+\S+\s*;\s*$/mg
|
|
91
|
-
const precisions = rest.match(precisionRe) ?? []
|
|
92
|
-
const body = rest.replace(precisionRe, '')
|
|
93
|
-
return version + precisions.join('\n') + '\n' + injected + '\n' + body
|
|
94
|
-
}
|
|
131
|
+
const pickVertDecls = `attribute float a_pickId;\nvarying float v_pickId;`
|
|
95
132
|
|
|
96
133
|
const drawConfig = {
|
|
97
|
-
vert: injectInto(this.vert, [spatialGlsl,
|
|
98
|
-
frag: injectInto(this.frag, [colorGlsl, filterGlsl]),
|
|
134
|
+
vert: injectPickIdAssignment(injectInto(this.vert, [spatialGlsl, filterGlsl, pickVertDecls])),
|
|
135
|
+
frag: injectInto(this.frag, [buildApplyColorGlsl(), colorGlsl, filterGlsl]),
|
|
99
136
|
attributes,
|
|
100
137
|
uniforms,
|
|
101
138
|
viewport: regl.prop("viewport"),
|
package/src/Plot.js
CHANGED
|
@@ -355,7 +355,8 @@ export class Plot {
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
_processLayers(layersConfig, data) {
|
|
358
|
-
for (
|
|
358
|
+
for (let configLayerIndex = 0; configLayerIndex < layersConfig.length; configLayerIndex++) {
|
|
359
|
+
const layerSpec = layersConfig[configLayerIndex]
|
|
359
360
|
const entries = Object.entries(layerSpec)
|
|
360
361
|
if (entries.length !== 1) {
|
|
361
362
|
throw new Error("Each layer specification must have exactly one layer type key")
|
|
@@ -385,6 +386,7 @@ export class Plot {
|
|
|
385
386
|
|
|
386
387
|
// Create one draw command per GPU config returned by the layer type.
|
|
387
388
|
for (const layer of layerType.createLayer(parameters, data)) {
|
|
389
|
+
layer.configLayerIndex = configLayerIndex
|
|
388
390
|
layer.draw = layer.type.createDrawCommand(this.regl, layer, this)
|
|
389
391
|
this.layers.push(layer)
|
|
390
392
|
}
|
|
@@ -811,7 +813,9 @@ export class Plot {
|
|
|
811
813
|
xScaleType: xIsLog ? 1.0 : 0.0,
|
|
812
814
|
yScaleType: yIsLog ? 1.0 : 0.0,
|
|
813
815
|
viewport: viewport,
|
|
814
|
-
count: layer.vertexCount ?? layer.attributes.x?.length ?? 0
|
|
816
|
+
count: layer.vertexCount ?? layer.attributes.x?.length ?? 0,
|
|
817
|
+
u_pickingMode: 0.0,
|
|
818
|
+
u_pickLayerIndex: 0.0,
|
|
815
819
|
}
|
|
816
820
|
|
|
817
821
|
if (layer.instanceCount !== null) {
|
|
@@ -973,4 +977,92 @@ export class Plot {
|
|
|
973
977
|
|
|
974
978
|
fullOverlay.call(zoomBehavior)
|
|
975
979
|
}
|
|
980
|
+
|
|
981
|
+
lookup(x, y) {
|
|
982
|
+
const result = {}
|
|
983
|
+
if (!this.axisRegistry) return result
|
|
984
|
+
const plotX = x - this.margin.left
|
|
985
|
+
const plotY = y - this.margin.top
|
|
986
|
+
for (const axisId of AXES) {
|
|
987
|
+
const scale = this.axisRegistry.getScale(axisId)
|
|
988
|
+
if (!scale) continue
|
|
989
|
+
const qk = this.axisRegistry.axisQuantityKinds[axisId]
|
|
990
|
+
const value = axisId.includes('y') ? scale.invert(plotY) : scale.invert(plotX)
|
|
991
|
+
result[axisId] = value
|
|
992
|
+
if (qk) result[qk] = value
|
|
993
|
+
}
|
|
994
|
+
return result
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
on(eventType, callback) {
|
|
998
|
+
const handler = (e) => {
|
|
999
|
+
if (!this.container.contains(e.target)) return
|
|
1000
|
+
const rect = this.container.getBoundingClientRect()
|
|
1001
|
+
const x = e.clientX - rect.left
|
|
1002
|
+
const y = e.clientY - rect.top
|
|
1003
|
+
callback(e, this.lookup(x, y))
|
|
1004
|
+
}
|
|
1005
|
+
window.addEventListener(eventType, handler, { capture: true })
|
|
1006
|
+
return { remove: () => window.removeEventListener(eventType, handler, { capture: true }) }
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
pick(x, y) {
|
|
1010
|
+
if (!this.regl || !this.layers.length) return null
|
|
1011
|
+
|
|
1012
|
+
const fbo = this.regl.framebuffer({
|
|
1013
|
+
width: this.width, height: this.height,
|
|
1014
|
+
colorFormat: 'rgba', colorType: 'uint8', depth: false,
|
|
1015
|
+
})
|
|
1016
|
+
|
|
1017
|
+
const glX = Math.round(x)
|
|
1018
|
+
const glY = this.height - Math.round(y) - 1
|
|
1019
|
+
|
|
1020
|
+
let result = null
|
|
1021
|
+
this.regl({ framebuffer: fbo })(() => {
|
|
1022
|
+
this.regl.clear({ color: [0, 0, 0, 0] })
|
|
1023
|
+
const viewport = {
|
|
1024
|
+
x: this.margin.left, y: this.margin.bottom,
|
|
1025
|
+
width: this.plotWidth, height: this.plotHeight
|
|
1026
|
+
}
|
|
1027
|
+
for (let i = 0; i < this.layers.length; i++) {
|
|
1028
|
+
const layer = this.layers[i]
|
|
1029
|
+
const xIsLog = layer.xAxis ? this.axisRegistry.isLogScale(layer.xAxis) : false
|
|
1030
|
+
const yIsLog = layer.yAxis ? this.axisRegistry.isLogScale(layer.yAxis) : false
|
|
1031
|
+
const props = {
|
|
1032
|
+
xDomain: layer.xAxis ? this.axisRegistry.getScale(layer.xAxis).domain() : [0, 1],
|
|
1033
|
+
yDomain: layer.yAxis ? this.axisRegistry.getScale(layer.yAxis).domain() : [0, 1],
|
|
1034
|
+
xScaleType: xIsLog ? 1.0 : 0.0,
|
|
1035
|
+
yScaleType: yIsLog ? 1.0 : 0.0,
|
|
1036
|
+
viewport,
|
|
1037
|
+
count: layer.vertexCount ?? layer.attributes.x?.length ?? 0,
|
|
1038
|
+
u_pickingMode: 1.0,
|
|
1039
|
+
u_pickLayerIndex: i,
|
|
1040
|
+
}
|
|
1041
|
+
if (layer.instanceCount !== null) props.instances = layer.instanceCount
|
|
1042
|
+
for (const qk of layer.colorAxes) {
|
|
1043
|
+
props[`colorscale_${qk}`] = this.colorAxisRegistry.getColorscaleIndex(qk)
|
|
1044
|
+
const range = this.colorAxisRegistry.getRange(qk)
|
|
1045
|
+
props[`color_range_${qk}`] = range ?? [0, 1]
|
|
1046
|
+
props[`color_scale_type_${qk}`] = this._getScaleTypeFloat(qk)
|
|
1047
|
+
}
|
|
1048
|
+
for (const qk of layer.filterAxes) {
|
|
1049
|
+
props[`filter_range_${qk}`] = this.filterAxisRegistry.getRangeUniform(qk)
|
|
1050
|
+
props[`filter_scale_type_${qk}`] = this._getScaleTypeFloat(qk)
|
|
1051
|
+
}
|
|
1052
|
+
layer.draw(props)
|
|
1053
|
+
}
|
|
1054
|
+
const pixels = this.regl.read({ x: glX, y: glY, width: 1, height: 1 })
|
|
1055
|
+
if (pixels[0] === 0) {
|
|
1056
|
+
result = null
|
|
1057
|
+
} else {
|
|
1058
|
+
const layerIndex = pixels[0] - 1
|
|
1059
|
+
const dataIndex = (pixels[1] << 16) | (pixels[2] << 8) | pixels[3]
|
|
1060
|
+
const layer = this.layers[layerIndex]
|
|
1061
|
+
result = { layerIndex, configLayerIndex: layer.configLayerIndex, dataIndex, layer }
|
|
1062
|
+
}
|
|
1063
|
+
})
|
|
1064
|
+
|
|
1065
|
+
fbo.destroy()
|
|
1066
|
+
return result
|
|
1067
|
+
}
|
|
976
1068
|
}
|
package/src/ScatterLayer.js
CHANGED
|
@@ -41,11 +41,10 @@ export const scatterLayerType = new LayerType({
|
|
|
41
41
|
uniform int colorscale;
|
|
42
42
|
uniform vec2 color_range;
|
|
43
43
|
uniform float color_scale_type;
|
|
44
|
+
uniform float alphaBlend;
|
|
44
45
|
varying float value;
|
|
45
46
|
void main() {
|
|
46
|
-
|
|
47
|
-
vec4 color = map_color_s(colorscale, color_range, value, color_scale_type);
|
|
48
|
-
gl_FragColor = vec4(color.rgb, t);
|
|
47
|
+
gl_FragColor = map_color_s(colorscale, color_range, value, color_scale_type, alphaBlend);
|
|
49
48
|
}
|
|
50
49
|
`,
|
|
51
50
|
schema: (data) => {
|
|
@@ -116,7 +115,7 @@ export const scatterLayerType = new LayerType({
|
|
|
116
115
|
|
|
117
116
|
return [{
|
|
118
117
|
attributes: { x, y, [vQK]: v },
|
|
119
|
-
uniforms: {},
|
|
118
|
+
uniforms: { alphaBlend: alphaBlend ? 1.0 : 0.0 },
|
|
120
119
|
domains,
|
|
121
120
|
nameMap: {
|
|
122
121
|
[vQK]: 'color_data',
|