import { Color, defaultValue, defined, JulianDate, Resource, Cesium3DTileContentFactory, Cesium3DTileset, TileBoundingSphere, } from "@cesium/engine"; import pollToPromise from "./pollToPromise.js"; const mockTile = { contentBoundingVolume: new TileBoundingSphere(), _contentBoundingVolume: new TileBoundingSphere(), _header: { content: { boundingVolume: { sphere: [0.0, 0.0, 0.0, 1.0], }, }, }, }; function Cesium3DTilesTester() {} function padStringToByteAlignment(string, byteAlignment) { const length = string.length; const paddedLength = Math.ceil(length / byteAlignment) * byteAlignment; // Round up to the required alignment const padding = paddedLength - length; let whitespace = ""; for (let i = 0; i < padding; ++i) { whitespace += " "; } return string + whitespace; } const time = new JulianDate(2457522.0); Cesium3DTilesTester.expectRender = function (scene, tileset, callback) { const renderOptions = { scene: scene, time: time, }; tileset.show = false; expect(renderOptions).toRender([0, 0, 0, 255]); tileset.show = true; expect(renderOptions).toRenderAndCall(function (rgba) { expect(rgba).not.toEqual([0, 0, 0, 255]); if (defined(callback)) { callback(rgba); } }); }; Cesium3DTilesTester.expectRenderBlank = function (scene, tileset) { const renderOptions = { scene: scene, time: time, }; tileset.show = false; expect(renderOptions).toRender([0, 0, 0, 255]); tileset.show = true; expect(renderOptions).toRender([0, 0, 0, 255]); }; Cesium3DTilesTester.expectRenderTileset = function (scene, tileset) { // Verify render before being picked Cesium3DTilesTester.expectRender(scene, tileset); // Pick a feature expect(scene).toPickAndCall(function (result) { expect(result).toBeDefined(); // Change the color of the picked feature to yellow result.color = Color.clone(Color.YELLOW, result.color); // Expect the pixel color to be some shade of yellow Cesium3DTilesTester.expectRender(scene, tileset, function (rgba) { expect(rgba[0]).toBeGreaterThan(0); expect(rgba[1]).toBeGreaterThan(0); expect(rgba[2]).toEqual(0); expect(rgba[3]).toEqual(255); }); // Turn show off and on result.show = false; Cesium3DTilesTester.expectRenderBlank(scene, tileset); result.show = true; Cesium3DTilesTester.expectRender(scene, tileset); }); }; Cesium3DTilesTester.waitForTilesLoaded = function (scene, tileset) { return pollToPromise(function () { scene.renderForSpecs(); return tileset.tilesLoaded; }).then(function () { scene.renderForSpecs(); return tileset; }); }; Cesium3DTilesTester.loadTileset = async function (scene, url, options) { options = defaultValue(options, {}); options.cullRequestsWhileMoving = defaultValue( options.cullRequestsWhileMoving, false ); const tileset = await Cesium3DTileset.fromUrl(url, options); // Load all visible tiles scene.primitives.add(tileset); await Cesium3DTilesTester.waitForTilesLoaded(scene, tileset); return tileset; }; Cesium3DTilesTester.createContentForMockTile = async function ( arrayBuffer, type ) { const tileset = {}; const url = Resource.createIfNeeded(""); return Cesium3DTileContentFactory[type]( tileset, mockTile, url, arrayBuffer, 0 ); }; Cesium3DTilesTester.tileDestroys = function (scene, url, options) { return Cesium3DTilesTester.loadTileset(scene, url, options).then(function ( tileset ) { const content = tileset.root.content; expect(content.isDestroyed()).toEqual(false); scene.primitives.remove(tileset); expect(content.isDestroyed()).toEqual(true); }); }; Cesium3DTilesTester.generateBatchedTileBuffer = function (options) { // Procedurally generate the tile array buffer for testing purposes options = defaultValue(options, defaultValue.EMPTY_OBJECT); const magic = defaultValue(options.magic, [98, 51, 100, 109]); const version = defaultValue(options.version, 1); const featuresLength = defaultValue(options.featuresLength, 1); const featureTableJson = { BATCH_LENGTH: featuresLength, }; const featureTableJsonString = JSON.stringify(featureTableJson); const featureTableJsonByteLength = featureTableJsonString.length; const headerByteLength = 28; const byteLength = headerByteLength + featureTableJsonByteLength; const buffer = new ArrayBuffer(byteLength); const view = new DataView(buffer); view.setUint8(0, magic[0]); view.setUint8(1, magic[1]); view.setUint8(2, magic[2]); view.setUint8(3, magic[3]); view.setUint32(4, version, true); // version view.setUint32(8, byteLength, true); // byteLength view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength view.setUint32(16, 0, true); // featureTableBinaryByteLength view.setUint32(20, 0, true); // batchTableJsonByteLength view.setUint32(24, 0, true); // batchTableBinaryByteLength let i; let byteOffset = headerByteLength; for (i = 0; i < featureTableJsonByteLength; i++) { view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i)); byteOffset++; } return buffer; }; Cesium3DTilesTester.generateInstancedTileBuffer = function (options) { // Procedurally generate the tile array buffer for testing purposes options = defaultValue(options, defaultValue.EMPTY_OBJECT); const magic = defaultValue(options.magic, [105, 51, 100, 109]); const version = defaultValue(options.version, 1); const gltfFormat = defaultValue(options.gltfFormat, 1); const gltfUri = defaultValue(options.gltfUri, "model.gltf"); const gltfUriByteLength = gltfUri.length; const featureTableJson = options.featureTableJson; let featureTableJsonString = ""; if (defined(featureTableJson)) { if (Object.keys(featureTableJson).length > 0) { featureTableJsonString = JSON.stringify(featureTableJson); } } else { const featuresLength = defaultValue(options.featuresLength, 1); featureTableJsonString = JSON.stringify({ INSTANCES_LENGTH: featuresLength, POSITION: new Array(featuresLength * 3).fill(0), }); } featureTableJsonString = padStringToByteAlignment(featureTableJsonString, 8); const featureTableJsonByteLength = featureTableJsonString.length; const featureTableBinary = defaultValue( options.featureTableBinary, new Uint8Array(0) ); const featureTableBinaryByteLength = featureTableBinary.length; const batchTableJson = options.batchTableJson; let batchTableJsonString = ""; if (defined(batchTableJson) && Object.keys(batchTableJson).length > 0) { batchTableJsonString = JSON.stringify(batchTableJson); } batchTableJsonString = padStringToByteAlignment(batchTableJsonString, 8); const batchTableJsonByteLength = batchTableJsonString.length; const batchTableBinary = defaultValue( options.batchTableBinary, new Uint8Array(0) ); const batchTableBinaryByteLength = batchTableBinary.length; const headerByteLength = 32; const byteLength = headerByteLength + featureTableJsonByteLength + featureTableBinaryByteLength + batchTableJsonByteLength + batchTableBinaryByteLength + gltfUriByteLength; const buffer = new ArrayBuffer(byteLength); const view = new DataView(buffer); view.setUint8(0, magic[0]); view.setUint8(1, magic[1]); view.setUint8(2, magic[2]); view.setUint8(3, magic[3]); view.setUint32(4, version, true); view.setUint32(8, byteLength, true); view.setUint32(12, featureTableJsonByteLength, true); view.setUint32(16, featureTableBinaryByteLength, true); view.setUint32(20, batchTableJsonByteLength, true); view.setUint32(24, batchTableBinaryByteLength, true); view.setUint32(28, gltfFormat, true); let i; let byteOffset = headerByteLength; for (i = 0; i < featureTableJsonByteLength; i++) { view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i)); byteOffset++; } for (i = 0; i < featureTableBinaryByteLength; i++) { view.setUint8(byteOffset, featureTableBinary[i]); byteOffset++; } for (i = 0; i < batchTableJsonByteLength; i++) { view.setUint8(byteOffset, batchTableJsonString.charCodeAt(i)); byteOffset++; } for (i = 0; i < batchTableBinaryByteLength; i++) { view.setUint8(byteOffset, batchTableBinary[i]); byteOffset++; } for (i = 0; i < gltfUriByteLength; i++) { view.setUint8(byteOffset, gltfUri.charCodeAt(i)); byteOffset++; } return buffer; }; Cesium3DTilesTester.generatePointCloudTileBuffer = function (options) { // Procedurally generate the tile array buffer for testing purposes options = defaultValue(options, defaultValue.EMPTY_OBJECT); const magic = defaultValue(options.magic, [112, 110, 116, 115]); const version = defaultValue(options.version, 1); let featureTableJson = options.featureTableJson; if (!defined(featureTableJson)) { featureTableJson = { POINTS_LENGTH: 1, POSITIONS: { byteOffset: 0, }, }; } let featureTableJsonString = JSON.stringify(featureTableJson); featureTableJsonString = padStringToByteAlignment(featureTableJsonString, 4); const featureTableJsonByteLength = defaultValue( options.featureTableJsonByteLength, featureTableJsonString.length ); const featureTableBinary = new ArrayBuffer(12); // Enough space to hold 3 floats const featureTableBinaryByteLength = featureTableBinary.byteLength; const headerByteLength = 28; const byteLength = headerByteLength + featureTableJsonByteLength + featureTableBinaryByteLength; const buffer = new ArrayBuffer(byteLength); const view = new DataView(buffer); view.setUint8(0, magic[0]); view.setUint8(1, magic[1]); view.setUint8(2, magic[2]); view.setUint8(3, magic[3]); view.setUint32(4, version, true); // version view.setUint32(8, byteLength, true); // byteLength view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength view.setUint32(16, featureTableBinaryByteLength, true); // featureTableBinaryByteLength view.setUint32(20, 0, true); // batchTableJsonByteLength view.setUint32(24, 0, true); // batchTableBinaryByteLength let i; let byteOffset = headerByteLength; for (i = 0; i < featureTableJsonByteLength; i++) { view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i)); byteOffset++; } for (i = 0; i < featureTableBinaryByteLength; i++) { view.setUint8(byteOffset, featureTableBinary[i]); byteOffset++; } return buffer; }; Cesium3DTilesTester.generateCompositeTileBuffer = function (options) { // Procedurally generate the tile array buffer for testing purposes options = defaultValue(options, defaultValue.EMPTY_OBJECT); const magic = defaultValue(options.magic, [99, 109, 112, 116]); const version = defaultValue(options.version, 1); const tiles = defaultValue(options.tiles, []); const tilesLength = tiles.length; let i; let tilesByteLength = 0; for (i = 0; i < tilesLength; ++i) { tilesByteLength += tiles[i].byteLength; } const headerByteLength = 16; const byteLength = headerByteLength + tilesByteLength; const buffer = new ArrayBuffer(byteLength); const uint8Array = new Uint8Array(buffer); const view = new DataView(buffer); view.setUint8(0, magic[0]); view.setUint8(1, magic[1]); view.setUint8(2, magic[2]); view.setUint8(3, magic[3]); view.setUint32(4, version, true); // version view.setUint32(8, byteLength, true); // byteLength view.setUint32(12, tilesLength, true); // tilesLength let byteOffset = headerByteLength; for (i = 0; i < tilesLength; ++i) { const tile = new Uint8Array(tiles[i]); uint8Array.set(tile, byteOffset); byteOffset += tile.byteLength; } return buffer; }; Cesium3DTilesTester.generateVectorTileBuffer = function (options) { // Procedurally generate the tile array buffer for testing purposes options = defaultValue(options, defaultValue.EMPTY_OBJECT); const magic = defaultValue(options.magic, [118, 99, 116, 114]); const version = defaultValue(options.version, 1); let featureTableJsonString; let featureTableJsonByteLength = 0; const defineFeatureTable = defaultValue(options.defineFeatureTable, true); if (defineFeatureTable) { const defineRegion = defaultValue(options.defineRegion, true); const featureTableJson = { REGION: defineRegion ? [-1.0, -1.0, 1.0, 1.0, -1.0, 1.0] : undefined, POLYGONS_LENGTH: defaultValue(options.polygonsLength, 0), POLYLINES_LENGTH: defaultValue(options.polylinesLength, 0), POINTS_LENGTH: defaultValue(options.pointsLength, 0), POLYGON_BATCH_IDS: options.polygonBatchIds, POLYLINE_BATCH_IDS: options.polylineBatchIds, POINT_BATCH_IDS: options.pointBatchIds, }; featureTableJsonString = JSON.stringify(featureTableJson); featureTableJsonByteLength = featureTableJsonString.length; } const headerByteLength = 44; const byteLength = headerByteLength + featureTableJsonByteLength; const buffer = new ArrayBuffer(byteLength); const view = new DataView(buffer); view.setUint8(0, magic[0]); view.setUint8(1, magic[1]); view.setUint8(2, magic[2]); view.setUint8(3, magic[3]); view.setUint32(4, version, true); // version view.setUint32(8, byteLength, true); // byteLength view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength view.setUint32(16, 0, true); // featureTableBinaryByteLength view.setUint32(20, 0, true); // batchTableJsonByteLength view.setUint32(24, 0, true); // batchTableBinaryByteLength view.setUint32(28, 0, true); // indicesByteLength view.setUint32(32, 0, true); // polygonPositionByteLength view.setUint32(36, 0, true); // polylinePositionByteLength view.setUint32(40, 0, true); // pointsPositionByteLength let i; let byteOffset = headerByteLength; for (i = 0; i < featureTableJsonByteLength; i++) { view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i)); byteOffset++; } return buffer; }; Cesium3DTilesTester.generateGeometryTileBuffer = function (options) { // Procedurally generate the tile array buffer for testing purposes options = defaultValue(options, defaultValue.EMPTY_OBJECT); const magic = defaultValue(options.magic, [103, 101, 111, 109]); const version = defaultValue(options.version, 1); let featureTableJsonString; let featureTableJsonByteLength = 0; const defineFeatureTable = defaultValue(options.defineFeatureTable, true); if (defineFeatureTable) { const featureTableJson = { BOXES_LENGTH: defaultValue(options.boxesLength, 0), CYLINDERS_LENGTH: defaultValue(options.cylindersLength, 0), ELLIPSOIDS_LENGTH: defaultValue(options.ellipsoidsLength, 0), SPHERES_LENGTH: defaultValue(options.spheresLength, 0), BOX_BATCH_IDS: options.boxBatchIds, CYLINDER_BATCH_IDS: options.cylinderBatchIds, ELLIPSOID_BATCH_IDS: options.ellipsoidBatchIds, SPHERE_BATCH_IDS: options.sphereBatchIds, }; featureTableJsonString = JSON.stringify(featureTableJson); featureTableJsonByteLength = featureTableJsonString.length; } const headerByteLength = 28; const byteLength = headerByteLength + featureTableJsonByteLength; const buffer = new ArrayBuffer(byteLength); const view = new DataView(buffer); view.setUint8(0, magic[0]); view.setUint8(1, magic[1]); view.setUint8(2, magic[2]); view.setUint8(3, magic[3]); view.setUint32(4, version, true); // version view.setUint32(8, byteLength, true); // byteLength view.setUint32(12, featureTableJsonByteLength, true); // featureTableJsonByteLength view.setUint32(16, 0, true); // featureTableBinaryByteLength view.setUint32(20, 0, true); // batchTableJsonByteLength view.setUint32(24, 0, true); // batchTableBinaryByteLength let i; let byteOffset = headerByteLength; for (i = 0; i < featureTableJsonByteLength; i++) { view.setUint8(byteOffset, featureTableJsonString.charCodeAt(i)); byteOffset++; } return buffer; }; export default Cesium3DTilesTester;