@photonviz/core 0.1.0 → 0.1.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/dist/index.d.ts +242 -3
- package/dist/index.js +1428 -193
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -854,6 +854,196 @@ var BoxLayer = class {
|
|
|
854
854
|
}
|
|
855
855
|
};
|
|
856
856
|
|
|
857
|
+
// src/layers/candlestick.ts
|
|
858
|
+
var BODY_VERT = (
|
|
859
|
+
/* glsl */
|
|
860
|
+
`#version 300 es
|
|
861
|
+
precision highp float;
|
|
862
|
+
layout(location = 0) in vec2 aCorner; // unit rect [0,1]^2
|
|
863
|
+
layout(location = 1) in vec4 aRect; // (x0,y0,x1,y1) offset data space
|
|
864
|
+
layout(location = 2) in vec4 aColor;
|
|
865
|
+
${TRANSFORM_GLSL}
|
|
866
|
+
out vec4 vColor;
|
|
867
|
+
void main() {
|
|
868
|
+
vec2 p = mix(aRect.xy, aRect.zw, aCorner);
|
|
869
|
+
vColor = aColor;
|
|
870
|
+
gl_Position = vec4(dataToClip(p), 0.0, 1.0);
|
|
871
|
+
}`
|
|
872
|
+
);
|
|
873
|
+
var WICK_VERT = (
|
|
874
|
+
/* glsl */
|
|
875
|
+
`#version 300 es
|
|
876
|
+
precision highp float;
|
|
877
|
+
layout(location = 0) in vec2 aCorner; // (along 0..1, side -1..1)
|
|
878
|
+
layout(location = 1) in vec4 aSeg; // (x0,y0,x1,y1) offset data space
|
|
879
|
+
layout(location = 2) in vec4 aColor;
|
|
880
|
+
uniform vec2 uResolution;
|
|
881
|
+
uniform float uWidth;
|
|
882
|
+
${TRANSFORM_GLSL}
|
|
883
|
+
out vec4 vColor;
|
|
884
|
+
void main() {
|
|
885
|
+
vec2 s0 = (dataToClip(aSeg.xy) * 0.5 + 0.5) * uResolution;
|
|
886
|
+
vec2 s1 = (dataToClip(aSeg.zw) * 0.5 + 0.5) * uResolution;
|
|
887
|
+
vec2 d = s1 - s0;
|
|
888
|
+
float len = length(d);
|
|
889
|
+
vec2 dir = len > 1e-6 ? d / len : vec2(1.0, 0.0);
|
|
890
|
+
vec2 nrm = vec2(-dir.y, dir.x);
|
|
891
|
+
vec2 pos = mix(s0, s1, aCorner.x) + nrm * (aCorner.y * uWidth * 0.5);
|
|
892
|
+
vColor = aColor;
|
|
893
|
+
gl_Position = vec4((pos / uResolution) * 2.0 - 1.0, 0.0, 1.0);
|
|
894
|
+
}`
|
|
895
|
+
);
|
|
896
|
+
var FRAG4 = (
|
|
897
|
+
/* glsl */
|
|
898
|
+
`#version 300 es
|
|
899
|
+
precision highp float;
|
|
900
|
+
in vec4 vColor;
|
|
901
|
+
out vec4 outColor;
|
|
902
|
+
void main() { outColor = vec4(vColor.rgb * vColor.a, vColor.a); }`
|
|
903
|
+
);
|
|
904
|
+
var RECT_CORNERS = new Float32Array([0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1]);
|
|
905
|
+
var SEG_CORNERS = new Float32Array([0, -1, 1, -1, 1, 1, 0, -1, 1, 1, 0, 1]);
|
|
906
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
907
|
+
function programs(gl) {
|
|
908
|
+
let p = cache.get(gl);
|
|
909
|
+
if (!p) {
|
|
910
|
+
p = { body: createProgram(gl, BODY_VERT, FRAG4), wick: createProgram(gl, WICK_VERT, FRAG4) };
|
|
911
|
+
cache.set(gl, p);
|
|
912
|
+
}
|
|
913
|
+
return p;
|
|
914
|
+
}
|
|
915
|
+
function medianSpacing2(x, n) {
|
|
916
|
+
if (n < 2) return 1;
|
|
917
|
+
const diffs = [];
|
|
918
|
+
for (let i = 1; i < n; i++) diffs.push(Math.abs(x[i] - x[i - 1]));
|
|
919
|
+
diffs.sort((a, b) => a - b);
|
|
920
|
+
return diffs[Math.floor(diffs.length / 2)] || 1;
|
|
921
|
+
}
|
|
922
|
+
var counter4 = 0;
|
|
923
|
+
var CandlestickLayer = class {
|
|
924
|
+
constructor(gl, opts) {
|
|
925
|
+
this.buffers = [];
|
|
926
|
+
this.xRef = 0;
|
|
927
|
+
this.yRef = 0;
|
|
928
|
+
this.xBounds = [0, 0];
|
|
929
|
+
this.yBounds = [0, 0];
|
|
930
|
+
this.id = `candlestick-${counter4++}`;
|
|
931
|
+
this.gl = gl;
|
|
932
|
+
this.progs = programs(gl);
|
|
933
|
+
this.name = opts.name ?? this.id;
|
|
934
|
+
this.yAxis = opts.yAxis ?? "y";
|
|
935
|
+
this.wickWidth = opts.wickWidth ?? 1.5;
|
|
936
|
+
const up = toColor(opts.upColor ?? "#26a69a");
|
|
937
|
+
const down = toColor(opts.downColor ?? "#ef5350");
|
|
938
|
+
this.colorCss = toColorCss(up);
|
|
939
|
+
const n = Math.min(opts.x.length, opts.open.length, opts.high.length, opts.low.length, opts.close.length);
|
|
940
|
+
this.count = n;
|
|
941
|
+
const width = opts.width ?? medianSpacing2(opts.x, n) * 0.7;
|
|
942
|
+
this.xRef = n > 0 ? opts.x[0] : 0;
|
|
943
|
+
this.yRef = n > 0 ? opts.open[0] : 0;
|
|
944
|
+
const rects = new Float32Array(n * 4);
|
|
945
|
+
const wicks = new Float32Array(n * 4);
|
|
946
|
+
const colors = new Float32Array(n * 4);
|
|
947
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
948
|
+
for (let i = 0; i < n; i++) {
|
|
949
|
+
const cx = opts.x[i], o = opts.open[i], h = opts.high[i], l = opts.low[i], c = opts.close[i];
|
|
950
|
+
const x0 = cx - width / 2, x1 = cx + width / 2;
|
|
951
|
+
rects[i * 4] = x0 - this.xRef;
|
|
952
|
+
rects[i * 4 + 1] = o - this.yRef;
|
|
953
|
+
rects[i * 4 + 2] = x1 - this.xRef;
|
|
954
|
+
rects[i * 4 + 3] = c - this.yRef;
|
|
955
|
+
wicks[i * 4] = cx - this.xRef;
|
|
956
|
+
wicks[i * 4 + 1] = l - this.yRef;
|
|
957
|
+
wicks[i * 4 + 2] = cx - this.xRef;
|
|
958
|
+
wicks[i * 4 + 3] = h - this.yRef;
|
|
959
|
+
const col = c >= o ? up : down;
|
|
960
|
+
colors[i * 4] = col[0];
|
|
961
|
+
colors[i * 4 + 1] = col[1];
|
|
962
|
+
colors[i * 4 + 2] = col[2];
|
|
963
|
+
colors[i * 4 + 3] = col[3];
|
|
964
|
+
minX = Math.min(minX, x0);
|
|
965
|
+
maxX = Math.max(maxX, x1);
|
|
966
|
+
minY = Math.min(minY, l);
|
|
967
|
+
maxY = Math.max(maxY, h);
|
|
968
|
+
}
|
|
969
|
+
this.xBounds = [minX, maxX];
|
|
970
|
+
this.yBounds = [minY, maxY];
|
|
971
|
+
const cornerRect = gl.createBuffer();
|
|
972
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerRect);
|
|
973
|
+
gl.bufferData(gl.ARRAY_BUFFER, RECT_CORNERS, gl.STATIC_DRAW);
|
|
974
|
+
const cornerSeg = gl.createBuffer();
|
|
975
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerSeg);
|
|
976
|
+
gl.bufferData(gl.ARRAY_BUFFER, SEG_CORNERS, gl.STATIC_DRAW);
|
|
977
|
+
const rectBuf = gl.createBuffer();
|
|
978
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, rectBuf);
|
|
979
|
+
gl.bufferData(gl.ARRAY_BUFFER, rects, gl.STATIC_DRAW);
|
|
980
|
+
const wickBuf = gl.createBuffer();
|
|
981
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, wickBuf);
|
|
982
|
+
gl.bufferData(gl.ARRAY_BUFFER, wicks, gl.STATIC_DRAW);
|
|
983
|
+
const colorBuf = gl.createBuffer();
|
|
984
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuf);
|
|
985
|
+
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
|
|
986
|
+
this.buffers = [cornerRect, cornerSeg, rectBuf, wickBuf, colorBuf];
|
|
987
|
+
this.bodyVao = gl.createVertexArray();
|
|
988
|
+
gl.bindVertexArray(this.bodyVao);
|
|
989
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerRect);
|
|
990
|
+
gl.enableVertexAttribArray(0);
|
|
991
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
992
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, rectBuf);
|
|
993
|
+
gl.enableVertexAttribArray(1);
|
|
994
|
+
gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0);
|
|
995
|
+
gl.vertexAttribDivisor(1, 1);
|
|
996
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuf);
|
|
997
|
+
gl.enableVertexAttribArray(2);
|
|
998
|
+
gl.vertexAttribPointer(2, 4, gl.FLOAT, false, 0, 0);
|
|
999
|
+
gl.vertexAttribDivisor(2, 1);
|
|
1000
|
+
this.wickVao = gl.createVertexArray();
|
|
1001
|
+
gl.bindVertexArray(this.wickVao);
|
|
1002
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerSeg);
|
|
1003
|
+
gl.enableVertexAttribArray(0);
|
|
1004
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
1005
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, wickBuf);
|
|
1006
|
+
gl.enableVertexAttribArray(1);
|
|
1007
|
+
gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0);
|
|
1008
|
+
gl.vertexAttribDivisor(1, 1);
|
|
1009
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuf);
|
|
1010
|
+
gl.enableVertexAttribArray(2);
|
|
1011
|
+
gl.vertexAttribPointer(2, 4, gl.FLOAT, false, 0, 0);
|
|
1012
|
+
gl.vertexAttribDivisor(2, 1);
|
|
1013
|
+
gl.bindVertexArray(null);
|
|
1014
|
+
this.uBody = uniformLocations(gl, this.progs.body, [...TRANSFORM_UNIFORMS]);
|
|
1015
|
+
this.uWick = uniformLocations(gl, this.progs.wick, [...TRANSFORM_UNIFORMS, "uResolution", "uWidth"]);
|
|
1016
|
+
}
|
|
1017
|
+
bounds() {
|
|
1018
|
+
if (this.count === 0) return null;
|
|
1019
|
+
return { x: this.xBounds, y: this.yBounds };
|
|
1020
|
+
}
|
|
1021
|
+
draw(state) {
|
|
1022
|
+
if (this.count === 0) return;
|
|
1023
|
+
const gl = state.gl;
|
|
1024
|
+
gl.useProgram(this.progs.wick);
|
|
1025
|
+
setTransformUniforms(gl, this.uWick, state.x, state.y, this.xRef, this.yRef);
|
|
1026
|
+
gl.uniform2f(this.uWick.uResolution, state.pixelWidth, state.pixelHeight);
|
|
1027
|
+
gl.uniform1f(this.uWick.uWidth, this.wickWidth * state.dpr);
|
|
1028
|
+
gl.bindVertexArray(this.wickVao);
|
|
1029
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, this.count);
|
|
1030
|
+
gl.bindVertexArray(null);
|
|
1031
|
+
gl.useProgram(this.progs.body);
|
|
1032
|
+
setTransformUniforms(gl, this.uBody, state.x, state.y, this.xRef, this.yRef);
|
|
1033
|
+
gl.bindVertexArray(this.bodyVao);
|
|
1034
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, this.count);
|
|
1035
|
+
gl.bindVertexArray(null);
|
|
1036
|
+
}
|
|
1037
|
+
dispose() {
|
|
1038
|
+
this.gl.deleteVertexArray(this.bodyVao);
|
|
1039
|
+
this.gl.deleteVertexArray(this.wickVao);
|
|
1040
|
+
for (const b of this.buffers) this.gl.deleteBuffer(b);
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
function toColor(input) {
|
|
1044
|
+
return Array.isArray(input) ? input : parseColor(input);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
857
1047
|
// src/color/colormap.ts
|
|
858
1048
|
var ANCHORS = {
|
|
859
1049
|
viridis: [
|
|
@@ -917,7 +1107,7 @@ ${TRANSFORM_GLSL}
|
|
|
917
1107
|
out vec4 vColor;
|
|
918
1108
|
void main() { vColor = aColor; gl_Position = vec4(dataToClip(aPos), 0.0, 1.0); }`
|
|
919
1109
|
);
|
|
920
|
-
var
|
|
1110
|
+
var FRAG5 = (
|
|
921
1111
|
/* glsl */
|
|
922
1112
|
`#version 300 es
|
|
923
1113
|
precision highp float;
|
|
@@ -947,15 +1137,15 @@ var programCache4 = /* @__PURE__ */ new WeakMap();
|
|
|
947
1137
|
function getProgram4(gl) {
|
|
948
1138
|
let p = programCache4.get(gl);
|
|
949
1139
|
if (!p) {
|
|
950
|
-
p = createProgram(gl, VERT4,
|
|
1140
|
+
p = createProgram(gl, VERT4, FRAG5);
|
|
951
1141
|
programCache4.set(gl, p);
|
|
952
1142
|
}
|
|
953
1143
|
return p;
|
|
954
1144
|
}
|
|
955
|
-
var
|
|
1145
|
+
var counter5 = 0;
|
|
956
1146
|
var ContourLayer = class {
|
|
957
1147
|
constructor(gl, opts) {
|
|
958
|
-
this.id = `contour-${
|
|
1148
|
+
this.id = `contour-${counter5++}`;
|
|
959
1149
|
this.gl = gl;
|
|
960
1150
|
this.program = getProgram4(gl);
|
|
961
1151
|
this.yAxis = opts.yAxis ?? "y";
|
|
@@ -1036,6 +1226,230 @@ var ContourLayer = class {
|
|
|
1036
1226
|
}
|
|
1037
1227
|
};
|
|
1038
1228
|
|
|
1229
|
+
// src/layers/errorbar.ts
|
|
1230
|
+
var SEG_VERT = (
|
|
1231
|
+
/* glsl */
|
|
1232
|
+
`#version 300 es
|
|
1233
|
+
precision highp float;
|
|
1234
|
+
layout(location = 0) in vec2 aCorner; // (along 0..1, side -1..1)
|
|
1235
|
+
layout(location = 1) in vec4 aSeg; // (x0,y0,x1,y1) offset data space
|
|
1236
|
+
uniform vec2 uResolution;
|
|
1237
|
+
uniform float uWidth;
|
|
1238
|
+
${TRANSFORM_GLSL}
|
|
1239
|
+
void main() {
|
|
1240
|
+
vec2 s0 = (dataToClip(aSeg.xy) * 0.5 + 0.5) * uResolution;
|
|
1241
|
+
vec2 s1 = (dataToClip(aSeg.zw) * 0.5 + 0.5) * uResolution;
|
|
1242
|
+
vec2 d = s1 - s0;
|
|
1243
|
+
float len = length(d);
|
|
1244
|
+
vec2 dir = len > 1e-6 ? d / len : vec2(1.0, 0.0);
|
|
1245
|
+
vec2 nrm = vec2(-dir.y, dir.x);
|
|
1246
|
+
vec2 pos = mix(s0, s1, aCorner.x) + nrm * (aCorner.y * uWidth * 0.5);
|
|
1247
|
+
gl_Position = vec4((pos / uResolution) * 2.0 - 1.0, 0.0, 1.0);
|
|
1248
|
+
}`
|
|
1249
|
+
);
|
|
1250
|
+
var CAP_VERT = (
|
|
1251
|
+
/* glsl */
|
|
1252
|
+
`#version 300 es
|
|
1253
|
+
precision highp float;
|
|
1254
|
+
layout(location = 0) in vec2 aCorner; // unit quad [-1,1]^2
|
|
1255
|
+
layout(location = 1) in vec3 aCap; // (cx, cy, orient) offset data space
|
|
1256
|
+
uniform vec2 uResolution;
|
|
1257
|
+
uniform float uCapSize;
|
|
1258
|
+
uniform float uWidth;
|
|
1259
|
+
${TRANSFORM_GLSL}
|
|
1260
|
+
void main() {
|
|
1261
|
+
vec2 c = (dataToClip(aCap.xy) * 0.5 + 0.5) * uResolution;
|
|
1262
|
+
vec2 h = aCap.z < 0.5 ? vec2(uCapSize * 0.5, uWidth * 0.5) : vec2(uWidth * 0.5, uCapSize * 0.5);
|
|
1263
|
+
vec2 pos = c + aCorner * h;
|
|
1264
|
+
gl_Position = vec4((pos / uResolution) * 2.0 - 1.0, 0.0, 1.0);
|
|
1265
|
+
}`
|
|
1266
|
+
);
|
|
1267
|
+
var BAND_VERT = (
|
|
1268
|
+
/* glsl */
|
|
1269
|
+
`#version 300 es
|
|
1270
|
+
precision highp float;
|
|
1271
|
+
layout(location = 0) in vec2 aPos; // offset data space
|
|
1272
|
+
${TRANSFORM_GLSL}
|
|
1273
|
+
void main() { gl_Position = vec4(dataToClip(aPos), 0.0, 1.0); }`
|
|
1274
|
+
);
|
|
1275
|
+
var SOLID_FRAG = (
|
|
1276
|
+
/* glsl */
|
|
1277
|
+
`#version 300 es
|
|
1278
|
+
precision highp float;
|
|
1279
|
+
uniform vec4 uColor;
|
|
1280
|
+
out vec4 outColor;
|
|
1281
|
+
void main() { outColor = vec4(uColor.rgb * uColor.a, uColor.a); }`
|
|
1282
|
+
);
|
|
1283
|
+
var SEG_CORNERS2 = new Float32Array([0, -1, 1, -1, 1, 1, 0, -1, 1, 1, 0, 1]);
|
|
1284
|
+
var QUAD_CORNERS = new Float32Array([-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]);
|
|
1285
|
+
var cache2 = /* @__PURE__ */ new WeakMap();
|
|
1286
|
+
function programs2(gl) {
|
|
1287
|
+
let p = cache2.get(gl);
|
|
1288
|
+
if (!p) {
|
|
1289
|
+
p = {
|
|
1290
|
+
seg: createProgram(gl, SEG_VERT, SOLID_FRAG),
|
|
1291
|
+
cap: createProgram(gl, CAP_VERT, SOLID_FRAG),
|
|
1292
|
+
band: createProgram(gl, BAND_VERT, SOLID_FRAG)
|
|
1293
|
+
};
|
|
1294
|
+
cache2.set(gl, p);
|
|
1295
|
+
}
|
|
1296
|
+
return p;
|
|
1297
|
+
}
|
|
1298
|
+
var errAt = (e, i) => e == null ? 0 : typeof e === "number" ? e : e[i] ?? 0;
|
|
1299
|
+
var counter6 = 0;
|
|
1300
|
+
var ErrorBarLayer = class {
|
|
1301
|
+
constructor(gl, opts) {
|
|
1302
|
+
this.buffers = [];
|
|
1303
|
+
this.segCount = 0;
|
|
1304
|
+
this.capCount = 0;
|
|
1305
|
+
this.bandVerts = 0;
|
|
1306
|
+
this.xRef = 0;
|
|
1307
|
+
this.yRef = 0;
|
|
1308
|
+
this.xBounds = [0, 0];
|
|
1309
|
+
this.yBounds = [0, 0];
|
|
1310
|
+
this.id = `errorbar-${counter6++}`;
|
|
1311
|
+
this.gl = gl;
|
|
1312
|
+
this.progs = programs2(gl);
|
|
1313
|
+
const colorInput = opts.color ?? "#3b82f6";
|
|
1314
|
+
this.color = Array.isArray(colorInput) ? colorInput : parseColor(colorInput);
|
|
1315
|
+
this.colorCss = typeof colorInput === "string" ? colorInput : toColorCss(this.color);
|
|
1316
|
+
this.name = opts.name ?? this.id;
|
|
1317
|
+
this.yAxis = opts.yAxis ?? "y";
|
|
1318
|
+
this.width = opts.width ?? 1.5;
|
|
1319
|
+
this.capSize = opts.capSize ?? 6;
|
|
1320
|
+
this.bandOpacity = opts.bandOpacity ?? 0.2;
|
|
1321
|
+
this.showBand = opts.band === true;
|
|
1322
|
+
this.showWhiskers = opts.whiskers ?? true;
|
|
1323
|
+
const n = Math.min(opts.x.length, opts.y.length);
|
|
1324
|
+
this.xRef = n > 0 ? opts.x[0] : 0;
|
|
1325
|
+
this.yRef = n > 0 ? opts.y[0] : 0;
|
|
1326
|
+
const segs = [];
|
|
1327
|
+
const caps = [];
|
|
1328
|
+
const lowPts = [];
|
|
1329
|
+
const highPts = [];
|
|
1330
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
1331
|
+
const hasY = opts.yerr != null || opts.yerrLow != null || opts.yerrHigh != null;
|
|
1332
|
+
const hasX = opts.xerr != null;
|
|
1333
|
+
for (let i = 0; i < n; i++) {
|
|
1334
|
+
const x = opts.x[i], y = opts.y[i];
|
|
1335
|
+
const eyLo = opts.yerrLow != null ? errAt(opts.yerrLow, i) : errAt(opts.yerr, i);
|
|
1336
|
+
const eyHi = opts.yerrHigh != null ? errAt(opts.yerrHigh, i) : errAt(opts.yerr, i);
|
|
1337
|
+
const ex = errAt(opts.xerr, i);
|
|
1338
|
+
const yLo = y - eyLo, yHi = y + eyHi;
|
|
1339
|
+
const xLo = x - ex, xHi = x + ex;
|
|
1340
|
+
if (hasY) {
|
|
1341
|
+
segs.push(x - this.xRef, yLo - this.yRef, x - this.xRef, yHi - this.yRef);
|
|
1342
|
+
caps.push(x - this.xRef, yLo - this.yRef, 0, x - this.xRef, yHi - this.yRef, 0);
|
|
1343
|
+
}
|
|
1344
|
+
if (hasX) {
|
|
1345
|
+
segs.push(xLo - this.xRef, y - this.yRef, xHi - this.xRef, y - this.yRef);
|
|
1346
|
+
caps.push(xLo - this.xRef, y - this.yRef, 1, xHi - this.xRef, y - this.yRef, 1);
|
|
1347
|
+
}
|
|
1348
|
+
lowPts.push([x - this.xRef, yLo - this.yRef]);
|
|
1349
|
+
highPts.push([x - this.xRef, yHi - this.yRef]);
|
|
1350
|
+
minX = Math.min(minX, xLo);
|
|
1351
|
+
maxX = Math.max(maxX, xHi);
|
|
1352
|
+
minY = Math.min(minY, yLo);
|
|
1353
|
+
maxY = Math.max(maxY, yHi);
|
|
1354
|
+
}
|
|
1355
|
+
this.xBounds = [minX, maxX];
|
|
1356
|
+
this.yBounds = [minY, maxY];
|
|
1357
|
+
this.segCount = segs.length / 4;
|
|
1358
|
+
this.capCount = this.capSize > 0 ? caps.length / 3 : 0;
|
|
1359
|
+
const band = [];
|
|
1360
|
+
for (let i = 0; i < n; i++) {
|
|
1361
|
+
band.push(highPts[i][0], highPts[i][1], lowPts[i][0], lowPts[i][1]);
|
|
1362
|
+
}
|
|
1363
|
+
this.bandVerts = n * 2;
|
|
1364
|
+
const cornerSeg = gl.createBuffer();
|
|
1365
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerSeg);
|
|
1366
|
+
gl.bufferData(gl.ARRAY_BUFFER, SEG_CORNERS2, gl.STATIC_DRAW);
|
|
1367
|
+
const cornerQuad = gl.createBuffer();
|
|
1368
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerQuad);
|
|
1369
|
+
gl.bufferData(gl.ARRAY_BUFFER, QUAD_CORNERS, gl.STATIC_DRAW);
|
|
1370
|
+
const segBuf = gl.createBuffer();
|
|
1371
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, segBuf);
|
|
1372
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(segs), gl.STATIC_DRAW);
|
|
1373
|
+
const capBuf = gl.createBuffer();
|
|
1374
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, capBuf);
|
|
1375
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(caps), gl.STATIC_DRAW);
|
|
1376
|
+
const bandBuf = gl.createBuffer();
|
|
1377
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, bandBuf);
|
|
1378
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(band), gl.STATIC_DRAW);
|
|
1379
|
+
this.buffers = [cornerSeg, cornerQuad, segBuf, capBuf, bandBuf];
|
|
1380
|
+
this.segVao = gl.createVertexArray();
|
|
1381
|
+
gl.bindVertexArray(this.segVao);
|
|
1382
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerSeg);
|
|
1383
|
+
gl.enableVertexAttribArray(0);
|
|
1384
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
1385
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, segBuf);
|
|
1386
|
+
gl.enableVertexAttribArray(1);
|
|
1387
|
+
gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0);
|
|
1388
|
+
gl.vertexAttribDivisor(1, 1);
|
|
1389
|
+
this.capVao = gl.createVertexArray();
|
|
1390
|
+
gl.bindVertexArray(this.capVao);
|
|
1391
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerQuad);
|
|
1392
|
+
gl.enableVertexAttribArray(0);
|
|
1393
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
1394
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, capBuf);
|
|
1395
|
+
gl.enableVertexAttribArray(1);
|
|
1396
|
+
gl.vertexAttribPointer(1, 3, gl.FLOAT, false, 0, 0);
|
|
1397
|
+
gl.vertexAttribDivisor(1, 1);
|
|
1398
|
+
this.bandVao = gl.createVertexArray();
|
|
1399
|
+
gl.bindVertexArray(this.bandVao);
|
|
1400
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, bandBuf);
|
|
1401
|
+
gl.enableVertexAttribArray(0);
|
|
1402
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
1403
|
+
gl.bindVertexArray(null);
|
|
1404
|
+
this.uSeg = uniformLocations(gl, this.progs.seg, [...TRANSFORM_UNIFORMS, "uColor", "uResolution", "uWidth"]);
|
|
1405
|
+
this.uCap = uniformLocations(gl, this.progs.cap, [...TRANSFORM_UNIFORMS, "uColor", "uResolution", "uCapSize", "uWidth"]);
|
|
1406
|
+
this.uBand = uniformLocations(gl, this.progs.band, [...TRANSFORM_UNIFORMS, "uColor"]);
|
|
1407
|
+
}
|
|
1408
|
+
bounds() {
|
|
1409
|
+
if (this.segCount === 0 && this.bandVerts === 0) return null;
|
|
1410
|
+
return { x: this.xBounds, y: this.yBounds };
|
|
1411
|
+
}
|
|
1412
|
+
draw(state) {
|
|
1413
|
+
const gl = state.gl;
|
|
1414
|
+
const [r, g, b, a] = this.color;
|
|
1415
|
+
if (this.showBand && this.bandVerts >= 4) {
|
|
1416
|
+
gl.useProgram(this.progs.band);
|
|
1417
|
+
setTransformUniforms(gl, this.uBand, state.x, state.y, this.xRef, this.yRef);
|
|
1418
|
+
gl.uniform4f(this.uBand.uColor, r, g, b, a * this.bandOpacity);
|
|
1419
|
+
gl.bindVertexArray(this.bandVao);
|
|
1420
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, this.bandVerts);
|
|
1421
|
+
gl.bindVertexArray(null);
|
|
1422
|
+
}
|
|
1423
|
+
if (this.showWhiskers && this.segCount > 0) {
|
|
1424
|
+
gl.useProgram(this.progs.seg);
|
|
1425
|
+
setTransformUniforms(gl, this.uSeg, state.x, state.y, this.xRef, this.yRef);
|
|
1426
|
+
gl.uniform4f(this.uSeg.uColor, r, g, b, a);
|
|
1427
|
+
gl.uniform2f(this.uSeg.uResolution, state.pixelWidth, state.pixelHeight);
|
|
1428
|
+
gl.uniform1f(this.uSeg.uWidth, this.width * state.dpr);
|
|
1429
|
+
gl.bindVertexArray(this.segVao);
|
|
1430
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, this.segCount);
|
|
1431
|
+
gl.bindVertexArray(null);
|
|
1432
|
+
if (this.capCount > 0) {
|
|
1433
|
+
gl.useProgram(this.progs.cap);
|
|
1434
|
+
setTransformUniforms(gl, this.uCap, state.x, state.y, this.xRef, this.yRef);
|
|
1435
|
+
gl.uniform4f(this.uCap.uColor, r, g, b, a);
|
|
1436
|
+
gl.uniform2f(this.uCap.uResolution, state.pixelWidth, state.pixelHeight);
|
|
1437
|
+
gl.uniform1f(this.uCap.uCapSize, this.capSize * state.dpr);
|
|
1438
|
+
gl.uniform1f(this.uCap.uWidth, this.width * state.dpr);
|
|
1439
|
+
gl.bindVertexArray(this.capVao);
|
|
1440
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, this.capCount);
|
|
1441
|
+
gl.bindVertexArray(null);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
dispose() {
|
|
1446
|
+
this.gl.deleteVertexArray(this.segVao);
|
|
1447
|
+
this.gl.deleteVertexArray(this.capVao);
|
|
1448
|
+
this.gl.deleteVertexArray(this.bandVao);
|
|
1449
|
+
for (const b of this.buffers) this.gl.deleteBuffer(b);
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1039
1453
|
// src/layers/heatmap.ts
|
|
1040
1454
|
var VERT5 = (
|
|
1041
1455
|
/* glsl */
|
|
@@ -1050,7 +1464,7 @@ void main() {
|
|
|
1050
1464
|
gl_Position = vec4(dataToClip(aPos), 0.0, 1.0);
|
|
1051
1465
|
}`
|
|
1052
1466
|
);
|
|
1053
|
-
var
|
|
1467
|
+
var FRAG6 = (
|
|
1054
1468
|
/* glsl */
|
|
1055
1469
|
`#version 300 es
|
|
1056
1470
|
precision highp float;
|
|
@@ -1066,15 +1480,15 @@ var programCache5 = /* @__PURE__ */ new WeakMap();
|
|
|
1066
1480
|
function getProgram5(gl) {
|
|
1067
1481
|
let p = programCache5.get(gl);
|
|
1068
1482
|
if (!p) {
|
|
1069
|
-
p = createProgram(gl, VERT5,
|
|
1483
|
+
p = createProgram(gl, VERT5, FRAG6);
|
|
1070
1484
|
programCache5.set(gl, p);
|
|
1071
1485
|
}
|
|
1072
1486
|
return p;
|
|
1073
1487
|
}
|
|
1074
|
-
var
|
|
1488
|
+
var counter7 = 0;
|
|
1075
1489
|
var HeatmapLayer = class {
|
|
1076
1490
|
constructor(gl, opts) {
|
|
1077
|
-
this.id = `heatmap-${
|
|
1491
|
+
this.id = `heatmap-${counter7++}`;
|
|
1078
1492
|
this.gl = gl;
|
|
1079
1493
|
this.program = getProgram5(gl);
|
|
1080
1494
|
this.yAxis = opts.yAxis ?? "y";
|
|
@@ -1191,7 +1605,7 @@ void main() {
|
|
|
1191
1605
|
gl_Position = vec4(dataToClip(aCenter + aCorner * uRadius), 0.0, 1.0);
|
|
1192
1606
|
}`
|
|
1193
1607
|
);
|
|
1194
|
-
var
|
|
1608
|
+
var FRAG7 = (
|
|
1195
1609
|
/* glsl */
|
|
1196
1610
|
`#version 300 es
|
|
1197
1611
|
precision highp float;
|
|
@@ -1214,12 +1628,12 @@ var programCache6 = /* @__PURE__ */ new WeakMap();
|
|
|
1214
1628
|
function getProgram6(gl) {
|
|
1215
1629
|
let p = programCache6.get(gl);
|
|
1216
1630
|
if (!p) {
|
|
1217
|
-
p = createProgram(gl, VERT6,
|
|
1631
|
+
p = createProgram(gl, VERT6, FRAG7);
|
|
1218
1632
|
programCache6.set(gl, p);
|
|
1219
1633
|
}
|
|
1220
1634
|
return p;
|
|
1221
1635
|
}
|
|
1222
|
-
var
|
|
1636
|
+
var counter8 = 0;
|
|
1223
1637
|
var HexbinLayer = class {
|
|
1224
1638
|
constructor(gl, opts) {
|
|
1225
1639
|
this.buffers = [];
|
|
@@ -1227,7 +1641,7 @@ var HexbinLayer = class {
|
|
|
1227
1641
|
this.yRef = 0;
|
|
1228
1642
|
this.xBounds = [0, 0];
|
|
1229
1643
|
this.yBounds = [0, 0];
|
|
1230
|
-
this.id = `hexbin-${
|
|
1644
|
+
this.id = `hexbin-${counter8++}`;
|
|
1231
1645
|
this.gl = gl;
|
|
1232
1646
|
this.program = getProgram6(gl);
|
|
1233
1647
|
this.yAxis = opts.yAxis ?? "y";
|
|
@@ -1325,29 +1739,245 @@ var HexbinLayer = class {
|
|
|
1325
1739
|
}
|
|
1326
1740
|
};
|
|
1327
1741
|
|
|
1328
|
-
// src/layers/line.ts
|
|
1329
|
-
|
|
1742
|
+
// src/layers/line-util.ts
|
|
1743
|
+
function decimateIndices(ys, i0, i1, cols) {
|
|
1744
|
+
const out = [i0];
|
|
1745
|
+
const visN = i1 - i0;
|
|
1746
|
+
for (let b = 0; b < cols; b++) {
|
|
1747
|
+
const lo = i0 + Math.floor(visN * b / cols);
|
|
1748
|
+
const hi = i0 + Math.floor(visN * (b + 1) / cols);
|
|
1749
|
+
if (hi <= lo) continue;
|
|
1750
|
+
let iMin = lo, iMax = lo;
|
|
1751
|
+
for (let i = lo; i < hi; i++) {
|
|
1752
|
+
if (ys[i] < ys[iMin]) iMin = i;
|
|
1753
|
+
if (ys[i] > ys[iMax]) iMax = i;
|
|
1754
|
+
}
|
|
1755
|
+
if (iMin < iMax) out.push(iMin, iMax);
|
|
1756
|
+
else out.push(iMax, iMin);
|
|
1757
|
+
}
|
|
1758
|
+
out.push(i1);
|
|
1759
|
+
return out;
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
// src/layers/gpu-decimate.ts
|
|
1763
|
+
var DEC_VERT = (
|
|
1330
1764
|
/* glsl */
|
|
1331
1765
|
`#version 300 es
|
|
1332
1766
|
precision highp float;
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
uniform
|
|
1337
|
-
uniform
|
|
1338
|
-
uniform
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1767
|
+
uniform sampler2D uPoints;
|
|
1768
|
+
uniform int uTexW;
|
|
1769
|
+
uniform int uI0;
|
|
1770
|
+
uniform int uI1;
|
|
1771
|
+
uniform int uCols;
|
|
1772
|
+
uniform int uMaxIter; // per-column iteration cap; stride-samples beyond it
|
|
1773
|
+
out vec2 vOut;
|
|
1774
|
+
|
|
1775
|
+
vec2 fetchPoint(int i) {
|
|
1776
|
+
return texelFetch(uPoints, ivec2(i % uTexW, i / uTexW), 0).xy;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1343
1779
|
void main() {
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1780
|
+
int v = gl_VertexID;
|
|
1781
|
+
int last = 2 * uCols + 1;
|
|
1782
|
+
int idx;
|
|
1783
|
+
if (v == 0) {
|
|
1784
|
+
idx = uI0; // left endpoint, so the line reaches the edge
|
|
1785
|
+
} else if (v >= last) {
|
|
1786
|
+
idx = uI1; // right endpoint
|
|
1787
|
+
} else {
|
|
1788
|
+
int col = (v - 1) / 2;
|
|
1789
|
+
int which = (v - 1) - col * 2; // 0 => min-index extreme, 1 => max-index extreme
|
|
1790
|
+
// Float division avoids int32 overflow of visN*col for huge series.
|
|
1791
|
+
float fvisN = float(uI1 - uI0);
|
|
1792
|
+
int lo = uI0 + int(floor(fvisN * float(col) / float(uCols)));
|
|
1793
|
+
int hi = uI0 + int(floor(fvisN * float(col + 1) / float(uCols)));
|
|
1794
|
+
if (hi <= lo) {
|
|
1795
|
+
idx = lo;
|
|
1796
|
+
} else {
|
|
1797
|
+
int stride = 1;
|
|
1798
|
+
int span = hi - lo;
|
|
1799
|
+
if (span > uMaxIter) stride = (span + uMaxIter - 1) / uMaxIter;
|
|
1800
|
+
int iMin = lo, iMax = lo;
|
|
1801
|
+
float yMin = fetchPoint(lo).y, yMax = yMin;
|
|
1802
|
+
for (int i = lo; i < hi; i += stride) {
|
|
1803
|
+
float y = fetchPoint(i).y;
|
|
1804
|
+
if (y < yMin) { yMin = y; iMin = i; }
|
|
1805
|
+
if (y > yMax) { yMax = y; iMax = i; }
|
|
1806
|
+
}
|
|
1807
|
+
// Emit in index order (matches decimateIndices) to preserve envelope shape.
|
|
1808
|
+
idx = (which == 0) ? min(iMin, iMax) : max(iMin, iMax);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
vOut = fetchPoint(idx);
|
|
1812
|
+
gl_Position = vec4(0.0, 0.0, 0.0, 1.0); // rasterizer-discarded
|
|
1813
|
+
}`
|
|
1814
|
+
);
|
|
1815
|
+
var DEC_FRAG = (
|
|
1816
|
+
/* glsl */
|
|
1817
|
+
`#version 300 es
|
|
1818
|
+
precision highp float;
|
|
1819
|
+
out vec4 o;
|
|
1820
|
+
void main() { o = vec4(0.0); }`
|
|
1821
|
+
);
|
|
1822
|
+
var MAX_ITER = 4096;
|
|
1823
|
+
function compile(gl, type, src) {
|
|
1824
|
+
const sh = gl.createShader(type);
|
|
1825
|
+
gl.shaderSource(sh, src);
|
|
1826
|
+
gl.compileShader(sh);
|
|
1827
|
+
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
|
|
1828
|
+
const log = gl.getShaderInfoLog(sh);
|
|
1829
|
+
gl.deleteShader(sh);
|
|
1830
|
+
throw new Error(`Decimation shader compile error:
|
|
1831
|
+
${log}`);
|
|
1832
|
+
}
|
|
1833
|
+
return sh;
|
|
1834
|
+
}
|
|
1835
|
+
var programCache7 = /* @__PURE__ */ new WeakMap();
|
|
1836
|
+
function getDecProgram(gl) {
|
|
1837
|
+
if (programCache7.has(gl)) return programCache7.get(gl);
|
|
1838
|
+
let result = null;
|
|
1839
|
+
try {
|
|
1840
|
+
const vs = compile(gl, gl.VERTEX_SHADER, DEC_VERT);
|
|
1841
|
+
const fs = compile(gl, gl.FRAGMENT_SHADER, DEC_FRAG);
|
|
1842
|
+
const program = gl.createProgram();
|
|
1843
|
+
gl.attachShader(program, vs);
|
|
1844
|
+
gl.attachShader(program, fs);
|
|
1845
|
+
gl.transformFeedbackVaryings(program, ["vOut"], gl.INTERLEAVED_ATTRIBS);
|
|
1846
|
+
gl.linkProgram(program);
|
|
1847
|
+
gl.deleteShader(vs);
|
|
1848
|
+
gl.deleteShader(fs);
|
|
1849
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
1850
|
+
gl.deleteProgram(program);
|
|
1851
|
+
} else {
|
|
1852
|
+
const u = {};
|
|
1853
|
+
for (const name of ["uPoints", "uTexW", "uI0", "uI1", "uCols", "uMaxIter"]) {
|
|
1854
|
+
u[name] = gl.getUniformLocation(program, name);
|
|
1855
|
+
}
|
|
1856
|
+
result = { program, u };
|
|
1857
|
+
}
|
|
1858
|
+
} catch {
|
|
1859
|
+
result = null;
|
|
1860
|
+
}
|
|
1861
|
+
programCache7.set(gl, result);
|
|
1862
|
+
return result;
|
|
1863
|
+
}
|
|
1864
|
+
var GpuDecimator = class {
|
|
1865
|
+
// point capacity currently allocated in the draw buffer
|
|
1866
|
+
constructor(gl) {
|
|
1867
|
+
this.tex = null;
|
|
1868
|
+
this.tf = null;
|
|
1869
|
+
this.emptyVao = null;
|
|
1870
|
+
this.texW = 0;
|
|
1871
|
+
this.decCapacity = -1;
|
|
1872
|
+
this.gl = gl;
|
|
1873
|
+
this.prog = getDecProgram(gl);
|
|
1874
|
+
this.supported = this.prog != null;
|
|
1875
|
+
}
|
|
1876
|
+
/**
|
|
1877
|
+
* (Re)upload the series into the point texture. `data` is interleaved
|
|
1878
|
+
* (x-xRef, y-yRef) float32, exactly as the draw buffer stores it. Returns
|
|
1879
|
+
* false and disables the GPU path if the texture can't be allocated.
|
|
1880
|
+
*/
|
|
1881
|
+
setPoints(data, n) {
|
|
1882
|
+
if (!this.supported || n === 0) return this.supported;
|
|
1883
|
+
const gl = this.gl;
|
|
1884
|
+
const maxTex = gl.getParameter(gl.MAX_TEXTURE_SIZE);
|
|
1885
|
+
const texW = Math.min(maxTex, Math.max(1, Math.ceil(Math.sqrt(n))));
|
|
1886
|
+
const texH = Math.ceil(n / texW);
|
|
1887
|
+
if (texH > maxTex) return false;
|
|
1888
|
+
this.texW = texW;
|
|
1889
|
+
const packed = data.length >= texW * texH * 2 ? data : new Float32Array(texW * texH * 2);
|
|
1890
|
+
if (packed !== data) packed.set(data.subarray(0, n * 2));
|
|
1891
|
+
try {
|
|
1892
|
+
if (!this.tex) this.tex = gl.createTexture();
|
|
1893
|
+
gl.bindTexture(gl.TEXTURE_2D, this.tex);
|
|
1894
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, texW, texH, 0, gl.RG, gl.FLOAT, packed);
|
|
1895
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
1896
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
1897
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
1898
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
1899
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
1900
|
+
} catch {
|
|
1901
|
+
return false;
|
|
1902
|
+
}
|
|
1903
|
+
if (!this.tf) this.tf = gl.createTransformFeedback();
|
|
1904
|
+
if (!this.emptyVao) this.emptyVao = gl.createVertexArray();
|
|
1905
|
+
return true;
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1908
|
+
* Run the reduction for the visible window `[i0, i1]` into `decBuf`, matching
|
|
1909
|
+
* the CPU envelope layout: left endpoint, `cols` min/max pairs, right endpoint.
|
|
1910
|
+
* Returns the emitted point count, or null if the GPU path can't run this call.
|
|
1911
|
+
*/
|
|
1912
|
+
run(i0, i1, cols, decBuf) {
|
|
1913
|
+
if (!this.supported || !this.tex || !this.prog) return null;
|
|
1914
|
+
const gl = this.gl;
|
|
1915
|
+
const outCount = 2 * cols + 2;
|
|
1916
|
+
if (this.decCapacity < outCount) {
|
|
1917
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, decBuf);
|
|
1918
|
+
gl.bufferData(gl.ARRAY_BUFFER, outCount * 2 * 4, gl.DYNAMIC_COPY);
|
|
1919
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
1920
|
+
this.decCapacity = outCount;
|
|
1921
|
+
}
|
|
1922
|
+
const { program, u } = this.prog;
|
|
1923
|
+
gl.useProgram(program);
|
|
1924
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
1925
|
+
gl.bindTexture(gl.TEXTURE_2D, this.tex);
|
|
1926
|
+
gl.uniform1i(u.uPoints, 0);
|
|
1927
|
+
gl.uniform1i(u.uTexW, this.texW);
|
|
1928
|
+
gl.uniform1i(u.uI0, i0);
|
|
1929
|
+
gl.uniform1i(u.uI1, i1);
|
|
1930
|
+
gl.uniform1i(u.uCols, cols);
|
|
1931
|
+
gl.uniform1i(u.uMaxIter, MAX_ITER);
|
|
1932
|
+
gl.bindVertexArray(this.emptyVao);
|
|
1933
|
+
gl.enable(gl.RASTERIZER_DISCARD);
|
|
1934
|
+
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, this.tf);
|
|
1935
|
+
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, decBuf);
|
|
1936
|
+
gl.beginTransformFeedback(gl.POINTS);
|
|
1937
|
+
gl.drawArrays(gl.POINTS, 0, outCount);
|
|
1938
|
+
gl.endTransformFeedback();
|
|
1939
|
+
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
|
|
1940
|
+
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
|
|
1941
|
+
gl.disable(gl.RASTERIZER_DISCARD);
|
|
1942
|
+
gl.bindVertexArray(null);
|
|
1943
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
1944
|
+
return outCount;
|
|
1945
|
+
}
|
|
1946
|
+
dispose() {
|
|
1947
|
+
const gl = this.gl;
|
|
1948
|
+
if (this.tex) gl.deleteTexture(this.tex);
|
|
1949
|
+
if (this.tf) gl.deleteTransformFeedback(this.tf);
|
|
1950
|
+
if (this.emptyVao) gl.deleteVertexArray(this.emptyVao);
|
|
1951
|
+
this.tex = null;
|
|
1952
|
+
this.tf = null;
|
|
1953
|
+
this.emptyVao = null;
|
|
1954
|
+
}
|
|
1955
|
+
};
|
|
1956
|
+
|
|
1957
|
+
// src/layers/line.ts
|
|
1958
|
+
var GPU_DECIMATE_MIN = 2e5;
|
|
1959
|
+
var VERT7 = (
|
|
1960
|
+
/* glsl */
|
|
1961
|
+
`#version 300 es
|
|
1962
|
+
precision highp float;
|
|
1963
|
+
layout(location = 0) in vec2 aCorner; // (along 0..1, side -1..1)
|
|
1964
|
+
layout(location = 1) in vec2 aP0;
|
|
1965
|
+
layout(location = 2) in vec2 aP1;
|
|
1966
|
+
uniform vec2 uResolution;
|
|
1967
|
+
uniform float uWidth;
|
|
1968
|
+
uniform float uRound;
|
|
1969
|
+
${TRANSFORM_GLSL}
|
|
1970
|
+
out vec2 vPix;
|
|
1971
|
+
out vec2 vS0;
|
|
1972
|
+
out vec2 vS1;
|
|
1973
|
+
void main() {
|
|
1974
|
+
vec2 s0 = (dataToClip(aP0) * 0.5 + 0.5) * uResolution;
|
|
1975
|
+
vec2 s1 = (dataToClip(aP1) * 0.5 + 0.5) * uResolution;
|
|
1976
|
+
vec2 d = s1 - s0;
|
|
1977
|
+
float len = length(d);
|
|
1978
|
+
vec2 dir = len > 1e-6 ? d / len : vec2(1.0, 0.0);
|
|
1979
|
+
vec2 nrm = vec2(-dir.y, dir.x);
|
|
1980
|
+
float hw = uWidth * 0.5 + 1.5; // half width + AA margin
|
|
1351
1981
|
float ext = uRound > 0.5 ? hw : 0.0; // extend for round caps
|
|
1352
1982
|
vec2 endpoint = mix(s0, s1, aCorner.x);
|
|
1353
1983
|
vec2 outward = (aCorner.x < 0.5 ? -dir : dir) * ext;
|
|
@@ -1356,7 +1986,7 @@ void main() {
|
|
|
1356
1986
|
gl_Position = vec4((pos / uResolution) * 2.0 - 1.0, 0.0, 1.0);
|
|
1357
1987
|
}`
|
|
1358
1988
|
);
|
|
1359
|
-
var
|
|
1989
|
+
var FRAG8 = (
|
|
1360
1990
|
/* glsl */
|
|
1361
1991
|
`#version 300 es
|
|
1362
1992
|
precision highp float;
|
|
@@ -1385,13 +2015,112 @@ void main() {
|
|
|
1385
2015
|
outColor = vec4(uColor.rgb * uColor.a, uColor.a) * alpha;
|
|
1386
2016
|
}`
|
|
1387
2017
|
);
|
|
2018
|
+
var JOIN_VERT = (
|
|
2019
|
+
/* glsl */
|
|
2020
|
+
`#version 300 es
|
|
2021
|
+
precision highp float;
|
|
2022
|
+
layout(location = 0) in vec2 aPrev;
|
|
2023
|
+
layout(location = 1) in vec2 aP0;
|
|
2024
|
+
layout(location = 2) in vec2 aNext;
|
|
2025
|
+
uniform vec2 uResolution;
|
|
2026
|
+
uniform float uWidth;
|
|
2027
|
+
uniform float uMiter; // >0.5 => miter, else bevel
|
|
2028
|
+
uniform float uMiterLimit;
|
|
2029
|
+
${TRANSFORM_GLSL}
|
|
2030
|
+
out vec2 vPix;
|
|
2031
|
+
flat out vec2 vE0;
|
|
2032
|
+
flat out vec2 vE1;
|
|
2033
|
+
flat out vec2 vInner;
|
|
2034
|
+
void main() {
|
|
2035
|
+
vec2 sp = (dataToClip(aPrev) * 0.5 + 0.5) * uResolution;
|
|
2036
|
+
vec2 s0 = (dataToClip(aP0) * 0.5 + 0.5) * uResolution;
|
|
2037
|
+
vec2 sn = (dataToClip(aNext) * 0.5 + 0.5) * uResolution;
|
|
2038
|
+
vec2 din = s0 - sp;
|
|
2039
|
+
vec2 dout = sn - s0;
|
|
2040
|
+
float inl = length(din), outl = length(dout);
|
|
2041
|
+
vec2 inN = vec2(0.0), outN = vec2(0.0);
|
|
2042
|
+
float crs = 0.0;
|
|
2043
|
+
bool ok = inl > 1e-6 && outl > 1e-6;
|
|
2044
|
+
if (ok) {
|
|
2045
|
+
din /= inl; dout /= outl;
|
|
2046
|
+
inN = vec2(-din.y, din.x);
|
|
2047
|
+
outN = vec2(-dout.y, dout.x);
|
|
2048
|
+
crs = din.x * dout.y - din.y * dout.x;
|
|
2049
|
+
ok = abs(crs) > 1e-6; // collinear => no notch
|
|
2050
|
+
}
|
|
2051
|
+
if (!ok) { // degenerate: cull the wedge
|
|
2052
|
+
vE0 = vec2(0.0); vE1 = vec2(0.0); vInner = vec2(0.0); vPix = vec2(0.0);
|
|
2053
|
+
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
float hw = uWidth * 0.5;
|
|
2057
|
+
float outerSign = crs > 0.0 ? -1.0 : 1.0;
|
|
2058
|
+
vec2 A = s0 + inN * hw * outerSign;
|
|
2059
|
+
vec2 B = s0 + outN * hw * outerSign;
|
|
2060
|
+
vec2 apex = 0.5 * (A + B); // bevel midpoint
|
|
2061
|
+
if (uMiter > 0.5) {
|
|
2062
|
+
vec2 mN = inN + outN;
|
|
2063
|
+
float ml = length(mN);
|
|
2064
|
+
if (ml > 1e-6) {
|
|
2065
|
+
mN /= ml;
|
|
2066
|
+
float denom = dot(mN, outN);
|
|
2067
|
+
if (denom > 1e-3) {
|
|
2068
|
+
float miterLen = 1.0 / denom;
|
|
2069
|
+
if (miterLen <= uMiterLimit) apex = s0 + mN * outerSign * hw * miterLen;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
int vid = gl_VertexID;
|
|
2074
|
+
vec2 pos;
|
|
2075
|
+
if (vid == 0 || vid == 3) pos = s0;
|
|
2076
|
+
else if (vid == 1) pos = A;
|
|
2077
|
+
else if (vid == 2 || vid == 4) pos = apex;
|
|
2078
|
+
else pos = B; // vid == 5
|
|
2079
|
+
if (vid < 3) { vE0 = A; vE1 = apex; } // triangle [s0, A, apex]
|
|
2080
|
+
else { vE0 = apex; vE1 = B; } // triangle [s0, apex, B]
|
|
2081
|
+
vInner = s0;
|
|
2082
|
+
vPix = pos;
|
|
2083
|
+
gl_Position = vec4((pos / uResolution) * 2.0 - 1.0, 0.0, 1.0);
|
|
2084
|
+
}`
|
|
2085
|
+
);
|
|
2086
|
+
var JOIN_FRAG = (
|
|
2087
|
+
/* glsl */
|
|
2088
|
+
`#version 300 es
|
|
2089
|
+
precision highp float;
|
|
2090
|
+
in vec2 vPix;
|
|
2091
|
+
flat in vec2 vE0;
|
|
2092
|
+
flat in vec2 vE1;
|
|
2093
|
+
flat in vec2 vInner;
|
|
2094
|
+
uniform vec4 uColor;
|
|
2095
|
+
out vec4 outColor;
|
|
2096
|
+
void main() {
|
|
2097
|
+
vec2 e = vE1 - vE0;
|
|
2098
|
+
float el = length(e);
|
|
2099
|
+
if (el < 1e-6) discard;
|
|
2100
|
+
vec2 n = vec2(-e.y, e.x) / el;
|
|
2101
|
+
float sideInner = dot(vInner - vE0, n) >= 0.0 ? 1.0 : -1.0;
|
|
2102
|
+
float d = dot(vPix - vE0, n) * sideInner; // >0 inside the outer edge
|
|
2103
|
+
float alpha = clamp(d + 0.5, 0.0, 1.0);
|
|
2104
|
+
if (alpha <= 0.0) discard;
|
|
2105
|
+
outColor = vec4(uColor.rgb * uColor.a, uColor.a) * alpha;
|
|
2106
|
+
}`
|
|
2107
|
+
);
|
|
1388
2108
|
var CORNERS2 = new Float32Array([0, -1, 1, -1, 1, 1, 0, -1, 1, 1, 0, 1]);
|
|
1389
|
-
var
|
|
2109
|
+
var programCache8 = /* @__PURE__ */ new WeakMap();
|
|
1390
2110
|
function getProgram7(gl) {
|
|
1391
|
-
let p =
|
|
2111
|
+
let p = programCache8.get(gl);
|
|
2112
|
+
if (!p) {
|
|
2113
|
+
p = createProgram(gl, VERT7, FRAG8);
|
|
2114
|
+
programCache8.set(gl, p);
|
|
2115
|
+
}
|
|
2116
|
+
return p;
|
|
2117
|
+
}
|
|
2118
|
+
var joinProgramCache = /* @__PURE__ */ new WeakMap();
|
|
2119
|
+
function getJoinProgram(gl) {
|
|
2120
|
+
let p = joinProgramCache.get(gl);
|
|
1392
2121
|
if (!p) {
|
|
1393
|
-
p = createProgram(gl,
|
|
1394
|
-
|
|
2122
|
+
p = createProgram(gl, JOIN_VERT, JOIN_FRAG);
|
|
2123
|
+
joinProgramCache.set(gl, p);
|
|
1395
2124
|
}
|
|
1396
2125
|
return p;
|
|
1397
2126
|
}
|
|
@@ -1424,20 +2153,24 @@ function upperBound(a, v) {
|
|
|
1424
2153
|
}
|
|
1425
2154
|
return lo;
|
|
1426
2155
|
}
|
|
1427
|
-
var
|
|
2156
|
+
var counter9 = 0;
|
|
1428
2157
|
var LineLayer = class {
|
|
1429
2158
|
constructor(gl, opts) {
|
|
2159
|
+
this.gpuDec = null;
|
|
1430
2160
|
this.xRef = 0;
|
|
1431
2161
|
this.yRef = 0;
|
|
1432
2162
|
this.xBounds = [0, 0];
|
|
1433
2163
|
this.yBounds = [0, 0];
|
|
1434
2164
|
this.decKey = "";
|
|
1435
2165
|
this.decSegments = 0;
|
|
1436
|
-
this.id = `line-${
|
|
2166
|
+
this.id = `line-${counter9++}`;
|
|
1437
2167
|
this.gl = gl;
|
|
1438
2168
|
this.program = getProgram7(gl);
|
|
2169
|
+
this.joinProgram = getJoinProgram(gl);
|
|
1439
2170
|
this.width = opts.width ?? 1.5;
|
|
1440
|
-
this.
|
|
2171
|
+
this.joinStyle = opts.join ?? "round";
|
|
2172
|
+
this.round = this.joinStyle === "round";
|
|
2173
|
+
this.miterLimit = opts.miterLimit ?? 4;
|
|
1441
2174
|
this.decimateOn = opts.decimate !== false;
|
|
1442
2175
|
this.step = opts.step;
|
|
1443
2176
|
const colorInput = opts.color ?? "#3b82f6";
|
|
@@ -1480,154 +2213,443 @@ var LineLayer = class {
|
|
|
1480
2213
|
this.decBuf = gl.createBuffer();
|
|
1481
2214
|
this.fullVao = gl.createVertexArray();
|
|
1482
2215
|
this.decVao = gl.createVertexArray();
|
|
2216
|
+
this.joinFullVao = gl.createVertexArray();
|
|
2217
|
+
this.joinDecVao = gl.createVertexArray();
|
|
1483
2218
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.cornerBuf);
|
|
1484
2219
|
gl.bufferData(gl.ARRAY_BUFFER, CORNERS2, gl.STATIC_DRAW);
|
|
1485
2220
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.posBuf);
|
|
1486
2221
|
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
|
|
1487
2222
|
this.configureVao(this.fullVao, this.posBuf);
|
|
1488
2223
|
this.configureVao(this.decVao, this.decBuf);
|
|
2224
|
+
this.configureJoinVao(this.joinFullVao, this.posBuf);
|
|
2225
|
+
this.configureJoinVao(this.joinDecVao, this.decBuf);
|
|
1489
2226
|
this.uniforms = uniformLocations(gl, this.program, [
|
|
1490
2227
|
...TRANSFORM_UNIFORMS,
|
|
1491
2228
|
"uColor",
|
|
1492
2229
|
"uResolution",
|
|
1493
|
-
"uWidth",
|
|
1494
|
-
"uRound"
|
|
2230
|
+
"uWidth",
|
|
2231
|
+
"uRound"
|
|
2232
|
+
]);
|
|
2233
|
+
this.joinUniforms = uniformLocations(gl, this.joinProgram, [
|
|
2234
|
+
...TRANSFORM_UNIFORMS,
|
|
2235
|
+
"uColor",
|
|
2236
|
+
"uResolution",
|
|
2237
|
+
"uWidth",
|
|
2238
|
+
"uMiter",
|
|
2239
|
+
"uMiterLimit"
|
|
2240
|
+
]);
|
|
2241
|
+
this.syncGpu(data, n);
|
|
2242
|
+
}
|
|
2243
|
+
// Keep the GPU decimation texture in sync for large series; disable it (fall
|
|
2244
|
+
// back to CPU decimation) if the context can't support the path.
|
|
2245
|
+
syncGpu(data, n) {
|
|
2246
|
+
if (!this.decimateOn || n < GPU_DECIMATE_MIN) {
|
|
2247
|
+
this.disposeGpu();
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
if (!this.gpuDec) {
|
|
2251
|
+
const dec = new GpuDecimator(this.gl);
|
|
2252
|
+
if (!dec.supported) return;
|
|
2253
|
+
this.gpuDec = dec;
|
|
2254
|
+
}
|
|
2255
|
+
if (!this.gpuDec.setPoints(data, n)) this.disposeGpu();
|
|
2256
|
+
}
|
|
2257
|
+
disposeGpu() {
|
|
2258
|
+
if (this.gpuDec) {
|
|
2259
|
+
this.gpuDec.dispose();
|
|
2260
|
+
this.gpuDec = null;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
configureVao(vao, pointBuf) {
|
|
2264
|
+
const gl = this.gl;
|
|
2265
|
+
gl.bindVertexArray(vao);
|
|
2266
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this.cornerBuf);
|
|
2267
|
+
gl.enableVertexAttribArray(0);
|
|
2268
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
2269
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, pointBuf);
|
|
2270
|
+
gl.enableVertexAttribArray(1);
|
|
2271
|
+
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 8, 0);
|
|
2272
|
+
gl.vertexAttribDivisor(1, 1);
|
|
2273
|
+
gl.enableVertexAttribArray(2);
|
|
2274
|
+
gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 8, 8);
|
|
2275
|
+
gl.vertexAttribDivisor(2, 1);
|
|
2276
|
+
gl.bindVertexArray(null);
|
|
2277
|
+
}
|
|
2278
|
+
// Join instances read three consecutive points (prev, p0, next) from the same
|
|
2279
|
+
// buffer via overlapping attribute offsets; instance i covers vertex i+1.
|
|
2280
|
+
configureJoinVao(vao, pointBuf) {
|
|
2281
|
+
const gl = this.gl;
|
|
2282
|
+
gl.bindVertexArray(vao);
|
|
2283
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, pointBuf);
|
|
2284
|
+
for (let loc = 0; loc < 3; loc++) {
|
|
2285
|
+
gl.enableVertexAttribArray(loc);
|
|
2286
|
+
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 8, loc * 8);
|
|
2287
|
+
gl.vertexAttribDivisor(loc, 1);
|
|
2288
|
+
}
|
|
2289
|
+
gl.bindVertexArray(null);
|
|
2290
|
+
}
|
|
2291
|
+
bounds() {
|
|
2292
|
+
if (this.count === 0) return null;
|
|
2293
|
+
return { x: this.xBounds, y: this.yBounds };
|
|
2294
|
+
}
|
|
2295
|
+
nearestByX(x) {
|
|
2296
|
+
if (this.count === 0) return null;
|
|
2297
|
+
let best = 0, bestDist = Infinity;
|
|
2298
|
+
for (let i = 0; i < this.count; i++) {
|
|
2299
|
+
const d = Math.abs(this.xs[i] - x);
|
|
2300
|
+
if (d < bestDist) {
|
|
2301
|
+
bestDist = d;
|
|
2302
|
+
best = i;
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
return { x: this.xs[best], y: this.ys[best], index: best };
|
|
2306
|
+
}
|
|
2307
|
+
/** Replace the series data and re-upload the GPU buffer (for streaming). */
|
|
2308
|
+
setData(x, y) {
|
|
2309
|
+
let n = Math.min(x.length, y.length);
|
|
2310
|
+
let xs = x, ys = y;
|
|
2311
|
+
if (this.step && n >= 2) {
|
|
2312
|
+
const e = stepExpand(x, y, n, this.step);
|
|
2313
|
+
xs = e.xs;
|
|
2314
|
+
ys = e.ys;
|
|
2315
|
+
n = e.xs.length;
|
|
2316
|
+
}
|
|
2317
|
+
this.xs = new Float64Array(n);
|
|
2318
|
+
this.ys = new Float64Array(n);
|
|
2319
|
+
this.xRef = n > 0 ? xs[0] : 0;
|
|
2320
|
+
this.yRef = n > 0 ? ys[0] : 0;
|
|
2321
|
+
const data = new Float32Array(n * 2);
|
|
2322
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity, mono = true;
|
|
2323
|
+
for (let i = 0; i < n; i++) {
|
|
2324
|
+
const vx = xs[i], vy = ys[i];
|
|
2325
|
+
this.xs[i] = vx;
|
|
2326
|
+
this.ys[i] = vy;
|
|
2327
|
+
data[i * 2] = vx - this.xRef;
|
|
2328
|
+
data[i * 2 + 1] = vy - this.yRef;
|
|
2329
|
+
if (i > 0 && vx < xs[i - 1]) mono = false;
|
|
2330
|
+
if (vx < minX) minX = vx;
|
|
2331
|
+
if (vx > maxX) maxX = vx;
|
|
2332
|
+
if (vy < minY) minY = vy;
|
|
2333
|
+
if (vy > maxY) maxY = vy;
|
|
2334
|
+
}
|
|
2335
|
+
this.xBounds = [minX, maxX];
|
|
2336
|
+
this.yBounds = [minY, maxY];
|
|
2337
|
+
this.count = n;
|
|
2338
|
+
this.monotonic = mono;
|
|
2339
|
+
this.decKey = "";
|
|
2340
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.posBuf);
|
|
2341
|
+
this.gl.bufferData(this.gl.ARRAY_BUFFER, data, this.gl.DYNAMIC_DRAW);
|
|
2342
|
+
this.syncGpu(data, n);
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
* Rebuild the min/max-decimated buffer for the visible x-window if it changed.
|
|
2346
|
+
* Returns the segment count to draw from `decVao`, or null to draw everything.
|
|
2347
|
+
*/
|
|
2348
|
+
decimate(x, cols) {
|
|
2349
|
+
if (!this.decimateOn || !this.monotonic || this.count < 4 * cols) return null;
|
|
2350
|
+
const target = Math.max(2, cols * 2);
|
|
2351
|
+
let i0 = upperBound(this.xs, x.lo) - 1;
|
|
2352
|
+
let i1 = upperBound(this.xs, x.hi);
|
|
2353
|
+
i0 = Math.max(0, i0);
|
|
2354
|
+
i1 = Math.min(this.count - 1, i1);
|
|
2355
|
+
const visN = i1 - i0;
|
|
2356
|
+
if (visN <= target * 1.5) return null;
|
|
2357
|
+
const key = `${i0}:${i1}:${target}`;
|
|
2358
|
+
if (key === this.decKey) return this.decSegments;
|
|
2359
|
+
if (this.gpuDec) {
|
|
2360
|
+
const outCount = this.gpuDec.run(i0, i1, cols, this.decBuf);
|
|
2361
|
+
if (outCount != null) {
|
|
2362
|
+
this.decKey = key;
|
|
2363
|
+
this.decSegments = outCount - 1;
|
|
2364
|
+
return this.decSegments;
|
|
2365
|
+
}
|
|
2366
|
+
this.disposeGpu();
|
|
2367
|
+
}
|
|
2368
|
+
this.decKey = key;
|
|
2369
|
+
const indices = decimateIndices(this.ys, i0, i1, cols);
|
|
2370
|
+
const out = new Float32Array(indices.length * 2);
|
|
2371
|
+
for (let k = 0; k < indices.length; k++) {
|
|
2372
|
+
const i = indices[k];
|
|
2373
|
+
out[k * 2] = this.xs[i] - this.xRef;
|
|
2374
|
+
out[k * 2 + 1] = this.ys[i] - this.yRef;
|
|
2375
|
+
}
|
|
2376
|
+
const gl = this.gl;
|
|
2377
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this.decBuf);
|
|
2378
|
+
gl.bufferData(gl.ARRAY_BUFFER, out, gl.DYNAMIC_DRAW);
|
|
2379
|
+
this.decSegments = indices.length - 1;
|
|
2380
|
+
return this.decSegments;
|
|
2381
|
+
}
|
|
2382
|
+
draw(state) {
|
|
2383
|
+
if (this.count < 2) return;
|
|
2384
|
+
const gl = state.gl;
|
|
2385
|
+
const decSegs = this.decimate(state.x, Math.max(1, Math.round(state.pixelWidth)));
|
|
2386
|
+
const decimated = decSegs != null;
|
|
2387
|
+
const vao = decimated ? this.decVao : this.fullVao;
|
|
2388
|
+
const segments = decimated ? decSegs : this.count - 1;
|
|
2389
|
+
if (segments < 1) return;
|
|
2390
|
+
const points = segments + 1;
|
|
2391
|
+
gl.useProgram(this.program);
|
|
2392
|
+
setTransformUniforms(gl, this.uniforms, state.x, state.y, this.xRef, this.yRef);
|
|
2393
|
+
gl.uniform4f(this.uniforms.uColor, this.color[0], this.color[1], this.color[2], this.color[3]);
|
|
2394
|
+
gl.uniform2f(this.uniforms.uResolution, state.pixelWidth, state.pixelHeight);
|
|
2395
|
+
gl.uniform1f(this.uniforms.uWidth, this.width * state.dpr);
|
|
2396
|
+
gl.uniform1f(this.uniforms.uRound, this.round ? 1 : 0);
|
|
2397
|
+
gl.bindVertexArray(vao);
|
|
2398
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, segments);
|
|
2399
|
+
gl.bindVertexArray(null);
|
|
2400
|
+
if ((this.joinStyle === "miter" || this.joinStyle === "bevel") && points >= 3) {
|
|
2401
|
+
gl.useProgram(this.joinProgram);
|
|
2402
|
+
setTransformUniforms(gl, this.joinUniforms, state.x, state.y, this.xRef, this.yRef);
|
|
2403
|
+
gl.uniform4f(this.joinUniforms.uColor, this.color[0], this.color[1], this.color[2], this.color[3]);
|
|
2404
|
+
gl.uniform2f(this.joinUniforms.uResolution, state.pixelWidth, state.pixelHeight);
|
|
2405
|
+
gl.uniform1f(this.joinUniforms.uWidth, this.width * state.dpr);
|
|
2406
|
+
gl.uniform1f(this.joinUniforms.uMiter, this.joinStyle === "miter" ? 1 : 0);
|
|
2407
|
+
gl.uniform1f(this.joinUniforms.uMiterLimit, this.miterLimit);
|
|
2408
|
+
gl.bindVertexArray(decimated ? this.joinDecVao : this.joinFullVao);
|
|
2409
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, points - 2);
|
|
2410
|
+
gl.bindVertexArray(null);
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
dispose() {
|
|
2414
|
+
const gl = this.gl;
|
|
2415
|
+
this.disposeGpu();
|
|
2416
|
+
gl.deleteVertexArray(this.fullVao);
|
|
2417
|
+
gl.deleteVertexArray(this.decVao);
|
|
2418
|
+
gl.deleteVertexArray(this.joinFullVao);
|
|
2419
|
+
gl.deleteVertexArray(this.joinDecVao);
|
|
2420
|
+
gl.deleteBuffer(this.cornerBuf);
|
|
2421
|
+
gl.deleteBuffer(this.posBuf);
|
|
2422
|
+
gl.deleteBuffer(this.decBuf);
|
|
2423
|
+
}
|
|
2424
|
+
};
|
|
2425
|
+
|
|
2426
|
+
// src/layers/quiver.ts
|
|
2427
|
+
var SHAFT_VERT = (
|
|
2428
|
+
/* glsl */
|
|
2429
|
+
`#version 300 es
|
|
2430
|
+
precision highp float;
|
|
2431
|
+
layout(location = 0) in vec2 aCorner; // (along 0..1, side -1..1)
|
|
2432
|
+
layout(location = 1) in vec4 aArrow; // (bx,by,tx,ty) offset data space
|
|
2433
|
+
layout(location = 2) in vec4 aColor;
|
|
2434
|
+
uniform vec2 uResolution;
|
|
2435
|
+
uniform float uWidth;
|
|
2436
|
+
${TRANSFORM_GLSL}
|
|
2437
|
+
out vec4 vColor;
|
|
2438
|
+
void main() {
|
|
2439
|
+
vec2 s0 = (dataToClip(aArrow.xy) * 0.5 + 0.5) * uResolution;
|
|
2440
|
+
vec2 s1 = (dataToClip(aArrow.zw) * 0.5 + 0.5) * uResolution;
|
|
2441
|
+
vec2 d = s1 - s0;
|
|
2442
|
+
float len = length(d);
|
|
2443
|
+
vec2 dir = len > 1e-6 ? d / len : vec2(1.0, 0.0);
|
|
2444
|
+
vec2 nrm = vec2(-dir.y, dir.x);
|
|
2445
|
+
vec2 pos = mix(s0, s1, aCorner.x) + nrm * (aCorner.y * uWidth * 0.5);
|
|
2446
|
+
vColor = aColor;
|
|
2447
|
+
gl_Position = vec4((pos / uResolution) * 2.0 - 1.0, 0.0, 1.0);
|
|
2448
|
+
}`
|
|
2449
|
+
);
|
|
2450
|
+
var HEAD_VERT = (
|
|
2451
|
+
/* glsl */
|
|
2452
|
+
`#version 300 es
|
|
2453
|
+
precision highp float;
|
|
2454
|
+
layout(location = 1) in vec4 aArrow; // (bx,by,tx,ty) offset data space
|
|
2455
|
+
layout(location = 2) in vec4 aColor;
|
|
2456
|
+
uniform vec2 uResolution;
|
|
2457
|
+
uniform float uHeadSize;
|
|
2458
|
+
${TRANSFORM_GLSL}
|
|
2459
|
+
out vec4 vColor;
|
|
2460
|
+
void main() {
|
|
2461
|
+
vec2 s0 = (dataToClip(aArrow.xy) * 0.5 + 0.5) * uResolution;
|
|
2462
|
+
vec2 s1 = (dataToClip(aArrow.zw) * 0.5 + 0.5) * uResolution;
|
|
2463
|
+
vec2 d = s1 - s0;
|
|
2464
|
+
float len = length(d);
|
|
2465
|
+
vec2 dir = len > 1e-6 ? d / len : vec2(1.0, 0.0);
|
|
2466
|
+
vec2 nrm = vec2(-dir.y, dir.x);
|
|
2467
|
+
float w = uHeadSize * 0.6;
|
|
2468
|
+
vec2 pos;
|
|
2469
|
+
if (gl_VertexID == 0) pos = s1; // tip
|
|
2470
|
+
else if (gl_VertexID == 1) pos = s1 - dir * uHeadSize + nrm * w; // back-left
|
|
2471
|
+
else pos = s1 - dir * uHeadSize - nrm * w; // back-right
|
|
2472
|
+
vColor = aColor;
|
|
2473
|
+
gl_Position = vec4((pos / uResolution) * 2.0 - 1.0, 0.0, 1.0);
|
|
2474
|
+
}`
|
|
2475
|
+
);
|
|
2476
|
+
var FRAG9 = (
|
|
2477
|
+
/* glsl */
|
|
2478
|
+
`#version 300 es
|
|
2479
|
+
precision highp float;
|
|
2480
|
+
in vec4 vColor;
|
|
2481
|
+
uniform vec4 uColor;
|
|
2482
|
+
uniform float uUseVertexColor;
|
|
2483
|
+
out vec4 outColor;
|
|
2484
|
+
void main() {
|
|
2485
|
+
vec4 c = uUseVertexColor > 0.5 ? vColor : uColor;
|
|
2486
|
+
outColor = vec4(c.rgb * c.a, c.a);
|
|
2487
|
+
}`
|
|
2488
|
+
);
|
|
2489
|
+
var SEG_CORNERS3 = new Float32Array([0, -1, 1, -1, 1, 1, 0, -1, 1, 1, 0, 1]);
|
|
2490
|
+
var cache3 = /* @__PURE__ */ new WeakMap();
|
|
2491
|
+
function programs3(gl) {
|
|
2492
|
+
let p = cache3.get(gl);
|
|
2493
|
+
if (!p) {
|
|
2494
|
+
p = { shaft: createProgram(gl, SHAFT_VERT, FRAG9), head: createProgram(gl, HEAD_VERT, FRAG9) };
|
|
2495
|
+
cache3.set(gl, p);
|
|
2496
|
+
}
|
|
2497
|
+
return p;
|
|
2498
|
+
}
|
|
2499
|
+
var counter10 = 0;
|
|
2500
|
+
var QuiverLayer = class {
|
|
2501
|
+
constructor(gl, opts) {
|
|
2502
|
+
this.buffers = [];
|
|
2503
|
+
this.xRef = 0;
|
|
2504
|
+
this.yRef = 0;
|
|
2505
|
+
this.xBounds = [0, 0];
|
|
2506
|
+
this.yBounds = [0, 0];
|
|
2507
|
+
this.id = `quiver-${counter10++}`;
|
|
2508
|
+
this.gl = gl;
|
|
2509
|
+
this.progs = programs3(gl);
|
|
2510
|
+
const colorInput = opts.color ?? "#3b82f6";
|
|
2511
|
+
this.color = Array.isArray(colorInput) ? colorInput : parseColor(colorInput);
|
|
2512
|
+
this.colorCss = typeof colorInput === "string" ? colorInput : toColorCss(this.color);
|
|
2513
|
+
this.name = opts.name ?? this.id;
|
|
2514
|
+
this.yAxis = opts.yAxis ?? "y";
|
|
2515
|
+
this.width = opts.width ?? 1.5;
|
|
2516
|
+
this.headSize = opts.headSize ?? 9;
|
|
2517
|
+
this.useVertexColor = opts.colorBy != null;
|
|
2518
|
+
const n = Math.min(opts.x.length, opts.y.length, opts.u.length, opts.v.length);
|
|
2519
|
+
this.count = n;
|
|
2520
|
+
this.xRef = n > 0 ? opts.x[0] : 0;
|
|
2521
|
+
this.yRef = n > 0 ? opts.y[0] : 0;
|
|
2522
|
+
let maxMag = 0;
|
|
2523
|
+
for (let i = 0; i < n; i++) maxMag = Math.max(maxMag, Math.hypot(opts.u[i], opts.v[i]));
|
|
2524
|
+
let scale = opts.scale;
|
|
2525
|
+
if (scale == null) {
|
|
2526
|
+
let minX2 = Infinity, maxX2 = -Infinity, minY2 = Infinity, maxY2 = -Infinity;
|
|
2527
|
+
for (let i = 0; i < n; i++) {
|
|
2528
|
+
minX2 = Math.min(minX2, opts.x[i]);
|
|
2529
|
+
maxX2 = Math.max(maxX2, opts.x[i]);
|
|
2530
|
+
minY2 = Math.min(minY2, opts.y[i]);
|
|
2531
|
+
maxY2 = Math.max(maxY2, opts.y[i]);
|
|
2532
|
+
}
|
|
2533
|
+
const diag = Math.hypot(maxX2 - minX2, maxY2 - minY2) || 1;
|
|
2534
|
+
const cell = diag / Math.max(1, Math.sqrt(n));
|
|
2535
|
+
scale = maxMag > 0 ? 0.9 * cell / maxMag : 1;
|
|
2536
|
+
}
|
|
2537
|
+
const arrows = new Float32Array(n * 4);
|
|
2538
|
+
const colors = new Float32Array(n * 4);
|
|
2539
|
+
const cmap = colormap(opts.colorBy?.colormap ?? "viridis");
|
|
2540
|
+
const vals = opts.colorBy?.values;
|
|
2541
|
+
let lo = opts.colorBy?.domain?.[0] ?? Infinity;
|
|
2542
|
+
let hi = opts.colorBy?.domain?.[1] ?? -Infinity;
|
|
2543
|
+
if (this.useVertexColor && !opts.colorBy?.domain) {
|
|
2544
|
+
for (let i = 0; i < n; i++) {
|
|
2545
|
+
const v = vals ? vals[i] : Math.hypot(opts.u[i], opts.v[i]);
|
|
2546
|
+
lo = Math.min(lo, v);
|
|
2547
|
+
hi = Math.max(hi, v);
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
const span = hi - lo || 1;
|
|
2551
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
2552
|
+
for (let i = 0; i < n; i++) {
|
|
2553
|
+
const x = opts.x[i], y = opts.y[i];
|
|
2554
|
+
const tx = x + opts.u[i] * scale, ty = y + opts.v[i] * scale;
|
|
2555
|
+
arrows[i * 4] = x - this.xRef;
|
|
2556
|
+
arrows[i * 4 + 1] = y - this.yRef;
|
|
2557
|
+
arrows[i * 4 + 2] = tx - this.xRef;
|
|
2558
|
+
arrows[i * 4 + 3] = ty - this.yRef;
|
|
2559
|
+
if (this.useVertexColor) {
|
|
2560
|
+
const v = vals ? vals[i] : Math.hypot(opts.u[i], opts.v[i]);
|
|
2561
|
+
const [r, g, b] = cmap((v - lo) / span);
|
|
2562
|
+
colors[i * 4] = r;
|
|
2563
|
+
colors[i * 4 + 1] = g;
|
|
2564
|
+
colors[i * 4 + 2] = b;
|
|
2565
|
+
colors[i * 4 + 3] = 1;
|
|
2566
|
+
}
|
|
2567
|
+
minX = Math.min(minX, x, tx);
|
|
2568
|
+
maxX = Math.max(maxX, x, tx);
|
|
2569
|
+
minY = Math.min(minY, y, ty);
|
|
2570
|
+
maxY = Math.max(maxY, y, ty);
|
|
2571
|
+
}
|
|
2572
|
+
this.xBounds = [minX, maxX];
|
|
2573
|
+
this.yBounds = [minY, maxY];
|
|
2574
|
+
const cornerBuf = gl.createBuffer();
|
|
2575
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerBuf);
|
|
2576
|
+
gl.bufferData(gl.ARRAY_BUFFER, SEG_CORNERS3, gl.STATIC_DRAW);
|
|
2577
|
+
const arrowBuf = gl.createBuffer();
|
|
2578
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, arrowBuf);
|
|
2579
|
+
gl.bufferData(gl.ARRAY_BUFFER, arrows, gl.STATIC_DRAW);
|
|
2580
|
+
const colorBuf = gl.createBuffer();
|
|
2581
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuf);
|
|
2582
|
+
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
|
|
2583
|
+
this.buffers = [cornerBuf, arrowBuf, colorBuf];
|
|
2584
|
+
this.shaftVao = gl.createVertexArray();
|
|
2585
|
+
gl.bindVertexArray(this.shaftVao);
|
|
2586
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerBuf);
|
|
2587
|
+
gl.enableVertexAttribArray(0);
|
|
2588
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
2589
|
+
this.bindInstanceAttribs(arrowBuf, colorBuf);
|
|
2590
|
+
gl.bindVertexArray(null);
|
|
2591
|
+
this.headVao = gl.createVertexArray();
|
|
2592
|
+
gl.bindVertexArray(this.headVao);
|
|
2593
|
+
this.bindInstanceAttribs(arrowBuf, colorBuf);
|
|
2594
|
+
gl.bindVertexArray(null);
|
|
2595
|
+
this.uShaft = uniformLocations(gl, this.progs.shaft, [
|
|
2596
|
+
...TRANSFORM_UNIFORMS,
|
|
2597
|
+
"uColor",
|
|
2598
|
+
"uResolution",
|
|
2599
|
+
"uWidth",
|
|
2600
|
+
"uUseVertexColor"
|
|
2601
|
+
]);
|
|
2602
|
+
this.uHead = uniformLocations(gl, this.progs.head, [
|
|
2603
|
+
...TRANSFORM_UNIFORMS,
|
|
2604
|
+
"uColor",
|
|
2605
|
+
"uResolution",
|
|
2606
|
+
"uHeadSize",
|
|
2607
|
+
"uUseVertexColor"
|
|
1495
2608
|
]);
|
|
1496
2609
|
}
|
|
1497
|
-
|
|
2610
|
+
bindInstanceAttribs(arrowBuf, colorBuf) {
|
|
1498
2611
|
const gl = this.gl;
|
|
1499
|
-
gl.
|
|
1500
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, this.cornerBuf);
|
|
1501
|
-
gl.enableVertexAttribArray(0);
|
|
1502
|
-
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
1503
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, pointBuf);
|
|
2612
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, arrowBuf);
|
|
1504
2613
|
gl.enableVertexAttribArray(1);
|
|
1505
|
-
gl.vertexAttribPointer(1,
|
|
2614
|
+
gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0);
|
|
1506
2615
|
gl.vertexAttribDivisor(1, 1);
|
|
2616
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuf);
|
|
1507
2617
|
gl.enableVertexAttribArray(2);
|
|
1508
|
-
gl.vertexAttribPointer(2,
|
|
2618
|
+
gl.vertexAttribPointer(2, 4, gl.FLOAT, false, 0, 0);
|
|
1509
2619
|
gl.vertexAttribDivisor(2, 1);
|
|
1510
|
-
gl.bindVertexArray(null);
|
|
1511
2620
|
}
|
|
1512
2621
|
bounds() {
|
|
1513
2622
|
if (this.count === 0) return null;
|
|
1514
2623
|
return { x: this.xBounds, y: this.yBounds };
|
|
1515
2624
|
}
|
|
1516
|
-
nearestByX(x) {
|
|
1517
|
-
if (this.count === 0) return null;
|
|
1518
|
-
let best = 0, bestDist = Infinity;
|
|
1519
|
-
for (let i = 0; i < this.count; i++) {
|
|
1520
|
-
const d = Math.abs(this.xs[i] - x);
|
|
1521
|
-
if (d < bestDist) {
|
|
1522
|
-
bestDist = d;
|
|
1523
|
-
best = i;
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
return { x: this.xs[best], y: this.ys[best], index: best };
|
|
1527
|
-
}
|
|
1528
|
-
/** Replace the series data and re-upload the GPU buffer (for streaming). */
|
|
1529
|
-
setData(x, y) {
|
|
1530
|
-
let n = Math.min(x.length, y.length);
|
|
1531
|
-
let xs = x, ys = y;
|
|
1532
|
-
if (this.step && n >= 2) {
|
|
1533
|
-
const e = stepExpand(x, y, n, this.step);
|
|
1534
|
-
xs = e.xs;
|
|
1535
|
-
ys = e.ys;
|
|
1536
|
-
n = e.xs.length;
|
|
1537
|
-
}
|
|
1538
|
-
this.xs = new Float64Array(n);
|
|
1539
|
-
this.ys = new Float64Array(n);
|
|
1540
|
-
this.xRef = n > 0 ? xs[0] : 0;
|
|
1541
|
-
this.yRef = n > 0 ? ys[0] : 0;
|
|
1542
|
-
const data = new Float32Array(n * 2);
|
|
1543
|
-
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity, mono = true;
|
|
1544
|
-
for (let i = 0; i < n; i++) {
|
|
1545
|
-
const vx = xs[i], vy = ys[i];
|
|
1546
|
-
this.xs[i] = vx;
|
|
1547
|
-
this.ys[i] = vy;
|
|
1548
|
-
data[i * 2] = vx - this.xRef;
|
|
1549
|
-
data[i * 2 + 1] = vy - this.yRef;
|
|
1550
|
-
if (i > 0 && vx < xs[i - 1]) mono = false;
|
|
1551
|
-
if (vx < minX) minX = vx;
|
|
1552
|
-
if (vx > maxX) maxX = vx;
|
|
1553
|
-
if (vy < minY) minY = vy;
|
|
1554
|
-
if (vy > maxY) maxY = vy;
|
|
1555
|
-
}
|
|
1556
|
-
this.xBounds = [minX, maxX];
|
|
1557
|
-
this.yBounds = [minY, maxY];
|
|
1558
|
-
this.count = n;
|
|
1559
|
-
this.monotonic = mono;
|
|
1560
|
-
this.decKey = "";
|
|
1561
|
-
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.posBuf);
|
|
1562
|
-
this.gl.bufferData(this.gl.ARRAY_BUFFER, data, this.gl.DYNAMIC_DRAW);
|
|
1563
|
-
}
|
|
1564
|
-
/**
|
|
1565
|
-
* Rebuild the min/max-decimated buffer for the visible x-window if it changed.
|
|
1566
|
-
* Returns the segment count to draw from `decVao`, or null to draw everything.
|
|
1567
|
-
*/
|
|
1568
|
-
decimate(x, cols) {
|
|
1569
|
-
if (!this.decimateOn || !this.monotonic || this.count < 4 * cols) return null;
|
|
1570
|
-
const target = Math.max(2, cols * 2);
|
|
1571
|
-
let i0 = upperBound(this.xs, x.lo) - 1;
|
|
1572
|
-
let i1 = upperBound(this.xs, x.hi);
|
|
1573
|
-
i0 = Math.max(0, i0);
|
|
1574
|
-
i1 = Math.min(this.count - 1, i1);
|
|
1575
|
-
const visN = i1 - i0;
|
|
1576
|
-
if (visN <= target * 1.5) return null;
|
|
1577
|
-
const key = `${i0}:${i1}:${target}`;
|
|
1578
|
-
if (key === this.decKey) return this.decSegments;
|
|
1579
|
-
this.decKey = key;
|
|
1580
|
-
const out = [];
|
|
1581
|
-
const push = (i) => out.push(this.xs[i] - this.xRef, this.ys[i] - this.yRef);
|
|
1582
|
-
push(i0);
|
|
1583
|
-
for (let b = 0; b < cols; b++) {
|
|
1584
|
-
const lo = i0 + Math.floor(visN * b / cols);
|
|
1585
|
-
const hi = i0 + Math.floor(visN * (b + 1) / cols);
|
|
1586
|
-
if (hi <= lo) continue;
|
|
1587
|
-
let iMin = lo, iMax = lo;
|
|
1588
|
-
for (let i = lo; i < hi; i++) {
|
|
1589
|
-
if (this.ys[i] < this.ys[iMin]) iMin = i;
|
|
1590
|
-
if (this.ys[i] > this.ys[iMax]) iMax = i;
|
|
1591
|
-
}
|
|
1592
|
-
if (iMin < iMax) {
|
|
1593
|
-
push(iMin);
|
|
1594
|
-
push(iMax);
|
|
1595
|
-
} else {
|
|
1596
|
-
push(iMax);
|
|
1597
|
-
push(iMin);
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
push(i1);
|
|
1601
|
-
const gl = this.gl;
|
|
1602
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, this.decBuf);
|
|
1603
|
-
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(out), gl.DYNAMIC_DRAW);
|
|
1604
|
-
this.decSegments = out.length / 2 - 1;
|
|
1605
|
-
return this.decSegments;
|
|
1606
|
-
}
|
|
1607
2625
|
draw(state) {
|
|
1608
|
-
if (this.count
|
|
2626
|
+
if (this.count === 0) return;
|
|
1609
2627
|
const gl = state.gl;
|
|
1610
|
-
const
|
|
1611
|
-
const
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
gl.
|
|
1615
|
-
|
|
1616
|
-
gl.
|
|
1617
|
-
gl.
|
|
1618
|
-
gl.
|
|
1619
|
-
gl.
|
|
1620
|
-
gl.bindVertexArray(
|
|
1621
|
-
gl.
|
|
2628
|
+
const [r, g, b, a] = this.color;
|
|
2629
|
+
const uvc = this.useVertexColor ? 1 : 0;
|
|
2630
|
+
gl.useProgram(this.progs.shaft);
|
|
2631
|
+
setTransformUniforms(gl, this.uShaft, state.x, state.y, this.xRef, this.yRef);
|
|
2632
|
+
gl.uniform4f(this.uShaft.uColor, r, g, b, a);
|
|
2633
|
+
gl.uniform2f(this.uShaft.uResolution, state.pixelWidth, state.pixelHeight);
|
|
2634
|
+
gl.uniform1f(this.uShaft.uWidth, this.width * state.dpr);
|
|
2635
|
+
gl.uniform1f(this.uShaft.uUseVertexColor, uvc);
|
|
2636
|
+
gl.bindVertexArray(this.shaftVao);
|
|
2637
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, this.count);
|
|
2638
|
+
gl.bindVertexArray(null);
|
|
2639
|
+
gl.useProgram(this.progs.head);
|
|
2640
|
+
setTransformUniforms(gl, this.uHead, state.x, state.y, this.xRef, this.yRef);
|
|
2641
|
+
gl.uniform4f(this.uHead.uColor, r, g, b, a);
|
|
2642
|
+
gl.uniform2f(this.uHead.uResolution, state.pixelWidth, state.pixelHeight);
|
|
2643
|
+
gl.uniform1f(this.uHead.uHeadSize, this.headSize * state.dpr);
|
|
2644
|
+
gl.uniform1f(this.uHead.uUseVertexColor, uvc);
|
|
2645
|
+
gl.bindVertexArray(this.headVao);
|
|
2646
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 3, this.count);
|
|
1622
2647
|
gl.bindVertexArray(null);
|
|
1623
2648
|
}
|
|
1624
2649
|
dispose() {
|
|
1625
|
-
|
|
1626
|
-
gl.deleteVertexArray(this.
|
|
1627
|
-
|
|
1628
|
-
gl.deleteBuffer(this.cornerBuf);
|
|
1629
|
-
gl.deleteBuffer(this.posBuf);
|
|
1630
|
-
gl.deleteBuffer(this.decBuf);
|
|
2650
|
+
this.gl.deleteVertexArray(this.shaftVao);
|
|
2651
|
+
this.gl.deleteVertexArray(this.headVao);
|
|
2652
|
+
for (const b of this.buffers) this.gl.deleteBuffer(b);
|
|
1631
2653
|
}
|
|
1632
2654
|
};
|
|
1633
2655
|
|
|
@@ -1652,7 +2674,7 @@ void main() {
|
|
|
1652
2674
|
gl_Position = vec4((pos / uResolution) * 2.0 - 1.0, 0.0, 1.0);
|
|
1653
2675
|
}`
|
|
1654
2676
|
);
|
|
1655
|
-
var
|
|
2677
|
+
var FRAG10 = (
|
|
1656
2678
|
/* glsl */
|
|
1657
2679
|
`#version 300 es
|
|
1658
2680
|
precision highp float;
|
|
@@ -1670,16 +2692,16 @@ void main() {
|
|
|
1670
2692
|
}`
|
|
1671
2693
|
);
|
|
1672
2694
|
var CORNERS3 = new Float32Array([-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]);
|
|
1673
|
-
var
|
|
2695
|
+
var programCache9 = /* @__PURE__ */ new WeakMap();
|
|
1674
2696
|
function getProgram8(gl) {
|
|
1675
|
-
let p =
|
|
2697
|
+
let p = programCache9.get(gl);
|
|
1676
2698
|
if (!p) {
|
|
1677
|
-
p = createProgram(gl, VERT8,
|
|
1678
|
-
|
|
2699
|
+
p = createProgram(gl, VERT8, FRAG10);
|
|
2700
|
+
programCache9.set(gl, p);
|
|
1679
2701
|
}
|
|
1680
2702
|
return p;
|
|
1681
2703
|
}
|
|
1682
|
-
var
|
|
2704
|
+
var counter11 = 0;
|
|
1683
2705
|
var ScatterLayer = class {
|
|
1684
2706
|
constructor(gl, opts) {
|
|
1685
2707
|
this.buffers = [];
|
|
@@ -1687,7 +2709,7 @@ var ScatterLayer = class {
|
|
|
1687
2709
|
this.yRef = 0;
|
|
1688
2710
|
this.xBounds = [0, 0];
|
|
1689
2711
|
this.yBounds = [0, 0];
|
|
1690
|
-
this.id = `scatter-${
|
|
2712
|
+
this.id = `scatter-${counter11++}`;
|
|
1691
2713
|
this.gl = gl;
|
|
1692
2714
|
this.program = getProgram8(gl);
|
|
1693
2715
|
this.size = opts.size ?? 5;
|
|
@@ -1832,6 +2854,204 @@ var ScatterLayer = class {
|
|
|
1832
2854
|
}
|
|
1833
2855
|
};
|
|
1834
2856
|
|
|
2857
|
+
// src/layers/stem.ts
|
|
2858
|
+
var STEM_VERT = (
|
|
2859
|
+
/* glsl */
|
|
2860
|
+
`#version 300 es
|
|
2861
|
+
precision highp float;
|
|
2862
|
+
layout(location = 0) in vec2 aCorner; // (along 0..1, side -1..1)
|
|
2863
|
+
layout(location = 1) in vec4 aSeg; // (x0,y0,x1,y1) offset data space
|
|
2864
|
+
uniform vec2 uResolution;
|
|
2865
|
+
uniform float uWidth;
|
|
2866
|
+
${TRANSFORM_GLSL}
|
|
2867
|
+
void main() {
|
|
2868
|
+
vec2 s0 = (dataToClip(aSeg.xy) * 0.5 + 0.5) * uResolution;
|
|
2869
|
+
vec2 s1 = (dataToClip(aSeg.zw) * 0.5 + 0.5) * uResolution;
|
|
2870
|
+
vec2 d = s1 - s0;
|
|
2871
|
+
float len = length(d);
|
|
2872
|
+
vec2 dir = len > 1e-6 ? d / len : vec2(1.0, 0.0);
|
|
2873
|
+
vec2 nrm = vec2(-dir.y, dir.x);
|
|
2874
|
+
vec2 pos = mix(s0, s1, aCorner.x) + nrm * (aCorner.y * uWidth * 0.5);
|
|
2875
|
+
gl_Position = vec4((pos / uResolution) * 2.0 - 1.0, 0.0, 1.0);
|
|
2876
|
+
}`
|
|
2877
|
+
);
|
|
2878
|
+
var MARKER_VERT = (
|
|
2879
|
+
/* glsl */
|
|
2880
|
+
`#version 300 es
|
|
2881
|
+
precision highp float;
|
|
2882
|
+
layout(location = 0) in vec2 aCorner; // unit quad [-1,1]^2
|
|
2883
|
+
layout(location = 1) in vec2 aPos; // tip point (offset data space)
|
|
2884
|
+
uniform vec2 uResolution;
|
|
2885
|
+
uniform float uSize; // radius in device px
|
|
2886
|
+
${TRANSFORM_GLSL}
|
|
2887
|
+
out vec2 vLocal;
|
|
2888
|
+
void main() {
|
|
2889
|
+
vec2 c = (dataToClip(aPos) * 0.5 + 0.5) * uResolution;
|
|
2890
|
+
vLocal = aCorner;
|
|
2891
|
+
vec2 pos = c + aCorner * uSize;
|
|
2892
|
+
gl_Position = vec4((pos / uResolution) * 2.0 - 1.0, 0.0, 1.0);
|
|
2893
|
+
}`
|
|
2894
|
+
);
|
|
2895
|
+
var SOLID_FRAG2 = (
|
|
2896
|
+
/* glsl */
|
|
2897
|
+
`#version 300 es
|
|
2898
|
+
precision highp float;
|
|
2899
|
+
uniform vec4 uColor;
|
|
2900
|
+
out vec4 outColor;
|
|
2901
|
+
void main() { outColor = vec4(uColor.rgb * uColor.a, uColor.a); }`
|
|
2902
|
+
);
|
|
2903
|
+
var DISC_FRAG = (
|
|
2904
|
+
/* glsl */
|
|
2905
|
+
`#version 300 es
|
|
2906
|
+
precision highp float;
|
|
2907
|
+
in vec2 vLocal;
|
|
2908
|
+
uniform vec4 uColor;
|
|
2909
|
+
out vec4 outColor;
|
|
2910
|
+
void main() {
|
|
2911
|
+
float r = length(vLocal);
|
|
2912
|
+
if (r > 1.0) discard;
|
|
2913
|
+
float alpha = smoothstep(1.0, 1.0 - 0.15, r);
|
|
2914
|
+
outColor = vec4(uColor.rgb * uColor.a * alpha, uColor.a * alpha);
|
|
2915
|
+
}`
|
|
2916
|
+
);
|
|
2917
|
+
var SEG_CORNERS4 = new Float32Array([0, -1, 1, -1, 1, 1, 0, -1, 1, 1, 0, 1]);
|
|
2918
|
+
var QUAD_CORNERS2 = new Float32Array([-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]);
|
|
2919
|
+
var cache4 = /* @__PURE__ */ new WeakMap();
|
|
2920
|
+
function programs4(gl) {
|
|
2921
|
+
let p = cache4.get(gl);
|
|
2922
|
+
if (!p) {
|
|
2923
|
+
p = { stem: createProgram(gl, STEM_VERT, SOLID_FRAG2), marker: createProgram(gl, MARKER_VERT, DISC_FRAG) };
|
|
2924
|
+
cache4.set(gl, p);
|
|
2925
|
+
}
|
|
2926
|
+
return p;
|
|
2927
|
+
}
|
|
2928
|
+
var counter12 = 0;
|
|
2929
|
+
var StemLayer = class {
|
|
2930
|
+
constructor(gl, opts) {
|
|
2931
|
+
this.buffers = [];
|
|
2932
|
+
this.xRef = 0;
|
|
2933
|
+
this.yRef = 0;
|
|
2934
|
+
this.xBounds = [0, 0];
|
|
2935
|
+
this.yBounds = [0, 0];
|
|
2936
|
+
this.id = `stem-${counter12++}`;
|
|
2937
|
+
this.gl = gl;
|
|
2938
|
+
this.progs = programs4(gl);
|
|
2939
|
+
const colorInput = opts.color ?? "#3b82f6";
|
|
2940
|
+
this.color = Array.isArray(colorInput) ? colorInput : parseColor(colorInput);
|
|
2941
|
+
this.colorCss = typeof colorInput === "string" ? colorInput : toColorCss(this.color);
|
|
2942
|
+
this.name = opts.name ?? this.id;
|
|
2943
|
+
this.yAxis = opts.yAxis ?? "y";
|
|
2944
|
+
this.width = opts.width ?? 1.5;
|
|
2945
|
+
this.markerSize = opts.markerSize ?? 6;
|
|
2946
|
+
this.baseline = opts.baseline ?? 0;
|
|
2947
|
+
const n = Math.min(opts.x.length, opts.y.length);
|
|
2948
|
+
this.count = n;
|
|
2949
|
+
this.xs = new Float64Array(n);
|
|
2950
|
+
this.ys = new Float64Array(n);
|
|
2951
|
+
this.xRef = n > 0 ? opts.x[0] : 0;
|
|
2952
|
+
this.yRef = n > 0 ? opts.y[0] : 0;
|
|
2953
|
+
const segs = new Float32Array(n * 4);
|
|
2954
|
+
const tips = new Float32Array(n * 2);
|
|
2955
|
+
let minX = Infinity, maxX = -Infinity;
|
|
2956
|
+
let minY = Math.min(this.baseline, Infinity), maxY = Math.max(this.baseline, -Infinity);
|
|
2957
|
+
for (let i = 0; i < n; i++) {
|
|
2958
|
+
const x = opts.x[i], y = opts.y[i];
|
|
2959
|
+
this.xs[i] = x;
|
|
2960
|
+
this.ys[i] = y;
|
|
2961
|
+
segs[i * 4] = x - this.xRef;
|
|
2962
|
+
segs[i * 4 + 1] = this.baseline - this.yRef;
|
|
2963
|
+
segs[i * 4 + 2] = x - this.xRef;
|
|
2964
|
+
segs[i * 4 + 3] = y - this.yRef;
|
|
2965
|
+
tips[i * 2] = x - this.xRef;
|
|
2966
|
+
tips[i * 2 + 1] = y - this.yRef;
|
|
2967
|
+
if (x < minX) minX = x;
|
|
2968
|
+
if (x > maxX) maxX = x;
|
|
2969
|
+
if (y < minY) minY = y;
|
|
2970
|
+
if (y > maxY) maxY = y;
|
|
2971
|
+
}
|
|
2972
|
+
this.xBounds = [minX, maxX];
|
|
2973
|
+
this.yBounds = [minY, maxY];
|
|
2974
|
+
const cornerSeg = gl.createBuffer();
|
|
2975
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerSeg);
|
|
2976
|
+
gl.bufferData(gl.ARRAY_BUFFER, SEG_CORNERS4, gl.STATIC_DRAW);
|
|
2977
|
+
const cornerQuad = gl.createBuffer();
|
|
2978
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerQuad);
|
|
2979
|
+
gl.bufferData(gl.ARRAY_BUFFER, QUAD_CORNERS2, gl.STATIC_DRAW);
|
|
2980
|
+
const segBuf = gl.createBuffer();
|
|
2981
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, segBuf);
|
|
2982
|
+
gl.bufferData(gl.ARRAY_BUFFER, segs, gl.STATIC_DRAW);
|
|
2983
|
+
const tipBuf = gl.createBuffer();
|
|
2984
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, tipBuf);
|
|
2985
|
+
gl.bufferData(gl.ARRAY_BUFFER, tips, gl.STATIC_DRAW);
|
|
2986
|
+
this.buffers = [cornerSeg, cornerQuad, segBuf, tipBuf];
|
|
2987
|
+
this.stemVao = gl.createVertexArray();
|
|
2988
|
+
gl.bindVertexArray(this.stemVao);
|
|
2989
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerSeg);
|
|
2990
|
+
gl.enableVertexAttribArray(0);
|
|
2991
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
2992
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, segBuf);
|
|
2993
|
+
gl.enableVertexAttribArray(1);
|
|
2994
|
+
gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0);
|
|
2995
|
+
gl.vertexAttribDivisor(1, 1);
|
|
2996
|
+
this.markerVao = gl.createVertexArray();
|
|
2997
|
+
gl.bindVertexArray(this.markerVao);
|
|
2998
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, cornerQuad);
|
|
2999
|
+
gl.enableVertexAttribArray(0);
|
|
3000
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
|
3001
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, tipBuf);
|
|
3002
|
+
gl.enableVertexAttribArray(1);
|
|
3003
|
+
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0);
|
|
3004
|
+
gl.vertexAttribDivisor(1, 1);
|
|
3005
|
+
gl.bindVertexArray(null);
|
|
3006
|
+
this.uStem = uniformLocations(gl, this.progs.stem, [...TRANSFORM_UNIFORMS, "uColor", "uResolution", "uWidth"]);
|
|
3007
|
+
this.uMarker = uniformLocations(gl, this.progs.marker, [...TRANSFORM_UNIFORMS, "uColor", "uResolution", "uSize"]);
|
|
3008
|
+
}
|
|
3009
|
+
bounds() {
|
|
3010
|
+
if (this.count === 0) return null;
|
|
3011
|
+
return { x: this.xBounds, y: this.yBounds };
|
|
3012
|
+
}
|
|
3013
|
+
nearestByX(x) {
|
|
3014
|
+
if (this.count === 0) return null;
|
|
3015
|
+
let best = 0, bestDist = Infinity;
|
|
3016
|
+
for (let i = 0; i < this.count; i++) {
|
|
3017
|
+
const d = Math.abs(this.xs[i] - x);
|
|
3018
|
+
if (d < bestDist) {
|
|
3019
|
+
bestDist = d;
|
|
3020
|
+
best = i;
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
return { x: this.xs[best], y: this.ys[best], index: best };
|
|
3024
|
+
}
|
|
3025
|
+
draw(state) {
|
|
3026
|
+
if (this.count === 0) return;
|
|
3027
|
+
const gl = state.gl;
|
|
3028
|
+
const [r, g, b, a] = this.color;
|
|
3029
|
+
gl.useProgram(this.progs.stem);
|
|
3030
|
+
setTransformUniforms(gl, this.uStem, state.x, state.y, this.xRef, this.yRef);
|
|
3031
|
+
gl.uniform4f(this.uStem.uColor, r, g, b, a);
|
|
3032
|
+
gl.uniform2f(this.uStem.uResolution, state.pixelWidth, state.pixelHeight);
|
|
3033
|
+
gl.uniform1f(this.uStem.uWidth, this.width * state.dpr);
|
|
3034
|
+
gl.bindVertexArray(this.stemVao);
|
|
3035
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, this.count);
|
|
3036
|
+
gl.bindVertexArray(null);
|
|
3037
|
+
if (this.markerSize > 0) {
|
|
3038
|
+
gl.useProgram(this.progs.marker);
|
|
3039
|
+
setTransformUniforms(gl, this.uMarker, state.x, state.y, this.xRef, this.yRef);
|
|
3040
|
+
gl.uniform4f(this.uMarker.uColor, r, g, b, a);
|
|
3041
|
+
gl.uniform2f(this.uMarker.uResolution, state.pixelWidth, state.pixelHeight);
|
|
3042
|
+
gl.uniform1f(this.uMarker.uSize, this.markerSize / 2 * state.dpr);
|
|
3043
|
+
gl.bindVertexArray(this.markerVao);
|
|
3044
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, this.count);
|
|
3045
|
+
gl.bindVertexArray(null);
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
dispose() {
|
|
3049
|
+
this.gl.deleteVertexArray(this.stemVao);
|
|
3050
|
+
this.gl.deleteVertexArray(this.markerVao);
|
|
3051
|
+
for (const b of this.buffers) this.gl.deleteBuffer(b);
|
|
3052
|
+
}
|
|
3053
|
+
};
|
|
3054
|
+
|
|
1835
3055
|
// src/render/overlay.ts
|
|
1836
3056
|
function plotRegion(layout) {
|
|
1837
3057
|
const { margin } = layout;
|
|
@@ -2409,6 +3629,18 @@ var Plot = class {
|
|
|
2409
3629
|
addContour(opts) {
|
|
2410
3630
|
return this.register(new ContourLayer(this.gl, opts));
|
|
2411
3631
|
}
|
|
3632
|
+
addErrorBar(opts) {
|
|
3633
|
+
return this.register(new ErrorBarLayer(this.gl, opts));
|
|
3634
|
+
}
|
|
3635
|
+
addStem(opts) {
|
|
3636
|
+
return this.register(new StemLayer(this.gl, opts));
|
|
3637
|
+
}
|
|
3638
|
+
addQuiver(opts) {
|
|
3639
|
+
return this.register(new QuiverLayer(this.gl, opts));
|
|
3640
|
+
}
|
|
3641
|
+
addCandlestick(opts) {
|
|
3642
|
+
return this.register(new CandlestickLayer(this.gl, opts));
|
|
3643
|
+
}
|
|
2412
3644
|
/** Compute an STFT of `signal` and render it as a heatmap (time × frequency). */
|
|
2413
3645
|
addHeatmapSpectrogram(signal, opts = {}) {
|
|
2414
3646
|
const s = spectrogram(signal, opts);
|
|
@@ -2746,18 +3978,19 @@ var Plot = class {
|
|
|
2746
3978
|
}
|
|
2747
3979
|
return { type: "plot" };
|
|
2748
3980
|
}
|
|
3981
|
+
// Pan/zoom shift the domain in the scale's *transformed* space (via invert),
|
|
3982
|
+
// not raw data space — so a log axis stays positive instead of crossing zero
|
|
3983
|
+
// into NaN. For linear/time scales this is identical to the old arithmetic.
|
|
2749
3984
|
panX(dxPx, region) {
|
|
2750
|
-
const
|
|
2751
|
-
this.scaleX.domain = [this.scaleX.
|
|
3985
|
+
const f = dxPx / region.width;
|
|
3986
|
+
this.scaleX.domain = [this.scaleX.invert(-f), this.scaleX.invert(1 - f)];
|
|
2752
3987
|
this.autoX = false;
|
|
2753
3988
|
}
|
|
2754
3989
|
panY(id, dyPx, region) {
|
|
2755
|
-
const
|
|
3990
|
+
const f = dyPx / region.height;
|
|
2756
3991
|
for (const ya of this.yAxes.values()) {
|
|
2757
3992
|
if (id && ya.id !== id) continue;
|
|
2758
|
-
|
|
2759
|
-
const d = dyFrac * span;
|
|
2760
|
-
ya.scale.domain = [ya.scale.domain[0] + d, ya.scale.domain[1] + d];
|
|
3993
|
+
ya.scale.domain = [ya.scale.invert(f), ya.scale.invert(1 + f)];
|
|
2761
3994
|
ya.auto = false;
|
|
2762
3995
|
}
|
|
2763
3996
|
}
|
|
@@ -2931,16 +4164,14 @@ var Plot = class {
|
|
|
2931
4164
|
zoomAround(nx, ny, factor) {
|
|
2932
4165
|
const lock = this.axisLock();
|
|
2933
4166
|
if (lock.x) {
|
|
2934
|
-
const
|
|
2935
|
-
|
|
2936
|
-
this.scaleX.domain = [cx + (x0 - cx) * factor, cx + (x1 - cx) * factor];
|
|
4167
|
+
const t = nx * (1 - factor);
|
|
4168
|
+
this.scaleX.domain = [this.scaleX.invert(t), this.scaleX.invert(t + factor)];
|
|
2937
4169
|
this.autoX = false;
|
|
2938
4170
|
}
|
|
2939
4171
|
if (lock.y) {
|
|
4172
|
+
const t = ny * (1 - factor);
|
|
2940
4173
|
for (const ya of this.yAxes.values()) {
|
|
2941
|
-
|
|
2942
|
-
const cy = y0 + ny * (y1 - y0);
|
|
2943
|
-
ya.scale.domain = [cy + (y0 - cy) * factor, cy + (y1 - cy) * factor];
|
|
4174
|
+
ya.scale.domain = [ya.scale.invert(t), ya.scale.invert(t + factor)];
|
|
2944
4175
|
ya.auto = false;
|
|
2945
4176
|
}
|
|
2946
4177
|
}
|
|
@@ -3217,7 +4448,7 @@ void main() {
|
|
|
3217
4448
|
gl_PointSize = uSize;
|
|
3218
4449
|
}`
|
|
3219
4450
|
);
|
|
3220
|
-
var
|
|
4451
|
+
var FRAG11 = (
|
|
3221
4452
|
/* glsl */
|
|
3222
4453
|
`#version 300 es
|
|
3223
4454
|
precision highp float;
|
|
@@ -3229,19 +4460,19 @@ void main() {
|
|
|
3229
4460
|
outColor = vec4(vColor, 1.0);
|
|
3230
4461
|
}`
|
|
3231
4462
|
);
|
|
3232
|
-
var
|
|
4463
|
+
var programCache10 = /* @__PURE__ */ new WeakMap();
|
|
3233
4464
|
function getProgram9(gl) {
|
|
3234
|
-
let p =
|
|
4465
|
+
let p = programCache10.get(gl);
|
|
3235
4466
|
if (!p) {
|
|
3236
|
-
p = createProgram(gl, VERT9,
|
|
3237
|
-
|
|
4467
|
+
p = createProgram(gl, VERT9, FRAG11);
|
|
4468
|
+
programCache10.set(gl, p);
|
|
3238
4469
|
}
|
|
3239
4470
|
return p;
|
|
3240
4471
|
}
|
|
3241
|
-
var
|
|
4472
|
+
var counter13 = 0;
|
|
3242
4473
|
var PointCloudLayer = class {
|
|
3243
4474
|
constructor(gl, opts) {
|
|
3244
|
-
this.id = `points3d-${
|
|
4475
|
+
this.id = `points3d-${counter13++}`;
|
|
3245
4476
|
this.gl = gl;
|
|
3246
4477
|
this.program = getProgram9(gl);
|
|
3247
4478
|
this.size = opts.size ?? 4;
|
|
@@ -3326,7 +4557,7 @@ out vec3 vColor;
|
|
|
3326
4557
|
out vec3 vN;
|
|
3327
4558
|
void main() { vColor = aColor; vN = aNormal; gl_Position = uMVP * vec4(aPos, 1.0); }`
|
|
3328
4559
|
);
|
|
3329
|
-
var
|
|
4560
|
+
var FRAG12 = (
|
|
3330
4561
|
/* glsl */
|
|
3331
4562
|
`#version 300 es
|
|
3332
4563
|
precision highp float;
|
|
@@ -3341,21 +4572,21 @@ void main() {
|
|
|
3341
4572
|
outColor = vec4(vColor * shade, 1.0);
|
|
3342
4573
|
}`
|
|
3343
4574
|
);
|
|
3344
|
-
var
|
|
4575
|
+
var programCache11 = /* @__PURE__ */ new WeakMap();
|
|
3345
4576
|
function getProgram10(gl) {
|
|
3346
|
-
let p =
|
|
4577
|
+
let p = programCache11.get(gl);
|
|
3347
4578
|
if (!p) {
|
|
3348
|
-
p = createProgram(gl, VERT10,
|
|
3349
|
-
|
|
4579
|
+
p = createProgram(gl, VERT10, FRAG12);
|
|
4580
|
+
programCache11.set(gl, p);
|
|
3350
4581
|
}
|
|
3351
4582
|
return p;
|
|
3352
4583
|
}
|
|
3353
|
-
var
|
|
4584
|
+
var counter14 = 0;
|
|
3354
4585
|
var SurfaceLayer = class {
|
|
3355
4586
|
constructor(gl, opts) {
|
|
3356
4587
|
this.lightDir = [0.5, 1, 0.35];
|
|
3357
4588
|
this.ambient = 0.35;
|
|
3358
|
-
this.id = `surface-${
|
|
4589
|
+
this.id = `surface-${counter14++}`;
|
|
3359
4590
|
this.gl = gl;
|
|
3360
4591
|
this.program = getProgram10(gl);
|
|
3361
4592
|
const { cols, rows, values } = opts;
|
|
@@ -3825,7 +5056,9 @@ export {
|
|
|
3825
5056
|
Axis,
|
|
3826
5057
|
BarLayer,
|
|
3827
5058
|
BoxLayer,
|
|
5059
|
+
CandlestickLayer,
|
|
3828
5060
|
ContourLayer,
|
|
5061
|
+
ErrorBarLayer,
|
|
3829
5062
|
HeatmapLayer,
|
|
3830
5063
|
HexbinLayer,
|
|
3831
5064
|
LineLayer,
|
|
@@ -3835,7 +5068,9 @@ export {
|
|
|
3835
5068
|
Plot3D,
|
|
3836
5069
|
PointCloudLayer,
|
|
3837
5070
|
PolarPlot,
|
|
5071
|
+
QuiverLayer,
|
|
3838
5072
|
ScatterLayer,
|
|
5073
|
+
StemLayer,
|
|
3839
5074
|
SurfaceLayer,
|
|
3840
5075
|
TimeScale,
|
|
3841
5076
|
autoTicks,
|