@fxi/zartigl 0.5.0 → 0.7.0
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/README.md +158 -12
- package/dist/backdrop-DctkJ45Q.js +79 -0
- package/dist/catalog.d.ts +175 -16
- package/dist/catalog.js +263 -58
- package/dist/engine.d.ts +918 -0
- package/dist/engine.js +82 -0
- package/dist/geovideo-index-CMGVia5M.js +1148 -0
- package/dist/story.d.ts +398 -0
- package/dist/story.js +561 -0
- package/dist/zartigl.d.ts +524 -52
- package/dist/zartigl.js +3815 -2679
- package/package.json +33 -4
|
@@ -0,0 +1,1148 @@
|
|
|
1
|
+
const $ = { label: "Thermal", tags: ["sequential", "temperature"], colors: ["#042333", "#2c3395", "#744796", "#b15f82", "#eb7958", "#fbb43d", "#e8fa5b"] }, W = { label: "Haline", tags: ["sequential", "salinity"], colors: ["#2a186c", "#17568b", "#1a7e8f", "#22a785", "#55c666", "#aadc32", "#fde333"] }, H = { label: "Solar", tags: ["sequential", "irradiance"], colors: ["#330f0b", "#6b2313", "#a83c12", "#d6621a", "#f3951e", "#f7c741", "#f9f871"] }, Y = { label: "Ice", tags: ["sequential"], colors: ["#040d1a", "#123365", "#1c5f9e", "#2b8ece", "#62bde3", "#aaddf2", "#f2faff"] }, z = { label: "Deep", tags: ["sequential", "depth"], colors: ["#fdfecc", "#c8e59b", "#74c8a3", "#3194b0", "#2a5b8e", "#281a5e", "#0c0820"] }, j = { label: "RdYlBu", tags: ["diverging"], colors: ["#3288bd", "#66c2a5", "#abdda4", "#e6f598", "#fee08b", "#fdae61", "#f46d43", "#d53e4f"] }, K = { label: "Dense", tags: ["sequential"], colors: ["#e6f1f1", "#92c4ca", "#44929a", "#20627a", "#1a3c6a", "#211a50", "#0d0621"] }, J = { label: "Algae", tags: ["sequential", "biology", "log"], colors: ["#d7f9d0", "#a2d595", "#64b463", "#129450", "#126e45", "#1a482f", "#122414"] }, Q = { label: "Oxygen", tags: ["sequential", "biology", "hypoxia"], colors: ["#09060f", "#5b1028", "#d43d2f", "#f39b3d", "#f3e7b3", "#79cbd1", "#176b9c"] }, Z = { label: "Matter", tags: ["sequential", "biology"], colors: ["#fdfecc", "#f2c98a", "#e07c4e", "#bc3754", "#8d1a6d", "#51156b", "#1c0c3f"] }, ee = { label: "Viridis", tags: ["sequential", "perceptual"], colors: ["#440154", "#443983", "#31688e", "#21918c", "#35b779", "#90d743", "#fde725"] }, te = { label: "Plasma", tags: ["sequential", "perceptual"], colors: ["#0d0887", "#5e01a6", "#9c179e", "#cc4778", "#ed7953", "#fdb42f", "#f0f921"] }, re = { label: "Cyclic", tags: ["cyclic", "direction"], colors: ["#2e22ea", "#b541fb", "#fc93c0", "#f1ee35", "#77c80d", "#458776", "#2c24e9"] }, ne = { label: "Tempo", tags: ["sequential"], colors: ["#fff6f4", "#c2d1b9", "#7db390", "#2a937f", "#156d73", "#1c455b", "#151d44"] }, ae = { label: "Turbid", tags: ["sequential", "biology", "log"], colors: ["#e9f5a0", "#c8c86e", "#a09845", "#786031", "#533324", "#341423", "#150d17"] }, ie = { label: "Speed", tags: ["sequential", "velocity"], colors: ["#fcfea4", "#d5e429", "#94c63e", "#52a657", "#2d8169", "#1d5b6d", "#0d3361"] }, oe = { label: "Amp", tags: ["sequential"], colors: ["#f2f2f2", "#c0b8db", "#9180b6", "#6c4e9e", "#4e2e83", "#32185d", "#110a35"] }, se = { label: "Balance", tags: ["diverging"], colors: ["#181c43", "#3066af", "#9bbcdd", "#f2f2f2", "#e07d53", "#b13626", "#2c0b0e"] }, ce = { label: "Delta", tags: ["diverging"], colors: ["#0e3f31", "#20877b", "#99d4c1", "#f2f2f2", "#a1b0d4", "#3c5b9e", "#0d1938"] }, de = { label: "Curl", tags: ["diverging"], colors: ["#152237", "#2471b3", "#8dbac4", "#f2f2f2", "#c79f84", "#a63827", "#270b17"] }, ue = { label: "Gray", tags: ["sequential"], colors: ["#000000", "#252525", "#4d4d4d", "#7a7a7a", "#a8a8a8", "#d4d4d4", "#ffffff"] }, g = {
|
|
2
|
+
thermal: $,
|
|
3
|
+
"thermal-alt": { label: "Thermal Alt", tags: ["sequential", "temperature"], colors: ["#176b9c", "#79cbd1", "#f3e7b3", "#f39b3d", "#d43d2f", "#5b1028", "#09060f"] },
|
|
4
|
+
haline: W,
|
|
5
|
+
solar: H,
|
|
6
|
+
ice: Y,
|
|
7
|
+
deep: z,
|
|
8
|
+
rdylbu: j,
|
|
9
|
+
dense: K,
|
|
10
|
+
algae: J,
|
|
11
|
+
oxygen: Q,
|
|
12
|
+
matter: Z,
|
|
13
|
+
viridis: ee,
|
|
14
|
+
plasma: te,
|
|
15
|
+
cyclic: re,
|
|
16
|
+
tempo: ne,
|
|
17
|
+
turbid: ae,
|
|
18
|
+
speed: ie,
|
|
19
|
+
amp: oe,
|
|
20
|
+
balance: se,
|
|
21
|
+
delta: ce,
|
|
22
|
+
curl: de,
|
|
23
|
+
"mono-black": { label: "Black", tags: ["monochrome", "particles"], colors: ["#000000", "#000000"] },
|
|
24
|
+
"mono-white": { label: "White", tags: ["monochrome", "particles"], colors: ["#ffffff", "#ffffff"] },
|
|
25
|
+
"mono-pink": { label: "Pink", tags: ["monochrome", "particles"], colors: ["#ff2aa1", "#ff2aa1"] },
|
|
26
|
+
gray: ue
|
|
27
|
+
};
|
|
28
|
+
function I(e) {
|
|
29
|
+
const t = {}, r = e.length - 1;
|
|
30
|
+
return e.forEach((n, a) => {
|
|
31
|
+
t[r === 0 ? 0 : a / r] = n;
|
|
32
|
+
}), t;
|
|
33
|
+
}
|
|
34
|
+
function fe(e, t) {
|
|
35
|
+
const r = g;
|
|
36
|
+
if (typeof e == "string") {
|
|
37
|
+
const n = r[e];
|
|
38
|
+
if (!n)
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Palette "${e}" not found. Available palettes: ${Object.keys(
|
|
41
|
+
r
|
|
42
|
+
).join(", ")}`
|
|
43
|
+
);
|
|
44
|
+
const a = t?.reverse ? [...n.colors].reverse() : n.colors;
|
|
45
|
+
return I(a);
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(e)) {
|
|
48
|
+
const n = t?.reverse ? [...e].reverse() : e;
|
|
49
|
+
return I(n);
|
|
50
|
+
}
|
|
51
|
+
return e;
|
|
52
|
+
}
|
|
53
|
+
function Ye() {
|
|
54
|
+
return Object.entries(g).map(([t, r]) => ({ id: t, ...r }));
|
|
55
|
+
}
|
|
56
|
+
function N(e, t, r) {
|
|
57
|
+
const n = e.createShader(t);
|
|
58
|
+
if (!n)
|
|
59
|
+
throw new Error("Failed to create shader");
|
|
60
|
+
if (e.shaderSource(n, r), e.compileShader(n), !e.getShaderParameter(n, e.COMPILE_STATUS)) {
|
|
61
|
+
const a = e.getShaderInfoLog(n);
|
|
62
|
+
throw e.deleteShader(n), new Error(`Shader compile error: ${a}`);
|
|
63
|
+
}
|
|
64
|
+
return n;
|
|
65
|
+
}
|
|
66
|
+
function ze(e, t, r) {
|
|
67
|
+
const n = e.createProgram();
|
|
68
|
+
if (!n)
|
|
69
|
+
throw new Error("Failed to create program");
|
|
70
|
+
const a = N(e, e.VERTEX_SHADER, t), i = N(e, e.FRAGMENT_SHADER, r);
|
|
71
|
+
if (e.attachShader(n, a), e.attachShader(n, i), e.linkProgram(n), !e.getProgramParameter(n, e.LINK_STATUS)) {
|
|
72
|
+
const s = e.getProgramInfoLog(n);
|
|
73
|
+
throw e.deleteProgram(n), new Error(`Program link error: ${s}`);
|
|
74
|
+
}
|
|
75
|
+
return e.deleteShader(a), e.deleteShader(i), n;
|
|
76
|
+
}
|
|
77
|
+
function je(e, t, r, n, a) {
|
|
78
|
+
const i = e.createTexture();
|
|
79
|
+
if (!i)
|
|
80
|
+
throw new Error("Failed to create texture");
|
|
81
|
+
return e.bindTexture(e.TEXTURE_2D, i), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, t), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, t), e.texImage2D(
|
|
82
|
+
e.TEXTURE_2D,
|
|
83
|
+
0,
|
|
84
|
+
e.RGBA,
|
|
85
|
+
n,
|
|
86
|
+
a,
|
|
87
|
+
0,
|
|
88
|
+
e.RGBA,
|
|
89
|
+
e.UNSIGNED_BYTE,
|
|
90
|
+
r
|
|
91
|
+
), e.bindTexture(e.TEXTURE_2D, null), i;
|
|
92
|
+
}
|
|
93
|
+
function le(e, t, r) {
|
|
94
|
+
const n = e.getParameter(
|
|
95
|
+
e.TEXTURE_BINDING_2D
|
|
96
|
+
), a = e.getParameter(
|
|
97
|
+
e.FRAMEBUFFER_BINDING
|
|
98
|
+
), i = e.createTexture(), s = e.createFramebuffer();
|
|
99
|
+
if (!i || !s)
|
|
100
|
+
return i && e.deleteTexture(i), s && e.deleteFramebuffer(s), e.bindTexture(e.TEXTURE_2D, n), e.bindFramebuffer(e.FRAMEBUFFER, a), !1;
|
|
101
|
+
let o = !1;
|
|
102
|
+
try {
|
|
103
|
+
e.bindTexture(e.TEXTURE_2D, i), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST), e.texImage2D(
|
|
104
|
+
e.TEXTURE_2D,
|
|
105
|
+
0,
|
|
106
|
+
t,
|
|
107
|
+
1,
|
|
108
|
+
1,
|
|
109
|
+
0,
|
|
110
|
+
e.RGBA,
|
|
111
|
+
r,
|
|
112
|
+
null
|
|
113
|
+
), e.bindFramebuffer(e.FRAMEBUFFER, s), e.framebufferTexture2D(
|
|
114
|
+
e.FRAMEBUFFER,
|
|
115
|
+
e.COLOR_ATTACHMENT0,
|
|
116
|
+
e.TEXTURE_2D,
|
|
117
|
+
i,
|
|
118
|
+
0
|
|
119
|
+
), o = e.checkFramebufferStatus(e.FRAMEBUFFER) === e.FRAMEBUFFER_COMPLETE;
|
|
120
|
+
} finally {
|
|
121
|
+
e.bindFramebuffer(e.FRAMEBUFFER, a), e.bindTexture(e.TEXTURE_2D, n), e.deleteFramebuffer(s), e.deleteTexture(i);
|
|
122
|
+
}
|
|
123
|
+
return o;
|
|
124
|
+
}
|
|
125
|
+
function he(e) {
|
|
126
|
+
return {
|
|
127
|
+
kind: "rgba8-packed",
|
|
128
|
+
internalFormat: e.RGBA,
|
|
129
|
+
type: e.UNSIGNED_BYTE
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function Ke(e) {
|
|
133
|
+
if (typeof WebGL2RenderingContext > "u" || !(e instanceof WebGL2RenderingContext))
|
|
134
|
+
throw new Error("zartigl requires WebGL2");
|
|
135
|
+
return e;
|
|
136
|
+
}
|
|
137
|
+
function Je(e) {
|
|
138
|
+
const t = [];
|
|
139
|
+
e.getExtension("EXT_color_buffer_float") ? t.push(
|
|
140
|
+
{ kind: "float32", internalFormat: e.RGBA32F, type: e.FLOAT },
|
|
141
|
+
{ kind: "float16", internalFormat: e.RGBA16F, type: e.HALF_FLOAT }
|
|
142
|
+
) : e.getExtension("EXT_color_buffer_half_float") && t.push({
|
|
143
|
+
kind: "float16",
|
|
144
|
+
internalFormat: e.RGBA16F,
|
|
145
|
+
type: e.HALF_FLOAT
|
|
146
|
+
});
|
|
147
|
+
for (const r of t)
|
|
148
|
+
if (le(
|
|
149
|
+
e,
|
|
150
|
+
r.internalFormat,
|
|
151
|
+
r.type
|
|
152
|
+
))
|
|
153
|
+
return r;
|
|
154
|
+
return he(e);
|
|
155
|
+
}
|
|
156
|
+
function Qe(e, t, r, n, a, i, s) {
|
|
157
|
+
const o = e.createTexture();
|
|
158
|
+
if (!o)
|
|
159
|
+
throw new Error("Failed to create texture");
|
|
160
|
+
return e.bindTexture(e.TEXTURE_2D, o), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, t), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, t), e.texImage2D(
|
|
161
|
+
e.TEXTURE_2D,
|
|
162
|
+
0,
|
|
163
|
+
i,
|
|
164
|
+
n,
|
|
165
|
+
a,
|
|
166
|
+
0,
|
|
167
|
+
e.RGBA,
|
|
168
|
+
s,
|
|
169
|
+
r
|
|
170
|
+
), e.bindTexture(e.TEXTURE_2D, null), o;
|
|
171
|
+
}
|
|
172
|
+
function Ze(e, t, r) {
|
|
173
|
+
e.activeTexture(e.TEXTURE0 + r), e.bindTexture(e.TEXTURE_2D, t);
|
|
174
|
+
}
|
|
175
|
+
function et(e, t) {
|
|
176
|
+
const r = e.createFramebuffer();
|
|
177
|
+
if (!r)
|
|
178
|
+
throw new Error("Failed to create framebuffer");
|
|
179
|
+
return e.bindFramebuffer(e.FRAMEBUFFER, r), e.framebufferTexture2D(
|
|
180
|
+
e.FRAMEBUFFER,
|
|
181
|
+
e.COLOR_ATTACHMENT0,
|
|
182
|
+
e.TEXTURE_2D,
|
|
183
|
+
t,
|
|
184
|
+
0
|
|
185
|
+
), e.bindFramebuffer(e.FRAMEBUFFER, null), r;
|
|
186
|
+
}
|
|
187
|
+
function tt(e) {
|
|
188
|
+
const t = e.createBuffer();
|
|
189
|
+
if (!t)
|
|
190
|
+
throw new Error("Failed to create buffer");
|
|
191
|
+
return e.bindBuffer(e.ARRAY_BUFFER, t), e.bufferData(
|
|
192
|
+
e.ARRAY_BUFFER,
|
|
193
|
+
new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
|
|
194
|
+
e.STATIC_DRAW
|
|
195
|
+
), t;
|
|
196
|
+
}
|
|
197
|
+
const me = {
|
|
198
|
+
0: "#3288bd",
|
|
199
|
+
0.1: "#66c2a5",
|
|
200
|
+
0.2: "#abdda4",
|
|
201
|
+
0.3: "#e6f598",
|
|
202
|
+
0.4: "#fee08b",
|
|
203
|
+
0.5: "#fdae61",
|
|
204
|
+
0.6: "#f46d43",
|
|
205
|
+
1: "#d53e4f"
|
|
206
|
+
};
|
|
207
|
+
function rt(e, t) {
|
|
208
|
+
const r = t != null ? fe(t) : me, n = document.createElement("canvas");
|
|
209
|
+
n.width = 256, n.height = 1;
|
|
210
|
+
const a = n.getContext("2d"), i = a.createLinearGradient(0, 0, 256, 0);
|
|
211
|
+
for (const [o, c] of Object.entries(r))
|
|
212
|
+
i.addColorStop(Number(o), c);
|
|
213
|
+
a.fillStyle = i, a.fillRect(0, 0, 256, 1);
|
|
214
|
+
const s = e.createTexture();
|
|
215
|
+
if (!s)
|
|
216
|
+
throw new Error("Failed to create color ramp texture");
|
|
217
|
+
return e.bindTexture(e.TEXTURE_2D, s), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.LINEAR), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.LINEAR), e.texImage2D(e.TEXTURE_2D, 0, e.RGBA, e.RGBA, e.UNSIGNED_BYTE, n), e.bindTexture(e.TEXTURE_2D, null), s;
|
|
218
|
+
}
|
|
219
|
+
function nt(e) {
|
|
220
|
+
const t = e.getExtension("WEBGL_debug_renderer_info");
|
|
221
|
+
return {
|
|
222
|
+
vendor: String(e.getParameter(e.VENDOR) ?? ""),
|
|
223
|
+
renderer: String(e.getParameter(e.RENDERER) ?? ""),
|
|
224
|
+
unmaskedVendor: t ? String(e.getParameter(t.UNMASKED_VENDOR_WEBGL) ?? "") : void 0,
|
|
225
|
+
unmaskedRenderer: t ? String(e.getParameter(t.UNMASKED_RENDERER_WEBGL) ?? "") : void 0,
|
|
226
|
+
version: String(e.getParameter(e.VERSION) ?? "")
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function at(e) {
|
|
230
|
+
return {
|
|
231
|
+
program: e.getParameter(e.CURRENT_PROGRAM),
|
|
232
|
+
activeTexture: e.getParameter(e.ACTIVE_TEXTURE),
|
|
233
|
+
arrayBuffer: e.getParameter(e.ARRAY_BUFFER_BINDING),
|
|
234
|
+
elementBuffer: e.getParameter(e.ELEMENT_ARRAY_BUFFER_BINDING),
|
|
235
|
+
framebuffer: e.getParameter(e.FRAMEBUFFER_BINDING),
|
|
236
|
+
blend: e.isEnabled(e.BLEND),
|
|
237
|
+
depthTest: e.isEnabled(e.DEPTH_TEST),
|
|
238
|
+
stencilTest: e.isEnabled(e.STENCIL_TEST),
|
|
239
|
+
viewport: e.getParameter(e.VIEWPORT),
|
|
240
|
+
blendFunc: [
|
|
241
|
+
e.getParameter(e.BLEND_SRC_RGB),
|
|
242
|
+
e.getParameter(e.BLEND_DST_RGB)
|
|
243
|
+
]
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function it(e, t) {
|
|
247
|
+
e.useProgram(t.program), e.activeTexture(t.activeTexture), e.bindBuffer(e.ARRAY_BUFFER, t.arrayBuffer), e.bindBuffer(e.ELEMENT_ARRAY_BUFFER, t.elementBuffer), e.bindFramebuffer(e.FRAMEBUFFER, t.framebuffer), t.blend ? e.enable(e.BLEND) : e.disable(e.BLEND), t.depthTest ? e.enable(e.DEPTH_TEST) : e.disable(e.DEPTH_TEST), t.stencilTest ? e.enable(e.STENCIL_TEST) : e.disable(e.STENCIL_TEST), e.viewport(t.viewport[0], t.viewport[1], t.viewport[2], t.viewport[3]), e.blendFunc(t.blendFunc[0], t.blendFunc[1]);
|
|
248
|
+
}
|
|
249
|
+
const L = 20, Ee = 600;
|
|
250
|
+
function Te(e) {
|
|
251
|
+
const t = Math.max(0, Math.min(1, e));
|
|
252
|
+
return L * Math.pow(Ee / L, t);
|
|
253
|
+
}
|
|
254
|
+
function ot(e) {
|
|
255
|
+
return Math.exp(-1 / Te(e));
|
|
256
|
+
}
|
|
257
|
+
const pe = 0.8, be = 120, we = 300;
|
|
258
|
+
class st {
|
|
259
|
+
constructor(t) {
|
|
260
|
+
this.base = t, this.moving = !1, this.start = 0, this.duration = 0, this.from = t, this.to = t;
|
|
261
|
+
}
|
|
262
|
+
/** Configured opacity, applied at once unless the camera moves. */
|
|
263
|
+
setBase(t) {
|
|
264
|
+
this.base = t, this.moving || (this.from = t, this.to = t, this.duration = 0);
|
|
265
|
+
}
|
|
266
|
+
setMoving(t, r) {
|
|
267
|
+
t !== this.moving && (this.from = this.value(r), this.moving = t, this.to = t ? pe : this.base, this.start = r, this.duration = t ? be : we);
|
|
268
|
+
}
|
|
269
|
+
value(t) {
|
|
270
|
+
if (this.duration === 0)
|
|
271
|
+
return this.to;
|
|
272
|
+
const r = Math.min(1, Math.max(0, (t - this.start) / this.duration));
|
|
273
|
+
return r === 1 ? (this.duration = 0, this.from = this.to, this.to) : this.from + (this.to - this.from) * r * r * (3 - 2 * r);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function ct(e) {
|
|
277
|
+
if (e == null)
|
|
278
|
+
return null;
|
|
279
|
+
if (!Number.isFinite(e[0]) || !Number.isFinite(e[1]) || e[0] >= e[1])
|
|
280
|
+
throw new RangeError(
|
|
281
|
+
"colorDomain must contain two finite, increasing values"
|
|
282
|
+
);
|
|
283
|
+
return [e[0], e[1]];
|
|
284
|
+
}
|
|
285
|
+
function m(e, t) {
|
|
286
|
+
if (typeof e != "number" || !Number.isFinite(e))
|
|
287
|
+
throw new Error(`Invalid GeoVideo manifest ${t}`);
|
|
288
|
+
return e;
|
|
289
|
+
}
|
|
290
|
+
function F(e, t) {
|
|
291
|
+
if (typeof e != "string")
|
|
292
|
+
throw new Error(`Invalid GeoVideo manifest ${t}`);
|
|
293
|
+
const r = new Date(e).getTime();
|
|
294
|
+
if (!Number.isFinite(r))
|
|
295
|
+
throw new Error(`Invalid GeoVideo manifest ${t}`);
|
|
296
|
+
return r;
|
|
297
|
+
}
|
|
298
|
+
function D(e) {
|
|
299
|
+
return typeof e == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(
|
|
300
|
+
e
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
function V(e) {
|
|
304
|
+
if (!e || typeof e != "object")
|
|
305
|
+
throw new Error("Invalid GeoVideo manifest");
|
|
306
|
+
const t = e;
|
|
307
|
+
if (t.schemaVersion !== 3 || t.type !== "geovideo")
|
|
308
|
+
throw new Error("Unsupported GeoVideo manifest version or type");
|
|
309
|
+
if (t.projection !== "equirectangular")
|
|
310
|
+
throw new Error(
|
|
311
|
+
`Unsupported GeoVideo projection: ${String(t.projection)}`
|
|
312
|
+
);
|
|
313
|
+
if (!Array.isArray(t.bounds) || t.bounds.length !== 4)
|
|
314
|
+
throw new Error("Invalid GeoVideo manifest bounds");
|
|
315
|
+
const [r, n, a, i] = t.bounds.map(
|
|
316
|
+
(f, T) => m(f, `bounds[${T}]`)
|
|
317
|
+
);
|
|
318
|
+
if (n < -90 || i > 90 || n >= i || r === a)
|
|
319
|
+
throw new Error("Invalid GeoVideo geographic bounds");
|
|
320
|
+
const s = t.media;
|
|
321
|
+
if (!s || typeof s.url != "string" || !s.url)
|
|
322
|
+
throw new Error("Invalid GeoVideo media");
|
|
323
|
+
for (const f of ["width", "height", "fps", "durationSeconds"])
|
|
324
|
+
if (m(s[f], `media.${f}`) <= 0)
|
|
325
|
+
throw new Error(`Invalid GeoVideo media.${f}`);
|
|
326
|
+
const o = t.encoding;
|
|
327
|
+
if (!o || o.bits !== 8 || o.colorSpace !== "bt709" || o.colorRange !== "limited" && o.colorRange !== "full")
|
|
328
|
+
throw new Error("Invalid GeoVideo encoding");
|
|
329
|
+
const c = m(o.codeMin, "encoding.codeMin"), d = m(o.codeMax, "encoding.codeMax");
|
|
330
|
+
if (c < 0 || d > 255 || c >= d)
|
|
331
|
+
throw new Error("Invalid GeoVideo code range");
|
|
332
|
+
let h = s.height;
|
|
333
|
+
if (o.kind === "scalar-luma") {
|
|
334
|
+
if (o.transfer !== "linear" || m(o.valueMin, "encoding.valueMin") >= m(o.valueMax, "encoding.valueMax"))
|
|
335
|
+
throw new Error("Invalid GeoVideo scalar-luma encoding");
|
|
336
|
+
} else if (o.kind === "vector-luma") {
|
|
337
|
+
if (o.transfer !== "linear" && o.transfer !== "sqrt" || o.layout !== "stacked-uv" || m(o.valueDomain, "encoding.valueDomain") <= 0 || s.height % 2 !== 0)
|
|
338
|
+
throw new Error("Invalid GeoVideo vector-luma encoding");
|
|
339
|
+
h = s.height / 2;
|
|
340
|
+
} else
|
|
341
|
+
throw new Error("Unsupported GeoVideo encoding");
|
|
342
|
+
const u = t.mask;
|
|
343
|
+
if (!u || u.kind !== "static-validity" || typeof u.url != "string" || !u.url || m(u.width, "mask.width") !== s.width || m(u.height, "mask.height") !== h || m(u.threshold, "mask.threshold") < 0 || u.threshold > 1)
|
|
344
|
+
throw new Error("Invalid GeoVideo static mask");
|
|
345
|
+
if (t.timeline?.kind === "range") {
|
|
346
|
+
const f = F(t.timeline.dateStart, "timeline.dateStart");
|
|
347
|
+
if (F(t.timeline.dateEnd, "timeline.dateEnd") <= f)
|
|
348
|
+
throw new Error("GeoVideo timeline end must follow start");
|
|
349
|
+
if (t.timeline.interpolation !== "linear" && t.timeline.interpolation !== "nearest")
|
|
350
|
+
throw new Error("Invalid GeoVideo interpolation");
|
|
351
|
+
} else if (t.timeline?.kind === "snapshot-loop")
|
|
352
|
+
F(t.timeline.date, "timeline.date");
|
|
353
|
+
else if (t.timeline?.kind === "sample-sequence") {
|
|
354
|
+
if (!Array.isArray(t.timeline.values) || t.timeline.values.length === 0)
|
|
355
|
+
throw new Error("GeoVideo sample sequence must contain values");
|
|
356
|
+
const f = t.timeline.values.map(
|
|
357
|
+
(T, b) => F(T, `timeline.values[${b}]`)
|
|
358
|
+
);
|
|
359
|
+
if (f.some((T, b) => b > 0 && T <= f[b - 1]))
|
|
360
|
+
throw new Error(
|
|
361
|
+
"GeoVideo sample sequence values must be strictly increasing"
|
|
362
|
+
);
|
|
363
|
+
} else
|
|
364
|
+
throw new Error("Invalid GeoVideo timeline");
|
|
365
|
+
const E = t.style?.colorDomain;
|
|
366
|
+
if (!Array.isArray(E) || E.length !== 2 || !E.every(Number.isFinite) || E[0] >= E[1])
|
|
367
|
+
throw new Error("Invalid GeoVideo color domain");
|
|
368
|
+
if (!D(t.id) || !t.provenance || !D(t.provenance.catalogEntryId) || !D(t.provenance.inputSourceId) || !Array.isArray(t.provenance.variables) || t.provenance.variables.length === 0)
|
|
369
|
+
throw new Error("Invalid GeoVideo provenance");
|
|
370
|
+
return { ...t, bounds: [r, n, a, i] };
|
|
371
|
+
}
|
|
372
|
+
async function Re(e, t) {
|
|
373
|
+
if (typeof e != "string")
|
|
374
|
+
return V(e);
|
|
375
|
+
const r = await fetch(e, { signal: t });
|
|
376
|
+
if (!r.ok)
|
|
377
|
+
throw new Error(
|
|
378
|
+
`GeoVideo manifest request failed (${r.status}): ${e}`
|
|
379
|
+
);
|
|
380
|
+
const n = await r.json(), a = V(n), i = typeof document < "u" ? new URL(e, document.baseURI).href : e;
|
|
381
|
+
return {
|
|
382
|
+
...a,
|
|
383
|
+
media: { ...a.media, url: new URL(a.media.url, i).href },
|
|
384
|
+
mask: { ...a.mask, url: new URL(a.mask.url, i).href }
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function _(e) {
|
|
388
|
+
if (e.timeline.kind === "snapshot-loop")
|
|
389
|
+
return [new Date(e.timeline.date).getTime()];
|
|
390
|
+
if (e.timeline.kind === "sample-sequence")
|
|
391
|
+
return e.timeline.values.map((a) => new Date(a).getTime());
|
|
392
|
+
const t = new Date(e.timeline.dateStart).getTime(), r = new Date(e.timeline.dateEnd).getTime(), n = Math.max(
|
|
393
|
+
2,
|
|
394
|
+
Math.round(e.media.durationSeconds * e.media.fps)
|
|
395
|
+
);
|
|
396
|
+
return Array.from(
|
|
397
|
+
{ length: n },
|
|
398
|
+
(a, i) => t + (r - t) * i / (n - 1)
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
function _e(e) {
|
|
402
|
+
return e.timeline.kind === "sample-sequence" ? e.timeline.values.length / e.media.durationSeconds : e.media.fps;
|
|
403
|
+
}
|
|
404
|
+
function dt(e, t) {
|
|
405
|
+
return t / _e(e);
|
|
406
|
+
}
|
|
407
|
+
function Ae(e, t) {
|
|
408
|
+
if (e.timeline.kind === "snapshot-loop")
|
|
409
|
+
return 0;
|
|
410
|
+
if (e.timeline.kind === "sample-sequence") {
|
|
411
|
+
const i = _(e);
|
|
412
|
+
let s = 0;
|
|
413
|
+
for (let c = 1; c < i.length; c += 1)
|
|
414
|
+
Math.abs(i[c] - t) < Math.abs(i[s] - t) && (s = c);
|
|
415
|
+
const o = e.media.durationSeconds / i.length;
|
|
416
|
+
return s * o + Math.min(o / 2, 0.5 / e.media.fps);
|
|
417
|
+
}
|
|
418
|
+
const r = new Date(e.timeline.dateStart).getTime(), n = new Date(e.timeline.dateEnd).getTime();
|
|
419
|
+
return Math.max(0, Math.min(1, (t - r) / (n - r))) * X(e);
|
|
420
|
+
}
|
|
421
|
+
function ut(e, t) {
|
|
422
|
+
if (e.timeline.kind === "snapshot-loop")
|
|
423
|
+
return new Date(e.timeline.date).getTime();
|
|
424
|
+
if (e.timeline.kind === "sample-sequence") {
|
|
425
|
+
const s = _(e), o = e.media.durationSeconds / s.length, c = Math.floor((t + 0.25 / e.media.fps) / o);
|
|
426
|
+
return s[Math.max(0, Math.min(s.length - 1, c))];
|
|
427
|
+
}
|
|
428
|
+
const r = new Date(e.timeline.dateStart).getTime(), n = new Date(e.timeline.dateEnd).getTime(), a = X(e), i = a > 0 ? Math.max(0, Math.min(1, t / a)) : 0;
|
|
429
|
+
return r + (n - r) * i;
|
|
430
|
+
}
|
|
431
|
+
function X(e) {
|
|
432
|
+
return Math.max(0, e.media.durationSeconds - 1 / e.media.fps);
|
|
433
|
+
}
|
|
434
|
+
function Fe(e, t) {
|
|
435
|
+
const r = new Uint8Array(e.length / 4);
|
|
436
|
+
for (let n = 0; n < r.length; n++) {
|
|
437
|
+
const a = e[n * 4];
|
|
438
|
+
r[n] = t === "limited" ? Math.round(a * 219 / 255 + 16) : a;
|
|
439
|
+
}
|
|
440
|
+
return r;
|
|
441
|
+
}
|
|
442
|
+
class xe {
|
|
443
|
+
constructor() {
|
|
444
|
+
this.gl = null, this.texture = null, this.framebuffer = null;
|
|
445
|
+
}
|
|
446
|
+
read(t, r, n) {
|
|
447
|
+
const a = this.context();
|
|
448
|
+
a.bindTexture(a.TEXTURE_2D, this.texture), a.pixelStorei(a.UNPACK_FLIP_Y_WEBGL, !1), a.pixelStorei(a.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !1), a.pixelStorei(a.UNPACK_COLORSPACE_CONVERSION_WEBGL, a.NONE), a.texParameteri(a.TEXTURE_2D, a.TEXTURE_MIN_FILTER, a.NEAREST), a.texParameteri(a.TEXTURE_2D, a.TEXTURE_MAG_FILTER, a.NEAREST), a.texParameteri(a.TEXTURE_2D, a.TEXTURE_WRAP_S, a.CLAMP_TO_EDGE), a.texParameteri(a.TEXTURE_2D, a.TEXTURE_WRAP_T, a.CLAMP_TO_EDGE), a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, a.RGBA, a.UNSIGNED_BYTE, t), a.bindFramebuffer(a.FRAMEBUFFER, this.framebuffer), a.framebufferTexture2D(
|
|
449
|
+
a.FRAMEBUFFER,
|
|
450
|
+
a.COLOR_ATTACHMENT0,
|
|
451
|
+
a.TEXTURE_2D,
|
|
452
|
+
this.texture,
|
|
453
|
+
0
|
|
454
|
+
);
|
|
455
|
+
const i = new Uint8Array(r * n * 4);
|
|
456
|
+
return a.readPixels(0, 0, r, n, a.RGBA, a.UNSIGNED_BYTE, i), a.bindFramebuffer(a.FRAMEBUFFER, null), i;
|
|
457
|
+
}
|
|
458
|
+
release() {
|
|
459
|
+
this.gl?.getExtension("WEBGL_lose_context")?.loseContext(), this.gl = null, this.texture = null, this.framebuffer = null;
|
|
460
|
+
}
|
|
461
|
+
context() {
|
|
462
|
+
if (this.gl)
|
|
463
|
+
return this.gl;
|
|
464
|
+
const t = document.createElement("canvas"), r = {
|
|
465
|
+
premultipliedAlpha: !1,
|
|
466
|
+
antialias: !1
|
|
467
|
+
}, n = t.getContext("webgl2", r);
|
|
468
|
+
if (!n)
|
|
469
|
+
throw new Error("WebGL2 is required to decode GeoVideo frames");
|
|
470
|
+
return this.gl = n, this.texture = n.createTexture(), this.framebuffer = n.createFramebuffer(), n;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function C(e) {
|
|
474
|
+
const t = Math.max(4, Math.floor(e.length / 4 / 997) * 4);
|
|
475
|
+
for (let r = 3; r < e.length; r += t)
|
|
476
|
+
if (e[r] !== 0)
|
|
477
|
+
return !1;
|
|
478
|
+
return !0;
|
|
479
|
+
}
|
|
480
|
+
function l() {
|
|
481
|
+
return new DOMException("GeoVideo frame request was cancelled", "AbortError");
|
|
482
|
+
}
|
|
483
|
+
function R(e, t, r) {
|
|
484
|
+
return t ? t.aborted ? (e.catch(() => {
|
|
485
|
+
}), r?.(), Promise.reject(l())) : new Promise((n, a) => {
|
|
486
|
+
const i = () => {
|
|
487
|
+
r?.(), a(l());
|
|
488
|
+
};
|
|
489
|
+
t.addEventListener("abort", i, { once: !0 }), e.then(n, a).finally(() => {
|
|
490
|
+
t.removeEventListener("abort", i);
|
|
491
|
+
});
|
|
492
|
+
}) : e;
|
|
493
|
+
}
|
|
494
|
+
function M(e, t) {
|
|
495
|
+
return new Promise(
|
|
496
|
+
(r) => e.addEventListener(t, () => r(), { once: !0 })
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
const ve = 250;
|
|
500
|
+
function ye(e) {
|
|
501
|
+
return "requestVideoFrameCallback" in e ? Promise.race([
|
|
502
|
+
new Promise(
|
|
503
|
+
(t) => e.requestVideoFrameCallback(() => t())
|
|
504
|
+
),
|
|
505
|
+
new Promise((t) => setTimeout(t, ve))
|
|
506
|
+
]) : Promise.resolve();
|
|
507
|
+
}
|
|
508
|
+
class O {
|
|
509
|
+
constructor(t, r, n = {}) {
|
|
510
|
+
this.reader = new xe(), this.video = null, this.mask = 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 = t, this.planes = r, this.cacheSize = Math.max(1, n.cacheSize ?? 3), this.width = t.media.width, this.height = t.media.height / r, this.times = Float64Array.from(_(t));
|
|
511
|
+
}
|
|
512
|
+
init() {
|
|
513
|
+
return this.initPromise ??= this.load().catch((t) => {
|
|
514
|
+
throw this.initPromise = null, t;
|
|
515
|
+
}), this.initPromise;
|
|
516
|
+
}
|
|
517
|
+
/** Abort every pending frame decode; frames already decoded stay cached. */
|
|
518
|
+
cancelAll() {
|
|
519
|
+
for (const t of [...this.pending])
|
|
520
|
+
this.drop(t);
|
|
521
|
+
}
|
|
522
|
+
/** Release the video element and GL context; a later init() reloads them. */
|
|
523
|
+
release() {
|
|
524
|
+
this.cancelAll(), this.frames.clear(), this.video && (this.video.removeAttribute("src"), this.video.load()), this.video = null, this.mask = null, this.presented = !1, this.primed = !1, this.initPromise = null, this.reader.release();
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* One decode per time step, cached and shared by concurrent requests.
|
|
528
|
+
* Aborting `request.signal` rejects this request with an AbortError; the
|
|
529
|
+
* decode goes on while another request waits for it.
|
|
530
|
+
*/
|
|
531
|
+
frame(t, r = {}) {
|
|
532
|
+
const { signal: n } = r;
|
|
533
|
+
if (n?.aborted)
|
|
534
|
+
return Promise.reject(l());
|
|
535
|
+
let a = this.frames.get(t);
|
|
536
|
+
if (a ? (this.frames.delete(t), this.frames.set(t, a)) : a = this.enqueue(t), a.settled)
|
|
537
|
+
return a.promise;
|
|
538
|
+
const i = a;
|
|
539
|
+
return i.waiters++, R(i.promise, n, () => this.leave(i));
|
|
540
|
+
}
|
|
541
|
+
enqueue(t) {
|
|
542
|
+
const r = new AbortController(), n = this.queue.then(
|
|
543
|
+
() => r.signal.aborted ? Promise.reject(l()) : this.decode(t, r.signal)
|
|
544
|
+
);
|
|
545
|
+
this.queue = n.catch(() => {
|
|
546
|
+
});
|
|
547
|
+
const a = {
|
|
548
|
+
index: t,
|
|
549
|
+
controller: r,
|
|
550
|
+
promise: R(n, r.signal),
|
|
551
|
+
waiters: 0,
|
|
552
|
+
settled: !1
|
|
553
|
+
};
|
|
554
|
+
for (this.pending.add(a), a.promise.then(
|
|
555
|
+
() => {
|
|
556
|
+
a.settled = !0, this.pending.delete(a);
|
|
557
|
+
},
|
|
558
|
+
() => {
|
|
559
|
+
a.settled = !0, this.pending.delete(a), this.frames.get(t) === a && this.frames.delete(t);
|
|
560
|
+
}
|
|
561
|
+
), this.frames.set(t, a); this.frames.size > this.cacheSize; )
|
|
562
|
+
this.frames.delete(this.frames.keys().next().value);
|
|
563
|
+
return a;
|
|
564
|
+
}
|
|
565
|
+
/** A request left; drop the decode when it was the last one waiting. */
|
|
566
|
+
leave(t) {
|
|
567
|
+
t.waiters--, t.waiters <= 0 && !t.settled && this.drop(t);
|
|
568
|
+
}
|
|
569
|
+
drop(t) {
|
|
570
|
+
t.controller.abort(), this.pending.delete(t), this.frames.get(t.index) === t && this.frames.delete(t.index);
|
|
571
|
+
}
|
|
572
|
+
async load() {
|
|
573
|
+
const [t] = await Promise.all([this.loadMask(), this.loadVideo()]);
|
|
574
|
+
this.mask = t;
|
|
575
|
+
}
|
|
576
|
+
async loadMask() {
|
|
577
|
+
const { url: t, width: r, height: n } = this.manifest.mask, a = new Image();
|
|
578
|
+
a.crossOrigin = "anonymous", a.src = t;
|
|
579
|
+
try {
|
|
580
|
+
await a.decode();
|
|
581
|
+
} catch {
|
|
582
|
+
throw new Error(`Failed to load GeoVideo mask: ${t}`);
|
|
583
|
+
}
|
|
584
|
+
if (a.naturalWidth !== r || a.naturalHeight !== n)
|
|
585
|
+
throw new Error(
|
|
586
|
+
`GeoVideo mask dimensions ${a.naturalWidth}x${a.naturalHeight} do not match manifest ${r}x${n}`
|
|
587
|
+
);
|
|
588
|
+
const i = this.reader.read(a, r, n), s = new Uint8Array(r * n);
|
|
589
|
+
for (let o = 0; o < s.length; o++)
|
|
590
|
+
s[o] = i[o * 4];
|
|
591
|
+
return s;
|
|
592
|
+
}
|
|
593
|
+
async loadVideo() {
|
|
594
|
+
const t = document.createElement("video");
|
|
595
|
+
t.crossOrigin = "anonymous", t.muted = !0, t.playsInline = !0, t.preload = "metadata", t.src = this.manifest.media.url, this.video = t, await new Promise((r, n) => {
|
|
596
|
+
t.addEventListener("loadedmetadata", () => r(), {
|
|
597
|
+
once: !0
|
|
598
|
+
}), t.addEventListener(
|
|
599
|
+
"error",
|
|
600
|
+
() => n(
|
|
601
|
+
new Error(
|
|
602
|
+
`Failed to load GeoVideo media: ${this.manifest.media.url}`
|
|
603
|
+
)
|
|
604
|
+
),
|
|
605
|
+
{ once: !0 }
|
|
606
|
+
);
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
/** Seek to a frame and wait until it is presented. */
|
|
610
|
+
async presentFrame(t, r) {
|
|
611
|
+
if (this.presented && Math.abs(t.currentTime - r) <= 1e-3 && !t.seeking)
|
|
612
|
+
return;
|
|
613
|
+
const n = ye(t), a = M(t, "seeked");
|
|
614
|
+
t.currentTime = r, await a, t.readyState < HTMLMediaElement.HAVE_CURRENT_DATA && await Promise.race([M(t, "loadeddata"), M(t, "canplay")]), await n, this.presented = !0;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Some mobile browsers expose no decoded pixels to WebGL until playback
|
|
618
|
+
* started once. A muted play/pause primes the decoder, only when needed:
|
|
619
|
+
* playback would otherwise buffer far beyond the requested frame.
|
|
620
|
+
*/
|
|
621
|
+
async primeDecoder(t) {
|
|
622
|
+
this.primed = !0;
|
|
623
|
+
try {
|
|
624
|
+
await t.play();
|
|
625
|
+
} catch {
|
|
626
|
+
}
|
|
627
|
+
t.pause(), this.presented = !1;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Seek, read back and decode one step. `signal` aborts it between steps;
|
|
631
|
+
* a seek already started completes first, keeping the queue serial.
|
|
632
|
+
*/
|
|
633
|
+
async decode(t, r) {
|
|
634
|
+
if (r.aborted)
|
|
635
|
+
throw l();
|
|
636
|
+
await this.init();
|
|
637
|
+
const n = this.video, a = this.mask;
|
|
638
|
+
if (r.aborted || !n || !a)
|
|
639
|
+
throw l();
|
|
640
|
+
const i = Ae(this.manifest, this.times[t]);
|
|
641
|
+
if (await this.presentFrame(n, i), r.aborted || n !== this.video)
|
|
642
|
+
throw l();
|
|
643
|
+
const s = this.height * this.planes;
|
|
644
|
+
let o = this.reader.read(n, this.width, s);
|
|
645
|
+
if (C(o) && !this.primed) {
|
|
646
|
+
if (await this.primeDecoder(n), await this.presentFrame(n, i), r.aborted || n !== this.video)
|
|
647
|
+
throw l();
|
|
648
|
+
o = this.reader.read(n, this.width, s);
|
|
649
|
+
}
|
|
650
|
+
if (C(o))
|
|
651
|
+
throw new Error("GeoVideo frame could not be read back from the decoder");
|
|
652
|
+
const c = Fe(o, this.manifest.encoding.colorRange);
|
|
653
|
+
return this.decodeCodes(c, a, this.manifest.mask.threshold);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
function De(e, t) {
|
|
657
|
+
let r = (e - t.codeMin) / (t.codeMax - t.codeMin) * 2 - 1;
|
|
658
|
+
return r = Math.max(-1, Math.min(1, r)), t.transfer === "sqrt" && (r = Math.sign(r) * r * r), r * t.valueDomain;
|
|
659
|
+
}
|
|
660
|
+
function Me(e, t, r, n) {
|
|
661
|
+
const a = t.length;
|
|
662
|
+
if (e.length !== 2 * a)
|
|
663
|
+
throw new Error(
|
|
664
|
+
`GeoVideo vector frame has ${e.length} codes, expected ${2 * a}`
|
|
665
|
+
);
|
|
666
|
+
const i = new Float32Array(256);
|
|
667
|
+
for (let d = 0; d < 256; d++)
|
|
668
|
+
i[d] = De(d, n);
|
|
669
|
+
const s = r * 255, o = new Float32Array(a), c = new Float32Array(a);
|
|
670
|
+
for (let d = 0; d < a; d++) {
|
|
671
|
+
const h = t[d] >= s;
|
|
672
|
+
o[d] = h ? i[e[d]] : NaN, c[d] = h ? i[e[a + d]] : NaN;
|
|
673
|
+
}
|
|
674
|
+
return { u: o, v: c };
|
|
675
|
+
}
|
|
676
|
+
class Ue extends O {
|
|
677
|
+
constructor(t, r = {}) {
|
|
678
|
+
if (t.encoding.kind !== "vector-luma")
|
|
679
|
+
throw new Error("GeoVideoVectorSource requires a vector-luma manifest");
|
|
680
|
+
super(t, 2, r), this.encoding = t.encoding;
|
|
681
|
+
const [n, a, i, s] = t.bounds, o = (i - n) / this.width, c = (s - a) / this.height;
|
|
682
|
+
this.coords = {
|
|
683
|
+
time: this.times,
|
|
684
|
+
vertical: new Float32Array([0]),
|
|
685
|
+
latitude: Float32Array.from(
|
|
686
|
+
{ length: this.height },
|
|
687
|
+
(d, h) => s - (h + 0.5) * c
|
|
688
|
+
),
|
|
689
|
+
longitude: Float32Array.from(
|
|
690
|
+
{ length: this.width },
|
|
691
|
+
(d, h) => n + (h + 0.5) * o
|
|
692
|
+
)
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
getValueDomain() {
|
|
696
|
+
return this.encoding.valueDomain;
|
|
697
|
+
}
|
|
698
|
+
getCoords() {
|
|
699
|
+
return this.coords;
|
|
700
|
+
}
|
|
701
|
+
getDimensions() {
|
|
702
|
+
return ["time", "latitude", "longitude"];
|
|
703
|
+
}
|
|
704
|
+
getChunkShape() {
|
|
705
|
+
return [1, this.height, this.width];
|
|
706
|
+
}
|
|
707
|
+
findDepthIndex() {
|
|
708
|
+
return 0;
|
|
709
|
+
}
|
|
710
|
+
findTimeIndex(t) {
|
|
711
|
+
const r = typeof t == "number" ? t : new Date(t).getTime(), n = this.coords.time;
|
|
712
|
+
let a = 0;
|
|
713
|
+
for (let i = 1; i < n.length; i++)
|
|
714
|
+
Math.abs(n[i] - r) < Math.abs(n[a] - r) && (a = i);
|
|
715
|
+
return a;
|
|
716
|
+
}
|
|
717
|
+
getChunksForBounds(t, r, n) {
|
|
718
|
+
const { latitude: a, longitude: i } = this.coords;
|
|
719
|
+
return [
|
|
720
|
+
{
|
|
721
|
+
timeIdx: r,
|
|
722
|
+
depthIdx: n,
|
|
723
|
+
latIdx: 0,
|
|
724
|
+
lonIdx: 0,
|
|
725
|
+
latRange: [a[0], a[this.height - 1]],
|
|
726
|
+
lonRange: [i[0], i[this.width - 1]],
|
|
727
|
+
latSize: this.height,
|
|
728
|
+
lonSize: this.width
|
|
729
|
+
}
|
|
730
|
+
];
|
|
731
|
+
}
|
|
732
|
+
async fetchSpatialChunkResult(t, r) {
|
|
733
|
+
const n = await this.frame(r.timeIndex);
|
|
734
|
+
return {
|
|
735
|
+
data: t === "v" ? n.v : n.u,
|
|
736
|
+
missing: !1,
|
|
737
|
+
url: this.manifest.media.url
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
decodeCodes(t, r, n) {
|
|
741
|
+
return Me(t, r, n, this.encoding);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
function Se(e, t) {
|
|
745
|
+
const r = Math.max(
|
|
746
|
+
0,
|
|
747
|
+
Math.min(
|
|
748
|
+
1,
|
|
749
|
+
(e - t.codeMin) / (t.codeMax - t.codeMin)
|
|
750
|
+
)
|
|
751
|
+
);
|
|
752
|
+
return t.valueMin + r * (t.valueMax - t.valueMin);
|
|
753
|
+
}
|
|
754
|
+
function Ge(e, t, r, n) {
|
|
755
|
+
const a = t.length;
|
|
756
|
+
if (e.length !== a)
|
|
757
|
+
throw new Error(
|
|
758
|
+
`GeoVideo scalar frame has ${e.length} codes, expected ${a}`
|
|
759
|
+
);
|
|
760
|
+
const i = new Float32Array(256);
|
|
761
|
+
for (let c = 0; c < 256; c++)
|
|
762
|
+
i[c] = Se(c, n);
|
|
763
|
+
const s = r * 255, o = new Float32Array(a);
|
|
764
|
+
for (let c = 0; c < a; c++)
|
|
765
|
+
o[c] = t[c] >= s ? i[e[c]] : NaN;
|
|
766
|
+
return o;
|
|
767
|
+
}
|
|
768
|
+
class Pe extends O {
|
|
769
|
+
constructor(t, r = {}) {
|
|
770
|
+
if (t.encoding.kind !== "scalar-luma")
|
|
771
|
+
throw new Error("GeoVideoScalarSource requires a scalar-luma manifest");
|
|
772
|
+
super(t, 1, r), this.encoding = t.encoding;
|
|
773
|
+
}
|
|
774
|
+
decodeCodes(t, r, n) {
|
|
775
|
+
return Ge(t, r, n, this.encoding);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
function ke(e, t, r = {}) {
|
|
779
|
+
const [n, a] = r.range ?? [-1 / 0, 1 / 0], i = [...e].sort((o, c) => o.start - c.start), s = i.find(
|
|
780
|
+
(o) => o.start > t && o.start <= a
|
|
781
|
+
) ?? (r.loop ? i.find(
|
|
782
|
+
(o) => o.end >= n && o.start <= a
|
|
783
|
+
) : void 0);
|
|
784
|
+
return s ? { chunk: s, time: Math.max(s.start, n) } : null;
|
|
785
|
+
}
|
|
786
|
+
function Ie(e, t) {
|
|
787
|
+
let r = null, n = -1 / 0;
|
|
788
|
+
for (const a of t) {
|
|
789
|
+
const i = Math.min(e.end, a.end) - Math.max(e.start, a.start);
|
|
790
|
+
Number.isFinite(i) && i >= 0 && i > n && (r = a, n = i);
|
|
791
|
+
}
|
|
792
|
+
return r;
|
|
793
|
+
}
|
|
794
|
+
function Ne(e) {
|
|
795
|
+
return {
|
|
796
|
+
width: e.mask.width,
|
|
797
|
+
height: e.mask.height,
|
|
798
|
+
bounds: e.bounds,
|
|
799
|
+
rowOrder: "north-to-south"
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function Le(e, t = {}) {
|
|
803
|
+
return e.encoding.kind === "vector-luma" ? new Ue(e, t) : new Pe(e, t);
|
|
804
|
+
}
|
|
805
|
+
const Ve = 3, Ce = 3, Be = 0.6, ge = 1.5;
|
|
806
|
+
function Xe(e, t) {
|
|
807
|
+
const r = e.length - 1;
|
|
808
|
+
if (t <= e[0])
|
|
809
|
+
return { step: 0, fraction: 0 };
|
|
810
|
+
if (t >= e[r])
|
|
811
|
+
return { step: r, fraction: 0 };
|
|
812
|
+
let n = 0, a = r;
|
|
813
|
+
for (; a - n > 1; ) {
|
|
814
|
+
const i = n + a >> 1;
|
|
815
|
+
e[i] <= t ? n = i : a = i;
|
|
816
|
+
}
|
|
817
|
+
return { step: n, fraction: (t - e[n]) / (e[a] - e[n]) };
|
|
818
|
+
}
|
|
819
|
+
function Oe(e) {
|
|
820
|
+
const t = new AbortController();
|
|
821
|
+
if (!e)
|
|
822
|
+
return { controller: t, unlink: () => {
|
|
823
|
+
} };
|
|
824
|
+
const r = () => t.abort();
|
|
825
|
+
return e.aborted && r(), e.addEventListener("abort", r, { once: !0 }), {
|
|
826
|
+
controller: t,
|
|
827
|
+
unlink: () => e.removeEventListener("abort", r)
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
class ft {
|
|
831
|
+
constructor(t) {
|
|
832
|
+
this.manifests = /* @__PURE__ */ new Map(), this.open = /* @__PURE__ */ new Map(), this.id = t.id, this.chunks = t.chunks.filter(
|
|
833
|
+
(r) => Number.isFinite(r.start) && Number.isFinite(r.end) && r.end >= r.start
|
|
834
|
+
).sort((r, n) => r.start - n.start), this.maxOpenChunks = Math.max(2, t.maxOpenChunks ?? Ve), this.frameCacheSize = Math.max(
|
|
835
|
+
1,
|
|
836
|
+
t.frameCacheSize ?? Ce
|
|
837
|
+
), this.prefetchAt = t.prefetchAt ?? Be, this.maxGap = t.maxGap, this.loadManifest = t.loadManifest ?? Re, this.createFrameSource = t.createFrameSource ?? ((r, n) => Le(
|
|
838
|
+
r,
|
|
839
|
+
n
|
|
840
|
+
));
|
|
841
|
+
}
|
|
842
|
+
/** First and last sample of the archive, or null without chunks. */
|
|
843
|
+
get range() {
|
|
844
|
+
const { chunks: t } = this;
|
|
845
|
+
return t.length ? [t[0].start, Math.max(...t.map((r) => r.end))] : null;
|
|
846
|
+
}
|
|
847
|
+
/** The chunk whose sample range holds `time`, or null. */
|
|
848
|
+
chunkAt(t) {
|
|
849
|
+
const r = this.chunkIndexAt(t), n = r >= 0 ? this.chunks[r] : null;
|
|
850
|
+
return n && t <= n.end ? n : null;
|
|
851
|
+
}
|
|
852
|
+
/** Every step time of the archive; loads all chunk manifests. */
|
|
853
|
+
async steps(t = {}) {
|
|
854
|
+
if (t.signal?.aborted)
|
|
855
|
+
throw l();
|
|
856
|
+
const r = await R(
|
|
857
|
+
Promise.all(this.chunks.map((n) => this.manifest(n))),
|
|
858
|
+
t.signal
|
|
859
|
+
);
|
|
860
|
+
return Float64Array.from(r.flatMap(_));
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* The frames bracketing `time`, decoded; null out of coverage (before the
|
|
864
|
+
* first step, after the last, or in a gap wider than `maxGap`).
|
|
865
|
+
*/
|
|
866
|
+
async sample(t, r = {}) {
|
|
867
|
+
const { signal: n } = r, a = r.mode ?? "interpolate";
|
|
868
|
+
if (n?.aborted)
|
|
869
|
+
throw l();
|
|
870
|
+
const i = this.chunkIndexAt(t);
|
|
871
|
+
if (i < 0)
|
|
872
|
+
return null;
|
|
873
|
+
const s = [], { controller: o, unlink: c } = Oe(n);
|
|
874
|
+
try {
|
|
875
|
+
const d = this.acquire(this.chunks[i], s), h = await R(d.ready, o.signal), { times: u } = h;
|
|
876
|
+
let E;
|
|
877
|
+
if (t <= u[u.length - 1]) {
|
|
878
|
+
const { step: p, fraction: w } = Xe(u, t);
|
|
879
|
+
E = a === "discrete" || w === 0 ? [d, p, d, p, 0] : [d, p, d, p + 1, w];
|
|
880
|
+
} else {
|
|
881
|
+
const p = this.chunks[i + 1];
|
|
882
|
+
if (!p)
|
|
883
|
+
return null;
|
|
884
|
+
const w = this.acquire(p, s), k = await R(w.ready, o.signal), A = u.length - 1, v = u[A], y = k.times[0];
|
|
885
|
+
if (y - v > this.gapLimit(u, k.times))
|
|
886
|
+
return null;
|
|
887
|
+
E = a === "discrete" ? [d, A, d, A, 0] : t >= y ? [w, 0, w, 0, 0] : [d, A, w, 0, (t - v) / (y - v)];
|
|
888
|
+
}
|
|
889
|
+
const [f, T, b, S, q] = E, G = f === b && T === S, P = [this.frame(f, T, o.signal)];
|
|
890
|
+
G || P.push(this.frame(b, S, o.signal));
|
|
891
|
+
let x;
|
|
892
|
+
try {
|
|
893
|
+
x = await Promise.all(P);
|
|
894
|
+
} catch (p) {
|
|
895
|
+
throw o.abort(), p;
|
|
896
|
+
}
|
|
897
|
+
return this.prefetchAfter(i, t), {
|
|
898
|
+
time: t,
|
|
899
|
+
a: x[0],
|
|
900
|
+
b: x[G ? 0 : 1],
|
|
901
|
+
mix: q
|
|
902
|
+
};
|
|
903
|
+
} finally {
|
|
904
|
+
c();
|
|
905
|
+
for (const d of s)
|
|
906
|
+
d.users--;
|
|
907
|
+
this.trim();
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
/** Open `chunk` and decode its first frame ahead of a request. */
|
|
911
|
+
prefetch(t) {
|
|
912
|
+
if (this.open.has(t.key))
|
|
913
|
+
return;
|
|
914
|
+
const r = this.touch(t), n = new AbortController();
|
|
915
|
+
r.prefetch = n, r.ready.then(({ source: a }) => a.frame(0, { signal: n.signal })).catch(() => {
|
|
916
|
+
}).finally(() => {
|
|
917
|
+
r.prefetch === n && (r.prefetch = null);
|
|
918
|
+
}), this.trim();
|
|
919
|
+
}
|
|
920
|
+
/** Release every open chunk source; later requests reopen them. */
|
|
921
|
+
release() {
|
|
922
|
+
for (const t of [...this.open.values()])
|
|
923
|
+
this.close(t);
|
|
924
|
+
}
|
|
925
|
+
chunkIndexAt(t) {
|
|
926
|
+
let r = 0, n = this.chunks.length - 1, a = -1;
|
|
927
|
+
for (; r <= n; ) {
|
|
928
|
+
const i = r + n >> 1;
|
|
929
|
+
this.chunks[i].start <= t ? (a = i, r = i + 1) : n = i - 1;
|
|
930
|
+
}
|
|
931
|
+
return a;
|
|
932
|
+
}
|
|
933
|
+
gapLimit(t, r) {
|
|
934
|
+
if (this.maxGap !== void 0)
|
|
935
|
+
return this.maxGap;
|
|
936
|
+
const n = [
|
|
937
|
+
t.length > 1 ? t[t.length - 1] - t[t.length - 2] : 0,
|
|
938
|
+
r.length > 1 ? r[1] - r[0] : 0
|
|
939
|
+
], a = Math.max(...n);
|
|
940
|
+
return a > 0 ? a * ge : -1 / 0;
|
|
941
|
+
}
|
|
942
|
+
prefetchAfter(t, r) {
|
|
943
|
+
const n = this.chunks[t], a = this.chunks[t + 1], i = n.end - n.start, s = i > 0 ? (r - n.start) / i : 1;
|
|
944
|
+
a && s > this.prefetchAt && this.prefetch(a);
|
|
945
|
+
}
|
|
946
|
+
manifest(t) {
|
|
947
|
+
let r = this.manifests.get(t.manifestUrl);
|
|
948
|
+
return r || (r = this.loadManifest(t.manifestUrl), this.manifests.set(t.manifestUrl, r), r.catch(() => {
|
|
949
|
+
this.manifests.get(t.manifestUrl) === r && this.manifests.delete(t.manifestUrl);
|
|
950
|
+
})), r;
|
|
951
|
+
}
|
|
952
|
+
/** The open chunk, pinned for a running sample listed in `used`. */
|
|
953
|
+
acquire(t, r) {
|
|
954
|
+
const n = this.touch(t);
|
|
955
|
+
return n.users++, r.push(n), n;
|
|
956
|
+
}
|
|
957
|
+
/** The open chunk, opened when needed, most recently used; not pinned. */
|
|
958
|
+
touch(t) {
|
|
959
|
+
let r = this.open.get(t.key);
|
|
960
|
+
if (r)
|
|
961
|
+
this.open.delete(t.key);
|
|
962
|
+
else {
|
|
963
|
+
const n = {
|
|
964
|
+
chunk: t,
|
|
965
|
+
ready: null,
|
|
966
|
+
opened: null,
|
|
967
|
+
users: 0,
|
|
968
|
+
prefetch: null,
|
|
969
|
+
released: !1
|
|
970
|
+
};
|
|
971
|
+
n.ready = this.manifest(t).then((a) => {
|
|
972
|
+
if (n.released)
|
|
973
|
+
throw l();
|
|
974
|
+
return n.opened = {
|
|
975
|
+
manifest: a,
|
|
976
|
+
times: Float64Array.from(_(a)),
|
|
977
|
+
grid: Ne(a),
|
|
978
|
+
source: this.createFrameSource(a, {
|
|
979
|
+
cacheSize: this.frameCacheSize
|
|
980
|
+
})
|
|
981
|
+
}, n.opened;
|
|
982
|
+
}), n.ready.catch(() => {
|
|
983
|
+
this.open.get(t.key) === n && this.open.delete(t.key);
|
|
984
|
+
}), r = n;
|
|
985
|
+
}
|
|
986
|
+
return this.open.set(t.key, r), r;
|
|
987
|
+
}
|
|
988
|
+
async frame(t, r, n) {
|
|
989
|
+
const { manifest: a, times: i, grid: s, source: o } = await R(
|
|
990
|
+
t.ready,
|
|
991
|
+
n
|
|
992
|
+
);
|
|
993
|
+
if (t.released)
|
|
994
|
+
throw l();
|
|
995
|
+
const c = await o.frame(r, { signal: n });
|
|
996
|
+
return {
|
|
997
|
+
key: `${this.id}/${t.chunk.key}/${r}`,
|
|
998
|
+
chunk: t.chunk,
|
|
999
|
+
step: r,
|
|
1000
|
+
time: i[r],
|
|
1001
|
+
data: c,
|
|
1002
|
+
grid: s,
|
|
1003
|
+
manifest: a
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
/** Release least recently used chunks beyond the bound, unless in use. */
|
|
1007
|
+
trim() {
|
|
1008
|
+
let t = this.open.size - this.maxOpenChunks;
|
|
1009
|
+
for (const r of [...this.open.values()]) {
|
|
1010
|
+
if (t <= 0)
|
|
1011
|
+
return;
|
|
1012
|
+
r.users === 0 && (this.close(r), t--);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
close(t) {
|
|
1016
|
+
t.released = !0, t.prefetch?.abort(), t.prefetch = null, t.opened?.source.release(), t.opened = null, this.open.get(t.chunk.key) === t && this.open.delete(t.chunk.key);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
function U(e) {
|
|
1020
|
+
const { start: t, end: r } = e.temporal ?? {};
|
|
1021
|
+
return {
|
|
1022
|
+
source: e,
|
|
1023
|
+
start: Date.parse(t ?? ""),
|
|
1024
|
+
end: Date.parse(r ?? t ?? "")
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function lt(e, t, r = {}) {
|
|
1028
|
+
const n = e.filter((i) => i.id === t.id && i.temporal?.start != null).map(U), a = ke(
|
|
1029
|
+
n,
|
|
1030
|
+
Date.parse(t.temporal?.end ?? ""),
|
|
1031
|
+
r
|
|
1032
|
+
);
|
|
1033
|
+
return a ? { source: a.chunk.source, time: a.time } : null;
|
|
1034
|
+
}
|
|
1035
|
+
function ht(e, t) {
|
|
1036
|
+
return Ie(U(e), t.map(U))?.source ?? null;
|
|
1037
|
+
}
|
|
1038
|
+
function mt(e) {
|
|
1039
|
+
return e.type === "geovideo" && typeof e.indexUrl == "string";
|
|
1040
|
+
}
|
|
1041
|
+
function Et(e, t) {
|
|
1042
|
+
return e.id === t.id && (e.type !== "geovideo" || t.type !== "geovideo" || e.manifestUrl === t.manifestUrl);
|
|
1043
|
+
}
|
|
1044
|
+
function B(e) {
|
|
1045
|
+
return typeof e == "string" && Number.isFinite(Date.parse(e));
|
|
1046
|
+
}
|
|
1047
|
+
function qe(e) {
|
|
1048
|
+
const t = e;
|
|
1049
|
+
if (!t || t.schemaVersion !== 1 || t.type !== "geovideo-index" || !Array.isArray(t.chunks))
|
|
1050
|
+
throw new Error("Unsupported GeoVideo index");
|
|
1051
|
+
for (const r of t.chunks)
|
|
1052
|
+
if (!B(r?.start) || !B(r.end) || Date.parse(r.end) < Date.parse(r.start) || typeof r.manifestUrl != "string")
|
|
1053
|
+
throw new Error("Invalid GeoVideo index chunk");
|
|
1054
|
+
return t;
|
|
1055
|
+
}
|
|
1056
|
+
async function Tt(e, t) {
|
|
1057
|
+
const r = await fetch(e, { signal: t });
|
|
1058
|
+
if (!r.ok)
|
|
1059
|
+
throw new Error(
|
|
1060
|
+
`GeoVideo index request failed (${r.status}): ${e}`
|
|
1061
|
+
);
|
|
1062
|
+
return qe(await r.json());
|
|
1063
|
+
}
|
|
1064
|
+
function $e(e) {
|
|
1065
|
+
return typeof document < "u" ? new URL(e, document.baseURI).href : e;
|
|
1066
|
+
}
|
|
1067
|
+
function We(e, t) {
|
|
1068
|
+
const r = $e(t);
|
|
1069
|
+
return e.chunks.map((n) => ({
|
|
1070
|
+
key: n.key,
|
|
1071
|
+
manifestUrl: new URL(n.manifestUrl, r).href,
|
|
1072
|
+
start: Date.parse(n.start),
|
|
1073
|
+
end: Date.parse(n.end),
|
|
1074
|
+
samples: n.samples,
|
|
1075
|
+
...n.provisional ? { provisional: !0 } : {}
|
|
1076
|
+
}));
|
|
1077
|
+
}
|
|
1078
|
+
function He(e, t, r) {
|
|
1079
|
+
if (t.sourceId !== e.id || t.catalogEntryId !== r.id)
|
|
1080
|
+
throw new Error(
|
|
1081
|
+
`GeoVideo index identity does not match catalog entry/source: ${e.id}`
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
function pt(e, t, r) {
|
|
1085
|
+
He(e, t, r);
|
|
1086
|
+
const n = We(t, e.indexUrl);
|
|
1087
|
+
return t.chunks.map(
|
|
1088
|
+
(a, i) => ({
|
|
1089
|
+
id: e.id,
|
|
1090
|
+
type: "geovideo",
|
|
1091
|
+
title: e.title,
|
|
1092
|
+
provenance: e.provenance,
|
|
1093
|
+
temporal: {
|
|
1094
|
+
mode: "fixed",
|
|
1095
|
+
cadence: e.temporal?.cadence,
|
|
1096
|
+
start: a.start,
|
|
1097
|
+
end: a.end
|
|
1098
|
+
},
|
|
1099
|
+
manifestUrl: n[i].manifestUrl
|
|
1100
|
+
})
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
export {
|
|
1104
|
+
Tt as A,
|
|
1105
|
+
pt as B,
|
|
1106
|
+
V as C,
|
|
1107
|
+
ft as D,
|
|
1108
|
+
O as E,
|
|
1109
|
+
Pe as F,
|
|
1110
|
+
Ue as G,
|
|
1111
|
+
He as H,
|
|
1112
|
+
Le as I,
|
|
1113
|
+
N as J,
|
|
1114
|
+
Te as K,
|
|
1115
|
+
Ne as L,
|
|
1116
|
+
We as M,
|
|
1117
|
+
ke as N,
|
|
1118
|
+
ht as O,
|
|
1119
|
+
Ie as P,
|
|
1120
|
+
qe as Q,
|
|
1121
|
+
st as T,
|
|
1122
|
+
tt as a,
|
|
1123
|
+
rt as b,
|
|
1124
|
+
ze as c,
|
|
1125
|
+
Je as d,
|
|
1126
|
+
Qe as e,
|
|
1127
|
+
et as f,
|
|
1128
|
+
nt as g,
|
|
1129
|
+
je as h,
|
|
1130
|
+
Ze as i,
|
|
1131
|
+
Ke as j,
|
|
1132
|
+
it as k,
|
|
1133
|
+
ot as l,
|
|
1134
|
+
Re as m,
|
|
1135
|
+
Ae as n,
|
|
1136
|
+
fe as o,
|
|
1137
|
+
ut as p,
|
|
1138
|
+
_ as q,
|
|
1139
|
+
he as r,
|
|
1140
|
+
at as s,
|
|
1141
|
+
Et as t,
|
|
1142
|
+
dt as u,
|
|
1143
|
+
ct as v,
|
|
1144
|
+
_e as w,
|
|
1145
|
+
Ye as x,
|
|
1146
|
+
mt as y,
|
|
1147
|
+
lt as z
|
|
1148
|
+
};
|