@gisatcz/deckgl-geolib 2.6.0-dev.4 → 2.6.0-dev.6
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/cjs/index.js +424 -70
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.min.js +2 -2
- package/dist/cjs/index.min.js.map +1 -1
- package/dist/esm/index.js +424 -71
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +2 -2
- package/dist/esm/index.min.js.map +1 -1
- package/dist/esm/types/core/CogTiles.d.ts +1 -0
- package/dist/esm/types/core/GeoImage.d.ts +4 -2
- package/dist/esm/types/core/lib/TerrainGenerator.d.ts +2 -1
- package/dist/esm/types/core/types.d.ts +2 -0
- package/dist/esm/types/index.d.ts +1 -0
- package/dist/esm/types/layers/CogTerrainLayer.d.ts +15 -0
- package/dist/esm/types/workers/TerrainWorkerPool.d.ts +58 -0
- package/dist/esm/types/workers/terrain.worker.d.ts +5 -0
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -4969,6 +4969,7 @@ const DefaultGeoImageOptions = {
|
|
|
4969
4969
|
disableLighting: false,
|
|
4970
4970
|
// --- Animation / Caching ---
|
|
4971
4971
|
cacheAllBands: false,
|
|
4972
|
+
disableWorkerPool: false,
|
|
4972
4973
|
};
|
|
4973
4974
|
|
|
4974
4975
|
class Martini {
|
|
@@ -6329,7 +6330,8 @@ class ReliefCompositor {
|
|
|
6329
6330
|
}
|
|
6330
6331
|
|
|
6331
6332
|
class TerrainGenerator {
|
|
6332
|
-
static async generate(input, options, meshMaxError)
|
|
6333
|
+
static async generate(input, options, meshMaxError, workerPool, // TerrainWorkerPool (optional for flexibility)
|
|
6334
|
+
signal) {
|
|
6333
6335
|
const { width, height } = input;
|
|
6334
6336
|
const isKernel = width === 258;
|
|
6335
6337
|
// 1. Compute Terrain Data (Extract Elevation)
|
|
@@ -6337,27 +6339,47 @@ class TerrainGenerator {
|
|
|
6337
6339
|
// For kernel tiles, the mesh uses the inner 257×257 sub-grid (rows 1–257, cols 1–257)
|
|
6338
6340
|
// so that row 0 / col 0 (kernel padding) is dropped while the bottom/right stitching
|
|
6339
6341
|
// overlap is preserved.
|
|
6340
|
-
|
|
6342
|
+
let meshTerrain = isKernel ? this.extractMeshRaster(terrain) : terrain;
|
|
6341
6343
|
const meshWidth = isKernel ? 257 : width;
|
|
6342
6344
|
const meshHeight = isKernel ? 257 : height;
|
|
6343
6345
|
// 2. Tesselate (Generate Mesh)
|
|
6344
6346
|
const { terrainSkirtHeight, verticalExaggeration = 1.0 } = options;
|
|
6345
6347
|
let mesh;
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6348
|
+
let meshTerrainForAttributes; // ← Will hold terrain for getMeshAttributes()
|
|
6349
|
+
if (workerPool) {
|
|
6350
|
+
// ✅ NEW: Offload to Web Worker with ZERO-COPY ROUNDTRIP
|
|
6351
|
+
// Transfer meshTerrain to worker; worker transfers it back alongside mesh
|
|
6352
|
+
// This avoids meshTerrain.slice() allocation on main thread
|
|
6353
|
+
const result = await workerPool.computeMesh({
|
|
6354
|
+
terrain: meshTerrain, // ← Transferred to worker (detached here)
|
|
6355
|
+
meshMaxError,
|
|
6356
|
+
tesselator: options.tesselator || 'martini',
|
|
6357
|
+
width: meshWidth,
|
|
6358
|
+
height: meshHeight,
|
|
6359
|
+
signal, // ← Thread cancellation signal to worker
|
|
6360
|
+
});
|
|
6361
|
+
mesh = { vertices: result.vertices, triangles: result.triangles };
|
|
6362
|
+
meshTerrainForAttributes = result.terrain; // ← Transferred back from worker
|
|
6363
|
+
meshTerrain = result.terrain; // ← Reassign for downstream use (tileResult.raw, etc.)
|
|
6364
|
+
}
|
|
6365
|
+
else {
|
|
6366
|
+
// ❌ FALLBACK: Synchronous (old behavior, kept for safety)
|
|
6367
|
+
switch (options.tesselator) {
|
|
6368
|
+
case 'martini':
|
|
6369
|
+
mesh = this.getMartiniTileMesh(meshMaxError, meshWidth, meshTerrain);
|
|
6370
|
+
break;
|
|
6371
|
+
case 'delatin':
|
|
6372
|
+
mesh = this.getDelatinTileMesh(meshMaxError, meshWidth, meshHeight, meshTerrain);
|
|
6373
|
+
break;
|
|
6374
|
+
default:
|
|
6375
|
+
mesh = this.getMartiniTileMesh(meshMaxError, meshWidth, meshTerrain);
|
|
6376
|
+
break;
|
|
6377
|
+
}
|
|
6378
|
+
meshTerrainForAttributes = meshTerrain; // ← Use original
|
|
6357
6379
|
}
|
|
6358
6380
|
const { vertices } = mesh;
|
|
6359
6381
|
let { triangles } = mesh;
|
|
6360
|
-
let attributes = this.getMeshAttributes(vertices,
|
|
6382
|
+
let attributes = this.getMeshAttributes(vertices, meshTerrainForAttributes, meshWidth, meshHeight, input.bounds, verticalExaggeration);
|
|
6361
6383
|
// Compute bounding box before adding skirt so that z values are not skewed
|
|
6362
6384
|
const boundingBox = getMeshBoundingBox(attributes);
|
|
6363
6385
|
if (terrainSkirtHeight) {
|
|
@@ -6624,13 +6646,14 @@ class GeoImage {
|
|
|
6624
6646
|
const data = await tiff.getImage(0);
|
|
6625
6647
|
this.data = data;
|
|
6626
6648
|
}
|
|
6627
|
-
async getMap(input, options, meshMaxError)
|
|
6649
|
+
async getMap(input, options, meshMaxError, workerPool, // TerrainWorkerPool (optional)
|
|
6650
|
+
signal) {
|
|
6628
6651
|
const mergedOptions = GeoImage.resolveVisualizationMode({ ...DefaultGeoImageOptions, ...options }, options);
|
|
6629
6652
|
switch (mergedOptions.type) {
|
|
6630
6653
|
case 'image':
|
|
6631
6654
|
return this.getBitmap(input, mergedOptions);
|
|
6632
6655
|
case 'terrain':
|
|
6633
|
-
return this.getHeightmap(input, mergedOptions, meshMaxError);
|
|
6656
|
+
return this.getHeightmap(input, mergedOptions, meshMaxError, workerPool, signal);
|
|
6634
6657
|
default:
|
|
6635
6658
|
return null;
|
|
6636
6659
|
}
|
|
@@ -6687,7 +6710,8 @@ class GeoImage {
|
|
|
6687
6710
|
return resolved;
|
|
6688
6711
|
}
|
|
6689
6712
|
// GetHeightmap uses only "useChannel" and "multiplier" options
|
|
6690
|
-
async getHeightmap(input, options, meshMaxError)
|
|
6713
|
+
async getHeightmap(input, options, meshMaxError, workerPool, // TerrainWorkerPool (optional)
|
|
6714
|
+
signal) {
|
|
6691
6715
|
let rasters = [];
|
|
6692
6716
|
let width;
|
|
6693
6717
|
let height;
|
|
@@ -6709,8 +6733,8 @@ class GeoImage {
|
|
|
6709
6733
|
bounds = input.bounds;
|
|
6710
6734
|
cellSizeMeters = input.cellSizeMeters;
|
|
6711
6735
|
}
|
|
6712
|
-
// Delegate to TerrainGenerator
|
|
6713
|
-
return await TerrainGenerator.generate({ width, height, rasters, bounds, cellSizeMeters }, options, meshMaxError);
|
|
6736
|
+
// Delegate to TerrainGenerator with worker pool and cancellation signal
|
|
6737
|
+
return await TerrainGenerator.generate({ width, height, rasters, bounds, cellSizeMeters }, options, meshMaxError, workerPool, signal);
|
|
6714
6738
|
}
|
|
6715
6739
|
async getBitmap(input, options) {
|
|
6716
6740
|
let rasters = [];
|
|
@@ -6734,6 +6758,218 @@ class GeoImage {
|
|
|
6734
6758
|
}
|
|
6735
6759
|
}
|
|
6736
6760
|
|
|
6761
|
+
function decodeBase64(base64, enableUnicode) {
|
|
6762
|
+
var binaryString = atob(base64);
|
|
6763
|
+
return binaryString;
|
|
6764
|
+
}
|
|
6765
|
+
|
|
6766
|
+
function createURL(base64, sourcemapArg, enableUnicodeArg) {
|
|
6767
|
+
var source = decodeBase64(base64);
|
|
6768
|
+
var start = source.indexOf('\n', 10) + 1;
|
|
6769
|
+
var body = source.substring(start) + ('');
|
|
6770
|
+
var blob = new Blob([body], { type: 'application/javascript' });
|
|
6771
|
+
return URL.createObjectURL(blob);
|
|
6772
|
+
}
|
|
6773
|
+
|
|
6774
|
+
function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) {
|
|
6775
|
+
var url;
|
|
6776
|
+
return function WorkerFactory(options) {
|
|
6777
|
+
url = url || createURL(base64);
|
|
6778
|
+
return new Worker(url, options);
|
|
6779
|
+
};
|
|
6780
|
+
}
|
|
6781
|
+
|
|
6782
|
+
var WorkerFactory = /*#__PURE__*/createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICAgJ3VzZSBzdHJpY3QnOwoKICAgIGNsYXNzIE1hcnRpbmkgewogICAgICAgIGNvbnN0cnVjdG9yKGdyaWRTaXplID0gMjU3KSB7CiAgICAgICAgICAgIHRoaXMuZ3JpZFNpemUgPSBncmlkU2l6ZTsKICAgICAgICAgICAgY29uc3QgdGlsZVNpemUgPSBncmlkU2l6ZSAtIDE7CiAgICAgICAgICAgIGlmICh0aWxlU2l6ZSAmICh0aWxlU2l6ZSAtIDEpKSB0aHJvdyBuZXcgRXJyb3IoCiAgICAgICAgICAgICAgICBgRXhwZWN0ZWQgZ3JpZCBzaXplIHRvIGJlIDJebisxLCBnb3QgJHtncmlkU2l6ZX0uYCk7CgogICAgICAgICAgICB0aGlzLm51bVRyaWFuZ2xlcyA9IHRpbGVTaXplICogdGlsZVNpemUgKiAyIC0gMjsKICAgICAgICAgICAgdGhpcy5udW1QYXJlbnRUcmlhbmdsZXMgPSB0aGlzLm51bVRyaWFuZ2xlcyAtIHRpbGVTaXplICogdGlsZVNpemU7CgogICAgICAgICAgICB0aGlzLmluZGljZXMgPSBuZXcgVWludDMyQXJyYXkodGhpcy5ncmlkU2l6ZSAqIHRoaXMuZ3JpZFNpemUpOwoKICAgICAgICAgICAgLy8gY29vcmRpbmF0ZXMgZm9yIGFsbCBwb3NzaWJsZSB0cmlhbmdsZXMgaW4gYW4gUlRJTiB0aWxlCiAgICAgICAgICAgIHRoaXMuY29vcmRzID0gbmV3IFVpbnQxNkFycmF5KHRoaXMubnVtVHJpYW5nbGVzICogNCk7CgogICAgICAgICAgICAvLyBnZXQgdHJpYW5nbGUgY29vcmRpbmF0ZXMgZnJvbSBpdHMgaW5kZXggaW4gYW4gaW1wbGljaXQgYmluYXJ5IHRyZWUKICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm51bVRyaWFuZ2xlczsgaSsrKSB7CiAgICAgICAgICAgICAgICBsZXQgaWQgPSBpICsgMjsKICAgICAgICAgICAgICAgIGxldCBheCA9IDAsIGF5ID0gMCwgYnggPSAwLCBieSA9IDAsIGN4ID0gMCwgY3kgPSAwOwogICAgICAgICAgICAgICAgaWYgKGlkICYgMSkgewogICAgICAgICAgICAgICAgICAgIGJ4ID0gYnkgPSBjeCA9IHRpbGVTaXplOyAvLyBib3R0b20tbGVmdCB0cmlhbmdsZQogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICBheCA9IGF5ID0gY3kgPSB0aWxlU2l6ZTsgLy8gdG9wLXJpZ2h0IHRyaWFuZ2xlCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB3aGlsZSAoKGlkID4+PSAxKSA+IDEpIHsKICAgICAgICAgICAgICAgICAgICBjb25zdCBteCA9IChheCArIGJ4KSA+PiAxOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IG15ID0gKGF5ICsgYnkpID4+IDE7CgogICAgICAgICAgICAgICAgICAgIGlmIChpZCAmIDEpIHsgLy8gbGVmdCBoYWxmCiAgICAgICAgICAgICAgICAgICAgICAgIGJ4ID0gYXg7IGJ5ID0gYXk7CiAgICAgICAgICAgICAgICAgICAgICAgIGF4ID0gY3g7IGF5ID0gY3k7CiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsgLy8gcmlnaHQgaGFsZgogICAgICAgICAgICAgICAgICAgICAgICBheCA9IGJ4OyBheSA9IGJ5OwogICAgICAgICAgICAgICAgICAgICAgICBieCA9IGN4OyBieSA9IGN5OwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICBjeCA9IG14OyBjeSA9IG15OwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgY29uc3QgayA9IGkgKiA0OwogICAgICAgICAgICAgICAgdGhpcy5jb29yZHNbayArIDBdID0gYXg7CiAgICAgICAgICAgICAgICB0aGlzLmNvb3Jkc1trICsgMV0gPSBheTsKICAgICAgICAgICAgICAgIHRoaXMuY29vcmRzW2sgKyAyXSA9IGJ4OwogICAgICAgICAgICAgICAgdGhpcy5jb29yZHNbayArIDNdID0gYnk7CiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGNyZWF0ZVRpbGUodGVycmFpbikgewogICAgICAgICAgICByZXR1cm4gbmV3IFRpbGUodGVycmFpbiwgdGhpcyk7CiAgICAgICAgfQogICAgfQoKICAgIGNsYXNzIFRpbGUgewogICAgICAgIGNvbnN0cnVjdG9yKHRlcnJhaW4sIG1hcnRpbmkpIHsKICAgICAgICAgICAgY29uc3Qgc2l6ZSA9IG1hcnRpbmkuZ3JpZFNpemU7CiAgICAgICAgICAgIGlmICh0ZXJyYWluLmxlbmd0aCAhPT0gc2l6ZSAqIHNpemUpIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgICAgIGBFeHBlY3RlZCB0ZXJyYWluIGRhdGEgb2YgbGVuZ3RoICR7c2l6ZSAqIHNpemV9ICgke3NpemV9IHggJHtzaXplfSksIGdvdCAke3RlcnJhaW4ubGVuZ3RofS5gKTsKCiAgICAgICAgICAgIHRoaXMudGVycmFpbiA9IHRlcnJhaW47CiAgICAgICAgICAgIHRoaXMubWFydGluaSA9IG1hcnRpbmk7CiAgICAgICAgICAgIHRoaXMuZXJyb3JzID0gbmV3IEZsb2F0MzJBcnJheSh0ZXJyYWluLmxlbmd0aCk7CiAgICAgICAgICAgIHRoaXMudXBkYXRlKCk7CiAgICAgICAgfQoKICAgICAgICB1cGRhdGUoKSB7CiAgICAgICAgICAgIGNvbnN0IHtudW1UcmlhbmdsZXMsIG51bVBhcmVudFRyaWFuZ2xlcywgY29vcmRzLCBncmlkU2l6ZTogc2l6ZX0gPSB0aGlzLm1hcnRpbmk7CiAgICAgICAgICAgIGNvbnN0IHt0ZXJyYWluLCBlcnJvcnN9ID0gdGhpczsKCiAgICAgICAgICAgIC8vIGl0ZXJhdGUgb3ZlciBhbGwgcG9zc2libGUgdHJpYW5nbGVzLCBzdGFydGluZyBmcm9tIHRoZSBzbWFsbGVzdCBsZXZlbAogICAgICAgICAgICBmb3IgKGxldCBpID0gbnVtVHJpYW5nbGVzIC0gMTsgaSA+PSAwOyBpLS0pIHsKICAgICAgICAgICAgICAgIGNvbnN0IGsgPSBpICogNDsKICAgICAgICAgICAgICAgIGNvbnN0IGF4ID0gY29vcmRzW2sgKyAwXTsKICAgICAgICAgICAgICAgIGNvbnN0IGF5ID0gY29vcmRzW2sgKyAxXTsKICAgICAgICAgICAgICAgIGNvbnN0IGJ4ID0gY29vcmRzW2sgKyAyXTsKICAgICAgICAgICAgICAgIGNvbnN0IGJ5ID0gY29vcmRzW2sgKyAzXTsKICAgICAgICAgICAgICAgIGNvbnN0IG14ID0gKGF4ICsgYngpID4+IDE7CiAgICAgICAgICAgICAgICBjb25zdCBteSA9IChheSArIGJ5KSA+PiAxOwogICAgICAgICAgICAgICAgY29uc3QgY3ggPSBteCArIG15IC0gYXk7CiAgICAgICAgICAgICAgICBjb25zdCBjeSA9IG15ICsgYXggLSBteDsKCiAgICAgICAgICAgICAgICAvLyBjYWxjdWxhdGUgZXJyb3IgaW4gdGhlIG1pZGRsZSBvZiB0aGUgbG9uZyBlZGdlIG9mIHRoZSB0cmlhbmdsZQogICAgICAgICAgICAgICAgY29uc3QgaW50ZXJwb2xhdGVkSGVpZ2h0ID0gKHRlcnJhaW5bYXkgKiBzaXplICsgYXhdICsgdGVycmFpbltieSAqIHNpemUgKyBieF0pIC8gMjsKICAgICAgICAgICAgICAgIGNvbnN0IG1pZGRsZUluZGV4ID0gbXkgKiBzaXplICsgbXg7CiAgICAgICAgICAgICAgICBjb25zdCBtaWRkbGVFcnJvciA9IE1hdGguYWJzKGludGVycG9sYXRlZEhlaWdodCAtIHRlcnJhaW5bbWlkZGxlSW5kZXhdKTsKCiAgICAgICAgICAgICAgICBlcnJvcnNbbWlkZGxlSW5kZXhdID0gTWF0aC5tYXgoZXJyb3JzW21pZGRsZUluZGV4XSwgbWlkZGxlRXJyb3IpOwoKICAgICAgICAgICAgICAgIGlmIChpIDwgbnVtUGFyZW50VHJpYW5nbGVzKSB7IC8vIGJpZ2dlciB0cmlhbmdsZXM7IGFjY3VtdWxhdGUgZXJyb3Igd2l0aCBjaGlsZHJlbgogICAgICAgICAgICAgICAgICAgIGNvbnN0IGxlZnRDaGlsZEluZGV4ID0gKChheSArIGN5KSA+PiAxKSAqIHNpemUgKyAoKGF4ICsgY3gpID4+IDEpOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IHJpZ2h0Q2hpbGRJbmRleCA9ICgoYnkgKyBjeSkgPj4gMSkgKiBzaXplICsgKChieCArIGN4KSA+PiAxKTsKICAgICAgICAgICAgICAgICAgICBlcnJvcnNbbWlkZGxlSW5kZXhdID0gTWF0aC5tYXgoZXJyb3JzW21pZGRsZUluZGV4XSwgZXJyb3JzW2xlZnRDaGlsZEluZGV4XSwgZXJyb3JzW3JpZ2h0Q2hpbGRJbmRleF0pOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBnZXRNZXNoKG1heEVycm9yID0gMCkgewogICAgICAgICAgICBjb25zdCB7Z3JpZFNpemU6IHNpemUsIGluZGljZXN9ID0gdGhpcy5tYXJ0aW5pOwogICAgICAgICAgICBjb25zdCB7ZXJyb3JzfSA9IHRoaXM7CiAgICAgICAgICAgIGxldCBudW1WZXJ0aWNlcyA9IDA7CiAgICAgICAgICAgIGxldCBudW1UcmlhbmdsZXMgPSAwOwogICAgICAgICAgICBjb25zdCBtYXggPSBzaXplIC0gMTsKCiAgICAgICAgICAgIC8vIHVzZSBhbiBpbmRleCBncmlkIHRvIGtlZXAgdHJhY2sgb2YgdmVydGljZXMgdGhhdCB3ZXJlIGFscmVhZHkgdXNlZCB0byBhdm9pZCBkdXBsaWNhdGlvbgogICAgICAgICAgICBpbmRpY2VzLmZpbGwoMCk7CgogICAgICAgICAgICAvLyByZXRyaWV2ZSBtZXNoIGluIHR3byBzdGFnZXMgdGhhdCBib3RoIHRyYXZlcnNlIHRoZSBlcnJvciBtYXA6CiAgICAgICAgICAgIC8vIC0gY291bnRFbGVtZW50czogZmluZCB1c2VkIHZlcnRpY2VzIChhbmQgYXNzaWduIGVhY2ggYW4gaW5kZXgpLCBhbmQgY291bnQgdHJpYW5nbGVzIChmb3IgbWluaW11bSBhbGxvY2F0aW9uKQogICAgICAgICAgICAvLyAtIHByb2Nlc3NUcmlhbmdsZTogZmlsbCB0aGUgYWxsb2NhdGVkIHZlcnRpY2VzICYgdHJpYW5nbGVzIHR5cGVkIGFycmF5cwoKICAgICAgICAgICAgZnVuY3Rpb24gY291bnRFbGVtZW50cyhheCwgYXksIGJ4LCBieSwgY3gsIGN5KSB7CiAgICAgICAgICAgICAgICBjb25zdCBteCA9IChheCArIGJ4KSA+PiAxOwogICAgICAgICAgICAgICAgY29uc3QgbXkgPSAoYXkgKyBieSkgPj4gMTsKCiAgICAgICAgICAgICAgICBpZiAoTWF0aC5hYnMoYXggLSBjeCkgKyBNYXRoLmFicyhheSAtIGN5KSA+IDEgJiYgZXJyb3JzW215ICogc2l6ZSArIG14XSA+IG1heEVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgY291bnRFbGVtZW50cyhjeCwgY3ksIGF4LCBheSwgbXgsIG15KTsKICAgICAgICAgICAgICAgICAgICBjb3VudEVsZW1lbnRzKGJ4LCBieSwgY3gsIGN5LCBteCwgbXkpOwogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICBpbmRpY2VzW2F5ICogc2l6ZSArIGF4XSA9IGluZGljZXNbYXkgKiBzaXplICsgYXhdIHx8ICsrbnVtVmVydGljZXM7CiAgICAgICAgICAgICAgICAgICAgaW5kaWNlc1tieSAqIHNpemUgKyBieF0gPSBpbmRpY2VzW2J5ICogc2l6ZSArIGJ4XSB8fCArK251bVZlcnRpY2VzOwogICAgICAgICAgICAgICAgICAgIGluZGljZXNbY3kgKiBzaXplICsgY3hdID0gaW5kaWNlc1tjeSAqIHNpemUgKyBjeF0gfHwgKytudW1WZXJ0aWNlczsKICAgICAgICAgICAgICAgICAgICBudW1UcmlhbmdsZXMrKzsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBjb3VudEVsZW1lbnRzKDAsIDAsIG1heCwgbWF4LCBtYXgsIDApOwogICAgICAgICAgICBjb3VudEVsZW1lbnRzKG1heCwgbWF4LCAwLCAwLCAwLCBtYXgpOwoKICAgICAgICAgICAgY29uc3QgdmVydGljZXMgPSBuZXcgVWludDE2QXJyYXkobnVtVmVydGljZXMgKiAyKTsKICAgICAgICAgICAgY29uc3QgdHJpYW5nbGVzID0gbmV3IFVpbnQzMkFycmF5KG51bVRyaWFuZ2xlcyAqIDMpOwogICAgICAgICAgICBsZXQgdHJpSW5kZXggPSAwOwoKICAgICAgICAgICAgZnVuY3Rpb24gcHJvY2Vzc1RyaWFuZ2xlKGF4LCBheSwgYngsIGJ5LCBjeCwgY3kpIHsKICAgICAgICAgICAgICAgIGNvbnN0IG14ID0gKGF4ICsgYngpID4+IDE7CiAgICAgICAgICAgICAgICBjb25zdCBteSA9IChheSArIGJ5KSA+PiAxOwoKICAgICAgICAgICAgICAgIGlmIChNYXRoLmFicyhheCAtIGN4KSArIE1hdGguYWJzKGF5IC0gY3kpID4gMSAmJiBlcnJvcnNbbXkgKiBzaXplICsgbXhdID4gbWF4RXJyb3IpIHsKICAgICAgICAgICAgICAgICAgICAvLyB0cmlhbmdsZSBkb2Vzbid0IGFwcHJveGltYXRlIHRoZSBzdXJmYWNlIHdlbGwgZW5vdWdoOyBkcmlsbCBkb3duIGZ1cnRoZXIKICAgICAgICAgICAgICAgICAgICBwcm9jZXNzVHJpYW5nbGUoY3gsIGN5LCBheCwgYXksIG14LCBteSk7CiAgICAgICAgICAgICAgICAgICAgcHJvY2Vzc1RyaWFuZ2xlKGJ4LCBieSwgY3gsIGN5LCBteCwgbXkpOwoKICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgLy8gYWRkIGEgdHJpYW5nbGUKICAgICAgICAgICAgICAgICAgICBjb25zdCBhID0gaW5kaWNlc1theSAqIHNpemUgKyBheF0gLSAxOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IGIgPSBpbmRpY2VzW2J5ICogc2l6ZSArIGJ4XSAtIDE7CiAgICAgICAgICAgICAgICAgICAgY29uc3QgYyA9IGluZGljZXNbY3kgKiBzaXplICsgY3hdIC0gMTsKCiAgICAgICAgICAgICAgICAgICAgdmVydGljZXNbMiAqIGFdID0gYXg7CiAgICAgICAgICAgICAgICAgICAgdmVydGljZXNbMiAqIGEgKyAxXSA9IGF5OwoKICAgICAgICAgICAgICAgICAgICB2ZXJ0aWNlc1syICogYl0gPSBieDsKICAgICAgICAgICAgICAgICAgICB2ZXJ0aWNlc1syICogYiArIDFdID0gYnk7CgogICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzWzIgKiBjXSA9IGN4OwogICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzWzIgKiBjICsgMV0gPSBjeTsKCiAgICAgICAgICAgICAgICAgICAgdHJpYW5nbGVzW3RyaUluZGV4KytdID0gYTsKICAgICAgICAgICAgICAgICAgICB0cmlhbmdsZXNbdHJpSW5kZXgrK10gPSBiOwogICAgICAgICAgICAgICAgICAgIHRyaWFuZ2xlc1t0cmlJbmRleCsrXSA9IGM7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcHJvY2Vzc1RyaWFuZ2xlKDAsIDAsIG1heCwgbWF4LCBtYXgsIDApOwogICAgICAgICAgICBwcm9jZXNzVHJpYW5nbGUobWF4LCBtYXgsIDAsIDAsIDAsIG1heCk7CgogICAgICAgICAgICByZXR1cm4ge3ZlcnRpY2VzLCB0cmlhbmdsZXN9OwogICAgICAgIH0KICAgIH0KCiAgICAvLyBsb2FkZXJzLmdsCiAgICAvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUCiAgICAvLyBDb3B5cmlnaHQgKGMpIHZpcy5nbCBjb250cmlidXRvcnMKICAgIC8vIElTQyBMaWNlbnNlCiAgICAvLyBDb3B5cmlnaHQoYykgMjAxOSwgTWljaGFlbCBGb2dsZW1hbiwgVmxhZGltaXIgQWdhZm9ua2luCiAgICAvKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvYmFuLXRzLWNvbW1lbnQgKi8KICAgIC8vIEB0cy1ub2NoZWNrCiAgICAvKiBlc2xpbnQtZW5hYmxlIEB0eXBlc2NyaXB0LWVzbGludC9iYW4tdHMtY29tbWVudCAqLwogICAgZnVuY3Rpb24gb3JpZW50KGF4LCBheSwgYngsIGJ5LCBjeCwgY3kpIHsKICAgICAgICByZXR1cm4gKGJ4IC0gY3gpICogKGF5IC0gY3kpIC0gKGJ5IC0gY3kpICogKGF4IC0gY3gpOwogICAgfQogICAgY2xhc3MgRGVsYXRpbiB7CiAgICAgICAgY29uc3RydWN0b3IoZGF0YSwgd2lkdGgsIGhlaWdodCA9IHdpZHRoKSB7CiAgICAgICAgICAgIHRoaXMuZGF0YSA9IGRhdGE7IC8vIGhlaWdodCBkYXRhCiAgICAgICAgICAgIHRoaXMud2lkdGggPSB3aWR0aDsKICAgICAgICAgICAgdGhpcy5oZWlnaHQgPSBoZWlnaHQ7CiAgICAgICAgICAgIHRoaXMuY29vcmRzID0gW107IC8vIHZlcnRleCBjb29yZGluYXRlcyAoeCwgeSkKICAgICAgICAgICAgdGhpcy50cmlhbmdsZXMgPSBbXTsgLy8gbWVzaCB0cmlhbmdsZSBpbmRpY2VzCiAgICAgICAgICAgIC8vIGFkZGl0aW9uYWwgdHJpYW5nbGUgZGF0YQogICAgICAgICAgICB0aGlzLl9oYWxmZWRnZXMgPSBbXTsKICAgICAgICAgICAgdGhpcy5fY2FuZGlkYXRlcyA9IFtdOwogICAgICAgICAgICB0aGlzLl9xdWV1ZUluZGljZXMgPSBbXTsKICAgICAgICAgICAgdGhpcy5fcXVldWUgPSBbXTsgLy8gcXVldWUgb2YgYWRkZWQgdHJpYW5nbGVzCiAgICAgICAgICAgIHRoaXMuX2Vycm9ycyA9IFtdOwogICAgICAgICAgICB0aGlzLl9ybXMgPSBbXTsKICAgICAgICAgICAgdGhpcy5fcGVuZGluZyA9IFtdOyAvLyB0cmlhbmdsZXMgcGVuZGluZyBhZGRpdGlvbiB0byBxdWV1ZQogICAgICAgICAgICB0aGlzLl9wZW5kaW5nTGVuID0gMDsKICAgICAgICAgICAgdGhpcy5fcm1zU3VtID0gMDsKICAgICAgICAgICAgY29uc3QgeDEgPSB3aWR0aCAtIDE7CiAgICAgICAgICAgIGNvbnN0IHkxID0gaGVpZ2h0IC0gMTsKICAgICAgICAgICAgY29uc3QgcDAgPSB0aGlzLl9hZGRQb2ludCgwLCAwKTsKICAgICAgICAgICAgY29uc3QgcDEgPSB0aGlzLl9hZGRQb2ludCh4MSwgMCk7CiAgICAgICAgICAgIGNvbnN0IHAyID0gdGhpcy5fYWRkUG9pbnQoMCwgeTEpOwogICAgICAgICAgICBjb25zdCBwMyA9IHRoaXMuX2FkZFBvaW50KHgxLCB5MSk7CiAgICAgICAgICAgIC8vIGFkZCBpbml0aWFsIHR3byB0cmlhbmdsZXMKICAgICAgICAgICAgY29uc3QgdDAgPSB0aGlzLl9hZGRUcmlhbmdsZShwMywgcDAsIHAyLCAtMSwgLTEsIC0xKTsKICAgICAgICAgICAgdGhpcy5fYWRkVHJpYW5nbGUocDAsIHAzLCBwMSwgdDAsIC0xLCAtMSk7CiAgICAgICAgICAgIHRoaXMuX2ZsdXNoKCk7CiAgICAgICAgfQogICAgICAgIC8vIHJlZmluZSB0aGUgbWVzaCB1bnRpbCBpdHMgbWF4aW11bSBlcnJvciBnZXRzIGJlbG93IHRoZSBnaXZlbiBvbmUKICAgICAgICBydW4obWF4RXJyb3IgPSAxKSB7CiAgICAgICAgICAgIHdoaWxlICh0aGlzLmdldE1heEVycm9yKCkgPiBtYXhFcnJvcikgewogICAgICAgICAgICAgICAgdGhpcy5yZWZpbmUoKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICAvLyByZWZpbmUgdGhlIG1lc2ggd2l0aCBhIHNpbmdsZSBwb2ludAogICAgICAgIHJlZmluZSgpIHsKICAgICAgICAgICAgdGhpcy5fc3RlcCgpOwogICAgICAgICAgICB0aGlzLl9mbHVzaCgpOwogICAgICAgIH0KICAgICAgICAvLyBtYXggZXJyb3Igb2YgdGhlIGN1cnJlbnQgbWVzaAogICAgICAgIGdldE1heEVycm9yKCkgewogICAgICAgICAgICByZXR1cm4gdGhpcy5fZXJyb3JzWzBdOwogICAgICAgIH0KICAgICAgICAvLyByb290LW1lYW4tc3F1YXJlIGRldmlhdGlvbiBvZiB0aGUgY3VycmVudCBtZXNoCiAgICAgICAgZ2V0Uk1TRCgpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3Jtc1N1bSA+IDAgPyBNYXRoLnNxcnQodGhpcy5fcm1zU3VtIC8gKHRoaXMud2lkdGggKiB0aGlzLmhlaWdodCkpIDogMDsKICAgICAgICB9CiAgICAgICAgLy8gaGVpZ2h0IHZhbHVlIGF0IGEgZ2l2ZW4gcG9zaXRpb24KICAgICAgICBoZWlnaHRBdCh4LCB5KSB7CiAgICAgICAgICAgIHJldHVybiB0aGlzLmRhdGFbdGhpcy53aWR0aCAqIHkgKyB4XTsKICAgICAgICB9CiAgICAgICAgLy8gcmFzdGVyaXplIGFuZCBxdWV1ZSBhbGwgdHJpYW5nbGVzIHRoYXQgZ290IGFkZGVkIG9yIHVwZGF0ZWQgaW4gX3N0ZXAKICAgICAgICBfZmx1c2goKSB7CiAgICAgICAgICAgIGNvbnN0IHsgY29vcmRzIH0gPSB0aGlzOwogICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuX3BlbmRpbmdMZW47IGkrKykgewogICAgICAgICAgICAgICAgY29uc3QgdCA9IHRoaXMuX3BlbmRpbmdbaV07CiAgICAgICAgICAgICAgICAvLyByYXN0ZXJpemUgdHJpYW5nbGUgdG8gZmluZCBtYXhpbXVtIHBpeGVsIGVycm9yCiAgICAgICAgICAgICAgICBjb25zdCBhID0gMiAqIHRoaXMudHJpYW5nbGVzW3QgKiAzICsgMF07CiAgICAgICAgICAgICAgICBjb25zdCBiID0gMiAqIHRoaXMudHJpYW5nbGVzW3QgKiAzICsgMV07CiAgICAgICAgICAgICAgICBjb25zdCBjID0gMiAqIHRoaXMudHJpYW5nbGVzW3QgKiAzICsgMl07CiAgICAgICAgICAgICAgICB0aGlzLl9maW5kQ2FuZGlkYXRlKGNvb3Jkc1thXSwgY29vcmRzW2EgKyAxXSwgY29vcmRzW2JdLCBjb29yZHNbYiArIDFdLCBjb29yZHNbY10sIGNvb3Jkc1tjICsgMV0sIHQpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuX3BlbmRpbmdMZW4gPSAwOwogICAgICAgIH0KICAgICAgICAvLyByYXN0ZXJpemUgYSB0cmlhbmdsZSwgZmluZCBpdHMgbWF4IGVycm9yLCBhbmQgcXVldWUgaXQgZm9yIHByb2Nlc3NpbmcKICAgICAgICBfZmluZENhbmRpZGF0ZShwMHgsIHAweSwgcDF4LCBwMXksIHAyeCwgcDJ5LCB0KSB7CiAgICAgICAgICAgIC8vIHRyaWFuZ2xlIGJvdW5kaW5nIGJveAogICAgICAgICAgICBjb25zdCBtaW5YID0gTWF0aC5taW4ocDB4LCBwMXgsIHAyeCk7CiAgICAgICAgICAgIGNvbnN0IG1pblkgPSBNYXRoLm1pbihwMHksIHAxeSwgcDJ5KTsKICAgICAgICAgICAgY29uc3QgbWF4WCA9IE1hdGgubWF4KHAweCwgcDF4LCBwMngpOwogICAgICAgICAgICBjb25zdCBtYXhZID0gTWF0aC5tYXgocDB5LCBwMXksIHAyeSk7CiAgICAgICAgICAgIC8vIGZvcndhcmQgZGlmZmVyZW5jaW5nIHZhcmlhYmxlcwogICAgICAgICAgICBsZXQgdzAwID0gb3JpZW50KHAxeCwgcDF5LCBwMngsIHAyeSwgbWluWCwgbWluWSk7CiAgICAgICAgICAgIGxldCB3MDEgPSBvcmllbnQocDJ4LCBwMnksIHAweCwgcDB5LCBtaW5YLCBtaW5ZKTsKICAgICAgICAgICAgbGV0IHcwMiA9IG9yaWVudChwMHgsIHAweSwgcDF4LCBwMXksIG1pblgsIG1pblkpOwogICAgICAgICAgICBjb25zdCBhMDEgPSBwMXkgLSBwMHk7CiAgICAgICAgICAgIGNvbnN0IGIwMSA9IHAweCAtIHAxeDsKICAgICAgICAgICAgY29uc3QgYTEyID0gcDJ5IC0gcDF5OwogICAgICAgICAgICBjb25zdCBiMTIgPSBwMXggLSBwMng7CiAgICAgICAgICAgIGNvbnN0IGEyMCA9IHAweSAtIHAyeTsKICAgICAgICAgICAgY29uc3QgYjIwID0gcDJ4IC0gcDB4OwogICAgICAgICAgICAvLyBwcmUtbXVsdGlwbGllZCB6IHZhbHVlcyBhdCB2ZXJ0aWNlcwogICAgICAgICAgICBjb25zdCBhID0gb3JpZW50KHAweCwgcDB5LCBwMXgsIHAxeSwgcDJ4LCBwMnkpOwogICAgICAgICAgICBjb25zdCB6MCA9IHRoaXMuaGVpZ2h0QXQocDB4LCBwMHkpIC8gYTsKICAgICAgICAgICAgY29uc3QgejEgPSB0aGlzLmhlaWdodEF0KHAxeCwgcDF5KSAvIGE7CiAgICAgICAgICAgIGNvbnN0IHoyID0gdGhpcy5oZWlnaHRBdChwMngsIHAyeSkgLyBhOwogICAgICAgICAgICAvLyBpdGVyYXRlIG92ZXIgcGl4ZWxzIGluIGJvdW5kaW5nIGJveAogICAgICAgICAgICBsZXQgbWF4RXJyb3IgPSAwOwogICAgICAgICAgICBsZXQgbXggPSAwOwogICAgICAgICAgICBsZXQgbXkgPSAwOwogICAgICAgICAgICBsZXQgcm1zID0gMDsKICAgICAgICAgICAgZm9yIChsZXQgeSA9IG1pblk7IHkgPD0gbWF4WTsgeSsrKSB7CiAgICAgICAgICAgICAgICAvLyBjb21wdXRlIHN0YXJ0aW5nIG9mZnNldAogICAgICAgICAgICAgICAgbGV0IGR4ID0gMDsKICAgICAgICAgICAgICAgIGlmICh3MDAgPCAwICYmIGExMiAhPT0gMCkgewogICAgICAgICAgICAgICAgICAgIGR4ID0gTWF0aC5tYXgoZHgsIE1hdGguZmxvb3IoLXcwMCAvIGExMikpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgaWYgKHcwMSA8IDAgJiYgYTIwICE9PSAwKSB7CiAgICAgICAgICAgICAgICAgICAgZHggPSBNYXRoLm1heChkeCwgTWF0aC5mbG9vcigtdzAxIC8gYTIwKSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBpZiAodzAyIDwgMCAmJiBhMDEgIT09IDApIHsKICAgICAgICAgICAgICAgICAgICBkeCA9IE1hdGgubWF4KGR4LCBNYXRoLmZsb29yKC13MDIgLyBhMDEpKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGxldCB3MCA9IHcwMCArIGExMiAqIGR4OwogICAgICAgICAgICAgICAgbGV0IHcxID0gdzAxICsgYTIwICogZHg7CiAgICAgICAgICAgICAgICBsZXQgdzIgPSB3MDIgKyBhMDEgKiBkeDsKICAgICAgICAgICAgICAgIGxldCB3YXNJbnNpZGUgPSBmYWxzZTsKICAgICAgICAgICAgICAgIGZvciAobGV0IHggPSBtaW5YICsgZHg7IHggPD0gbWF4WDsgeCsrKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gY2hlY2sgaWYgaW5zaWRlIHRyaWFuZ2xlCiAgICAgICAgICAgICAgICAgICAgaWYgKHcwID49IDAgJiYgdzEgPj0gMCAmJiB3MiA+PSAwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHdhc0luc2lkZSA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGNvbXB1dGUgeiB1c2luZyBiYXJ5Y2VudHJpYyBjb29yZGluYXRlcwogICAgICAgICAgICAgICAgICAgICAgICBjb25zdCB6ID0gejAgKiB3MCArIHoxICogdzEgKyB6MiAqIHcyOwogICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBkeiA9IE1hdGguYWJzKHogLSB0aGlzLmhlaWdodEF0KHgsIHkpKTsKICAgICAgICAgICAgICAgICAgICAgICAgcm1zICs9IGR6ICogZHo7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkeiA+IG1heEVycm9yKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhFcnJvciA9IGR6OwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbXggPSB4OwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbXkgPSB5OwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIGVsc2UgaWYgKHdhc0luc2lkZSkgewogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgdzAgKz0gYTEyOwogICAgICAgICAgICAgICAgICAgIHcxICs9IGEyMDsKICAgICAgICAgICAgICAgICAgICB3MiArPSBhMDE7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB3MDAgKz0gYjEyOwogICAgICAgICAgICAgICAgdzAxICs9IGIyMDsKICAgICAgICAgICAgICAgIHcwMiArPSBiMDE7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKChteCA9PT0gcDB4ICYmIG15ID09PSBwMHkpIHx8IChteCA9PT0gcDF4ICYmIG15ID09PSBwMXkpIHx8IChteCA9PT0gcDJ4ICYmIG15ID09PSBwMnkpKSB7CiAgICAgICAgICAgICAgICBtYXhFcnJvciA9IDA7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLy8gdXBkYXRlIHRyaWFuZ2xlIG1ldGFkYXRhCiAgICAgICAgICAgIHRoaXMuX2NhbmRpZGF0ZXNbMiAqIHRdID0gbXg7CiAgICAgICAgICAgIHRoaXMuX2NhbmRpZGF0ZXNbMiAqIHQgKyAxXSA9IG15OwogICAgICAgICAgICB0aGlzLl9ybXNbdF0gPSBybXM7CiAgICAgICAgICAgIC8vIGFkZCB0cmlhbmdsZSB0byBwcmlvcml0eSBxdWV1ZQogICAgICAgICAgICB0aGlzLl9xdWV1ZVB1c2godCwgbWF4RXJyb3IsIHJtcyk7CiAgICAgICAgfQogICAgICAgIC8vIHByb2Nlc3MgdGhlIG5leHQgdHJpYW5nbGUgaW4gdGhlIHF1ZXVlLCBzcGxpdHRpbmcgaXQgd2l0aCBhIG5ldyBwb2ludAogICAgICAgIF9zdGVwKCkgewogICAgICAgICAgICAvLyBwb3AgdHJpYW5nbGUgd2l0aCBoaWdoZXN0IGVycm9yIGZyb20gcHJpb3JpdHkgcXVldWUKICAgICAgICAgICAgY29uc3QgdCA9IHRoaXMuX3F1ZXVlUG9wKCk7CiAgICAgICAgICAgIGNvbnN0IGUwID0gdCAqIDMgKyAwOwogICAgICAgICAgICBjb25zdCBlMSA9IHQgKiAzICsgMTsKICAgICAgICAgICAgY29uc3QgZTIgPSB0ICogMyArIDI7CiAgICAgICAgICAgIGNvbnN0IHAwID0gdGhpcy50cmlhbmdsZXNbZTBdOwogICAgICAgICAgICBjb25zdCBwMSA9IHRoaXMudHJpYW5nbGVzW2UxXTsKICAgICAgICAgICAgY29uc3QgcDIgPSB0aGlzLnRyaWFuZ2xlc1tlMl07CiAgICAgICAgICAgIGNvbnN0IGF4ID0gdGhpcy5jb29yZHNbMiAqIHAwXTsKICAgICAgICAgICAgY29uc3QgYXkgPSB0aGlzLmNvb3Jkc1syICogcDAgKyAxXTsKICAgICAgICAgICAgY29uc3QgYnggPSB0aGlzLmNvb3Jkc1syICogcDFdOwogICAgICAgICAgICBjb25zdCBieSA9IHRoaXMuY29vcmRzWzIgKiBwMSArIDFdOwogICAgICAgICAgICBjb25zdCBjeCA9IHRoaXMuY29vcmRzWzIgKiBwMl07CiAgICAgICAgICAgIGNvbnN0IGN5ID0gdGhpcy5jb29yZHNbMiAqIHAyICsgMV07CiAgICAgICAgICAgIGNvbnN0IHB4ID0gdGhpcy5fY2FuZGlkYXRlc1syICogdF07CiAgICAgICAgICAgIGNvbnN0IHB5ID0gdGhpcy5fY2FuZGlkYXRlc1syICogdCArIDFdOwogICAgICAgICAgICBjb25zdCBwbiA9IHRoaXMuX2FkZFBvaW50KHB4LCBweSk7CiAgICAgICAgICAgIGlmIChvcmllbnQoYXgsIGF5LCBieCwgYnksIHB4LCBweSkgPT09IDApIHsKICAgICAgICAgICAgICAgIHRoaXMuX2hhbmRsZUNvbGxpbmVhcihwbiwgZTApOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsc2UgaWYgKG9yaWVudChieCwgYnksIGN4LCBjeSwgcHgsIHB5KSA9PT0gMCkgewogICAgICAgICAgICAgICAgdGhpcy5faGFuZGxlQ29sbGluZWFyKHBuLCBlMSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxzZSBpZiAob3JpZW50KGN4LCBjeSwgYXgsIGF5LCBweCwgcHkpID09PSAwKSB7CiAgICAgICAgICAgICAgICB0aGlzLl9oYW5kbGVDb2xsaW5lYXIocG4sIGUyKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgIGNvbnN0IGgwID0gdGhpcy5faGFsZmVkZ2VzW2UwXTsKICAgICAgICAgICAgICAgIGNvbnN0IGgxID0gdGhpcy5faGFsZmVkZ2VzW2UxXTsKICAgICAgICAgICAgICAgIGNvbnN0IGgyID0gdGhpcy5faGFsZmVkZ2VzW2UyXTsKICAgICAgICAgICAgICAgIGNvbnN0IHQwID0gdGhpcy5fYWRkVHJpYW5nbGUocDAsIHAxLCBwbiwgaDAsIC0xLCAtMSwgZTApOwogICAgICAgICAgICAgICAgY29uc3QgdDEgPSB0aGlzLl9hZGRUcmlhbmdsZShwMSwgcDIsIHBuLCBoMSwgLTEsIHQwICsgMSk7CiAgICAgICAgICAgICAgICBjb25zdCB0MiA9IHRoaXMuX2FkZFRyaWFuZ2xlKHAyLCBwMCwgcG4sIGgyLCB0MCArIDIsIHQxICsgMSk7CiAgICAgICAgICAgICAgICB0aGlzLl9sZWdhbGl6ZSh0MCk7CiAgICAgICAgICAgICAgICB0aGlzLl9sZWdhbGl6ZSh0MSk7CiAgICAgICAgICAgICAgICB0aGlzLl9sZWdhbGl6ZSh0Mik7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgLy8gYWRkIGNvb3JkaW5hdGVzIGZvciBhIG5ldyB2ZXJ0ZXgKICAgICAgICBfYWRkUG9pbnQoeCwgeSkgewogICAgICAgICAgICBjb25zdCBpID0gdGhpcy5jb29yZHMubGVuZ3RoID4+IDE7CiAgICAgICAgICAgIHRoaXMuY29vcmRzLnB1c2goeCwgeSk7CiAgICAgICAgICAgIHJldHVybiBpOwogICAgICAgIH0KICAgICAgICAvLyBhZGQgb3IgdXBkYXRlIGEgdHJpYW5nbGUgaW4gdGhlIG1lc2gKICAgICAgICBfYWRkVHJpYW5nbGUoYSwgYiwgYywgYWIsIGJjLCBjYSwgZSA9IHRoaXMudHJpYW5nbGVzLmxlbmd0aCkgewogICAgICAgICAgICBjb25zdCB0ID0gZSAvIDM7IC8vIG5ldyB0cmlhbmdsZSBpbmRleAogICAgICAgICAgICAvLyBhZGQgdHJpYW5nbGUgdmVydGljZXMKICAgICAgICAgICAgdGhpcy50cmlhbmdsZXNbZSArIDBdID0gYTsKICAgICAgICAgICAgdGhpcy50cmlhbmdsZXNbZSArIDFdID0gYjsKICAgICAgICAgICAgdGhpcy50cmlhbmdsZXNbZSArIDJdID0gYzsKICAgICAgICAgICAgLy8gYWRkIHRyaWFuZ2xlIGhhbGZlZGdlcwogICAgICAgICAgICB0aGlzLl9oYWxmZWRnZXNbZSArIDBdID0gYWI7CiAgICAgICAgICAgIHRoaXMuX2hhbGZlZGdlc1tlICsgMV0gPSBiYzsKICAgICAgICAgICAgdGhpcy5faGFsZmVkZ2VzW2UgKyAyXSA9IGNhOwogICAgICAgICAgICAvLyBsaW5rIG5laWdoYm9yaW5nIGhhbGZlZGdlcwogICAgICAgICAgICBpZiAoYWIgPj0gMCkgewogICAgICAgICAgICAgICAgdGhpcy5faGFsZmVkZ2VzW2FiXSA9IGUgKyAwOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChiYyA+PSAwKSB7CiAgICAgICAgICAgICAgICB0aGlzLl9oYWxmZWRnZXNbYmNdID0gZSArIDE7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKGNhID49IDApIHsKICAgICAgICAgICAgICAgIHRoaXMuX2hhbGZlZGdlc1tjYV0gPSBlICsgMjsKICAgICAgICAgICAgfQogICAgICAgICAgICAvLyBpbml0IHRyaWFuZ2xlIG1ldGFkYXRhCiAgICAgICAgICAgIHRoaXMuX2NhbmRpZGF0ZXNbMiAqIHQgKyAwXSA9IDA7CiAgICAgICAgICAgIHRoaXMuX2NhbmRpZGF0ZXNbMiAqIHQgKyAxXSA9IDA7CiAgICAgICAgICAgIHRoaXMuX3F1ZXVlSW5kaWNlc1t0XSA9IC0xOwogICAgICAgICAgICB0aGlzLl9ybXNbdF0gPSAwOwogICAgICAgICAgICAvLyBhZGQgdHJpYW5nbGUgdG8gcGVuZGluZyBxdWV1ZSBmb3IgbGF0ZXIgcmFzdGVyaXphdGlvbgogICAgICAgICAgICB0aGlzLl9wZW5kaW5nW3RoaXMuX3BlbmRpbmdMZW4rK10gPSB0OwogICAgICAgICAgICAvLyByZXR1cm4gZmlyc3QgaGFsZmVkZ2UgaW5kZXgKICAgICAgICAgICAgcmV0dXJuIGU7CiAgICAgICAgfQogICAgICAgIF9sZWdhbGl6ZShhKSB7CiAgICAgICAgICAgIC8vIGlmIHRoZSBwYWlyIG9mIHRyaWFuZ2xlcyBkb2Vzbid0IHNhdGlzZnkgdGhlIERlbGF1bmF5IGNvbmRpdGlvbgogICAgICAgICAgICAvLyAocDEgaXMgaW5zaWRlIHRoZSBjaXJjdW1jaXJjbGUgb2YgW3AwLCBwbCwgcHJdKSwgZmxpcCB0aGVtLAogICAgICAgICAgICAvLyB0aGVuIGRvIHRoZSBzYW1lIGNoZWNrL2ZsaXAgcmVjdXJzaXZlbHkgZm9yIHRoZSBuZXcgcGFpciBvZiB0cmlhbmdsZXMKICAgICAgICAgICAgLy8KICAgICAgICAgICAgLy8gICAgICAgICAgIHBsICAgICAgICAgICAgICAgICAgICBwbAogICAgICAgICAgICAvLyAgICAgICAgICAvfHxcICAgICAgICAgICAgICAgICAgLyAgXAogICAgICAgICAgICAvLyAgICAgICBhbC8gfHwgXGJsICAgICAgICAgICAgYWwvICAgIFxhCiAgICAgICAgICAgIC8vICAgICAgICAvICB8fCAgXCAgICAgICAgICAgICAgLyAgICAgIFwKICAgICAgICAgICAgLy8gICAgICAgLyAgYXx8YiAgXCAgICBmbGlwICAgIC9fX19hcl9fX1wKICAgICAgICAgICAgLy8gICAgIHAwXCAgIHx8ICAgL3AxICAgPT4gICBwMFwtLS1ibC0tLS9wMQogICAgICAgICAgICAvLyAgICAgICAgXCAgfHwgIC8gICAgICAgICAgICAgIFwgICAgICAvCiAgICAgICAgICAgIC8vICAgICAgIGFyXCB8fCAvYnIgICAgICAgICAgICAgYlwgICAgL2JyCiAgICAgICAgICAgIC8vICAgICAgICAgIFx8fC8gICAgICAgICAgICAgICAgICBcICAvCiAgICAgICAgICAgIC8vICAgICAgICAgICBwciAgICAgICAgICAgICAgICAgICAgcHIKICAgICAgICAgICAgY29uc3QgYiA9IHRoaXMuX2hhbGZlZGdlc1thXTsKICAgICAgICAgICAgaWYgKGIgPCAwKSB7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgYTAgPSBhIC0gKGEgJSAzKTsKICAgICAgICAgICAgY29uc3QgYjAgPSBiIC0gKGIgJSAzKTsKICAgICAgICAgICAgY29uc3QgYWwgPSBhMCArICgoYSArIDEpICUgMyk7CiAgICAgICAgICAgIGNvbnN0IGFyID0gYTAgKyAoKGEgKyAyKSAlIDMpOwogICAgICAgICAgICBjb25zdCBibCA9IGIwICsgKChiICsgMikgJSAzKTsKICAgICAgICAgICAgY29uc3QgYnIgPSBiMCArICgoYiArIDEpICUgMyk7CiAgICAgICAgICAgIGNvbnN0IHAwID0gdGhpcy50cmlhbmdsZXNbYXJdOwogICAgICAgICAgICBjb25zdCBwciA9IHRoaXMudHJpYW5nbGVzW2FdOwogICAgICAgICAgICBjb25zdCBwbCA9IHRoaXMudHJpYW5nbGVzW2FsXTsKICAgICAgICAgICAgY29uc3QgcDEgPSB0aGlzLnRyaWFuZ2xlc1tibF07CiAgICAgICAgICAgIGNvbnN0IHsgY29vcmRzIH0gPSB0aGlzOwogICAgICAgICAgICBpZiAoIWluQ2lyY2xlKGNvb3Jkc1syICogcDBdLCBjb29yZHNbMiAqIHAwICsgMV0sIGNvb3Jkc1syICogcHJdLCBjb29yZHNbMiAqIHByICsgMV0sIGNvb3Jkc1syICogcGxdLCBjb29yZHNbMiAqIHBsICsgMV0sIGNvb3Jkc1syICogcDFdLCBjb29yZHNbMiAqIHAxICsgMV0pKSB7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgaGFsID0gdGhpcy5faGFsZmVkZ2VzW2FsXTsKICAgICAgICAgICAgY29uc3QgaGFyID0gdGhpcy5faGFsZmVkZ2VzW2FyXTsKICAgICAgICAgICAgY29uc3QgaGJsID0gdGhpcy5faGFsZmVkZ2VzW2JsXTsKICAgICAgICAgICAgY29uc3QgaGJyID0gdGhpcy5faGFsZmVkZ2VzW2JyXTsKICAgICAgICAgICAgdGhpcy5fcXVldWVSZW1vdmUoYTAgLyAzKTsKICAgICAgICAgICAgdGhpcy5fcXVldWVSZW1vdmUoYjAgLyAzKTsKICAgICAgICAgICAgY29uc3QgdDAgPSB0aGlzLl9hZGRUcmlhbmdsZShwMCwgcDEsIHBsLCAtMSwgaGJsLCBoYWwsIGEwKTsKICAgICAgICAgICAgY29uc3QgdDEgPSB0aGlzLl9hZGRUcmlhbmdsZShwMSwgcDAsIHByLCB0MCwgaGFyLCBoYnIsIGIwKTsKICAgICAgICAgICAgdGhpcy5fbGVnYWxpemUodDAgKyAxKTsKICAgICAgICAgICAgdGhpcy5fbGVnYWxpemUodDEgKyAyKTsKICAgICAgICB9CiAgICAgICAgLy8gaGFuZGxlIGEgY2FzZSB3aGVyZSBuZXcgdmVydGV4IGlzIG9uIHRoZSBlZGdlIG9mIGEgdHJpYW5nbGUKICAgICAgICBfaGFuZGxlQ29sbGluZWFyKHBuLCBhKSB7CiAgICAgICAgICAgIGNvbnN0IGEwID0gYSAtIChhICUgMyk7CiAgICAgICAgICAgIGNvbnN0IGFsID0gYTAgKyAoKGEgKyAxKSAlIDMpOwogICAgICAgICAgICBjb25zdCBhciA9IGEwICsgKChhICsgMikgJSAzKTsKICAgICAgICAgICAgY29uc3QgcDAgPSB0aGlzLnRyaWFuZ2xlc1thcl07CiAgICAgICAgICAgIGNvbnN0IHByID0gdGhpcy50cmlhbmdsZXNbYV07CiAgICAgICAgICAgIGNvbnN0IHBsID0gdGhpcy50cmlhbmdsZXNbYWxdOwogICAgICAgICAgICBjb25zdCBoYWwgPSB0aGlzLl9oYWxmZWRnZXNbYWxdOwogICAgICAgICAgICBjb25zdCBoYXIgPSB0aGlzLl9oYWxmZWRnZXNbYXJdOwogICAgICAgICAgICBjb25zdCBiID0gdGhpcy5faGFsZmVkZ2VzW2FdOwogICAgICAgICAgICBpZiAoYiA8IDApIHsKICAgICAgICAgICAgICAgIGNvbnN0IHQwID0gdGhpcy5fYWRkVHJpYW5nbGUocG4sIHAwLCBwciwgLTEsIGhhciwgLTEsIGEwKTsKICAgICAgICAgICAgICAgIGNvbnN0IHQxID0gdGhpcy5fYWRkVHJpYW5nbGUocDAsIHBuLCBwbCwgdDAsIC0xLCBoYWwpOwogICAgICAgICAgICAgICAgdGhpcy5fbGVnYWxpemUodDAgKyAxKTsKICAgICAgICAgICAgICAgIHRoaXMuX2xlZ2FsaXplKHQxICsgMik7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgYjAgPSBiIC0gKGIgJSAzKTsKICAgICAgICAgICAgY29uc3QgYmwgPSBiMCArICgoYiArIDIpICUgMyk7CiAgICAgICAgICAgIGNvbnN0IGJyID0gYjAgKyAoKGIgKyAxKSAlIDMpOwogICAgICAgICAgICBjb25zdCBwMSA9IHRoaXMudHJpYW5nbGVzW2JsXTsKICAgICAgICAgICAgY29uc3QgaGJsID0gdGhpcy5faGFsZmVkZ2VzW2JsXTsKICAgICAgICAgICAgY29uc3QgaGJyID0gdGhpcy5faGFsZmVkZ2VzW2JyXTsKICAgICAgICAgICAgdGhpcy5fcXVldWVSZW1vdmUoYjAgLyAzKTsKICAgICAgICAgICAgY29uc3QgdDAgPSB0aGlzLl9hZGRUcmlhbmdsZShwMCwgcHIsIHBuLCBoYXIsIC0xLCAtMSwgYTApOwogICAgICAgICAgICBjb25zdCB0MSA9IHRoaXMuX2FkZFRyaWFuZ2xlKHByLCBwMSwgcG4sIGhiciwgLTEsIHQwICsgMSwgYjApOwogICAgICAgICAgICBjb25zdCB0MiA9IHRoaXMuX2FkZFRyaWFuZ2xlKHAxLCBwbCwgcG4sIGhibCwgLTEsIHQxICsgMSk7CiAgICAgICAgICAgIGNvbnN0IHQzID0gdGhpcy5fYWRkVHJpYW5nbGUocGwsIHAwLCBwbiwgaGFsLCB0MCArIDIsIHQyICsgMSk7CiAgICAgICAgICAgIHRoaXMuX2xlZ2FsaXplKHQwKTsKICAgICAgICAgICAgdGhpcy5fbGVnYWxpemUodDEpOwogICAgICAgICAgICB0aGlzLl9sZWdhbGl6ZSh0Mik7CiAgICAgICAgICAgIHRoaXMuX2xlZ2FsaXplKHQzKTsKICAgICAgICB9CiAgICAgICAgLy8gcHJpb3JpdHkgcXVldWUgbWV0aG9kcwogICAgICAgIF9xdWV1ZVB1c2godCwgZXJyb3IsIHJtcykgewogICAgICAgICAgICBjb25zdCBpID0gdGhpcy5fcXVldWUubGVuZ3RoOwogICAgICAgICAgICB0aGlzLl9xdWV1ZUluZGljZXNbdF0gPSBpOwogICAgICAgICAgICB0aGlzLl9xdWV1ZS5wdXNoKHQpOwogICAgICAgICAgICB0aGlzLl9lcnJvcnMucHVzaChlcnJvcik7CiAgICAgICAgICAgIHRoaXMuX3Jtc1N1bSArPSBybXM7CiAgICAgICAgICAgIHRoaXMuX3F1ZXVlVXAoaSk7CiAgICAgICAgfQogICAgICAgIF9xdWV1ZVBvcCgpIHsKICAgICAgICAgICAgY29uc3QgbiA9IHRoaXMuX3F1ZXVlLmxlbmd0aCAtIDE7CiAgICAgICAgICAgIHRoaXMuX3F1ZXVlU3dhcCgwLCBuKTsKICAgICAgICAgICAgdGhpcy5fcXVldWVEb3duKDAsIG4pOwogICAgICAgICAgICByZXR1cm4gdGhpcy5fcXVldWVQb3BCYWNrKCk7CiAgICAgICAgfQogICAgICAgIF9xdWV1ZVBvcEJhY2soKSB7CiAgICAgICAgICAgIGNvbnN0IHQgPSB0aGlzLl9xdWV1ZS5wb3AoKTsKICAgICAgICAgICAgdGhpcy5fZXJyb3JzLnBvcCgpOwogICAgICAgICAgICB0aGlzLl9ybXNTdW0gLT0gdGhpcy5fcm1zW3RdOwogICAgICAgICAgICB0aGlzLl9xdWV1ZUluZGljZXNbdF0gPSAtMTsKICAgICAgICAgICAgcmV0dXJuIHQ7CiAgICAgICAgfQogICAgICAgIF9xdWV1ZVJlbW92ZSh0KSB7CiAgICAgICAgICAgIGNvbnN0IGkgPSB0aGlzLl9xdWV1ZUluZGljZXNbdF07CiAgICAgICAgICAgIGlmIChpIDwgMCkgewogICAgICAgICAgICAgICAgY29uc3QgaXQgPSB0aGlzLl9wZW5kaW5nLmluZGV4T2YodCk7CiAgICAgICAgICAgICAgICBpZiAoaXQgIT09IC0xKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5fcGVuZGluZ1tpdF0gPSB0aGlzLl9wZW5kaW5nWy0tdGhpcy5fcGVuZGluZ0xlbl07CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Jyb2tlbiB0cmlhbmd1bGF0aW9uIChzb21ldGhpbmcgd2VudCB3cm9uZykuJyk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY29uc3QgbiA9IHRoaXMuX3F1ZXVlLmxlbmd0aCAtIDE7CiAgICAgICAgICAgIGlmIChuICE9PSBpKSB7CiAgICAgICAgICAgICAgICB0aGlzLl9xdWV1ZVN3YXAoaSwgbik7CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuX3F1ZXVlRG93bihpLCBuKSkgewogICAgICAgICAgICAgICAgICAgIHRoaXMuX3F1ZXVlVXAoaSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgdGhpcy5fcXVldWVQb3BCYWNrKCk7CiAgICAgICAgfQogICAgICAgIF9xdWV1ZUxlc3MoaSwgaikgewogICAgICAgICAgICByZXR1cm4gdGhpcy5fZXJyb3JzW2ldID4gdGhpcy5fZXJyb3JzW2pdOwogICAgICAgIH0KICAgICAgICBfcXVldWVTd2FwKGksIGopIHsKICAgICAgICAgICAgY29uc3QgcGkgPSB0aGlzLl9xdWV1ZVtpXTsKICAgICAgICAgICAgY29uc3QgcGogPSB0aGlzLl9xdWV1ZVtqXTsKICAgICAgICAgICAgdGhpcy5fcXVldWVbaV0gPSBwajsKICAgICAgICAgICAgdGhpcy5fcXVldWVbal0gPSBwaTsKICAgICAgICAgICAgdGhpcy5fcXVldWVJbmRpY2VzW3BpXSA9IGo7CiAgICAgICAgICAgIHRoaXMuX3F1ZXVlSW5kaWNlc1twal0gPSBpOwogICAgICAgICAgICBjb25zdCBlID0gdGhpcy5fZXJyb3JzW2ldOwogICAgICAgICAgICB0aGlzLl9lcnJvcnNbaV0gPSB0aGlzLl9lcnJvcnNbal07CiAgICAgICAgICAgIHRoaXMuX2Vycm9yc1tqXSA9IGU7CiAgICAgICAgfQogICAgICAgIF9xdWV1ZVVwKGowKSB7CiAgICAgICAgICAgIGxldCBqID0gajA7CiAgICAgICAgICAgIHdoaWxlICh0cnVlKSB7CiAgICAgICAgICAgICAgICBjb25zdCBpID0gKGogLSAxKSA+PiAxOwogICAgICAgICAgICAgICAgaWYgKGkgPT09IGogfHwgIXRoaXMuX3F1ZXVlTGVzcyhqLCBpKSkgewogICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy5fcXVldWVTd2FwKGksIGopOwogICAgICAgICAgICAgICAgaiA9IGk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgX3F1ZXVlRG93bihpMCwgbikgewogICAgICAgICAgICBsZXQgaSA9IGkwOwogICAgICAgICAgICB3aGlsZSAodHJ1ZSkgewogICAgICAgICAgICAgICAgY29uc3QgajEgPSAyICogaSArIDE7CiAgICAgICAgICAgICAgICBpZiAoajEgPj0gbiB8fCBqMSA8IDApIHsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGNvbnN0IGoyID0gajEgKyAxOwogICAgICAgICAgICAgICAgbGV0IGogPSBqMTsKICAgICAgICAgICAgICAgIGlmIChqMiA8IG4gJiYgdGhpcy5fcXVldWVMZXNzKGoyLCBqMSkpIHsKICAgICAgICAgICAgICAgICAgICBqID0gajI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuX3F1ZXVlTGVzcyhqLCBpKSkgewogICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdGhpcy5fcXVldWVTd2FwKGksIGopOwogICAgICAgICAgICAgICAgaSA9IGo7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIGkgPiBpMDsKICAgICAgICB9CiAgICB9CiAgICBmdW5jdGlvbiBpbkNpcmNsZShheCwgYXksIGJ4LCBieSwgY3gsIGN5LCBweCwgcHkpIHsKICAgICAgICBjb25zdCBkeCA9IGF4IC0gcHg7CiAgICAgICAgY29uc3QgZHkgPSBheSAtIHB5OwogICAgICAgIGNvbnN0IGV4ID0gYnggLSBweDsKICAgICAgICBjb25zdCBleSA9IGJ5IC0gcHk7CiAgICAgICAgY29uc3QgZnggPSBjeCAtIHB4OwogICAgICAgIGNvbnN0IGZ5ID0gY3kgLSBweTsKICAgICAgICBjb25zdCBhcCA9IGR4ICogZHggKyBkeSAqIGR5OwogICAgICAgIGNvbnN0IGJwID0gZXggKiBleCArIGV5ICogZXk7CiAgICAgICAgY29uc3QgY3AgPSBmeCAqIGZ4ICsgZnkgKiBmeTsKICAgICAgICByZXR1cm4gZHggKiAoZXkgKiBjcCAtIGJwICogZnkpIC0gZHkgKiAoZXggKiBjcCAtIGJwICogZngpICsgYXAgKiAoZXggKiBmeSAtIGV5ICogZngpIDwgMDsKICAgIH0KCiAgICAvKioKICAgICAqIFdlYiBXb3JrZXIgZm9yIHRlcnJhaW4gbWVzaCB0ZXNzZWxsYXRpb24uCiAgICAgKiBSdW5zIE1hcnRpbmkvRGVsYXRpbiBhbGdvcml0aG1zIG9uIGEgYmFja2dyb3VuZCB0aHJlYWQgdG8gYXZvaWQgYmxvY2tpbmcgdGhlIG1haW4gdGhyZWFkLgogICAgICovCiAgICAvLyBUcmFjayBhYm9ydGVkIHRhc2tzIHRvIGF2b2lkIHNlbmRpbmcgcmVzdWx0cyBmb3IgY2FuY2VsbGVkIHdvcmsKICAgIGNvbnN0IGFib3J0ZWRUYXNrcyA9IG5ldyBNYXAoKTsKICAgIHNlbGYub25tZXNzYWdlID0gKGUpID0+IHsKICAgICAgICBjb25zdCBkYXRhID0gZS5kYXRhOwogICAgICAgIGlmIChkYXRhLnR5cGUgPT09ICdhYm9ydCcpIHsKICAgICAgICAgICAgLy8gTWFyayB0YXNrIGFzIGFib3J0ZWQ7IGlmIGl0J3Mgc3RpbGwgY29tcHV0aW5nLCB3ZSdsbCBza2lwIHRoZSByZXNwb25zZQogICAgICAgICAgICBhYm9ydGVkVGFza3Muc2V0KGRhdGEudGFza0lkLCB0cnVlKTsKICAgICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICBpZiAoZGF0YS50eXBlID09PSAnY29tcHV0ZU1lc2gnKSB7CiAgICAgICAgICAgIGNvbnN0IHsgdGFza0lkLCB0ZXJyYWluLCBtZXNoTWF4RXJyb3IsIHRlc3NlbGF0b3IsIHdpZHRoLCBoZWlnaHQgfSA9IGRhdGE7CiAgICAgICAgICAgIGxldCBtZXNoOwogICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgaWYgKHRlc3NlbGF0b3IgPT09ICdkZWxhdGluJykgewogICAgICAgICAgICAgICAgICAgIC8vIERlbGF0aW4gdGVzc2VsbGF0aW9uCiAgICAgICAgICAgICAgICAgICAgY29uc3Qgd2lkdGhQbHVzID0gd2lkdGggPT09IDI1NyA/IDI1NyA6IHdpZHRoICsgMTsKICAgICAgICAgICAgICAgICAgICBjb25zdCBoZWlnaHRQbHVzID0gaGVpZ2h0ID09PSAyNTcgPyAyNTcgOiBoZWlnaHQgKyAxOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IHRpbiA9IG5ldyBEZWxhdGluKHRlcnJhaW4sIHdpZHRoUGx1cywgaGVpZ2h0UGx1cyk7CiAgICAgICAgICAgICAgICAgICAgdGluLnJ1bihtZXNoTWF4RXJyb3IpOwogICAgICAgICAgICAgICAgICAgIC8vIEB0cy1leHBlY3QtZXJyb3I6IERlbGF0aW4gaW5zdGFuY2UgcHJvcGVydGllcyAnY29vcmRzJyBhbmQgJ3RyaWFuZ2xlcycgYXJlIG5vdCBleHBsaWNpdGx5IHR5cGVkIGluIHRoZSBsaWJyYXJ5IHBvcnQKICAgICAgICAgICAgICAgICAgICBjb25zdCB7IGNvb3JkcywgdHJpYW5nbGVzIH0gPSB0aW47CiAgICAgICAgICAgICAgICAgICAgLy8gY29vcmRzIGlzIGEgcGxhaW4gYXJyYXkg4oCUIGNvbnZlcnQgdG8gRmxvYXQ2NEFycmF5IHNvIGl0IGhhcyAuYnVmZmVyIGZvciB0cmFuc2ZlcgogICAgICAgICAgICAgICAgICAgIGNvbnN0IHZlcnRpY2VzVHlwZWQgPSBGbG9hdDY0QXJyYXkuZnJvbShjb29yZHMpOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IHRyaWFuZ2xlc1R5cGVkID0gVWludDMyQXJyYXkuZnJvbSh0cmlhbmdsZXMpOwogICAgICAgICAgICAgICAgICAgIG1lc2ggPSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzOiB2ZXJ0aWNlc1R5cGVkLAogICAgICAgICAgICAgICAgICAgICAgICB0cmlhbmdsZXM6IHRyaWFuZ2xlc1R5cGVkLAogICAgICAgICAgICAgICAgICAgIH07CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAvLyBNYXJ0aW5pIHRlc3NlbGxhdGlvbiAoZGVmYXVsdCkKICAgICAgICAgICAgICAgICAgICBjb25zdCBncmlkU2l6ZSA9IHdpZHRoID09PSAyNTcgPyAyNTcgOiB3aWR0aCArIDE7IC8vIE9ubHkgYWRkIDEgaWYgd2lkdGggaXMgbm90IGFscmVhZHkgMl5uKzEKICAgICAgICAgICAgICAgICAgICBjb25zdCBtYXJ0aW5pID0gbmV3IE1hcnRpbmkoZ3JpZFNpemUpOwogICAgICAgICAgICAgICAgICAgIGNvbnN0IHRpbGUgPSBtYXJ0aW5pLmNyZWF0ZVRpbGUodGVycmFpbik7CiAgICAgICAgICAgICAgICAgICAgbWVzaCA9IHRpbGUuZ2V0TWVzaChtZXNoTWF4RXJyb3IpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgLy8gT25seSBzZW5kIHJlc3VsdCBpZiBub3QgYWJvcnRlZAogICAgICAgICAgICAgICAgaWYgKCFhYm9ydGVkVGFza3MuZ2V0KHRhc2tJZCkpIHsKICAgICAgICAgICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IHsKICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ21lc2hSZXN1bHQnLAogICAgICAgICAgICAgICAgICAgICAgICB0YXNrSWQsCiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdDogbWVzaCwKICAgICAgICAgICAgICAgICAgICAgICAgdGVycmFpbiwgLy8g4oaQIENSSVRJQ0FMOiBSZXR1cm4gdGVycmFpbiBidWZmZXIgdG8gbWFpbiB0aHJlYWQKICAgICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgICAgICAgIC8vIFRyYW5zZmVyIG93bmVyc2hpcCBvZiBBTEwgYnVmZmVycyBiYWNrIHRvIG1haW4gdGhyZWFkICh6ZXJvLWNvcHkgcm91bmR0cmlwKQogICAgICAgICAgICAgICAgICAgIC8vIFRoaXMgYXZvaWRzIG1lc2hUZXJyYWluLnNsaWNlKCkgYWxsb2NhdGlvbiBvbiBtYWluIHRocmVhZAogICAgICAgICAgICAgICAgICAgIHNlbGYucG9zdE1lc3NhZ2UocmVzcG9uc2UsIHsKICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNmZXI6IFsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc2gudmVydGljZXMuYnVmZmVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVzaC50cmlhbmdsZXMuYnVmZmVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVycmFpbi5idWZmZXIsIC8vIOKGkCBUcmFuc2ZlciB0ZXJyYWluIGJhY2sKICAgICAgICAgICAgICAgICAgICAgICAgXSwKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBjYXRjaCAoZXJyb3IpIHsKICAgICAgICAgICAgICAgIC8vIE9ubHkgcmVwb3J0IGVycm9ycyBmb3Igbm9uLWFib3J0ZWQgdGFza3MKICAgICAgICAgICAgICAgIGlmICghYWJvcnRlZFRhc2tzLmdldCh0YXNrSWQpKSB7CiAgICAgICAgICAgICAgICAgICAgc2VsZi5wb3N0TWVzc2FnZSh7CiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdlcnJvcicsCiAgICAgICAgICAgICAgICAgICAgICAgIHRhc2tJZCwKICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKSwKICAgICAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBmaW5hbGx5IHsKICAgICAgICAgICAgICAgIC8vIENsZWFuIHVwIGFib3J0IHRyYWNraW5nCiAgICAgICAgICAgICAgICBhYm9ydGVkVGFza3MuZGVsZXRlKHRhc2tJZCk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9OwogICAgLy8gU2lnbmFsIHRoYXQgd29ya2VyIGlzIHJlYWR5CiAgICBzZWxmLnBvc3RNZXNzYWdlKHsgdHlwZTogJ3JlYWR5JyB9KTsKCn0pKCk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPXRlcnJhaW4ud29ya2VyLmpzLm1hcAoK');
|
|
6783
|
+
/* eslint-enable */
|
|
6784
|
+
|
|
6785
|
+
/**
|
|
6786
|
+
* Pool of Web Workers for parallel terrain tessellation.
|
|
6787
|
+
* Distributes work across multiple workers based on CPU core count.
|
|
6788
|
+
*
|
|
6789
|
+
* SINGLETON PATTERN: One global pool is shared across all CogTiles instances
|
|
6790
|
+
* to avoid expensive worker creation/destruction during deck.gl layer recreations.
|
|
6791
|
+
*/
|
|
6792
|
+
// @ts-expect-error - The import statement will be handled by both Rollup (web-worker: prefix)
|
|
6793
|
+
// and our Vite plugin (web-worker: → ?worker conversion)
|
|
6794
|
+
/**
|
|
6795
|
+
* Manages a pool of terrain tessellation workers.
|
|
6796
|
+
* Automatically scales to CPU core count (capped at 8 for memory safety).
|
|
6797
|
+
*/
|
|
6798
|
+
class TerrainWorkerPool {
|
|
6799
|
+
workers = [];
|
|
6800
|
+
pendingTasks = new Map();
|
|
6801
|
+
taskCounter = 0;
|
|
6802
|
+
roundRobinIndex = 0;
|
|
6803
|
+
constructor(poolSize) {
|
|
6804
|
+
// Detect browser environment; default to 1 worker in non-browser contexts (SSR/Node/tests)
|
|
6805
|
+
let defaultSize = 1;
|
|
6806
|
+
if (typeof navigator !== 'undefined') {
|
|
6807
|
+
// Default to CPU core count, fallback to 4, cap at 8 to prevent memory exhaustion
|
|
6808
|
+
defaultSize = Math.min(navigator.hardwareConcurrency || 4, 8);
|
|
6809
|
+
// On low-memory devices (<4GB), use only 2 workers to avoid OOM
|
|
6810
|
+
if (navigator.deviceMemory && navigator.deviceMemory < 4) {
|
|
6811
|
+
defaultSize = 2;
|
|
6812
|
+
}
|
|
6813
|
+
}
|
|
6814
|
+
const size = Math.max(1, poolSize ?? defaultSize);
|
|
6815
|
+
for (let i = 0; i < size; i++) {
|
|
6816
|
+
const worker = new WorkerFactory();
|
|
6817
|
+
worker.onmessage = this.handleWorkerMessage.bind(this);
|
|
6818
|
+
worker.onerror = this.handleWorkerError.bind(this);
|
|
6819
|
+
this.workers.push(worker);
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6822
|
+
/**
|
|
6823
|
+
* Compute terrain mesh using the worker pool.
|
|
6824
|
+
* Returns a Promise that resolves with the mesh data.
|
|
6825
|
+
* Supports cancellation via AbortSignal.
|
|
6826
|
+
*/
|
|
6827
|
+
async computeMesh(options) {
|
|
6828
|
+
const { terrain, meshMaxError, tesselator, width, height, signal } = options;
|
|
6829
|
+
// Check if already aborted
|
|
6830
|
+
if (signal?.aborted) {
|
|
6831
|
+
throw new DOMException('Aborted', 'AbortError');
|
|
6832
|
+
}
|
|
6833
|
+
const taskId = `task_${++this.taskCounter}`;
|
|
6834
|
+
return new Promise((resolve, reject) => {
|
|
6835
|
+
// Pick worker once — used for both dispatch and abort to ensure correct routing
|
|
6836
|
+
const worker = this.getNextWorker();
|
|
6837
|
+
const task = { resolve, reject, aborted: false, worker };
|
|
6838
|
+
this.pendingTasks.set(taskId, task);
|
|
6839
|
+
// Handle abort signal
|
|
6840
|
+
if (signal) {
|
|
6841
|
+
signal.addEventListener('abort', () => {
|
|
6842
|
+
// Guard against late aborts: only process if task still exists
|
|
6843
|
+
if (!this.pendingTasks.has(taskId)) {
|
|
6844
|
+
return;
|
|
6845
|
+
}
|
|
6846
|
+
task.aborted = true;
|
|
6847
|
+
this.pendingTasks.delete(taskId);
|
|
6848
|
+
// Send abort to the same worker that owns this task
|
|
6849
|
+
worker.postMessage({ type: 'abort', taskId });
|
|
6850
|
+
reject(new DOMException('Aborted', 'AbortError'));
|
|
6851
|
+
}, { once: true });
|
|
6852
|
+
}
|
|
6853
|
+
// Transfer terrain buffer ownership to avoid copy
|
|
6854
|
+
// NOTE: After this, `terrain` becomes detached on main thread
|
|
6855
|
+
worker.postMessage({
|
|
6856
|
+
type: 'computeMesh',
|
|
6857
|
+
taskId,
|
|
6858
|
+
terrain,
|
|
6859
|
+
meshMaxError,
|
|
6860
|
+
tesselator,
|
|
6861
|
+
width,
|
|
6862
|
+
height,
|
|
6863
|
+
}, [terrain.buffer] // ← Transferable
|
|
6864
|
+
);
|
|
6865
|
+
});
|
|
6866
|
+
}
|
|
6867
|
+
getNextWorker() {
|
|
6868
|
+
const worker = this.workers[this.roundRobinIndex];
|
|
6869
|
+
this.roundRobinIndex = (this.roundRobinIndex + 1) % this.workers.length;
|
|
6870
|
+
return worker;
|
|
6871
|
+
}
|
|
6872
|
+
handleWorkerMessage(e) {
|
|
6873
|
+
const { type, taskId, result, terrain, error } = e.data;
|
|
6874
|
+
if (type === 'ready') {
|
|
6875
|
+
// Worker initialization complete (can be ignored)
|
|
6876
|
+
return;
|
|
6877
|
+
}
|
|
6878
|
+
const task = this.pendingTasks.get(taskId);
|
|
6879
|
+
if (!task) {
|
|
6880
|
+
// Task was aborted or already resolved
|
|
6881
|
+
return;
|
|
6882
|
+
}
|
|
6883
|
+
this.pendingTasks.delete(taskId);
|
|
6884
|
+
if (task.aborted) {
|
|
6885
|
+
// Ignore result for aborted task
|
|
6886
|
+
return;
|
|
6887
|
+
}
|
|
6888
|
+
if (type === 'meshResult') {
|
|
6889
|
+
// Combine result with terrain that was transferred back
|
|
6890
|
+
task.resolve({
|
|
6891
|
+
vertices: result.vertices,
|
|
6892
|
+
triangles: result.triangles,
|
|
6893
|
+
terrain, // ← Include terrain from message
|
|
6894
|
+
});
|
|
6895
|
+
}
|
|
6896
|
+
else if (type === 'error') {
|
|
6897
|
+
task.reject(new Error(`Worker error: ${error}`));
|
|
6898
|
+
}
|
|
6899
|
+
}
|
|
6900
|
+
handleWorkerError(e) {
|
|
6901
|
+
// Find which worker failed (e.target is the Worker instance)
|
|
6902
|
+
const failedWorker = e.target;
|
|
6903
|
+
// Find all pending tasks assigned to this worker
|
|
6904
|
+
const failedTaskIds = [];
|
|
6905
|
+
this.pendingTasks.forEach((task, taskId) => {
|
|
6906
|
+
if (task.worker === failedWorker) {
|
|
6907
|
+
failedTaskIds.push(taskId);
|
|
6908
|
+
}
|
|
6909
|
+
});
|
|
6910
|
+
// Reject all tasks for the failed worker
|
|
6911
|
+
for (const taskId of failedTaskIds) {
|
|
6912
|
+
const task = this.pendingTasks.get(taskId);
|
|
6913
|
+
if (task) {
|
|
6914
|
+
this.pendingTasks.delete(taskId);
|
|
6915
|
+
task.reject(new Error(`Worker crashed: ${e.message || 'Unknown error'}`));
|
|
6916
|
+
}
|
|
6917
|
+
}
|
|
6918
|
+
// Respawn the failed worker to maintain pool capacity
|
|
6919
|
+
const workerIndex = this.workers.indexOf(failedWorker);
|
|
6920
|
+
if (workerIndex !== -1) {
|
|
6921
|
+
try {
|
|
6922
|
+
const newWorker = new WorkerFactory();
|
|
6923
|
+
newWorker.onmessage = this.handleWorkerMessage.bind(this);
|
|
6924
|
+
newWorker.onerror = this.handleWorkerError.bind(this);
|
|
6925
|
+
this.workers[workerIndex] = newWorker;
|
|
6926
|
+
}
|
|
6927
|
+
catch (spawnError) {
|
|
6928
|
+
// eslint-disable-next-line no-console
|
|
6929
|
+
console.error('[TerrainWorkerPool] Failed to respawn worker:', spawnError);
|
|
6930
|
+
}
|
|
6931
|
+
}
|
|
6932
|
+
}
|
|
6933
|
+
/**
|
|
6934
|
+
* Terminate all workers.
|
|
6935
|
+
* ⚠️ NOTE: Because this is a singleton, terminate() should only be called
|
|
6936
|
+
* on app shutdown, not on individual layer unmount.
|
|
6937
|
+
*/
|
|
6938
|
+
terminate() {
|
|
6939
|
+
// Reject all pending tasks before terminating workers
|
|
6940
|
+
this.pendingTasks.forEach(task => {
|
|
6941
|
+
task.reject(new DOMException('Worker pool terminated', 'AbortError'));
|
|
6942
|
+
});
|
|
6943
|
+
this.pendingTasks.clear();
|
|
6944
|
+
this.workers.forEach(w => w.terminate());
|
|
6945
|
+
this.workers = [];
|
|
6946
|
+
}
|
|
6947
|
+
}
|
|
6948
|
+
// ─── SINGLETON INSTANCE ───
|
|
6949
|
+
// Lazily initialized on first use; shared across all CogTiles instances
|
|
6950
|
+
let globalWorkerPool = null;
|
|
6951
|
+
/**
|
|
6952
|
+
* Gets the global terrain worker pool, creating it on first use.
|
|
6953
|
+
* All CogTiles instances share this pool to avoid expensive worker churn
|
|
6954
|
+
* during deck.gl layer recreations.
|
|
6955
|
+
*/
|
|
6956
|
+
function getGlobalTerrainWorkerPool() {
|
|
6957
|
+
if (!globalWorkerPool) {
|
|
6958
|
+
globalWorkerPool = new TerrainWorkerPool();
|
|
6959
|
+
}
|
|
6960
|
+
return globalWorkerPool;
|
|
6961
|
+
}
|
|
6962
|
+
/**
|
|
6963
|
+
* Terminates the global worker pool.
|
|
6964
|
+
* Only call this on app shutdown, not on layer unmount.
|
|
6965
|
+
*/
|
|
6966
|
+
function terminateGlobalTerrainWorkerPool() {
|
|
6967
|
+
if (globalWorkerPool) {
|
|
6968
|
+
globalWorkerPool.terminate();
|
|
6969
|
+
globalWorkerPool = null;
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
|
|
6737
6973
|
const EARTH_CIRCUMFERENCE = 2 * Math.PI * 6378137;
|
|
6738
6974
|
const EARTH_HALF_CIRCUMFERENCE = EARTH_CIRCUMFERENCE / 2;
|
|
6739
6975
|
const webMercatorOrigin = [-20037508.342789244, 20037508.342789244];
|
|
@@ -7087,12 +7323,19 @@ class CogTiles {
|
|
|
7087
7323
|
// future cache hits just await the already-resolved promise directly.
|
|
7088
7324
|
cache = new TileCacheManager();
|
|
7089
7325
|
tileReader;
|
|
7326
|
+
workerPool; // TerrainWorkerPool (for terrain tiles)
|
|
7090
7327
|
// Store initialization promise to prevent concurrent duplicate initializations
|
|
7091
7328
|
initializePromise;
|
|
7092
7329
|
// Track the last successfully initialized URL to detect URL changes
|
|
7093
7330
|
lastInitializedUrl;
|
|
7094
7331
|
constructor(options) {
|
|
7095
7332
|
this.options = { ...CogTilesGeoImageOptionsDefaults, ...options };
|
|
7333
|
+
// Get reference to global worker pool for terrain tiles
|
|
7334
|
+
// Do NOT create a new pool per instance — reuse the singleton
|
|
7335
|
+
// Skip if disableWorkerPool is true (e.g., for smooth animation during rapid band changes)
|
|
7336
|
+
if (options.type === 'terrain' && typeof Worker !== 'undefined' && !options.disableWorkerPool) {
|
|
7337
|
+
this.workerPool = getGlobalTerrainWorkerPool();
|
|
7338
|
+
}
|
|
7096
7339
|
}
|
|
7097
7340
|
async initializeCog(url) {
|
|
7098
7341
|
// Reuse existing initialization while it is in progress, or when the same URL
|
|
@@ -7624,7 +7867,7 @@ class CogTiles {
|
|
|
7624
7867
|
height: requiredSize,
|
|
7625
7868
|
bounds: bounds ?? [0, 0, 0, 0],
|
|
7626
7869
|
cellSizeMeters,
|
|
7627
|
-
}, generatorOptions, resolvedMeshMaxError);
|
|
7870
|
+
}, generatorOptions, resolvedMeshMaxError, this.workerPool, controller.signal);
|
|
7628
7871
|
})();
|
|
7629
7872
|
const entry = {
|
|
7630
7873
|
promise: pipeline,
|
|
@@ -7678,7 +7921,7 @@ class CogTiles {
|
|
|
7678
7921
|
height: this.tileSize,
|
|
7679
7922
|
bounds: bounds ?? [0, 0, 0, 0],
|
|
7680
7923
|
cellSizeMeters,
|
|
7681
|
-
}, this.options, meshMaxError ?? 4.0);
|
|
7924
|
+
}, this.options, meshMaxError ?? 4.0, this.workerPool, signal);
|
|
7682
7925
|
}
|
|
7683
7926
|
async getBitmapTile(x, y, z, bounds, cellSizeMeters, meshMaxError, signal) {
|
|
7684
7927
|
const rasterKey = this.cache.getTileCacheKey(x, y, z);
|
|
@@ -7700,7 +7943,7 @@ class CogTiles {
|
|
|
7700
7943
|
height: this.tileSize,
|
|
7701
7944
|
bounds: bounds ?? [0, 0, 0, 0],
|
|
7702
7945
|
cellSizeMeters,
|
|
7703
|
-
}, this.options, meshMaxError ?? 4.0);
|
|
7946
|
+
}, this.options, meshMaxError ?? 4.0, this.workerPool);
|
|
7704
7947
|
}
|
|
7705
7948
|
async getTileAllBands(x, y, z, meshMaxError, signal, bounds) {
|
|
7706
7949
|
if (!this.cog) {
|
|
@@ -7797,7 +8040,7 @@ class CogTiles {
|
|
|
7797
8040
|
height: FETCH_SIZE,
|
|
7798
8041
|
bounds: bounds ?? [0, 0, 0, 0],
|
|
7799
8042
|
cellSizeMeters,
|
|
7800
|
-
}, generatorOptions, resolvedMeshMaxError);
|
|
8043
|
+
}, generatorOptions, resolvedMeshMaxError, this.workerPool, signal);
|
|
7801
8044
|
if (tileResult)
|
|
7802
8045
|
results.push(tileResult);
|
|
7803
8046
|
}
|
|
@@ -8060,6 +8303,8 @@ const defaultProps = {
|
|
|
8060
8303
|
// Same as SimpleMeshLayer wireframe
|
|
8061
8304
|
wireframe: false,
|
|
8062
8305
|
material: true,
|
|
8306
|
+
// Enable progressive loading by default (overview tiles first, then detail)
|
|
8307
|
+
enableProgressiveLoading: true,
|
|
8063
8308
|
// loaders: [TerrainLoader],
|
|
8064
8309
|
};
|
|
8065
8310
|
// Turns array of templates into a single string to work around shallow change
|
|
@@ -8077,12 +8322,15 @@ class CogTerrainLayer extends CompositeLayer {
|
|
|
8077
8322
|
static layerName = 'CogTerrainLayer';
|
|
8078
8323
|
// terrainCogTiles: CogTiles;
|
|
8079
8324
|
terrainUrl = '';
|
|
8325
|
+
zRangeUpdateTimeoutId = null;
|
|
8326
|
+
lastZRangeValue = null;
|
|
8080
8327
|
async initializeState(context) {
|
|
8081
8328
|
super.initializeState(context);
|
|
8082
8329
|
const terrainCogTiles = this.props.cogTiles || new CogTiles(this.props.terrainOptions);
|
|
8083
8330
|
this.setState({
|
|
8084
8331
|
terrainCogTiles,
|
|
8085
8332
|
initialized: false,
|
|
8333
|
+
overviewLoaded: false,
|
|
8086
8334
|
});
|
|
8087
8335
|
// Only initialize if not already done (e.g., provided cogTiles instance may be pre-initialized)
|
|
8088
8336
|
if (!terrainCogTiles.cog) {
|
|
@@ -8128,6 +8376,26 @@ class CogTerrainLayer extends CompositeLayer {
|
|
|
8128
8376
|
this.state.terrainCogTiles.options.useChannelIndex = null; // Clear derived channel index
|
|
8129
8377
|
this.state.terrainCogTiles.clearTileResultCache(); // Invalidate cached tiles from previous channel
|
|
8130
8378
|
}
|
|
8379
|
+
// Update kernel visualization options when hillshade/slope/relief settings change.
|
|
8380
|
+
// These affect tile texture generation — the cache must be cleared and options synced
|
|
8381
|
+
// so the next getTileData call uses the updated kernel settings.
|
|
8382
|
+
const kernelOptionsChanged = props?.terrainOptions?.useHillshade !== oldProps.terrainOptions?.useHillshade ||
|
|
8383
|
+
props?.terrainOptions?.useSlope !== oldProps.terrainOptions?.useSlope ||
|
|
8384
|
+
props?.terrainOptions?.useSwissRelief !== oldProps.terrainOptions?.useSwissRelief ||
|
|
8385
|
+
props?.terrainOptions?.hillshadeAzimuth !== oldProps.terrainOptions?.hillshadeAzimuth ||
|
|
8386
|
+
props?.terrainOptions?.hillshadeAltitude !== oldProps.terrainOptions?.hillshadeAltitude ||
|
|
8387
|
+
props?.terrainOptions?.zFactor !== oldProps.terrainOptions?.zFactor;
|
|
8388
|
+
if (kernelOptionsChanged && this.state.terrainCogTiles) {
|
|
8389
|
+
// Sync updated options into the shared CogTiles instance
|
|
8390
|
+
this.state.terrainCogTiles.options.useHillshade = props.terrainOptions?.useHillshade;
|
|
8391
|
+
this.state.terrainCogTiles.options.useSlope = props.terrainOptions?.useSlope;
|
|
8392
|
+
this.state.terrainCogTiles.options.useSwissRelief = props.terrainOptions?.useSwissRelief;
|
|
8393
|
+
this.state.terrainCogTiles.options.hillshadeAzimuth = props.terrainOptions?.hillshadeAzimuth;
|
|
8394
|
+
this.state.terrainCogTiles.options.hillshadeAltitude = props.terrainOptions?.hillshadeAltitude;
|
|
8395
|
+
this.state.terrainCogTiles.options.zFactor = props.terrainOptions?.zFactor;
|
|
8396
|
+
// Invalidate cached tiles — kernel output is baked into the texture
|
|
8397
|
+
this.state.terrainCogTiles.clearTileResultCache();
|
|
8398
|
+
}
|
|
8131
8399
|
// Update skipTexture when wireframe/operation/disableTexture changes so cache keys are correct
|
|
8132
8400
|
const newSkipTexture = !!(props?.wireframe || props?.operation === 'terrain' || props?.disableTexture);
|
|
8133
8401
|
const oldSkipTexture = !!(oldProps?.wireframe || oldProps?.operation === 'terrain' || oldProps?.disableTexture);
|
|
@@ -8138,8 +8406,20 @@ class CogTerrainLayer extends CompositeLayer {
|
|
|
8138
8406
|
// When the external cogTiles instance is swapped (e.g. mode switch), update state so
|
|
8139
8407
|
// renderLayers picks up the new reference and the TileLayer updateTrigger fires a refetch
|
|
8140
8408
|
// while keeping old tile content visible until new tiles are ready.
|
|
8409
|
+
// Also reset progressive loading state for the new dataset.
|
|
8141
8410
|
if (props.cogTiles && props.cogTiles !== oldProps.cogTiles) {
|
|
8142
|
-
|
|
8411
|
+
const newState = { terrainCogTiles: props.cogTiles };
|
|
8412
|
+
// Only reset progressive loading state if it's actually enabled
|
|
8413
|
+
if (this.props.enableProgressiveLoading) {
|
|
8414
|
+
newState.overviewLoaded = false;
|
|
8415
|
+
}
|
|
8416
|
+
this.setState(newState);
|
|
8417
|
+
}
|
|
8418
|
+
else if (elevationDataChanged) {
|
|
8419
|
+
// Reset progressive loading state when dataset URL changes
|
|
8420
|
+
if (this.props.enableProgressiveLoading) {
|
|
8421
|
+
this.setState({ overviewLoaded: false });
|
|
8422
|
+
}
|
|
8143
8423
|
}
|
|
8144
8424
|
if (props.workerUrl) {
|
|
8145
8425
|
log.removed('workerUrl', 'loadOptions.terrain.workerUrl')();
|
|
@@ -8231,9 +8511,21 @@ class CogTerrainLayer extends CompositeLayer {
|
|
|
8231
8511
|
_instanced: false,
|
|
8232
8512
|
pickable: props.pickable,
|
|
8233
8513
|
coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
|
|
8514
|
+
// Dynamic polygon offset: pull higher zoom levels closer to camera to depth-test in front.
|
|
8515
|
+
// Uses tile.index.z from closure to avoid Z-fighting between ancestor tiles and high-res detail.
|
|
8516
|
+
// Formula: zoom 0 = offset 0, zoom 9 = offset -9000, zoom 12 = offset -12000, etc.
|
|
8517
|
+
// getPolygonOffset must be a function (deck.gl calls it as getPolygonOffset(uniforms)).
|
|
8518
|
+
// If the user supplied a custom override on the CogTerrainLayer, respect it;
|
|
8519
|
+
// otherwise apply the tile-zoom-based dynamic offset.
|
|
8520
|
+
getPolygonOffset: (this.props.getPolygonOffset != null
|
|
8521
|
+
&& this.props.getPolygonOffset !== ((CogTerrainLayer.defaultProps.getPolygonOffset?.value) ?? CogTerrainLayer.defaultProps.getPolygonOffset))
|
|
8522
|
+
? this.props.getPolygonOffset
|
|
8523
|
+
: () => [0, -((props.tile?.index?.z ?? 0) * 1000)],
|
|
8234
8524
|
// getPosition: (d) => [0, 0, 0],
|
|
8235
8525
|
getColor: tileTexture ? [255, 255, 255] : color,
|
|
8236
8526
|
wireframe,
|
|
8527
|
+
// ADDED: Forward parameters prop down to the SimpleMeshLayer for depthRange to work
|
|
8528
|
+
parameters: this.props.parameters,
|
|
8237
8529
|
});
|
|
8238
8530
|
}
|
|
8239
8531
|
// Update zRange of viewport
|
|
@@ -8255,60 +8547,121 @@ class CogTerrainLayer extends CompositeLayer {
|
|
|
8255
8547
|
if (ranges.length === 0) {
|
|
8256
8548
|
return;
|
|
8257
8549
|
}
|
|
8258
|
-
const
|
|
8259
|
-
|
|
8550
|
+
const minValues = ranges
|
|
8551
|
+
.map((x) => x?.[0])
|
|
8552
|
+
.filter((n) => n !== undefined && Number.isFinite(n));
|
|
8553
|
+
const maxValues = ranges
|
|
8554
|
+
.map((x) => x?.[1])
|
|
8555
|
+
.filter((n) => n !== undefined && Number.isFinite(n));
|
|
8556
|
+
if (minValues.length === 0 || maxValues.length === 0) {
|
|
8557
|
+
return;
|
|
8558
|
+
}
|
|
8559
|
+
const minZ = Math.min(...minValues);
|
|
8560
|
+
const maxZ = Math.max(...maxValues);
|
|
8260
8561
|
if (!zRange || minZ < zRange[0] || maxZ > zRange[1]) {
|
|
8261
8562
|
const newZRange = [Number.isFinite(minZ) ? minZ : 0, Number.isFinite(maxZ) ? maxZ : 0];
|
|
8262
8563
|
this.setState({ zRange: newZRange });
|
|
8263
|
-
|
|
8564
|
+
// Debounce onZRangeUpdate callback to avoid excessive React re-renders during rapid updates
|
|
8565
|
+
// (e.g., slider scrubbing in animation use cases). Wait 200ms of stable zRange before firing.
|
|
8566
|
+
if (this.zRangeUpdateTimeoutId !== null) {
|
|
8567
|
+
clearTimeout(this.zRangeUpdateTimeoutId);
|
|
8568
|
+
}
|
|
8569
|
+
this.lastZRangeValue = newZRange;
|
|
8570
|
+
this.zRangeUpdateTimeoutId = setTimeout(() => {
|
|
8571
|
+
this.props.onZRangeUpdate?.(this.lastZRangeValue);
|
|
8572
|
+
this.zRangeUpdateTimeoutId = null;
|
|
8573
|
+
}, 200);
|
|
8264
8574
|
}
|
|
8265
8575
|
}
|
|
8266
8576
|
renderLayers() {
|
|
8267
|
-
const {
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
//
|
|
8272
|
-
//
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8577
|
+
const { elevationData, meshMaxError, elevationDecoder, tileSize, extent, maxRequests, onTileUnload, onTileError, maxCacheSize, maxCacheByteSize, refinementStrategy, } = this.props;
|
|
8578
|
+
if (!this.state.isTiled || !this.state.initialized) {
|
|
8579
|
+
return null;
|
|
8580
|
+
}
|
|
8581
|
+
// Auto-enable LOD gate: lock at minZoom until overview tile loads, then release to full range.
|
|
8582
|
+
// User's explicit zoomOverride takes precedence over auto-gate.
|
|
8583
|
+
let effectiveMinZoom = this.state.minZoom;
|
|
8584
|
+
let effectiveMaxZoom = this.state.maxZoom;
|
|
8585
|
+
if (this.props.zoomOverride !== undefined) {
|
|
8586
|
+
effectiveMinZoom = this.props.zoomOverride;
|
|
8587
|
+
effectiveMaxZoom = this.props.zoomOverride;
|
|
8588
|
+
}
|
|
8589
|
+
else if (this.props.enableProgressiveLoading && !this.state.overviewLoaded) {
|
|
8590
|
+
// Gate: lock at minZoom until the overview viewport is fully covered
|
|
8591
|
+
effectiveMinZoom = this.state.minZoom;
|
|
8592
|
+
effectiveMaxZoom = this.state.minZoom;
|
|
8593
|
+
}
|
|
8594
|
+
return new TileLayer(this.getSubLayerProps({ id: 'tiles' }), {
|
|
8595
|
+
getTileData: this.getTiledTerrainData.bind(this),
|
|
8596
|
+
renderSubLayers: this.renderSubLayers.bind(this),
|
|
8597
|
+
pickable: this.props.pickable,
|
|
8598
|
+
onClick: this.props.onClick,
|
|
8599
|
+
updateTriggers: {
|
|
8600
|
+
getTileData: {
|
|
8601
|
+
elevationData: urlTemplateToUpdateTrigger(elevationData),
|
|
8602
|
+
meshMaxError,
|
|
8603
|
+
elevationDecoder,
|
|
8604
|
+
terrainCogTiles: this.state.terrainCogTiles,
|
|
8605
|
+
skipTexture: !!(this.props.wireframe || this.props.operation === 'terrain' || this.props.disableTexture),
|
|
8606
|
+
useChannel: this.props.terrainOptions?.useChannel,
|
|
8607
|
+
// Only include kernel visualization triggers if they're explicitly set to avoid false positives
|
|
8608
|
+
// when animating with different useChannel values
|
|
8609
|
+
...(this.props.terrainOptions?.useHillshade !== undefined && {
|
|
8610
|
+
useHillshade: this.props.terrainOptions.useHillshade,
|
|
8611
|
+
}),
|
|
8612
|
+
...(this.props.terrainOptions?.useSlope !== undefined && {
|
|
8613
|
+
useSlope: this.props.terrainOptions.useSlope,
|
|
8614
|
+
}),
|
|
8615
|
+
...(this.props.terrainOptions?.useSwissRelief !== undefined && {
|
|
8616
|
+
useSwissRelief: this.props.terrainOptions.useSwissRelief,
|
|
8617
|
+
}),
|
|
8618
|
+
...(this.props.terrainOptions?.hillshadeAzimuth !== undefined && {
|
|
8619
|
+
hillshadeAzimuth: this.props.terrainOptions.hillshadeAzimuth,
|
|
8620
|
+
}),
|
|
8621
|
+
...(this.props.terrainOptions?.hillshadeAltitude !== undefined && {
|
|
8622
|
+
hillshadeAltitude: this.props.terrainOptions.hillshadeAltitude,
|
|
8623
|
+
}),
|
|
8624
|
+
...(this.props.terrainOptions?.zFactor !== undefined && {
|
|
8625
|
+
zFactor: this.props.terrainOptions.zFactor,
|
|
8626
|
+
}),
|
|
8295
8627
|
},
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8628
|
+
renderSubLayers: {
|
|
8629
|
+
disableTexture: this.props.disableTexture,
|
|
8630
|
+
terrainOptions: this.props.terrainOptions,
|
|
8631
|
+
},
|
|
8632
|
+
},
|
|
8633
|
+
onViewportLoad: this.onViewportLoad.bind(this),
|
|
8634
|
+
zRange: this.state.zRange || null,
|
|
8635
|
+
tileSize,
|
|
8636
|
+
minZoom: effectiveMinZoom,
|
|
8637
|
+
maxZoom: effectiveMaxZoom,
|
|
8638
|
+
extent,
|
|
8639
|
+
maxRequests,
|
|
8640
|
+
onTileLoad: (tile) => {
|
|
8641
|
+
// Release progressive loading gate as soon as any minZoom tile finishes loading.
|
|
8642
|
+
// This fires mid-cycle so the TileLayer immediately re-selects high-res tiles
|
|
8643
|
+
// for the current viewport without requiring a zoom/pan interaction.
|
|
8644
|
+
if (this.props.enableProgressiveLoading &&
|
|
8645
|
+
tile.index.z === this.state.minZoom &&
|
|
8646
|
+
!this.state.overviewLoaded) {
|
|
8647
|
+
this.setState({ overviewLoaded: true });
|
|
8648
|
+
}
|
|
8649
|
+
this.props.onTileLoad?.(tile);
|
|
8650
|
+
},
|
|
8651
|
+
onTileUnload,
|
|
8652
|
+
onTileError,
|
|
8653
|
+
maxCacheSize,
|
|
8654
|
+
maxCacheByteSize,
|
|
8655
|
+
refinementStrategy,
|
|
8656
|
+
});
|
|
8657
|
+
}
|
|
8658
|
+
_finalize() {
|
|
8659
|
+
// Clean up pending zRangeUpdate callback timer on layer unmount
|
|
8660
|
+
if (this.zRangeUpdateTimeoutId !== null) {
|
|
8661
|
+
clearTimeout(this.zRangeUpdateTimeoutId);
|
|
8662
|
+
this.zRangeUpdateTimeoutId = null;
|
|
8310
8663
|
}
|
|
8311
|
-
|
|
8664
|
+
super._finalize?.();
|
|
8312
8665
|
}
|
|
8313
8666
|
}
|
|
8314
8667
|
|
|
@@ -15670,5 +16023,5 @@ var webimage = /*#__PURE__*/Object.freeze({
|
|
|
15670
16023
|
default: WebImageDecoder
|
|
15671
16024
|
});
|
|
15672
16025
|
|
|
15673
|
-
export { CogBitmapLayer, CogTerrainLayer, CogTiles, GeoImage, extractTerrainCoordinate, sampleTerrainTileCoordinates, suppressGlobalAbortErrors };
|
|
16026
|
+
export { CogBitmapLayer, CogTerrainLayer, CogTiles, GeoImage, extractTerrainCoordinate, sampleTerrainTileCoordinates, suppressGlobalAbortErrors, terminateGlobalTerrainWorkerPool };
|
|
15674
16027
|
//# sourceMappingURL=index.js.map
|