@fxi/zartigl 0.10.1 → 0.10.2
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/catalog.js +1 -1
- package/dist/engine.d.ts +266 -0
- package/dist/engine.js +69 -53
- package/dist/geovideo-index-BqOLqzgA.js +1839 -0
- package/dist/{index-Bf2yvpf1.js → index-RrmAk1JY.js} +116 -116
- package/dist/zartigl.d.ts +69 -131
- package/dist/zartigl.js +2324 -2626
- package/package.json +1 -1
- package/dist/geovideo-index-ClNfzbOG.js +0 -1342
|
@@ -0,0 +1,1839 @@
|
|
|
1
|
+
const te = { label: "Thermal", tags: ["sequential", "temperature"], colors: ["#042333", "#2c3395", "#744796", "#b15f82", "#eb7958", "#fbb43d", "#e8fa5b"] }, ie = { label: "Haline", tags: ["sequential", "salinity"], colors: ["#2a186c", "#17568b", "#1a7e8f", "#22a785", "#55c666", "#aadc32", "#fde333"] }, re = { label: "Solar", tags: ["sequential", "irradiance"], colors: ["#330f0b", "#6b2313", "#a83c12", "#d6621a", "#f3951e", "#f7c741", "#f9f871"] }, ne = { label: "Ice", tags: ["sequential"], colors: ["#040d1a", "#123365", "#1c5f9e", "#2b8ece", "#62bde3", "#aaddf2", "#f2faff"] }, se = { label: "Deep", tags: ["sequential", "depth"], colors: ["#fdfecc", "#c8e59b", "#74c8a3", "#3194b0", "#2a5b8e", "#281a5e", "#0c0820"] }, ae = { label: "RdYlBu", tags: ["diverging"], colors: ["#3288bd", "#66c2a5", "#abdda4", "#e6f598", "#fee08b", "#fdae61", "#f46d43", "#d53e4f"] }, oe = { label: "Dense", tags: ["sequential"], colors: ["#e6f1f1", "#92c4ca", "#44929a", "#20627a", "#1a3c6a", "#211a50", "#0d0621"] }, ce = { label: "Algae", tags: ["sequential", "biology", "log"], colors: ["#d7f9d0", "#a2d595", "#64b463", "#129450", "#126e45", "#1a482f", "#122414"] }, de = { label: "Oxygen", tags: ["sequential", "biology", "hypoxia"], colors: ["#09060f", "#5b1028", "#d43d2f", "#f39b3d", "#f3e7b3", "#79cbd1", "#176b9c"] }, le = { label: "Matter", tags: ["sequential", "biology"], colors: ["#fdfecc", "#f2c98a", "#e07c4e", "#bc3754", "#8d1a6d", "#51156b", "#1c0c3f"] }, he = { label: "Viridis", tags: ["sequential", "perceptual"], colors: ["#440154", "#443983", "#31688e", "#21918c", "#35b779", "#90d743", "#fde725"] }, ue = { label: "Plasma", tags: ["sequential", "perceptual"], colors: ["#0d0887", "#5e01a6", "#9c179e", "#cc4778", "#ed7953", "#fdb42f", "#f0f921"] }, fe = { label: "Cyclic", tags: ["cyclic", "direction"], colors: ["#2e22ea", "#b541fb", "#fc93c0", "#f1ee35", "#77c80d", "#458776", "#2c24e9"] }, me = { label: "Tempo", tags: ["sequential"], colors: ["#fff6f4", "#c2d1b9", "#7db390", "#2a937f", "#156d73", "#1c455b", "#151d44"] }, Ee = { label: "Turbid", tags: ["sequential", "biology", "log"], colors: ["#e9f5a0", "#c8c86e", "#a09845", "#786031", "#533324", "#341423", "#150d17"] }, pe = { label: "Speed", tags: ["sequential", "velocity"], colors: ["#fcfea4", "#d5e429", "#94c63e", "#52a657", "#2d8169", "#1d5b6d", "#0d3361"] }, Te = { label: "Amp", tags: ["sequential"], colors: ["#f2f2f2", "#c0b8db", "#9180b6", "#6c4e9e", "#4e2e83", "#32185d", "#110a35"] }, be = { label: "Heat", tags: ["sequential"], colors: ["#fde725", "#fde725", "#f8961e", "#e63946", "#9d0208", "#3a0007"] }, Re = { label: "Balance", tags: ["diverging"], colors: ["#181c43", "#3066af", "#9bbcdd", "#f2f2f2", "#e07d53", "#b13626", "#2c0b0e"] }, ge = { label: "Delta", tags: ["diverging"], colors: ["#0e3f31", "#20877b", "#99d4c1", "#f2f2f2", "#a1b0d4", "#3c5b9e", "#0d1938"] }, we = { label: "Curl", tags: ["diverging"], colors: ["#152237", "#2471b3", "#8dbac4", "#f2f2f2", "#c79f84", "#a63827", "#270b17"] }, _e = { label: "Gray", tags: ["sequential"], colors: ["#000000", "#252525", "#4d4d4d", "#7a7a7a", "#a8a8a8", "#d4d4d4", "#ffffff"] }, K = {
|
|
2
|
+
thermal: te,
|
|
3
|
+
"thermal-alt": { label: "Thermal Alt", tags: ["sequential", "temperature"], colors: ["#176b9c", "#79cbd1", "#f3e7b3", "#f39b3d", "#d43d2f", "#5b1028", "#09060f"] },
|
|
4
|
+
haline: ie,
|
|
5
|
+
solar: re,
|
|
6
|
+
ice: ne,
|
|
7
|
+
deep: se,
|
|
8
|
+
rdylbu: ae,
|
|
9
|
+
dense: oe,
|
|
10
|
+
algae: ce,
|
|
11
|
+
oxygen: de,
|
|
12
|
+
matter: le,
|
|
13
|
+
viridis: he,
|
|
14
|
+
plasma: ue,
|
|
15
|
+
cyclic: fe,
|
|
16
|
+
tempo: me,
|
|
17
|
+
turbid: Ee,
|
|
18
|
+
speed: pe,
|
|
19
|
+
amp: Te,
|
|
20
|
+
heat: be,
|
|
21
|
+
"heat-alert": { label: "Heat Alert", tags: ["sequential", "alpha", "deprecated"], colors: ["#fde72500", "#fde725cc", "#f8961e", "#e63946", "#9d0208", "#3a0007"] },
|
|
22
|
+
balance: Re,
|
|
23
|
+
delta: ge,
|
|
24
|
+
curl: we,
|
|
25
|
+
"mono-black": { label: "Black", tags: ["monochrome", "particles"], colors: ["#000000", "#000000"] },
|
|
26
|
+
"mono-white": { label: "White", tags: ["monochrome", "particles"], colors: ["#ffffff", "#ffffff"] },
|
|
27
|
+
"mono-pink": { label: "Pink", tags: ["monochrome", "particles"], colors: ["#ff2aa1", "#ff2aa1"] },
|
|
28
|
+
gray: _e
|
|
29
|
+
}, B = 64, ye = {
|
|
30
|
+
linear: (t) => t,
|
|
31
|
+
// Rises fast: values just inside the zone already show.
|
|
32
|
+
log: (t) => Math.log10(1 + 9 * t),
|
|
33
|
+
// Stays faint longer: codec noise at the end of the range stays hidden.
|
|
34
|
+
exp: (t) => (10 ** t - 1) / 9
|
|
35
|
+
};
|
|
36
|
+
function xe(t, e) {
|
|
37
|
+
const i = ye[e.curve ?? "linear"], r = e.low ?? 0, n = e.high ?? 0;
|
|
38
|
+
let s = 1;
|
|
39
|
+
return r > 0 && t < r && (s = Math.min(s, i(Math.max(t, 0) / r))), n > 0 && t > 1 - n && (s = Math.min(s, i(Math.max(1 - t, 0) / n))), s;
|
|
40
|
+
}
|
|
41
|
+
function Ae(t) {
|
|
42
|
+
let e = t.replace("#", "");
|
|
43
|
+
(e.length === 3 || e.length === 4) && (e = [...e].map((r) => r + r).join(""));
|
|
44
|
+
const i = (r) => parseInt(e.slice(r, r + 2), 16);
|
|
45
|
+
return [
|
|
46
|
+
i(0),
|
|
47
|
+
i(2),
|
|
48
|
+
i(4),
|
|
49
|
+
e.length === 8 ? i(6) : 255
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
function ke([t, e, i, r]) {
|
|
53
|
+
return `#${[t, e, i, r].map(
|
|
54
|
+
(n) => Math.round(Math.min(255, Math.max(0, n))).toString(16).padStart(2, "0")
|
|
55
|
+
).join("")}`;
|
|
56
|
+
}
|
|
57
|
+
function Fe(t, e) {
|
|
58
|
+
if (e <= t[0][0])
|
|
59
|
+
return t[0][1];
|
|
60
|
+
for (let i = 1; i < t.length; i++) {
|
|
61
|
+
const [r, n] = t[i];
|
|
62
|
+
if (e <= r) {
|
|
63
|
+
const [s, o] = t[i - 1], a = r === s ? 1 : (e - s) / (r - s);
|
|
64
|
+
return o.map((d, c) => d + (n[c] - d) * a);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return t[t.length - 1][1];
|
|
68
|
+
}
|
|
69
|
+
function ve(t, e) {
|
|
70
|
+
const i = Object.entries(t).map(
|
|
71
|
+
([n, s]) => [Number(n), Ae(s)]
|
|
72
|
+
).sort((n, s) => n[0] - s[0]), r = {};
|
|
73
|
+
for (let n = 0; n <= B; n++) {
|
|
74
|
+
const s = n / B, [o, a, d, c] = Fe(i, s);
|
|
75
|
+
r[s] = ke([o, a, d, c * xe(s, e)]);
|
|
76
|
+
}
|
|
77
|
+
return r;
|
|
78
|
+
}
|
|
79
|
+
const Se = (t) => !!t && ((t.low ?? 0) > 0 || (t.high ?? 0) > 0);
|
|
80
|
+
function X(t) {
|
|
81
|
+
const e = {}, i = t.length - 1;
|
|
82
|
+
return t.forEach((r, n) => {
|
|
83
|
+
e[i === 0 ? 0 : n / i] = r;
|
|
84
|
+
}), e;
|
|
85
|
+
}
|
|
86
|
+
function Ue(t, e) {
|
|
87
|
+
const i = Pe(t, e?.reverse ?? !1);
|
|
88
|
+
return Se(e?.alphaFade) ? ve(i, e.alphaFade) : i;
|
|
89
|
+
}
|
|
90
|
+
function Pe(t, e) {
|
|
91
|
+
const i = K;
|
|
92
|
+
if (typeof t == "string") {
|
|
93
|
+
const r = i[t];
|
|
94
|
+
if (!r)
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Palette "${t}" not found. Available palettes: ${Object.keys(
|
|
97
|
+
i
|
|
98
|
+
).join(", ")}`
|
|
99
|
+
);
|
|
100
|
+
const n = e ? [...r.colors].reverse() : r.colors;
|
|
101
|
+
return X(n);
|
|
102
|
+
}
|
|
103
|
+
if (Array.isArray(t)) {
|
|
104
|
+
const r = e ? [...t].reverse() : t;
|
|
105
|
+
return X(r);
|
|
106
|
+
}
|
|
107
|
+
return e ? Object.fromEntries(
|
|
108
|
+
Object.entries(t).map(([r, n]) => [1 - Number(r), n])
|
|
109
|
+
) : t;
|
|
110
|
+
}
|
|
111
|
+
function wt(t = {}) {
|
|
112
|
+
return Object.entries(K).map(([i, r]) => ({ id: i, ...r })).filter(
|
|
113
|
+
(i) => t.includeDeprecated || !i.tags.includes("deprecated")
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
function O(t, e, i) {
|
|
117
|
+
const r = t.createShader(e);
|
|
118
|
+
if (!r)
|
|
119
|
+
throw new Error("Failed to create shader");
|
|
120
|
+
if (t.shaderSource(r, i), t.compileShader(r), !t.getShaderParameter(r, t.COMPILE_STATUS)) {
|
|
121
|
+
const n = t.getShaderInfoLog(r);
|
|
122
|
+
throw t.deleteShader(r), new Error(`Shader compile error: ${n}`);
|
|
123
|
+
}
|
|
124
|
+
return r;
|
|
125
|
+
}
|
|
126
|
+
function _t(t, e, i) {
|
|
127
|
+
const r = t.createProgram();
|
|
128
|
+
if (!r)
|
|
129
|
+
throw new Error("Failed to create program");
|
|
130
|
+
const n = O(t, t.VERTEX_SHADER, e), s = O(t, t.FRAGMENT_SHADER, i);
|
|
131
|
+
if (t.attachShader(r, n), t.attachShader(r, s), t.linkProgram(r), !t.getProgramParameter(r, t.LINK_STATUS)) {
|
|
132
|
+
const o = t.getProgramInfoLog(r);
|
|
133
|
+
throw t.deleteProgram(r), new Error(`Program link error: ${o}`);
|
|
134
|
+
}
|
|
135
|
+
return t.deleteShader(n), t.deleteShader(s), r;
|
|
136
|
+
}
|
|
137
|
+
function yt(t, e, i, r, n) {
|
|
138
|
+
const s = t.createTexture();
|
|
139
|
+
if (!s)
|
|
140
|
+
throw new Error("Failed to create texture");
|
|
141
|
+
return t.bindTexture(t.TEXTURE_2D, s), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_S, t.CLAMP_TO_EDGE), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_T, t.CLAMP_TO_EDGE), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MIN_FILTER, e), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MAG_FILTER, e), t.texImage2D(
|
|
142
|
+
t.TEXTURE_2D,
|
|
143
|
+
0,
|
|
144
|
+
t.RGBA,
|
|
145
|
+
r,
|
|
146
|
+
n,
|
|
147
|
+
0,
|
|
148
|
+
t.RGBA,
|
|
149
|
+
t.UNSIGNED_BYTE,
|
|
150
|
+
i
|
|
151
|
+
), t.bindTexture(t.TEXTURE_2D, null), s;
|
|
152
|
+
}
|
|
153
|
+
function De(t, e, i) {
|
|
154
|
+
const r = t.getParameter(
|
|
155
|
+
t.TEXTURE_BINDING_2D
|
|
156
|
+
), n = t.getParameter(
|
|
157
|
+
t.FRAMEBUFFER_BINDING
|
|
158
|
+
), s = t.createTexture(), o = t.createFramebuffer();
|
|
159
|
+
if (!s || !o)
|
|
160
|
+
return s && t.deleteTexture(s), o && t.deleteFramebuffer(o), t.bindTexture(t.TEXTURE_2D, r), t.bindFramebuffer(t.FRAMEBUFFER, n), !1;
|
|
161
|
+
let a = !1;
|
|
162
|
+
try {
|
|
163
|
+
t.bindTexture(t.TEXTURE_2D, s), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_S, t.CLAMP_TO_EDGE), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_T, t.CLAMP_TO_EDGE), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MIN_FILTER, t.NEAREST), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MAG_FILTER, t.NEAREST), t.texImage2D(
|
|
164
|
+
t.TEXTURE_2D,
|
|
165
|
+
0,
|
|
166
|
+
e,
|
|
167
|
+
1,
|
|
168
|
+
1,
|
|
169
|
+
0,
|
|
170
|
+
t.RGBA,
|
|
171
|
+
i,
|
|
172
|
+
null
|
|
173
|
+
), t.bindFramebuffer(t.FRAMEBUFFER, o), t.framebufferTexture2D(
|
|
174
|
+
t.FRAMEBUFFER,
|
|
175
|
+
t.COLOR_ATTACHMENT0,
|
|
176
|
+
t.TEXTURE_2D,
|
|
177
|
+
s,
|
|
178
|
+
0
|
|
179
|
+
), a = t.checkFramebufferStatus(t.FRAMEBUFFER) === t.FRAMEBUFFER_COMPLETE;
|
|
180
|
+
} finally {
|
|
181
|
+
t.bindFramebuffer(t.FRAMEBUFFER, n), t.bindTexture(t.TEXTURE_2D, r), t.deleteFramebuffer(o), t.deleteTexture(s);
|
|
182
|
+
}
|
|
183
|
+
return a;
|
|
184
|
+
}
|
|
185
|
+
function Me(t) {
|
|
186
|
+
return {
|
|
187
|
+
kind: "rgba8-packed",
|
|
188
|
+
internalFormat: t.RGBA,
|
|
189
|
+
type: t.UNSIGNED_BYTE
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function xt(t) {
|
|
193
|
+
if (typeof WebGL2RenderingContext > "u" || !(t instanceof WebGL2RenderingContext))
|
|
194
|
+
throw new Error("zartigl requires WebGL2");
|
|
195
|
+
return t;
|
|
196
|
+
}
|
|
197
|
+
function At(t) {
|
|
198
|
+
const e = [];
|
|
199
|
+
t.getExtension("EXT_color_buffer_float") ? e.push(
|
|
200
|
+
{ kind: "float32", internalFormat: t.RGBA32F, type: t.FLOAT },
|
|
201
|
+
{ kind: "float16", internalFormat: t.RGBA16F, type: t.HALF_FLOAT }
|
|
202
|
+
) : t.getExtension("EXT_color_buffer_half_float") && e.push({
|
|
203
|
+
kind: "float16",
|
|
204
|
+
internalFormat: t.RGBA16F,
|
|
205
|
+
type: t.HALF_FLOAT
|
|
206
|
+
});
|
|
207
|
+
for (const i of e)
|
|
208
|
+
if (De(
|
|
209
|
+
t,
|
|
210
|
+
i.internalFormat,
|
|
211
|
+
i.type
|
|
212
|
+
))
|
|
213
|
+
return i;
|
|
214
|
+
return Me(t);
|
|
215
|
+
}
|
|
216
|
+
function kt(t, e, i, r, n, s, o) {
|
|
217
|
+
const a = t.createTexture();
|
|
218
|
+
if (!a)
|
|
219
|
+
throw new Error("Failed to create texture");
|
|
220
|
+
return t.bindTexture(t.TEXTURE_2D, a), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_S, t.CLAMP_TO_EDGE), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_T, t.CLAMP_TO_EDGE), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MIN_FILTER, e), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MAG_FILTER, e), t.texImage2D(
|
|
221
|
+
t.TEXTURE_2D,
|
|
222
|
+
0,
|
|
223
|
+
s,
|
|
224
|
+
r,
|
|
225
|
+
n,
|
|
226
|
+
0,
|
|
227
|
+
t.RGBA,
|
|
228
|
+
o,
|
|
229
|
+
i
|
|
230
|
+
), t.bindTexture(t.TEXTURE_2D, null), a;
|
|
231
|
+
}
|
|
232
|
+
function Ft(t, e, i) {
|
|
233
|
+
t.activeTexture(t.TEXTURE0 + i), t.bindTexture(t.TEXTURE_2D, e);
|
|
234
|
+
}
|
|
235
|
+
function vt(t, e) {
|
|
236
|
+
const i = t.createFramebuffer();
|
|
237
|
+
if (!i)
|
|
238
|
+
throw new Error("Failed to create framebuffer");
|
|
239
|
+
return t.bindFramebuffer(t.FRAMEBUFFER, i), t.framebufferTexture2D(
|
|
240
|
+
t.FRAMEBUFFER,
|
|
241
|
+
t.COLOR_ATTACHMENT0,
|
|
242
|
+
t.TEXTURE_2D,
|
|
243
|
+
e,
|
|
244
|
+
0
|
|
245
|
+
), t.bindFramebuffer(t.FRAMEBUFFER, null), i;
|
|
246
|
+
}
|
|
247
|
+
function St(t) {
|
|
248
|
+
const e = t.createBuffer();
|
|
249
|
+
if (!e)
|
|
250
|
+
throw new Error("Failed to create buffer");
|
|
251
|
+
return t.bindBuffer(t.ARRAY_BUFFER, e), t.bufferData(
|
|
252
|
+
t.ARRAY_BUFFER,
|
|
253
|
+
new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
|
|
254
|
+
t.STATIC_DRAW
|
|
255
|
+
), e;
|
|
256
|
+
}
|
|
257
|
+
const Ne = {
|
|
258
|
+
0: "#3288bd",
|
|
259
|
+
0.1: "#66c2a5",
|
|
260
|
+
0.2: "#abdda4",
|
|
261
|
+
0.3: "#e6f598",
|
|
262
|
+
0.4: "#fee08b",
|
|
263
|
+
0.5: "#fdae61",
|
|
264
|
+
0.6: "#f46d43",
|
|
265
|
+
1: "#d53e4f"
|
|
266
|
+
};
|
|
267
|
+
function Ut(t, e, i) {
|
|
268
|
+
const r = Ue(e ?? Ne, i), n = document.createElement("canvas");
|
|
269
|
+
n.width = 256, n.height = 1;
|
|
270
|
+
const s = n.getContext("2d"), o = s.createLinearGradient(0, 0, 256, 0);
|
|
271
|
+
for (const [d, c] of Object.entries(r))
|
|
272
|
+
o.addColorStop(Number(d), c);
|
|
273
|
+
s.fillStyle = o, s.fillRect(0, 0, 256, 1);
|
|
274
|
+
const a = t.createTexture();
|
|
275
|
+
if (!a)
|
|
276
|
+
throw new Error("Failed to create color ramp texture");
|
|
277
|
+
return t.bindTexture(t.TEXTURE_2D, a), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_S, t.CLAMP_TO_EDGE), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_T, t.CLAMP_TO_EDGE), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MIN_FILTER, t.LINEAR), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MAG_FILTER, t.LINEAR), t.texImage2D(t.TEXTURE_2D, 0, t.RGBA, t.RGBA, t.UNSIGNED_BYTE, n), t.bindTexture(t.TEXTURE_2D, null), a;
|
|
278
|
+
}
|
|
279
|
+
function Pt(t) {
|
|
280
|
+
const e = t.getExtension("WEBGL_debug_renderer_info");
|
|
281
|
+
return {
|
|
282
|
+
vendor: String(t.getParameter(t.VENDOR) ?? ""),
|
|
283
|
+
renderer: String(t.getParameter(t.RENDERER) ?? ""),
|
|
284
|
+
unmaskedVendor: e ? String(t.getParameter(e.UNMASKED_VENDOR_WEBGL) ?? "") : void 0,
|
|
285
|
+
unmaskedRenderer: e ? String(t.getParameter(e.UNMASKED_RENDERER_WEBGL) ?? "") : void 0,
|
|
286
|
+
version: String(t.getParameter(t.VERSION) ?? "")
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function Dt(t) {
|
|
290
|
+
return {
|
|
291
|
+
program: t.getParameter(t.CURRENT_PROGRAM),
|
|
292
|
+
activeTexture: t.getParameter(t.ACTIVE_TEXTURE),
|
|
293
|
+
arrayBuffer: t.getParameter(t.ARRAY_BUFFER_BINDING),
|
|
294
|
+
elementBuffer: t.getParameter(t.ELEMENT_ARRAY_BUFFER_BINDING),
|
|
295
|
+
framebuffer: t.getParameter(t.FRAMEBUFFER_BINDING),
|
|
296
|
+
blend: t.isEnabled(t.BLEND),
|
|
297
|
+
depthTest: t.isEnabled(t.DEPTH_TEST),
|
|
298
|
+
stencilTest: t.isEnabled(t.STENCIL_TEST),
|
|
299
|
+
viewport: t.getParameter(t.VIEWPORT),
|
|
300
|
+
blendFunc: [
|
|
301
|
+
t.getParameter(t.BLEND_SRC_RGB),
|
|
302
|
+
t.getParameter(t.BLEND_DST_RGB)
|
|
303
|
+
]
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function Mt(t, e) {
|
|
307
|
+
t.useProgram(e.program), t.activeTexture(e.activeTexture), t.bindBuffer(t.ARRAY_BUFFER, e.arrayBuffer), t.bindBuffer(t.ELEMENT_ARRAY_BUFFER, e.elementBuffer), t.bindFramebuffer(t.FRAMEBUFFER, e.framebuffer), e.blend ? t.enable(t.BLEND) : t.disable(t.BLEND), e.depthTest ? t.enable(t.DEPTH_TEST) : t.disable(t.DEPTH_TEST), e.stencilTest ? t.enable(t.STENCIL_TEST) : t.disable(t.STENCIL_TEST), t.viewport(e.viewport[0], e.viewport[1], e.viewport[2], e.viewport[3]), t.blendFunc(e.blendFunc[0], e.blendFunc[1]);
|
|
308
|
+
}
|
|
309
|
+
const Ie = {
|
|
310
|
+
degrees_c: "°C",
|
|
311
|
+
degc: "°C",
|
|
312
|
+
degrees_celsius: "°C",
|
|
313
|
+
degree_celsius: "°C",
|
|
314
|
+
celsius: "°C",
|
|
315
|
+
// Degree heating weeks accumulate °C over weeks: a product, not a rate.
|
|
316
|
+
degree_celsius_weeks: "°C-weeks",
|
|
317
|
+
degree: "°",
|
|
318
|
+
degrees: "°",
|
|
319
|
+
kelvin: "K",
|
|
320
|
+
1: "",
|
|
321
|
+
"1e-3": "PSU",
|
|
322
|
+
"0.001": "PSU",
|
|
323
|
+
psu: "PSU",
|
|
324
|
+
stress_level: ""
|
|
325
|
+
}, Ge = {
|
|
326
|
+
meter: "m",
|
|
327
|
+
meters: "m",
|
|
328
|
+
metre: "m",
|
|
329
|
+
metres: "m",
|
|
330
|
+
second: "s",
|
|
331
|
+
seconds: "s",
|
|
332
|
+
day: "d",
|
|
333
|
+
days: "d",
|
|
334
|
+
hour: "h",
|
|
335
|
+
hours: "h",
|
|
336
|
+
gram: "g",
|
|
337
|
+
grams: "g",
|
|
338
|
+
milligram: "mg",
|
|
339
|
+
milligrams: "mg",
|
|
340
|
+
kilogram: "kg",
|
|
341
|
+
kilograms: "kg",
|
|
342
|
+
mole: "mol",
|
|
343
|
+
millimole: "mmol",
|
|
344
|
+
watt: "W",
|
|
345
|
+
newton: "N",
|
|
346
|
+
pascal: "Pa",
|
|
347
|
+
kelvin: "K"
|
|
348
|
+
}, Le = "⁰¹²³⁴⁵⁶⁷⁸⁹";
|
|
349
|
+
function q(t) {
|
|
350
|
+
return t === 1 ? "" : [...String(t)].map((e) => Le[Number(e)]).join("");
|
|
351
|
+
}
|
|
352
|
+
function Nt(t) {
|
|
353
|
+
const e = t?.trim() ?? "", i = Ie[e.toLowerCase()];
|
|
354
|
+
if (i != null)
|
|
355
|
+
return i;
|
|
356
|
+
if (e.includes("/"))
|
|
357
|
+
return e;
|
|
358
|
+
const r = [], n = [];
|
|
359
|
+
for (const s of e.split(/\s+/)) {
|
|
360
|
+
const o = /^([A-Za-z]+)\^?(-?\d+)?$/.exec(s);
|
|
361
|
+
if (!o)
|
|
362
|
+
return e.replace(/_/g, " ");
|
|
363
|
+
const a = Ge[o[1]] ?? o[1], d = Number(o[2] ?? 1);
|
|
364
|
+
d < 0 ? n.push(a + q(-d)) : r.push(a + q(d));
|
|
365
|
+
}
|
|
366
|
+
return [r.join(" ") || "1", ...n].join("/");
|
|
367
|
+
}
|
|
368
|
+
const $ = 20, Ce = 600;
|
|
369
|
+
function Ve(t) {
|
|
370
|
+
const e = Math.max(0, Math.min(1, t));
|
|
371
|
+
return $ * Math.pow(Ce / $, e);
|
|
372
|
+
}
|
|
373
|
+
function It(t) {
|
|
374
|
+
return Math.exp(-1 / Ve(t));
|
|
375
|
+
}
|
|
376
|
+
const Be = 0.8, Xe = 120, Oe = 300;
|
|
377
|
+
class Gt {
|
|
378
|
+
constructor(e) {
|
|
379
|
+
this.base = e, this.moving = !1, this.start = 0, this.duration = 0, this.from = e, this.to = e;
|
|
380
|
+
}
|
|
381
|
+
/** Configured opacity, applied at once unless the camera moves. */
|
|
382
|
+
setBase(e) {
|
|
383
|
+
this.base = e, this.moving || (this.from = e, this.to = e, this.duration = 0);
|
|
384
|
+
}
|
|
385
|
+
setMoving(e, i) {
|
|
386
|
+
e !== this.moving && (this.from = this.value(i), this.moving = e, this.to = e ? Be : this.base, this.start = i, this.duration = e ? Xe : Oe);
|
|
387
|
+
}
|
|
388
|
+
value(e) {
|
|
389
|
+
if (this.duration === 0)
|
|
390
|
+
return this.to;
|
|
391
|
+
const i = Math.min(1, Math.max(0, (e - this.start) / this.duration));
|
|
392
|
+
return i === 1 ? (this.duration = 0, this.from = this.to, this.to) : this.from + (this.to - this.from) * i * i * (3 - 2 * i);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function Lt(t) {
|
|
396
|
+
if (t == null)
|
|
397
|
+
return null;
|
|
398
|
+
if (!Number.isFinite(t[0]) || !Number.isFinite(t[1]) || t[0] >= t[1])
|
|
399
|
+
throw new RangeError(
|
|
400
|
+
"colorDomain must contain two finite, increasing values"
|
|
401
|
+
);
|
|
402
|
+
return [t[0], t[1]];
|
|
403
|
+
}
|
|
404
|
+
function p(t, e) {
|
|
405
|
+
if (typeof t != "number" || !Number.isFinite(t))
|
|
406
|
+
throw new Error(`Invalid GeoVideo manifest ${e}`);
|
|
407
|
+
return t;
|
|
408
|
+
}
|
|
409
|
+
function A(t, e) {
|
|
410
|
+
if (typeof t != "string")
|
|
411
|
+
throw new Error(`Invalid GeoVideo manifest ${e}`);
|
|
412
|
+
const i = new Date(t).getTime();
|
|
413
|
+
if (!Number.isFinite(i))
|
|
414
|
+
throw new Error(`Invalid GeoVideo manifest ${e}`);
|
|
415
|
+
return i;
|
|
416
|
+
}
|
|
417
|
+
function D(t) {
|
|
418
|
+
return typeof t == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(
|
|
419
|
+
t
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
function W(t) {
|
|
423
|
+
if (!t || typeof t != "object")
|
|
424
|
+
throw new Error("Invalid GeoVideo manifest");
|
|
425
|
+
const e = t;
|
|
426
|
+
if (e.schemaVersion !== 3 || e.type !== "geovideo")
|
|
427
|
+
throw new Error("Unsupported GeoVideo manifest version or type");
|
|
428
|
+
if (e.projection !== "equirectangular")
|
|
429
|
+
throw new Error(
|
|
430
|
+
`Unsupported GeoVideo projection: ${String(e.projection)}`
|
|
431
|
+
);
|
|
432
|
+
if (!Array.isArray(e.bounds) || e.bounds.length !== 4)
|
|
433
|
+
throw new Error("Invalid GeoVideo manifest bounds");
|
|
434
|
+
const [i, r, n, s] = e.bounds.map(
|
|
435
|
+
(u, m) => p(u, `bounds[${m}]`)
|
|
436
|
+
);
|
|
437
|
+
if (r < -90 || s > 90 || r >= s || i === n)
|
|
438
|
+
throw new Error("Invalid GeoVideo geographic bounds");
|
|
439
|
+
const o = e.media;
|
|
440
|
+
if (!o || typeof o.url != "string" || !o.url)
|
|
441
|
+
throw new Error("Invalid GeoVideo media");
|
|
442
|
+
for (const u of ["width", "height", "fps", "durationSeconds"])
|
|
443
|
+
if (p(o[u], `media.${u}`) <= 0)
|
|
444
|
+
throw new Error(`Invalid GeoVideo media.${u}`);
|
|
445
|
+
const a = e.encoding;
|
|
446
|
+
if (!a || a.bits !== 8 || a.colorSpace !== "bt709" || a.colorRange !== "limited" && a.colorRange !== "full")
|
|
447
|
+
throw new Error("Invalid GeoVideo encoding");
|
|
448
|
+
const d = p(a.codeMin, "encoding.codeMin"), c = p(a.codeMax, "encoding.codeMax");
|
|
449
|
+
if (d < 0 || c > 255 || d >= c)
|
|
450
|
+
throw new Error("Invalid GeoVideo code range");
|
|
451
|
+
let l = o.height;
|
|
452
|
+
if (a.kind === "scalar-luma") {
|
|
453
|
+
if (a.transfer !== "linear" && a.transfer !== "log10-1p9" || p(a.valueMin, "encoding.valueMin") >= p(a.valueMax, "encoding.valueMax"))
|
|
454
|
+
throw new Error("Invalid GeoVideo scalar-luma encoding");
|
|
455
|
+
} else if (a.kind === "vector-luma") {
|
|
456
|
+
if (a.transfer !== "linear" && a.transfer !== "sqrt" || a.layout !== "stacked-uv" || p(a.valueDomain, "encoding.valueDomain") <= 0 || o.height % 2 !== 0)
|
|
457
|
+
throw new Error("Invalid GeoVideo vector-luma encoding");
|
|
458
|
+
l = o.height / 2;
|
|
459
|
+
} else
|
|
460
|
+
throw new Error("Unsupported GeoVideo encoding");
|
|
461
|
+
const h = e.mask;
|
|
462
|
+
if (!h || h.kind !== "static-validity" || typeof h.url != "string" || !h.url || p(h.width, "mask.width") !== o.width || p(h.height, "mask.height") !== l || p(h.threshold, "mask.threshold") < 0 || h.threshold > 1)
|
|
463
|
+
throw new Error("Invalid GeoVideo static mask");
|
|
464
|
+
if (e.timeline?.kind === "range") {
|
|
465
|
+
const u = A(e.timeline.dateStart, "timeline.dateStart");
|
|
466
|
+
if (A(e.timeline.dateEnd, "timeline.dateEnd") <= u)
|
|
467
|
+
throw new Error("GeoVideo timeline end must follow start");
|
|
468
|
+
if (e.timeline.interpolation !== "linear" && e.timeline.interpolation !== "nearest")
|
|
469
|
+
throw new Error("Invalid GeoVideo interpolation");
|
|
470
|
+
} else if (e.timeline?.kind === "snapshot-loop")
|
|
471
|
+
A(e.timeline.date, "timeline.date");
|
|
472
|
+
else if (e.timeline?.kind === "sample-sequence") {
|
|
473
|
+
if (!Array.isArray(e.timeline.values) || e.timeline.values.length === 0)
|
|
474
|
+
throw new Error("GeoVideo sample sequence must contain values");
|
|
475
|
+
const u = e.timeline.values.map(
|
|
476
|
+
(m, T) => A(m, `timeline.values[${T}]`)
|
|
477
|
+
);
|
|
478
|
+
if (u.some((m, T) => T > 0 && m <= u[T - 1]))
|
|
479
|
+
throw new Error(
|
|
480
|
+
"GeoVideo sample sequence values must be strictly increasing"
|
|
481
|
+
);
|
|
482
|
+
} else
|
|
483
|
+
throw new Error("Invalid GeoVideo timeline");
|
|
484
|
+
const f = e.style?.colorDomain;
|
|
485
|
+
if (!Array.isArray(f) || f.length !== 2 || !f.every(Number.isFinite) || f[0] >= f[1])
|
|
486
|
+
throw new Error("Invalid GeoVideo color domain");
|
|
487
|
+
if (!D(e.id) || !e.provenance || !D(e.provenance.catalogEntryId) || !D(e.provenance.inputSourceId) || !Array.isArray(e.provenance.variables) || e.provenance.variables.length === 0)
|
|
488
|
+
throw new Error("Invalid GeoVideo provenance");
|
|
489
|
+
return { ...e, bounds: [i, r, n, s] };
|
|
490
|
+
}
|
|
491
|
+
async function qe(t, e) {
|
|
492
|
+
if (typeof t != "string")
|
|
493
|
+
return W(t);
|
|
494
|
+
const i = await fetch(t, { signal: e });
|
|
495
|
+
if (!i.ok)
|
|
496
|
+
throw new Error(
|
|
497
|
+
`GeoVideo manifest request failed (${i.status}): ${t}`
|
|
498
|
+
);
|
|
499
|
+
const r = await i.json(), n = W(r), s = typeof document < "u" ? new URL(t, document.baseURI).href : t;
|
|
500
|
+
return {
|
|
501
|
+
...n,
|
|
502
|
+
media: { ...n.media, url: new URL(n.media.url, s).href },
|
|
503
|
+
mask: { ...n.mask, url: new URL(n.mask.url, s).href }
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function w(t) {
|
|
507
|
+
if (t.timeline.kind === "snapshot-loop")
|
|
508
|
+
return [new Date(t.timeline.date).getTime()];
|
|
509
|
+
if (t.timeline.kind === "sample-sequence")
|
|
510
|
+
return t.timeline.values.map((n) => new Date(n).getTime());
|
|
511
|
+
const e = new Date(t.timeline.dateStart).getTime(), i = new Date(t.timeline.dateEnd).getTime(), r = Math.max(
|
|
512
|
+
2,
|
|
513
|
+
Math.round(t.media.durationSeconds * t.media.fps)
|
|
514
|
+
);
|
|
515
|
+
return Array.from(
|
|
516
|
+
{ length: r },
|
|
517
|
+
(n, s) => e + (i - e) * s / (r - 1)
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
function z(t) {
|
|
521
|
+
return t.timeline.kind === "sample-sequence" ? t.timeline.values.length / t.media.durationSeconds : t.media.fps;
|
|
522
|
+
}
|
|
523
|
+
function Ct(t, e) {
|
|
524
|
+
return e / z(t);
|
|
525
|
+
}
|
|
526
|
+
function $e(t, e) {
|
|
527
|
+
if (t.timeline.kind === "snapshot-loop")
|
|
528
|
+
return 0;
|
|
529
|
+
if (t.timeline.kind === "sample-sequence") {
|
|
530
|
+
const s = w(t);
|
|
531
|
+
let o = 0;
|
|
532
|
+
for (let d = 1; d < s.length; d += 1)
|
|
533
|
+
Math.abs(s[d] - e) < Math.abs(s[o] - e) && (o = d);
|
|
534
|
+
const a = t.media.durationSeconds / s.length;
|
|
535
|
+
return o * a + Math.min(a / 2, 0.5 / t.media.fps);
|
|
536
|
+
}
|
|
537
|
+
const i = new Date(t.timeline.dateStart).getTime(), r = new Date(t.timeline.dateEnd).getTime();
|
|
538
|
+
return Math.max(0, Math.min(1, (e - i) / (r - i))) * Z(t);
|
|
539
|
+
}
|
|
540
|
+
function Vt(t, e) {
|
|
541
|
+
if (t.timeline.kind === "snapshot-loop")
|
|
542
|
+
return new Date(t.timeline.date).getTime();
|
|
543
|
+
if (t.timeline.kind === "sample-sequence") {
|
|
544
|
+
const o = w(t), a = t.media.durationSeconds / o.length, d = Math.floor((e + 0.25 / t.media.fps) / a);
|
|
545
|
+
return o[Math.max(0, Math.min(o.length - 1, d))];
|
|
546
|
+
}
|
|
547
|
+
const i = new Date(t.timeline.dateStart).getTime(), r = new Date(t.timeline.dateEnd).getTime(), n = Z(t), s = n > 0 ? Math.max(0, Math.min(1, e / n)) : 0;
|
|
548
|
+
return i + (r - i) * s;
|
|
549
|
+
}
|
|
550
|
+
function Z(t) {
|
|
551
|
+
return Math.max(0, t.media.durationSeconds - 1 / t.media.fps);
|
|
552
|
+
}
|
|
553
|
+
function We(t, e) {
|
|
554
|
+
const i = new Uint8Array(t.length / 4);
|
|
555
|
+
for (let r = 0; r < i.length; r++) {
|
|
556
|
+
const n = t[r * 4];
|
|
557
|
+
i[r] = e === "limited" ? Math.round(n * 219 / 255 + 16) : n;
|
|
558
|
+
}
|
|
559
|
+
return i;
|
|
560
|
+
}
|
|
561
|
+
class He {
|
|
562
|
+
constructor() {
|
|
563
|
+
this.gl = null, this.texture = null, this.framebuffer = null;
|
|
564
|
+
}
|
|
565
|
+
read(e, i, r) {
|
|
566
|
+
const n = this.context();
|
|
567
|
+
n.bindTexture(n.TEXTURE_2D, this.texture), n.pixelStorei(n.UNPACK_FLIP_Y_WEBGL, !1), n.pixelStorei(n.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !1), n.pixelStorei(n.UNPACK_COLORSPACE_CONVERSION_WEBGL, n.NONE), n.texParameteri(n.TEXTURE_2D, n.TEXTURE_MIN_FILTER, n.NEAREST), n.texParameteri(n.TEXTURE_2D, n.TEXTURE_MAG_FILTER, n.NEAREST), n.texParameteri(n.TEXTURE_2D, n.TEXTURE_WRAP_S, n.CLAMP_TO_EDGE), n.texParameteri(n.TEXTURE_2D, n.TEXTURE_WRAP_T, n.CLAMP_TO_EDGE), n.texImage2D(n.TEXTURE_2D, 0, n.RGBA, n.RGBA, n.UNSIGNED_BYTE, e), n.bindFramebuffer(n.FRAMEBUFFER, this.framebuffer), n.framebufferTexture2D(
|
|
568
|
+
n.FRAMEBUFFER,
|
|
569
|
+
n.COLOR_ATTACHMENT0,
|
|
570
|
+
n.TEXTURE_2D,
|
|
571
|
+
this.texture,
|
|
572
|
+
0
|
|
573
|
+
);
|
|
574
|
+
const s = new Uint8Array(i * r * 4);
|
|
575
|
+
return n.readPixels(0, 0, i, r, n.RGBA, n.UNSIGNED_BYTE, s), n.bindFramebuffer(n.FRAMEBUFFER, null), s;
|
|
576
|
+
}
|
|
577
|
+
release() {
|
|
578
|
+
this.gl?.getExtension("WEBGL_lose_context")?.loseContext(), this.gl = null, this.texture = null, this.framebuffer = null;
|
|
579
|
+
}
|
|
580
|
+
context() {
|
|
581
|
+
if (this.gl)
|
|
582
|
+
return this.gl;
|
|
583
|
+
const e = document.createElement("canvas"), i = {
|
|
584
|
+
premultipliedAlpha: !1,
|
|
585
|
+
antialias: !1
|
|
586
|
+
}, r = e.getContext("webgl2", i);
|
|
587
|
+
if (!r)
|
|
588
|
+
throw new Error("WebGL2 is required to decode GeoVideo frames");
|
|
589
|
+
return this.gl = r, this.texture = r.createTexture(), this.framebuffer = r.createFramebuffer(), r;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function H(t) {
|
|
593
|
+
const e = Math.max(4, Math.floor(t.length / 4 / 997) * 4);
|
|
594
|
+
for (let i = 3; i < t.length; i += e)
|
|
595
|
+
if (t[i] !== 0)
|
|
596
|
+
return !1;
|
|
597
|
+
return !0;
|
|
598
|
+
}
|
|
599
|
+
function E() {
|
|
600
|
+
return new DOMException("GeoVideo frame request was cancelled", "AbortError");
|
|
601
|
+
}
|
|
602
|
+
function _(t, e, i) {
|
|
603
|
+
return e ? e.aborted ? (t.catch(() => {
|
|
604
|
+
}), i?.(), Promise.reject(E())) : new Promise((r, n) => {
|
|
605
|
+
const s = () => {
|
|
606
|
+
i?.(), n(E());
|
|
607
|
+
};
|
|
608
|
+
e.addEventListener("abort", s, { once: !0 }), t.then(r, n).finally(() => {
|
|
609
|
+
e.removeEventListener("abort", s);
|
|
610
|
+
});
|
|
611
|
+
}) : t;
|
|
612
|
+
}
|
|
613
|
+
function M(t, e) {
|
|
614
|
+
return new Promise(
|
|
615
|
+
(i) => t.addEventListener(e, () => i(), { once: !0 })
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
const Ye = 250;
|
|
619
|
+
function je(t) {
|
|
620
|
+
return "requestVideoFrameCallback" in t ? Promise.race([
|
|
621
|
+
new Promise(
|
|
622
|
+
(e) => t.requestVideoFrameCallback(() => e())
|
|
623
|
+
),
|
|
624
|
+
new Promise((e) => setTimeout(e, Ye))
|
|
625
|
+
]) : Promise.resolve();
|
|
626
|
+
}
|
|
627
|
+
class J {
|
|
628
|
+
constructor(e, i, r = {}) {
|
|
629
|
+
this.reader = new He(), this.video = null, this.maskCodes = null, this.initPromise = null, this.presented = !1, this.primed = !1, this.queue = Promise.resolve(), this.frames = /* @__PURE__ */ new Map(), this.pending = /* @__PURE__ */ new Set(), this.manifest = e, this.planes = i, this.cacheSize = Math.max(1, r.cacheSize ?? 3), this.width = e.media.width, this.height = e.media.height / i, this.times = Float64Array.from(w(e));
|
|
630
|
+
}
|
|
631
|
+
/** Static validity, one 8-bit value per grid cell; null before init(). */
|
|
632
|
+
get mask() {
|
|
633
|
+
return this.maskCodes;
|
|
634
|
+
}
|
|
635
|
+
/** Luma codes of the presented frame, or null when it is blank. */
|
|
636
|
+
readCodes(e, i) {
|
|
637
|
+
const r = this.reader.read(e, this.width, i);
|
|
638
|
+
return H(r) ? null : We(r, this.manifest.encoding.colorRange);
|
|
639
|
+
}
|
|
640
|
+
/** True when the decoder exposed no pixels; reads back one row only. */
|
|
641
|
+
isBlank(e) {
|
|
642
|
+
return H(this.reader.read(e, this.width, 1));
|
|
643
|
+
}
|
|
644
|
+
init() {
|
|
645
|
+
return this.initPromise ??= this.load().catch((e) => {
|
|
646
|
+
throw this.initPromise = null, e;
|
|
647
|
+
}), this.initPromise;
|
|
648
|
+
}
|
|
649
|
+
/** Abort every pending frame decode; frames already decoded stay cached. */
|
|
650
|
+
cancelAll() {
|
|
651
|
+
for (const e of [...this.pending])
|
|
652
|
+
this.drop(e);
|
|
653
|
+
}
|
|
654
|
+
/** Release the video element and GL context; a later init() reloads them. */
|
|
655
|
+
release() {
|
|
656
|
+
this.cancelAll(), this.frames.clear(), this.video && (this.video.removeAttribute("src"), this.video.load()), this.video = null, this.maskCodes = null, this.presented = !1, this.primed = !1, this.initPromise = null, this.reader.release();
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* One decode per time step, cached and shared by concurrent requests.
|
|
660
|
+
* Aborting `request.signal` rejects this request with an AbortError; the
|
|
661
|
+
* decode goes on while another request waits for it.
|
|
662
|
+
*/
|
|
663
|
+
frame(e, i = {}) {
|
|
664
|
+
const { signal: r } = i;
|
|
665
|
+
if (r?.aborted)
|
|
666
|
+
return Promise.reject(E());
|
|
667
|
+
let n = this.frames.get(e);
|
|
668
|
+
if (n ? (this.frames.delete(e), this.frames.set(e, n)) : n = this.enqueue(e), n.settled)
|
|
669
|
+
return n.promise;
|
|
670
|
+
const s = n;
|
|
671
|
+
return s.waiters++, _(s.promise, r, () => this.leave(s));
|
|
672
|
+
}
|
|
673
|
+
enqueue(e) {
|
|
674
|
+
const i = new AbortController(), r = this.queue.then(
|
|
675
|
+
() => i.signal.aborted ? Promise.reject(E()) : this.decode(e, i.signal)
|
|
676
|
+
);
|
|
677
|
+
this.queue = r.catch(() => {
|
|
678
|
+
});
|
|
679
|
+
const n = {
|
|
680
|
+
index: e,
|
|
681
|
+
controller: i,
|
|
682
|
+
promise: _(r, i.signal),
|
|
683
|
+
waiters: 0,
|
|
684
|
+
settled: !1
|
|
685
|
+
};
|
|
686
|
+
for (this.pending.add(n), n.promise.then(
|
|
687
|
+
() => {
|
|
688
|
+
n.settled = !0, this.pending.delete(n);
|
|
689
|
+
},
|
|
690
|
+
() => {
|
|
691
|
+
n.settled = !0, this.pending.delete(n), this.frames.get(e) === n && this.frames.delete(e);
|
|
692
|
+
}
|
|
693
|
+
), this.frames.set(e, n); this.frames.size > this.cacheSize; )
|
|
694
|
+
this.frames.delete(this.frames.keys().next().value);
|
|
695
|
+
return n;
|
|
696
|
+
}
|
|
697
|
+
/** A request left; drop the decode when it was the last one waiting. */
|
|
698
|
+
leave(e) {
|
|
699
|
+
e.waiters--, e.waiters <= 0 && !e.settled && this.drop(e);
|
|
700
|
+
}
|
|
701
|
+
drop(e) {
|
|
702
|
+
e.controller.abort(), this.pending.delete(e), this.frames.get(e.index) === e && this.frames.delete(e.index);
|
|
703
|
+
}
|
|
704
|
+
async load() {
|
|
705
|
+
const [e] = await Promise.all([this.loadMask(), this.loadVideo()]);
|
|
706
|
+
this.maskCodes = e;
|
|
707
|
+
}
|
|
708
|
+
async loadMask() {
|
|
709
|
+
const { url: e, width: i, height: r } = this.manifest.mask, n = new Image();
|
|
710
|
+
n.crossOrigin = "anonymous", n.src = e;
|
|
711
|
+
try {
|
|
712
|
+
await n.decode();
|
|
713
|
+
} catch {
|
|
714
|
+
throw new Error(`Failed to load GeoVideo mask: ${e}`);
|
|
715
|
+
}
|
|
716
|
+
if (n.naturalWidth !== i || n.naturalHeight !== r)
|
|
717
|
+
throw new Error(
|
|
718
|
+
`GeoVideo mask dimensions ${n.naturalWidth}x${n.naturalHeight} do not match manifest ${i}x${r}`
|
|
719
|
+
);
|
|
720
|
+
const s = this.reader.read(n, i, r), o = new Uint8Array(i * r);
|
|
721
|
+
for (let a = 0; a < o.length; a++)
|
|
722
|
+
o[a] = s[a * 4];
|
|
723
|
+
return o;
|
|
724
|
+
}
|
|
725
|
+
async loadVideo() {
|
|
726
|
+
const e = document.createElement("video");
|
|
727
|
+
if (e.crossOrigin = "anonymous", e.muted = !0, e.playsInline = !0, e.preload = "metadata", e.src = this.manifest.media.url, this.video = e, await new Promise((i, r) => {
|
|
728
|
+
e.addEventListener("loadedmetadata", () => i(), {
|
|
729
|
+
once: !0
|
|
730
|
+
}), e.addEventListener(
|
|
731
|
+
"error",
|
|
732
|
+
() => r(
|
|
733
|
+
new Error(
|
|
734
|
+
`Failed to load GeoVideo media: ${this.manifest.media.url}`
|
|
735
|
+
)
|
|
736
|
+
),
|
|
737
|
+
{ once: !0 }
|
|
738
|
+
);
|
|
739
|
+
}), e.videoWidth !== this.manifest.media.width || e.videoHeight !== this.manifest.media.height)
|
|
740
|
+
throw new Error(
|
|
741
|
+
`GeoVideo media dimensions ${e.videoWidth}x${e.videoHeight} do not match manifest ${this.manifest.media.width}x${this.manifest.media.height}`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
/** Seek to a frame and wait until it is presented. */
|
|
745
|
+
async presentFrame(e, i) {
|
|
746
|
+
if (this.presented && Math.abs(e.currentTime - i) <= 1e-3 && !e.seeking)
|
|
747
|
+
return;
|
|
748
|
+
const r = je(e), n = M(e, "seeked");
|
|
749
|
+
e.currentTime = i, await n, e.readyState < HTMLMediaElement.HAVE_CURRENT_DATA && await Promise.race([M(e, "loadeddata"), M(e, "canplay")]), await r, this.presented = !0;
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Some mobile browsers expose no decoded pixels to WebGL until playback
|
|
753
|
+
* started once. A muted play/pause primes the decoder, only when needed:
|
|
754
|
+
* playback would otherwise buffer far beyond the requested frame.
|
|
755
|
+
*/
|
|
756
|
+
async primeDecoder(e) {
|
|
757
|
+
this.primed = !0;
|
|
758
|
+
try {
|
|
759
|
+
await e.play();
|
|
760
|
+
} catch {
|
|
761
|
+
}
|
|
762
|
+
e.pause(), this.presented = !1;
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Seek and capture one step. `signal` aborts it between steps; a seek
|
|
766
|
+
* already started completes first, keeping the queue serial.
|
|
767
|
+
*/
|
|
768
|
+
async decode(e, i) {
|
|
769
|
+
if (i.aborted)
|
|
770
|
+
throw E();
|
|
771
|
+
await this.init();
|
|
772
|
+
const r = this.video, n = this.maskCodes;
|
|
773
|
+
if (i.aborted || !r || !n)
|
|
774
|
+
throw E();
|
|
775
|
+
const s = this.manifest.timeline.kind === "snapshot-loop" ? e / this.manifest.media.fps : $e(this.manifest, this.times[e]);
|
|
776
|
+
if (await this.presentFrame(r, s), i.aborted || r !== this.video)
|
|
777
|
+
throw E();
|
|
778
|
+
const o = this.height * this.planes;
|
|
779
|
+
let a = await this.capture(r, o, n);
|
|
780
|
+
if (a === null && !this.primed) {
|
|
781
|
+
if (await this.primeDecoder(r), await this.presentFrame(r, s), i.aborted || r !== this.video)
|
|
782
|
+
throw E();
|
|
783
|
+
a = await this.capture(r, o, n);
|
|
784
|
+
}
|
|
785
|
+
if (a === null)
|
|
786
|
+
throw new Error("GeoVideo frame could not be read back from the decoder");
|
|
787
|
+
return a;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function Ke(t) {
|
|
791
|
+
return {
|
|
792
|
+
codeRange: [t.codeMin / 255, t.codeMax / 255],
|
|
793
|
+
valueRange: [t.valueMin, t.valueMax],
|
|
794
|
+
transfer: t.transfer === "log10-1p9" ? 1 : 0
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
class ze extends J {
|
|
798
|
+
constructor(e, i = {}) {
|
|
799
|
+
if (e.encoding.kind !== "scalar-luma")
|
|
800
|
+
throw new Error("GeoVideoScalarSource requires a scalar-luma manifest");
|
|
801
|
+
super(e, 1, i);
|
|
802
|
+
}
|
|
803
|
+
async capture(e) {
|
|
804
|
+
return this.isBlank(e) ? null : createImageBitmap(e, {
|
|
805
|
+
premultiplyAlpha: "none",
|
|
806
|
+
colorSpaceConversion: "none"
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function R(t, e) {
|
|
811
|
+
if (e <= t[0])
|
|
812
|
+
return { index: 0, fraction: 0 };
|
|
813
|
+
const i = t.length - 1;
|
|
814
|
+
if (e >= t[i])
|
|
815
|
+
return { index: i, fraction: 0 };
|
|
816
|
+
let r = 0, n = i;
|
|
817
|
+
for (; n - r > 1; ) {
|
|
818
|
+
const s = r + n >> 1;
|
|
819
|
+
t[s] <= e ? r = s : n = s;
|
|
820
|
+
}
|
|
821
|
+
return { index: r, fraction: (e - t[r]) / (t[n] - t[r]) };
|
|
822
|
+
}
|
|
823
|
+
function N(t, e) {
|
|
824
|
+
const { index: i, fraction: r } = R(t, e);
|
|
825
|
+
return i + r;
|
|
826
|
+
}
|
|
827
|
+
function Ze(t, e) {
|
|
828
|
+
const i = Math.max(0, Math.min(t.length - 1, Math.floor(e))), r = Math.min(i + 1, t.length - 1);
|
|
829
|
+
return t[i] + (t[r] - t[i]) * Math.max(0, e - i);
|
|
830
|
+
}
|
|
831
|
+
function Je(t, e) {
|
|
832
|
+
let i = (t - e.codeMin) / (e.codeMax - e.codeMin) * 2 - 1;
|
|
833
|
+
return i = Math.max(-1, Math.min(1, i)), e.transfer === "sqrt" && (i = Math.sign(i) * i * i), i * e.valueDomain;
|
|
834
|
+
}
|
|
835
|
+
function Qe(t, e, i, r) {
|
|
836
|
+
const n = e.length;
|
|
837
|
+
if (t.length !== 2 * n)
|
|
838
|
+
throw new Error(
|
|
839
|
+
`GeoVideo vector frame has ${t.length} codes, expected ${2 * n}`
|
|
840
|
+
);
|
|
841
|
+
const s = new Float32Array(256);
|
|
842
|
+
for (let c = 0; c < 256; c++)
|
|
843
|
+
s[c] = Je(c, r);
|
|
844
|
+
const o = i * 255, a = new Float32Array(n), d = new Float32Array(n);
|
|
845
|
+
for (let c = 0; c < n; c++) {
|
|
846
|
+
const l = e[c] >= o;
|
|
847
|
+
a[c] = l ? s[t[c]] : NaN, d[c] = l ? s[t[n + c]] : NaN;
|
|
848
|
+
}
|
|
849
|
+
return { u: a, v: d };
|
|
850
|
+
}
|
|
851
|
+
class et extends J {
|
|
852
|
+
constructor(e, i = {}) {
|
|
853
|
+
if (e.encoding.kind !== "vector-luma")
|
|
854
|
+
throw new Error("GeoVideoVectorSource requires a vector-luma manifest");
|
|
855
|
+
super(e, 2, i), this.encoding = e.encoding;
|
|
856
|
+
const [r, n, s, o] = e.bounds, a = (s - r) / this.width, d = (o - n) / this.height;
|
|
857
|
+
this.coords = {
|
|
858
|
+
time: this.times,
|
|
859
|
+
vertical: new Float32Array([0]),
|
|
860
|
+
latitude: Float32Array.from(
|
|
861
|
+
{ length: this.height },
|
|
862
|
+
(c, l) => o - (l + 0.5) * d
|
|
863
|
+
),
|
|
864
|
+
longitude: Float32Array.from(
|
|
865
|
+
{ length: this.width },
|
|
866
|
+
(c, l) => r + (l + 0.5) * a
|
|
867
|
+
)
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
getValueDomain() {
|
|
871
|
+
return this.encoding.valueDomain;
|
|
872
|
+
}
|
|
873
|
+
getCoords() {
|
|
874
|
+
return this.coords;
|
|
875
|
+
}
|
|
876
|
+
getDimensions() {
|
|
877
|
+
return ["time", "latitude", "longitude"];
|
|
878
|
+
}
|
|
879
|
+
getChunkShape() {
|
|
880
|
+
return [1, this.height, this.width];
|
|
881
|
+
}
|
|
882
|
+
findDepthIndex() {
|
|
883
|
+
return 0;
|
|
884
|
+
}
|
|
885
|
+
findTimeIndex(e) {
|
|
886
|
+
const i = typeof e == "number" ? e : new Date(e).getTime(), r = this.coords.time;
|
|
887
|
+
let n = 0;
|
|
888
|
+
for (let s = 1; s < r.length; s++)
|
|
889
|
+
Math.abs(r[s] - i) < Math.abs(r[n] - i) && (n = s);
|
|
890
|
+
return n;
|
|
891
|
+
}
|
|
892
|
+
getChunksForBounds(e, i, r) {
|
|
893
|
+
const { latitude: n, longitude: s } = this.coords;
|
|
894
|
+
return [
|
|
895
|
+
{
|
|
896
|
+
timeIdx: i,
|
|
897
|
+
depthIdx: r,
|
|
898
|
+
latIdx: 0,
|
|
899
|
+
lonIdx: 0,
|
|
900
|
+
latRange: [n[0], n[this.height - 1]],
|
|
901
|
+
lonRange: [s[0], s[this.width - 1]],
|
|
902
|
+
latSize: this.height,
|
|
903
|
+
lonSize: this.width
|
|
904
|
+
}
|
|
905
|
+
];
|
|
906
|
+
}
|
|
907
|
+
async fetchSpatialChunkResult(e, i) {
|
|
908
|
+
const r = await this.frame(i.timeIndex);
|
|
909
|
+
return {
|
|
910
|
+
data: e === "v" ? r.v : r.u,
|
|
911
|
+
missing: !1,
|
|
912
|
+
url: this.manifest.media.url
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
/** u/v codes are written into luma: read back, then decoded on the CPU. */
|
|
916
|
+
async capture(e, i, r) {
|
|
917
|
+
const n = this.readCodes(e, i);
|
|
918
|
+
return n ? Qe(
|
|
919
|
+
n,
|
|
920
|
+
r,
|
|
921
|
+
this.manifest.mask.threshold,
|
|
922
|
+
this.encoding
|
|
923
|
+
) : null;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
function tt(t, e, i = {}) {
|
|
927
|
+
const [r, n] = i.range ?? [-1 / 0, 1 / 0], s = [...t].sort((a, d) => a.start - d.start), o = s.find(
|
|
928
|
+
(a) => a.start > e && a.start <= n
|
|
929
|
+
) ?? (i.loop ? s.find(
|
|
930
|
+
(a) => a.end >= r && a.start <= n
|
|
931
|
+
) : void 0);
|
|
932
|
+
return o ? { chunk: o, time: Math.max(o.start, r) } : null;
|
|
933
|
+
}
|
|
934
|
+
function it(t, e) {
|
|
935
|
+
let i = null, r = -1 / 0;
|
|
936
|
+
for (const n of e) {
|
|
937
|
+
const s = Math.min(t.end, n.end) - Math.max(t.start, n.start);
|
|
938
|
+
Number.isFinite(s) && s >= 0 && s > r && (i = n, r = s);
|
|
939
|
+
}
|
|
940
|
+
return i;
|
|
941
|
+
}
|
|
942
|
+
function rt(t) {
|
|
943
|
+
return {
|
|
944
|
+
width: t.mask.width,
|
|
945
|
+
height: t.mask.height,
|
|
946
|
+
bounds: t.bounds,
|
|
947
|
+
rowOrder: "north-to-south"
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
function Q(t, e = {}) {
|
|
951
|
+
return t.encoding.kind === "vector-luma" ? new et(t, e) : new ze(t, e);
|
|
952
|
+
}
|
|
953
|
+
const nt = 3, st = 3, at = 0.6, ot = 1.5;
|
|
954
|
+
function ct(t) {
|
|
955
|
+
const e = new AbortController();
|
|
956
|
+
if (!t)
|
|
957
|
+
return { controller: e, unlink: () => {
|
|
958
|
+
} };
|
|
959
|
+
const i = () => e.abort();
|
|
960
|
+
return t.aborted && i(), t.addEventListener("abort", i, { once: !0 }), {
|
|
961
|
+
controller: e,
|
|
962
|
+
unlink: () => t.removeEventListener("abort", i)
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
class Bt {
|
|
966
|
+
constructor(e) {
|
|
967
|
+
this.manifests = /* @__PURE__ */ new Map(), this.open = /* @__PURE__ */ new Map(), this.id = e.id, this.chunks = e.chunks.filter(
|
|
968
|
+
(i) => Number.isFinite(i.start) && Number.isFinite(i.end) && i.end >= i.start
|
|
969
|
+
).sort((i, r) => i.start - r.start), this.maxOpenChunks = Math.max(2, e.maxOpenChunks ?? nt), this.frameCacheSize = Math.max(
|
|
970
|
+
1,
|
|
971
|
+
e.frameCacheSize ?? st
|
|
972
|
+
), this.prefetchAt = e.prefetchAt ?? at, this.maxGap = e.maxGap, this.loadManifest = e.loadManifest ?? qe, this.createFrameSource = e.createFrameSource ?? ((i, r) => Q(
|
|
973
|
+
i,
|
|
974
|
+
r
|
|
975
|
+
));
|
|
976
|
+
}
|
|
977
|
+
/** First and last sample of the archive, or null without chunks. */
|
|
978
|
+
get range() {
|
|
979
|
+
const { chunks: e } = this;
|
|
980
|
+
return e.length ? [e[0].start, Math.max(...e.map((i) => i.end))] : null;
|
|
981
|
+
}
|
|
982
|
+
/** The chunk whose sample range holds `time`, or null. */
|
|
983
|
+
chunkAt(e) {
|
|
984
|
+
const i = this.chunkIndexAt(e), r = i >= 0 ? this.chunks[i] : null;
|
|
985
|
+
return r && e <= r.end ? r : null;
|
|
986
|
+
}
|
|
987
|
+
/** Every step time of the archive; loads all chunk manifests. */
|
|
988
|
+
async steps(e = {}) {
|
|
989
|
+
if (e.signal?.aborted)
|
|
990
|
+
throw E();
|
|
991
|
+
const i = await _(
|
|
992
|
+
Promise.all(this.chunks.map((r) => this.manifest(r))),
|
|
993
|
+
e.signal
|
|
994
|
+
);
|
|
995
|
+
return Float64Array.from(i.flatMap(w));
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* The frames bracketing `time`, decoded; null out of coverage (before the
|
|
999
|
+
* first step, after the last, or in a gap wider than `maxGap`).
|
|
1000
|
+
*/
|
|
1001
|
+
async sample(e, i = {}) {
|
|
1002
|
+
const { signal: r } = i, n = i.mode ?? "interpolate";
|
|
1003
|
+
if (r?.aborted)
|
|
1004
|
+
throw E();
|
|
1005
|
+
const s = this.chunkIndexAt(e);
|
|
1006
|
+
if (s < 0)
|
|
1007
|
+
return null;
|
|
1008
|
+
const o = [], { controller: a, unlink: d } = ct(r);
|
|
1009
|
+
try {
|
|
1010
|
+
const c = this.acquire(this.chunks[s], o), l = await _(c.ready, a.signal), { times: h } = l;
|
|
1011
|
+
let f;
|
|
1012
|
+
if (e <= h[h.length - 1]) {
|
|
1013
|
+
const { index: b, fraction: g } = R(h, e);
|
|
1014
|
+
f = n === "discrete" || g === 0 ? [c, b, c, b, 0] : [c, b, c, b + 1, g];
|
|
1015
|
+
} else {
|
|
1016
|
+
const b = this.chunks[s + 1];
|
|
1017
|
+
if (!b)
|
|
1018
|
+
return null;
|
|
1019
|
+
const g = this.acquire(b, o), V = await _(g.ready, a.signal), x = h.length - 1, U = h[x], P = V.times[0];
|
|
1020
|
+
if (P - U > this.gapLimit(h, V.times))
|
|
1021
|
+
return null;
|
|
1022
|
+
f = n === "discrete" ? [c, x, c, x, 0] : e >= P ? [g, 0, g, 0, 0] : [c, x, g, 0, (e - U) / (P - U)];
|
|
1023
|
+
}
|
|
1024
|
+
const [u, m, T, v, y] = f, L = u === T && m === v, C = [this.frame(u, m, a.signal)];
|
|
1025
|
+
L || C.push(this.frame(T, v, a.signal));
|
|
1026
|
+
let S;
|
|
1027
|
+
try {
|
|
1028
|
+
S = await Promise.all(C);
|
|
1029
|
+
} catch (b) {
|
|
1030
|
+
throw a.abort(), b;
|
|
1031
|
+
}
|
|
1032
|
+
return this.prefetchAfter(s, e), {
|
|
1033
|
+
time: e,
|
|
1034
|
+
a: S[0],
|
|
1035
|
+
b: S[L ? 0 : 1],
|
|
1036
|
+
mix: y
|
|
1037
|
+
};
|
|
1038
|
+
} finally {
|
|
1039
|
+
d();
|
|
1040
|
+
for (const c of o)
|
|
1041
|
+
c.users--;
|
|
1042
|
+
this.trim();
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
/** Open `chunk` and decode its first frame ahead of a request. */
|
|
1046
|
+
prefetch(e) {
|
|
1047
|
+
if (this.open.has(e.key))
|
|
1048
|
+
return;
|
|
1049
|
+
const i = this.touch(e), r = new AbortController();
|
|
1050
|
+
i.prefetch = r, i.ready.then(({ source: n }) => n.frame(0, { signal: r.signal })).catch(() => {
|
|
1051
|
+
}).finally(() => {
|
|
1052
|
+
i.prefetch === r && (i.prefetch = null);
|
|
1053
|
+
}), this.trim();
|
|
1054
|
+
}
|
|
1055
|
+
/** Release every open chunk source; later requests reopen them. */
|
|
1056
|
+
release() {
|
|
1057
|
+
for (const e of [...this.open.values()])
|
|
1058
|
+
this.close(e);
|
|
1059
|
+
}
|
|
1060
|
+
chunkIndexAt(e) {
|
|
1061
|
+
let i = 0, r = this.chunks.length - 1, n = -1;
|
|
1062
|
+
for (; i <= r; ) {
|
|
1063
|
+
const s = i + r >> 1;
|
|
1064
|
+
this.chunks[s].start <= e ? (n = s, i = s + 1) : r = s - 1;
|
|
1065
|
+
}
|
|
1066
|
+
return n;
|
|
1067
|
+
}
|
|
1068
|
+
gapLimit(e, i) {
|
|
1069
|
+
if (this.maxGap !== void 0)
|
|
1070
|
+
return this.maxGap;
|
|
1071
|
+
const r = [
|
|
1072
|
+
e.length > 1 ? e[e.length - 1] - e[e.length - 2] : 0,
|
|
1073
|
+
i.length > 1 ? i[1] - i[0] : 0
|
|
1074
|
+
], n = Math.max(...r);
|
|
1075
|
+
return n > 0 ? n * ot : -1 / 0;
|
|
1076
|
+
}
|
|
1077
|
+
prefetchAfter(e, i) {
|
|
1078
|
+
const r = this.chunks[e], n = this.chunks[e + 1], s = r.end - r.start, o = s > 0 ? (i - r.start) / s : 1;
|
|
1079
|
+
n && o > this.prefetchAt && this.prefetch(n);
|
|
1080
|
+
}
|
|
1081
|
+
manifest(e) {
|
|
1082
|
+
let i = this.manifests.get(e.manifestUrl);
|
|
1083
|
+
return i || (i = this.loadManifest(e.manifestUrl), this.manifests.set(e.manifestUrl, i), i.catch(() => {
|
|
1084
|
+
this.manifests.get(e.manifestUrl) === i && this.manifests.delete(e.manifestUrl);
|
|
1085
|
+
})), i;
|
|
1086
|
+
}
|
|
1087
|
+
/** The open chunk, pinned for a running sample listed in `used`. */
|
|
1088
|
+
acquire(e, i) {
|
|
1089
|
+
const r = this.touch(e);
|
|
1090
|
+
return r.users++, i.push(r), r;
|
|
1091
|
+
}
|
|
1092
|
+
/** The open chunk, opened when needed, most recently used; not pinned. */
|
|
1093
|
+
touch(e) {
|
|
1094
|
+
let i = this.open.get(e.key);
|
|
1095
|
+
if (i)
|
|
1096
|
+
this.open.delete(e.key);
|
|
1097
|
+
else {
|
|
1098
|
+
const r = {
|
|
1099
|
+
chunk: e,
|
|
1100
|
+
ready: null,
|
|
1101
|
+
opened: null,
|
|
1102
|
+
users: 0,
|
|
1103
|
+
prefetch: null,
|
|
1104
|
+
released: !1
|
|
1105
|
+
};
|
|
1106
|
+
r.ready = this.manifest(e).then((n) => {
|
|
1107
|
+
if (r.released)
|
|
1108
|
+
throw E();
|
|
1109
|
+
return r.opened = {
|
|
1110
|
+
manifest: n,
|
|
1111
|
+
times: Float64Array.from(w(n)),
|
|
1112
|
+
grid: rt(n),
|
|
1113
|
+
source: this.createFrameSource(n, {
|
|
1114
|
+
cacheSize: this.frameCacheSize
|
|
1115
|
+
})
|
|
1116
|
+
}, r.opened;
|
|
1117
|
+
}), r.ready.catch(() => {
|
|
1118
|
+
this.open.get(e.key) === r && this.open.delete(e.key);
|
|
1119
|
+
}), i = r;
|
|
1120
|
+
}
|
|
1121
|
+
return this.open.set(e.key, i), i;
|
|
1122
|
+
}
|
|
1123
|
+
async frame(e, i, r) {
|
|
1124
|
+
const { manifest: n, times: s, grid: o, source: a } = await _(
|
|
1125
|
+
e.ready,
|
|
1126
|
+
r
|
|
1127
|
+
);
|
|
1128
|
+
if (e.released)
|
|
1129
|
+
throw E();
|
|
1130
|
+
const d = await a.frame(i, { signal: r });
|
|
1131
|
+
return {
|
|
1132
|
+
key: `${this.id}/${e.chunk.key}/${i}`,
|
|
1133
|
+
chunk: e.chunk,
|
|
1134
|
+
step: i,
|
|
1135
|
+
time: s[i],
|
|
1136
|
+
data: d,
|
|
1137
|
+
grid: o,
|
|
1138
|
+
manifest: n
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
/** Release least recently used chunks beyond the bound, unless in use. */
|
|
1142
|
+
trim() {
|
|
1143
|
+
let e = this.open.size - this.maxOpenChunks;
|
|
1144
|
+
for (const i of [...this.open.values()]) {
|
|
1145
|
+
if (e <= 0)
|
|
1146
|
+
return;
|
|
1147
|
+
i.users === 0 && (this.close(i), e--);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
close(e) {
|
|
1151
|
+
e.released = !0, e.prefetch?.abort(), e.prefetch = null, e.opened?.source.release(), e.opened = null, this.open.get(e.chunk.key) === e && this.open.delete(e.chunk.key);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
const dt = {
|
|
1155
|
+
a: "u_vel0",
|
|
1156
|
+
b: "u_vel1",
|
|
1157
|
+
mix: "u_vel_mix",
|
|
1158
|
+
bounds: "u_bounds"
|
|
1159
|
+
}, lt = {
|
|
1160
|
+
a: "u_tint0",
|
|
1161
|
+
b: "u_tint1",
|
|
1162
|
+
mix: "u_tint_mix",
|
|
1163
|
+
bounds: "u_tint_bounds"
|
|
1164
|
+
};
|
|
1165
|
+
function ht(t) {
|
|
1166
|
+
const e = new Float32Array(t.u.length * 4);
|
|
1167
|
+
for (let i = 0; i < t.u.length; i++) {
|
|
1168
|
+
const r = t.u[i], n = t.v[i];
|
|
1169
|
+
Number.isNaN(r) || Number.isNaN(n) || (e[i * 4] = r, e[i * 4 + 1] = n, e[i * 4 + 2] = 1);
|
|
1170
|
+
}
|
|
1171
|
+
return e;
|
|
1172
|
+
}
|
|
1173
|
+
class ut {
|
|
1174
|
+
constructor(e, i, r = {}) {
|
|
1175
|
+
this.gl = e, this.manifest = i, this.options = r, this.textures = /* @__PURE__ */ new Map(), this.pending = /* @__PURE__ */ new Map(), this.mask = null, this.pair = null, this.destroyed = !1, this.linearFilter = !0, this.uploadedFrames = 0, this.lastUploadDurationMs = 0, this.vector = i.encoding.kind === "vector-luma", this.source = Q(i, { cacheSize: 2 }), this.uniforms = this.vector ? dt : lt;
|
|
1176
|
+
const n = w(i);
|
|
1177
|
+
this.times = r.animateSnapshot && i.timeline.kind === "snapshot-loop" ? Array.from(
|
|
1178
|
+
{
|
|
1179
|
+
length: Math.max(
|
|
1180
|
+
1,
|
|
1181
|
+
Math.round(i.media.durationSeconds * i.media.fps)
|
|
1182
|
+
)
|
|
1183
|
+
},
|
|
1184
|
+
(s, o) => n[0] + o * 1e3 / i.media.fps
|
|
1185
|
+
) : n, this.bounds = i.bounds;
|
|
1186
|
+
}
|
|
1187
|
+
get currentPair() {
|
|
1188
|
+
return this.pair;
|
|
1189
|
+
}
|
|
1190
|
+
/** Fixed catalog color domain of a scalar field. */
|
|
1191
|
+
get domain() {
|
|
1192
|
+
return this.manifest.style.colorDomain;
|
|
1193
|
+
}
|
|
1194
|
+
/** Top of the color ramp and of the particle speed normalization. */
|
|
1195
|
+
get maxSpeed() {
|
|
1196
|
+
const { encoding: e, style: i } = this.manifest;
|
|
1197
|
+
return e.kind === "vector-luma" ? e.valueDomain : i.colorDomain[1];
|
|
1198
|
+
}
|
|
1199
|
+
/** Resolves once the step at `ms` is uploaded. */
|
|
1200
|
+
async prime(e) {
|
|
1201
|
+
const { index: i } = R(this.times, e);
|
|
1202
|
+
await this.request(i);
|
|
1203
|
+
}
|
|
1204
|
+
/** True once the frames interpolated at `ms` are uploaded. */
|
|
1205
|
+
ready(e) {
|
|
1206
|
+
const { index: i, fraction: r } = R(this.times, e);
|
|
1207
|
+
return this.textures.has(i) && (r === 0 || this.textures.has(i + 1));
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Latest wins while scrubbing: drop queued decodes outside the frames at
|
|
1211
|
+
* `ms`, then request those first.
|
|
1212
|
+
*/
|
|
1213
|
+
focus(e) {
|
|
1214
|
+
const { index: i } = R(this.times, e), r = Math.min(i + 1, this.times.length - 1);
|
|
1215
|
+
[...this.pending.keys()].some(
|
|
1216
|
+
(s) => s !== i && s !== r
|
|
1217
|
+
) && (this.source.cancelAll(), this.pending.clear());
|
|
1218
|
+
for (const s of [i, r])
|
|
1219
|
+
this.request(s).catch(() => {
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Bracketing textures and their mix, retaining the last complete pair
|
|
1224
|
+
* while a requested frame decodes.
|
|
1225
|
+
*/
|
|
1226
|
+
sample(e, i = !0) {
|
|
1227
|
+
const { index: r, fraction: n } = R(this.times, e), s = Math.min(r + 1, this.times.length - 1);
|
|
1228
|
+
[...this.pending.keys()].some((c) => c < r || c > s + 1) && (this.source.cancelAll(), this.pending.clear());
|
|
1229
|
+
for (const c of [r, s, s + 1])
|
|
1230
|
+
c < this.times.length && this.request(c).catch(() => {
|
|
1231
|
+
});
|
|
1232
|
+
const o = this.textures.get(r), a = this.textures.get(s), d = i ? n : 0;
|
|
1233
|
+
return o && (d === 0 || a) && (this.pair = [o, d === 0 ? o : a, d]), this.evict(r), this.pair;
|
|
1234
|
+
}
|
|
1235
|
+
/** Switch spatial filtering without changing the decoded-step cache. */
|
|
1236
|
+
setFilter(e) {
|
|
1237
|
+
if (this.linearFilter === e)
|
|
1238
|
+
return;
|
|
1239
|
+
this.linearFilter = e;
|
|
1240
|
+
const i = this.gl, r = i.getParameter(
|
|
1241
|
+
i.TEXTURE_BINDING_2D
|
|
1242
|
+
);
|
|
1243
|
+
for (const n of this.textures.values())
|
|
1244
|
+
i.bindTexture(i.TEXTURE_2D, n), i.texParameteri(
|
|
1245
|
+
i.TEXTURE_2D,
|
|
1246
|
+
i.TEXTURE_MIN_FILTER,
|
|
1247
|
+
e ? i.LINEAR : i.NEAREST
|
|
1248
|
+
), i.texParameteri(
|
|
1249
|
+
i.TEXTURE_2D,
|
|
1250
|
+
i.TEXTURE_MAG_FILTER,
|
|
1251
|
+
e ? i.LINEAR : i.NEAREST
|
|
1252
|
+
);
|
|
1253
|
+
i.bindTexture(i.TEXTURE_2D, r);
|
|
1254
|
+
}
|
|
1255
|
+
get maskTexture() {
|
|
1256
|
+
return this.mask;
|
|
1257
|
+
}
|
|
1258
|
+
get cachedFrames() {
|
|
1259
|
+
return this.textures.size;
|
|
1260
|
+
}
|
|
1261
|
+
bind(e, i, r = 0) {
|
|
1262
|
+
if (!this.sample(i) || !this.pair)
|
|
1263
|
+
return !1;
|
|
1264
|
+
const n = this.uniforms;
|
|
1265
|
+
if (e.texture(n.a, r, this.pair[0]), e.texture(n.b, r + 1, this.pair[1]), e.float(n.mix, this.pair[2]), e.vec4(n.bounds, this.bounds), this.mask && this.manifest.encoding.kind === "scalar-luma") {
|
|
1266
|
+
const { codeRange: s, valueRange: o, transfer: a } = Ke(
|
|
1267
|
+
this.manifest.encoding
|
|
1268
|
+
);
|
|
1269
|
+
e.texture("u_tint_mask", r + 2, this.mask), e.vec2("u_code_range", ...s), e.vec2("u_value_range", ...o), e.float("u_scalar_transfer", a);
|
|
1270
|
+
}
|
|
1271
|
+
return !0;
|
|
1272
|
+
}
|
|
1273
|
+
destroy() {
|
|
1274
|
+
this.destroyed = !0, this.source.cancelAll(), this.source.release();
|
|
1275
|
+
for (const e of this.textures.values())
|
|
1276
|
+
this.gl.deleteTexture(e);
|
|
1277
|
+
this.textures.clear(), this.gl.deleteTexture(this.mask), this.mask = null, this.pair = null;
|
|
1278
|
+
}
|
|
1279
|
+
request(e) {
|
|
1280
|
+
if (this.textures.has(e))
|
|
1281
|
+
return Promise.resolve();
|
|
1282
|
+
const i = this.pending.get(e);
|
|
1283
|
+
if (i)
|
|
1284
|
+
return i;
|
|
1285
|
+
const r = this.source.frame(e).then((n) => {
|
|
1286
|
+
if (!this.destroyed && this.pending.get(e) === r && !this.textures.has(e)) {
|
|
1287
|
+
const s = performance.now();
|
|
1288
|
+
this.textures.set(e, this.uploadPreservingState(n)), this.uploadedFrames++, this.lastUploadDurationMs = performance.now() - s, this.options.onReady?.();
|
|
1289
|
+
}
|
|
1290
|
+
}).catch((n) => {
|
|
1291
|
+
throw !this.destroyed && this.pending.get(e) === r && !(n instanceof Error && n.name === "AbortError") && this.options.onError?.(n), n;
|
|
1292
|
+
}).finally(() => {
|
|
1293
|
+
this.pending.get(e) === r && this.pending.delete(e);
|
|
1294
|
+
});
|
|
1295
|
+
return this.pending.set(e, r), r;
|
|
1296
|
+
}
|
|
1297
|
+
uploadPreservingState(e) {
|
|
1298
|
+
const i = this.gl, r = i.getParameter(
|
|
1299
|
+
i.TEXTURE_BINDING_2D
|
|
1300
|
+
), n = [
|
|
1301
|
+
i.UNPACK_FLIP_Y_WEBGL,
|
|
1302
|
+
i.UNPACK_PREMULTIPLY_ALPHA_WEBGL,
|
|
1303
|
+
i.UNPACK_COLORSPACE_CONVERSION_WEBGL,
|
|
1304
|
+
i.UNPACK_ALIGNMENT
|
|
1305
|
+
], s = n.map((o) => i.getParameter(o));
|
|
1306
|
+
try {
|
|
1307
|
+
return this.upload(e);
|
|
1308
|
+
} finally {
|
|
1309
|
+
i.bindTexture(i.TEXTURE_2D, r), n.forEach((o, a) => i.pixelStorei(o, s[a]));
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
upload(e) {
|
|
1313
|
+
const i = this.gl, { width: r, height: n } = this.source, s = this.gridTexture();
|
|
1314
|
+
return this.vector ? i.texImage2D(
|
|
1315
|
+
i.TEXTURE_2D,
|
|
1316
|
+
0,
|
|
1317
|
+
i.RGBA16F,
|
|
1318
|
+
r,
|
|
1319
|
+
n,
|
|
1320
|
+
0,
|
|
1321
|
+
i.RGBA,
|
|
1322
|
+
i.FLOAT,
|
|
1323
|
+
ht(e)
|
|
1324
|
+
) : (i.pixelStorei(i.UNPACK_FLIP_Y_WEBGL, !1), i.pixelStorei(i.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !1), i.pixelStorei(i.UNPACK_COLORSPACE_CONVERSION_WEBGL, i.NONE), i.texImage2D(
|
|
1325
|
+
i.TEXTURE_2D,
|
|
1326
|
+
0,
|
|
1327
|
+
i.RGBA8,
|
|
1328
|
+
i.RGBA,
|
|
1329
|
+
i.UNSIGNED_BYTE,
|
|
1330
|
+
e
|
|
1331
|
+
), this.mask ??= this.uploadMask()), i.bindTexture(i.TEXTURE_2D, null), s;
|
|
1332
|
+
}
|
|
1333
|
+
/** The source's mask, loaded with its first frame, as an R8 texture. */
|
|
1334
|
+
uploadMask() {
|
|
1335
|
+
const e = this.source.mask;
|
|
1336
|
+
if (!e)
|
|
1337
|
+
return null;
|
|
1338
|
+
const i = this.gl, r = this.gridTexture();
|
|
1339
|
+
return this.options.repeatLongitude === !1 && (i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MIN_FILTER, i.NEAREST), i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MAG_FILTER, i.NEAREST)), i.pixelStorei(i.UNPACK_ALIGNMENT, 1), i.texImage2D(
|
|
1340
|
+
i.TEXTURE_2D,
|
|
1341
|
+
0,
|
|
1342
|
+
i.R8,
|
|
1343
|
+
this.source.width,
|
|
1344
|
+
this.source.height,
|
|
1345
|
+
0,
|
|
1346
|
+
i.RED,
|
|
1347
|
+
i.UNSIGNED_BYTE,
|
|
1348
|
+
e
|
|
1349
|
+
), i.pixelStorei(i.UNPACK_ALIGNMENT, 4), r;
|
|
1350
|
+
}
|
|
1351
|
+
/** A bound, linearly filtered texture over the field grid. */
|
|
1352
|
+
gridTexture() {
|
|
1353
|
+
const e = this.gl, i = e.createTexture();
|
|
1354
|
+
return e.bindTexture(e.TEXTURE_2D, i), e.texParameteri(
|
|
1355
|
+
e.TEXTURE_2D,
|
|
1356
|
+
e.TEXTURE_WRAP_S,
|
|
1357
|
+
this.options.repeatLongitude !== !1 && this.bounds[2] - this.bounds[0] >= 359 ? e.REPEAT : e.CLAMP_TO_EDGE
|
|
1358
|
+
), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(
|
|
1359
|
+
e.TEXTURE_2D,
|
|
1360
|
+
e.TEXTURE_MIN_FILTER,
|
|
1361
|
+
this.linearFilter ? e.LINEAR : e.NEAREST
|
|
1362
|
+
), e.texParameteri(
|
|
1363
|
+
e.TEXTURE_2D,
|
|
1364
|
+
e.TEXTURE_MAG_FILTER,
|
|
1365
|
+
this.linearFilter ? e.LINEAR : e.NEAREST
|
|
1366
|
+
), i;
|
|
1367
|
+
}
|
|
1368
|
+
/** Keep a small window around the playhead. */
|
|
1369
|
+
evict(e) {
|
|
1370
|
+
for (const [i, r] of this.textures) {
|
|
1371
|
+
const n = this.pair && (this.pair[0] === r || this.pair[1] === r);
|
|
1372
|
+
(i < e - 1 || i > e + 3) && !n && (this.gl.deleteTexture(r), this.textures.delete(i));
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
class ft {
|
|
1377
|
+
constructor(e, i) {
|
|
1378
|
+
this.onPlaying = i, this.time = 0, this.endFraction = 0, this.held = !1, this.playingValue = e;
|
|
1379
|
+
}
|
|
1380
|
+
/** Playhead, epoch ms. Assigning it also resets a terminal step's hold. */
|
|
1381
|
+
get ms() {
|
|
1382
|
+
return this.time;
|
|
1383
|
+
}
|
|
1384
|
+
set ms(e) {
|
|
1385
|
+
this.time = e, this.endFraction = 0;
|
|
1386
|
+
}
|
|
1387
|
+
get playing() {
|
|
1388
|
+
return this.playingValue;
|
|
1389
|
+
}
|
|
1390
|
+
setPlaying(e) {
|
|
1391
|
+
this.playingValue !== e && (this.playingValue = e, this.onPlaying(e));
|
|
1392
|
+
}
|
|
1393
|
+
/** Playing and not held. */
|
|
1394
|
+
get advancing() {
|
|
1395
|
+
return this.playingValue && !this.held;
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* Advance in step units, including irregular timelines. Returns true at a
|
|
1399
|
+
* non-looping end. holdLastStep gives the final sample a full step's duration.
|
|
1400
|
+
*/
|
|
1401
|
+
advance(e, i, r, n = [i[0], i[i.length - 1]], s = !1, o = !1) {
|
|
1402
|
+
if (!this.advancing || !i.length)
|
|
1403
|
+
return !1;
|
|
1404
|
+
const a = N(i, n[0]), d = N(i, n[1]), c = d + (o ? 1 : 0);
|
|
1405
|
+
let l = Math.max(a, N(i, this.ms) + this.endFraction) + Math.max(0, e) * r;
|
|
1406
|
+
const h = o ? l >= c : l > c;
|
|
1407
|
+
return h && s && c > a ? l = a + (l - a) % (c - a) : l = Math.min(c, l), this.time = Ze(i, Math.min(d, l)), this.endFraction = Math.max(0, l - d), h && !s;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
function k(t) {
|
|
1411
|
+
const e = w(t);
|
|
1412
|
+
return [e[0], e[e.length - 1]];
|
|
1413
|
+
}
|
|
1414
|
+
function I(t, e) {
|
|
1415
|
+
return e ? [Math.max(t[0], e[0]), Math.min(t[1], e[1])] : t;
|
|
1416
|
+
}
|
|
1417
|
+
function F([t, e], i) {
|
|
1418
|
+
return Math.max(t, Math.min(e, i));
|
|
1419
|
+
}
|
|
1420
|
+
class Xt {
|
|
1421
|
+
constructor(e, i, r) {
|
|
1422
|
+
this.repaint = i, this.emit = r, this.field = null, this.manifest = null, this.gl = null, this.pendingField = null, this.standby = null, this.awaitingNext = !1, this.timeRange = null, this.lastTick = null, this.lastStepTime = null, this.seekId = 0, this.seeking = !1, this.failed = !1, this.exactTime = 0, this.endEmitted = !1, this.destroyed = !1, this.resumePlayback = !1, this.playOnReady = e.autoplay ?? !0, this.loop = e.loop ?? !0, this.playbackRate = e.playbackRate ?? 1, this.stepsPerSecond = e.stepsPerSecond;
|
|
1423
|
+
const n = typeof e.time == "string" ? Date.parse(e.time) : e.time;
|
|
1424
|
+
this.requestedTime = n != null && Number.isFinite(n) ? n : null, this.requestedTimeRange = e.timeRange, this.clock = new ft(
|
|
1425
|
+
!1,
|
|
1426
|
+
(s) => this.emit("playbackChange", s)
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
async load(e, i) {
|
|
1430
|
+
if (this.gl = e, this.manifest = i, this.timeRange = I(
|
|
1431
|
+
k(i),
|
|
1432
|
+
this.requestedTimeRange
|
|
1433
|
+
), this.timeRange[0] > this.timeRange[1])
|
|
1434
|
+
throw new Error(
|
|
1435
|
+
"GeoVideo time range does not overlap the manifest timeline"
|
|
1436
|
+
);
|
|
1437
|
+
this.pendingField = this.createField(i), await this.prepare(this.pendingField);
|
|
1438
|
+
}
|
|
1439
|
+
sample(e) {
|
|
1440
|
+
return this.tick(e), this.field ? this.pendingField || this.seeking ? this.field.currentPair : this.field.sample(
|
|
1441
|
+
this.clock.advancing ? this.clock.ms : this.exactTime,
|
|
1442
|
+
this.clock.advancing && !this.awaitingNext
|
|
1443
|
+
) : null;
|
|
1444
|
+
}
|
|
1445
|
+
get time() {
|
|
1446
|
+
return this.clock.ms;
|
|
1447
|
+
}
|
|
1448
|
+
get preloadedMediaUrl() {
|
|
1449
|
+
return this.standby?.field.manifest.media.url;
|
|
1450
|
+
}
|
|
1451
|
+
setStepsPerSecond(e) {
|
|
1452
|
+
if (!Number.isFinite(e) || e <= 0)
|
|
1453
|
+
throw new Error("GeoVideo steps per second must be positive");
|
|
1454
|
+
this.stepsPerSecond = e;
|
|
1455
|
+
}
|
|
1456
|
+
suspend() {
|
|
1457
|
+
this.resumePlayback = this.isPlaying(), this.pause();
|
|
1458
|
+
}
|
|
1459
|
+
resume() {
|
|
1460
|
+
this.resumePlayback && this.play(), this.resumePlayback = !1;
|
|
1461
|
+
}
|
|
1462
|
+
destroy() {
|
|
1463
|
+
this.destroyed = !0, this.seekId++, this.pendingField?.destroy(), this.pendingField = null, this.discardStandby(), this.field?.destroy(), this.field = null, this.gl = null;
|
|
1464
|
+
}
|
|
1465
|
+
tick(e) {
|
|
1466
|
+
if (!this.field || this.pendingField || this.seeking || this.awaitingNext || !this.clock.advancing) {
|
|
1467
|
+
this.lastTick = null;
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
const i = this.lastTick === null ? 0 : (e - this.lastTick) / 1e3;
|
|
1471
|
+
this.lastTick = e;
|
|
1472
|
+
const r = this.clock.advance(
|
|
1473
|
+
i,
|
|
1474
|
+
this.field.times,
|
|
1475
|
+
this.stepsPerSecond ?? this.playbackRate * z(this.field.manifest),
|
|
1476
|
+
this.playbackRange(this.field),
|
|
1477
|
+
this.loop,
|
|
1478
|
+
!0
|
|
1479
|
+
), { index: n } = R(this.field.times, this.clock.ms), s = this.field.manifest.timeline.kind === "snapshot-loop" ? this.field.times[0] : F(this.timeRange, this.field.times[n]);
|
|
1480
|
+
s !== this.lastStepTime && (this.lastStepTime = s, this.emit("timeChange", s)), r ? this.reachEnd() : this.repaint();
|
|
1481
|
+
}
|
|
1482
|
+
setTime(e) {
|
|
1483
|
+
const i = typeof e == "number" ? e : Date.parse(e);
|
|
1484
|
+
if (!Number.isFinite(i))
|
|
1485
|
+
return;
|
|
1486
|
+
this.requestedTime = i, this.awaitingNext = !1;
|
|
1487
|
+
const r = this.pendingField ?? this.field;
|
|
1488
|
+
r && this.prepare(r).catch((n) => this.reportError(n));
|
|
1489
|
+
}
|
|
1490
|
+
/** Load the requested step first; stale seeks and swaps cannot replace it. */
|
|
1491
|
+
async prepare(e, i) {
|
|
1492
|
+
this.failed = !1;
|
|
1493
|
+
const r = ++this.seekId, n = this.timeRange ?? k(e.manifest), s = i ?? F(n, this.requestedTime ?? n[0]), o = R(e.times, s), a = Math.min(
|
|
1494
|
+
e.times.length - 1,
|
|
1495
|
+
o.index + (o.fraction > 0.5 ? 1 : 0)
|
|
1496
|
+
), d = e.times[a];
|
|
1497
|
+
this.seeking = !0, this.lastTick = null, e.focus(d);
|
|
1498
|
+
try {
|
|
1499
|
+
await e.prime(d);
|
|
1500
|
+
} catch (l) {
|
|
1501
|
+
if (r !== this.seekId)
|
|
1502
|
+
return;
|
|
1503
|
+
throw l;
|
|
1504
|
+
}
|
|
1505
|
+
if (r !== this.seekId || this.destroyed)
|
|
1506
|
+
return;
|
|
1507
|
+
const c = this.pendingField === e;
|
|
1508
|
+
if (c && (this.field?.destroy(), this.field = e, this.pendingField = null, this.lastStepTime = null), this.exactTime = d, this.clock.ms = F(this.playbackRange(e), d), e.sample(d, !1), this.seeking = !1, this.endEmitted = !1, this.repaint(), c) {
|
|
1509
|
+
const l = e.manifest;
|
|
1510
|
+
if (this.emit("loaded", {
|
|
1511
|
+
min: l.style.colorDomain[0],
|
|
1512
|
+
max: l.style.colorDomain[1],
|
|
1513
|
+
unit: l.style.unit ?? "",
|
|
1514
|
+
time: new Date(this.clock.ms).toISOString()
|
|
1515
|
+
}), this.emit("status", { phase: "ready", time: this.clock.ms }), this.playOnReady) {
|
|
1516
|
+
this.playOnReady = !1;
|
|
1517
|
+
const h = this.clock.playing;
|
|
1518
|
+
await this.play(), h && this.emit("playbackChange", !0);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
/** Keep the outgoing pair until the incoming chunk's requested step is ready. */
|
|
1523
|
+
replaceManifest(e, i = {}) {
|
|
1524
|
+
const r = this.replacementTimeRange(e, i);
|
|
1525
|
+
if (!r || !this.gl)
|
|
1526
|
+
return !1;
|
|
1527
|
+
this.playOnReady ||= this.isPlaying(), this.pendingField?.destroy();
|
|
1528
|
+
const n = this.standby;
|
|
1529
|
+
return this.standby = null, n && n.field.manifest.media.url !== e.media.url && n.field.destroy(), this.pendingField = n?.field.manifest.media.url === e.media.url ? n.field : this.createField(e), this.awaitingNext = !1, this.manifest = e, this.requestedTimeRange = i.timeRange, this.timeRange = r, this.requestedTime = i.time ?? r[0], this.loop = i.loop ?? this.loop, this.playbackRate = i.playbackRate ?? this.playbackRate, this.stepsPerSecond = i.stepsPerSecond ?? this.stepsPerSecond, this.lastTick = null, this.prepare(this.pendingField).catch(
|
|
1530
|
+
(s) => this.reportError(s)
|
|
1531
|
+
), !0;
|
|
1532
|
+
}
|
|
1533
|
+
/** Decode the next chunk ahead of its swap, including its static mask. */
|
|
1534
|
+
preloadManifest(e, i = {}) {
|
|
1535
|
+
const r = this.replacementTimeRange(e, i);
|
|
1536
|
+
if (!r || !this.gl || e.media.url === this.manifest?.media.url)
|
|
1537
|
+
return !1;
|
|
1538
|
+
const n = F(r, i.time ?? r[0]);
|
|
1539
|
+
this.standby?.field.manifest.media.url !== e.media.url && (this.discardStandby(), this.standby = { field: this.createField(e), time: n });
|
|
1540
|
+
const s = this.standby;
|
|
1541
|
+
return s.time = n, s.field.prime(n).catch(() => {
|
|
1542
|
+
this.standby === s && this.discardStandby();
|
|
1543
|
+
}), !0;
|
|
1544
|
+
}
|
|
1545
|
+
discardStandby() {
|
|
1546
|
+
this.standby?.field.destroy(), this.standby = null;
|
|
1547
|
+
}
|
|
1548
|
+
/** Clamped time range of a compatible replacement, else null. */
|
|
1549
|
+
replacementTimeRange(e, i) {
|
|
1550
|
+
const r = this.manifest;
|
|
1551
|
+
if (!r || e.encoding.kind !== r.encoding.kind || e.media.width !== r.media.width || e.media.height !== r.media.height || e.mask.width !== r.mask.width || e.mask.height !== r.mask.height)
|
|
1552
|
+
return null;
|
|
1553
|
+
const n = I(
|
|
1554
|
+
k(e),
|
|
1555
|
+
i.timeRange
|
|
1556
|
+
);
|
|
1557
|
+
return n[0] <= n[1] ? n : null;
|
|
1558
|
+
}
|
|
1559
|
+
async play() {
|
|
1560
|
+
if (!this.field || this.pendingField || this.awaitingNext) {
|
|
1561
|
+
this.playOnReady = !0, this.clock.setPlaying(!0);
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
this.failed && this.prepare(this.field, this.clock.ms).catch(
|
|
1565
|
+
(r) => this.reportError(r)
|
|
1566
|
+
);
|
|
1567
|
+
const e = this.endEmitted;
|
|
1568
|
+
this.endEmitted = !1;
|
|
1569
|
+
const i = this.playbackRange(this.field);
|
|
1570
|
+
e && this.setTime(i[0]), this.lastTick = null, this.clock.setPlaying(!0), this.repaint();
|
|
1571
|
+
}
|
|
1572
|
+
pause() {
|
|
1573
|
+
const e = this.playOnReady && !this.clock.playing;
|
|
1574
|
+
if (this.playOnReady = !1, this.awaitingNext = !1, this.clock.setPlaying(!1), this.field && !this.pendingField && !this.seeking && !this.endEmitted) {
|
|
1575
|
+
const { index: i } = R(this.field.times, this.clock.ms);
|
|
1576
|
+
this.prepare(this.field, this.field.times[i]).catch(
|
|
1577
|
+
(r) => this.reportError(r)
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
e && this.emit("playbackChange", !1), this.lastTick = null, this.repaint();
|
|
1581
|
+
}
|
|
1582
|
+
isPlaying() {
|
|
1583
|
+
return this.clock.playing || this.playOnReady;
|
|
1584
|
+
}
|
|
1585
|
+
reachEnd() {
|
|
1586
|
+
this.endEmitted || (this.endEmitted = !0, this.exactTime = this.clock.ms, this.lastTick = null, this.standby ? (this.awaitingNext = !0, this.playOnReady = !0) : this.clock.setPlaying(!1), this.emit("playbackEnd"));
|
|
1587
|
+
}
|
|
1588
|
+
setLoop(e) {
|
|
1589
|
+
this.loop = e;
|
|
1590
|
+
}
|
|
1591
|
+
setPlaybackRate(e) {
|
|
1592
|
+
if (!Number.isFinite(e) || e <= 0)
|
|
1593
|
+
throw new Error("GeoVideo playback rate must be positive");
|
|
1594
|
+
this.playbackRate = e, this.stepsPerSecond = void 0;
|
|
1595
|
+
}
|
|
1596
|
+
setTimeRange(e) {
|
|
1597
|
+
if (!Number.isFinite(e[0]) || !Number.isFinite(e[1]) || e[0] > e[1])
|
|
1598
|
+
throw new Error("Invalid GeoVideo time range");
|
|
1599
|
+
const i = I(
|
|
1600
|
+
this.manifest ? k(this.manifest) : e,
|
|
1601
|
+
e
|
|
1602
|
+
);
|
|
1603
|
+
if (i[0] > i[1])
|
|
1604
|
+
throw new Error(
|
|
1605
|
+
"GeoVideo time range does not overlap the manifest timeline"
|
|
1606
|
+
);
|
|
1607
|
+
this.requestedTimeRange = e, this.timeRange = i, this.setTime(
|
|
1608
|
+
this.pendingField ? this.requestedTime ?? i[0] : this.clock.ms
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1611
|
+
playbackRange(e) {
|
|
1612
|
+
return e.manifest.timeline.kind === "snapshot-loop" ? [e.times[0], e.times[e.times.length - 1]] : this.timeRange ?? [
|
|
1613
|
+
e.times[0],
|
|
1614
|
+
e.times[e.times.length - 1]
|
|
1615
|
+
];
|
|
1616
|
+
}
|
|
1617
|
+
createField(e) {
|
|
1618
|
+
const i = new ut(this.gl, e, {
|
|
1619
|
+
repeatLongitude: !1,
|
|
1620
|
+
animateSnapshot: !0,
|
|
1621
|
+
onError: (r) => {
|
|
1622
|
+
this.field === i && !this.seeking && this.reportError(r);
|
|
1623
|
+
},
|
|
1624
|
+
onReady: () => this.repaint()
|
|
1625
|
+
});
|
|
1626
|
+
return i;
|
|
1627
|
+
}
|
|
1628
|
+
reportError(e) {
|
|
1629
|
+
const i = e instanceof Error ? e : new Error(String(e));
|
|
1630
|
+
i.name === "AbortError" || this.destroyed || (this.playOnReady = !1, this.awaitingNext = !1, this.failed = !0, this.seeking = !0, this.clock.setPlaying(!1), this.emit("status", { phase: "error", error: i }), this.emit("error", i));
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
function G(t) {
|
|
1634
|
+
const { start: e, end: i } = t.temporal ?? {};
|
|
1635
|
+
return {
|
|
1636
|
+
source: t,
|
|
1637
|
+
start: Date.parse(e ?? ""),
|
|
1638
|
+
end: Date.parse(i ?? e ?? "")
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
function Ot(t, e, i = {}) {
|
|
1642
|
+
const r = t.filter((s) => s.id === e.id && s.temporal?.start != null).map(G), n = tt(
|
|
1643
|
+
r,
|
|
1644
|
+
Date.parse(e.temporal?.end ?? ""),
|
|
1645
|
+
i
|
|
1646
|
+
);
|
|
1647
|
+
return n ? { source: n.chunk.source, time: n.time } : null;
|
|
1648
|
+
}
|
|
1649
|
+
function qt(t, e) {
|
|
1650
|
+
return it(G(t), e.map(G))?.source ?? null;
|
|
1651
|
+
}
|
|
1652
|
+
const mt = /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/;
|
|
1653
|
+
function Et(t) {
|
|
1654
|
+
return t instanceof Date ? t.getTime() : typeof t == "number" ? t : new Date(t).getTime();
|
|
1655
|
+
}
|
|
1656
|
+
function Y(t, e) {
|
|
1657
|
+
const i = Et(t);
|
|
1658
|
+
if (!Number.isFinite(i))
|
|
1659
|
+
throw new Error(`Invalid ${e}`);
|
|
1660
|
+
return i;
|
|
1661
|
+
}
|
|
1662
|
+
function ee(t, e) {
|
|
1663
|
+
const i = e.match(mt);
|
|
1664
|
+
if (!i)
|
|
1665
|
+
throw new Error(`Invalid trailing time duration: ${e}`);
|
|
1666
|
+
const r = i.slice(1).map((y) => Number(y ?? 0));
|
|
1667
|
+
if (!r.some((y) => y > 0))
|
|
1668
|
+
throw new Error("Trailing time duration must be positive");
|
|
1669
|
+
const [n, s, o, a, d, c, l] = r, h = new Date(t), f = h.getUTCFullYear() * 12 + h.getUTCMonth() - n * 12 - s, u = Math.floor(f / 12), m = (f % 12 + 12) % 12, T = new Date(Date.UTC(u, m + 1, 0)).getUTCDate();
|
|
1670
|
+
return Date.UTC(
|
|
1671
|
+
u,
|
|
1672
|
+
m,
|
|
1673
|
+
Math.min(h.getUTCDate(), T),
|
|
1674
|
+
h.getUTCHours(),
|
|
1675
|
+
h.getUTCMinutes(),
|
|
1676
|
+
h.getUTCSeconds(),
|
|
1677
|
+
h.getUTCMilliseconds()
|
|
1678
|
+
) - (((o * 7 + a) * 24 + d) * 60 + c) * 6e4 - l * 1e3;
|
|
1679
|
+
}
|
|
1680
|
+
function $t(t, [e, i]) {
|
|
1681
|
+
let r = e, n = i;
|
|
1682
|
+
if (typeof t.trailing == "string" ? r = ee(i, t.trailing) : (t.start != null && (r = Y(t.start, "time range start")), t.end != null && (n = Y(t.end, "time range end"))), r > n)
|
|
1683
|
+
throw new Error("Time range start must not follow its end");
|
|
1684
|
+
return [Math.max(e, r), Math.min(i, n)];
|
|
1685
|
+
}
|
|
1686
|
+
const pt = (t) => typeof t == "object" && t !== null && !Array.isArray(t);
|
|
1687
|
+
function Wt(t, e) {
|
|
1688
|
+
if (!pt(t))
|
|
1689
|
+
throw new Error(`${e} must be an object`);
|
|
1690
|
+
for (const s of Object.keys(t))
|
|
1691
|
+
if (s !== "start" && s !== "end" && s !== "trailing")
|
|
1692
|
+
throw new Error(`${e}.${s} is not a time range key`);
|
|
1693
|
+
if (t.trailing !== void 0) {
|
|
1694
|
+
if (t.start !== void 0 || t.end !== void 0)
|
|
1695
|
+
throw new Error(`${e}.trailing excludes start and end`);
|
|
1696
|
+
if (typeof t.trailing != "string")
|
|
1697
|
+
throw new Error(`${e}.trailing must be an ISO 8601 duration`);
|
|
1698
|
+
return ee(0, t.trailing), { trailing: t.trailing };
|
|
1699
|
+
}
|
|
1700
|
+
const i = (s) => {
|
|
1701
|
+
const o = t[s];
|
|
1702
|
+
if (o !== void 0) {
|
|
1703
|
+
if (typeof o != "string" || !Number.isFinite(Date.parse(o)))
|
|
1704
|
+
throw new Error(`${e}.${s} is not a date`);
|
|
1705
|
+
return o;
|
|
1706
|
+
}
|
|
1707
|
+
}, r = i("start"), n = i("end");
|
|
1708
|
+
if (r !== void 0 && n !== void 0 && Date.parse(r) > Date.parse(n))
|
|
1709
|
+
throw new Error(`${e}.start must not follow its end`);
|
|
1710
|
+
return {
|
|
1711
|
+
...r !== void 0 && { start: r },
|
|
1712
|
+
...n !== void 0 && { end: n }
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function Ht(t) {
|
|
1716
|
+
return t.type === "geovideo" && typeof t.indexUrl == "string";
|
|
1717
|
+
}
|
|
1718
|
+
function Yt(t, e) {
|
|
1719
|
+
return t.id === e.id && (t.type !== "geovideo" || e.type !== "geovideo" || t.manifestUrl === e.manifestUrl);
|
|
1720
|
+
}
|
|
1721
|
+
function j(t) {
|
|
1722
|
+
return typeof t == "string" && Number.isFinite(Date.parse(t));
|
|
1723
|
+
}
|
|
1724
|
+
function Tt(t) {
|
|
1725
|
+
const e = t;
|
|
1726
|
+
if (!e || e.schemaVersion !== 1 || e.type !== "geovideo-index" || !Array.isArray(e.chunks))
|
|
1727
|
+
throw new Error("Unsupported GeoVideo index");
|
|
1728
|
+
for (const i of e.chunks)
|
|
1729
|
+
if (!j(i?.start) || !j(i.end) || Date.parse(i.end) < Date.parse(i.start) || typeof i.manifestUrl != "string")
|
|
1730
|
+
throw new Error("Invalid GeoVideo index chunk");
|
|
1731
|
+
return e;
|
|
1732
|
+
}
|
|
1733
|
+
async function jt(t, e) {
|
|
1734
|
+
const i = await fetch(t, { signal: e });
|
|
1735
|
+
if (!i.ok)
|
|
1736
|
+
throw new Error(
|
|
1737
|
+
`GeoVideo index request failed (${i.status}): ${t}`
|
|
1738
|
+
);
|
|
1739
|
+
return Tt(await i.json());
|
|
1740
|
+
}
|
|
1741
|
+
function bt(t) {
|
|
1742
|
+
return typeof document < "u" ? new URL(t, document.baseURI).href : t;
|
|
1743
|
+
}
|
|
1744
|
+
function Rt(t, e) {
|
|
1745
|
+
const i = bt(e);
|
|
1746
|
+
return t.chunks.map((r) => ({
|
|
1747
|
+
key: r.key,
|
|
1748
|
+
manifestUrl: new URL(r.manifestUrl, i).href,
|
|
1749
|
+
start: Date.parse(r.start),
|
|
1750
|
+
end: Date.parse(r.end),
|
|
1751
|
+
samples: r.samples,
|
|
1752
|
+
...r.provisional ? { provisional: !0 } : {}
|
|
1753
|
+
}));
|
|
1754
|
+
}
|
|
1755
|
+
function gt(t, e, i) {
|
|
1756
|
+
if (e.sourceId !== t.id || e.catalogEntryId !== i.id)
|
|
1757
|
+
throw new Error(
|
|
1758
|
+
`GeoVideo index identity does not match catalog entry/source: ${t.id}`
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
function Kt(t, e, i) {
|
|
1762
|
+
gt(t, e, i);
|
|
1763
|
+
const r = Rt(e, t.indexUrl);
|
|
1764
|
+
return e.chunks.map(
|
|
1765
|
+
(n, s) => ({
|
|
1766
|
+
id: t.id,
|
|
1767
|
+
type: "geovideo",
|
|
1768
|
+
title: t.title,
|
|
1769
|
+
provenance: t.provenance,
|
|
1770
|
+
temporal: {
|
|
1771
|
+
mode: "fixed",
|
|
1772
|
+
cadence: t.temporal?.cadence,
|
|
1773
|
+
start: n.start,
|
|
1774
|
+
end: n.end
|
|
1775
|
+
},
|
|
1776
|
+
manifestUrl: r[s].manifestUrl
|
|
1777
|
+
})
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
export {
|
|
1781
|
+
ee as $,
|
|
1782
|
+
Ht as A,
|
|
1783
|
+
Ot as B,
|
|
1784
|
+
jt as C,
|
|
1785
|
+
Kt as D,
|
|
1786
|
+
$t as E,
|
|
1787
|
+
Vt as F,
|
|
1788
|
+
Xt as G,
|
|
1789
|
+
W as H,
|
|
1790
|
+
Bt as I,
|
|
1791
|
+
ft as J,
|
|
1792
|
+
ut as K,
|
|
1793
|
+
J as L,
|
|
1794
|
+
ze as M,
|
|
1795
|
+
et as N,
|
|
1796
|
+
xe as O,
|
|
1797
|
+
gt as P,
|
|
1798
|
+
Q,
|
|
1799
|
+
O as R,
|
|
1800
|
+
Ve as S,
|
|
1801
|
+
Gt as T,
|
|
1802
|
+
rt as U,
|
|
1803
|
+
Rt as V,
|
|
1804
|
+
tt as W,
|
|
1805
|
+
qt as X,
|
|
1806
|
+
it as Y,
|
|
1807
|
+
Wt as Z,
|
|
1808
|
+
Ze as _,
|
|
1809
|
+
St as a,
|
|
1810
|
+
R as a0,
|
|
1811
|
+
N as a1,
|
|
1812
|
+
Tt as a2,
|
|
1813
|
+
ht as a3,
|
|
1814
|
+
Ut as b,
|
|
1815
|
+
_t as c,
|
|
1816
|
+
At as d,
|
|
1817
|
+
kt as e,
|
|
1818
|
+
vt as f,
|
|
1819
|
+
Pt as g,
|
|
1820
|
+
yt as h,
|
|
1821
|
+
Ft as i,
|
|
1822
|
+
xt as j,
|
|
1823
|
+
Mt as k,
|
|
1824
|
+
It as l,
|
|
1825
|
+
qe as m,
|
|
1826
|
+
Ke as n,
|
|
1827
|
+
Ue as o,
|
|
1828
|
+
$e as p,
|
|
1829
|
+
wt as q,
|
|
1830
|
+
Me as r,
|
|
1831
|
+
Dt as s,
|
|
1832
|
+
Y as t,
|
|
1833
|
+
w as u,
|
|
1834
|
+
Lt as v,
|
|
1835
|
+
Yt as w,
|
|
1836
|
+
Ct as x,
|
|
1837
|
+
z as y,
|
|
1838
|
+
Nt as z
|
|
1839
|
+
};
|