From f925c85995a9eae21033271ce9747b239680e308 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 7 Mar 2023 16:50:14 +0100 Subject: [PATCH 01/26] Move gzip/gunzip methods into Buffers class --- src/base/Buffers.ts | 32 ++++++++++++++++++++++++ src/contentOperations/ContentOps.ts | 17 ------------- src/pipelines/TilesetEntries.ts | 6 ++--- src/tilesetProcessing/TilesetUpgrader.ts | 2 +- 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/base/Buffers.ts b/src/base/Buffers.ts index 24171a26..3158a600 100644 --- a/src/base/Buffers.ts +++ b/src/base/Buffers.ts @@ -1,3 +1,5 @@ +import zlib from "zlib"; + import { defined } from "./defined"; /** @@ -10,6 +12,36 @@ import { defined } from "./defined"; * @internal */ export class Buffers { + /** + * Applies GZIP compression to the given buffer, and returns + * the result. + * + * @param inputBuffer - The input buffer + * @returns The resulting buffer + */ + static gzip(inputBuffer: Buffer): Buffer { + const outputBuffer = zlib.gzipSync(inputBuffer); + return outputBuffer; + } + + /** + * If the given buffer is compressed with GZIP, then it is + * unzipped, and the result is returned. Otherwise, the + * given buffer is returned as it is. + * + * @param inputBuffer - The input buffer + * @returns The resulting buffer + */ + static gunzip(inputBuffer: Buffer): Buffer { + let outputBuffer: Buffer; + if (Buffers.isGzipped(inputBuffer)) { + outputBuffer = zlib.gunzipSync(inputBuffer); + } else { + outputBuffer = inputBuffer; + } + return outputBuffer; + } + /** * Obtains the magic header from the given buffer. * diff --git a/src/contentOperations/ContentOps.ts b/src/contentOperations/ContentOps.ts index 332c28b9..acd60f80 100644 --- a/src/contentOperations/ContentOps.ts +++ b/src/contentOperations/ContentOps.ts @@ -1,25 +1,8 @@ -import zlib from "zlib"; - -import { Buffers } from "../base/Buffers"; import { GltfUtilities } from "./GtlfUtilities"; import { TileFormats } from "../tileFormats/TileFormats"; export class ContentOps { - static gzipBuffer(inputBuffer: Buffer): Buffer { - const outputBuffer = zlib.gzipSync(inputBuffer); - return outputBuffer; - } - - static gunzipBuffer(inputBuffer: Buffer): Buffer { - let outputBuffer: Buffer; - if (Buffers.isGzipped(inputBuffer)) { - outputBuffer = zlib.gunzipSync(inputBuffer); - } else { - outputBuffer = inputBuffer; - } - return outputBuffer; - } static b3dmToGlbBuffer(inputBuffer: Buffer): Buffer { const inputTileData = TileFormats.readTileData(inputBuffer); diff --git a/src/pipelines/TilesetEntries.ts b/src/pipelines/TilesetEntries.ts index 3257e728..d7beae81 100644 --- a/src/pipelines/TilesetEntries.ts +++ b/src/pipelines/TilesetEntries.ts @@ -1,4 +1,4 @@ -import { ContentOps } from "../contentOperations/ContentOps"; +import { Buffers } from "../base/Buffers"; import { TilesetEntry } from "../tilesetData/TilesetEntry"; @@ -7,7 +7,7 @@ export class TilesetEntries { const inputKey = inputEntry.key; const inputValue = inputEntry.value; const outputKey = inputKey; - const outputValue = ContentOps.gzipBuffer(inputValue); + const outputValue = Buffers.gzip(inputValue); return { key: outputKey, value: outputValue, @@ -18,7 +18,7 @@ export class TilesetEntries { const inputKey = inputEntry.key; const inputValue = inputEntry.value; const outputKey = inputKey; - const outputValue = ContentOps.gunzipBuffer(inputValue); + const outputValue = Buffers.gunzip(inputValue); return { key: outputKey, value: outputValue, diff --git a/src/tilesetProcessing/TilesetUpgrader.ts b/src/tilesetProcessing/TilesetUpgrader.ts index 2e99d87c..e08227ca 100644 --- a/src/tilesetProcessing/TilesetUpgrader.ts +++ b/src/tilesetProcessing/TilesetUpgrader.ts @@ -164,7 +164,7 @@ export class TilesetUpgrader { let tilesetJsonBufferWasZipped = false; if (Buffers.isGzipped(tilesetJsonBuffer)) { tilesetJsonBufferWasZipped = true; - tilesetJsonBuffer = ContentOps.gunzipBuffer(tilesetJsonBuffer); + tilesetJsonBuffer = Buffers.gunzip(tilesetJsonBuffer); } const tileset = JSON.parse(tilesetJsonBuffer.toString()) as Tileset; From dbc8b1eddd8c54b53ff294de94af4308ab5f474f Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 7 Mar 2023 19:16:57 +0100 Subject: [PATCH 02/26] Fix for method that was moved to Buffers class --- src/tilesetProcessing/TilesetUpgrader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tilesetProcessing/TilesetUpgrader.ts b/src/tilesetProcessing/TilesetUpgrader.ts index e08227ca..edbecda8 100644 --- a/src/tilesetProcessing/TilesetUpgrader.ts +++ b/src/tilesetProcessing/TilesetUpgrader.ts @@ -176,7 +176,7 @@ export class TilesetUpgrader { const resultTilesetJsonString = JSON.stringify(tileset, null, 2); let resultTilesetJsonBuffer = Buffer.from(resultTilesetJsonString); if (tilesetJsonBufferWasZipped) { - resultTilesetJsonBuffer = ContentOps.gzipBuffer(resultTilesetJsonBuffer); + resultTilesetJsonBuffer = Buffers.gzip(resultTilesetJsonBuffer); } this.tilesetTarget.addEntry( tilesetTargetJsonFileName, From 961a0cefae0d6e4dfc395a9aa44d1ff2bd885aeb Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 7 Mar 2023 19:18:57 +0100 Subject: [PATCH 03/26] Fix typo in documentation --- src/base/defined.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/defined.ts b/src/base/defined.ts index 08e0b74d..463f464f 100644 --- a/src/base/defined.ts +++ b/src/base/defined.ts @@ -15,8 +15,8 @@ * const value = 0; * if (defined(value)) console.log('defined'); * else console.log('not defined'); - * if (value) console.log('truthy); - * else console.log('not truthy); + * if (value) console.log('truthy'); + * else console.log('not truthy'); * ``` * will be * ``` From de39b5c884b65db9f37bcbe62bd7193f90b35d11 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 7 Mar 2023 19:26:52 +0100 Subject: [PATCH 04/26] Removed unused import --- src/tilesetProcessing/TilesetUpgrader.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tilesetProcessing/TilesetUpgrader.ts b/src/tilesetProcessing/TilesetUpgrader.ts index edbecda8..c5696ea4 100644 --- a/src/tilesetProcessing/TilesetUpgrader.ts +++ b/src/tilesetProcessing/TilesetUpgrader.ts @@ -20,7 +20,6 @@ import { Tilesets } from "../tilesets/Tilesets"; import { TileFormats } from "../tileFormats/TileFormats"; import { GltfUtilities } from "../contentOperations/GtlfUtilities"; -import { ContentOps } from "../contentOperations/ContentOps"; /** * The options for the upgrade. This is only used internally, From d8ca024be6c5e78573d594ff0b9565b2ae2d20da Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 7 Mar 2023 19:43:14 +0100 Subject: [PATCH 05/26] Moving traversal code from validator to tools Moved the 'binary', 'implicitTiling', 'metadata', and 'traversal' parts from the validator to the tools, including cleanups for the usage of 'defined' and minor changes in the directory structure. --- demos/TraversalDemo.ts | 69 +++ src/base/Buffers.ts | 15 + src/binary/BinaryBufferData.ts | 23 + src/binary/BinaryBufferDataResolver.ts | 82 +++ src/binary/BinaryBufferStructure.ts | 12 + src/binary/BinaryBuffers.ts | 99 ++++ src/binary/BinaryDataError.ts | 15 + src/contentOperations/ContentOps.ts | 1 - src/implicitTiling/AvailabilityInfo.ts | 23 + src/implicitTiling/AvailabilityInfos.ts | 91 +++ src/implicitTiling/BufferAvailabilityInfo.ts | 33 ++ .../ConstantAvailabilityInfo.ts | 27 + src/implicitTiling/ImplicitTilingError.ts | 19 + src/implicitTiling/ImplicitTilings.ts | 293 ++++++++++ src/implicitTiling/MortonOrder.ts | 105 ++++ src/implicitTiling/OctreeCoordinates.ts | 105 ++++ src/implicitTiling/Octrees.ts | 60 ++ src/implicitTiling/QuadtreeCoordinates.ts | 93 +++ src/implicitTiling/Quadtrees.ts | 57 ++ src/implicitTiling/SubtreeInfo.ts | 33 ++ src/implicitTiling/SubtreeInfos.ts | 170 ++++++ src/implicitTiling/TemplateUris.ts | 98 ++++ src/implicitTiling/TreeCoordinates.ts | 78 +++ src/metadata/ArrayValues.ts | 275 +++++++++ src/metadata/DefaultMetadataEntityModel.ts | 55 ++ src/metadata/MetadataComponentTypes.ts | 157 +++++ src/metadata/MetadataEntityModel.ts | 38 ++ src/metadata/MetadataEntityModels.ts | 103 ++++ src/metadata/MetadataError.ts | 18 + src/metadata/MetadataTypes.ts | 82 +++ src/metadata/MetadataUtilities.ts | 129 ++++ src/metadata/MetadataValues.ts | 106 ++++ src/metadata/binary/ArrayBuffers.ts | 25 + src/metadata/binary/BinaryEnumInfo.ts | 24 + .../binary/BinaryMetadataEntityModel.ts | 69 +++ src/metadata/binary/BinaryPropertyTable.ts | 53 ++ src/metadata/binary/BinaryPropertyTables.ts | 551 ++++++++++++++++++ .../binary/BooleanArrayPropertyModel.ts | 54 ++ src/metadata/binary/BooleanPropertyModel.ts | 37 ++ .../binary/NumericArrayPropertyModel.ts | 81 +++ src/metadata/binary/NumericBuffers.ts | 151 +++++ src/metadata/binary/NumericPropertyModel.ts | 46 ++ src/metadata/binary/PropertyModel.ts | 30 + src/metadata/binary/PropertyModels.ts | 295 ++++++++++ src/metadata/binary/PropertyTableModel.ts | 136 +++++ .../binary/StringArrayPropertyModel.ts | 80 +++ src/metadata/binary/StringPropertyModel.ts | 47 ++ src/structure/extensions/BoundingVolumeS2.ts | 7 + src/traversal/ExplicitTraversedTile.ts | 254 ++++++++ src/traversal/ImplicitTileTraversal.ts | 238 ++++++++ src/traversal/ImplicitTraversedTile.ts | 356 +++++++++++ src/traversal/TilesetTraverser.ts | 75 +++ src/traversal/TraversalCallback.ts | 20 + src/traversal/TraversedTile.ts | 92 +++ .../cesium/BoundingVolumeDerivation.ts | 345 +++++++++++ src/traversal/cesium/HilbertOrder.ts | 104 ++++ src/traversal/cesium/S2Cell.ts | 106 ++++ 57 files changed, 5839 insertions(+), 1 deletion(-) create mode 100644 demos/TraversalDemo.ts create mode 100644 src/binary/BinaryBufferData.ts create mode 100644 src/binary/BinaryBufferDataResolver.ts create mode 100644 src/binary/BinaryBufferStructure.ts create mode 100644 src/binary/BinaryBuffers.ts create mode 100644 src/binary/BinaryDataError.ts create mode 100644 src/implicitTiling/AvailabilityInfo.ts create mode 100644 src/implicitTiling/AvailabilityInfos.ts create mode 100644 src/implicitTiling/BufferAvailabilityInfo.ts create mode 100644 src/implicitTiling/ConstantAvailabilityInfo.ts create mode 100644 src/implicitTiling/ImplicitTilingError.ts create mode 100644 src/implicitTiling/ImplicitTilings.ts create mode 100644 src/implicitTiling/MortonOrder.ts create mode 100644 src/implicitTiling/OctreeCoordinates.ts create mode 100644 src/implicitTiling/Octrees.ts create mode 100644 src/implicitTiling/QuadtreeCoordinates.ts create mode 100644 src/implicitTiling/Quadtrees.ts create mode 100644 src/implicitTiling/SubtreeInfo.ts create mode 100644 src/implicitTiling/SubtreeInfos.ts create mode 100644 src/implicitTiling/TemplateUris.ts create mode 100644 src/implicitTiling/TreeCoordinates.ts create mode 100644 src/metadata/ArrayValues.ts create mode 100644 src/metadata/DefaultMetadataEntityModel.ts create mode 100644 src/metadata/MetadataComponentTypes.ts create mode 100644 src/metadata/MetadataEntityModel.ts create mode 100644 src/metadata/MetadataEntityModels.ts create mode 100644 src/metadata/MetadataError.ts create mode 100644 src/metadata/MetadataTypes.ts create mode 100644 src/metadata/MetadataUtilities.ts create mode 100644 src/metadata/MetadataValues.ts create mode 100644 src/metadata/binary/ArrayBuffers.ts create mode 100644 src/metadata/binary/BinaryEnumInfo.ts create mode 100644 src/metadata/binary/BinaryMetadataEntityModel.ts create mode 100644 src/metadata/binary/BinaryPropertyTable.ts create mode 100644 src/metadata/binary/BinaryPropertyTables.ts create mode 100644 src/metadata/binary/BooleanArrayPropertyModel.ts create mode 100644 src/metadata/binary/BooleanPropertyModel.ts create mode 100644 src/metadata/binary/NumericArrayPropertyModel.ts create mode 100644 src/metadata/binary/NumericBuffers.ts create mode 100644 src/metadata/binary/NumericPropertyModel.ts create mode 100644 src/metadata/binary/PropertyModel.ts create mode 100644 src/metadata/binary/PropertyModels.ts create mode 100644 src/metadata/binary/PropertyTableModel.ts create mode 100644 src/metadata/binary/StringArrayPropertyModel.ts create mode 100644 src/metadata/binary/StringPropertyModel.ts create mode 100644 src/structure/extensions/BoundingVolumeS2.ts create mode 100644 src/traversal/ExplicitTraversedTile.ts create mode 100644 src/traversal/ImplicitTileTraversal.ts create mode 100644 src/traversal/ImplicitTraversedTile.ts create mode 100644 src/traversal/TilesetTraverser.ts create mode 100644 src/traversal/TraversalCallback.ts create mode 100644 src/traversal/TraversedTile.ts create mode 100644 src/traversal/cesium/BoundingVolumeDerivation.ts create mode 100644 src/traversal/cesium/HilbertOrder.ts create mode 100644 src/traversal/cesium/S2Cell.ts diff --git a/demos/TraversalDemo.ts b/demos/TraversalDemo.ts new file mode 100644 index 00000000..6038029a --- /dev/null +++ b/demos/TraversalDemo.ts @@ -0,0 +1,69 @@ +import fs from "fs"; +import path from "path"; + +import { ResourceResolvers } from "../src"; +import { TilesetTraverser } from "../src/traversal/TilesetTraverser"; + +/** + * Only for internal use and basic tests: + * + * Reads a JSON file, parses it, and returns the result. + * If the file cannot be read or parsed, then an error + * message will be printed and `undefined` is returned. + * + * @param filePath - The path to the file + * @returns A promise that resolves with the result or `undefined` + */ +async function readJsonUnchecked(filePath: string): Promise { + try { + const data = fs.readFileSync(filePath); + if (!data) { + console.error("Could not read " + filePath); + return undefined; + } + const jsonString = data.toString(); + const result = JSON.parse(jsonString); + return result; + } catch (error) { + console.error("Could not parse JSON", error); + return undefined; + } + } + + +async function tilesetTraversalDemo(filePath: string) { + const directory = path.dirname(filePath); + const resourceResolver = + ResourceResolvers.createFileResourceResolver(directory); + const tileset = await readJsonUnchecked(filePath); + // Note: External schemas are not considered here + const schema = tileset.schema; + const depthFirst = false; + console.log("Traversing tileset"); + await TilesetTraverser.traverse( + tileset, + schema, + resourceResolver, + async (traversedTile) => { + const contentUris = traversedTile.getContents().map((c) => c.uri); + const geometricError = traversedTile.asTile().geometricError; + console.log( + ` Traversed tile: ${traversedTile}, ` + + `path: ${traversedTile.path}, ` + + `contents [${contentUris}], ` + + `geometricError ${geometricError}` + ); + return true; + }, + depthFirst + ); + console.log("Traversing tileset DONE"); + } + + async function runDemo() { + const tilesetFile = "../3d-tiles-samples/1.1/SparseImplicitQuadtree/tileset.json"; + await tilesetTraversalDemo(tilesetFile); + } + + runDemo(); + \ No newline at end of file diff --git a/src/base/Buffers.ts b/src/base/Buffers.ts index 3158a600..35ce67a7 100644 --- a/src/base/Buffers.ts +++ b/src/base/Buffers.ts @@ -223,4 +223,19 @@ export class Buffers { } return true; } + + /** + * Creates a string representation of the given buffer where each + * byte is encoded in its binary form, consisting of 8 bits. + * + * Warning: This is primarily intended for debugging. The resulting + * string may be very long... + * + * @param buffer - The input buffer + * @returns The resulting string + */ + static createBinaryString(buffer: Buffer): string { + const s = [...buffer].map((b) => b.toString(2).padStart(8, "0")).join(""); + return s; + } } diff --git a/src/binary/BinaryBufferData.ts b/src/binary/BinaryBufferData.ts new file mode 100644 index 00000000..22ed6da5 --- /dev/null +++ b/src/binary/BinaryBufferData.ts @@ -0,0 +1,23 @@ +/** + * A basic structure holding binary data that consists of + * buffers that are split into buffer views. This is the + * actual data that corresponds to a `BinaryBufferStructure` + * + * @internal + */ +export interface BinaryBufferData { + /** + * An array of buffers that contain the data that correspond + * to `BufferView` objects. + * + * These are usually (but not necessarily) slices of + * the 'buffersData' elements. + */ + bufferViewsData: Buffer[]; + + /** + * An array of buffers that contain the data that correspond + * to `BufferObject` objects. + */ + buffersData: Buffer[]; +} diff --git a/src/binary/BinaryBufferDataResolver.ts b/src/binary/BinaryBufferDataResolver.ts new file mode 100644 index 00000000..139d3bb5 --- /dev/null +++ b/src/binary/BinaryBufferDataResolver.ts @@ -0,0 +1,82 @@ +import { defined } from "../base/defined"; + +import { ResourceResolver } from "../io/ResourceResolver"; + +import { BinaryBufferData } from "./BinaryBufferData"; +import { BinaryBufferStructure } from "./BinaryBufferStructure"; +import { BinaryDataError } from "./BinaryDataError"; + +/** + * A class for resolving binary buffer data. + */ +export class BinaryBufferDataResolver { + /** + * Resolves the buffer data that is defined in the given structure. + * + * It receives a `BinaryBufferStructure` that contains the + * `BufferObject` and `BufferView` definitions, resolves the + * data from the buffer URIs using the given resource resolver, + * and returns a `BinaryBufferData` that contains the actual + * binary buffer data. + * + * The given `binaryBuffer` will be used as the buffer data + * for any buffer that does not have a URI (intended for + * binary subtree files)) + * + * @param binaryBufferStructure - The `BinaryBufferStructure` + * @param binaryBuffer - The optional binary buffer + * @param resourceResolver - The `ResourceResolver` + * @returns The `BinaryBufferData` + * @throws BinaryDataError If the data could not be resolved + */ + static async resolve( + binaryBufferStructure: BinaryBufferStructure, + binaryBuffer: Buffer | undefined, + resourceResolver: ResourceResolver + ): Promise { + // Obtain the buffer data objects: One `Buffer` for + // each `BufferObject` + const buffersData: Buffer[] = []; + const buffers = binaryBufferStructure.buffers; + if (buffers) { + for (const buffer of buffers) { + if (!defined(buffer.uri)) { + if (!binaryBuffer) { + throw new BinaryDataError( + "Expected a binary buffer, but got undefined" + ); + } + buffersData.push(binaryBuffer); + } else { + //console.log("Obtaining buffer data from " + buffer.uri); + const bufferData = await resourceResolver.resolveData(buffer.uri); + if (!bufferData) { + const message = `Could not resolve buffer ${buffer.uri}`; + throw new BinaryDataError(message); + } + buffersData.push(bufferData); + } + } + } + + // Obtain the buffer view data objects: One `Buffer` for + // each `BufferView` + const bufferViewsData = []; + const bufferViews = binaryBufferStructure.bufferViews; + if (bufferViews) { + for (const bufferView of bufferViews) { + const bufferData = buffersData[bufferView.buffer]; + const start = bufferView.byteOffset; + const end = start + bufferView.byteLength; + const bufferViewData = bufferData.subarray(start, end); + bufferViewsData.push(bufferViewData); + } + } + + const binarybBufferData: BinaryBufferData = { + buffersData: buffersData, + bufferViewsData: bufferViewsData, + }; + return binarybBufferData; + } +} diff --git a/src/binary/BinaryBufferStructure.ts b/src/binary/BinaryBufferStructure.ts new file mode 100644 index 00000000..dec62dfa --- /dev/null +++ b/src/binary/BinaryBufferStructure.ts @@ -0,0 +1,12 @@ +import { BufferObject } from "../structure/BufferObject"; +import { BufferView } from "../structure/BufferView"; + +/** + * A basic class holding information about the structure of + * buffers that are split into buffer views, for example, + * from a `Subtree` object. + */ +export interface BinaryBufferStructure { + buffers: BufferObject[]; + bufferViews: BufferView[]; +} diff --git a/src/binary/BinaryBuffers.ts b/src/binary/BinaryBuffers.ts new file mode 100644 index 00000000..56fbf13d --- /dev/null +++ b/src/binary/BinaryBuffers.ts @@ -0,0 +1,99 @@ +import { BufferObject } from "../structure/BufferObject"; +import { BufferView } from "../structure/BufferView"; + +import { BinaryBufferStructure } from "./BinaryBufferStructure"; +import { BinaryBufferData } from "./BinaryBufferData"; + +/** + * Methods related to binary buffers. + * + * This class can be used for building a basic `BinaryBufferData` structure + * from a sequence of buffers that represent buffer views. + * + * @internal + */ +export class BinaryBuffers { + /** + * Add a set of buffer views to the given `BinaryBufferData`, and return + * a `BinaryBufferStructure` that describes their layout. + * + * This will combine the given buffer views into a single buffer, and add the + * buffer views and the generated buffer to the given `BinaryBufferData`. + * + * The layout (byte offset and lengths, and the `buffer` index) will be + * returned via the `BinaryBufferStructure`. + * + * NOTE: This function could be generalized in many ways. Right now, it + * creates a single `BufferObject` for the given data, without a `uri`. + * Further configuration options might be added in the future. + * + * @param binaryBufferData - The `BinaryBufferData` + * @param newBufferViewsData - The buffer views that should be added + * @returns A `BinaryBufferStructure` that describes the structure + * of the buffer views, after they have been assembled into a buffer. + */ + static createBinaryBufferStructure( + binaryBufferData: BinaryBufferData, + newBufferViewsData: Buffer[] + ): BinaryBufferStructure { + const alignment = 8; + const bufferViewsData = binaryBufferData.bufferViewsData; + const buffersData = binaryBufferData.buffersData; + + // The arrays for the output data + const buffers: BufferObject[] = []; + const bufferViews: BufferView[] = []; + const currentBufferIndex = buffersData.length; + + // Create a new buffer data combines all the given buffer views, + // including possible padding bytes + let currentBufferData = Buffer.alloc(0); + let currentByteOffset = 0; + for (let i = 0; i < newBufferViewsData.length; i++) { + const newBufferViewData = newBufferViewsData[i]; + const requiredPadding = + (alignment - (currentByteOffset % alignment)) % alignment; + if (requiredPadding != 0) { + const paddingBuffer = Buffer.alloc(requiredPadding); + currentBufferData = Buffer.concat([currentBufferData, paddingBuffer]); + currentByteOffset += requiredPadding; + } + currentBufferData = Buffer.concat([currentBufferData, newBufferViewData]); + + // Store the buffer view + const bufferView: BufferView = { + buffer: currentBufferIndex, + byteOffset: currentByteOffset, + byteLength: newBufferViewData.length, + }; + bufferViews.push(bufferView); + + // Store the data of the buffer view. This should have the same + // contents as the `newBufferViewData`, but will be a subarray + // of the newly created buffer + const bufferViewData = currentBufferData.subarray( + currentByteOffset, + currentByteOffset + newBufferViewData.length + ); + bufferViewsData.push(bufferViewData); + + currentByteOffset += newBufferViewData.length; + } + + // Store the buffer + const buffer: BufferObject = { + byteLength: currentBufferData.length, + }; + buffers.push(buffer); + + // Store the buffer data + buffersData.push(currentBufferData); + + // Return the resulting buffer structure + const binaryBufferStructure: BinaryBufferStructure = { + buffers: buffers, + bufferViews: bufferViews, + }; + return binaryBufferStructure; + } +} diff --git a/src/binary/BinaryDataError.ts b/src/binary/BinaryDataError.ts new file mode 100644 index 00000000..412ef8ff --- /dev/null +++ b/src/binary/BinaryDataError.ts @@ -0,0 +1,15 @@ +/** + * An error that indicates that binary data was structurally invalid. + */ +export class BinaryDataError extends Error { + constructor(message: string) { + super(message); + // See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes + // #extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, BinaryDataError.prototype); + } + + override toString = (): string => { + return `${this.name}: ${this.message}`; + }; +} diff --git a/src/contentOperations/ContentOps.ts b/src/contentOperations/ContentOps.ts index acd60f80..b7e22e67 100644 --- a/src/contentOperations/ContentOps.ts +++ b/src/contentOperations/ContentOps.ts @@ -3,7 +3,6 @@ import { GltfUtilities } from "./GtlfUtilities"; import { TileFormats } from "../tileFormats/TileFormats"; export class ContentOps { - static b3dmToGlbBuffer(inputBuffer: Buffer): Buffer { const inputTileData = TileFormats.readTileData(inputBuffer); const outputBuffer = inputTileData.payload; diff --git a/src/implicitTiling/AvailabilityInfo.ts b/src/implicitTiling/AvailabilityInfo.ts new file mode 100644 index 00000000..4b850b3f --- /dev/null +++ b/src/implicitTiling/AvailabilityInfo.ts @@ -0,0 +1,23 @@ +/** + * An interface that describes the availability information + * in a subtree. This is used for tile, content, and child + * subtree availability. + */ +export interface AvailabilityInfo { + /** + * Returns the length of the availability information. + * + * @returns The length + */ + get length(): number; + + /** + * Returns whether the element at the specified index is + * available. + * + * @param index - The index + * @throws RangeError If the index is negative or not smaller + * than the length. + */ + isAvailable(index: number): boolean; +} diff --git a/src/implicitTiling/AvailabilityInfos.ts b/src/implicitTiling/AvailabilityInfos.ts new file mode 100644 index 00000000..8ee9873a --- /dev/null +++ b/src/implicitTiling/AvailabilityInfos.ts @@ -0,0 +1,91 @@ +import { AvailabilityInfo } from "./AvailabilityInfo"; +import { BufferAvailabilityInfo } from "./BufferAvailabilityInfo"; +import { ConstantAvailabilityInfo } from "./ConstantAvailabilityInfo"; +import { ImplicitTilings } from "./ImplicitTilings"; + +import { Availability } from "../structure/Availability"; +import { TileImplicitTiling } from "../structure/TileImplicitTiling"; +import { defined } from "../base/defined"; +import { MetadataError } from "../metadata/MetadataError"; + +/** + * Methods for creating `AvailabilityInfo` instances + * + * @internal + */ +export class AvailabilityInfos { + /** + * Creates a new `AvailabilityInfo` for the given availability + * information, for tile- or content availability. + * + * @param availability - The `Availability` object + * @param bufferViewDatas - The `BufferView` data chunks + * @param implicitTiling - The `TileImplicitTiling` object + * @returns The `AvailabilityInfo` object + * @throws ImplicitTilingError If the given data is structurally + * invalid. + */ + static createTileOrContent( + availability: Availability, + bufferViewDatas: Buffer[], + implicitTiling: TileImplicitTiling + ): AvailabilityInfo { + const length = + ImplicitTilings.computeNumberOfNodesPerSubtree(implicitTiling); + return AvailabilityInfos.create(availability, bufferViewDatas, length); + } + + /** + * Creates a new `AvailabilityInfo` for the given availability + * information, for child subtree availability + * + * @param availability - The `Availability` object + * @param bufferViewDatas - The `BufferView` data chunks + * @param implicitTiling - The `TileImplicitTiling` object + * @returns The `AvailabilityInfo` object + * @throws ImplicitTilingError If the given data is structurally + * invalid. + */ + static createChildSubtree( + availability: Availability, + bufferViewDatas: Buffer[], + implicitTiling: TileImplicitTiling + ): AvailabilityInfo { + const length = ImplicitTilings.computeNumberOfNodesInLevel( + implicitTiling, + implicitTiling.subtreeLevels + ); + return AvailabilityInfos.create(availability, bufferViewDatas, length); + } + + /** + * Creates a new `AvailabilityInfo` for the given availability + * information, for child subtree availability + * + * @param availability - The `Availability` object + * @param bufferViewDatas - The `BufferView` data chunks + * @param length - The length of the availability info + * @returns The `AvailabilityInfo` object + * @throws MetadataError If the data is structurally invalid + */ + private static create( + availability: Availability, + bufferViewDatas: Buffer[], + length: number + ): AvailabilityInfo { + const constant = availability.constant; + if (defined(constant)) { + const available = constant === 1; + return new ConstantAvailabilityInfo(available, length); + } + // The bitstream MUST be defined when constant is undefined + const bitstream = availability.bitstream; + if (!defined(bitstream)) { + throw new MetadataError( + "The availability neither defines a constant nor a bitstream" + ); + } + const bufferViewData = bufferViewDatas[bitstream]; + return new BufferAvailabilityInfo(bufferViewData, length); + } +} diff --git a/src/implicitTiling/BufferAvailabilityInfo.ts b/src/implicitTiling/BufferAvailabilityInfo.ts new file mode 100644 index 00000000..5fef1a64 --- /dev/null +++ b/src/implicitTiling/BufferAvailabilityInfo.ts @@ -0,0 +1,33 @@ +import { AvailabilityInfo } from "./AvailabilityInfo"; + +/** + * Implementation of an `AvailabilityInfo` that is backed by + * a Buffer. + */ +export class BufferAvailabilityInfo implements AvailabilityInfo { + private readonly _buffer: Buffer; + private readonly _length: number; + + constructor(buffer: Buffer, length: number) { + this._buffer = buffer; + this._length = length; + } + + get length(): number { + return this._length; + } + + isAvailable(index: number): boolean { + if (index < 0 || index >= this.length) { + throw new RangeError( + `Index must be in [0,${this.length}), but is ${index}` + ); + } + const byteIndex = index >> 3; + const bitIndex = index % 8; + const b = this._buffer[byteIndex]; + const bit = 1 << bitIndex; + const result = (b & bit) != 0; + return result; + } +} diff --git a/src/implicitTiling/ConstantAvailabilityInfo.ts b/src/implicitTiling/ConstantAvailabilityInfo.ts new file mode 100644 index 00000000..d0c32ef0 --- /dev/null +++ b/src/implicitTiling/ConstantAvailabilityInfo.ts @@ -0,0 +1,27 @@ +import { AvailabilityInfo } from "./AvailabilityInfo"; + +/** + * Implementation of an `AvailabilityInfo` that has a constant value. + */ +export class ConstantAvailabilityInfo implements AvailabilityInfo { + private readonly _available: boolean; + private readonly _length: number; + + constructor(available: boolean, length: number) { + this._available = available; + this._length = length; + } + + get length(): number { + return this._length; + } + + isAvailable(index: number): boolean { + if (index < 0 || index >= this.length) { + throw new RangeError( + `Index must be in [0,${this.length}), but is ${index}` + ); + } + return this._available; + } +} diff --git a/src/implicitTiling/ImplicitTilingError.ts b/src/implicitTiling/ImplicitTilingError.ts new file mode 100644 index 00000000..c02a4c12 --- /dev/null +++ b/src/implicitTiling/ImplicitTilingError.ts @@ -0,0 +1,19 @@ +/** + * An error that indicates that implicit tiling data was structurally + * invalid. + * + * This may be thrown by methods that create the convenience classes + * for this package, when the given resources are not valid. + */ +export class ImplicitTilingError extends Error { + constructor(message: string) { + super(message); + // See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes + // #extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, ImplicitTilingError.prototype); + } + + override toString = (): string => { + return `${this.name}: ${this.message}`; + }; +} diff --git a/src/implicitTiling/ImplicitTilings.ts b/src/implicitTiling/ImplicitTilings.ts new file mode 100644 index 00000000..7d069313 --- /dev/null +++ b/src/implicitTiling/ImplicitTilings.ts @@ -0,0 +1,293 @@ +import { TreeCoordinates } from "./TreeCoordinates"; +import { Quadtrees } from "./Quadtrees"; +import { QuadtreeCoordinates } from "./QuadtreeCoordinates"; +import { Octrees } from "./Octrees"; +import { OctreeCoordinates } from "./OctreeCoordinates"; +import { TemplateUris } from "./TemplateUris"; + +import { ImplicitTilingError } from "./ImplicitTilingError"; + +import { TileImplicitTiling } from "../structure/TileImplicitTiling"; + +/** + * Utility methods related to `TileImplicitTiling` instances. + * + * Preliminary! + * + * The purpose of these methods is mainly to hide the differences + * between `QUADTREE` and `OCTREE` subdivision schemes. It is + * possible to completely hide these differences with the + * appropriate abstractions. But the resulting structures may + * involve interfaces that have to be passed all the way to the + * point where they are used for seemingly trivial computations, and + * this may look obscure and overengineered at the first glance. + * However, these methods in this class might eventually be moved + * into something like an instantiable `ImplicitTilingInfo` class. + * + * The methods usually assume that the subdivison scheme is + * either `QUADTREE` and `OCTREE`, and will throw an + * `ImplicitTilingError` if this is not the case. + * + * @internal + */ +export class ImplicitTilings { + /** + * Returns a generator for the tile coordinates of a single subtree + * for the given implicit tiling object. + * + * @param implicitTiling - The `TileImplicitTiling` object + * @returns The generator + * @throws ImplicitTilingError if the given object does not + * have a valid `subdivisionScheme`. + */ + static createSubtreeCoordinatesIterator( + implicitTiling: TileImplicitTiling + ): IterableIterator { + const r = this.createRootCoordinates(implicitTiling); + const depthFirst = false; + return r.descendants(implicitTiling.subtreeLevels - 1, depthFirst); + } + + /** + * Returns the total number of nodes in one subtree for the given + * implicit tiling object. + * + * @param implicitTiling - The `TileImplicitTiling` object + * @returns The number of nodes + * @throws ImplicitTilingError if the given object does not + * have a valid `subdivisionScheme`. + */ + static computeNumberOfNodesPerSubtree( + implicitTiling: TileImplicitTiling + ): number { + const levels = implicitTiling.subtreeLevels; + if (implicitTiling.subdivisionScheme === "QUADTREE") { + return Quadtrees.computeNumberOfNodesForLevels(levels); + } + if (implicitTiling.subdivisionScheme === "OCTREE") { + return Octrees.computeNumberOfNodesForLevels(levels); + } + throw new ImplicitTilingError( + `Invalid subdivisionScheme: ${implicitTiling.subdivisionScheme}` + ); + } + + /** + * Returns the number of nodes in the specified level of a + * tree with the given implicit tiling + * + * @param implicitTiling - The `TileImplicitTiling` object + * @param level - The level + * @returns The number of nodes + * @throws ImplicitTilingError if the given object does not + * have a valid `subdivisionScheme`, or the level is negative. + */ + static computeNumberOfNodesInLevel( + implicitTiling: TileImplicitTiling, + level: number + ): number { + if (level < 0) { + throw new ImplicitTilingError(`Invalid level: ${level}`); + } + const size = 1 << level; + if (implicitTiling.subdivisionScheme === "QUADTREE") { + return size * size; + } + if (implicitTiling.subdivisionScheme === "OCTREE") { + return size * size * size; + } + throw new ImplicitTilingError( + `Invalid subdivisionScheme: ${implicitTiling.subdivisionScheme}` + ); + } + + /** + * Substitutes the given coordinates into the given template URI. + * + * @param subdivisionScheme - The subdivision scheme + * @param templateUri - The template URI + * @param coordinates - The tree coordinates + * @returns The resulting URI + * @throws ImplicitTilingError if the `subdivisionScheme` is not valid. + */ + static substituteTemplateUri( + subdivisionScheme: string, + templateUri: string, + coordinates: TreeCoordinates + ): string { + if (subdivisionScheme === "QUADTREE") { + const quadtreeCoordinates = coordinates as QuadtreeCoordinates; + return TemplateUris.substituteQuadtree(templateUri, quadtreeCoordinates); + } + if (subdivisionScheme === "OCTREE") { + const octreeCoordinates = coordinates as OctreeCoordinates; + return TemplateUris.substituteOctree(templateUri, octreeCoordinates); + } + throw new ImplicitTilingError( + `Invalid subdivisionScheme: ${subdivisionScheme}` + ); + } + + /** + * Creates a string representation for the given coordinates, describing + * them as coordinates of a tile within an implicit tileset. + * + * Details about the returned string are not specified. But it is + * supposed to be a string that contains the level,x,y,[z] components + * of the given coordinates for `QuadtreeCoordinates` and + * `OctreeCoordinates`. + * + * @param coordinates - The tree coordinates + * @returns The result + * @throws ImplicitTilingError if the coordinates are neither + * `QuadtreeCoordinates` nor `OctreeCoordinates`. + */ + static createString(coordinates: TreeCoordinates): string { + if (coordinates instanceof QuadtreeCoordinates) { + const quadtreeCoordinates = coordinates as QuadtreeCoordinates; + const level = quadtreeCoordinates.level; + const x = quadtreeCoordinates.x; + const y = quadtreeCoordinates.y; + const result = `at[${level}][${x},${y}]`; + return result; + } + if (coordinates instanceof OctreeCoordinates) { + const octreeCoordinates = coordinates as OctreeCoordinates; + const level = octreeCoordinates.level; + const x = octreeCoordinates.x; + const y = octreeCoordinates.y; + const z = octreeCoordinates.z; + const result = `at[${level}][${x},${y},${z}]`; + return result; + } + throw new ImplicitTilingError(`Invalid coordinates type: ${coordinates}`); + } + + /** + * Returns the root coordinates for the specified implicit tileset. + * + * @param implicitTiling - The `TileImplicitTiling` object + * @returns The root coordinates + * @throws ImplicitTilingError if the given object does not + * have a valid `subdivisionScheme`. + */ + static createRootCoordinates( + implicitTiling: TileImplicitTiling + ): TreeCoordinates { + const subdivisionScheme = implicitTiling.subdivisionScheme; + if (subdivisionScheme === "QUADTREE") { + return new QuadtreeCoordinates(0, 0, 0); + } + if (subdivisionScheme === "OCTREE") { + return new OctreeCoordinates(0, 0, 0, 0); + } + throw new ImplicitTilingError( + `Invalid subdivisionScheme: ${subdivisionScheme}` + ); + } + + /** + * Computes the global coordinates from the given local ones. + * + * The `rootCoordinates` are the root coordinates of a subtree + * in the given implicit tiling. The `coordinates` are the local + * coordinates of a node within this subtree. + * The result will be the global coordinates for the node within + * the given implicit tiling. + * + * @param implicitTiling - The `TileImplicitTiling` object + * @param rootCoordinates - The root coordinates + * @param coordinates - The coordinates + * @returns The global coordinates + * @throws ImplicitTilingError if the given object does not + * have a valid `subdivisionScheme`. + */ + static globalizeCoordinates( + implicitTiling: TileImplicitTiling, + rootCoordinates: TreeCoordinates, + coordinates: TreeCoordinates + ): TreeCoordinates { + const subdivisionScheme = implicitTiling.subdivisionScheme; + if (subdivisionScheme === "QUADTREE") { + const quadtreeRootCoordinates = rootCoordinates as QuadtreeCoordinates; + const quadtreeCoordinates = coordinates as QuadtreeCoordinates; + return ImplicitTilings.globalizeQuadtreeCoords( + quadtreeRootCoordinates, + quadtreeCoordinates + ); + } + if (subdivisionScheme === "OCTREE") { + const octreeRootCoordinates = rootCoordinates as OctreeCoordinates; + const octreeCoordinates = coordinates as OctreeCoordinates; + return ImplicitTilings.globalizeOctreeCoords( + octreeRootCoordinates, + octreeCoordinates + ); + } + throw new ImplicitTilingError( + `Invalid subdivisionScheme: ${subdivisionScheme}` + ); + } + + /** + * Compute the global quadtree coordinates for the given coordinates. + * + * @param rootCoords - The (global) root coordinates of the subtree + * @param localCoords - The local coordinates inside the subtree + * @returns The global coordinates + */ + private static globalizeQuadtreeCoords( + rootCoords: QuadtreeCoordinates, + localCoords: QuadtreeCoordinates + ): QuadtreeCoordinates { + const rootLevel = rootCoords.level; + const rootX = rootCoords.x; + const rootY = rootCoords.y; + + const localLevel = localCoords.level; + const localX = localCoords.x; + const localY = localCoords.y; + + const globalLevel = rootLevel + localLevel; + const globalX = (rootX << localLevel) + localX; + const globalY = (rootY << localLevel) + localY; + + const globalCoords = new QuadtreeCoordinates(globalLevel, globalX, globalY); + return globalCoords; + } + + /** + * Compute the global octree coordinates for the given coordinates. + * + * @param rootCoords - The (global) root coordinates of the subtree + * @param localCoords - The local coordinates inside the subtree + * @returns The global coordinates + */ + private static globalizeOctreeCoords( + rootCoords: OctreeCoordinates, + localCoords: OctreeCoordinates + ): OctreeCoordinates { + const rootLevel = rootCoords.level; + const rootX = rootCoords.x; + const rootY = rootCoords.y; + const rootZ = rootCoords.z; + + const localLevel = localCoords.level; + const localX = localCoords.x; + const localY = localCoords.y; + const localZ = localCoords.z; + + const globalLevel = rootLevel + localLevel; + const globalX = (rootX << localLevel) + localX; + const globalY = (rootY << localLevel) + localY; + const globalZ = (rootZ << localLevel) + localZ; + + const globalCoords = new OctreeCoordinates( + globalLevel, + globalX, + globalY, + globalZ + ); + return globalCoords; + } +} diff --git a/src/implicitTiling/MortonOrder.ts b/src/implicitTiling/MortonOrder.ts new file mode 100644 index 00000000..3509dfd8 --- /dev/null +++ b/src/implicitTiling/MortonOrder.ts @@ -0,0 +1,105 @@ +// Ported from https://github.com/CesiumGS/cesium/blob/4b333bc145fa9f7aed0c7ad7e0f46cb001a94ddd/Source/Core/MortonOrder.js + +/** + * Morton Order (aka Z-Order Curve) helper functions. + * @see {@link https://en.wikipedia.org/wiki/Z-order_curve} + * + * @internal + */ +export class MortonOrder { + /** + * Inserts one 0 bit of spacing between a number's bits. This is the opposite of removeOneSpacing. + * + * Example: + * input: 6 + * input (binary): 110 + * output (binary): 10100 + * ^ ^ (added) + * output: 20 + * + * @param v - A 16-bit unsigned integer. + * @returns A 32-bit unsigned integer. + * @see {@link https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/} + */ + private static insertOneSpacing(v: number): number { + v = (v ^ (v << 8)) & 0x00ff00ff; + v = (v ^ (v << 4)) & 0x0f0f0f0f; + v = (v ^ (v << 2)) & 0x33333333; + v = (v ^ (v << 1)) & 0x55555555; + return v; + } + + /** + * Inserts two 0 bits of spacing between a number's bits. This is the opposite of removeTwoSpacing. + * + * Example: + * input: 6 + * input (binary): 110 + * output (binary): 1001000 + * ^^ ^^ (added) + * output: 72 + * + * @internal + * @param v - A 10-bit unsigned integer. + * @returns A 30-bit unsigned integer. + * @see {@link https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/} + */ + private static insertTwoSpacing(v: number): number { + v = (v ^ (v << 16)) & 0x030000ff; + v = (v ^ (v << 8)) & 0x0300f00f; + v = (v ^ (v << 4)) & 0x030c30c3; + v = (v ^ (v << 2)) & 0x09249249; + return v; + } + + /** + * Computes the Morton index from 2D coordinates. This is equivalent to interleaving their bits. + * The inputs must be 16-bit unsigned integers (resulting in 32-bit Morton index) due to 32-bit bitwise operator limitation in JavaScript. + * + * @param x - The X coordinate in the range [0, (2^16)-1]. + * @param y - The Y coordinate in the range [0, (2^16)-1]. + * @returns The Morton index. + * @internal + */ + static encode2D(x: number, y: number): number { + //>>includeStart('debug', pragmas.debug); + if (x < 0 || x > 65535 || y < 0 || y > 65535) { + throw new Error("inputs must be 16-bit unsigned integers"); + } + //>>includeEnd('debug'); + + // Note: JavaScript bitwise operations return signed 32-bit integers, so the + // final result needs to be reintepreted as an unsigned integer using >>> 0. + // This is not needed for encode3D because the result is guaranteed to be at most + // 30 bits and thus will always be interpreted as an unsigned value. + return ( + (MortonOrder.insertOneSpacing(x) | + (MortonOrder.insertOneSpacing(y) << 1)) >>> + 0 + ); + } + + /** + * Computes the Morton index from 3D coordinates. This is equivalent to interleaving their bits. + * The inputs must be 10-bit unsigned integers (resulting in 30-bit Morton index) due to 32-bit bitwise operator limitation in JavaScript. + * + * @param x - The X coordinate in the range [0, (2^10)-1]. + * @param y - The Y coordinate in the range [0, (2^10)-1]. + * @param z - The Z coordinate in the range [0, (2^10)-1]. + * @returns The Morton index. + * @internal + */ + static encode3D(x: number, y: number, z: number): number { + //>>includeStart('debug', pragmas.debug); + if (x < 0 || x > 1023 || y < 0 || y > 1023 || z < 0 || z > 1023) { + throw new Error("inputs must be 10-bit unsigned integers"); + } + //>>includeEnd('debug'); + + return ( + MortonOrder.insertTwoSpacing(x) | + (MortonOrder.insertTwoSpacing(y) << 1) | + (MortonOrder.insertTwoSpacing(z) << 2) + ); + } +} diff --git a/src/implicitTiling/OctreeCoordinates.ts b/src/implicitTiling/OctreeCoordinates.ts new file mode 100644 index 00000000..5f6c5952 --- /dev/null +++ b/src/implicitTiling/OctreeCoordinates.ts @@ -0,0 +1,105 @@ +import { MortonOrder } from "./MortonOrder"; +import { Octrees } from "./Octrees"; +import { TreeCoordinates } from "./TreeCoordinates"; + +/** + * An implementation of `TreeCoordinates` for octrees + */ +export class OctreeCoordinates implements TreeCoordinates { + private readonly _level: number; + private readonly _x: number; + private readonly _y: number; + private readonly _z: number; + + constructor(level: number, x: number, y: number, z: number) { + this._level = level; + this._x = x; + this._y = y; + this._z = z; + } + + get level(): number { + return this._level; + } + + get x(): number { + return this._x; + } + + get y(): number { + return this._y; + } + + get z(): number { + return this._z; + } + + parent(): OctreeCoordinates | null { + if (this._level === 0) { + return null; + } + const pLevel = this._level - 1; + const px = this._x >> 1; + const py = this._y >> 1; + const pz = this._z >> 1; + return new OctreeCoordinates(pLevel, px, py, pz); + } + + *children(): IterableIterator { + const nLevel = this._level + 1; + const nX = this._x << 1; + const nY = this._y << 1; + const nZ = this._z << 1; + yield new OctreeCoordinates(nLevel, nX + 0, nY + 0, nZ + 0); + yield new OctreeCoordinates(nLevel, nX + 1, nY + 0, nZ + 0); + yield new OctreeCoordinates(nLevel, nX + 0, nY + 1, nZ + 0); + yield new OctreeCoordinates(nLevel, nX + 1, nY + 1, nZ + 0); + yield new OctreeCoordinates(nLevel, nX + 0, nY + 0, nZ + 1); + yield new OctreeCoordinates(nLevel, nX + 1, nY + 0, nZ + 1); + yield new OctreeCoordinates(nLevel, nX + 0, nY + 1, nZ + 1); + yield new OctreeCoordinates(nLevel, nX + 1, nY + 1, nZ + 1); + } + + descendants( + maxLevelInclusive: number, + // eslint-disable-next-line @typescript-eslint/no-inferrable-types + depthFirst: boolean = false + ): IterableIterator { + const queue: OctreeCoordinates[] = [this]; + const result = { + [Symbol.iterator]() { + return this; + }, + next(): IteratorResult { + const element = depthFirst ? queue.pop() : queue.shift(); + if (!element) { + return { value: undefined, done: true }; + } + if (element.level < maxLevelInclusive) { + for (const c of element.children()) { + queue.push(c); + } + } + return { value: element, done: false }; + }, + }; + return result; + } + + toArray(): number[] { + return [this.level, this.x, this.y, this.z]; + } + + toIndex(): number { + const offset = Octrees.computeNumberOfNodesForLevels(this._level); + return offset + this.toIndexInLevel(); + } + + toIndexInLevel(): number { + return MortonOrder.encode3D(this._x, this._y, this._z); + } + + toString = (): string => { + return `(level ${this.level}, (${this._x},${this._y},${this._z}))`; + }; +} diff --git a/src/implicitTiling/Octrees.ts b/src/implicitTiling/Octrees.ts new file mode 100644 index 00000000..b6b81311 --- /dev/null +++ b/src/implicitTiling/Octrees.ts @@ -0,0 +1,60 @@ +import { OctreeCoordinates } from "./OctreeCoordinates"; + +export class Octrees { + /** + * Computes the number of nodes of an octree with the given number of + * levels. + * + * @param levels - The number of levels + * @returns The number of nodes + */ + static computeNumberOfNodesForLevels(levels: number) { + return ((1 << (levels * 3)) - 1) / (8 - 1); + } + + /** + * Returns a generator over all coordinates in the given level. + * + * @param level - The level + * @returns The coordinates + */ + static *coordinatesForLevel(level: number) { + const size = 1 << level; + for (let z = 0; z < size; z++) { + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + yield new OctreeCoordinates(level, x, y, z); + } + } + } + } + + /** + * Returns whether the given coordinates are valid. This means + * that the x, y, and z components are in the range that is + * determined by the level of the coordinates. + * + * @param c - The coordinates + * @returns Whether the coordinates are valid + */ + static isValid(c: OctreeCoordinates) { + const level = c.level; + const x = c.x; + const y = c.y; + const z = c.z; + if (level < 0) { + return false; + } + const size = 1 << level; + if (x < 0 || x >= size) { + return false; + } + if (y < 0 || y >= size) { + return false; + } + if (z < 0 || z >= size) { + return false; + } + return true; + } +} diff --git a/src/implicitTiling/QuadtreeCoordinates.ts b/src/implicitTiling/QuadtreeCoordinates.ts new file mode 100644 index 00000000..9cbc7e78 --- /dev/null +++ b/src/implicitTiling/QuadtreeCoordinates.ts @@ -0,0 +1,93 @@ +import { MortonOrder } from "./MortonOrder"; +import { Quadtrees } from "./Quadtrees"; +import { TreeCoordinates } from "./TreeCoordinates"; + +/** + * An implementation of `TreeCoordinates` for octrees + */ +export class QuadtreeCoordinates implements TreeCoordinates { + private readonly _level: number; + private readonly _x: number; + private readonly _y: number; + + constructor(level: number, x: number, y: number) { + this._level = level; + this._x = x; + this._y = y; + } + + get level(): number { + return this._level; + } + + get x(): number { + return this._x; + } + + get y(): number { + return this._y; + } + + parent(): QuadtreeCoordinates | null { + if (this._level === 0) { + return null; + } + const pLevel = this._level - 1; + const px = this._x >> 1; + const py = this._y >> 1; + return new QuadtreeCoordinates(pLevel, px, py); + } + + *children(): IterableIterator { + const nLevel = this._level + 1; + const nX = this._x << 1; + const nY = this._y << 1; + yield new QuadtreeCoordinates(nLevel, nX + 0, nY + 0); + yield new QuadtreeCoordinates(nLevel, nX + 1, nY + 0); + yield new QuadtreeCoordinates(nLevel, nX + 0, nY + 1); + yield new QuadtreeCoordinates(nLevel, nX + 1, nY + 1); + } + + descendants( + maxLevelInclusive: number, + // eslint-disable-next-line @typescript-eslint/no-inferrable-types + depthFirst: boolean = false + ): IterableIterator { + const queue: QuadtreeCoordinates[] = [this]; + const result = { + [Symbol.iterator]() { + return this; + }, + next(): IteratorResult { + const element = depthFirst ? queue.pop() : queue.shift(); + if (!element) { + return { value: undefined, done: true }; + } + if (element.level < maxLevelInclusive) { + for (const c of element.children()) { + queue.push(c); + } + } + return { value: element, done: false }; + }, + }; + return result; + } + + toArray(): number[] { + return [this.level, this.x, this.y]; + } + + toIndex(): number { + const offset = Quadtrees.computeNumberOfNodesForLevels(this._level); + return offset + this.toIndexInLevel(); + } + + toIndexInLevel(): number { + return MortonOrder.encode2D(this._x, this._y); + } + + toString = (): string => { + return `(level ${this.level}, (${this._x},${this._y}))`; + }; +} diff --git a/src/implicitTiling/Quadtrees.ts b/src/implicitTiling/Quadtrees.ts new file mode 100644 index 00000000..3dc5610d --- /dev/null +++ b/src/implicitTiling/Quadtrees.ts @@ -0,0 +1,57 @@ +import { QuadtreeCoordinates } from "./QuadtreeCoordinates"; + +/** + * Methods related to quadtrees. + */ +export class Quadtrees { + /** + * Computes the number of nodes of a quadtree with the given number of + * levels. + * + * @param levels - The number of levels + * @returns The number of nodes + */ + static computeNumberOfNodesForLevels(levels: number) { + return ((1 << (levels * 2)) - 1) / (4 - 1); + } + + /** + * Returns a generator over all coordinates in the given level. + * + * @param level - The level + * @returns The coordinates + */ + static *coordinatesForLevel(level: number) { + const size = 1 << level; + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + yield new QuadtreeCoordinates(level, x, y); + } + } + } + + /** + * Returns whether the given coordinates are valid. This means + * that the x and y components are in the range that is + * determined by the level of the coordinates. + * + * @param c - The coordinates + * @returns Whether the coordinates are valid + */ + static isValid(c: QuadtreeCoordinates) { + const level = c.level; + const x = c.x; + const y = c.y; + if (level < 0) { + return false; + } + const size = 1 << level; + if (x < 0 || x >= size) { + return false; + } + if (y < 0 || y >= size) { + return false; + } + return true; + } +} diff --git a/src/implicitTiling/SubtreeInfo.ts b/src/implicitTiling/SubtreeInfo.ts new file mode 100644 index 00000000..f1182992 --- /dev/null +++ b/src/implicitTiling/SubtreeInfo.ts @@ -0,0 +1,33 @@ +import { AvailabilityInfo } from "./AvailabilityInfo"; + +/** + * Summarizes the information about a subtree. + * + * It offers the availability information for tiles, child + * subtrees, and contents, as `AvailabilityInfo` objects. + */ +export class SubtreeInfo { + private readonly _tileAvailabilityInfo: AvailabilityInfo; + private readonly _contentAvailabilityInfos: AvailabilityInfo[]; + private readonly _childSubtreeAvailabilityInfo: AvailabilityInfo; + + constructor( + tileAvailabilityInfo: AvailabilityInfo, + contentAvailabilityInfos: AvailabilityInfo[], + childSubtreeAvailabilityInfo: AvailabilityInfo + ) { + (this._tileAvailabilityInfo = tileAvailabilityInfo), + (this._contentAvailabilityInfos = contentAvailabilityInfos), + (this._childSubtreeAvailabilityInfo = childSubtreeAvailabilityInfo); + } + + getTileAvailabilityInfo(): AvailabilityInfo { + return this._tileAvailabilityInfo; + } + getContentAvailabilityInfos(): AvailabilityInfo[] { + return this._contentAvailabilityInfos; + } + getChildSubtreeAvailabilityInfo(): AvailabilityInfo { + return this._childSubtreeAvailabilityInfo; + } +} diff --git a/src/implicitTiling/SubtreeInfos.ts b/src/implicitTiling/SubtreeInfos.ts new file mode 100644 index 00000000..b6ceeef5 --- /dev/null +++ b/src/implicitTiling/SubtreeInfos.ts @@ -0,0 +1,170 @@ +import { Buffers } from "../base/Buffers"; + +import { ResourceResolver } from "../io/ResourceResolver"; + +import { SubtreeInfo } from "./SubtreeInfo"; +import { AvailabilityInfos } from "./AvailabilityInfos"; +import { ImplicitTilingError } from "./ImplicitTilingError"; + +import { Subtree } from "../structure/Subtree"; +import { TileImplicitTiling } from "../structure/TileImplicitTiling"; + +import { BinaryBufferDataResolver } from "../binary/BinaryBufferDataResolver"; +import { BinaryBufferStructure } from "../binary/BinaryBufferStructure"; +import { BinaryDataError } from "../binary/BinaryDataError"; + +/** + * Methods to create `SubtreeInfo` instances. + */ +export class SubtreeInfos { + /** + * Creates a new `SubtreeInfo` from the given binary subtree data. + * + * This method assumes that the given binary data is consistent + * and valid. This can be checked with the `SubtreeValidator` + * class. + * + * @param input - The whole buffer of a binary subtree file + * @param implicitTiling - The `TileImplicitTiling` that + * defines the expected structure of the subtree data + * @param resourceResolver - The `ResourceResolver` that + * will be used to resolve buffer URIs + * @returns A promise with the `SubtreeInfo` + * @throws An ImplicitTilingError when the subtree JSON could + * not be parsed, or there was a buffer without a URI + * and no binary buffer was given, or one of the requested + * buffers could not be resolved. + */ + static async createFromBuffer( + input: Buffer, + implicitTiling: TileImplicitTiling, + resourceResolver: ResourceResolver + ): Promise { + const headerByteLength = 24; + const jsonByteLength = input.readBigUint64LE(8); + const binaryByteLength = input.readBigUint64LE(16); + + // Extract the JSON data + const jsonStartByteOffset = headerByteLength; + const jsonEndByteOffset = jsonStartByteOffset + Number(jsonByteLength); + const jsonBuffer = input.subarray(jsonStartByteOffset, jsonEndByteOffset); + let subtreeJson: any; + let subtree: Subtree; + try { + subtreeJson = Buffers.getJson(jsonBuffer); + subtree = subtreeJson; + } catch (error) { + throw new ImplicitTilingError("Could not parse subtree JSON data"); + } + + // Extract the binary buffer + const binaryStartByteOffset = jsonEndByteOffset; + const binaryEndByteOffset = + binaryStartByteOffset + Number(binaryByteLength); + const binaryBufferSlice = input.subarray( + binaryStartByteOffset, + binaryEndByteOffset + ); + const binaryBuffer = + binaryBufferSlice.length > 0 ? binaryBufferSlice : undefined; + + return SubtreeInfos.create( + subtree, + binaryBuffer, + implicitTiling, + resourceResolver + ); + } + + /** + * Creates a new `SubtreeInfo` from the given `Subtree` object + * and the (optional) binary buffer. + * + * This method assumes that the given data is consistent + * and valid. This can be checked with the `SubtreeValidator` + * class. + * + * @param subtree - The `Subtree` object + * @param binaryBuffer - The optional binary buffer + * @param implicitTiling - The `TileImplicitTiling` that + * defines the expected structure of the subtree data + * @param resourceResolver - The `ResourceResolver` that + * will be used to resolve buffer URIs + * @returns A promise with the `SubtreeInfo` + * @throws A ImplicitTilingError when there was a buffer without + * a URI and no binary buffer was given, or the requested buffer + * data could not be resolved. + */ + static async create( + subtree: Subtree, + binaryBuffer: Buffer | undefined, + implicitTiling: TileImplicitTiling, + resourceResolver: ResourceResolver + ): Promise { + if (!subtree.buffers) { + throw new ImplicitTilingError("The subtree did not define any buffers"); + } + if (!subtree.bufferViews) { + throw new ImplicitTilingError( + "The subtree did not define any buffer views" + ); + } + const binaryBufferStructure: BinaryBufferStructure = { + buffers: subtree.buffers, + bufferViews: subtree.bufferViews, + }; + let binaryBufferData; + try { + binaryBufferData = await BinaryBufferDataResolver.resolve( + binaryBufferStructure, + binaryBuffer, + resourceResolver + ); + } catch (error) { + if (error instanceof BinaryDataError) { + const message = `Could not read subtree data: ${error.message}`; + throw new ImplicitTilingError(message); + } + throw error; + } + const bufferViewsData = binaryBufferData.bufferViewsData; + + // Create the `AvailabilityInfo` for the tile availability + const tileAvailability = subtree.tileAvailability; + const tileAvailabilityInfo = AvailabilityInfos.createTileOrContent( + tileAvailability, + bufferViewsData, + implicitTiling + ); + + // Create the `AvailabilityInfo` objects, one for + // each content availability + const contentAvailabilityInfos = []; + const contentAvailabilities = subtree.contentAvailability; + if (contentAvailabilities) { + for (const contentAvailability of contentAvailabilities) { + const contentAvailabilityInfo = AvailabilityInfos.createTileOrContent( + contentAvailability, + bufferViewsData, + implicitTiling + ); + contentAvailabilityInfos.push(contentAvailabilityInfo); + } + } + + // Create the `AvailabilityInfo` for the child subtree availability + const childSubtreeAvailability = subtree.childSubtreeAvailability; + const childSubtreeAvailabilityInfo = AvailabilityInfos.createChildSubtree( + childSubtreeAvailability, + bufferViewsData, + implicitTiling + ); + + const result = new SubtreeInfo( + tileAvailabilityInfo, + contentAvailabilityInfos, + childSubtreeAvailabilityInfo + ); + return result; + } +} diff --git a/src/implicitTiling/TemplateUris.ts b/src/implicitTiling/TemplateUris.ts new file mode 100644 index 00000000..1542217f --- /dev/null +++ b/src/implicitTiling/TemplateUris.ts @@ -0,0 +1,98 @@ +import { OctreeCoordinates } from "./OctreeCoordinates"; +import { QuadtreeCoordinates } from "./QuadtreeCoordinates"; + +/** + * Method related to template URIs for implicit tiling. + */ +export class TemplateUris { + /** + * Substitute all appearances of \{level\}, \{x\}, and \{y\} in + * the given string with the respective value from the given + * coordinates. + * + * @param templateUri - The template URI string + * @param coordinates - The coordinates + * @returns The string with the substitutions applied + */ + static substituteQuadtree( + templateUri: string, + coordinates: QuadtreeCoordinates + ) { + return TemplateUris.substituteQuadtreeInternal( + templateUri, + coordinates.level, + coordinates.x, + coordinates.y + ); + } + + /** + * Resolves each appearance of \{level\}, \{x\}, + * and \{y\} in the given template string with the + * respective parameters. + * + * @param templateUri - The template URI + * @param level - The level + * @param x - The x-coordinate + * @param y - The y-coordinate + * @returns The result + */ + static substituteQuadtreeInternal( + templateUri: string, + level: number, + x: number, + y: number + ) { + let result = templateUri; + result = result.replace(/{level}/g, `${level}`); + result = result.replace(/{x}/g, `${x}`); + result = result.replace(/{y}/g, `${y}`); + return result; + } + + /** + * Substitute all appearances of \{level\}, \{x\}, \{y\}, and \{z\} in + * the given string with the respective value from the given + * coordinates. + * + * @param templateUri - The template URI string + * @param coordinates - The coordinates + * @returns The string with the substitutions applied + */ + static substituteOctree(templateUri: string, coordinates: OctreeCoordinates) { + return TemplateUris.substituteOctreeInternal( + templateUri, + coordinates.level, + coordinates.x, + coordinates.y, + coordinates.z + ); + } + + /** + * Resolves each appearance of \{level\}, \{x\}, + * \{y\}, and \{z\} in the given template string + * with the respective parameters. + * + * @param templateUri - The template URI + * @param level - The level + * @param x - The x-coordinate + * @param y - The y-coordinate + * @param z - The z-coordinate + * @returns The result + */ + static substituteOctreeInternal( + templateUri: string, + level: number, + x: number, + y: number, + z: number + ) { + let result = templateUri; + result = result.replace(/{level}/g, `${level}`); + result = result.replace(/{x}/g, `${x}`); + result = result.replace(/{y}/g, `${y}`); + result = result.replace(/{z}/g, `${z}`); + return result; + } +} diff --git a/src/implicitTiling/TreeCoordinates.ts b/src/implicitTiling/TreeCoordinates.ts new file mode 100644 index 00000000..903eb83e --- /dev/null +++ b/src/implicitTiling/TreeCoordinates.ts @@ -0,0 +1,78 @@ +/** + * An interface for coordinates within a tree structure. + */ +export interface TreeCoordinates { + /** + * Returns the level of the coordinates, with 0 being + * the root node. + * + * @returns The level + */ + get level(): number; + + /** + * Returns the parent coordinates of these coordinates, + * or `null` if this is the root. + * + * @returns The parent coordinates + */ + parent(): TreeCoordinates | null; + + /** + * Returns a generator for the child coordinates of these coordinates + * + * @returns The child coordinates + */ + children(): IterableIterator; + + /** + * Returns a generator for all coordinates that are descendants + * of these coordinates, up to the given level, **inclusive!**. + * + * @param maxLevelInclusive - The maximum level, **inclusive** + * @param depthFirst - Whether the traversal should be depth first + * @returns The child coordinates + */ + descendants( + maxLevelInclusive: number, + depthFirst: boolean + ): IterableIterator; + + /** + * Preliminary: + * + * Returns these coordinates as an array. This returns the + * level, x, and y coordinates (and z for octrees) in one + * array. + * + * @returns The coordinates as an array + */ + toArray(): number[]; + + /** + * Preliminary: + * + * Returns the index that corresponds to these coordinates. + * + * This returns the (stacked) Morton index. This could be considered + * as an implementation detail, but is frequently used in implicit + * tiling, and therefore, part of this interface. + * + * @returns The coordinates as an index + */ + toIndex(): number; + + /** + * Preliminary: + * + * Returns the index that corresponds to these coordinates, within + * their level. + * + * This returns the local Morton index. This could be considered + * as an implementation detail, but is frequently used in implicit + * tiling, and therefore, part of this interface. + * + * @returns The index of these coordinates within their level + */ + toIndexInLevel(): number; +} diff --git a/src/metadata/ArrayValues.ts b/src/metadata/ArrayValues.ts new file mode 100644 index 00000000..f5737de9 --- /dev/null +++ b/src/metadata/ArrayValues.ts @@ -0,0 +1,275 @@ +import { defined } from "../base/defined"; +import { defaultValue } from "../base/defaultValue"; + +/** + * Utility functions for generic operations on values that + * may be numbers or bigints or (potentially multi-dimensional) + * arrays of numbers or bigints. + * + * These methods are mainly used for performing operations + * on metadata values that have been found to be numeric + * values (i.e. SCALAR values, VECn or MATn values, or + * arrays thereof) + * + * When two values are involved, then the methods assume + * that the values have the same structure, i.e. they are + * both numeric, or arrays with the same length. + */ +export class ArrayValues { + // Implementation note: + // The way how these functions are written is drilling a hole + // into the type checks that COULD be offered by TypeScript. + // However, defining these methods in a type-safe way is fairly + // non-trivial, and (more importantly) would require the proper + // type checks to be done by the caller. + + /** + * Multiplies the given input value with the given factor. + * + * If the factor is undefined, then the original value + * is returned. + * + * This considers the case that the values are numbers or + * (potentially multi-dimensional) arrays of numbers. + * + * @param value - The input value + * @param factor - The optional factor + * @returns The resulting value + */ + static deepMultiply(value: any, factor: any): any { + if (!defined(factor)) { + return value; + } + const f = factor as any; + if (!Array.isArray(value)) { + return value * f; + } + for (let i = 0; i < value.length; i++) { + value[i] = ArrayValues.deepMultiply(value[i], f[i]); + } + return value; + } + + /** + * Adds the given addend to the given input value. + * + * If the addend is undefined, then the original value + * is returned. + * + * This considers the case that the values are numbers or + * (potentially multi-dimensional) arrays of numbers. + * + * @param value - The input value + * @param addend - The optional addend + * @returns The resulting value + */ + static deepAdd(value: any, addend: any): any { + if (!defined(addend)) { + return value; + } + if (!Array.isArray(value)) { + return value + addend; + } + const a = addend as any; + for (let i = 0; i < value.length; i++) { + value[i] = ArrayValues.deepAdd(value[i], a[i]); + } + return value; + } + + /** + * Computes the minimum of the given values. + * + * This considers the case that the values are numbers or + * (potentially multi-dimensional) arrays of numbers, + * and computes the component-wise minimum. + * + * @param a - The first value + * @param b - THe second value + * @returns The mimimum value + */ + static deepMin(a: any, b: any): any { + if (Array.isArray(a) && Array.isArray(b)) { + const result = a.slice(); + for (let i = 0; i < a.length; ++i) { + result[i] = ArrayValues.deepMin(a[i], b[i]); + } + return result; + } + return Math.min(a, b); + } + + /** + * Computes the maximum of the given values. + * + * This considers the case that the values are numbers or + * (potentially multi-dimensional) arrays of numbers, + * and computes the component-wise maximum. + * + * @param a - The first value + * @param b - THe second value + * @returns The maximum value + */ + static deepMax(a: any, b: any): any { + if (Array.isArray(a) && Array.isArray(b)) { + const result = a.slice(); + for (let i = 0; i < a.length; ++i) { + result[i] = ArrayValues.deepMax(a[i], b[i]); + } + return result; + } + return Math.max(a, b); + } + + /** + * Returns whether the given values are equal up to the + * give relative epsilon. + * + * This considers the case that the values are numbers or + * (potentially multi-dimensional) arrays of numbers. + * + * @param a - The first element + * @param b - The second element + * @param epsilon - A relative epsilon + * @returns Whether the objects are equal + */ + static deepEqualsEpsilon(a: any, b: any, epsilon: number): boolean { + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) { + return false; + } + for (let i = 0; i < a.length; ++i) { + if (!ArrayValues.deepEqualsEpsilon(a[i], b[i], epsilon)) { + return false; + } + } + return true; + } + return ArrayValues.equalsEpsilon(a, b, epsilon); + } + + /** + * From CesiumJS: + * + * Returns whether two numbers are equal, up to a certain epsilon + * + * @param left - The first value + * @param right - The second value + * @param relativeEpsilon - The maximum inclusive delta for the relative tolerance test. + * @param absoluteEpsilon - The maximum inclusive delta for the absolute tolerance test. + * @returns Whether the values are equal within the epsilon + */ + private static equalsEpsilon( + left: number, + right: number, + relativeEpsilon: number, + absoluteEpsilon?: number + ) { + relativeEpsilon = defaultValue(relativeEpsilon, 0.0); + absoluteEpsilon = defaultValue(absoluteEpsilon, relativeEpsilon); + const absDiff = Math.abs(left - right); + return ( + absDiff <= absoluteEpsilon || + absDiff <= relativeEpsilon * Math.max(Math.abs(left), Math.abs(right)) + ); + } + + /** + * Checks whether two values are equal. + * + * This considers the case that the values are numbers or + * (potentially multi-dimensional) arrays of numbers. + * + * @param a - The first value + * @param b - The second value + * @returns Whether the values are equal + */ + static deepEquals(a: any, b: any) { + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (!ArrayValues.deepEquals(a[i], b[i])) { + return false; + } + } + return true; + } + return a === b; + } + + /** + * Returns a deep clone of the given value. + * + * This considers the case that the values are (potentially + * multi-dimensional) arrays. Non-array values (including + * objects!) will be returned directly. + * + * @param value - The input value + * @returns The result value + */ + static deepClone(value: any) { + if (!Array.isArray(value)) { + return value; + } + const result = value.slice(); + for (let i = 0; i < value.length; i++) { + result[i] = ArrayValues.deepClone(value[i]); + } + return result; + } + + /** + * Returns whether one value is less than another. + * + * This considers the case that the values are numbers or + * (potentially multi-dimensional) arrays of numbers. + * + * It returns whether the first number is smaller than + * the second number. For arrays, it recursively checks + * whether ANY element of the first array is smaller + * than the corresponding element of the secon array. + * + * @param a - The first value + * @param b - The second value + * @returns Whether the first value is less than the second + */ + static anyDeepLessThan(a: any, b: any): boolean { + if (Array.isArray(a) && Array.isArray(b)) { + for (let i = 0; i < a.length; ++i) { + if (ArrayValues.anyDeepLessThan(a[i], b[i])) { + return true; + } + } + return false; + } + return a < b; + } + /** + * Returns whether one value is greater than another. + * + * This considers the case that the values are numbers or + * (potentially multi-dimensional) arrays of numbers. + * + * It returns whether the first number is greater than + * the second number. For arrays, it recursively checks + * whether ANY element of the first array is greater + * than the corresponding element of the secon array. + * + * @param a - The first value + * @param b - The second value + * @returns Whether the first value is greater than the second + */ + static anyDeepGreaterThan(a: any, b: any): boolean { + if (Array.isArray(a) && Array.isArray(b)) { + for (let i = 0; i < a.length; ++i) { + if (ArrayValues.anyDeepGreaterThan(a[i], b[i])) { + return true; + } + } + return false; + } + return a > b; + } +} diff --git a/src/metadata/DefaultMetadataEntityModel.ts b/src/metadata/DefaultMetadataEntityModel.ts new file mode 100644 index 00000000..545019a8 --- /dev/null +++ b/src/metadata/DefaultMetadataEntityModel.ts @@ -0,0 +1,55 @@ +import { defined } from "../base/defined"; + +import { MetadataEntityModel } from "./MetadataEntityModel"; +import { MetadataValues } from "./MetadataValues"; +import { MetadataError } from "./MetadataError"; + +import { MetadataClass } from "../structure/Metadata/MetadataClass"; + +/** + * Default implementation of a `MetadataEntityModel` that is backed + * by the JSON representation of the metadata. + * + * (The JSON representation are just the `metadataEntity.properties` + * from the input JSON) + * + * @internal + */ +export class DefaultMetadataEntityModel implements MetadataEntityModel { + private readonly _metadataClass: MetadataClass; + private readonly _json: any; + private readonly _semanticToPropertyId: { [key: string]: string }; + + constructor( + metadataClass: MetadataClass, + semanticToPropertyId: { [key: string]: string }, + json: any + ) { + this._metadataClass = metadataClass; + this._semanticToPropertyId = semanticToPropertyId; + this._json = json; + } + + getPropertyValue(propertyId: string): any { + const properties = this._metadataClass.properties; + if (!properties) { + throw new MetadataError(`Metadata class does not have any properties`); + } + const property = properties[propertyId]; + if (!property) { + throw new MetadataError( + `Metadata class does not have property ${propertyId}` + ); + } + const value = this._json[propertyId]; + return MetadataValues.processValue(property, undefined, undefined, value); + } + + getPropertyValueBySemantic(semantic: string): any { + const propertyId = this._semanticToPropertyId[semantic]; + if (!defined(propertyId)) { + return undefined; + } + return this.getPropertyValue(propertyId); + } +} diff --git a/src/metadata/MetadataComponentTypes.ts b/src/metadata/MetadataComponentTypes.ts new file mode 100644 index 00000000..5aa668dc --- /dev/null +++ b/src/metadata/MetadataComponentTypes.ts @@ -0,0 +1,157 @@ +import { defined } from "../base/defined"; +import { MetadataError } from "./MetadataError"; + +/** + * Internal utilities related to the `componentType` of the + * `ClassProperty` instances of `MetadataClass` objects + * + * @internal + */ +export class MetadataComponentTypes { + /** + * The valid values for the `class.property.componentType` property + */ + static allComponentTypes: string[] = [ + "INT8", + "UINT8", + "INT16", + "UINT16", + "INT32", + "UINT32", + "INT64", + "UINT64", + "FLOAT32", + "FLOAT64", + ]; + + /** + * Integer component types. + * These are the types for which a property can be `normalized`, + * and the valid values for the `enum.valueType` property + */ + static integerComponentTypes: string[] = [ + "INT8", + "UINT8", + "INT16", + "UINT16", + "INT32", + "UINT32", + "INT64", + "UINT64", + ]; + + /** + * Unsigned component types. + */ + static unsignedComponentTypes: string[] = [ + "UINT8", + "UINT16", + "UINT32", + "UINT64", + ]; + + /** + * Returns whether the given component type is an integer component + * type. + * + * @param componentType - The component type + * @returns Whether the component type is an integer component type + */ + static isIntegerComponentType(componentType: string | undefined) { + if (!defined(componentType)) { + return false; + } + return MetadataComponentTypes.integerComponentTypes.includes(componentType); + } + + /** + * Returns whether the given component type is an unsigned component + * type. + * + * @param componentType - The component type + * @returns Whether the component type is an unsigned component type + */ + static isUnsignedComponentType(componentType: string | undefined) { + if (!defined(componentType)) { + return false; + } + return MetadataComponentTypes.unsignedComponentTypes.includes( + componentType + ); + } + + /** + * Returns the size of the given component type in bytes + * + * @param componentType - The type + * @returns The size in bytes + * @throws MetadataError If the given component type is not + * one of the `allComponentTypes` + */ + static byteSizeForComponentType(componentType: string): number { + switch (componentType) { + case "INT8": + return 1; + case "UINT8": + return 1; + case "INT16": + return 2; + case "UINT16": + return 2; + case "INT32": + return 4; + case "UINT32": + return 4; + case "INT64": + return 8; + case "UINT64": + return 8; + case "FLOAT32": + return 4; + case "FLOAT64": + return 8; + } + throw new MetadataError(`Invalid component type: ${componentType}`); + } + + // Partially adapted from CesiumJS + static normalize(value: number, componentType: string | undefined): number { + if (MetadataComponentTypes.isIntegerComponentType(componentType)) { + return Math.max( + Number(value) / + Number(MetadataComponentTypes.maximumValue(componentType)), + -1.0 + ); + } + return value; + } + + // Partially adapted from CesiumJS + private static maximumValue( + componentType: string | undefined + ): number | bigint | undefined { + switch (componentType) { + case "INT8": + return 127; + case "UINT8": + return 255; + case "INT16": + return 32767; + case "UINT16": + return 65535; + case "INT32": + return 2147483647; + case "UINT32": + return 4294967295; + case "INT64": + return BigInt("9223372036854775807"); + case "UINT64": + return BigInt("18446744073709551615"); + case "FLOAT32": + return 340282346638528859811704183484516925440.0; + case "FLOAT64": + return Number.MAX_VALUE; + } + throw new MetadataError(`Invalid component type: ${componentType}`); + } +} diff --git a/src/metadata/MetadataEntityModel.ts b/src/metadata/MetadataEntityModel.ts new file mode 100644 index 00000000..4cf9d86d --- /dev/null +++ b/src/metadata/MetadataEntityModel.ts @@ -0,0 +1,38 @@ +/** + * A minimalistic interface for a model that describes a + * metadata entity, in the context of the 3D Metadata + * specification. + * + * @internal + */ +export interface MetadataEntityModel { + /** + * Obtains the value of the metadata property with the given name/ID. + * + * This will return the final, actual value of the property, based + * on the input data and the type definition of the respective + * property. This includes normalization, offset and scale for + * the type, as well as the handling of possible default values. + * + * @param propertyId - The name/ID of the property + * @returns The property value + * @throws MetadataError If the schema class that this entity + * is an instance of does not define a property with the given + * name/ID. + */ + getPropertyValue(propertyId: string): any; + + /** + * Obtains the value of the metadata property with the given semantic. + * + * This return the result of calling `getPropertyValue` with the + * name/ID of the property that has the given semantic, or + * `undefined` if there is no property with this semantic. + * + * @param semantic - The semantic + * @throws MetadataError If the schema class that this entity + * is an instance of does not define a property with the + * resulting name/ID. + */ + getPropertyValueBySemantic(semantic: string): any; +} diff --git a/src/metadata/MetadataEntityModels.ts b/src/metadata/MetadataEntityModels.ts new file mode 100644 index 00000000..8554302a --- /dev/null +++ b/src/metadata/MetadataEntityModels.ts @@ -0,0 +1,103 @@ +import { defined } from "../base/defined"; + +import { DefaultMetadataEntityModel } from "./DefaultMetadataEntityModel"; +import { MetadataEntityModel } from "./MetadataEntityModel"; +import { MetadataError } from "./MetadataError"; + +import { Schema } from "../structure/Metadata/Schema"; +import { MetadataEntity } from "../structure/MetadataEntity"; +import { MetadataClass } from "../structure/Metadata/MetadataClass"; + +/** + * Methods related to `MetadataEntityModel` instances. + * + * @internal + */ +export class MetadataEntityModels { + /** + * Creates a new `MetadataEntityModel` from the given schema and + * metadata entity. + * + * This receives the raw `Schema`, and the `MetadataEntity` that + * may, for example, have been found in the JSON input as the + * `tileset.metadata`. It will create an instance of the model + * class that can be used to access the property values. + * + * This assumes that the schema and entity have already been + * ensured to be structurally valid. This means that the class + * of the entity must appear in the schema, and the values + * that are stored in the entity have a structure that matches + * the required structure according to the type of the respective + * metadata class properties. + * + * @param schema - The `Schema` + * @param entity - The `MetadataEntity` + * @returns The `MetadataEntityModel` + * @throws MetadataError If the metadata entity refers to a class + * that is not found in the given schema. + */ + static create(schema: Schema, entity: MetadataEntity): MetadataEntityModel { + const classes = schema.classes; + if (!classes) { + throw new MetadataError( + `Schema does not define any classes, ` + + `but expected ${entity.class} to be defined` + ); + } + const metadataClass = classes[entity.class]; + if (!metadataClass) { + throw new MetadataError(`Schema does not contain class ${entity.class}`); + } + const entityProperties = entity.properties ?? {}; + return this.createFromClass(metadataClass, entityProperties); + } + + /** + * Creates a new `MetadataEntityModel` from the given schema class and + * metadata entity properties. + * + * See the `create` method for details. + * + * @param metadataClass - The `MetadataClass` + * @param entityProperties - The properties of the `MetadataEntity` + * @returns The `MetadataEntityModel` + */ + static createFromClass( + metadataClass: MetadataClass, + entityProperties: { [key: string]: any } + ) { + // TODO This should not be done for each entity. The lookup + // should be computed once, and associated with the class. + const semanticToPropertyId = + MetadataEntityModels.computeSemanticToPropertyIdMapping(metadataClass); + const metadataEntityModel = new DefaultMetadataEntityModel( + metadataClass, + semanticToPropertyId, + entityProperties + ); + return metadataEntityModel; + } + + /** + * Compute the mapping from 'semantic' strings to property IDs + * that have the respective semantic in the given metadata class. + * + * @param metadataClass - The `MetadataClass` + * @returns The mapping + */ + static computeSemanticToPropertyIdMapping(metadataClass: MetadataClass): { + [key: string]: string; + } { + const semanticToPropertyId: { [key: string]: string } = {}; + const classProperties = metadataClass.properties; + if (classProperties) { + for (const classPropertyId of Object.keys(classProperties)) { + const property = classProperties[classPropertyId]; + if (defined(property.semantic)) { + semanticToPropertyId[property.semantic] = classPropertyId; + } + } + } + return semanticToPropertyId; + } +} diff --git a/src/metadata/MetadataError.ts b/src/metadata/MetadataError.ts new file mode 100644 index 00000000..4c76f990 --- /dev/null +++ b/src/metadata/MetadataError.ts @@ -0,0 +1,18 @@ +/** + * An error that indicates that metadata was structurally invalid. + * + * This may be thrown by methods that create the convenience classes + * for this package, when the given inputs are not valid. + */ +export class MetadataError extends Error { + constructor(message: string) { + super(message); + // See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes + // #extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, MetadataError.prototype); + } + + override toString = (): string => { + return `${this.name}: ${this.message}`; + }; +} diff --git a/src/metadata/MetadataTypes.ts b/src/metadata/MetadataTypes.ts new file mode 100644 index 00000000..44ef0002 --- /dev/null +++ b/src/metadata/MetadataTypes.ts @@ -0,0 +1,82 @@ +import { MetadataError } from "./MetadataError"; + +/** + * Internal utilities related to the `type` of the + * `ClassProperty` instances of `MetadataClass` objects + * + * @internal + */ +export class MetadataTypes { + /** + * The valid values for the `class.property.type` property + */ + static allTypes: string[] = [ + "SCALAR", + "VEC2", + "VEC3", + "VEC4", + "MAT2", + "MAT3", + "MAT4", + "STRING", + "BOOLEAN", + "ENUM", + ]; + + /** + * The valid values for the `class.property.type` property + * that count as "numeric" types. These are the ones where + * a `class.property.componentType` is given + */ + static numericTypes: string[] = [ + "SCALAR", + "VEC2", + "VEC3", + "VEC4", + "MAT2", + "MAT3", + "MAT4", + ]; + + /** + * Returns whether the given type is numeric, i.e. whether + * it is SCALAR, VECn, or MATn. + * + * @param type - The type + * @returns Whether the type is numeric + */ + static isNumericType(type: string) { + return MetadataTypes.numericTypes.includes(type); + } + + /** + * Returns the number of components for the given type + * + * @param type - The type + * @returns The number of components + * @throws MetadataError If the given string is not one + * of the types contained in `allTypes` + */ + static componentCountForType(type: string): number { + switch (type) { + case "SCALAR": + case "STRING": + case "ENUM": + case "BOOLEAN": + return 1; + case "VEC2": + return 2; + case "VEC3": + return 3; + case "VEC4": + return 4; + case "MAT2": + return 4; + case "MAT3": + return 9; + case "MAT4": + return 16; + } + throw new MetadataError(`Invalid type: ${type}`); + } +} diff --git a/src/metadata/MetadataUtilities.ts b/src/metadata/MetadataUtilities.ts new file mode 100644 index 00000000..a8e4a0d8 --- /dev/null +++ b/src/metadata/MetadataUtilities.ts @@ -0,0 +1,129 @@ +import { defined } from "../base/defined"; + +import { BinaryEnumInfo } from "./binary/BinaryEnumInfo"; + +import { Schema } from "../structure/Metadata/Schema"; +import { ClassProperty } from "../structure/Metadata/ClassProperty"; + +/** + * Internal utilities related to metadata + * + * @internal + */ +export class MetadataUtilities { + /** + * Computes the `BianaryEnumInfo` that summarizes information + * about the binary representation of `MetadataEnum` values + * from the given schema. + * + * @param schema - The metadata `Schema` + * @returns The `BinaryEnumInfo` + */ + static computeBinaryEnumInfo(schema: Schema): BinaryEnumInfo { + const binaryEnumInfo: BinaryEnumInfo = { + enumValueTypes: MetadataUtilities.computeEnumValueTypes(schema), + enumValueNameValues: MetadataUtilities.computeEnumValueNameValues(schema), + }; + return binaryEnumInfo; + } + + /** + * Computes a mapping from enum type names to the `valueType` that + * the respective `MetdataEnum` has (defaulting to `UINT16` if it + * did not define one) + * + * @param schema - The metadata `Schema` + * @returns The mapping from enum type names to enum value types + */ + private static computeEnumValueTypes(schema: Schema): { + [key: string]: string; + } { + const enumValueTypes: { [key: string]: string } = {}; + const enums = schema.enums; + if (enums) { + for (const enumName of Object.keys(enums)) { + const metadataEnum = enums[enumName]; + const valueType = metadataEnum.valueType ?? "UINT16"; + enumValueTypes[enumName] = valueType; + } + } + return enumValueTypes; + } + + /** + * Computes a mapping from enum type names to the dictionaries + * that map the enum values names to the enum value values. + * + * @param schema - The metadata `Schema` + * @returns The mapping from enum type names to dictionaries + */ + private static computeEnumValueNameValues(schema: Schema): { + [key: string]: { + [key: string]: number; + }; + } { + const enumValueNameValues: { + [key: string]: { + [key: string]: number; + }; + } = {}; + const enums = schema.enums; + if (enums) { + for (const enumName of Object.keys(enums)) { + const metadataEnum = enums[enumName]; + const nameValues: { + [key: string]: number; + } = {}; + for (let i = 0; i < metadataEnum.values.length; i++) { + const enumValue = metadataEnum.values[i]; + const value = enumValue.value; + const name = enumValue.name; + nameValues[name] = value; + } + enumValueNameValues[enumName] = nameValues; + } + } + return enumValueNameValues; + } + + /** + * Internal method to obtain the names of enum values for the + * given property. + * + * This tries to return the list of all + * `schema.enums[classProperty.enumType].values[i].name` + * values, returning the empty list if the property does not + * have an enum type or any element is not defined. + * + * @param classProperty - The `ClassProperty` + * @param schema - The `Schema` + * @returns The enum value names + */ + static obtainEnumValueNames( + classProperty: ClassProperty, + schema: Schema + ): string[] { + const type = classProperty.type; + if (type !== "ENUM") { + return []; + } + const enumType = classProperty.enumType; + if (!defined(enumType)) { + return []; + } + const enums = schema.enums; + if (!enums) { + return []; + } + const theEnum = enums[enumType]; + if (!defined(theEnum)) { + return []; + } + const enumValues = theEnum.values; + if (!enumValues) { + return []; + } + const enumValueNames = enumValues.map((e: { name: string }) => e.name); + return enumValueNames; + } +} diff --git a/src/metadata/MetadataValues.ts b/src/metadata/MetadataValues.ts new file mode 100644 index 00000000..4292c6ef --- /dev/null +++ b/src/metadata/MetadataValues.ts @@ -0,0 +1,106 @@ +import { defined } from "../base/defined"; + +import { ClassProperty } from "../structure/Metadata/ClassProperty"; + +import { MetadataComponentTypes } from "./MetadataComponentTypes"; +import { ArrayValues } from "./ArrayValues"; + +/** + * Internal methods related to metadata values. + * + * @internal + */ +export class MetadataValues { + /** + * Processes the given "raw" value that was obtained for a metadata + * property (e.g. from the JSON representation), and returns the + * processed value according to the type definition that is given + * by the given class property. + * + * If the type defines a `noData` value, and the given value + * is the `noData` value, then the `default` value of the type + * is returned. + * + * If the type defines the value to be `normalized`, then the + * normalization is applied to the given values. + * + * If the type defines an `offset`, then the offset is added + * to the value. + * + * If the type defines a `scale`, then this is multiplied + * with the value. + * + * @param classProperty - The `ClassProperty` + * @param offsetOverride -: An optional override for the + * `offset` of the `ClassProperty`. If this is defined, then + * it will be used instead of the one from the class property. + * @param scaleOverride -: An optional override for the + * `scale` of the `ClassProperty`. If this is defined, then + * it will be used instead of the one from the class property. + * @param value - The value + * @returns The processed value + */ + static processValue( + classProperty: ClassProperty, + offsetOverride: any, + scaleOverride: any, + value: any + ): any { + const noData = classProperty.noData; + const defaultValue = classProperty.default; + if (defined(noData)) { + if (ArrayValues.deepEquals(value, noData)) { + return ArrayValues.deepClone(defaultValue); + } + } + if (!defined(value)) { + return ArrayValues.deepClone(defaultValue); + } + value = ArrayValues.deepClone(value); + + if (classProperty.normalized === true) { + const componentType = classProperty.componentType; + value = MetadataValues.normalize(value, componentType); + } + const offset = defined(offsetOverride) + ? offsetOverride + : classProperty.offset; + const scale = defined(scaleOverride) ? scaleOverride : classProperty.scale; + value = MetadataValues.transform(value, offset, scale); + return value; + } + + /** + * Normalize the given input value, based on the given component type. + * + * If example, the value of `255` for `UINT8` will be normalized to `1.0`. + * + * @param value - The input value + * @param componentType - The component type + * @returns The normalized value + */ + private static normalize(value: any, componentType: string | undefined): any { + if (!Array.isArray(value)) { + return MetadataComponentTypes.normalize(value, componentType); + } + for (let i = 0; i < value.length; i++) { + value[i] = MetadataValues.normalize(value[i], componentType); + } + return value; + } + + /** + * Applies the given offset and scale to the given input value, if they + * are defined. + * + * @param value - The input value + * @param offset - The optional offset + * @param scale - The optional scale + * @returns The transformed value + */ + private static transform(value: any, offset: any, scale: any): any { + value = ArrayValues.deepMultiply(value, scale); + value = ArrayValues.deepAdd(value, offset); + return value; + } +} diff --git a/src/metadata/binary/ArrayBuffers.ts b/src/metadata/binary/ArrayBuffers.ts new file mode 100644 index 00000000..b87db371 --- /dev/null +++ b/src/metadata/binary/ArrayBuffers.ts @@ -0,0 +1,25 @@ +/** + * Utility methods for buffer handling + * + * @internal + */ +export class ArrayBuffers { + /** + * Returns an `ArrayBuffer` that corresponds to the given buffer. + * + * @param buffer - The `Buffer` + * @returns The `ArrayBuffer` + */ + static fromBuffer(buffer: Buffer): ArrayBuffer { + // NOTE: The `buffer.buffer` may be totally unrelated to + // the `buffer` itself, because the buffer might have been + // allocated with one of the "unsafe" methods that use + // the internal buffer pool. This obscure line makes sure + // that the result is an `ArrayBuffer` that actually + // has the same contents as the `buffer`.... + return buffer.buffer.slice( + buffer.byteOffset, + buffer.byteOffset + buffer.byteLength + ); + } +} diff --git a/src/metadata/binary/BinaryEnumInfo.ts b/src/metadata/binary/BinaryEnumInfo.ts new file mode 100644 index 00000000..727f7fe6 --- /dev/null +++ b/src/metadata/binary/BinaryEnumInfo.ts @@ -0,0 +1,24 @@ +/** + * A basic structure holding information about `MetadataEnum` + * instances that appear in a `Schema`. + * + * It summarizes the information that is required for reading + * and writing the metadata values in binary form. + * + * @internal + */ +export interface BinaryEnumInfo { + /** + * A mapping from enum type names (enum IDs) to the + * `metadataEnum.valueType` of the respective enum, + * defaulting to `UINT16` when it was not defined. + */ + enumValueTypes: { [key: string]: string }; + + /** + * A mapping from enum type names (enum IDs) to + * dictionaries that map the `enum.values[i].value` + * to the `enum.values[i].value` + */ + enumValueNameValues: { [key: string]: { [key: string]: number } }; +} diff --git a/src/metadata/binary/BinaryMetadataEntityModel.ts b/src/metadata/binary/BinaryMetadataEntityModel.ts new file mode 100644 index 00000000..97710fb5 --- /dev/null +++ b/src/metadata/binary/BinaryMetadataEntityModel.ts @@ -0,0 +1,69 @@ +import { defined } from "../../base/defined"; + +import { MetadataEntityModel } from "../MetadataEntityModel"; +import { MetadataValues } from "../MetadataValues"; +import { MetadataError } from "../MetadataError"; + +import { PropertyTableModel } from "./PropertyTableModel"; + +/** + * Implementation of a `MetadataEntityModel` that is backed by binary + * data (specifically, by a `PropertyTableModel`) + * + * @internal + */ +export class BinaryMetadataEntityModel implements MetadataEntityModel { + private readonly _propertyTableModel: PropertyTableModel; + private readonly _entityIndex: number; + private readonly _semanticToPropertyId: { [key: string]: string }; + + constructor( + propertyTableModel: PropertyTableModel, + entityIndex: number, + semanticToPropertyId: { [key: string]: string } + ) { + this._propertyTableModel = propertyTableModel; + this._entityIndex = entityIndex; + this._semanticToPropertyId = semanticToPropertyId; + } + + getPropertyValue(propertyId: string): any { + const propertyTableModel = this._propertyTableModel; + const classProperty = propertyTableModel.getClassProperty(propertyId); + if (!defined(classProperty)) { + const message = `The class does not define a property ${propertyId}`; + throw new MetadataError(message); + } + const propertyTableProperty = + propertyTableModel.getPropertyTableProperty(propertyId); + if (!defined(propertyTableProperty)) { + const message = `The property table does not define a property ${propertyId}`; + throw new MetadataError(message); + } + const propertyModel = propertyTableModel.getPropertyModel(propertyId); + if (!propertyModel) { + const message = + `The property table does not ` + + `define a property model for ${propertyId}`; + throw new MetadataError(message); + } + const value = propertyModel.getPropertyValue(this._entityIndex); + const offsetOverride = propertyTableProperty.offset; + const scaleOverride = propertyTableProperty.scale; + const processedValue = MetadataValues.processValue( + classProperty, + offsetOverride, + scaleOverride, + value + ); + return processedValue; + } + + getPropertyValueBySemantic(semantic: string): any { + const propertyId = this._semanticToPropertyId[semantic]; + if (!defined(propertyId)) { + return undefined; + } + return this.getPropertyValue(propertyId); + } +} diff --git a/src/metadata/binary/BinaryPropertyTable.ts b/src/metadata/binary/BinaryPropertyTable.ts new file mode 100644 index 00000000..5a536e93 --- /dev/null +++ b/src/metadata/binary/BinaryPropertyTable.ts @@ -0,0 +1,53 @@ +import { BinaryBufferData } from "../../binary/BinaryBufferData"; +import { BinaryBufferStructure } from "../../binary/BinaryBufferStructure"; + +import { BinaryEnumInfo } from "./BinaryEnumInfo"; + +import { MetadataClass } from "../../structure/Metadata/MetadataClass"; +import { PropertyTable } from "../../structure/PropertyTable"; + +/** + * A basic structure summarizing the (raw) elements of a binary + * property table. + * + * It contains information about the structure of the table itself, + * consisting of the `PropertyTable` and the `MetadataClass`, as + * well as the binary data, stored in `Buffer` objects. + * + * Instances of this interface serve as the input for the + * construction of a `PropertyTableModel`. + * + * @internal + */ +export interface BinaryPropertyTable { + /** + * The actual `PropertyTable` object + */ + propertyTable: PropertyTable; + + /** + * The `MetadataClass` that corresponds to the `propertyTable.class` + */ + metadataClass: MetadataClass; + + /** + * Information about the binary representation of `MetadataEnum` + * values + */ + binaryEnumInfo: BinaryEnumInfo; + + /** + * The binary buffer structure, containing the `BufferObject` and + * `BufferView` objects. The `PropertyTableProperty` objects + * from the `PropertyTable` contain indices (e.g. the `values` + * index) that refer to this structure. + */ + binaryBufferStructure: BinaryBufferStructure; + + /** + * The binary buffer data. These are the actual buffers with + * the binary data that correspond to the elements of the + * `BinaryBufferStructure`. + */ + binaryBufferData: BinaryBufferData; +} diff --git a/src/metadata/binary/BinaryPropertyTables.ts b/src/metadata/binary/BinaryPropertyTables.ts new file mode 100644 index 00000000..f4cc90db --- /dev/null +++ b/src/metadata/binary/BinaryPropertyTables.ts @@ -0,0 +1,551 @@ +import { defined } from "../../base/defined"; +import { defaultValue } from "../../base/defaultValue"; + +import { BinaryPropertyTable } from "./BinaryPropertyTable"; + +import { BinaryBufferData } from "../../binary/BinaryBufferData"; +import { BinaryBuffers } from "../../binary/BinaryBuffers"; + +import { MetadataUtilities } from "../MetadataUtilities"; +import { MetadataError } from "../MetadataError"; + +import { PropertyTable } from "../../structure/PropertyTable"; +import { PropertyTableProperty } from "../../structure/PropertyTableProperty"; + +import { Schema } from "../../structure/Metadata/Schema"; +import { MetadataClass } from "../../structure/Metadata/MetadataClass"; +import { ClassProperty } from "../../structure/Metadata/ClassProperty"; +import { EnumValue } from "../../structure/Metadata/EnumValue"; +import { MetadataEnum } from "../../structure/Metadata/MetadataEnum"; + +/** + * Methods to create `BinaryPropertyTable` instances from individual + * properties and their associated data. + * + * Right now, the methods in this class are mainly intended for + * generating test data. They can be used to create property + * tables based on single properties and their associated values. + * + * When a method expects such `values` to be passed in, then the + * structure is assumed to be a "JSON-representation" of the data + * that corresponds to one column of the table: + * + * - scalar properties are given as an array of values + * - properties of structured types (VECn or MATn) are given + * as an array of arrays + * - array properties are given as an array of the aforementioned + * inputs. + * + * For example: + * - a string property will be ["row0", "row1"] + * - a string array property will be + * [ ["row0-col0", "row0-col1"], + * ["row1-col0", "row1-col1"] ] + * - a VEC2 property will be [ [0,0], [1,1] ] + * - a VEC2 array property will be + * [ [ [0,0], [0,1], [0,2] ], + * [ [1,0], [1,1], [1,2] ] ] + * + * + * TODO Some methods in this class are creating plain JSON + * structures programmatically (e.g. a Schema that contains + * a single class with a single property). Some of these + * methods may be omitted in the future, if these structures + * have to be created manually for unit tests anyhow. + * + * @internal + */ +export class BinaryPropertyTables { + /** + * Creates a (dummy) `MetadataClass` that only contains the given property + * + * @param propertyName - The property name + * @param classProperty - The `ClassProperty` + * @returns The `MetadataClass` + */ + private static createMetadataClassFromClassProperty( + propertyName: string, + classProperty: ClassProperty + ): MetadataClass { + const classProperties: { [key: string]: ClassProperty } = {}; + classProperties[propertyName] = classProperty; + + const metadataClass: MetadataClass = { + name: "testMetadataClass", + properties: classProperties, + }; + return metadataClass; + } + + /** + * Creates a (dummy) `Schema` that only contains the given class + * + * @param className - The class name + * @param metadataClass - The `MetadataClass` + * @returns The metadata `Schema` + */ + private static createSchemaFromMetadataClass( + className: string, + metadataClass: MetadataClass + ): Schema { + const classes: { [key: string]: MetadataClass } = {}; + classes[className] = metadataClass; + const metadataSchema: Schema = { + id: "testMetadataMetadataSchemaId", + name: "testMetadataSchema", + classes: classes, + }; + return metadataSchema; + } + + /** + * Creates a `PropertyTable` from the given input. + * + * This creates a dummy `PropertyTable` with a single property, + * which is used for the other methods in this class that can + * create `BinaryPropertyTable` or `PropertyTableModel` objects + * from single properties. + * + * @param className - The class name + * @param propertyName - The property name + * @param count - The count (number of rows) of the table + * @param propertyTableProperty - The `PropertyTableProperty` + * @returns The `PropertyTable` + */ + private static createPropertyTableFromProperty( + className: string, + propertyName: string, + count: number, + propertyTableProperty: PropertyTableProperty + ): PropertyTable { + const propertyTableProperties: { [key: string]: PropertyTableProperty } = + {}; + propertyTableProperties[propertyName] = propertyTableProperty; + const propertyTable: PropertyTable = { + name: "testPropertyTable", + class: className, + count: count, + properties: propertyTableProperties, + }; + return propertyTable; + } + + /** + * Creates a `PropertyTableProperty` from the given inputs. + * + * This receives the `ClassProperty` itself and the associated values, + * and generates the `PropertyTableProperty` and its associated + * binary data. The binary data will include the buffer views for the + * `values`, `arrayOffsets`, and `stringOffsets`, which will be + * added to the given `bufferViewsData` array. + * + * @param classProperty - The `ClassProperty` + * @param schema - The metadata `Schema`. This is only used internally + * for looking up information about (binary) enum values, if the + * given property is an ENUM property. + * @param values - The values for the property + * @param arrayOffsetType - The `arrayOffsetType` for the property + * (only used when the property is a variable-length array, + * defaulting to `UINT32`) + * @param stringOffsetType - The `stringOffsetType` for the property + * (only used when the property is a STRING property, + * defaulting to `UINT32`)) + * @param bufferViewsData - The array that will receive the buffer + * view buffers + * @returns The `PropertyTableProperty` + */ + private static createPropertyTableProperty( + classProperty: ClassProperty, + schema: Schema, + values: any, + arrayOffsetType: string | undefined, + stringOffsetType: string | undefined, + bufferViewsData: Buffer[] + ): PropertyTableProperty { + const valuesBuffer = BinaryPropertyTables.createValuesBuffer( + classProperty, + schema, + values + ); + const valuesBufferView = bufferViewsData.length; + bufferViewsData.push(valuesBuffer); + + const propertyTableProperty: PropertyTableProperty = { + values: valuesBufferView, + offset: undefined, + scale: undefined, + max: undefined, + min: undefined, + }; + + const isVariableLengthArray = + classProperty.array && !defined(classProperty.count); + if (isVariableLengthArray) { + const arrayOffsetBuffer = BinaryPropertyTables.createArrayOffsetBuffer( + values, + arrayOffsetType + ); + const arrayOffsetBufferView = bufferViewsData.length; + bufferViewsData.push(arrayOffsetBuffer); + propertyTableProperty.arrayOffsets = arrayOffsetBufferView; + } + + if (classProperty.type === "STRING") { + const stringOffsetBuffer = BinaryPropertyTables.createStringOffsetBuffer( + values, + stringOffsetType + ); + const stringOffsetBufferView = bufferViewsData.length; + bufferViewsData.push(stringOffsetBuffer); + propertyTableProperty.stringOffsets = stringOffsetBufferView; + } + + return propertyTableProperty; + } + + /** + * Creates a `Schema` from the given input. + * + * This function is mainly intended for generating test data. + * It generates a "dummy" schema that only contains a class + * with the given property, and the given enum. + * + * @param propertyName - The property name + * @param classProperty - The `ClassProperty` + * @param metadataEnum - The optional `MetadataEnum` when the + * property is an enum property + * @returns The schema + */ + static createSchemaFromClassProperty( + propertyName: string, + classProperty: ClassProperty, + metadataEnum: MetadataEnum | undefined + ): Schema { + const className = "testMetadataClass"; + const metadataClass = + BinaryPropertyTables.createMetadataClassFromClassProperty( + propertyName, + classProperty + ); + const schema = BinaryPropertyTables.createSchemaFromMetadataClass( + className, + metadataClass + ); + if (metadataEnum) { + const enums: { [key: string]: MetadataEnum } = {}; + enums["testMetadataEnum"] = metadataEnum; + schema.enums = enums; + } + return schema; + } + + /** + * Creates a `BinaryPropertyTable` from the given input. + * + * This function is mainly intended for generating test data: + * It receives a predefined `ClassProperty` and associated + * values, and generates a ("dummy") class, schema, and + * property table for exactly this single property, together + * with the associated binary data. + * + * @param propertyName - The property name + * @param classProperty - The `ClassProperty` + * @param values - The property values + * @param arrayOffsetType - The `arrayOffsetType`, only used + * for variable-length array properties, defaulting to `UINT32` + * @param stringOffsetType - The `stringOffsetType`, only used + * for STRING properties, defaulting to `UINT32` + * @param metadataEnum - The optional `MetadataEnum` that defines + * the (numeric) values that are written into the binary data, + * based on the (string) values from the `values` parameter + * @returns The `BinaryPropertyTable` + */ + static createBinaryPropertyTableFromProperty( + propertyName: string, + classProperty: ClassProperty, + values: any, + arrayOffsetType: string | undefined, + stringOffsetType: string | undefined, + metadataEnum: MetadataEnum | undefined + ): BinaryPropertyTable { + const schema = BinaryPropertyTables.createSchemaFromClassProperty( + propertyName, + classProperty, + metadataEnum + ); + const className = "testMetadataClass"; + const binaryPropertyTable = BinaryPropertyTables.createBinaryPropertyTable( + schema, + className, + propertyName, + values, + arrayOffsetType, + stringOffsetType + ); + return binaryPropertyTable; + } + + /** + * Creates a `BinaryPropertyTable` from the given input. + * + * This function is mainly intended for generating test data. + * It receives information about the property (via the `className` + * and the `propertyName`, referring to the given schema), and the + * values for the property, and generates a property table for + * exactly this single property, together with the associated + * binary data. + * + * @param schema - The `Schema` + * @param className - The class name + * @param propertyName - The property name + * @param values - The property values + * @param arrayOffsetType - The `arrayOffsetType`, only used + * for variable-length array properties, defaulting to `UINT32` + * @param stringOffsetType - The `stringOffsetType`, only used + * for STRING properties, defaulting to `UINT32` + * @returns The `BinaryPropertyTable` + * @throws MetadataError If the input is not structurally valid + */ + static createBinaryPropertyTable( + schema: Schema, + className: string, + propertyName: string, + values: any, + arrayOffsetType: string | undefined, + stringOffsetType: string | undefined + ) { + const classes = schema.classes; + if (!classes) { + throw new MetadataError(`The schema does not define any classes`); + } + const metadataClass = classes[className]; + if (!metadataClass) { + throw new MetadataError( + `The schema does not define the class ${className}` + ); + } + const classProperties = metadataClass.properties; + if (!classProperties) { + throw new MetadataError( + `The schema class ${className} does not define any properties` + ); + } + const classProperty = classProperties[propertyName]; + if (!classProperty) { + throw new MetadataError( + `The schema class ${className} does not define property ${propertyName}` + ); + } + + const createdBufferViewsData: Buffer[] = []; + const propertyTableProperty = + BinaryPropertyTables.createPropertyTableProperty( + classProperty, + schema, + values, + arrayOffsetType, + stringOffsetType, + createdBufferViewsData + ); + const count = values.length; + const propertyTable = BinaryPropertyTables.createPropertyTableFromProperty( + className, + propertyName, + count, + propertyTableProperty + ); + + const binaryBufferData: BinaryBufferData = { + bufferViewsData: [], + buffersData: [], + }; + + const binaryBufferStructure = BinaryBuffers.createBinaryBufferStructure( + binaryBufferData, + createdBufferViewsData + ); + + const binaryEnumInfo = MetadataUtilities.computeBinaryEnumInfo(schema); + + const binaryPropertyTable: BinaryPropertyTable = { + metadataClass: metadataClass, + propertyTable: propertyTable, + binaryEnumInfo: binaryEnumInfo, + binaryBufferStructure: binaryBufferStructure, + binaryBufferData: binaryBufferData, + }; + return binaryPropertyTable; + } + + // Parts of the following are ""ported""" from the CesiumJS 'MetadataTester' class at + // https://github.com/CesiumGS/cesium/blob/b4097de3b8d3d007ed38b3b6fb83717ab6de43ba/Specs/MetadataTester.js + // A rewrite would have been taken less time and resulted in cleaner code, + // but it should do what it is supposed to do for now... + + private static toBuffer(arrayBuffer: ArrayBuffer): Buffer { + const buffer = Buffer.from(arrayBuffer); + return buffer; + } + + private static createBuffer( + values: any, + componentType: string | undefined + ): Buffer { + const buffer = BinaryPropertyTables.toBuffer( + BinaryPropertyTables.createBufferInternal(values, componentType) + ); + return buffer; + } + + private static createBufferInternal( + values: any, + componentType: string | undefined + ): ArrayBuffer { + const flatValues = BinaryPropertyTables.flattenFully(values); + switch (componentType) { + case "INT8": + return new Int8Array(flatValues).buffer; + case "UINT8": + return new Uint8Array(flatValues).buffer; + case "INT16": + return new Int16Array(flatValues).buffer; + case "UINT16": + return new Uint16Array(flatValues).buffer; + case "INT32": + return new Int32Array(flatValues).buffer; + case "UINT32": + return new Uint32Array(flatValues).buffer; + case "INT64": + return new BigInt64Array(flatValues.map((v: any) => BigInt(v))).buffer; + case "UINT64": + return new BigUint64Array(flatValues.map((v: any) => BigInt(v))).buffer; + case "FLOAT32": + return new Float32Array(flatValues).buffer; + case "FLOAT64": + return new Float64Array(flatValues).buffer; + } + throw new MetadataError(`${componentType} is not a valid component type`); + } + + private static createStringBuffer(values: any): Buffer { + return BinaryPropertyTables.toBuffer( + BinaryPropertyTables.createStringBufferInternal(values) + ); + } + + private static createStringBufferInternal(inputValues: any): Uint8Array { + const values = BinaryPropertyTables.flattenFully(inputValues); + const encoder = new TextEncoder(); + return encoder.encode(values.join("")); + } + + private static createBooleanBuffer(values: any): Buffer { + return BinaryPropertyTables.toBuffer( + BinaryPropertyTables.createBooleanBufferInternal(values) + ); + } + + private static createBooleanBufferInternal(inputValues: any): Uint8Array { + const values = BinaryPropertyTables.flattenFully(inputValues); + const length = Math.ceil(values.length / 8); + const typedArray = new Uint8Array(length); // Initialized as 0's + for (let i = 0; i < values.length; ++i) { + const byteIndex = i >> 3; + const bitIndex = i % 8; + if (values[i]) { + typedArray[byteIndex] |= 1 << bitIndex; + } + } + return typedArray; + } + + private static flatten(values: any): any { + return [...values]; + } + + private static flattenFully(values: any): any { + let result = values; + if (Array.isArray(result)) { + result = []; + for (let i = 0; i < values.length; i++) { + result = result.concat(BinaryPropertyTables.flattenFully(values[i])); + } + } + return result; + } + + private static createValuesBuffer( + classProperty: ClassProperty, + schema: Schema, + values: any + ): Buffer { + const type = classProperty.type; + let componentType = classProperty.componentType; + const enumType = classProperty.enumType; + let flattenedValues = BinaryPropertyTables.flatten(values); + + if (type === "STRING") { + return BinaryPropertyTables.createStringBuffer(flattenedValues); + } + + if (type === "BOOLEAN") { + return BinaryPropertyTables.createBooleanBuffer(flattenedValues); + } + + if (defined(enumType)) { + flattenedValues = BinaryPropertyTables.flattenFully(flattenedValues); + const length = flattenedValues.length; + const metadataEnums = schema.enums; + if (!metadataEnums) { + throw new MetadataError(`The schema does not define any enums`); + } + const metadataEnum = metadataEnums[enumType]; + if (!metadataEnum) { + throw new MetadataError(`The schema does not define enum ${enumType}`); + } + const valueNames = metadataEnum.values.map((v: EnumValue) => v.name); + componentType = defaultValue(metadataEnum.valueType, "UINT16"); + for (let i = 0; i < length; ++i) { + const valueName = flattenedValues[i]; + const index = valueNames.indexOf(valueName); + flattenedValues[i] = index; + } + } + + return BinaryPropertyTables.createBuffer(flattenedValues, componentType); + } + + private static createStringOffsetBuffer( + values: any, + offsetType: string | undefined + ) { + const encoder = new TextEncoder(); + const strings = BinaryPropertyTables.flattenFully(values); + const length = strings.length; + const offsets = new Array(length + 1); + let offset = 0; + for (let i = 0; i < length; ++i) { + offsets[i] = offset; + offset += encoder.encode(strings[i]).length; + } + offsets[length] = offset; + offsetType = defaultValue(offsetType, "UINT32"); + return BinaryPropertyTables.createBuffer(offsets, offsetType); + } + + private static createArrayOffsetBuffer( + values: any, + offsetType: string | undefined + ) { + const length = values.length; + const offsets = new Array(length + 1); + let offset = 0; + for (let i = 0; i < length; ++i) { + offsets[i] = offset; + offset += values[i].length; + } + offsets[length] = offset; + offsetType = defaultValue(offsetType, "UINT32"); + return BinaryPropertyTables.createBuffer(offsets, offsetType); + } +} diff --git a/src/metadata/binary/BooleanArrayPropertyModel.ts b/src/metadata/binary/BooleanArrayPropertyModel.ts new file mode 100644 index 00000000..6e5bdd93 --- /dev/null +++ b/src/metadata/binary/BooleanArrayPropertyModel.ts @@ -0,0 +1,54 @@ +import { PropertyModel } from "./PropertyModel"; +import { PropertyModels } from "./PropertyModels"; +import { BooleanPropertyModel } from "./BooleanPropertyModel"; + +/** + * Implementation of a `PropertyModel` for boolean arrays + * + * @internal + */ +export class BooleanArrayPropertyModel implements PropertyModel { + private readonly _valuesBuffer: Buffer; + private readonly _arrayOffsetsBuffer: Buffer | undefined; + private readonly _arrayOffsetType: string; + private readonly _count: number | undefined; + + constructor( + valuesBuffer: Buffer, + arrayOffsetsBuffer: Buffer | undefined, + arrayOffsetType: string, + count: number | undefined + ) { + this._valuesBuffer = valuesBuffer; + this._arrayOffsetsBuffer = arrayOffsetsBuffer; + this._arrayOffsetType = arrayOffsetType; + this._count = count; + } + + getPropertyValue(index: number): any { + const valuesBuffer = this._valuesBuffer; + const arrayOffsetsBuffer = this._arrayOffsetsBuffer; + const arrayOffsetType = this._arrayOffsetType; + const count = this._count; + + const arraySlice = PropertyModels.computeSlice( + index, + arrayOffsetsBuffer, + arrayOffsetType, + count + ); + const arrayOffset = arraySlice.offset; + const arrayLength = arraySlice.length; + + const result = new Array(arrayLength); + for (let i = 0; i < arrayLength; i++) { + const n = arrayOffset + i; + const element = BooleanPropertyModel.getBooleanFromBuffer( + valuesBuffer, + n + ); + result[i] = element; + } + return result; + } +} diff --git a/src/metadata/binary/BooleanPropertyModel.ts b/src/metadata/binary/BooleanPropertyModel.ts new file mode 100644 index 00000000..7eb3d109 --- /dev/null +++ b/src/metadata/binary/BooleanPropertyModel.ts @@ -0,0 +1,37 @@ +import { PropertyModel } from "./PropertyModel"; +import { NumericBuffers } from "./NumericBuffers"; + +/** + * Implementation of a `PropertyModel` for booleans + * + * @internal + */ +export class BooleanPropertyModel implements PropertyModel { + private readonly _valuesBuffer: Buffer; + + constructor(valuesBuffer: Buffer) { + this._valuesBuffer = valuesBuffer; + } + + getPropertyValue(index: number): any { + const valuesBuffer = this._valuesBuffer; + const result = BooleanPropertyModel.getBooleanFromBuffer( + valuesBuffer, + index + ); + return result; + } + + static getBooleanFromBuffer(buffer: Buffer, index: number): boolean { + const byteIndex = Math.floor(index / 8); + const bitIndex = index % 8; + const byte = NumericBuffers.getNumericFromBuffer( + buffer, + byteIndex, + "UINT8" + ); + const bit = 1 << bitIndex; + const result = (byte & bit) !== 0; + return result; + } +} diff --git a/src/metadata/binary/NumericArrayPropertyModel.ts b/src/metadata/binary/NumericArrayPropertyModel.ts new file mode 100644 index 00000000..762be700 --- /dev/null +++ b/src/metadata/binary/NumericArrayPropertyModel.ts @@ -0,0 +1,81 @@ +import { PropertyModel } from "./PropertyModel"; +import { NumericBuffers } from "./NumericBuffers"; +import { PropertyModels } from "./PropertyModels"; + +import { MetadataTypes } from "../MetadataTypes"; + +/** + * Implementation of a `PropertyModel` for numeric array types. + * + * This includes all types that have numeric component types, + * i.e. the `SCALAR`, `VECn` and `MATn` types, and the + * (binary, and therefore numeric) representation of `ENUM`. + * + * @internal + */ +export class NumericArrayPropertyModel implements PropertyModel { + private readonly _type: string; + private readonly _valuesBuffer: Buffer; + private readonly _componentType: string; + private readonly _arrayOffsetsBuffer: Buffer | undefined; + private readonly _arrayOffsetType: string; + private readonly _count: number | undefined; + + constructor( + type: string, + valuesBuffer: Buffer, + componentType: string, + arrayOffsetsBuffer: Buffer | undefined, + arrayOffsetType: string, + count: number | undefined + ) { + this._type = type; + this._valuesBuffer = valuesBuffer; + this._componentType = componentType; + this._arrayOffsetsBuffer = arrayOffsetsBuffer; + this._arrayOffsetType = arrayOffsetType; + this._count = count; + } + + getPropertyValue(index: number): any { + const type = this._type; + const valuesBuffer = this._valuesBuffer; + const componentType = this._componentType; + const arrayOffsetsBuffer = this._arrayOffsetsBuffer; + const arrayOffsetType = this._arrayOffsetType; + const count = this._count; + const componentCount = MetadataTypes.componentCountForType(type); + + const arraySlice = PropertyModels.computeSlice( + index, + arrayOffsetsBuffer, + arrayOffsetType, + count + ); + const arrayOffset = arraySlice.offset; + const arrayLength = arraySlice.length; + + const result = new Array(arrayLength); + for (let i = 0; i < arrayLength; i++) { + const n = arrayOffset + i; + + let element = undefined; + if (type === "SCALAR" || type === "ENUM") { + element = NumericBuffers.getNumericFromBuffer( + valuesBuffer, + n, + componentType + ); + } else { + element = NumericBuffers.getNumericArrayFromBuffer( + valuesBuffer, + n, + componentCount, + componentType + ); + } + result[i] = element; + } + return result; + } +} diff --git a/src/metadata/binary/NumericBuffers.ts b/src/metadata/binary/NumericBuffers.ts new file mode 100644 index 00000000..53eb73f4 --- /dev/null +++ b/src/metadata/binary/NumericBuffers.ts @@ -0,0 +1,151 @@ +import { ArrayBuffers } from "./ArrayBuffers"; + +import { MetadataComponentTypes } from "../MetadataComponentTypes"; +import { MetadataError } from "../MetadataError"; + +/** + * Methods for extracting `number`- or `bigint` values or arrays thereof + * from `Buffer` objects + * + * @internal + */ +export class NumericBuffers { + /** + * Obtains a single number or bigint from the given buffer, + * at the given index, based on the given component type. + * + * @param buffer - The buffer + * @param index - The index + * @param componentType - The component type + * @returns The number or bigint + * @throws MetadataError If the given `componentType` is not valid + */ + static getNumericFromBuffer( + buffer: Buffer, + index: number, + componentType: string + ): any { + const byteSize = + MetadataComponentTypes.byteSizeForComponentType(componentType); + const byteOffset = index * byteSize; + switch (componentType) { + case "INT8": + return buffer.readInt8(byteOffset); + case "UINT8": + return buffer.readUint8(byteOffset); + case "INT16": + return buffer.readInt16LE(byteOffset); + case "UINT16": + return buffer.readUInt16LE(byteOffset); + case "INT32": + return buffer.readInt32LE(byteOffset); + case "UINT32": + return buffer.readUInt32LE(byteOffset); + case "INT64": + return buffer.readBigInt64LE(byteOffset); + case "UINT64": + return buffer.readBigUInt64LE(byteOffset); + case "FLOAT32": + return buffer.readFloatLE(byteOffset); + case "FLOAT64": + return buffer.readDoubleLE(byteOffset); + } + throw new MetadataError(`Unknown component type: ${componentType}`); + } + + /** + * Obtains an array of number or bigint values from the given buffer, + * starting at the given index, based on the given component type. + * + * @param buffer - The buffer + * @param index - The index + * @param arrayLength - The length of the array, in number of elements + * @param componentType - The component type + * @returns The number or bigint array + * @throws MetadataError If the given `componentType` is not valid + */ + static getNumericArrayFromBuffer( + buffer: Buffer, + index: number, + arrayLength: number, + componentType: string + ): any { + const typedArray = NumericBuffers.getTypedArrayFromBuffer( + buffer, + index, + arrayLength, + componentType + ); + const array = [...typedArray]; + return array; + } + + /** + * Obtains a typed array of number or bigint values from the given buffer, + * starting at the given index, based on the given component type. + * + * @param buffer - The buffer + * @param index - The index + * @param arrayLength - The length of the array, in number of elements + * @param componentType - The component type + * @returns The number or bigint typed array + * @throws MetadataError If the given `componentType` is not valid + */ + private static getTypedArrayFromBuffer( + buffer: Buffer, + index: number, + arrayLength: number, + componentType: string + ) { + const componentSize = + MetadataComponentTypes.byteSizeForComponentType(componentType); + const elementSize = arrayLength * componentSize; + const byteOffset = index * elementSize; + const arrayBuffer = ArrayBuffers.fromBuffer(buffer); + switch (componentType) { + case "INT8": + return new Int8Array(arrayBuffer, byteOffset, arrayLength); + case "UINT8": + return new Uint8Array(arrayBuffer, byteOffset, arrayLength); + case "INT16": + return new Int16Array(arrayBuffer, byteOffset, arrayLength); + case "UINT16": + return new Uint16Array(arrayBuffer, byteOffset, arrayLength); + case "INT32": + return new Int32Array(arrayBuffer, byteOffset, arrayLength); + case "UINT32": + return new Uint32Array(arrayBuffer, byteOffset, arrayLength); + case "INT64": + return new BigInt64Array(arrayBuffer, byteOffset, arrayLength); + case "UINT64": + return new BigUint64Array(arrayBuffer, byteOffset, arrayLength); + case "FLOAT32": + return new Float32Array(arrayBuffer, byteOffset, arrayLength); + case "FLOAT64": + return new Float64Array(arrayBuffer, byteOffset, arrayLength); + } + throw new MetadataError(`Unknown component type: ${componentType}`); + } + + /** + * Returns an array that contains the `number`- or `bigint` values + * that are stored in the given buffer + * + * @param buffer - The buffer + * @param componentType - The component type + * @returns The number or bigint array + * @throws MetadataError If the given `componentType` is not valid + */ + static getNumericBufferAsArray(buffer: Buffer, componentType: string): any { + const size = MetadataComponentTypes.byteSizeForComponentType(componentType); + const arrayLength = buffer.length / size; + const typedArray = NumericBuffers.getTypedArrayFromBuffer( + buffer, + 0, + arrayLength, + componentType + ); + const array = [...typedArray]; + return array; + } +} diff --git a/src/metadata/binary/NumericPropertyModel.ts b/src/metadata/binary/NumericPropertyModel.ts new file mode 100644 index 00000000..371c2f50 --- /dev/null +++ b/src/metadata/binary/NumericPropertyModel.ts @@ -0,0 +1,46 @@ +import { PropertyModel } from "./PropertyModel"; +import { NumericBuffers } from "./NumericBuffers"; + +import { MetadataTypes } from "../MetadataTypes"; + +/** + * Implementation of a `PropertyModel` for numeric types. + * + * This includes all types that have numeric component types, + * i.e. the `SCALAR`, `VECn` and `MATn` types, and the + * (binary, and therefore numeric) representation of `ENUM`. + * + * @internal + */ +export class NumericPropertyModel implements PropertyModel { + private readonly _type: string; + private readonly _valuesBuffer: Buffer; + private readonly _componentType: string; + + constructor(type: string, valuesBuffer: Buffer, componentType: string) { + this._type = type; + this._valuesBuffer = valuesBuffer; + this._componentType = componentType; + } + + getPropertyValue(index: number): any { + const valuesBuffer = this._valuesBuffer; + const componentType = this._componentType; + const type = this._type; + + if (type === "SCALAR" || type === "ENUM") { + return NumericBuffers.getNumericFromBuffer( + valuesBuffer, + index, + componentType + ); + } + const componentCount = MetadataTypes.componentCountForType(type); + return NumericBuffers.getNumericArrayFromBuffer( + valuesBuffer, + index, + componentCount, + componentType + ); + } +} diff --git a/src/metadata/binary/PropertyModel.ts b/src/metadata/binary/PropertyModel.ts new file mode 100644 index 00000000..869bb38f --- /dev/null +++ b/src/metadata/binary/PropertyModel.ts @@ -0,0 +1,30 @@ +/** + * A basic interface for a property in a property table. + * + * This can be imagined as one "column" in the table. + * + * @internal + */ +export interface PropertyModel { + /** + * Obtains the property value at the given index. + * + * The index corresponds to a "row" in the property table. + * + * The type of the returned object depends on the type of + * the property: + * - For `STRING` properties, it will be a `string` + * - For `BOOLEAN` properties, it will be a `boolean` + * - For `ENUM` properties, it will be the numeric value + * that corresponds to the respective enum constant. + * - For `SCALAR` properties, it will be a `number` or `bigint` + * - For `VECn`- or `MATn` properties, it will be an array + * of `number`- or `bigint` elements + * - For array properties, it will be an array of the + * respective elements + * + * @param index - The index + * @returns The property value + */ + getPropertyValue(index: number): any; +} diff --git a/src/metadata/binary/PropertyModels.ts b/src/metadata/binary/PropertyModels.ts new file mode 100644 index 00000000..374a594f --- /dev/null +++ b/src/metadata/binary/PropertyModels.ts @@ -0,0 +1,295 @@ +import { defined } from "../../base/defined"; + +import { BinaryPropertyTable } from "./BinaryPropertyTable"; +import { PropertyModel } from "./PropertyModel"; +import { StringPropertyModel } from "./StringPropertyModel"; +import { BooleanPropertyModel } from "./BooleanPropertyModel"; +import { NumericPropertyModel } from "./NumericPropertyModel"; +import { NumericArrayPropertyModel } from "./NumericArrayPropertyModel"; +import { StringArrayPropertyModel } from "./StringArrayPropertyModel"; +import { BooleanArrayPropertyModel } from "./BooleanArrayPropertyModel"; +import { NumericBuffers } from "./NumericBuffers"; + +import { MetadataError } from "../MetadataError"; +/** + * Methods related to `PropertyModel` instances + * + * @internal + */ +export class PropertyModels { + /** + * Creates a `PropertyModel` for the specified property in + * the given `BinaryPropertyTable`. + * + * This assumes that the input is structurally valid. + * + * This will determine the type of the property and access its + * associated data (i.e. the required buffer views data) from + * the given `BinaryPropertyTable`. For each type of property, + * this will return a matching implementation of the + * `PropertyModel` interface. + * + * @param binaryPropertyTable - The `BinaryPropertyTable` + * @param propertyId - The property ID + * @returns The `PropertyModel` + * @throws MetadataError If the input data is not structurally + * valid + */ + static createPropertyModel( + binaryPropertyTable: BinaryPropertyTable, + propertyId: string + ): PropertyModel { + // Implementation note: + // It would be nice to do some of the sanity checks upfront (e.g. + // checking that the string offsets have been defined properly + // when the type is 'STRING'). But in order to exploit the + // TypeScript definedness checks, and avoid the use of the + // non-null-assertion '!', some checks have to be done right + // before a certain value is used. It might be possible to + // find a more "elegant" solution here, but it's unlikely + // that the final, nested `if(isArray) { if (type===X) {}}` + // check could be avoided. + + // Obtain the `ClassProperty` + const metadataClass = binaryPropertyTable.metadataClass; + const classProperties = metadataClass.properties; + if (!classProperties) { + throw new MetadataError(`The class does not define any properties`); + } + const classProperty = classProperties[propertyId]; + if (!classProperty) { + throw new MetadataError( + `The class does not define property ${propertyId}` + ); + } + + // Obtain the `PropertyTableProperty` + const propertyTable = binaryPropertyTable.propertyTable; + const propertyTableProperties = propertyTable.properties; + if (!propertyTableProperties) { + throw new MetadataError( + `The property table does not define any properties` + ); + } + const propertyTableProperty = propertyTableProperties[propertyId]; + if (!propertyTableProperty) { + throw new MetadataError( + `The property table does not define property ${propertyId}` + ); + } + + // Obtain the required buffers from the binary data: + const binaryBufferData = binaryPropertyTable.binaryBufferData; + const bufferViewsData = binaryBufferData.bufferViewsData; + + // Obtain the `values` buffer view data + const valuesBufferViewIndex = propertyTableProperty.values; + const valuesBufferViewData = bufferViewsData[valuesBufferViewIndex]; + + // Obtain the `arrayOffsets` buffer view data + const arrayOffsetsBufferViewIndex = propertyTableProperty.arrayOffsets; + let arrayOffsetsBufferViewData = undefined; + if (defined(arrayOffsetsBufferViewIndex)) { + arrayOffsetsBufferViewData = bufferViewsData[arrayOffsetsBufferViewIndex]; + } + const arrayOffsetType = propertyTableProperty.arrayOffsetType ?? "UINT32"; + + // Obtain the `stringOffsets` buffer view data + const stringOffsetsBufferViewIndex = propertyTableProperty.stringOffsets; + let stringOffsetsBufferViewData = undefined; + if (defined(stringOffsetsBufferViewIndex)) { + stringOffsetsBufferViewData = + bufferViewsData[stringOffsetsBufferViewIndex]; + } + const stringOffsetType = propertyTableProperty.stringOffsetType ?? "UINT32"; + + // Determine the `enumValueType` of the property + const enumType = classProperty.enumType; + let enumValueType = undefined; + if (defined(enumType)) { + const binaryEnumInfo = binaryPropertyTable.binaryEnumInfo; + const enumValueTypes = binaryEnumInfo.enumValueTypes; + enumValueType = enumValueTypes[enumType] ?? "UINT16"; + } + + // Create the `PropertyModel` implementation that matches + // the type of the property + const type = classProperty.type; + const componentType = classProperty.componentType; + const count = classProperty.count; + const isArray = classProperty.array === true; + if (isArray) { + if (type === "STRING") { + if (!stringOffsetsBufferViewData) { + throw new MetadataError( + `The property ${propertyId} is an array of strings, ` + + `but no string offsets have been defined` + ); + } + + const propertyModel = new StringArrayPropertyModel( + valuesBufferViewData, + arrayOffsetsBufferViewData, + arrayOffsetType, + stringOffsetsBufferViewData, + stringOffsetType, + count + ); + return propertyModel; + } + if (type === "BOOLEAN") { + const propertyModel = new BooleanArrayPropertyModel( + valuesBufferViewData, + arrayOffsetsBufferViewData, + arrayOffsetType, + count + ); + return propertyModel; + } + if (type === "ENUM") { + if (!enumValueType) { + throw new MetadataError( + `The property ${propertyId} is an enum, ` + + `but no enum value type has been defined` + ); + } + + const propertyModel = new NumericArrayPropertyModel( + type, + valuesBufferViewData, + enumValueType, + arrayOffsetsBufferViewData, + arrayOffsetType, + count + ); + return propertyModel; + } + // The 'type' must be a numeric (array) type here + + if (!defined(componentType)) { + throw new MetadataError( + `The property ${propertyId} is a numeric array, ` + + `but no component type has been defined` + ); + } + + const propertyModel = new NumericArrayPropertyModel( + type, + valuesBufferViewData, + componentType, + arrayOffsetsBufferViewData, + arrayOffsetType, + count + ); + return propertyModel; + } + + // The property must be a non-array property here: + if (type === "STRING") { + if (!stringOffsetsBufferViewData) { + throw new MetadataError( + `The property ${propertyId} has the type 'STRING', ` + + `but no string offsets have been defined` + ); + } + + const propertyModel = new StringPropertyModel( + valuesBufferViewData, + stringOffsetsBufferViewData, + stringOffsetType + ); + return propertyModel; + } + if (type === "BOOLEAN") { + const propertyModel = new BooleanPropertyModel(valuesBufferViewData); + return propertyModel; + } + if (type === "ENUM") { + if (!enumValueType) { + throw new MetadataError( + `The property ${propertyId} is an enum, ` + + `but no enum value type has been defined` + ); + } + + const propertyModel = new NumericPropertyModel( + type, + valuesBufferViewData, + enumValueType + ); + return propertyModel; + } + + // The property must be a (non-array) numeric property here + + if (!defined(componentType)) { + throw new MetadataError( + `The property ${propertyId} is numeric, ` + + `but no component type has been defined` + ); + } + + const propertyModel = new NumericPropertyModel( + type, + valuesBufferViewData, + componentType + ); + return propertyModel; + } + + /** + * Returns the 'slice' information that is given by an offsets + * buffer or a fixed number. + * + * This returns `{ offset, length }` for the `arrayOffsets` or + * `stringOffsets` of a property, for a given index. + * + * When the given `count` is defined, then the result will + * just be `{ index * count, count }`. + * + * Otherwise, the result will be `{ offset, length }`, where `offset` + * is the offset that is read from the given buffer at index `index`, + * and `length` is `offset[index+1] - offset[index]`. + * + * @param index - The index + * @param offsetsBuffer - The offsets + * @param offsetType - The `componentType` for the offsets + * @param count - The count + * @returns The slice information + * @throws MetadataError If both the `count` and the `offsetsBuffer` + * are `undefined`. + */ + static computeSlice( + index: number, + offsetsBuffer: Buffer | undefined, + offsetType: string, + count: number | undefined + ): { offset: number; length: number } { + if (defined(count)) { + return { + offset: index * count, + length: count, + }; + } + if (!offsetsBuffer) { + throw new MetadataError( + `Neither the 'count' nor the offsets buffer have been defined` + ); + } + const offset = NumericBuffers.getNumericFromBuffer( + offsetsBuffer, + index, + offsetType + ); + const nextOffset = NumericBuffers.getNumericFromBuffer( + offsetsBuffer, + index + 1, + offsetType + ); + const length = nextOffset - offset; + return { + offset: offset, + length: length, + }; + } +} diff --git a/src/metadata/binary/PropertyTableModel.ts b/src/metadata/binary/PropertyTableModel.ts new file mode 100644 index 00000000..afe7e71a --- /dev/null +++ b/src/metadata/binary/PropertyTableModel.ts @@ -0,0 +1,136 @@ +import { BinaryMetadataEntityModel } from "./BinaryMetadataEntityModel"; +import { BinaryPropertyTable } from "./BinaryPropertyTable"; +import { PropertyModel } from "./PropertyModel"; +import { PropertyModels } from "./PropertyModels"; + +import { MetadataEntityModel } from "../MetadataEntityModel"; +import { MetadataEntityModels } from "../MetadataEntityModels"; +import { MetadataError } from "../MetadataError"; + +import { ClassProperty } from "../../structure/Metadata/ClassProperty"; +import { PropertyTableProperty } from "../../structure/PropertyTableProperty"; + +/** + * Implementation of a model for a property table that is backed + * by binary data. + * + * @internal + */ +export class PropertyTableModel { + /** + * The structure containing the raw data of the binary + * property table + */ + private readonly _binaryPropertyTable: BinaryPropertyTable; + + /** + * A mapping from property IDs to the `PropertyModel` + * instances that provide the property values. These + * are the "columns" of the table + */ + private readonly _propertyIdToModel: { [key: string]: PropertyModel } = {}; + + /** + * A mapping from 'semantic' strings to the 'propertyId' + * strings of the properties that have the respective + * semantic + */ + private readonly _semanticToPropertyId: { [key: string]: string }; + + constructor(binaryPropertyTable: BinaryPropertyTable) { + this._binaryPropertyTable = binaryPropertyTable; + + // Initialize the `PropertyModel` instances for + // the property table properties + const propertyTable = this._binaryPropertyTable.propertyTable; + const propertyTableProperties = propertyTable.properties; + if (propertyTableProperties) { + for (const propertyId of Object.keys(propertyTableProperties)) { + const propertyModel = PropertyModels.createPropertyModel( + this._binaryPropertyTable, + propertyId + ); + this._propertyIdToModel[propertyId] = propertyModel; + } + } + + const metadataClass = binaryPropertyTable.metadataClass; + this._semanticToPropertyId = + MetadataEntityModels.computeSemanticToPropertyIdMapping(metadataClass); + } + + /** + * Returns the `MetadataEntityModel` that corresponds to the + * row of the table with the given index. + * + * @param index - The index (i.e. the table row) + * @returns The `MetdataEntityModel` + * @throws MetadataError If the index is out of range + */ + getMetadataEntityModel(index: number): MetadataEntityModel { + const propertyTable = this._binaryPropertyTable.propertyTable; + const count = propertyTable.count; + if (index < 0 || index >= count) { + const message = `The index must be in [0,${count}), but is ${index}`; + throw new MetadataError(message); + } + const semanticToPropertyId = this._semanticToPropertyId; + const metadataEntityModel = new BinaryMetadataEntityModel( + this, + index, + semanticToPropertyId + ); + return metadataEntityModel; + } + + /** + * Returns the `ClassProperty` that defines the structure of the + * property with the given ID, or `undefined` if this table was + * created for a `MetadataClass` that does not define this property. + * + * @param propertyId - The property ID + * @returns The `ClassProperty` + */ + getClassProperty(propertyId: string): ClassProperty | undefined { + const binaryPropertyTable = this._binaryPropertyTable; + const metadataClass = binaryPropertyTable.metadataClass; + const classProperties = metadataClass.properties; + if (!classProperties) { + return undefined; + } + return classProperties[propertyId]; + } + + /** + * Returns the `PropertyTableProperty` that defines the structure of the + * property with the given ID, or `undefined` if this table was + * created for a `PropertyTable` that does not define this property. + * + * @param propertyId - The property ID + * @returns The `PropertyTableProperty` + */ + getPropertyTableProperty( + propertyId: string + ): PropertyTableProperty | undefined { + const binaryPropertyTable = this._binaryPropertyTable; + const propertyTable = binaryPropertyTable.propertyTable; + const propertyTableProperties = propertyTable.properties; + if (!propertyTableProperties) { + return undefined; + } + return propertyTableProperties[propertyId]; + } + + /** + * Returns the `PropertyModel` for the property with the given ID. + * This is the "column" of the table that contains the property + * data. Returns `undefined` if this table was created for + * a `MetadataClass` that does not define this property. + * + * @param propertyId - The property ID + * @returns The `PropertyModel` + */ + getPropertyModel(propertyId: string): PropertyModel | undefined { + return this._propertyIdToModel[propertyId]; + } +} diff --git a/src/metadata/binary/StringArrayPropertyModel.ts b/src/metadata/binary/StringArrayPropertyModel.ts new file mode 100644 index 00000000..7a862481 --- /dev/null +++ b/src/metadata/binary/StringArrayPropertyModel.ts @@ -0,0 +1,80 @@ +import { PropertyModel } from "./PropertyModel"; +import { PropertyModels } from "./PropertyModels"; + +import { ArrayBuffers } from "./ArrayBuffers"; + +/** + * Implementation of a `PropertyModel` for string arrays + * + * @internal + */ +export class StringArrayPropertyModel implements PropertyModel { + // Implementation note: + // Either the `arrayOffsetsBuffer` or the `count` may be undefined. + // When `arrayOffsetsBuffer` is defined, then this indicates a + // variable-length array. When the `count` is defined, then this + // indicates a fixed-length array. + + private static readonly decoder = new TextDecoder(); + + private readonly _valuesBuffer: Buffer; + private readonly _arrayOffsetsBuffer: Buffer | undefined; + private readonly _arrayOffsetType: string; + private readonly _stringOffsetsBuffer: Buffer; + private readonly _stringOffsetType: string; + private readonly _count: number | undefined; + + constructor( + valuesBuffer: Buffer, + arrayOffsetsBuffer: Buffer | undefined, + arrayOffsetType: string, + stringOffsetsBuffer: Buffer, + stringOffsetType: string, + count: number | undefined + ) { + this._valuesBuffer = valuesBuffer; + this._arrayOffsetsBuffer = arrayOffsetsBuffer; + this._arrayOffsetType = arrayOffsetType; + this._stringOffsetsBuffer = stringOffsetsBuffer; + this._stringOffsetType = stringOffsetType; + this._count = count; + } + + getPropertyValue(index: number): any { + const valuesBuffer = this._valuesBuffer; + const arrayOffsetsBuffer = this._arrayOffsetsBuffer; + const arrayOffsetType = this._arrayOffsetType; + const stringOffsetsBuffer = this._stringOffsetsBuffer; + const stringOffsetType = this._stringOffsetType; + const count = this._count; + + const arraySlice = PropertyModels.computeSlice( + index, + arrayOffsetsBuffer, + arrayOffsetType, + count + ); + const arrayOffset = arraySlice.offset; + const arrayLength = arraySlice.length; + + const result = new Array(arrayLength); + for (let i = 0; i < arrayLength; i++) { + const n = arrayOffset + i; + + const stringSlice = PropertyModels.computeSlice( + n, + stringOffsetsBuffer, + stringOffsetType, + undefined + ); + const stringOffset = stringSlice.offset; + const stringLength = stringSlice.length; + const arrayBuffer = ArrayBuffers.fromBuffer(valuesBuffer); + const element = StringArrayPropertyModel.decoder.decode( + arrayBuffer.slice(stringOffset, stringOffset + stringLength) + ); + result[i] = element; + } + return result; + } +} diff --git a/src/metadata/binary/StringPropertyModel.ts b/src/metadata/binary/StringPropertyModel.ts new file mode 100644 index 00000000..7a031710 --- /dev/null +++ b/src/metadata/binary/StringPropertyModel.ts @@ -0,0 +1,47 @@ +import { PropertyModel } from "./PropertyModel"; +import { PropertyModels } from "./PropertyModels"; +import { ArrayBuffers } from "./ArrayBuffers"; + +/** + * Implementation of a `PropertyModel` for strings + * + * @internal + */ +export class StringPropertyModel implements PropertyModel { + private static readonly decoder = new TextDecoder(); + + private readonly _valuesBuffer: Buffer; + private readonly _stringOffsetsBuffer: Buffer; + private readonly _stringOffsetType: string; + + constructor( + valuesBuffer: Buffer, + stringOffsetsBuffer: Buffer, + stringOffsetType: string + ) { + this._valuesBuffer = valuesBuffer; + this._stringOffsetsBuffer = stringOffsetsBuffer; + this._stringOffsetType = stringOffsetType; + } + + getPropertyValue(index: number): any { + const valuesBuffer = this._valuesBuffer; + const stringOffsetsBuffer = this._stringOffsetsBuffer; + const stringOffsetType = this._stringOffsetType; + + const stringSlice = PropertyModels.computeSlice( + index, + stringOffsetsBuffer, + stringOffsetType, + undefined + ); + const stringOffset = stringSlice.offset; + const stringLength = stringSlice.length; + + const arrayBuffer = ArrayBuffers.fromBuffer(valuesBuffer); + const result = StringPropertyModel.decoder.decode( + arrayBuffer.slice(stringOffset, stringOffset + stringLength) + ); + return result; + } +} diff --git a/src/structure/extensions/BoundingVolumeS2.ts b/src/structure/extensions/BoundingVolumeS2.ts new file mode 100644 index 00000000..f78c2b28 --- /dev/null +++ b/src/structure/extensions/BoundingVolumeS2.ts @@ -0,0 +1,7 @@ +import { RootProperty } from "../RootProperty"; + +export interface BoundingVolumeS2 extends RootProperty { + token: string; + minimumHeight: number; + maximumHeight: number; +} diff --git a/src/traversal/ExplicitTraversedTile.ts b/src/traversal/ExplicitTraversedTile.ts new file mode 100644 index 00000000..8d8a1f41 --- /dev/null +++ b/src/traversal/ExplicitTraversedTile.ts @@ -0,0 +1,254 @@ +import { defined } from "../base/defined"; + +import { ResourceResolver } from "../io/ResourceResolver"; + +import { TraversedTile } from "./TraversedTile"; +import { ImplicitTileTraversal } from "./ImplicitTileTraversal"; + +import { Tile } from "../structure/Tile"; +import { Content } from "../structure/Content"; +import { TileImplicitTiling } from "../structure/TileImplicitTiling"; +import { MetadataEntity } from "../structure/MetadataEntity"; +import { Schema } from "../structure/Metadata/Schema"; + +import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; +import { ImplicitTilings } from "../implicitTiling/ImplicitTilings"; + +import { MetadataEntityModels } from "../metadata/MetadataEntityModels"; + +/** + * An implementation of a `TraversedTile` that reflects a tile + * that actually appears as a JSON representation in the tileset. + * + * @internal + */ +export class ExplicitTraversedTile implements TraversedTile { + /** + * The parent tile, or `undefined` if this is the root + */ + private readonly _parent: TraversedTile | undefined; + + /** + * The `Tile` object that this traversed tile was created for + */ + private readonly _tile: Tile; + + /** + * A JSON-path like path identifying this tile + */ + private readonly _path: string; + + /** + * The global level. This is the level starting at the + * root of the tileset. + */ + private readonly _level: number; + + /** + * The metadata schema in the context of which this tile + * is created. This is the schema that was obtained from + * the `tileset.schema` or `tileset.schemaUri`. If this + * is defined, it is assumed to be valid. If it is + * undefined and a tile with metadata is encountered, + * then an error will be thrown in `asTile`. + */ + private readonly _schema: Schema | undefined; + + /** + * The `ResourceResolver` that will resolve resources + * that may be required if this is the root of an + * implicit tileset (e.g. the subtree files). + */ + private readonly _resourceResolver; + + constructor( + tile: Tile, + path: string, + level: number, + parent: TraversedTile | undefined, + schema: Schema | undefined, + resourceResolver: ResourceResolver + ) { + this._tile = tile; + this._path = path; + this._level = level; + this._parent = parent; + this._schema = schema; + this._resourceResolver = resourceResolver; + } + + asTile(): Tile { + const tile = this._tile; + + const schema = this._schema; + const metadata = tile.metadata; + + const boundingVolume = tile.boundingVolume; + let transform = tile.transform; + let refine = tile.refine; + let geometricError = tile.geometricError; + + if (metadata && schema) { + let metadataEntityModel = undefined; + try { + metadataEntityModel = MetadataEntityModels.create(schema, metadata); + } catch (error) { + // Errors from creating the entity model should have been + // prevented by the metadata- and schema validation: + const message = `Error while traversing tileset: ${error}`; + throw new ImplicitTilingError(message); + } + if (metadataEntityModel) { + // Apply the semantic-based overrides from the metadata + const semanticBoundingBox = + metadataEntityModel.getPropertyValueBySemantic("TILE_BOUNDING_BOX"); + if (semanticBoundingBox) { + boundingVolume.box = semanticBoundingBox; + } + + const semanticBoundingRegion = + metadataEntityModel.getPropertyValueBySemantic( + "TILE_BOUNDING_REGION" + ); + if (semanticBoundingRegion) { + boundingVolume.region = semanticBoundingRegion; + } + + const semanticBoundingSphere = + metadataEntityModel.getPropertyValueBySemantic( + "TILE_BOUNDING_SPHERE" + ); + if (semanticBoundingSphere) { + boundingVolume.sphere = semanticBoundingSphere; + } + + const semanticGeometricError = + metadataEntityModel.getPropertyValueBySemantic( + "TILE_GEOMETRIC_ERROR" + ); + if (defined(semanticGeometricError)) { + geometricError = semanticGeometricError as number; + } + + const semanticRefine = + metadataEntityModel.getPropertyValueBySemantic("TILE_REFINE"); + if (semanticRefine === 0) { + refine = "ADD"; + } else if (semanticRefine === 1) { + refine = "REPLACE"; + } + + const semanticTransform = + metadataEntityModel.getPropertyValueBySemantic("TILE_TRANSFORM"); + if (semanticTransform) { + transform = semanticTransform; + } + } + } + + const viewerRequestVolume = tile.viewerRequestVolume; + const content = tile.content; + const contents = tile.contents; + const children = tile.children; + const implicitTiling = tile.implicitTiling; + const extensions = tile.extensions; + const extras = tile.extras; + + return { + boundingVolume: boundingVolume, + viewerRequestVolume: viewerRequestVolume, + geometricError: geometricError, + refine: refine, + transform: transform, + content: content, + contents: contents, + children: children, + metadata: metadata, + implicitTiling: implicitTiling, + extensions: extensions, + extras: extras, + }; + } + + get path(): string { + return this._path; + } + get level(): number { + return this._level; + } + + getParent(): TraversedTile | undefined { + return this._parent; + } + + async getChildren(): Promise { + const implicitTiling = this._tile.implicitTiling; + if (implicitTiling) { + const children = await ImplicitTileTraversal.createTraversedChildren( + implicitTiling, + this, + this._resourceResolver + ); + return children; + } + + if (!this._tile.children) { + return []; + } + const children = this._tile.children; + const childLevel = this._level + 1; + const traversedChildren = []; + for (let i = 0; i < children.length; i++) { + const child = children[i]; + const childPath = this.path + "/children/" + i; + const traversedChild = new ExplicitTraversedTile( + child, + childPath, + childLevel, + this, + this._schema, + this._resourceResolver + ); + traversedChildren.push(traversedChild); + } + return traversedChildren; + } + + getContents(): Content[] { + if (this._tile.content) { + return [this._tile.content]; + } + if (this._tile.contents) { + return this._tile.contents; + } + return []; + } + + getSubtreeUri(): string | undefined { + const implicitTiling = this._tile.implicitTiling; + if (implicitTiling) { + const rootCoordinates = + ImplicitTilings.createRootCoordinates(implicitTiling); + const subtreeUri = ImplicitTilings.substituteTemplateUri( + implicitTiling.subdivisionScheme, + implicitTiling.subtrees.uri, + rootCoordinates + ); + return subtreeUri; + } + return undefined; + } + + getImplicitTiling(): TileImplicitTiling | undefined { + return this._tile.implicitTiling; + } + + getMetadata(): MetadataEntity | undefined { + return this._tile.metadata; + } + + // TODO For debugging + toString = (): string => { + return `ExplicitTraversedTile, level ${this.level}, path ${this.path}`; + }; +} diff --git a/src/traversal/ImplicitTileTraversal.ts b/src/traversal/ImplicitTileTraversal.ts new file mode 100644 index 00000000..2d9d7c84 --- /dev/null +++ b/src/traversal/ImplicitTileTraversal.ts @@ -0,0 +1,238 @@ +import path from "path"; + +import { defined } from "../base/defined"; +import { Buffers } from "../base/Buffers"; + +import { ResourceResolver } from "../io/ResourceResolver"; + +import { TraversedTile } from "./TraversedTile"; +import { ExplicitTraversedTile } from "./ExplicitTraversedTile"; +import { ImplicitTraversedTile } from "./ImplicitTraversedTile"; + +import { Subtree } from "../structure/Subtree"; +import { TileImplicitTiling } from "../structure/TileImplicitTiling"; + +import { SubtreeInfos } from "../implicitTiling/SubtreeInfos"; +import { TreeCoordinates } from "../implicitTiling/TreeCoordinates"; +import { SubtreeInfo } from "../implicitTiling/SubtreeInfo"; +import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; +import { ImplicitTilings } from "../implicitTiling/ImplicitTilings"; + +/** + * Methods related to the traversal of implicit tilesets. + * + * @internal + */ +export class ImplicitTileTraversal { + /** + * Create the traversed children for the given explicit traversed tile. + * + * This method will be called from `ExplicitTraversedTile` instances + * when the contain `implicitTiling` information, in order to create + * the traversed children. + * + * The children will then be a single-element array that contains the + * root node of the implicit tileset, as an `ImplicitTraversedTile`. + * + * @param implicitTiling - The `TileImplicitTiling` + * @param parent - The `ExplicitTraversedTile` + * @param resourceResolver - The `ResourceResolver` that + * will be used e.g. for subtree files + * @returns The traversed children + * @throws ImplicitTilingError If the input was structurally invalid + */ + static async createTraversedChildren( + implicitTiling: TileImplicitTiling, + parent: ExplicitTraversedTile, + resourceResolver: ResourceResolver + ): Promise { + const subdivisionScheme = implicitTiling.subdivisionScheme; + if (subdivisionScheme === "QUADTREE") { + const child = await ImplicitTileTraversal.createImplicitQuadtreeRoot( + implicitTiling, + parent, + resourceResolver + ); + return [child]; + } + if (subdivisionScheme === "OCTREE") { + const child = await ImplicitTileTraversal.createImplicitOctreeRoot( + implicitTiling, + parent, + resourceResolver + ); + return [child]; + } + throw new ImplicitTilingError( + "Invalid subdivisionScheme: " + subdivisionScheme + ); + } + + /** + * Creates the root node for the traversal of an implicit quadtree. + * + * @param implicitTiling - The `TileImplicitTiling` + * @param parent - The `ExplicitTraversedTile` + * @param resourceResolver - The `ResourceResolver` that + * will be used e.g. for subtree files + * @returns The root of an implicit quadtree + * @throws ImplicitTilingError If the input was structurally invalid + */ + private static async createImplicitQuadtreeRoot( + implicitTiling: TileImplicitTiling, + parent: ExplicitTraversedTile, + resourceResolver: ResourceResolver + ): Promise { + const rootCoordinates = + ImplicitTilings.createRootCoordinates(implicitTiling); + const subtreeInfo = await ImplicitTileTraversal.resolveSubtreeInfo( + implicitTiling, + resourceResolver, + rootCoordinates + ); + // The path is composed from the path of the parent and the string + // representation of the root coordinates + const coordinateString = ImplicitTilings.createString(rootCoordinates); + const path = `${parent.path}/${coordinateString}`; + const root = new ImplicitTraversedTile( + implicitTiling, + resourceResolver, + parent, + path, + subtreeInfo, + parent.level + 1, + rootCoordinates, + rootCoordinates, + rootCoordinates, + parent + ); + return root; + } + + /** + * Creates the root node for the traversal of an implicit octree. + * + * @param implicitTiling - The `TileImplicitTiling` + * @param parent - The `ExplicitTraversedTile` + * @param resourceResolver - The `ResourceResolver` that + * will be used e.g. for subtree files + * @returns The root of an implicit octree + * @throws ImplicitTilingError If the input was structurally invalid + */ + private static async createImplicitOctreeRoot( + implicitTiling: TileImplicitTiling, + parent: ExplicitTraversedTile, + resourceResolver: ResourceResolver + ): Promise { + const rootCoordinates = + ImplicitTilings.createRootCoordinates(implicitTiling); + const subtreeInfo = await ImplicitTileTraversal.resolveSubtreeInfo( + implicitTiling, + resourceResolver, + rootCoordinates + ); + // The path is composed from the path of the parent and the string + // representation of the root coordinates + const coordinateString = ImplicitTilings.createString(rootCoordinates); + const path = `${parent.path}/${coordinateString}`; + const root = new ImplicitTraversedTile( + implicitTiling, + resourceResolver, + parent, + path, + subtreeInfo, + parent.level + 1, + rootCoordinates, + rootCoordinates, + rootCoordinates, + parent + ); + return root; + } + + /** + * Resolve the `SubtreeInfo` for the subtree with the given root coordinates. + * + * This will substitute the given coordinates into the subtree template + * URI from the given implicit tiling object. Then it will attempt to load + * the subtree data from this URI. The resulting data will be used to + * construct the `SubtreeInfo` object. + * + * @param implicitTiling - The `TileImplicitTiling` + * @param resourceResolver - The `ResourceResolver` for the subtree + * files and buffers + * @param coordinates - The root coordinates of the subtree + * @returns The `SubtreeInfo` + * @throws ImplicitTilingError If the input was structurally invalid + */ + static async resolveSubtreeInfo( + implicitTiling: TileImplicitTiling, + resourceResolver: ResourceResolver, + coordinates: TreeCoordinates + ): Promise { + const subtreeUri = ImplicitTilings.substituteTemplateUri( + implicitTiling.subdivisionScheme, + implicitTiling.subtrees.uri, + coordinates + ); + if (!defined(subtreeUri)) { + const message = + `Could not substitute coordinates ${coordinates} in ` + + `template URI ${implicitTiling.subtrees.uri}`; + throw new ImplicitTilingError(message); + } + const subtreeData = await resourceResolver.resolveData(subtreeUri); + if (!subtreeData) { + const message = + `Could not resolve subtree URI ${subtreeUri} that was ` + + `created from template URI ${implicitTiling.subtrees.uri} ` + + `for coordinates ${coordinates}`; + throw new ImplicitTilingError(message); + } + + const subtreeDirectory = path.dirname(subtreeUri); + const subtreeResourceResolver = resourceResolver.derive(subtreeDirectory); + + // If the subtree data was JSON, just parse it and + // create a SubtreeInfo without an internal buffer + const isJson = Buffers.isProbablyJson(subtreeData); + if (isJson) { + let subtreeJson: any; + let subtree: Subtree; + try { + subtreeJson = Buffers.getJson(subtreeData); + subtree = subtreeJson; + } catch (error) { + const message = + `Could not parse subtree JSON from URI ${subtreeUri} that was ` + + `created from template URI ${implicitTiling.subtrees.uri} ` + + `for coordinates ${coordinates}`; + throw new ImplicitTilingError(message); + } + return SubtreeInfos.create( + subtree, + undefined, + implicitTiling, + subtreeResourceResolver + ); + } + + // For SUBT (binary subtree data), create the SubtreeInfo + // from the whole buffer + const isSubt = Buffers.getMagic(subtreeData) === "subt"; + if (isSubt) { + const subtreeInfo = await SubtreeInfos.createFromBuffer( + subtreeData, + implicitTiling, + subtreeResourceResolver + ); + return subtreeInfo; + } + + const message = + `Subtree data from URI ${subtreeUri} that was created from ` + + `template URI ${implicitTiling.subtrees.uri} for coordinates ` + + `${coordinates} did neither contain JSON nor binary subtree data`; + throw new ImplicitTilingError(message); + } +} diff --git a/src/traversal/ImplicitTraversedTile.ts b/src/traversal/ImplicitTraversedTile.ts new file mode 100644 index 00000000..a4bf5553 --- /dev/null +++ b/src/traversal/ImplicitTraversedTile.ts @@ -0,0 +1,356 @@ +import { defined } from "../base/defined"; + +import { ResourceResolver } from "../io/ResourceResolver"; + +import { TraversedTile } from "./TraversedTile"; +import { ImplicitTileTraversal } from "./ImplicitTileTraversal"; + +import { Tile } from "../structure/Tile"; +import { Content } from "../structure/Content"; +import { MetadataEntity } from "../structure/MetadataEntity"; +import { TileImplicitTiling } from "../structure/TileImplicitTiling"; + +import { BoundingVolumeDerivation } from "./cesium/BoundingVolumeDerivation"; + +import { TreeCoordinates } from "../implicitTiling/TreeCoordinates"; +import { SubtreeInfo } from "../implicitTiling/SubtreeInfo"; +import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; +import { ImplicitTilings } from "../implicitTiling/ImplicitTilings"; + +/** + * An implementation of a `TraversedTile` that represents a tile + * within an implicit tileset during its traversal. + * + * @internal + */ +export class ImplicitTraversedTile implements TraversedTile { + /** + * The `TileImplicitTiling` that this tile belongs to + */ + private readonly _implicitTiling: TileImplicitTiling; + + /** + * The `ResourceResolver` that will be used for loading + * subtree files. + */ + private readonly _resourceResolver: ResourceResolver; + + /** + * The tile that corresponds to the root tile from the + * tileset JSON (i.e. the one that contained the + * `TileImplicitTiling` object) + */ + private readonly _root: TraversedTile; + + /** + * A JSON-path like path identifying this tile + */ + private readonly _path: string; + + /** + * The `SubtreeInfo` object that will be used for accessing + * the availability information for the subtree that this + * tile belongs to. + */ + private readonly _subtreeInfo: SubtreeInfo; + + /** + * The global level of this tile. This refers to the + * root of the tileset. + */ + private readonly _globalLevel: number; + + /** + * The global coordinate of this tile within the implicit tileset. + */ + private readonly _globalCoordinate: TreeCoordinates; + + /** + * The root coordinate of the subtree that this tile belongs + * to, within the whole implicit tileset. + */ + private readonly _rootCoordinate: TreeCoordinates; + + /** + * The local coordinate of this tile within the subtree that + * starts at the `_rootCoordinate` + */ + private readonly _localCoordinate: TreeCoordinates; + + /** + * The parent tile + */ + private readonly _parent: TraversedTile; + + constructor( + implicitTiling: TileImplicitTiling, + resourceResolver: ResourceResolver, + root: TraversedTile, + path: string, + subtreeInfo: SubtreeInfo, + globalLevel: number, + globalCoordinate: TreeCoordinates, + rootCoordinate: TreeCoordinates, + localCoordinate: TreeCoordinates, + parent: TraversedTile + ) { + this._implicitTiling = implicitTiling; + this._resourceResolver = resourceResolver; + this._root = root; + this._path = path; + this._subtreeInfo = subtreeInfo; + this._globalLevel = globalLevel; + this._globalCoordinate = globalCoordinate; + this._rootCoordinate = rootCoordinate; + this._localCoordinate = localCoordinate; + this._parent = parent; + } + + asTile(): Tile { + // TODO The bounding volume and geometric error + // may be overridden via semantics! + const rootTile = this._root.asTile(); + + const boundingVolume = BoundingVolumeDerivation.deriveBoundingVolume( + rootTile.boundingVolume, + this._globalCoordinate.toArray() + ); + if (!boundingVolume) { + // The bounding volume neither contained a region nor a box. + // This should have been detected by previous validation. + throw new ImplicitTilingError("Could not subdivide bounding volume"); + } + const level = this._globalCoordinate.level; + const geometricError = rootTile.geometricError / Math.pow(2, level); + + const viewerRequestVolume = rootTile.viewerRequestVolume; + const refine = rootTile.refine; + const transform = undefined; + const metadata = undefined; + const contents = this.getContents(); + const implicitTiling = undefined; + const extensions = undefined; + const extras = undefined; + + return { + boundingVolume: boundingVolume, + viewerRequestVolume: viewerRequestVolume, + geometricError: geometricError, + refine: refine, + transform: transform, + metadata: metadata, + contents: contents, + implicitTiling: implicitTiling, + extensions: extensions, + extras: extras, + }; + } + + get path(): string { + return this._path; + } + get level(): number { + return this._globalLevel; + } + getLocalCoordinate(): TreeCoordinates { + return this._localCoordinate; + } + + getGlobalCoordinate(): TreeCoordinates { + return this._globalCoordinate; + } + + getParent(): TraversedTile | undefined { + return this._parent; + } + + async getChildren(): Promise { + const localLevel = this._localCoordinate.level; + if (localLevel === this._implicitTiling.subtreeLevels - 1) { + const children = await this.createNextSubtreeLevelChildren(); + return children; + } + const children = await this.createDirectChildren(); + return children; + } + + /** + * Creates the children for this tile at which a new subtree starts. + * + * This assumes that this tile is in the last level of the subtree + * that it belongs to. This method will create one child tile for + * each available child subtree. These children will be the "local + * roots" of their respective subtree. + * + * @returns The children + * @throws ImplicitTilingError If the input data was invalid + */ + private async createNextSubtreeLevelChildren(): Promise { + const traversedChildren = []; + const localChildCoordinates = this._localCoordinate.children(); + for (const localChildCoordinate of localChildCoordinates) { + const globalChildCoordinate = ImplicitTilings.globalizeCoordinates( + this._implicitTiling, + this._rootCoordinate, + localChildCoordinate + ); + const childSubtreeAvailability = + this._subtreeInfo.getChildSubtreeAvailabilityInfo(); + const childSubtreeAvailable = childSubtreeAvailability.isAvailable( + localChildCoordinate.toIndexInLevel() + ); + if (childSubtreeAvailable) { + const childSubtreeInfo = await ImplicitTileTraversal.resolveSubtreeInfo( + this._implicitTiling, + this._resourceResolver, + globalChildCoordinate + ); + const childLocalCoordinate = ImplicitTilings.createRootCoordinates( + this._implicitTiling + ); + // The path is composed from the path of the root and the string + // representation of the global coordinates of the child + const coordinateString = ImplicitTilings.createString( + globalChildCoordinate + ); + const childPath = `${this._root.path}/${coordinateString}`; + + const traversedChild = new ImplicitTraversedTile( + this._implicitTiling, + this._resourceResolver, + this._root, + childPath, + childSubtreeInfo, + this._globalLevel + 1, + globalChildCoordinate, + globalChildCoordinate, + childLocalCoordinate, + this + ); + traversedChildren.push(traversedChild); + } + } + return traversedChildren; + } + + /** + * Creates the children for this tile that are still within the same subtree. + * + * This assumes that this tile is **NOT** in the last level of the subtree. + * It will return all children that are marked as available, via the + * tile availability information in the subtree that this tile belongs to. + * + * @returns The children + * @throws ImplicitTilingError If the input data was invalid + */ + private async createDirectChildren(): Promise { + const tileAvailabilityInfo = this._subtreeInfo.getTileAvailabilityInfo(); + const localChildCoordinates = this._localCoordinate.children(); + const traversedChildren = []; + for (const localChildCoordinate of localChildCoordinates) { + const available = tileAvailabilityInfo.isAvailable( + localChildCoordinate.toIndex() + ); + if (available) { + const globalChildCoordinate = ImplicitTilings.globalizeCoordinates( + this._implicitTiling, + this._rootCoordinate, + localChildCoordinate + ); + + // The path is composed from the path of the root and the string + // representation of the global coordinates of the child + const coordinateString = ImplicitTilings.createString( + globalChildCoordinate + ); + const childPath = `${this._root.path}/${coordinateString}`; + + const traversedChild = new ImplicitTraversedTile( + this._implicitTiling, + this._resourceResolver, + this._root, + childPath, + this._subtreeInfo, + this._globalLevel + 1, + globalChildCoordinate, + this._rootCoordinate, + localChildCoordinate, + this + ); + traversedChildren.push(traversedChild); + } + } + return traversedChildren; + } + + getContents(): Content[] { + const contents = []; + const contentAvailabilityInfos = + this._subtreeInfo.getContentAvailabilityInfos(); + for (const contentAvailabilityInfo of contentAvailabilityInfos) { + const available = contentAvailabilityInfo.isAvailable( + this._localCoordinate.toIndex() + ); + if (available) { + // TODO The existence of the root content URI should + // have been validated. So this could also throw + // an error if the template URI is not found. + const templateUri = this._root.asTile().content?.uri; + if (defined(templateUri)) { + const contentUri = ImplicitTilings.substituteTemplateUri( + this._implicitTiling.subdivisionScheme, + templateUri, + this._globalCoordinate + ); + // TODO Check semantics! + const content: Content = { + boundingVolume: undefined, + uri: contentUri, + metadata: undefined, + group: undefined, + }; + contents.push(content); + } + } + } + return contents; + } + + getSubtreeUri(): string | undefined { + const localCoordinate = this._localCoordinate; + if (localCoordinate.level === 0) { + const globalCoordinate = this._globalCoordinate; + const implicitTiling = this._implicitTiling; + const subtreeUri = ImplicitTilings.substituteTemplateUri( + implicitTiling.subdivisionScheme, + implicitTiling.subtrees.uri, + globalCoordinate + ); + return subtreeUri; + } + return undefined; + } + + getImplicitTiling(): TileImplicitTiling | undefined { + const localCoordinate = this._localCoordinate; + if (localCoordinate.level === 0) { + return this._implicitTiling; + } + } + + getMetadata(): MetadataEntity | undefined { + return undefined; + } + + // TODO For debugging + toString = (): string => { + return ( + `ImplicitTraversedTile, ` + + `level ${this._globalLevel}, ` + + `global: ${this._globalCoordinate}, ` + + `root: ${this._rootCoordinate}, ` + + `local: ${this._localCoordinate}` + //`path ${this.path}` + ); + }; +} diff --git a/src/traversal/TilesetTraverser.ts b/src/traversal/TilesetTraverser.ts new file mode 100644 index 00000000..fb3ac562 --- /dev/null +++ b/src/traversal/TilesetTraverser.ts @@ -0,0 +1,75 @@ +import { ResourceResolver } from "../io/ResourceResolver"; + +import { Tileset } from "../structure/Tileset"; +import { Schema } from "../structure/Metadata/Schema"; + +import { TraversedTile } from "./TraversedTile"; +import { ExplicitTraversedTile } from "./ExplicitTraversedTile"; +import { TraversalCallback } from "./TraversalCallback"; +import { DeveloperError } from "../base/DeveloperError"; + +/** + * A class that can traverse the tiles of a tileset. + * + * @internal + */ +export class TilesetTraverser { + /** + * Traverses the tiles in the given tileset. + * + * This will traverse the tiles of the given tileset, starting + * at the root. It will pass all tiles to the given callback, + * as `TraversedTile` instances. + * + * @param tileset - The `Tileset` + * @param schema - The schema from the `tileset.schema` or the + * `tileset.schemaUri`. If this is defined, then it is assumed + * to be a valid schema definition. + * @param resourceResolver - The `ResourceResolver` that is used to + * resolve resources for implicit tilesets (subtree files) + * @param traversalCallback - The `TraversalCallback` + * @param depthFirst - Whether the traversal should be depth-first + * @returns A Promise that resolves when the traversal finished + */ + static async traverse( + tileset: Tileset, + schema: Schema | undefined, + resourceResolver: ResourceResolver, + traversalCallback: TraversalCallback, + depthFirst: boolean + ): Promise { + const root = tileset.root; + if (!root) { + return; + } + const stack: TraversedTile[] = []; + + const traversedRoot = new ExplicitTraversedTile( + root, + "/root", + 0, + undefined, + schema, + resourceResolver + ); + stack.push(traversedRoot); + + while (stack.length > 0) { + const traversedTile = depthFirst ? stack.pop() : stack.shift(); + if (!traversedTile) { + // This cannot happen, but TypeScript does not know this: + throw new DeveloperError("Empty stack during traversal"); + } + const traverseChildren = await traversalCallback(traversedTile); + + if (traverseChildren) { + const children = await traversedTile.getChildren(); + const length = children.length; + for (let i = 0; i < length; i++) { + const traversedChild = children[i]; + stack.push(traversedChild); + } + } + } + } +} diff --git a/src/traversal/TraversalCallback.ts b/src/traversal/TraversalCallback.ts new file mode 100644 index 00000000..cf27b0f9 --- /dev/null +++ b/src/traversal/TraversalCallback.ts @@ -0,0 +1,20 @@ +import { TraversedTile } from "./TraversedTile"; + +/** + * An interface with a function that will be called by + * a `TilesetTraverser` + * + * @internal + */ +export interface TraversalCallback { + /** + * Will be called with each traversed tile during the + * traversal process. + * + * @param traversedTile - The `TraversedTile` instance + * @returns A promise that resolves to `true` if the + * traversal should continue, and to `false` if the + * traversal should stop. + */ + (traversedTile: TraversedTile): Promise; +} diff --git a/src/traversal/TraversedTile.ts b/src/traversal/TraversedTile.ts new file mode 100644 index 00000000..8a3a1b73 --- /dev/null +++ b/src/traversal/TraversedTile.ts @@ -0,0 +1,92 @@ +import { Tile } from "../structure/Tile"; +import { Content } from "../structure/Content"; +import { MetadataEntity } from "../structure/MetadataEntity"; +import { TileImplicitTiling } from "../structure/TileImplicitTiling"; + +/** + * An interface that summarizes context information for + * a tile during traversal. + * + * @internal + */ +export interface TraversedTile { + /** + * Returns a `Tile` object that contains the "JSON"-representation + * of the tile. This is just a plain data structure corresponding + * the tile. + * + * Values that may be overridden (for example, via metadata semantics) + * are already substituted in the returned object. + * + * This means that the return value of this method reflects the + * actual tile if and only if the underlying metadata was valid. + * + * @returns A `Tile` with information about this traversed tile + * @throws ImplicitTilingError If the representation of this traversed + * tile could not be created due to invalid input structures. + */ + asTile(): Tile; + + /** + * Returns the level of the tile in the traversed hierarchy, with + * 0 being the root tile. + * + * @returns The level + */ + get level(): number; + + /** + * Returns a path that identifies this tile within the hierarchy. + * + * This resembles a JSON path. But for cases like implicit tilesets, + * it may contain elements that are not part of the JSONPath format. + * It may therefore only be used as a semi-human-readable identifier. + * + * @returns The path + */ + get path(): string; + + /** + * Returns the parent of this tile, or `undefined` if this is the + * root tile. + * + * @returns The parent tile + */ + getParent(): TraversedTile | undefined; + + /** + * Returns the children of this tile. + * + * For external tilesets or implicit tiling, this may have to + * resolve external resources, and therefore, returns a promise + * that is resolved when the required child tiles are available. + * + * @returns The children + * @throws ImplicitTilingError When there was an error while + * trying to obtain the traversed children. This may be caused + * by invalid input structures, or when a required resource + * (like a subtree file or one of its buffers) could not + * be resolved. + */ + getChildren(): Promise; + + /** + * Returns the `Content` objects of the tile. + * + * This is either an empty array (when the tile does not have + * content), or a single-element array (when the tile has a + * single `tile.content` object), or an array that resembles + * the `tile.contents` array. + * + * @returns The contents + */ + getContents(): Content[]; + + // TODO Some information has to be exposed here solely + // for the validation. This should preferably not be + // visible in this interface. The traversal might be + // refactored to hide this information here. + getSubtreeUri(): string | undefined; + getImplicitTiling(): TileImplicitTiling | undefined; + getMetadata(): MetadataEntity | undefined; +} diff --git a/src/traversal/cesium/BoundingVolumeDerivation.ts b/src/traversal/cesium/BoundingVolumeDerivation.ts new file mode 100644 index 00000000..b8522a38 --- /dev/null +++ b/src/traversal/cesium/BoundingVolumeDerivation.ts @@ -0,0 +1,345 @@ +import { Cartesian3, Math as CesiumMath, Matrix3, Rectangle } from "cesium"; +import { defined } from "../../base/defined"; + +import { BoundingVolume } from "../../structure/BoundingVolume"; + +import { HilbertOrder } from "./HilbertOrder"; +import { S2Cell } from "./S2Cell"; + +import { BoundingVolumeS2 } from "../../structure/extensions/BoundingVolumeS2"; + +/** + * Methods to derive bounding volumes of implicit tiles. + * + * Largely ported from CesiumJS Implicit3DTileContent.js + * + * @internal + */ +export class BoundingVolumeDerivation { + /** + * Given the coordinates of a tile, derive its bounding volume from the root. + * + * @param rootBoundingVolume - The root bounding volume + * @param implicitCoordinates - The coordinates of the child tile, as an + * array [level,x,y] for quadtrees or [level,x,y,z] for octrees. + * @returns An object containing the JSON for a bounding volume + */ + static deriveBoundingVolume( + rootBoundingVolume: BoundingVolume, + implicitCoordinates: number[] + ): BoundingVolume | undefined { + const level = implicitCoordinates[0]; + const x = implicitCoordinates[1]; + const y = implicitCoordinates[2]; + const z = + implicitCoordinates.length > 3 ? implicitCoordinates[3] : undefined; + + if ( + rootBoundingVolume.extensions && + BoundingVolumeDerivation.hasExtension( + rootBoundingVolume, + "3DTILES_bounding_volume_S2" + ) + ) { + const extensions = rootBoundingVolume.extensions; + const s2Object = extensions["3DTILES_bounding_volume_S2"]; + const boundingVolumeS2 = s2Object as BoundingVolumeS2; + const childBoundingVolumeS2 = + BoundingVolumeDerivation.deriveBoundingVolumeS2( + boundingVolumeS2, + level, + x, + y, + z + ); + return { + extensions: { + "3DTILES_bounding_volume_S2": childBoundingVolumeS2, + }, + }; + } + + if (rootBoundingVolume.region) { + const childRegion = BoundingVolumeDerivation.deriveBoundingRegion( + rootBoundingVolume.region, + level, + x, + y, + z + ); + + return { + region: childRegion, + }; + } + if (rootBoundingVolume.box) { + const childBox = BoundingVolumeDerivation.deriveBoundingBox( + rootBoundingVolume.box, + level, + x, + y, + z + ); + + return { + box: childBox, + }; + } + } + + /** + * Check if a specific extension is present on a JSON object. This can be used + * for either 3D Tiles extensions or glTF extensions + * @param json - The JSON object + * @param extensionName - The name of the extension, e.g. '3DTILES_implicit_tiling' + * @returns True if the extension is present + * @internal + */ + private static hasExtension(json: any, extensionName: string): boolean { + return json && json.extensions && json.extensions[extensionName]; + } + + // See https://github.com/CesiumGS/cesium/issues/10801 + private static Matrix3_multiplyByScale = function ( + matrix: Matrix3, + scale: Cartesian3, + result: Matrix3 + ) { + const array = new Array(9); + array[0] = matrix[0] * scale.x; + array[1] = matrix[1] * scale.x; + array[2] = matrix[2] * scale.x; + array[3] = matrix[3] * scale.y; + array[4] = matrix[4] * scale.y; + array[5] = matrix[5] * scale.y; + array[6] = matrix[6] * scale.z; + array[7] = matrix[7] * scale.z; + array[8] = matrix[8] * scale.z; + Matrix3.fromArray(array, 0, result); + + return result; + }; + + static scratchScaleFactors = new Cartesian3(); + static scratchRootCenter = new Cartesian3(); + static scratchCenter = new Cartesian3(); + static scratchHalfAxes = new Matrix3(); + /** + * Derive a bounding volume for a descendant tile (child, grandchild, etc.), + * assuming a quadtree or octree implicit tiling scheme. The (level, x, y, [z]) + * coordinates are given to select the descendant tile and compute its position + * and dimensions. + *

+ * If z is present, octree subdivision is used. Otherwise, quadtree subdivision + * is used. Quadtrees are always divided at the midpoint of the the horizontal + * dimensions, i.e. (x, y), leaving the z axis unchanged. + *

+ *

+ * This computes the child volume directly from the root bounding volume rather + * than recursively subdividing to minimize floating point error. + *

+ * + * @param rootBox - An array of 12 numbers representing the bounding box of the root tile + * @param level - The level of the descendant tile relative to the root implicit tile + * @param x - The x coordinate of the descendant tile + * @param y - The y coordinate of the descendant tile + * @param z - The z coordinate of the descendant tile (octree only) + * @returns An array of 12 numbers representing the bounding box of the descendant tile. + */ + private static deriveBoundingBox( + rootBox: number[], + level: number, + x: number, + y: number, + z: number | undefined + ): number[] { + if (level === 0) { + return rootBox; + } + + const rootCenter = Cartesian3.unpack( + rootBox, + 0, + BoundingVolumeDerivation.scratchRootCenter + ); + const rootHalfAxes = Matrix3.unpack( + rootBox, + 3, + BoundingVolumeDerivation.scratchHalfAxes + ); + + const tileScale = Math.pow(2, -level); + const modelSpaceX = -1 + (2 * x + 1) * tileScale; + const modelSpaceY = -1 + (2 * y + 1) * tileScale; + + let modelSpaceZ = 0; + const scaleFactors = Cartesian3.fromElements( + tileScale, + tileScale, + 1, + BoundingVolumeDerivation.scratchScaleFactors + ); + + if (defined(z)) { + modelSpaceZ = -1 + (2 * z + 1) * tileScale; + scaleFactors.z = tileScale; + } + + let center = Cartesian3.fromElements( + modelSpaceX, + modelSpaceY, + modelSpaceZ, + BoundingVolumeDerivation.scratchCenter + ); + center = Matrix3.multiplyByVector( + rootHalfAxes, + center, + BoundingVolumeDerivation.scratchCenter + ); + center = Cartesian3.add( + center, + rootCenter, + BoundingVolumeDerivation.scratchCenter + ); + + let halfAxes = Matrix3.clone(rootHalfAxes); + halfAxes = BoundingVolumeDerivation.Matrix3_multiplyByScale( + halfAxes, + scaleFactors, + halfAxes + ); + + const childBox = new Array(12); + Cartesian3.pack(center, childBox); + Matrix3.pack(halfAxes, childBox, 3); + return childBox; + } + + static scratchRectangle = new Rectangle(); + /** + * Derive a bounding volume for a descendant tile (child, grandchild, etc.), + * assuming a quadtree or octree implicit tiling scheme. The (level, x, y, [z]) + * coordinates are given to select the descendant tile and compute its position + * and dimensions. + *

+ * If z is present, octree subdivision is used. Otherwise, quadtree subdivision + * is used. Quadtrees are always divided at the midpoint of the the horizontal + * dimensions, i.e. (mid_longitude, mid_latitude), leaving the height values + * unchanged. + *

+ *

+ * This computes the child volume directly from the root bounding volume rather + * than recursively subdividing to minimize floating point error. + *

+ * @param rootRegion - An array of 6 numbers representing the root implicit tile + * @param level - The level of the descendant tile relative to the root implicit tile + * @param x - The x coordinate of the descendant tile + * @param y - The x coordinate of the descendant tile + * @param z - The z coordinate of the descendant tile (octree only) + * @returns An array of 6 numbers representing the bounding region of the descendant tile + * @internal + */ + private static deriveBoundingRegion( + rootRegion: number[], + level: number, + x: number, + y: number, + z: number | undefined + ): number[] { + if (level === 0) { + return rootRegion.slice(); + } + + const rectangle = Rectangle.unpack( + rootRegion, + 0, + BoundingVolumeDerivation.scratchRectangle + ); + const rootMinimumHeight = rootRegion[4]; + const rootMaximumHeight = rootRegion[5]; + const tileScale = Math.pow(2, -level); + + const childWidth = tileScale * rectangle.width; + const west = CesiumMath.negativePiToPi(rectangle.west + x * childWidth); + const east = CesiumMath.negativePiToPi(west + childWidth); + + const childHeight = tileScale * rectangle.height; + const south = CesiumMath.negativePiToPi(rectangle.south + y * childHeight); + const north = CesiumMath.negativePiToPi(south + childHeight); + + // Height is only subdivided for octrees; It remains constant for quadtrees. + let minimumHeight = rootMinimumHeight; + let maximumHeight = rootMaximumHeight; + if (defined(z)) { + const childThickness = + tileScale * (rootMaximumHeight - rootMinimumHeight); + minimumHeight += z * childThickness; + maximumHeight = minimumHeight + childThickness; + } + + return [west, south, east, north, minimumHeight, maximumHeight]; + } + + /** + * Derive a bounding volume for a descendant tile (child, grandchild, etc.), + * assuming a quadtree or octree implicit tiling scheme. The (level, x, y, [z]) + * coordinates are given to select the descendant tile and compute its position + * and dimensions. + *

+ * If z is present, octree subdivision is used. Otherwise, quadtree subdivision + * is used. Quadtrees are always divided at the midpoint of the the horizontal + * dimensions, i.e. (x, y), leaving the z axis unchanged. + *

+ * + * @param level - The level of the descendant tile relative to the root implicit tile + * @param x - The x coordinate of the descendant tile + * @param y - The y coordinate of the descendant tile + * @param z - The z coordinate of the descendant tile (octree only) + * @returns The new bounding volume + * @internal + */ + private static deriveBoundingVolumeS2( + boundingVolumeS2: BoundingVolumeS2, + level: number, + x: number, + y: number, + z: number | undefined + ): BoundingVolumeS2 { + if (level === 0) { + return boundingVolumeS2; + } + // Extract the first 3 face bits from the 64-bit S2 cell ID. + // eslint-disable-next-line no-undef + const baseCellId = S2Cell.getIdFromToken(boundingVolumeS2.token); + const face = Number(baseCellId >> BigInt(61)); + // The Hilbert curve is rotated for the "odd" faces on the S2 Earthcube. + // See http://s2geometry.io/devguide/img/s2cell_global.jpg + const position = + face % 2 === 0 + ? HilbertOrder.encode2D(level, x, y) + : HilbertOrder.encode2D(level, y, x); + const cellId = S2Cell.fromFacePositionLevel(face, BigInt(position), level); + + let minHeight, maxHeight; + if (defined(z)) { + // In CesiumJS, this information was computed from + // the "childIndex" that was passed along, i.e. this + // is equivalent to the condition "childIndex < 4" + const lower = (z & 1) === 0; + + const midpointHeight = + (boundingVolumeS2.maximumHeight + boundingVolumeS2.minimumHeight) / 2; + minHeight = lower ? boundingVolumeS2.minimumHeight : midpointHeight; + maxHeight = lower ? midpointHeight : boundingVolumeS2.maximumHeight; + } else { + minHeight = boundingVolumeS2.minimumHeight; + maxHeight = boundingVolumeS2.maximumHeight; + } + + return { + token: S2Cell.getTokenFromId(cellId), + minimumHeight: minHeight, + maximumHeight: maxHeight, + }; + } +} diff --git a/src/traversal/cesium/HilbertOrder.ts b/src/traversal/cesium/HilbertOrder.ts new file mode 100644 index 00000000..f8cd53a9 --- /dev/null +++ b/src/traversal/cesium/HilbertOrder.ts @@ -0,0 +1,104 @@ +// Ported from https://github.com/CesiumGS/cesium/blob/4b333bc145fa9f7aed0c7ad7e0f46cb001a94ddd/Source/Core/HilbertOrder.js + +import { DeveloperError } from "../../base/DeveloperError"; + +/** + * Hilbert Order helper functions. + */ +export class HilbertOrder { + /** + * Computes the Hilbert index at the given level from 2D coordinates. + * + * @param level - The level of the curve + * @param x - The X coordinate + * @param y - The Y coordinate + * @returns The Hilbert index. + */ + static encode2D(level: number, x: number, y: number): bigint { + const n = Math.pow(2, level); + if (level < 1) { + throw new DeveloperError("Hilbert level cannot be less than 1."); + } + if (x < 0 || x >= n || y < 0 || y >= n) { + throw new DeveloperError("Invalid coordinates for given level."); + } + + const p = { + x: x, + y: y, + }; + let rx, + ry, + s, + index = BigInt(0); + + for (s = n / 2; s > 0; s /= 2) { + rx = (p.x & s) > 0 ? 1 : 0; + ry = (p.y & s) > 0 ? 1 : 0; + index += BigInt(((3 * rx) ^ ry) * s * s); + HilbertOrder.rotate(n, p, rx, ry); + } + + return index; + } + + /** + * Computes the 2D coordinates from the Hilbert index at the given level. + * + * @param level - The level of the curve + * @param index - The Hilbert index + * @returns An array containing the 2D coordinates ([x, y]) corresponding to the Morton index. + * @internal + */ + static decode2D(level: number, index: bigint): number[] { + if (level < 1) { + throw new DeveloperError("Hilbert level cannot be less than 1."); + } + if (index < BigInt(0) || index >= BigInt(Math.pow(4, level))) { + throw new DeveloperError( + "Hilbert index exceeds valid maximum for given level." + ); + } + + const n = Math.pow(2, level); + const p = { + x: 0, + y: 0, + }; + let rx, ry, s, t; + + for (s = 1, t = index; s < n; s *= 2) { + rx = 1 & Number(t / BigInt(2)); + ry = 1 & Number(t ^ BigInt(rx)); + HilbertOrder.rotate(s, p, rx, ry); + p.x += s * rx; + p.y += s * ry; + t /= BigInt(4); + } + + return [p.x, p.y]; + } + + /** + * @internal + */ + private static rotate( + n: number, + p: { x: number; y: number }, + rx: number, + ry: number + ) { + if (ry !== 0) { + return; + } + + if (rx === 1) { + p.x = n - 1 - p.x; + p.y = n - 1 - p.y; + } + + const t = p.x; + p.x = p.y; + p.y = t; + } +} diff --git a/src/traversal/cesium/S2Cell.ts b/src/traversal/cesium/S2Cell.ts new file mode 100644 index 00000000..bcb2b023 --- /dev/null +++ b/src/traversal/cesium/S2Cell.ts @@ -0,0 +1,106 @@ +// Relevant functions ported from https://github.com/CesiumGS/cesium/blob/4b333bc145fa9f7aed0c7ad7e0f46cb001a94ddd/Source/Core/S2Cell.js + +import { DeveloperError } from "../../base/DeveloperError"; + +/** + * Functions related to S2 cells + */ +export class S2Cell { + // The maximum level supported within an S2 cell ID. Each level is represented by two bits in the final cell ID + private static readonly S2_MAX_LEVEL = 30; + + // The number of bits in a S2 cell ID used for specifying the position along the Hilbert curve + private static readonly S2_POSITION_BITS = 2 * S2Cell.S2_MAX_LEVEL + 1; + + // Lookup table for getting trailing zero bits. + // https://graphics.stanford.edu/~seander/bithacks.html + private static readonly Mod67BitPosition = [ + 64, 0, 1, 39, 2, 15, 40, 23, 3, 12, 16, 59, 41, 19, 24, 54, 4, 64, 13, 10, + 17, 62, 60, 28, 42, 30, 20, 51, 25, 44, 55, 47, 5, 32, 65, 38, 14, 22, 11, + 58, 18, 53, 63, 9, 61, 27, 29, 50, 43, 46, 31, 37, 21, 57, 52, 8, 26, 49, + 45, 36, 56, 7, 48, 35, 6, 34, 33, 0, + ]; + + /** + * Converts a 64-bit S2 cell ID to an S2 cell token. + * + * @param cellId - The S2 cell ID. + * @returns Returns hexadecimal representation of an S2CellId. + * @internal + */ + static getTokenFromId = function (cellId: bigint): string { + const trailingZeroHexChars = Math.floor( + S2Cell.countTrailingZeroBits(cellId) / 4 + ); + const hexString = cellId.toString(16).replace(/0*$/, ""); + + const zeroString = Array(17 - trailingZeroHexChars - hexString.length).join( + "0" + ); + return zeroString + hexString; + }; + + /** + * Return the number of trailing zeros in number. + * @internal + */ + private static countTrailingZeroBits(x: bigint) { + const index = (-x & x) % BigInt(67); + return S2Cell.Mod67BitPosition[Number(index)]; + } + + /** + * Converts an S2 cell token to a 64-bit S2 cell ID. + * + * @param token - The hexadecimal representation of an S2CellId. Expected to be a valid S2 token. + * @returns Returns the S2 cell ID. + * @internal + */ + static getIdFromToken = function (token: string): bigint { + return BigInt("0x" + token + "0".repeat(16 - token.length)); + }; + + /** + * Creates an S2Cell from its face, position along the Hilbert curve for a given level. + * + * @param face - The root face of S2 this cell is on. Must be in the range [0-5]. + * @param position - The position along the Hilbert curve. Must be in the range [0-4**level). + * @param level - The level of the S2 curve. Must be in the range [0-30]. + * @returns A new S2Cell ID from the given parameters. + * @internal + */ + static fromFacePositionLevel( + face: number, + position: bigint, + level: number + ): bigint { + if (face < 0 || face > 5) { + throw new DeveloperError("Invalid S2 Face (must be within 0-5)"); + } + + if (level < 0 || level > S2Cell.S2_MAX_LEVEL) { + throw new DeveloperError("Invalid level (must be within 0-30)"); + } + if (position < 0 || position >= Math.pow(4, level)) { + throw new DeveloperError("Invalid Hilbert position for level"); + } + + const faceBitString = + (face < 4 ? "0" : "") + (face < 2 ? "0" : "") + face.toString(2); + const positionBitString = position.toString(2); + const positionPrefixPadding = Array( + 2 * level - positionBitString.length + 1 + ).join("0"); + const positionSuffixPadding = Array( + S2Cell.S2_POSITION_BITS - 2 * level + ).join("0"); + + const cellId = BigInt( + `0b${faceBitString}${positionPrefixPadding}${positionBitString}1${ + // Adding the sentinel bit that always follows the position bits. + positionSuffixPadding + }` + ); + return cellId; + } +} From dfa212a1b88c6320cc1fc2b6c53e17ee91771473 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 8 Mar 2023 15:41:31 +0100 Subject: [PATCH 06/26] Removed workaround for CesiumJS bug --- package.json | 2 +- .../cesium/BoundingVolumeDerivation.ts | 40 ++++++------------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 92ddb89f..dd7f3799 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@types/better-sqlite3": "^7.6.2", "archiver": "^5.3.1", "better-sqlite3": "^7.5.3", - "cesium": "^1.102.0", + "cesium": "^1.103.0", "gltf-pipeline": "^4.0.1", "minimist": "^1.2.7", "node-stream-zip": "^1.15.0", diff --git a/src/traversal/cesium/BoundingVolumeDerivation.ts b/src/traversal/cesium/BoundingVolumeDerivation.ts index b8522a38..5ba02939 100644 --- a/src/traversal/cesium/BoundingVolumeDerivation.ts +++ b/src/traversal/cesium/BoundingVolumeDerivation.ts @@ -22,7 +22,10 @@ export class BoundingVolumeDerivation { * @param rootBoundingVolume - The root bounding volume * @param implicitCoordinates - The coordinates of the child tile, as an * array [level,x,y] for quadtrees or [level,x,y,z] for octrees. - * @returns An object containing the JSON for a bounding volume + * @returns An object containing the JSON for a bounding volume, + * or `undefined` if the given bounding volume was of a type from + * which no other bounding volume can be derived (i.e. when it + * was a bounding sphere) */ static deriveBoundingVolume( rootBoundingVolume: BoundingVolume, @@ -85,6 +88,9 @@ export class BoundingVolumeDerivation { box: childBox, }; } + + // Bounding spheres cannot be derived: + return undefined; } /** @@ -99,31 +105,11 @@ export class BoundingVolumeDerivation { return json && json.extensions && json.extensions[extensionName]; } - // See https://github.com/CesiumGS/cesium/issues/10801 - private static Matrix3_multiplyByScale = function ( - matrix: Matrix3, - scale: Cartesian3, - result: Matrix3 - ) { - const array = new Array(9); - array[0] = matrix[0] * scale.x; - array[1] = matrix[1] * scale.x; - array[2] = matrix[2] * scale.x; - array[3] = matrix[3] * scale.y; - array[4] = matrix[4] * scale.y; - array[5] = matrix[5] * scale.y; - array[6] = matrix[6] * scale.z; - array[7] = matrix[7] * scale.z; - array[8] = matrix[8] * scale.z; - Matrix3.fromArray(array, 0, result); - - return result; - }; + private static readonly scratchScaleFactors = new Cartesian3(); + private static readonly scratchRootCenter = new Cartesian3(); + private static readonly scratchCenter = new Cartesian3(); + private static readonly scratchHalfAxes = new Matrix3(); - static scratchScaleFactors = new Cartesian3(); - static scratchRootCenter = new Cartesian3(); - static scratchCenter = new Cartesian3(); - static scratchHalfAxes = new Matrix3(); /** * Derive a bounding volume for a descendant tile (child, grandchild, etc.), * assuming a quadtree or octree implicit tiling scheme. The (level, x, y, [z]) @@ -203,7 +189,7 @@ export class BoundingVolumeDerivation { ); let halfAxes = Matrix3.clone(rootHalfAxes); - halfAxes = BoundingVolumeDerivation.Matrix3_multiplyByScale( + halfAxes = Matrix3.multiplyByScale( halfAxes, scaleFactors, halfAxes @@ -215,7 +201,7 @@ export class BoundingVolumeDerivation { return childBox; } - static scratchRectangle = new Rectangle(); + private static readonly scratchRectangle = new Rectangle(); /** * Derive a bounding volume for a descendant tile (child, grandchild, etc.), * assuming a quadtree or octree implicit tiling scheme. The (level, x, y, [z]) From 70e8964dc3876c4886b8970e6c4833a277b22cb9 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 9 Mar 2023 00:20:20 +0100 Subject: [PATCH 07/26] Add an await that may or may not be necessary. --- src/packages/TilesetTarget3tz.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/TilesetTarget3tz.ts b/src/packages/TilesetTarget3tz.ts index 3cf4f07b..28debca7 100644 --- a/src/packages/TilesetTarget3tz.ts +++ b/src/packages/TilesetTarget3tz.ts @@ -111,7 +111,7 @@ export class TilesetTarget3tz implements TilesetTarget { // Create the index data, and add it as the LAST entry of the ZIP const indexData = this.indexBuilder.createBuffer(); this.archive.append(indexData, { name: "@3dtilesIndex1@" }); - this.archive.finalize(); + await this.archive.finalize(); await this.finishedPromise; this.finishedPromise = undefined; From 79077610e5f632f42698c4ca404d481ebd8c5d24 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 9 Mar 2023 16:23:46 +0100 Subject: [PATCH 08/26] Minor linting and formatting fixes --- src/implicitTiling/OctreeCoordinates.ts | 3 +-- src/implicitTiling/QuadtreeCoordinates.ts | 3 +-- src/traversal/cesium/BoundingVolumeDerivation.ts | 6 +----- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/implicitTiling/OctreeCoordinates.ts b/src/implicitTiling/OctreeCoordinates.ts index 5f6c5952..c7288c1e 100644 --- a/src/implicitTiling/OctreeCoordinates.ts +++ b/src/implicitTiling/OctreeCoordinates.ts @@ -62,8 +62,7 @@ export class OctreeCoordinates implements TreeCoordinates { descendants( maxLevelInclusive: number, - // eslint-disable-next-line @typescript-eslint/no-inferrable-types - depthFirst: boolean = false + depthFirst: boolean ): IterableIterator { const queue: OctreeCoordinates[] = [this]; const result = { diff --git a/src/implicitTiling/QuadtreeCoordinates.ts b/src/implicitTiling/QuadtreeCoordinates.ts index 9cbc7e78..790a2445 100644 --- a/src/implicitTiling/QuadtreeCoordinates.ts +++ b/src/implicitTiling/QuadtreeCoordinates.ts @@ -50,8 +50,7 @@ export class QuadtreeCoordinates implements TreeCoordinates { descendants( maxLevelInclusive: number, - // eslint-disable-next-line @typescript-eslint/no-inferrable-types - depthFirst: boolean = false + depthFirst: boolean ): IterableIterator { const queue: QuadtreeCoordinates[] = [this]; const result = { diff --git a/src/traversal/cesium/BoundingVolumeDerivation.ts b/src/traversal/cesium/BoundingVolumeDerivation.ts index 5ba02939..5175baf2 100644 --- a/src/traversal/cesium/BoundingVolumeDerivation.ts +++ b/src/traversal/cesium/BoundingVolumeDerivation.ts @@ -189,11 +189,7 @@ export class BoundingVolumeDerivation { ); let halfAxes = Matrix3.clone(rootHalfAxes); - halfAxes = Matrix3.multiplyByScale( - halfAxes, - scaleFactors, - halfAxes - ); + halfAxes = Matrix3.multiplyByScale(halfAxes, scaleFactors, halfAxes); const childBox = new Array(12); Cartesian3.pack(center, childBox); From d01a0ce4e4c995fafbcf03da2304c9bbcc513040 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 9 Mar 2023 16:27:48 +0100 Subject: [PATCH 09/26] Preparing subtree metadata and override mechanism Add BinarySubtreeData as the raw (but resolved) data for one subtree. Introduced SubtreeModel (combining SubtreeInfo and SubtreeMetadataModel) to be created from the binary subtree data. Differentiated between "raw" tile data and "final" tile data for traversed tiles, with the latter including the overrides. Minor refactoring for metadata implementations. --- demos/TraversalDemo.ts | 93 +++++---- src/implicitTiling/BinarySubtreeData.ts | 34 ++++ .../BinarySubtreeDataResolver.ts | 145 ++++++++++++++ src/implicitTiling/SubtreeInfo.ts | 38 ++-- src/implicitTiling/SubtreeInfos.ts | 132 +++++-------- src/metadata/{binary => }/PropertyModel.ts | 0 src/metadata/PropertyTableModel.ts | 55 ++++++ .../binary/BinaryMetadataEntityModel.ts | 3 +- ...pertyModels.ts => BinaryPropertyModels.ts} | 10 +- ...leModel.ts => BinaryPropertyTableModel.ts} | 58 ++---- .../binary/BooleanArrayPropertyModel.ts | 7 +- src/metadata/binary/BooleanPropertyModel.ts | 3 +- .../binary/NumericArrayPropertyModel.ts | 10 +- src/metadata/binary/NumericPropertyModel.ts | 2 +- .../binary/StringArrayPropertyModel.ts | 8 +- src/metadata/binary/StringPropertyModel.ts | 7 +- src/traversal/ExplicitTraversedTile.ts | 149 ++++++-------- ...Traversal.ts => ExplicitTraversedTiles.ts} | 131 +++---------- src/traversal/ImplicitTraversedTile.ts | 61 +++--- src/traversal/MetadataSemanticOverrides.ts | 61 ++++++ src/traversal/SubtreeMetadataModel.ts | 58 ++++++ src/traversal/SubtreeMetadataModels.ts | 181 ++++++++++++++++++ src/traversal/SubtreeModel.ts | 18 ++ src/traversal/SubtreeModels.ts | 163 ++++++++++++++++ src/traversal/TraversedTile.ts | 44 ++++- 25 files changed, 1028 insertions(+), 443 deletions(-) create mode 100644 src/implicitTiling/BinarySubtreeData.ts create mode 100644 src/implicitTiling/BinarySubtreeDataResolver.ts rename src/metadata/{binary => }/PropertyModel.ts (100%) create mode 100644 src/metadata/PropertyTableModel.ts rename src/metadata/binary/{PropertyModels.ts => BinaryPropertyModels.ts} (98%) rename src/metadata/binary/{PropertyTableModel.ts => BinaryPropertyTableModel.ts} (68%) rename src/traversal/{ImplicitTileTraversal.ts => ExplicitTraversedTiles.ts} (52%) create mode 100644 src/traversal/MetadataSemanticOverrides.ts create mode 100644 src/traversal/SubtreeMetadataModel.ts create mode 100644 src/traversal/SubtreeMetadataModels.ts create mode 100644 src/traversal/SubtreeModel.ts create mode 100644 src/traversal/SubtreeModels.ts diff --git a/demos/TraversalDemo.ts b/demos/TraversalDemo.ts index 6038029a..ab89eb64 100644 --- a/demos/TraversalDemo.ts +++ b/demos/TraversalDemo.ts @@ -15,55 +15,54 @@ import { TilesetTraverser } from "../src/traversal/TilesetTraverser"; * @returns A promise that resolves with the result or `undefined` */ async function readJsonUnchecked(filePath: string): Promise { - try { - const data = fs.readFileSync(filePath); - if (!data) { - console.error("Could not read " + filePath); - return undefined; - } - const jsonString = data.toString(); - const result = JSON.parse(jsonString); - return result; - } catch (error) { - console.error("Could not parse JSON", error); + try { + const data = fs.readFileSync(filePath); + if (!data) { + console.error("Could not read " + filePath); return undefined; } + const jsonString = data.toString(); + const result = JSON.parse(jsonString); + return result; + } catch (error) { + console.error("Could not parse JSON", error); + return undefined; } - +} async function tilesetTraversalDemo(filePath: string) { - const directory = path.dirname(filePath); - const resourceResolver = - ResourceResolvers.createFileResourceResolver(directory); - const tileset = await readJsonUnchecked(filePath); - // Note: External schemas are not considered here - const schema = tileset.schema; - const depthFirst = false; - console.log("Traversing tileset"); - await TilesetTraverser.traverse( - tileset, - schema, - resourceResolver, - async (traversedTile) => { - const contentUris = traversedTile.getContents().map((c) => c.uri); - const geometricError = traversedTile.asTile().geometricError; - console.log( - ` Traversed tile: ${traversedTile}, ` + - `path: ${traversedTile.path}, ` + - `contents [${contentUris}], ` + - `geometricError ${geometricError}` - ); - return true; - }, - depthFirst - ); - console.log("Traversing tileset DONE"); - } - - async function runDemo() { - const tilesetFile = "../3d-tiles-samples/1.1/SparseImplicitQuadtree/tileset.json"; - await tilesetTraversalDemo(tilesetFile); - } - - runDemo(); - \ No newline at end of file + const directory = path.dirname(filePath); + const resourceResolver = + ResourceResolvers.createFileResourceResolver(directory); + const tileset = await readJsonUnchecked(filePath); + // Note: External schemas are not considered here + const schema = tileset.schema; + const depthFirst = false; + console.log("Traversing tileset"); + await TilesetTraverser.traverse( + tileset, + schema, + resourceResolver, + async (traversedTile) => { + const contentUris = traversedTile.getContents().map((c) => c.uri); + const geometricError = traversedTile.asFinalTile().geometricError; + console.log( + ` Traversed tile: ${traversedTile}, ` + + `path: ${traversedTile.path}, ` + + `contents [${contentUris}], ` + + `geometricError ${geometricError}` + ); + return true; + }, + depthFirst + ); + console.log("Traversing tileset DONE"); +} + +async function runDemo() { + const tilesetFile = + "../3d-tiles-samples/1.1/SparseImplicitQuadtree/tileset.json"; + await tilesetTraversalDemo(tilesetFile); +} + +runDemo(); diff --git a/src/implicitTiling/BinarySubtreeData.ts b/src/implicitTiling/BinarySubtreeData.ts new file mode 100644 index 00000000..507c0f2d --- /dev/null +++ b/src/implicitTiling/BinarySubtreeData.ts @@ -0,0 +1,34 @@ +import { BinaryBufferData } from "../binary/BinaryBufferData"; +import { BinaryBufferStructure } from "../binary/BinaryBufferStructure"; + +import { Subtree } from "../structure/Subtree"; + +/** + * An interface summarizing the binary data that is associated + * with a subtree. + * + * Instances of this interface are created with the + * `BinarySubtreeDataResolver`, which will resolve + * the URIs of external buffers, and create an instance + * of this class that combines all the resolved binary + * buffer data. + * + * @internal + */ +export interface BinarySubtreeData { + /** + * The `Subtree` that this data belongs to + */ + subtree: Subtree; + + /** + * The structure of the binary buffers. These are + * the buffers and buffer views from the subtree. + */ + binaryBufferStructure: BinaryBufferStructure; + + /** + * The resolved binary buffer data. + */ + binaryBufferData: BinaryBufferData; +} diff --git a/src/implicitTiling/BinarySubtreeDataResolver.ts b/src/implicitTiling/BinarySubtreeDataResolver.ts new file mode 100644 index 00000000..426bf2f1 --- /dev/null +++ b/src/implicitTiling/BinarySubtreeDataResolver.ts @@ -0,0 +1,145 @@ +import { Buffers } from "../base/Buffers"; + +import { ResourceResolver } from "../io/ResourceResolver"; + +import { BinaryBufferDataResolver } from "../binary/BinaryBufferDataResolver"; +import { BinaryBufferStructure } from "../binary/BinaryBufferStructure"; +import { BinaryDataError } from "../binary/BinaryDataError"; + +import { Subtree } from "../structure/Subtree"; + +import { BinarySubtreeData } from "./BinarySubtreeData"; +import { ImplicitTilingError } from "./ImplicitTilingError"; + +/** + * A class for resolving the binary data that is associated with + * a subtree. + * + * @internal + */ +export class BinarySubtreeDataResolver { + /** + * Creates a new `BinarySubtreeData` from the given subtree + * that was parsed from a subtree JSON file. + * + * This will resolve all buffer references that are + * contained in the subtree JSON. + * + * @param subtree - The `Subtree` + * @param resourceResolver - The `ResourceResolver` that + * will be used to resolve buffer URIs + * @returns A promise with the `BinarySubtreeData` + * @throws An ImplicitTilingError when there was a buffer without + * a URI (which is not valid when no binary buffer was given), + * or one of the requested buffers could not be resolved. + */ + static async resolveFromJson( + subtree: Subtree, + resourceResolver: ResourceResolver + ): Promise { + const binarySubtreeData = await BinarySubtreeDataResolver.resolveInternal( + subtree, + undefined, + resourceResolver + ); + return binarySubtreeData; + } + + /** + * Creates a new `BinarySubtreeData` from the given binary subtree + * data that was directly read from a ".subtree" file. + * + * This will extract the JSON- and binary buffer part of the + * subtree data, and resolve all buffer references that are + * contained in the subtree JSON. + * + * @param input - The whole buffer of a binary subtree file + * @param resourceResolver - The `ResourceResolver` that + * will be used to resolve buffer URIs + * @returns A promise with the `BinarySubtreeData` + * @throws An ImplicitTilingError when the subtree JSON could + * not be parsed, or there was a buffer without a URI + * and no binary buffer was given, or one of the requested + * buffers could not be resolved. + */ + static async resolveFromBuffer( + input: Buffer, + resourceResolver: ResourceResolver + ): Promise { + const headerByteLength = 24; + const jsonByteLength = input.readBigUint64LE(8); + const binaryByteLength = input.readBigUint64LE(16); + + // Extract the JSON data + const jsonStartByteOffset = headerByteLength; + const jsonEndByteOffset = jsonStartByteOffset + Number(jsonByteLength); + const jsonBuffer = input.subarray(jsonStartByteOffset, jsonEndByteOffset); + let subtreeJson: any; + let subtree: Subtree; + try { + subtreeJson = Buffers.getJson(jsonBuffer); + subtree = subtreeJson; + } catch (error) { + throw new ImplicitTilingError("Could not parse subtree JSON data"); + } + + // Extract the binary buffer + const binaryStartByteOffset = jsonEndByteOffset; + const binaryEndByteOffset = + binaryStartByteOffset + Number(binaryByteLength); + const binaryBufferSlice = input.subarray( + binaryStartByteOffset, + binaryEndByteOffset + ); + const binaryBuffer = + binaryBufferSlice.length > 0 ? binaryBufferSlice : undefined; + + const binarySubtreeData = await BinarySubtreeDataResolver.resolveInternal( + subtree, + binaryBuffer, + resourceResolver + ); + return binarySubtreeData; + } + + /** + * A thin wrapper around `BinaryBufferDataResolver.resolve` + * that obtains the binary buffer structure information from + * the subtree, resolves it, and returns it as part of + * the `BinarySubtreeData` + * + * @param subtree - The `Subtree` + * @param binaryBuffer - The binary buffer of the subtree + * @param resourceResolver The resource resolver + * @returns A promise to the resolved binary subtree data + */ + static async resolveInternal( + subtree: Subtree, + binaryBuffer: Buffer | undefined, + resourceResolver: ResourceResolver + ): Promise { + const binaryBufferStructure: BinaryBufferStructure = { + buffers: subtree.buffers ?? [], + bufferViews: subtree.bufferViews ?? [], + }; + let binaryBufferData; + try { + binaryBufferData = await BinaryBufferDataResolver.resolve( + binaryBufferStructure, + binaryBuffer, + resourceResolver + ); + } catch (error) { + if (error instanceof BinaryDataError) { + const message = `Could not read subtree data: ${error.message}`; + throw new ImplicitTilingError(message); + } + throw error; + } + return { + subtree: subtree, + binaryBufferStructure: binaryBufferStructure, + binaryBufferData: binaryBufferData, + }; + } +} diff --git a/src/implicitTiling/SubtreeInfo.ts b/src/implicitTiling/SubtreeInfo.ts index f1182992..23cea07b 100644 --- a/src/implicitTiling/SubtreeInfo.ts +++ b/src/implicitTiling/SubtreeInfo.ts @@ -1,33 +1,25 @@ import { AvailabilityInfo } from "./AvailabilityInfo"; /** - * Summarizes the information about a subtree. + * Summarizes the information about the elements that are + * available in a subtree. * * It offers the availability information for tiles, child * subtrees, and contents, as `AvailabilityInfo` objects. */ -export class SubtreeInfo { - private readonly _tileAvailabilityInfo: AvailabilityInfo; - private readonly _contentAvailabilityInfos: AvailabilityInfo[]; - private readonly _childSubtreeAvailabilityInfo: AvailabilityInfo; +export interface SubtreeInfo { + /** + * The AvailabilityInfo for the tiles + */ + tileAvailabilityInfo: AvailabilityInfo; - constructor( - tileAvailabilityInfo: AvailabilityInfo, - contentAvailabilityInfos: AvailabilityInfo[], - childSubtreeAvailabilityInfo: AvailabilityInfo - ) { - (this._tileAvailabilityInfo = tileAvailabilityInfo), - (this._contentAvailabilityInfos = contentAvailabilityInfos), - (this._childSubtreeAvailabilityInfo = childSubtreeAvailabilityInfo); - } + /** + * The AvailabilityInfo for the content + */ + contentAvailabilityInfos: AvailabilityInfo[]; - getTileAvailabilityInfo(): AvailabilityInfo { - return this._tileAvailabilityInfo; - } - getContentAvailabilityInfos(): AvailabilityInfo[] { - return this._contentAvailabilityInfos; - } - getChildSubtreeAvailabilityInfo(): AvailabilityInfo { - return this._childSubtreeAvailabilityInfo; - } + /** + * The AvailabilityInfo for the child subtrees + */ + childSubtreeAvailabilityInfo: AvailabilityInfo; } diff --git a/src/implicitTiling/SubtreeInfos.ts b/src/implicitTiling/SubtreeInfos.ts index b6ceeef5..13f1ccaf 100644 --- a/src/implicitTiling/SubtreeInfos.ts +++ b/src/implicitTiling/SubtreeInfos.ts @@ -1,28 +1,23 @@ -import { Buffers } from "../base/Buffers"; - import { ResourceResolver } from "../io/ResourceResolver"; import { SubtreeInfo } from "./SubtreeInfo"; import { AvailabilityInfos } from "./AvailabilityInfos"; -import { ImplicitTilingError } from "./ImplicitTilingError"; +import { BinarySubtreeData } from "./BinarySubtreeData"; +import { BinarySubtreeDataResolver } from "./BinarySubtreeDataResolver"; import { Subtree } from "../structure/Subtree"; import { TileImplicitTiling } from "../structure/TileImplicitTiling"; -import { BinaryBufferDataResolver } from "../binary/BinaryBufferDataResolver"; -import { BinaryBufferStructure } from "../binary/BinaryBufferStructure"; -import { BinaryDataError } from "../binary/BinaryDataError"; - /** * Methods to create `SubtreeInfo` instances. */ export class SubtreeInfos { /** - * Creates a new `SubtreeInfo` from the given binary subtree data. + * Creates a new `SubtreeInfo` from the given binary subtree data + * that was directly read from a ".subtree" file. * * This method assumes that the given binary data is consistent - * and valid. This can be checked with the `SubtreeValidator` - * class. + * and valid. * * @param input - The whole buffer of a binary subtree file * @param implicitTiling - The `TileImplicitTiling` that @@ -40,93 +35,60 @@ export class SubtreeInfos { implicitTiling: TileImplicitTiling, resourceResolver: ResourceResolver ): Promise { - const headerByteLength = 24; - const jsonByteLength = input.readBigUint64LE(8); - const binaryByteLength = input.readBigUint64LE(16); - - // Extract the JSON data - const jsonStartByteOffset = headerByteLength; - const jsonEndByteOffset = jsonStartByteOffset + Number(jsonByteLength); - const jsonBuffer = input.subarray(jsonStartByteOffset, jsonEndByteOffset); - let subtreeJson: any; - let subtree: Subtree; - try { - subtreeJson = Buffers.getJson(jsonBuffer); - subtree = subtreeJson; - } catch (error) { - throw new ImplicitTilingError("Could not parse subtree JSON data"); - } - - // Extract the binary buffer - const binaryStartByteOffset = jsonEndByteOffset; - const binaryEndByteOffset = - binaryStartByteOffset + Number(binaryByteLength); - const binaryBufferSlice = input.subarray( - binaryStartByteOffset, - binaryEndByteOffset + const binarySubtreeData = await BinarySubtreeDataResolver.resolveFromBuffer( + input, + resourceResolver ); - const binaryBuffer = - binaryBufferSlice.length > 0 ? binaryBufferSlice : undefined; + const result = SubtreeInfos.create(binarySubtreeData, implicitTiling); + return result; + } - return SubtreeInfos.create( + /** + * Creates a new `SubtreeInfo` from the given subtree + * that was read from a subtree JSON file. + * + * @param subtree - The parsed subtree + * @param implicitTiling - The `TileImplicitTiling` that + * defines the expected structure of the subtree data + * @param resourceResolver - The `ResourceResolver` that + * will be used to resolve buffer URIs + * @returns A promise with the `SubtreeInfo` + * @throws An ImplicitTilingError when there was a buffer without + * a URI, or one of the requested buffers could not be resolved. + */ + static async createFromJson( + subtree: Subtree, + implicitTiling: TileImplicitTiling, + resourceResolver: ResourceResolver + ): Promise { + const binarySubtreeData = await BinarySubtreeDataResolver.resolveFromJson( subtree, - binaryBuffer, - implicitTiling, resourceResolver ); + const result = SubtreeInfos.create(binarySubtreeData, implicitTiling); + return result; } /** - * Creates a new `SubtreeInfo` from the given `Subtree` object - * and the (optional) binary buffer. + * Creates a new `SubtreeInfo` from the given binary subtree data. * * This method assumes that the given data is consistent - * and valid. This can be checked with the `SubtreeValidator` - * class. + * and valid. * - * @param subtree - The `Subtree` object - * @param binaryBuffer - The optional binary buffer + * @param binarySubtreeData - The `BinarySubtreeData` * @param implicitTiling - The `TileImplicitTiling` that * defines the expected structure of the subtree data - * @param resourceResolver - The `ResourceResolver` that - * will be used to resolve buffer URIs - * @returns A promise with the `SubtreeInfo` + * @returns The `SubtreeInfo` * @throws A ImplicitTilingError when there was a buffer without * a URI and no binary buffer was given, or the requested buffer * data could not be resolved. */ - static async create( - subtree: Subtree, - binaryBuffer: Buffer | undefined, - implicitTiling: TileImplicitTiling, - resourceResolver: ResourceResolver - ): Promise { - if (!subtree.buffers) { - throw new ImplicitTilingError("The subtree did not define any buffers"); - } - if (!subtree.bufferViews) { - throw new ImplicitTilingError( - "The subtree did not define any buffer views" - ); - } - const binaryBufferStructure: BinaryBufferStructure = { - buffers: subtree.buffers, - bufferViews: subtree.bufferViews, - }; - let binaryBufferData; - try { - binaryBufferData = await BinaryBufferDataResolver.resolve( - binaryBufferStructure, - binaryBuffer, - resourceResolver - ); - } catch (error) { - if (error instanceof BinaryDataError) { - const message = `Could not read subtree data: ${error.message}`; - throw new ImplicitTilingError(message); - } - throw error; - } + static create( + binarySubtreeData: BinarySubtreeData, + implicitTiling: TileImplicitTiling + ): SubtreeInfo { + const subtree = binarySubtreeData.subtree; + const binaryBufferData = binarySubtreeData.binaryBufferData; const bufferViewsData = binaryBufferData.bufferViewsData; // Create the `AvailabilityInfo` for the tile availability @@ -160,11 +122,11 @@ export class SubtreeInfos { implicitTiling ); - const result = new SubtreeInfo( - tileAvailabilityInfo, - contentAvailabilityInfos, - childSubtreeAvailabilityInfo - ); + const result: SubtreeInfo = { + tileAvailabilityInfo: tileAvailabilityInfo, + contentAvailabilityInfos: contentAvailabilityInfos, + childSubtreeAvailabilityInfo: childSubtreeAvailabilityInfo, + }; return result; } } diff --git a/src/metadata/binary/PropertyModel.ts b/src/metadata/PropertyModel.ts similarity index 100% rename from src/metadata/binary/PropertyModel.ts rename to src/metadata/PropertyModel.ts diff --git a/src/metadata/PropertyTableModel.ts b/src/metadata/PropertyTableModel.ts new file mode 100644 index 00000000..cf668416 --- /dev/null +++ b/src/metadata/PropertyTableModel.ts @@ -0,0 +1,55 @@ +import { PropertyModel } from "./PropertyModel"; +import { MetadataEntityModel } from "./MetadataEntityModel"; + +import { ClassProperty } from "../structure/Metadata/ClassProperty"; +import { PropertyTableProperty } from "../structure/PropertyTableProperty"; + +/** + * A basic interface for a property table + * + * @internal + */ +export interface PropertyTableModel { + /** + * Returns the `MetadataEntityModel` that corresponds to the + * row of the table with the given index. + * + * @param index - The index (i.e. the table row) + * @returns The `MetdataEntityModel` + * @throws MetadataError If the index is out of range + */ + getMetadataEntityModel(index: number): MetadataEntityModel; + + /** + * Returns the `PropertyModel` for the property with the given ID. + * This is the "column" of the table that contains the property + * data. Returns `undefined` if this table was created for + * a `MetadataClass` that does not define this property. + * + * @param propertyId - The property ID + * @returns The `PropertyModel` + */ + getPropertyModel(propertyId: string): PropertyModel | undefined; + + /** + * Returns the `ClassProperty` that defines the structure of the + * property with the given ID, or `undefined` if this table was + * created for a `MetadataClass` that does not define this property. + * + * @param propertyId - The property ID + * @returns The `ClassProperty` + */ + getClassProperty(propertyId: string): ClassProperty | undefined; + + /** + * Returns the `PropertyTableProperty` that defines the structure of the + * property with the given ID, or `undefined` if this table was + * created for a `PropertyTable` that does not define this property. + * + * @param propertyId - The property ID + * @returns The `PropertyTableProperty` + */ + getPropertyTableProperty( + propertyId: string + ): PropertyTableProperty | undefined; +} diff --git a/src/metadata/binary/BinaryMetadataEntityModel.ts b/src/metadata/binary/BinaryMetadataEntityModel.ts index 97710fb5..5922776b 100644 --- a/src/metadata/binary/BinaryMetadataEntityModel.ts +++ b/src/metadata/binary/BinaryMetadataEntityModel.ts @@ -3,8 +3,7 @@ import { defined } from "../../base/defined"; import { MetadataEntityModel } from "../MetadataEntityModel"; import { MetadataValues } from "../MetadataValues"; import { MetadataError } from "../MetadataError"; - -import { PropertyTableModel } from "./PropertyTableModel"; +import { PropertyTableModel } from "../PropertyTableModel"; /** * Implementation of a `MetadataEntityModel` that is backed by binary diff --git a/src/metadata/binary/PropertyModels.ts b/src/metadata/binary/BinaryPropertyModels.ts similarity index 98% rename from src/metadata/binary/PropertyModels.ts rename to src/metadata/binary/BinaryPropertyModels.ts index 374a594f..3bc148c3 100644 --- a/src/metadata/binary/PropertyModels.ts +++ b/src/metadata/binary/BinaryPropertyModels.ts @@ -1,7 +1,9 @@ import { defined } from "../../base/defined"; +import { PropertyModel } from "../PropertyModel"; +import { MetadataError } from "../MetadataError"; + import { BinaryPropertyTable } from "./BinaryPropertyTable"; -import { PropertyModel } from "./PropertyModel"; import { StringPropertyModel } from "./StringPropertyModel"; import { BooleanPropertyModel } from "./BooleanPropertyModel"; import { NumericPropertyModel } from "./NumericPropertyModel"; @@ -10,13 +12,13 @@ import { StringArrayPropertyModel } from "./StringArrayPropertyModel"; import { BooleanArrayPropertyModel } from "./BooleanArrayPropertyModel"; import { NumericBuffers } from "./NumericBuffers"; -import { MetadataError } from "../MetadataError"; /** - * Methods related to `PropertyModel` instances + * Methods related to `PropertyModel` instances that are created + * from binary data. * * @internal */ -export class PropertyModels { +export class BinaryPropertyModels { /** * Creates a `PropertyModel` for the specified property in * the given `BinaryPropertyTable`. diff --git a/src/metadata/binary/PropertyTableModel.ts b/src/metadata/binary/BinaryPropertyTableModel.ts similarity index 68% rename from src/metadata/binary/PropertyTableModel.ts rename to src/metadata/binary/BinaryPropertyTableModel.ts index afe7e71a..92e884d5 100644 --- a/src/metadata/binary/PropertyTableModel.ts +++ b/src/metadata/binary/BinaryPropertyTableModel.ts @@ -1,22 +1,23 @@ -import { BinaryMetadataEntityModel } from "./BinaryMetadataEntityModel"; +import { BinaryPropertyModels } from "./BinaryPropertyModels"; import { BinaryPropertyTable } from "./BinaryPropertyTable"; -import { PropertyModel } from "./PropertyModel"; -import { PropertyModels } from "./PropertyModels"; +import { BinaryMetadataEntityModel } from "./BinaryMetadataEntityModel"; import { MetadataEntityModel } from "../MetadataEntityModel"; import { MetadataEntityModels } from "../MetadataEntityModels"; import { MetadataError } from "../MetadataError"; +import { PropertyModel } from "../PropertyModel"; +import { PropertyTableModel } from "../PropertyTableModel"; import { ClassProperty } from "../../structure/Metadata/ClassProperty"; import { PropertyTableProperty } from "../../structure/PropertyTableProperty"; /** - * Implementation of a model for a property table that is backed + * Implementation of the `PropertyTableModel` interface that is backed * by binary data. * * @internal */ -export class PropertyTableModel { +export class BinaryPropertyTableModel implements PropertyTableModel { /** * The structure containing the raw data of the binary * property table @@ -46,7 +47,7 @@ export class PropertyTableModel { const propertyTableProperties = propertyTable.properties; if (propertyTableProperties) { for (const propertyId of Object.keys(propertyTableProperties)) { - const propertyModel = PropertyModels.createPropertyModel( + const propertyModel = BinaryPropertyModels.createPropertyModel( this._binaryPropertyTable, propertyId ); @@ -59,14 +60,7 @@ export class PropertyTableModel { MetadataEntityModels.computeSemanticToPropertyIdMapping(metadataClass); } - /** - * Returns the `MetadataEntityModel` that corresponds to the - * row of the table with the given index. - * - * @param index - The index (i.e. the table row) - * @returns The `MetdataEntityModel` - * @throws MetadataError If the index is out of range - */ + /** {@inheritDoc PropertyTableModel.getMetadataEntityModel} */ getMetadataEntityModel(index: number): MetadataEntityModel { const propertyTable = this._binaryPropertyTable.propertyTable; const count = propertyTable.count; @@ -83,14 +77,12 @@ export class PropertyTableModel { return metadataEntityModel; } - /** - * Returns the `ClassProperty` that defines the structure of the - * property with the given ID, or `undefined` if this table was - * created for a `MetadataClass` that does not define this property. - * - * @param propertyId - The property ID - * @returns The `ClassProperty` - */ + /** {@inheritDoc PropertyTableModel.getPropertyModel} */ + getPropertyModel(propertyId: string): PropertyModel | undefined { + return this._propertyIdToModel[propertyId]; + } + + /** {@inheritDoc PropertyTableModel.getClassProperty} */ getClassProperty(propertyId: string): ClassProperty | undefined { const binaryPropertyTable = this._binaryPropertyTable; const metadataClass = binaryPropertyTable.metadataClass; @@ -101,14 +93,7 @@ export class PropertyTableModel { return classProperties[propertyId]; } - /** - * Returns the `PropertyTableProperty` that defines the structure of the - * property with the given ID, or `undefined` if this table was - * created for a `PropertyTable` that does not define this property. - * - * @param propertyId - The property ID - * @returns The `PropertyTableProperty` - */ + /** {@inheritDoc PropertyTableModel.getPropertyTableProperty} */ getPropertyTableProperty( propertyId: string ): PropertyTableProperty | undefined { @@ -120,17 +105,4 @@ export class PropertyTableModel { } return propertyTableProperties[propertyId]; } - - /** - * Returns the `PropertyModel` for the property with the given ID. - * This is the "column" of the table that contains the property - * data. Returns `undefined` if this table was created for - * a `MetadataClass` that does not define this property. - * - * @param propertyId - The property ID - * @returns The `PropertyModel` - */ - getPropertyModel(propertyId: string): PropertyModel | undefined { - return this._propertyIdToModel[propertyId]; - } } diff --git a/src/metadata/binary/BooleanArrayPropertyModel.ts b/src/metadata/binary/BooleanArrayPropertyModel.ts index 6e5bdd93..e376c89d 100644 --- a/src/metadata/binary/BooleanArrayPropertyModel.ts +++ b/src/metadata/binary/BooleanArrayPropertyModel.ts @@ -1,5 +1,6 @@ -import { PropertyModel } from "./PropertyModel"; -import { PropertyModels } from "./PropertyModels"; +import { PropertyModel } from "../PropertyModel"; + +import { BinaryPropertyModels } from "./BinaryPropertyModels"; import { BooleanPropertyModel } from "./BooleanPropertyModel"; /** @@ -31,7 +32,7 @@ export class BooleanArrayPropertyModel implements PropertyModel { const arrayOffsetType = this._arrayOffsetType; const count = this._count; - const arraySlice = PropertyModels.computeSlice( + const arraySlice = BinaryPropertyModels.computeSlice( index, arrayOffsetsBuffer, arrayOffsetType, diff --git a/src/metadata/binary/BooleanPropertyModel.ts b/src/metadata/binary/BooleanPropertyModel.ts index 7eb3d109..094a4952 100644 --- a/src/metadata/binary/BooleanPropertyModel.ts +++ b/src/metadata/binary/BooleanPropertyModel.ts @@ -1,4 +1,5 @@ -import { PropertyModel } from "./PropertyModel"; +import { PropertyModel } from "../PropertyModel"; + import { NumericBuffers } from "./NumericBuffers"; /** diff --git a/src/metadata/binary/NumericArrayPropertyModel.ts b/src/metadata/binary/NumericArrayPropertyModel.ts index 762be700..1a32a06a 100644 --- a/src/metadata/binary/NumericArrayPropertyModel.ts +++ b/src/metadata/binary/NumericArrayPropertyModel.ts @@ -1,9 +1,9 @@ -import { PropertyModel } from "./PropertyModel"; -import { NumericBuffers } from "./NumericBuffers"; -import { PropertyModels } from "./PropertyModels"; - +import { PropertyModel } from "../PropertyModel"; import { MetadataTypes } from "../MetadataTypes"; +import { NumericBuffers } from "./NumericBuffers"; +import { BinaryPropertyModels } from "./BinaryPropertyModels"; + /** * Implementation of a `PropertyModel` for numeric array types. * @@ -46,7 +46,7 @@ export class NumericArrayPropertyModel implements PropertyModel { const count = this._count; const componentCount = MetadataTypes.componentCountForType(type); - const arraySlice = PropertyModels.computeSlice( + const arraySlice = BinaryPropertyModels.computeSlice( index, arrayOffsetsBuffer, arrayOffsetType, diff --git a/src/metadata/binary/NumericPropertyModel.ts b/src/metadata/binary/NumericPropertyModel.ts index 371c2f50..0edebcfd 100644 --- a/src/metadata/binary/NumericPropertyModel.ts +++ b/src/metadata/binary/NumericPropertyModel.ts @@ -1,6 +1,6 @@ -import { PropertyModel } from "./PropertyModel"; import { NumericBuffers } from "./NumericBuffers"; +import { PropertyModel } from "../PropertyModel"; import { MetadataTypes } from "../MetadataTypes"; /** diff --git a/src/metadata/binary/StringArrayPropertyModel.ts b/src/metadata/binary/StringArrayPropertyModel.ts index 7a862481..c253fde4 100644 --- a/src/metadata/binary/StringArrayPropertyModel.ts +++ b/src/metadata/binary/StringArrayPropertyModel.ts @@ -1,6 +1,6 @@ -import { PropertyModel } from "./PropertyModel"; -import { PropertyModels } from "./PropertyModels"; +import { PropertyModel } from "../PropertyModel"; +import { BinaryPropertyModels } from "./BinaryPropertyModels"; import { ArrayBuffers } from "./ArrayBuffers"; /** @@ -48,7 +48,7 @@ export class StringArrayPropertyModel implements PropertyModel { const stringOffsetType = this._stringOffsetType; const count = this._count; - const arraySlice = PropertyModels.computeSlice( + const arraySlice = BinaryPropertyModels.computeSlice( index, arrayOffsetsBuffer, arrayOffsetType, @@ -61,7 +61,7 @@ export class StringArrayPropertyModel implements PropertyModel { for (let i = 0; i < arrayLength; i++) { const n = arrayOffset + i; - const stringSlice = PropertyModels.computeSlice( + const stringSlice = BinaryPropertyModels.computeSlice( n, stringOffsetsBuffer, stringOffsetType, diff --git a/src/metadata/binary/StringPropertyModel.ts b/src/metadata/binary/StringPropertyModel.ts index 7a031710..7dcf485c 100644 --- a/src/metadata/binary/StringPropertyModel.ts +++ b/src/metadata/binary/StringPropertyModel.ts @@ -1,5 +1,6 @@ -import { PropertyModel } from "./PropertyModel"; -import { PropertyModels } from "./PropertyModels"; +import { PropertyModel } from "../PropertyModel"; + +import { BinaryPropertyModels } from "./BinaryPropertyModels"; import { ArrayBuffers } from "./ArrayBuffers"; /** @@ -29,7 +30,7 @@ export class StringPropertyModel implements PropertyModel { const stringOffsetsBuffer = this._stringOffsetsBuffer; const stringOffsetType = this._stringOffsetType; - const stringSlice = PropertyModels.computeSlice( + const stringSlice = BinaryPropertyModels.computeSlice( index, stringOffsetsBuffer, stringOffsetType, diff --git a/src/traversal/ExplicitTraversedTile.ts b/src/traversal/ExplicitTraversedTile.ts index 8d8a1f41..acd6dc2f 100644 --- a/src/traversal/ExplicitTraversedTile.ts +++ b/src/traversal/ExplicitTraversedTile.ts @@ -1,9 +1,8 @@ -import { defined } from "../base/defined"; - import { ResourceResolver } from "../io/ResourceResolver"; import { TraversedTile } from "./TraversedTile"; -import { ImplicitTileTraversal } from "./ImplicitTileTraversal"; +import { ExplicitTraversedTiles } from "./ExplicitTraversedTiles"; +import { MetadataSemanticOverrides } from "./MetadataSemanticOverrides"; import { Tile } from "../structure/Tile"; import { Content } from "../structure/Content"; @@ -77,97 +76,69 @@ export class ExplicitTraversedTile implements TraversedTile { this._resourceResolver = resourceResolver; } - asTile(): Tile { + asRawTile(): Tile { + return this._tile; + } + + asFinalTile(): Tile { const tile = this._tile; + const finalTile = { + boundingVolume: tile.boundingVolume, + viewerRequestVolume: tile.viewerRequestVolume, + geometricError: tile.geometricError, + refine: tile.refine, + transform: tile.transform, + content: tile.content, + contents: tile.contents, + children: tile.children, + metadata: tile.metadata, + implicitTiling: tile.implicitTiling, + extensions: tile.extensions, + extras: tile.extras, + }; + const schema = this._schema; const metadata = tile.metadata; - - const boundingVolume = tile.boundingVolume; - let transform = tile.transform; - let refine = tile.refine; - let geometricError = tile.geometricError; - if (metadata && schema) { - let metadataEntityModel = undefined; - try { - metadataEntityModel = MetadataEntityModels.create(schema, metadata); - } catch (error) { - // Errors from creating the entity model should have been - // prevented by the metadata- and schema validation: - const message = `Error while traversing tileset: ${error}`; - throw new ImplicitTilingError(message); - } - if (metadataEntityModel) { - // Apply the semantic-based overrides from the metadata - const semanticBoundingBox = - metadataEntityModel.getPropertyValueBySemantic("TILE_BOUNDING_BOX"); - if (semanticBoundingBox) { - boundingVolume.box = semanticBoundingBox; - } - - const semanticBoundingRegion = - metadataEntityModel.getPropertyValueBySemantic( - "TILE_BOUNDING_REGION" - ); - if (semanticBoundingRegion) { - boundingVolume.region = semanticBoundingRegion; - } - - const semanticBoundingSphere = - metadataEntityModel.getPropertyValueBySemantic( - "TILE_BOUNDING_SPHERE" - ); - if (semanticBoundingSphere) { - boundingVolume.sphere = semanticBoundingSphere; - } - - const semanticGeometricError = - metadataEntityModel.getPropertyValueBySemantic( - "TILE_GEOMETRIC_ERROR" - ); - if (defined(semanticGeometricError)) { - geometricError = semanticGeometricError as number; - } - - const semanticRefine = - metadataEntityModel.getPropertyValueBySemantic("TILE_REFINE"); - if (semanticRefine === 0) { - refine = "ADD"; - } else if (semanticRefine === 1) { - refine = "REPLACE"; - } - - const semanticTransform = - metadataEntityModel.getPropertyValueBySemantic("TILE_TRANSFORM"); - if (semanticTransform) { - transform = semanticTransform; - } - } + this.applyMetadataSemanticOverrides(finalTile); + } + return finalTile; + } + + /** + * Perform the overrides of the properties of the given tile that + * are given by metadata semantics. + * + * If this instance contains a `Schema` and a `MetadataEntity`, + * then the property values of that metadata entity are examined. + * The property values that have a semantic will be used to + * override the corresponding values in the given tile. + * + * For example, when the metadata entity has a property with the + * semantic `TILE_GEOMETRIC_ERROR`, then the `geometricError` in + * the given tile will be replaced with the corresponding value + * from the metadata entity. + * + * @param finalTile - The tile + * @throws ImplicitTilingError If the input (for example, the + * schema and the metadata entity) are not structurally valid. + */ + private applyMetadataSemanticOverrides(finalTile: Tile) { + const schema = this._schema; + const metadata = this._tile.metadata; + if (!metadata || !schema) { + return; } - const viewerRequestVolume = tile.viewerRequestVolume; - const content = tile.content; - const contents = tile.contents; - const children = tile.children; - const implicitTiling = tile.implicitTiling; - const extensions = tile.extensions; - const extras = tile.extras; - - return { - boundingVolume: boundingVolume, - viewerRequestVolume: viewerRequestVolume, - geometricError: geometricError, - refine: refine, - transform: transform, - content: content, - contents: contents, - children: children, - metadata: metadata, - implicitTiling: implicitTiling, - extensions: extensions, - extras: extras, - }; + let metadataEntityModel = undefined; + try { + metadataEntityModel = MetadataEntityModels.create(schema, metadata); + } catch (error) { + const message = `Error while traversing tileset: ${error}`; + throw new ImplicitTilingError(message); + } + MetadataSemanticOverrides.applyToTile(finalTile, metadataEntityModel); } get path(): string { @@ -183,9 +154,11 @@ export class ExplicitTraversedTile implements TraversedTile { async getChildren(): Promise { const implicitTiling = this._tile.implicitTiling; + const schema = this._schema; if (implicitTiling) { - const children = await ImplicitTileTraversal.createTraversedChildren( + const children = await ExplicitTraversedTiles.createTraversedChildren( implicitTiling, + schema, this, this._resourceResolver ); diff --git a/src/traversal/ImplicitTileTraversal.ts b/src/traversal/ExplicitTraversedTiles.ts similarity index 52% rename from src/traversal/ImplicitTileTraversal.ts rename to src/traversal/ExplicitTraversedTiles.ts index 2d9d7c84..65f2e7e0 100644 --- a/src/traversal/ImplicitTileTraversal.ts +++ b/src/traversal/ExplicitTraversedTiles.ts @@ -1,29 +1,26 @@ -import path from "path"; - -import { defined } from "../base/defined"; -import { Buffers } from "../base/Buffers"; - import { ResourceResolver } from "../io/ResourceResolver"; import { TraversedTile } from "./TraversedTile"; import { ExplicitTraversedTile } from "./ExplicitTraversedTile"; import { ImplicitTraversedTile } from "./ImplicitTraversedTile"; +import { SubtreeModels } from "./SubtreeModels"; -import { Subtree } from "../structure/Subtree"; import { TileImplicitTiling } from "../structure/TileImplicitTiling"; +import { Schema } from "../structure/Metadata/Schema"; -import { SubtreeInfos } from "../implicitTiling/SubtreeInfos"; -import { TreeCoordinates } from "../implicitTiling/TreeCoordinates"; -import { SubtreeInfo } from "../implicitTiling/SubtreeInfo"; import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; import { ImplicitTilings } from "../implicitTiling/ImplicitTilings"; /** - * Methods related to the traversal of implicit tilesets. + * Internal methods used in the `ExplicitTraversedTile` class. + * + * (Specifically: Methods to create the children of explicit + * tiles, when these explicit tiles define the root of an + * implicit tileset) * * @internal */ -export class ImplicitTileTraversal { +export class ExplicitTraversedTiles { /** * Create the traversed children for the given explicit traversed tile. * @@ -36,6 +33,7 @@ export class ImplicitTileTraversal { * * @param implicitTiling - The `TileImplicitTiling` * @param parent - The `ExplicitTraversedTile` + * @param schema - The optional metadata schema * @param resourceResolver - The `ResourceResolver` that * will be used e.g. for subtree files * @returns The traversed children @@ -43,21 +41,24 @@ export class ImplicitTileTraversal { */ static async createTraversedChildren( implicitTiling: TileImplicitTiling, + schema: Schema | undefined, parent: ExplicitTraversedTile, resourceResolver: ResourceResolver ): Promise { const subdivisionScheme = implicitTiling.subdivisionScheme; if (subdivisionScheme === "QUADTREE") { - const child = await ImplicitTileTraversal.createImplicitQuadtreeRoot( + const child = await ExplicitTraversedTiles.createImplicitQuadtreeRoot( implicitTiling, + schema, parent, resourceResolver ); return [child]; } if (subdivisionScheme === "OCTREE") { - const child = await ImplicitTileTraversal.createImplicitOctreeRoot( + const child = await ExplicitTraversedTiles.createImplicitOctreeRoot( implicitTiling, + schema, parent, resourceResolver ); @@ -72,6 +73,7 @@ export class ImplicitTileTraversal { * Creates the root node for the traversal of an implicit quadtree. * * @param implicitTiling - The `TileImplicitTiling` + * @param schema - The optional metadata schema * @param parent - The `ExplicitTraversedTile` * @param resourceResolver - The `ResourceResolver` that * will be used e.g. for subtree files @@ -80,16 +82,20 @@ export class ImplicitTileTraversal { */ private static async createImplicitQuadtreeRoot( implicitTiling: TileImplicitTiling, + schema: Schema | undefined, parent: ExplicitTraversedTile, resourceResolver: ResourceResolver ): Promise { const rootCoordinates = ImplicitTilings.createRootCoordinates(implicitTiling); - const subtreeInfo = await ImplicitTileTraversal.resolveSubtreeInfo( + + const subtreeModel = await SubtreeModels.resolve( implicitTiling, + schema, resourceResolver, rootCoordinates ); + // The path is composed from the path of the parent and the string // representation of the root coordinates const coordinateString = ImplicitTilings.createString(rootCoordinates); @@ -99,7 +105,7 @@ export class ImplicitTileTraversal { resourceResolver, parent, path, - subtreeInfo, + subtreeModel, parent.level + 1, rootCoordinates, rootCoordinates, @@ -113,6 +119,7 @@ export class ImplicitTileTraversal { * Creates the root node for the traversal of an implicit octree. * * @param implicitTiling - The `TileImplicitTiling` + * @param schema - The optional metadata schema * @param parent - The `ExplicitTraversedTile` * @param resourceResolver - The `ResourceResolver` that * will be used e.g. for subtree files @@ -121,16 +128,20 @@ export class ImplicitTileTraversal { */ private static async createImplicitOctreeRoot( implicitTiling: TileImplicitTiling, + schema: Schema | undefined, parent: ExplicitTraversedTile, resourceResolver: ResourceResolver ): Promise { const rootCoordinates = ImplicitTilings.createRootCoordinates(implicitTiling); - const subtreeInfo = await ImplicitTileTraversal.resolveSubtreeInfo( + + const subtreeModel = await SubtreeModels.resolve( implicitTiling, + schema, resourceResolver, rootCoordinates ); + // The path is composed from the path of the parent and the string // representation of the root coordinates const coordinateString = ImplicitTilings.createString(rootCoordinates); @@ -140,7 +151,7 @@ export class ImplicitTileTraversal { resourceResolver, parent, path, - subtreeInfo, + subtreeModel, parent.level + 1, rootCoordinates, rootCoordinates, @@ -149,90 +160,4 @@ export class ImplicitTileTraversal { ); return root; } - - /** - * Resolve the `SubtreeInfo` for the subtree with the given root coordinates. - * - * This will substitute the given coordinates into the subtree template - * URI from the given implicit tiling object. Then it will attempt to load - * the subtree data from this URI. The resulting data will be used to - * construct the `SubtreeInfo` object. - * - * @param implicitTiling - The `TileImplicitTiling` - * @param resourceResolver - The `ResourceResolver` for the subtree - * files and buffers - * @param coordinates - The root coordinates of the subtree - * @returns The `SubtreeInfo` - * @throws ImplicitTilingError If the input was structurally invalid - */ - static async resolveSubtreeInfo( - implicitTiling: TileImplicitTiling, - resourceResolver: ResourceResolver, - coordinates: TreeCoordinates - ): Promise { - const subtreeUri = ImplicitTilings.substituteTemplateUri( - implicitTiling.subdivisionScheme, - implicitTiling.subtrees.uri, - coordinates - ); - if (!defined(subtreeUri)) { - const message = - `Could not substitute coordinates ${coordinates} in ` + - `template URI ${implicitTiling.subtrees.uri}`; - throw new ImplicitTilingError(message); - } - const subtreeData = await resourceResolver.resolveData(subtreeUri); - if (!subtreeData) { - const message = - `Could not resolve subtree URI ${subtreeUri} that was ` + - `created from template URI ${implicitTiling.subtrees.uri} ` + - `for coordinates ${coordinates}`; - throw new ImplicitTilingError(message); - } - - const subtreeDirectory = path.dirname(subtreeUri); - const subtreeResourceResolver = resourceResolver.derive(subtreeDirectory); - - // If the subtree data was JSON, just parse it and - // create a SubtreeInfo without an internal buffer - const isJson = Buffers.isProbablyJson(subtreeData); - if (isJson) { - let subtreeJson: any; - let subtree: Subtree; - try { - subtreeJson = Buffers.getJson(subtreeData); - subtree = subtreeJson; - } catch (error) { - const message = - `Could not parse subtree JSON from URI ${subtreeUri} that was ` + - `created from template URI ${implicitTiling.subtrees.uri} ` + - `for coordinates ${coordinates}`; - throw new ImplicitTilingError(message); - } - return SubtreeInfos.create( - subtree, - undefined, - implicitTiling, - subtreeResourceResolver - ); - } - - // For SUBT (binary subtree data), create the SubtreeInfo - // from the whole buffer - const isSubt = Buffers.getMagic(subtreeData) === "subt"; - if (isSubt) { - const subtreeInfo = await SubtreeInfos.createFromBuffer( - subtreeData, - implicitTiling, - subtreeResourceResolver - ); - return subtreeInfo; - } - - const message = - `Subtree data from URI ${subtreeUri} that was created from ` + - `template URI ${implicitTiling.subtrees.uri} for coordinates ` + - `${coordinates} did neither contain JSON nor binary subtree data`; - throw new ImplicitTilingError(message); - } } diff --git a/src/traversal/ImplicitTraversedTile.ts b/src/traversal/ImplicitTraversedTile.ts index a4bf5553..8dad56f1 100644 --- a/src/traversal/ImplicitTraversedTile.ts +++ b/src/traversal/ImplicitTraversedTile.ts @@ -3,20 +3,20 @@ import { defined } from "../base/defined"; import { ResourceResolver } from "../io/ResourceResolver"; import { TraversedTile } from "./TraversedTile"; -import { ImplicitTileTraversal } from "./ImplicitTileTraversal"; +import { SubtreeModel } from "./SubtreeModel"; +import { SubtreeModels } from "./SubtreeModels"; import { Tile } from "../structure/Tile"; import { Content } from "../structure/Content"; import { MetadataEntity } from "../structure/MetadataEntity"; import { TileImplicitTiling } from "../structure/TileImplicitTiling"; -import { BoundingVolumeDerivation } from "./cesium/BoundingVolumeDerivation"; - import { TreeCoordinates } from "../implicitTiling/TreeCoordinates"; -import { SubtreeInfo } from "../implicitTiling/SubtreeInfo"; import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; import { ImplicitTilings } from "../implicitTiling/ImplicitTilings"; +import { BoundingVolumeDerivation } from "./cesium/BoundingVolumeDerivation"; + /** * An implementation of a `TraversedTile` that represents a tile * within an implicit tileset during its traversal. @@ -48,11 +48,11 @@ export class ImplicitTraversedTile implements TraversedTile { private readonly _path: string; /** - * The `SubtreeInfo` object that will be used for accessing - * the availability information for the subtree that this - * tile belongs to. + * The `SubtreeModel` object that will be used for accessing + * the availability information and metadata for the subtree + * that this tile belongs to. */ - private readonly _subtreeInfo: SubtreeInfo; + private readonly _subtreeModel: SubtreeModel; /** * The global level of this tile. This refers to the @@ -87,7 +87,7 @@ export class ImplicitTraversedTile implements TraversedTile { resourceResolver: ResourceResolver, root: TraversedTile, path: string, - subtreeInfo: SubtreeInfo, + subtreeModel: SubtreeModel, globalLevel: number, globalCoordinate: TreeCoordinates, rootCoordinate: TreeCoordinates, @@ -98,7 +98,7 @@ export class ImplicitTraversedTile implements TraversedTile { this._resourceResolver = resourceResolver; this._root = root; this._path = path; - this._subtreeInfo = subtreeInfo; + this._subtreeModel = subtreeModel; this._globalLevel = globalLevel; this._globalCoordinate = globalCoordinate; this._rootCoordinate = rootCoordinate; @@ -106,18 +106,15 @@ export class ImplicitTraversedTile implements TraversedTile { this._parent = parent; } - asTile(): Tile { - // TODO The bounding volume and geometric error - // may be overridden via semantics! - const rootTile = this._root.asTile(); + asRawTile(): Tile { + const rootTile = this._root.asFinalTile(); const boundingVolume = BoundingVolumeDerivation.deriveBoundingVolume( rootTile.boundingVolume, this._globalCoordinate.toArray() ); if (!boundingVolume) { - // The bounding volume neither contained a region nor a box. - // This should have been detected by previous validation. + // The bounding volume was not a region, box, or S2 Cell. throw new ImplicitTilingError("Could not subdivide bounding volume"); } const level = this._globalCoordinate.level; @@ -146,6 +143,15 @@ export class ImplicitTraversedTile implements TraversedTile { }; } + asFinalTile(): Tile { + const tile = this.asRawTile(); + + // TODO Apply overrides here + const TODO = "Apply overrides here"; + + return tile; + } + get path(): string { return this._path; } @@ -186,6 +192,7 @@ export class ImplicitTraversedTile implements TraversedTile { * @throws ImplicitTilingError If the input data was invalid */ private async createNextSubtreeLevelChildren(): Promise { + const subtreeInfo = this._subtreeModel.subtreeInfo; const traversedChildren = []; const localChildCoordinates = this._localCoordinate.children(); for (const localChildCoordinate of localChildCoordinates) { @@ -194,17 +201,20 @@ export class ImplicitTraversedTile implements TraversedTile { this._rootCoordinate, localChildCoordinate ); - const childSubtreeAvailability = - this._subtreeInfo.getChildSubtreeAvailabilityInfo(); + const childSubtreeAvailability = subtreeInfo.childSubtreeAvailabilityInfo; const childSubtreeAvailable = childSubtreeAvailability.isAvailable( localChildCoordinate.toIndexInLevel() ); if (childSubtreeAvailable) { - const childSubtreeInfo = await ImplicitTileTraversal.resolveSubtreeInfo( + const schema = this._subtreeModel.subtreeMetadataModel?.schema; + + const childSubtreeModel = await SubtreeModels.resolve( this._implicitTiling, + schema, this._resourceResolver, globalChildCoordinate ); + const childLocalCoordinate = ImplicitTilings.createRootCoordinates( this._implicitTiling ); @@ -220,7 +230,7 @@ export class ImplicitTraversedTile implements TraversedTile { this._resourceResolver, this._root, childPath, - childSubtreeInfo, + childSubtreeModel, this._globalLevel + 1, globalChildCoordinate, globalChildCoordinate, @@ -244,7 +254,8 @@ export class ImplicitTraversedTile implements TraversedTile { * @throws ImplicitTilingError If the input data was invalid */ private async createDirectChildren(): Promise { - const tileAvailabilityInfo = this._subtreeInfo.getTileAvailabilityInfo(); + const subtreeInfo = this._subtreeModel.subtreeInfo; + const tileAvailabilityInfo = subtreeInfo.tileAvailabilityInfo; const localChildCoordinates = this._localCoordinate.children(); const traversedChildren = []; for (const localChildCoordinate of localChildCoordinates) { @@ -270,7 +281,7 @@ export class ImplicitTraversedTile implements TraversedTile { this._resourceResolver, this._root, childPath, - this._subtreeInfo, + this._subtreeModel, this._globalLevel + 1, globalChildCoordinate, this._rootCoordinate, @@ -285,8 +296,8 @@ export class ImplicitTraversedTile implements TraversedTile { getContents(): Content[] { const contents = []; - const contentAvailabilityInfos = - this._subtreeInfo.getContentAvailabilityInfos(); + const subtreeInfo = this._subtreeModel.subtreeInfo; + const contentAvailabilityInfos = subtreeInfo.contentAvailabilityInfos; for (const contentAvailabilityInfo of contentAvailabilityInfos) { const available = contentAvailabilityInfo.isAvailable( this._localCoordinate.toIndex() @@ -295,7 +306,7 @@ export class ImplicitTraversedTile implements TraversedTile { // TODO The existence of the root content URI should // have been validated. So this could also throw // an error if the template URI is not found. - const templateUri = this._root.asTile().content?.uri; + const templateUri = this._root.asRawTile().content?.uri; if (defined(templateUri)) { const contentUri = ImplicitTilings.substituteTemplateUri( this._implicitTiling.subdivisionScheme, diff --git a/src/traversal/MetadataSemanticOverrides.ts b/src/traversal/MetadataSemanticOverrides.ts new file mode 100644 index 00000000..edb03d70 --- /dev/null +++ b/src/traversal/MetadataSemanticOverrides.ts @@ -0,0 +1,61 @@ +import { defined } from "../base/defined"; + +import { Tile } from "../structure/Tile"; + +import { MetadataEntityModel } from "../metadata/MetadataEntityModel"; + +/** + * Methods for overriding properties in `Tile` and `Content` objects, + * based on metadata semantics. + * + * @internal + */ +export class MetadataSemanticOverrides { + /** + * Applies all overrides to the given tile, based in the property + * values that are found in the given metadata entity model. + * + * @param tile - The tile that will be modified + * @param metadataEntityModel - The `MetadataEntityModel` + */ + static applyToTile(tile: Tile, metadataEntityModel: MetadataEntityModel) { + // Apply the semantic-based overrides from the metadata + const semanticBoundingBox = + metadataEntityModel.getPropertyValueBySemantic("TILE_BOUNDING_BOX"); + if (semanticBoundingBox) { + tile.boundingVolume.box = semanticBoundingBox; + } + + const semanticBoundingRegion = + metadataEntityModel.getPropertyValueBySemantic("TILE_BOUNDING_REGION"); + if (semanticBoundingRegion) { + tile.boundingVolume.region = semanticBoundingRegion; + } + + const semanticBoundingSphere = + metadataEntityModel.getPropertyValueBySemantic("TILE_BOUNDING_SPHERE"); + if (semanticBoundingSphere) { + tile.boundingVolume.sphere = semanticBoundingSphere; + } + + const semanticGeometricError = + metadataEntityModel.getPropertyValueBySemantic("TILE_GEOMETRIC_ERROR"); + if (defined(semanticGeometricError)) { + tile.geometricError = semanticGeometricError as number; + } + + const semanticRefine = + metadataEntityModel.getPropertyValueBySemantic("TILE_REFINE"); + if (semanticRefine === 0) { + tile.refine = "ADD"; + } else if (semanticRefine === 1) { + tile.refine = "REPLACE"; + } + + const semanticTransform = + metadataEntityModel.getPropertyValueBySemantic("TILE_TRANSFORM"); + if (semanticTransform) { + tile.transform = semanticTransform; + } + } +} diff --git a/src/traversal/SubtreeMetadataModel.ts b/src/traversal/SubtreeMetadataModel.ts new file mode 100644 index 00000000..6c4f0829 --- /dev/null +++ b/src/traversal/SubtreeMetadataModel.ts @@ -0,0 +1,58 @@ +import { PropertyTableModel } from "../metadata/PropertyTableModel"; +import { Schema } from "../structure/Metadata/Schema"; + +/** + * An interface summarizing the metadata that may be associated + * with a subtree. + * + * (Note: One could consider to offer something like this in the + * `implicitTiling` package, alongside the `SubtreeInfo`. But + * the structure of this interface is too much tailored for + * its use in the traversal right now) + * + * @internal + */ +export interface SubtreeMetadataModel { + /** + * The schema to which this metadata complies + */ + schema: Schema; + + /** + * The tile metadata. + * + * This is the property table that is indexed via `subtree.tileMetadata` + * in the array of property tables that are defined by the subtree + * (or `undefined` when the subtree does not have tile metadata) + */ + tileMetadataModel: PropertyTableModel | undefined; + + /** + * The mapping from tile indices to the rows of the + * tileMetadataModel that contain the metadata for + * the respective tile. + * + * (See SubtreeMetadataModels.computeAvailabilityIndexingMapping + * for details) + */ + tileIndexMapping: number[] | undefined; + + /** + * The content metadata. + * + * These are the property tables that are indexed via `subtree.contentMetadata` + * in the array of property tables that are defined by the subtree, or + * the empty array when the subtree does not have content metadata. + */ + contentMetadataModels: PropertyTableModel[]; + + /** + * The mappings from content indices to the rows of the + * contentMetadataModels that contain the metadata for + * the respective content. + * + * (See SubtreeMetadataModels.computeAvailabilityIndexingMapping + * for details) + */ + contentIndexMappings: number[][]; +} diff --git a/src/traversal/SubtreeMetadataModels.ts b/src/traversal/SubtreeMetadataModels.ts new file mode 100644 index 00000000..8d083d64 --- /dev/null +++ b/src/traversal/SubtreeMetadataModels.ts @@ -0,0 +1,181 @@ +import { defined } from "../base/defined"; + +import { Schema } from "../structure/Metadata/Schema"; + +import { MetadataError } from "../metadata/MetadataError"; +import { MetadataUtilities } from "../metadata/MetadataUtilities"; +import { PropertyTableModel } from "../metadata/PropertyTableModel"; + +import { BinaryPropertyTable } from "../metadata/binary/BinaryPropertyTable"; +import { BinaryPropertyTableModel } from "../metadata/binary/BinaryPropertyTableModel"; + +import { BinarySubtreeData } from "../implicitTiling/BinarySubtreeData"; +import { SubtreeInfo } from "../implicitTiling/SubtreeInfo"; +import { AvailabilityInfo } from "../implicitTiling/AvailabilityInfo"; + +import { SubtreeMetadataModel } from "./SubtreeMetadataModel"; + +/** + * Methods to create `SubtreeMetadataModel` instances + * + * @internal + */ +export class SubtreeMetadataModels { + /** + * Creates a `SubtreeMetadataModel` from the given data. + * + * TODO Maybe add some proper comment here... + * + * @param binarySubtreeData - The `BinarySubtreeData` + * @param subtreeInfo - The `SubtreeInfo` + * @param schema - The metadata `Schema` + * @returns The `SubtreeMetadataModel` + */ + static create( + binarySubtreeData: BinarySubtreeData, + subtreeInfo: SubtreeInfo, + schema: Schema + ): SubtreeMetadataModel { + const subtree = binarySubtreeData.subtree; + const binaryBufferStructure = binarySubtreeData.binaryBufferStructure; + const binaryBufferData = binarySubtreeData.binaryBufferData; + + // Obtain the structural information about the schema that + // is required for creating the property table models + const propertyTableModels = []; + const binaryEnumInfo = MetadataUtilities.computeBinaryEnumInfo(schema); + const propertyTables = subtree.propertyTables; + if (propertyTables) { + const classes = schema.classes ?? {}; + + for (const propertyTable of propertyTables) { + const classId = propertyTable.class; + const metadataClass = classes[classId]; + if (!metadataClass) { + throw new MetadataError( + `The property table refers to class ${classId}, ` + + `but the schema does not define this class` + ); + } + + // Create the `BinaryPropertyTable` for each property table, + // which contains everything that is required for creating + // the binary PropertyTableModel + const binaryPropertyTable: BinaryPropertyTable = { + propertyTable: propertyTable, + metadataClass: metadataClass, + binaryEnumInfo: binaryEnumInfo, + binaryBufferStructure: binaryBufferStructure, + binaryBufferData: binaryBufferData, + }; + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + propertyTableModels.push(propertyTableModel); + } + } + + // Obtain the property table model that is pointed to + // by subtree.tileMetadata + let tileMetadataModel = undefined; + if (defined(subtree.tileMetadata)) { + tileMetadataModel = propertyTableModels[subtree.tileMetadata]; + if (!tileMetadataModel) { + throw new MetadataError( + `The subtree tileMetadata refers to property ` + + `table ${subtree.tileMetadata}, but the subtree only ` + + `defines ${propertyTableModels.length} property tables` + ); + } + } + + // Obtain the property table models that are pointed to + // by subtree.contentMetadata + const contentMetadataModels: PropertyTableModel[] = []; + if (subtree.contentMetadata) { + for (const contentMetadata of subtree.contentMetadata) { + const contentMetadataModel = propertyTableModels[contentMetadata]; + if (!contentMetadataModel) { + throw new MetadataError( + `The subtree contentMetadata refers to property ` + + `table ${contentMetadata}, but the subtree only ` + + `defines ${propertyTableModels.length} property tables` + ); + } + } + } + + // Compute the mapping from (available) tiles to the indices + // of their metadata (i.e. the row index in the property table + // that contains the metadata for the respective tile) + const tileAvailabilityInfo = subtreeInfo.tileAvailabilityInfo; + const tileMetadataIndexMapping = + SubtreeMetadataModels.computeAvailabilityIndexingMapping( + tileAvailabilityInfo + ); + + // Compute the mapping from (available) contents to the indices + // of their metadata (i.e. the row index in the property table + // that contains the metadata for the respective content) + const contentMetadataIndexMappings: number[][] = []; + const contentAvailabilityInfos = subtreeInfo.contentAvailabilityInfos; + for (const contentAvailabilityInfo of contentAvailabilityInfos) { + const contentMetadataIndexMapping = + SubtreeMetadataModels.computeAvailabilityIndexingMapping( + contentAvailabilityInfo + ); + contentMetadataIndexMappings.push(contentMetadataIndexMapping); + } + + const subtreeMetadataModel: SubtreeMetadataModel = { + schema: schema, + tileMetadataModel: tileMetadataModel, + tileIndexMapping: tileMetadataIndexMapping, + contentMetadataModels: contentMetadataModels, + contentIndexMappings: contentMetadataIndexMappings, + }; + return subtreeMetadataModel; + } + + /** + * Computes the mapping of indices inside the availability + * information to the number that says how many elements + * have been available up to this index, if the element + * at the respective index is available. + * + * Yes, that sounds complicated. But it is used for accessing + * the metadata that is stored in the subtree (see the 3D Tiles + * specification, "ImplicitTiling - Tile Metadata"). + * + * Quote: + * "If `i` available tiles occur before a particular tile, that + * tile’s property values are stored at index `i` of each + * property value array." + * + * This means that when the availability bitstream is + * [1, 0, 1, 1, 0] then this method will return an array + * [0, _, 1, 2, _]. + * + * The value of the `_` entries will be set to `-1`. + * + * @param availabilityInfo - The `AvailabilityInfo` + * @returns The index mapping + */ + private static computeAvailabilityIndexingMapping( + availabilityInfo: AvailabilityInfo + ): number[] { + const n = availabilityInfo.length; + const indexMapping = new Array(n); + let index = 0; + for (let i = 0; i < n; i++) { + const available = availabilityInfo.isAvailable(i); + if (available) { + indexMapping[i] = index; + index++; + } else { + indexMapping[i] = -1; + } + } + return indexMapping; + } +} diff --git a/src/traversal/SubtreeModel.ts b/src/traversal/SubtreeModel.ts new file mode 100644 index 00000000..b4bc7734 --- /dev/null +++ b/src/traversal/SubtreeModel.ts @@ -0,0 +1,18 @@ +import { SubtreeInfo } from "../implicitTiling/SubtreeInfo"; +import { SubtreeMetadataModel } from "./SubtreeMetadataModel"; + +/** + * An interface that summarizes the information for a subtree. + */ +export interface SubtreeModel { + /** + * The `SubtreeInfo` that summarizes the availability information + */ + subtreeInfo: SubtreeInfo; + + /** + * The optional `SubtreeMetadataModel` that contains the metadata + * associated with this subtree. + */ + subtreeMetadataModel: SubtreeMetadataModel | undefined; +} diff --git a/src/traversal/SubtreeModels.ts b/src/traversal/SubtreeModels.ts new file mode 100644 index 00000000..0fa85987 --- /dev/null +++ b/src/traversal/SubtreeModels.ts @@ -0,0 +1,163 @@ +import path from "path"; + +import { defined } from "../base/defined"; +import { Buffers } from "../base/Buffers"; + +import { ResourceResolver } from "../io/ResourceResolver"; + +import { BinarySubtreeData } from "../implicitTiling/BinarySubtreeData"; +import { BinarySubtreeDataResolver } from "../implicitTiling/BinarySubtreeDataResolver"; +import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; +import { ImplicitTilings } from "../implicitTiling/ImplicitTilings"; +import { SubtreeInfos } from "../implicitTiling/SubtreeInfos"; +import { TreeCoordinates } from "../implicitTiling/TreeCoordinates"; + +import { Subtree } from "../structure/Subtree"; +import { TileImplicitTiling } from "../structure/TileImplicitTiling"; +import { Schema } from "../structure/Metadata/Schema"; + +import { SubtreeModel } from "./SubtreeModel"; +import { SubtreeMetadataModels } from "./SubtreeMetadataModels"; + +/** + * Methods to resolve subtree information. + * + * The methods will resolve the data for a subtree, based on the template + * URI from the implicit tiling and the root coordinates of the subtree, + * and offer this information as `SubtreeModel` objects. + */ +export class SubtreeModels { + /** + * Resolve the `SubtreeModel` for the subtree with the given root coordinates. + * + * This will substitute the given coordinates into the subtree template + * URI from the given implicit tiling object. Then it will attempt to load + * the subtree data from this URI. The resulting data will be used to + * construct the `SubtreeModel` object. + * + * @param implicitTiling - The `TileImplicitTiling` + * @param schema - The optional metadata schema + * @param resourceResolver - The `ResourceResolver` for the subtree + * files and buffers + * @param coordinates - The root coordinates of the subtree + * @returns The `SubtreeModel` + * @throws ImplicitTilingError If the input was structurally invalid + */ + static async resolve( + implicitTiling: TileImplicitTiling, + schema: Schema | undefined, + resourceResolver: ResourceResolver, + coordinates: TreeCoordinates + ): Promise { + // Obtain the raw subtree data by resolving the data from + // the URI that is created from the template URI and the + // coordinates + const subtreeUri = ImplicitTilings.substituteTemplateUri( + implicitTiling.subdivisionScheme, + implicitTiling.subtrees.uri, + coordinates + ); + if (!defined(subtreeUri)) { + const message = + `Could not substitute coordinates ${coordinates} in ` + + `subtree template URI ${implicitTiling.subtrees.uri}`; + throw new ImplicitTilingError(message); + } + const subtreeData = await resourceResolver.resolveData(subtreeUri); + if (!subtreeData) { + const message = + `Could not resolve subtree URI ${subtreeUri} that was ` + + `created from template URI ${implicitTiling.subtrees.uri} ` + + `for coordinates ${coordinates}`; + throw new ImplicitTilingError(message); + } + + // Create the resource resolver that will be used + // for resolving references (buffer URIs) relative + // to the directory that contained the subtree data + const subtreeDirectory = path.dirname(subtreeUri); + const subtreeResourceResolver = resourceResolver.derive(subtreeDirectory); + + // If the subtree data was JSON, just parse it and + // create a SubtreeModel from it + const isJson = Buffers.isProbablyJson(subtreeData); + if (isJson) { + let subtreeJson: any; + let subtree: Subtree; + try { + subtreeJson = Buffers.getJson(subtreeData); + subtree = subtreeJson; + } catch (error) { + const message = + `Could not parse subtree JSON from URI ${subtreeUri} that was ` + + `created from template URI ${implicitTiling.subtrees.uri} ` + + `for coordinates ${coordinates}`; + throw new ImplicitTilingError(message); + } + + const binarySubtreeData = await BinarySubtreeDataResolver.resolveFromJson( + subtree, + subtreeResourceResolver + ); + const subtreeModel = SubtreeModels.create( + binarySubtreeData, + implicitTiling, + schema + ); + return subtreeModel; + } + + // For SUBT (binary subtree data), create the SubtreeModel + // from the whole buffer + const isSubt = Buffers.getMagic(subtreeData) === "subt"; + if (isSubt) { + const binarySubtreeData = + await BinarySubtreeDataResolver.resolveFromBuffer( + subtreeData, + subtreeResourceResolver + ); + const subtreeModel = SubtreeModels.create( + binarySubtreeData, + implicitTiling, + schema + ); + return subtreeModel; + } + + const message = + `Subtree data from URI ${subtreeUri} that was created from ` + + `template URI ${implicitTiling.subtrees.uri} for coordinates ` + + `${coordinates} did neither contain JSON nor binary subtree data`; + throw new ImplicitTilingError(message); + } + + /** + * Creates the `SubtreeModel` from the given binary subtree data + * + * @param binarySubtreeData - The binary subtree data + * @param implicitTiling The `TileImplicitTiling` + * @param schema The optional metadata schema + * @returns The `SubtreeModel` + * @throws ImplicitTilingError If the input was structurally invalid + */ + private static create( + binarySubtreeData: BinarySubtreeData, + implicitTiling: TileImplicitTiling, + schema: Schema | undefined + ): SubtreeModel { + const subtreeInfo = SubtreeInfos.create(binarySubtreeData, implicitTiling); + let subtreeMetadataModel = undefined; + if (schema) { + subtreeMetadataModel = SubtreeMetadataModels.create( + binarySubtreeData, + subtreeInfo, + schema + ); + } + const subtreeModel: SubtreeModel = { + subtreeInfo: subtreeInfo, + subtreeMetadataModel: subtreeMetadataModel, + }; + return subtreeModel; + } +} diff --git a/src/traversal/TraversedTile.ts b/src/traversal/TraversedTile.ts index 8a3a1b73..314707d4 100644 --- a/src/traversal/TraversedTile.ts +++ b/src/traversal/TraversedTile.ts @@ -13,19 +13,49 @@ export interface TraversedTile { /** * Returns a `Tile` object that contains the "JSON"-representation * of the tile. This is just a plain data structure corresponding - * the tile. + * to the tile. + * + * The returned object reflects the "raw" state of the tile that + * is either contained in the tileset JSON, or derived from the + * subdivision rules of implicit tiles. + * + * Specifically: This is the state BEFORE any semantic-based + * overrides have been applied. When there is metadata + * associated with the tile, and this metadata has semantics + * that override certain tile properties, then these overrides + * are NOT reflected in the returned tile. + * + * In order to obtain a tile where the semantic-based overrides + * are applied, `asFinalTile` can be used. * - * Values that may be overridden (for example, via metadata semantics) - * are already substituted in the returned object. + * Note that there are no guarantees about identities for the + * returned object. This means that callers should NOT modify + * the returned object or any of its properties, because they + * may be the actual objects that are stored in the tileset + * JSON. + * + * @returns A `Tile` with information about this traversed tile + * @throws ImplicitTilingError If the representation of this traversed + * tile could not be created due to invalid input structures. + */ + asRawTile(): Tile; + + /** + * Returns a `Tile` object that contains the "JSON"-representation + * of the tile. This is just a plain data structure corresponding + * the tile. * - * This means that the return value of this method reflects the - * actual tile if and only if the underlying metadata was valid. + * In contrast to `asRawTile`, this method returns a `Tile` object + * where semantic-based overrides have already been applied. When + * there is metadata associated with the tile, and this metadata + * has semantics that override certain tile properties, then these + * overrides ARE reflected in the returned tile. * * @returns A `Tile` with information about this traversed tile * @throws ImplicitTilingError If the representation of this traversed * tile could not be created due to invalid input structures. */ - asTile(): Tile; + asFinalTile(): Tile; /** * Returns the level of the tile in the traversed hierarchy, with @@ -41,6 +71,8 @@ export interface TraversedTile { * This resembles a JSON path. But for cases like implicit tilesets, * it may contain elements that are not part of the JSONPath format. * It may therefore only be used as a semi-human-readable identifier. + * The exact format is not specified. Callers should not rely on + * the format. * * @returns The path */ From b38274eaf1e29fbeb6493bf71ac7123e73bc90f9 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 11 Mar 2023 16:55:50 +0100 Subject: [PATCH 10/26] Add inheritDoc annotations for interface implementations --- src/implicitTiling/BufferAvailabilityInfo.ts | 2 ++ src/implicitTiling/ConstantAvailabilityInfo.ts | 2 ++ src/implicitTiling/OctreeCoordinates.ts | 7 +++++++ src/implicitTiling/QuadtreeCoordinates.ts | 7 +++++++ src/io/FileResourceResolver.ts | 5 +++++ src/io/TilesetSourceResourceResolver.ts | 4 ++++ src/io/UnzippingResourceResolver.ts | 5 +++++ src/metadata/DefaultMetadataEntityModel.ts | 2 ++ src/metadata/binary/BinaryMetadataEntityModel.ts | 2 ++ src/metadata/binary/BooleanArrayPropertyModel.ts | 1 + src/metadata/binary/BooleanPropertyModel.ts | 1 + src/metadata/binary/NumericArrayPropertyModel.ts | 1 + src/metadata/binary/NumericPropertyModel.ts | 1 + src/metadata/binary/StringArrayPropertyModel.ts | 1 + src/metadata/binary/StringPropertyModel.ts | 1 + src/packages/TilesetSource3dtiles.ts | 4 ++++ src/packages/TilesetSource3tz.ts | 6 +++++- src/packages/TilesetTarget3dtiles.ts | 3 +++ src/packages/TilesetTarget3tz.ts | 14 ++++++++++++++ src/tilesetData/TilesetSourceFs.ts | 9 ++++++++- src/tilesetData/TilesetTargetFs.ts | 3 +++ src/traversal/ExplicitTraversedTile.ts | 11 +++++++++++ 22 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/implicitTiling/BufferAvailabilityInfo.ts b/src/implicitTiling/BufferAvailabilityInfo.ts index 5fef1a64..aa70894f 100644 --- a/src/implicitTiling/BufferAvailabilityInfo.ts +++ b/src/implicitTiling/BufferAvailabilityInfo.ts @@ -13,10 +13,12 @@ export class BufferAvailabilityInfo implements AvailabilityInfo { this._length = length; } + /** {@inheritDoc AvailabilityInfo.length} */ get length(): number { return this._length; } + /** {@inheritDoc AvailabilityInfo.isAvailable} */ isAvailable(index: number): boolean { if (index < 0 || index >= this.length) { throw new RangeError( diff --git a/src/implicitTiling/ConstantAvailabilityInfo.ts b/src/implicitTiling/ConstantAvailabilityInfo.ts index d0c32ef0..9d2e2f96 100644 --- a/src/implicitTiling/ConstantAvailabilityInfo.ts +++ b/src/implicitTiling/ConstantAvailabilityInfo.ts @@ -12,10 +12,12 @@ export class ConstantAvailabilityInfo implements AvailabilityInfo { this._length = length; } + /** {@inheritDoc AvailabilityInfo.length} */ get length(): number { return this._length; } + /** {@inheritDoc AvailabilityInfo.isAvailable} */ isAvailable(index: number): boolean { if (index < 0 || index >= this.length) { throw new RangeError( diff --git a/src/implicitTiling/OctreeCoordinates.ts b/src/implicitTiling/OctreeCoordinates.ts index c7288c1e..a2651b5e 100644 --- a/src/implicitTiling/OctreeCoordinates.ts +++ b/src/implicitTiling/OctreeCoordinates.ts @@ -18,6 +18,7 @@ export class OctreeCoordinates implements TreeCoordinates { this._z = z; } + /** {@inheritDoc TreeCoordinates.level} */ get level(): number { return this._level; } @@ -34,6 +35,7 @@ export class OctreeCoordinates implements TreeCoordinates { return this._z; } + /** {@inheritDoc TreeCoordinates.parent} */ parent(): OctreeCoordinates | null { if (this._level === 0) { return null; @@ -45,6 +47,7 @@ export class OctreeCoordinates implements TreeCoordinates { return new OctreeCoordinates(pLevel, px, py, pz); } + /** {@inheritDoc TreeCoordinates.children} */ *children(): IterableIterator { const nLevel = this._level + 1; const nX = this._x << 1; @@ -60,6 +63,7 @@ export class OctreeCoordinates implements TreeCoordinates { yield new OctreeCoordinates(nLevel, nX + 1, nY + 1, nZ + 1); } + /** {@inheritDoc TreeCoordinates.descendants} */ descendants( maxLevelInclusive: number, depthFirst: boolean @@ -85,15 +89,18 @@ export class OctreeCoordinates implements TreeCoordinates { return result; } + /** {@inheritDoc TreeCoordinates.toArray} */ toArray(): number[] { return [this.level, this.x, this.y, this.z]; } + /** {@inheritDoc TreeCoordinates.toIndex} */ toIndex(): number { const offset = Octrees.computeNumberOfNodesForLevels(this._level); return offset + this.toIndexInLevel(); } + /** {@inheritDoc TreeCoordinates.toIndexInLevel} */ toIndexInLevel(): number { return MortonOrder.encode3D(this._x, this._y, this._z); } diff --git a/src/implicitTiling/QuadtreeCoordinates.ts b/src/implicitTiling/QuadtreeCoordinates.ts index 790a2445..7358a0f5 100644 --- a/src/implicitTiling/QuadtreeCoordinates.ts +++ b/src/implicitTiling/QuadtreeCoordinates.ts @@ -16,6 +16,7 @@ export class QuadtreeCoordinates implements TreeCoordinates { this._y = y; } + /** {@inheritDoc TreeCoordinates.level} */ get level(): number { return this._level; } @@ -28,6 +29,7 @@ export class QuadtreeCoordinates implements TreeCoordinates { return this._y; } + /** {@inheritDoc TreeCoordinates.parent} */ parent(): QuadtreeCoordinates | null { if (this._level === 0) { return null; @@ -38,6 +40,7 @@ export class QuadtreeCoordinates implements TreeCoordinates { return new QuadtreeCoordinates(pLevel, px, py); } + /** {@inheritDoc TreeCoordinates.children} */ *children(): IterableIterator { const nLevel = this._level + 1; const nX = this._x << 1; @@ -48,6 +51,7 @@ export class QuadtreeCoordinates implements TreeCoordinates { yield new QuadtreeCoordinates(nLevel, nX + 1, nY + 1); } + /** {@inheritDoc TreeCoordinates.descendants} */ descendants( maxLevelInclusive: number, depthFirst: boolean @@ -73,15 +77,18 @@ export class QuadtreeCoordinates implements TreeCoordinates { return result; } + /** {@inheritDoc TreeCoordinates.toArray} */ toArray(): number[] { return [this.level, this.x, this.y]; } + /** {@inheritDoc TreeCoordinates.toIndex} */ toIndex(): number { const offset = Quadtrees.computeNumberOfNodesForLevels(this._level); return offset + this.toIndexInLevel(); } + /** {@inheritDoc TreeCoordinates.toIndexInLevel} */ toIndexInLevel(): number { return MortonOrder.encode2D(this._x, this._y); } diff --git a/src/io/FileResourceResolver.ts b/src/io/FileResourceResolver.ts index 3b2165fa..6211ed1d 100644 --- a/src/io/FileResourceResolver.ts +++ b/src/io/FileResourceResolver.ts @@ -17,12 +17,14 @@ export class FileResourceResolver implements ResourceResolver { this._basePath = basePath; } + /** {@inheritDoc ResourceResolver.resolveUri} */ resolveUri(uri: string): string { let resolved = path.resolve(this._basePath, decodeURIComponent(uri)); resolved = resolved.replace(/\\/g, "/"); return resolved; } + /** {@inheritDoc ResourceResolver.resolveDataPartial} */ async resolveDataPartial( uri: string, maxBytes: number @@ -46,6 +48,7 @@ export class FileResourceResolver implements ResourceResolver { } } + /** {@inheritDoc ResourceResolver.resolveData} */ async resolveData(uri: string): Promise { if (Uris.isDataUri(uri)) { const data = Buffer.from(uri.split(",")[1], "base64"); @@ -66,6 +69,8 @@ export class FileResourceResolver implements ResourceResolver { ); return Buffer.from(actualData); } + + /** {@inheritDoc ResourceResolver.derive} */ derive(uri: string): ResourceResolver { const resolved = path.join(this._basePath, decodeURIComponent(uri)); return new FileResourceResolver(resolved); diff --git a/src/io/TilesetSourceResourceResolver.ts b/src/io/TilesetSourceResourceResolver.ts index cdd42603..4d389ef2 100644 --- a/src/io/TilesetSourceResourceResolver.ts +++ b/src/io/TilesetSourceResourceResolver.ts @@ -27,11 +27,13 @@ export class TilesetSourceResourceResolver implements ResourceResolver { this._tilesetSource = tilesetSource; } + /** {@inheritDoc ResourceResolver.resolveUri} */ resolveUri(uri: string): string { const resolved = path.resolve(this._basePath, decodeURIComponent(uri)); return resolved; } + /** {@inheritDoc ResourceResolver.resolveData} */ async resolveData(uri: string): Promise { if (Uris.isDataUri(uri)) { const data = Buffer.from(uri.split(",")[1], "base64"); @@ -48,6 +50,7 @@ export class TilesetSourceResourceResolver implements ResourceResolver { return value; } + /** {@inheritDoc ResourceResolver.resolveDataPartial} */ async resolveDataPartial( uri: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -56,6 +59,7 @@ export class TilesetSourceResourceResolver implements ResourceResolver { return await this.resolveData(uri); } + /** {@inheritDoc ResourceResolver.derive} */ derive(uri: string): ResourceResolver { const resolved = Paths.join(this._basePath, decodeURIComponent(uri)); const result = new TilesetSourceResourceResolver( diff --git a/src/io/UnzippingResourceResolver.ts b/src/io/UnzippingResourceResolver.ts index c67dc165..2676d28b 100644 --- a/src/io/UnzippingResourceResolver.ts +++ b/src/io/UnzippingResourceResolver.ts @@ -17,10 +17,12 @@ export class UnzippingResourceResolver implements ResourceResolver { this._delegate = delegate; } + /** {@inheritDoc ResourceResolver.resolveUri} */ resolveUri(uri: string): string { return this._delegate.resolveUri(uri); } + /** {@inheritDoc ResourceResolver.resolveData} */ async resolveData(uri: string): Promise { const delegateData = await this._delegate.resolveData(uri); if (delegateData === null) { @@ -34,6 +36,7 @@ export class UnzippingResourceResolver implements ResourceResolver { return data; } + /** {@inheritDoc ResourceResolver.resolveDataPartial} */ async resolveDataPartial( uri: string, maxBytes: number @@ -56,6 +59,8 @@ export class UnzippingResourceResolver implements ResourceResolver { const data = zlib.gunzipSync(fullDelegateData); return data; } + + /** {@inheritDoc ResourceResolver.derive} */ derive(uri: string): ResourceResolver { return new UnzippingResourceResolver(this._delegate.derive(uri)); } diff --git a/src/metadata/DefaultMetadataEntityModel.ts b/src/metadata/DefaultMetadataEntityModel.ts index 545019a8..d36cc644 100644 --- a/src/metadata/DefaultMetadataEntityModel.ts +++ b/src/metadata/DefaultMetadataEntityModel.ts @@ -30,6 +30,7 @@ export class DefaultMetadataEntityModel implements MetadataEntityModel { this._json = json; } + /** {@inheritDoc MetadataEntityModel.getPropertyValue} */ getPropertyValue(propertyId: string): any { const properties = this._metadataClass.properties; if (!properties) { @@ -45,6 +46,7 @@ export class DefaultMetadataEntityModel implements MetadataEntityModel { return MetadataValues.processValue(property, undefined, undefined, value); } + /** {@inheritDoc MetadataEntityModel.getPropertyValueBySemantic} */ getPropertyValueBySemantic(semantic: string): any { const propertyId = this._semanticToPropertyId[semantic]; if (!defined(propertyId)) { diff --git a/src/metadata/binary/BinaryMetadataEntityModel.ts b/src/metadata/binary/BinaryMetadataEntityModel.ts index 5922776b..546b6584 100644 --- a/src/metadata/binary/BinaryMetadataEntityModel.ts +++ b/src/metadata/binary/BinaryMetadataEntityModel.ts @@ -26,6 +26,7 @@ export class BinaryMetadataEntityModel implements MetadataEntityModel { this._semanticToPropertyId = semanticToPropertyId; } + /** {@inheritDoc MetadataEntityModel.getPropertyValue} */ getPropertyValue(propertyId: string): any { const propertyTableModel = this._propertyTableModel; const classProperty = propertyTableModel.getClassProperty(propertyId); @@ -58,6 +59,7 @@ export class BinaryMetadataEntityModel implements MetadataEntityModel { return processedValue; } + /** {@inheritDoc MetadataEntityModel.getPropertyValueBySemantic} */ getPropertyValueBySemantic(semantic: string): any { const propertyId = this._semanticToPropertyId[semantic]; if (!defined(propertyId)) { diff --git a/src/metadata/binary/BooleanArrayPropertyModel.ts b/src/metadata/binary/BooleanArrayPropertyModel.ts index e376c89d..014934fb 100644 --- a/src/metadata/binary/BooleanArrayPropertyModel.ts +++ b/src/metadata/binary/BooleanArrayPropertyModel.ts @@ -26,6 +26,7 @@ export class BooleanArrayPropertyModel implements PropertyModel { this._count = count; } + /** {@inheritDoc PropertyModel.getPropertyValue} */ getPropertyValue(index: number): any { const valuesBuffer = this._valuesBuffer; const arrayOffsetsBuffer = this._arrayOffsetsBuffer; diff --git a/src/metadata/binary/BooleanPropertyModel.ts b/src/metadata/binary/BooleanPropertyModel.ts index 094a4952..daca8b6a 100644 --- a/src/metadata/binary/BooleanPropertyModel.ts +++ b/src/metadata/binary/BooleanPropertyModel.ts @@ -14,6 +14,7 @@ export class BooleanPropertyModel implements PropertyModel { this._valuesBuffer = valuesBuffer; } + /** {@inheritDoc PropertyModel.getPropertyValue} */ getPropertyValue(index: number): any { const valuesBuffer = this._valuesBuffer; const result = BooleanPropertyModel.getBooleanFromBuffer( diff --git a/src/metadata/binary/NumericArrayPropertyModel.ts b/src/metadata/binary/NumericArrayPropertyModel.ts index 1a32a06a..e9f2e047 100644 --- a/src/metadata/binary/NumericArrayPropertyModel.ts +++ b/src/metadata/binary/NumericArrayPropertyModel.ts @@ -37,6 +37,7 @@ export class NumericArrayPropertyModel implements PropertyModel { this._count = count; } + /** {@inheritDoc PropertyModel.getPropertyValue} */ getPropertyValue(index: number): any { const type = this._type; const valuesBuffer = this._valuesBuffer; diff --git a/src/metadata/binary/NumericPropertyModel.ts b/src/metadata/binary/NumericPropertyModel.ts index 0edebcfd..81cae276 100644 --- a/src/metadata/binary/NumericPropertyModel.ts +++ b/src/metadata/binary/NumericPropertyModel.ts @@ -23,6 +23,7 @@ export class NumericPropertyModel implements PropertyModel { this._componentType = componentType; } + /** {@inheritDoc PropertyModel.getPropertyValue} */ getPropertyValue(index: number): any { const valuesBuffer = this._valuesBuffer; const componentType = this._componentType; diff --git a/src/metadata/binary/StringArrayPropertyModel.ts b/src/metadata/binary/StringArrayPropertyModel.ts index c253fde4..56488dae 100644 --- a/src/metadata/binary/StringArrayPropertyModel.ts +++ b/src/metadata/binary/StringArrayPropertyModel.ts @@ -40,6 +40,7 @@ export class StringArrayPropertyModel implements PropertyModel { this._count = count; } + /** {@inheritDoc PropertyModel.getPropertyValue} */ getPropertyValue(index: number): any { const valuesBuffer = this._valuesBuffer; const arrayOffsetsBuffer = this._arrayOffsetsBuffer; diff --git a/src/metadata/binary/StringPropertyModel.ts b/src/metadata/binary/StringPropertyModel.ts index 7dcf485c..0f669c42 100644 --- a/src/metadata/binary/StringPropertyModel.ts +++ b/src/metadata/binary/StringPropertyModel.ts @@ -25,6 +25,7 @@ export class StringPropertyModel implements PropertyModel { this._stringOffsetType = stringOffsetType; } + /** {@inheritDoc PropertyModel.getPropertyValue} */ getPropertyValue(index: number): any { const valuesBuffer = this._valuesBuffer; const stringOffsetsBuffer = this._stringOffsetsBuffer; diff --git a/src/packages/TilesetSource3dtiles.ts b/src/packages/TilesetSource3dtiles.ts index 3d4024be..bedda030 100644 --- a/src/packages/TilesetSource3dtiles.ts +++ b/src/packages/TilesetSource3dtiles.ts @@ -25,6 +25,7 @@ export class TilesetSource3dtiles implements TilesetSource { this.db = undefined; } + /** {@inheritDoc TilesetSource.open} */ open(fullInputName: string): void { if (this.db) { throw new TilesetError("Database already opened"); @@ -32,6 +33,7 @@ export class TilesetSource3dtiles implements TilesetSource { this.db = new DatabaseConstructor(fullInputName); } + /** {@inheritDoc TilesetSource.getKeys} */ getKeys(): IterableIterator { if (!this.db) { throw new TilesetError("Source is not opened. Call 'open' first."); @@ -41,6 +43,7 @@ export class TilesetSource3dtiles implements TilesetSource { return Iterables.map(iterator, (row) => row.key); } + /** {@inheritDoc TilesetSource.getValue} */ getValue(key: string): Buffer | undefined { if (!this.db) { throw new Error("Source is not opened. Call 'open' first."); @@ -53,6 +56,7 @@ export class TilesetSource3dtiles implements TilesetSource { return undefined; } + /** {@inheritDoc TilesetSource.close} */ close() { if (!this.db) { throw new Error("Source is not opened. Call 'open' first."); diff --git a/src/packages/TilesetSource3tz.ts b/src/packages/TilesetSource3tz.ts index 529a21da..37b9e614 100644 --- a/src/packages/TilesetSource3tz.ts +++ b/src/packages/TilesetSource3tz.ts @@ -41,6 +41,7 @@ export class TilesetSource3tz implements TilesetSource { return this.zipIndex; } + /** {@inheritDoc TilesetSource.open} */ open(fullInputName: string) { if (defined(this.fd)) { throw new TilesetError("Source already opened"); @@ -50,6 +51,7 @@ export class TilesetSource3tz implements TilesetSource { this.zipIndex = ArchiveFunctions3tz.readZipIndex(this.fd); } + /** {@inheritDoc TilesetSource.getKeys} */ getKeys(): IterableIterator { if (!defined(this.fd) || !this.zipIndex) { throw new TilesetError("Source is not opened. Call 'open' first."); @@ -81,7 +83,8 @@ export class TilesetSource3tz implements TilesetSource { return iterator; } - getValue(key: string) { + /** {@inheritDoc TilesetSource.getValue} */ + getValue(key: string): Buffer | undefined { if (!defined(this.fd) || !this.zipIndex) { throw new TilesetError("Source is not opened. Call 'open' first."); } @@ -93,6 +96,7 @@ export class TilesetSource3tz implements TilesetSource { return entryData; } + /** {@inheritDoc TilesetSource.close} */ close() { if (!defined(this.fd) || !this.zipIndex) { throw new TilesetError("Source is not opened. Call 'open' first."); diff --git a/src/packages/TilesetTarget3dtiles.ts b/src/packages/TilesetTarget3dtiles.ts index 7445975a..965f258b 100644 --- a/src/packages/TilesetTarget3dtiles.ts +++ b/src/packages/TilesetTarget3dtiles.ts @@ -26,6 +26,7 @@ export class TilesetTarget3dtiles implements TilesetTarget { this.db = undefined; } + /** {@inheritDoc TilesetTarget.begin} */ begin(fullOutputName: string, overwrite: boolean): void { if (fs.existsSync(fullOutputName)) { if (overwrite) { @@ -51,6 +52,7 @@ export class TilesetTarget3dtiles implements TilesetTarget { .run(); } + /** {@inheritDoc TilesetTarget.addEntry} */ addEntry(key: string, content: Buffer): void { if (!this.db) { throw new TilesetError("Target is not opened. Call 'begin' first."); @@ -59,6 +61,7 @@ export class TilesetTarget3dtiles implements TilesetTarget { insertion.run(key, content); } + /** {@inheritDoc TilesetTarget.end} */ async end(): Promise { if (!this.db) { throw new TilesetError("Target is not opened. Call 'begin' first."); diff --git a/src/packages/TilesetTarget3tz.ts b/src/packages/TilesetTarget3tz.ts index 28debca7..3827d565 100644 --- a/src/packages/TilesetTarget3tz.ts +++ b/src/packages/TilesetTarget3tz.ts @@ -45,6 +45,7 @@ export class TilesetTarget3tz implements TilesetTarget { this.indexBuilder = new IndexBuilder(); } + /** {@inheritDoc TilesetTarget.begin} */ begin(fullOutputName: string, overwrite: boolean) { if (fs.existsSync(fullOutputName)) { if (overwrite) { @@ -83,6 +84,17 @@ export class TilesetTarget3tz implements TilesetTarget { }); } + /** + * Creates a promise that is fulfilled when the data has fully been + * written to the target. Or maybe not. In any case, one has to wait + * for the promise that is returned from "archiver.finalize()" AND for + * this promise, to make sure that everything is written. For details, + * see https://github.com/archiverjs/node-archiver/issues/476 ... + * + * @param archive - The archiver archive + * @param outputStream - The output stream that the archive is writing to + * @returns The promise that has to be waited for in "close" + */ private static createFinishedPromise( archive: archiver.Archiver, outputStream: fs.WriteStream @@ -95,6 +107,7 @@ export class TilesetTarget3tz implements TilesetTarget { return finishedPromise; } + /** {@inheritDoc TilesetTarget.addEntry} */ addEntry(key: string, content: Buffer) { if (!this.archive) { throw new TilesetError("Target is not opened. Call 'begin' first."); @@ -103,6 +116,7 @@ export class TilesetTarget3tz implements TilesetTarget { this.indexBuilder.addEntry(key, content.length); } + /** {@inheritDoc TilesetTarget.end} */ async end() { if (!this.archive) { throw new TilesetError("Target is not opened. Call 'begin' first."); diff --git a/src/tilesetData/TilesetSourceFs.ts b/src/tilesetData/TilesetSourceFs.ts index ce41a949..4fe827ff 100644 --- a/src/tilesetData/TilesetSourceFs.ts +++ b/src/tilesetData/TilesetSourceFs.ts @@ -26,6 +26,7 @@ export class TilesetSourceFs implements TilesetSource { this.fullInputName = undefined; } + /** {@inheritDoc TilesetSource.open} */ open(fullInputName: string) { if (this.fullInputName) { throw new TilesetError("Source already opened"); @@ -33,6 +34,7 @@ export class TilesetSourceFs implements TilesetSource { this.fullInputName = fullInputName; } + /** {@inheritDoc TilesetSource.getKeys} */ getKeys() { if (!this.fullInputName) { throw new TilesetError("Source is not opened. Call 'open' first."); @@ -45,7 +47,8 @@ export class TilesetSourceFs implements TilesetSource { ); } - getValue(key: string) { + /** {@inheritDoc TilesetSource.getValue} */ + getValue(key: string): Buffer | undefined { if (!this.fullInputName) { throw new TilesetError("Source is not opened. Call 'open' first."); } @@ -54,9 +57,13 @@ export class TilesetSourceFs implements TilesetSource { return undefined; } const data = fs.readFileSync(fullFileName); + if (data === null) { + return undefined; + } return data; } + /** {@inheritDoc TilesetSource.close} */ close() { if (!this.fullInputName) { throw new TilesetError("Source is not opened. Call 'open' first."); diff --git a/src/tilesetData/TilesetTargetFs.ts b/src/tilesetData/TilesetTargetFs.ts index dcd7f9ef..9681be63 100644 --- a/src/tilesetData/TilesetTargetFs.ts +++ b/src/tilesetData/TilesetTargetFs.ts @@ -30,6 +30,7 @@ export class TilesetTargetFs implements TilesetTarget { this.overwrite = false; } + /** {@inheritDoc TilesetTarget.begin} */ begin(fullOutputName: string, overwrite: boolean) { if (this.fullOutputName) { throw new TilesetError("Target already opened"); @@ -41,6 +42,7 @@ export class TilesetTargetFs implements TilesetTarget { } } + /** {@inheritDoc TilesetTarget.addEntry} */ addEntry(key: string, content: Buffer) { if (!this.fullOutputName) { throw new TilesetError("Target is not opened. Call 'begin' first."); @@ -56,6 +58,7 @@ export class TilesetTargetFs implements TilesetTarget { fs.writeFileSync(fullOutputFileName, content); } + /** {@inheritDoc TilesetTarget.end} */ async end() { if (!this.fullOutputName) { throw new TilesetError("Target is not opened. Call 'begin' first."); diff --git a/src/traversal/ExplicitTraversedTile.ts b/src/traversal/ExplicitTraversedTile.ts index acd6dc2f..7d6c1663 100644 --- a/src/traversal/ExplicitTraversedTile.ts +++ b/src/traversal/ExplicitTraversedTile.ts @@ -76,10 +76,12 @@ export class ExplicitTraversedTile implements TraversedTile { this._resourceResolver = resourceResolver; } + /** {@inheritDoc TraversedTile.asRawTile} */ asRawTile(): Tile { return this._tile; } + /** {@inheritDoc TraversedTile.asFinalTile} */ asFinalTile(): Tile { const tile = this._tile; @@ -141,17 +143,22 @@ export class ExplicitTraversedTile implements TraversedTile { MetadataSemanticOverrides.applyToTile(finalTile, metadataEntityModel); } + /** {@inheritDoc TraversedTile.path} */ get path(): string { return this._path; } + + /** {@inheritDoc TraversedTile.level} */ get level(): number { return this._level; } + /** {@inheritDoc TraversedTile.getParent} */ getParent(): TraversedTile | undefined { return this._parent; } + /** {@inheritDoc TraversedTile.getChildren} */ async getChildren(): Promise { const implicitTiling = this._tile.implicitTiling; const schema = this._schema; @@ -187,6 +194,7 @@ export class ExplicitTraversedTile implements TraversedTile { return traversedChildren; } + /** {@inheritDoc TraversedTile.getContents} */ getContents(): Content[] { if (this._tile.content) { return [this._tile.content]; @@ -197,6 +205,7 @@ export class ExplicitTraversedTile implements TraversedTile { return []; } + /** {@inheritDoc TraversedTile.getSubtreeUri} - PRELIMINARY */ getSubtreeUri(): string | undefined { const implicitTiling = this._tile.implicitTiling; if (implicitTiling) { @@ -212,10 +221,12 @@ export class ExplicitTraversedTile implements TraversedTile { return undefined; } + /** {@inheritDoc TraversedTile.getImplicitTiling} - PRELIMINARY */ getImplicitTiling(): TileImplicitTiling | undefined { return this._tile.implicitTiling; } + /** {@inheritDoc TraversedTile.getMetadata} - PRELIMINARY */ getMetadata(): MetadataEntity | undefined { return this._tile.metadata; } From ac6b8db0e3c91258c29afa0f40feee0bafd70e8e Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 11 Mar 2023 16:56:11 +0100 Subject: [PATCH 11/26] Apply tile-based semantic overrides for implicit tiles --- src/traversal/ImplicitTraversedTile.ts | 45 ++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/traversal/ImplicitTraversedTile.ts b/src/traversal/ImplicitTraversedTile.ts index 8dad56f1..41e139c5 100644 --- a/src/traversal/ImplicitTraversedTile.ts +++ b/src/traversal/ImplicitTraversedTile.ts @@ -16,6 +16,7 @@ import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; import { ImplicitTilings } from "../implicitTiling/ImplicitTilings"; import { BoundingVolumeDerivation } from "./cesium/BoundingVolumeDerivation"; +import { MetadataSemanticOverrides } from "./MetadataSemanticOverrides"; /** * An implementation of a `TraversedTile` that represents a tile @@ -106,6 +107,7 @@ export class ImplicitTraversedTile implements TraversedTile { this._parent = parent; } + /** {@inheritDoc TraversedTile.asRawTile} */ asRawTile(): Tile { const rootTile = this._root.asFinalTile(); @@ -143,33 +145,66 @@ export class ImplicitTraversedTile implements TraversedTile { }; } + /** {@inheritDoc TraversedTile.asFinalTile} */ asFinalTile(): Tile { const tile = this.asRawTile(); - // TODO Apply overrides here - const TODO = "Apply overrides here"; - + const subtreeMetadataModel = this._subtreeModel.subtreeMetadataModel; + if (subtreeMetadataModel) { + if ( + subtreeMetadataModel.tileMetadataModel && + subtreeMetadataModel.tileIndexMapping + ) { + const tileIndex = this._localCoordinate.toIndex(); + const metadataIndex = subtreeMetadataModel.tileIndexMapping[tileIndex]; + const tileMetadataModel = subtreeMetadataModel.tileMetadataModel; + const metadataEntityModel = + tileMetadataModel.getMetadataEntityModel(metadataIndex); + MetadataSemanticOverrides.applyToTile(tile, metadataEntityModel); + } + } return tile; } + /** {@inheritDoc TraversedTile.path} */ get path(): string { return this._path; } + + /** {@inheritDoc TraversedTile.level} */ get level(): number { return this._globalLevel; } + + /** + * Returns the local coordinate of this implicit tile. + * + * This is the coordinate referring to the nearest subtree root. + * + * @returns The local coordinate + */ getLocalCoordinate(): TreeCoordinates { return this._localCoordinate; } + /** + * Returns the global coordinate of this implicit tile. + * + * This is the coordinate referring to the root of the + * implicit tile hierarchy. + * + * @returns The global coordinate + */ getGlobalCoordinate(): TreeCoordinates { return this._globalCoordinate; } + /** {@inheritDoc TraversedTile.getParent} */ getParent(): TraversedTile | undefined { return this._parent; } + /** {@inheritDoc TraversedTile.getChildren} */ async getChildren(): Promise { const localLevel = this._localCoordinate.level; if (localLevel === this._implicitTiling.subtreeLevels - 1) { @@ -294,6 +329,7 @@ export class ImplicitTraversedTile implements TraversedTile { return traversedChildren; } + /** {@inheritDoc TraversedTile.getContents} */ getContents(): Content[] { const contents = []; const subtreeInfo = this._subtreeModel.subtreeInfo; @@ -327,6 +363,7 @@ export class ImplicitTraversedTile implements TraversedTile { return contents; } + /** {@inheritDoc TraversedTile.getSubtreeUri} PRELIMINARY */ getSubtreeUri(): string | undefined { const localCoordinate = this._localCoordinate; if (localCoordinate.level === 0) { @@ -342,6 +379,7 @@ export class ImplicitTraversedTile implements TraversedTile { return undefined; } + /** {@inheritDoc TraversedTile.getImplicitTiling} PRELIMINARY */ getImplicitTiling(): TileImplicitTiling | undefined { const localCoordinate = this._localCoordinate; if (localCoordinate.level === 0) { @@ -349,6 +387,7 @@ export class ImplicitTraversedTile implements TraversedTile { } } + /** {@inheritDoc TraversedTile.getMetadata} PRELIMINARY */ getMetadata(): MetadataEntity | undefined { return undefined; } From 89c8cba866e7db62e048bffa8f0ce275021a8a13 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 11 Mar 2023 18:34:15 +0100 Subject: [PATCH 12/26] Metadata overrides for content. Differentiate "raw content" and "final content", similar to "raw tile" and "final tile". Some cleanups and documentation still pending. --- demos/TraversalDemo.ts | 6 +- src/traversal/ExplicitTraversedTile.ts | 108 ++++++----- src/traversal/ImplicitTraversedTile.ts | 51 +++-- src/traversal/MetadataSemanticOverrides.ts | 210 ++++++++++++++++++++- src/traversal/SubtreeMetadataModel.ts | 17 +- src/traversal/TraversedTile.ts | 7 +- 6 files changed, 320 insertions(+), 79 deletions(-) diff --git a/demos/TraversalDemo.ts b/demos/TraversalDemo.ts index ab89eb64..9cedff38 100644 --- a/demos/TraversalDemo.ts +++ b/demos/TraversalDemo.ts @@ -44,7 +44,7 @@ async function tilesetTraversalDemo(filePath: string) { schema, resourceResolver, async (traversedTile) => { - const contentUris = traversedTile.getContents().map((c) => c.uri); + const contentUris = traversedTile.getFinalContents().map((c) => c.uri); const geometricError = traversedTile.asFinalTile().geometricError; console.log( ` Traversed tile: ${traversedTile}, ` + @@ -60,9 +60,9 @@ async function tilesetTraversalDemo(filePath: string) { } async function runDemo() { - const tilesetFile = + const tilesetFileName = "../3d-tiles-samples/1.1/SparseImplicitQuadtree/tileset.json"; - await tilesetTraversalDemo(tilesetFile); + await tilesetTraversalDemo(tilesetFileName); } runDemo(); diff --git a/src/traversal/ExplicitTraversedTile.ts b/src/traversal/ExplicitTraversedTile.ts index 7d6c1663..f8d45187 100644 --- a/src/traversal/ExplicitTraversedTile.ts +++ b/src/traversal/ExplicitTraversedTile.ts @@ -10,11 +10,8 @@ import { TileImplicitTiling } from "../structure/TileImplicitTiling"; import { MetadataEntity } from "../structure/MetadataEntity"; import { Schema } from "../structure/Metadata/Schema"; -import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; import { ImplicitTilings } from "../implicitTiling/ImplicitTilings"; -import { MetadataEntityModels } from "../metadata/MetadataEntityModels"; - /** * An implementation of a `TraversedTile` that reflects a tile * that actually appears as a JSON representation in the tileset. @@ -85,14 +82,16 @@ export class ExplicitTraversedTile implements TraversedTile { asFinalTile(): Tile { const tile = this._tile; + const contents = this.getFinalContents(); + const finalTile = { boundingVolume: tile.boundingVolume, viewerRequestVolume: tile.viewerRequestVolume, geometricError: tile.geometricError, refine: tile.refine, transform: tile.transform, - content: tile.content, - contents: tile.contents, + content: undefined, + contents: contents, children: tile.children, metadata: tile.metadata, implicitTiling: tile.implicitTiling, @@ -101,48 +100,15 @@ export class ExplicitTraversedTile implements TraversedTile { }; const schema = this._schema; - const metadata = tile.metadata; - if (metadata && schema) { - this.applyMetadataSemanticOverrides(finalTile); + if (schema) { + MetadataSemanticOverrides.applyExplicitTileMetadataSemanticOverrides( + finalTile, + schema + ); } return finalTile; } - /** - * Perform the overrides of the properties of the given tile that - * are given by metadata semantics. - * - * If this instance contains a `Schema` and a `MetadataEntity`, - * then the property values of that metadata entity are examined. - * The property values that have a semantic will be used to - * override the corresponding values in the given tile. - * - * For example, when the metadata entity has a property with the - * semantic `TILE_GEOMETRIC_ERROR`, then the `geometricError` in - * the given tile will be replaced with the corresponding value - * from the metadata entity. - * - * @param finalTile - The tile - * @throws ImplicitTilingError If the input (for example, the - * schema and the metadata entity) are not structurally valid. - */ - private applyMetadataSemanticOverrides(finalTile: Tile) { - const schema = this._schema; - const metadata = this._tile.metadata; - if (!metadata || !schema) { - return; - } - - let metadataEntityModel = undefined; - try { - metadataEntityModel = MetadataEntityModels.create(schema, metadata); - } catch (error) { - const message = `Error while traversing tileset: ${error}`; - throw new ImplicitTilingError(message); - } - MetadataSemanticOverrides.applyToTile(finalTile, metadataEntityModel); - } - /** {@inheritDoc TraversedTile.path} */ get path(): string { return this._path; @@ -194,8 +160,8 @@ export class ExplicitTraversedTile implements TraversedTile { return traversedChildren; } - /** {@inheritDoc TraversedTile.getContents} */ - getContents(): Content[] { + /** {@inheritDoc TraversedTile.getRawContents} */ + getRawContents(): Content[] { if (this._tile.content) { return [this._tile.content]; } @@ -205,20 +171,47 @@ export class ExplicitTraversedTile implements TraversedTile { return []; } + /** {@inheritDoc TraversedTile.getFinalContents} */ + getFinalContents(): Content[] { + const rawContents = this.getRawContents(); + const schema = this._schema; + if (!schema) { + return rawContents; + } + const finalContents: Content[] = []; + for (let i = 0; i < rawContents.length; i++) { + const rawContent = rawContents[i]; + const finalContent: Content = { + boundingVolume: rawContent.boundingVolume, + uri: rawContent.uri, + metadata: rawContent.metadata, + group: rawContent.group, + extensions: rawContent.extensions, + extras: rawContent.extras, + }; + MetadataSemanticOverrides.applyExplicitContentMetadataSemanticOverrides( + finalContent, + schema + ); + finalContents.push(finalContent); + } + return finalContents; + } + /** {@inheritDoc TraversedTile.getSubtreeUri} - PRELIMINARY */ getSubtreeUri(): string | undefined { const implicitTiling = this._tile.implicitTiling; - if (implicitTiling) { - const rootCoordinates = - ImplicitTilings.createRootCoordinates(implicitTiling); - const subtreeUri = ImplicitTilings.substituteTemplateUri( - implicitTiling.subdivisionScheme, - implicitTiling.subtrees.uri, - rootCoordinates - ); - return subtreeUri; + if (!implicitTiling) { + return undefined; } - return undefined; + const rootCoordinates = + ImplicitTilings.createRootCoordinates(implicitTiling); + const subtreeUri = ImplicitTilings.substituteTemplateUri( + implicitTiling.subdivisionScheme, + implicitTiling.subtrees.uri, + rootCoordinates + ); + return subtreeUri; } /** {@inheritDoc TraversedTile.getImplicitTiling} - PRELIMINARY */ @@ -231,6 +224,11 @@ export class ExplicitTraversedTile implements TraversedTile { return this._tile.metadata; } + /** {@inheritDoc TraversedTile.resolveUri} - PRELIMINARY */ + resolveUri(uri: string): string { + return this._resourceResolver.resolveUri(uri); + } + // TODO For debugging toString = (): string => { return `ExplicitTraversedTile, level ${this.level}, path ${this.path}`; diff --git a/src/traversal/ImplicitTraversedTile.ts b/src/traversal/ImplicitTraversedTile.ts index 41e139c5..287b6f11 100644 --- a/src/traversal/ImplicitTraversedTile.ts +++ b/src/traversal/ImplicitTraversedTile.ts @@ -126,7 +126,7 @@ export class ImplicitTraversedTile implements TraversedTile { const refine = rootTile.refine; const transform = undefined; const metadata = undefined; - const contents = this.getContents(); + const contents = this.getRawContents(); const implicitTiling = undefined; const extensions = undefined; const extras = undefined; @@ -148,20 +148,15 @@ export class ImplicitTraversedTile implements TraversedTile { /** {@inheritDoc TraversedTile.asFinalTile} */ asFinalTile(): Tile { const tile = this.asRawTile(); - + tile.contents = this.getFinalContents(); const subtreeMetadataModel = this._subtreeModel.subtreeMetadataModel; if (subtreeMetadataModel) { - if ( - subtreeMetadataModel.tileMetadataModel && - subtreeMetadataModel.tileIndexMapping - ) { - const tileIndex = this._localCoordinate.toIndex(); - const metadataIndex = subtreeMetadataModel.tileIndexMapping[tileIndex]; - const tileMetadataModel = subtreeMetadataModel.tileMetadataModel; - const metadataEntityModel = - tileMetadataModel.getMetadataEntityModel(metadataIndex); - MetadataSemanticOverrides.applyToTile(tile, metadataEntityModel); - } + const tileIndex = this._localCoordinate.toIndex(); + MetadataSemanticOverrides.applyImplicitTileMetadataSemanticOverrides( + tile, + tileIndex, + subtreeMetadataModel + ); } return tile; } @@ -329,8 +324,8 @@ export class ImplicitTraversedTile implements TraversedTile { return traversedChildren; } - /** {@inheritDoc TraversedTile.getContents} */ - getContents(): Content[] { + /** {@inheritDoc TraversedTile.getRawContents} */ + getRawContents(): Content[] { const contents = []; const subtreeInfo = this._subtreeModel.subtreeInfo; const contentAvailabilityInfos = subtreeInfo.contentAvailabilityInfos; @@ -349,7 +344,6 @@ export class ImplicitTraversedTile implements TraversedTile { templateUri, this._globalCoordinate ); - // TODO Check semantics! const content: Content = { boundingVolume: undefined, uri: contentUri, @@ -363,6 +357,26 @@ export class ImplicitTraversedTile implements TraversedTile { return contents; } + /** {@inheritDoc TraversedTile.getFinalContents} */ + getFinalContents(): Content[] { + const contents = this.getRawContents(); + const subtreeMetadataModel = this._subtreeModel.subtreeMetadataModel; + if (!subtreeMetadataModel) { + return contents; + } + for (let i = 0; i < contents.length; i++) { + const content = contents[i]; + const tileIndex = this._localCoordinate.toIndex(); + MetadataSemanticOverrides.applyImplicitContentMetadataSemanticOverrides( + content, + i, + tileIndex, + subtreeMetadataModel + ); + } + return contents; + } + /** {@inheritDoc TraversedTile.getSubtreeUri} PRELIMINARY */ getSubtreeUri(): string | undefined { const localCoordinate = this._localCoordinate; @@ -392,6 +406,11 @@ export class ImplicitTraversedTile implements TraversedTile { return undefined; } + /** {@inheritDoc TraversedTile.resolveUri} - PRELIMINARY */ + resolveUri(uri: string): string { + return this._resourceResolver.resolveUri(uri); + } + // TODO For debugging toString = (): string => { return ( diff --git a/src/traversal/MetadataSemanticOverrides.ts b/src/traversal/MetadataSemanticOverrides.ts index edb03d70..fde547fb 100644 --- a/src/traversal/MetadataSemanticOverrides.ts +++ b/src/traversal/MetadataSemanticOverrides.ts @@ -1,8 +1,15 @@ import { defined } from "../base/defined"; import { Tile } from "../structure/Tile"; +import { Content } from "../structure/Content"; +import { Schema } from "../structure/Metadata/Schema"; import { MetadataEntityModel } from "../metadata/MetadataEntityModel"; +import { MetadataEntityModels } from "../metadata/MetadataEntityModels"; + +import { SubtreeMetadataModel } from "./SubtreeMetadataModel"; + +import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; /** * Methods for overriding properties in `Tile` and `Content` objects, @@ -11,6 +18,151 @@ import { MetadataEntityModel } from "../metadata/MetadataEntityModel"; * @internal */ export class MetadataSemanticOverrides { + // TODO There are far too few error checks (e.g. for invalid + // indices) here. This COULD be delegated to the assumption + // that the input is "valid" (as determined by the validator), + // but the error handling here should still be improvev. + + /** + * Perform the overrides of the properties of the given tile that + * are given by metadata semantics. + * + * If the given tile contains a `MetadataEntity`, then the property + * values of that metadata entity are examined. The property values + * that have a semantic will be used to override the corresponding + * values in the given tile. + * + * For example, when the metadata entity has a property with the + * semantic `TILE_GEOMETRIC_ERROR`, then the `geometricError` in + * the given tile will be replaced with the corresponding value + * from the metadata entity. + * + * @param tile - The tile + * @param schema - The metadata schema + * @throws ImplicitTilingError If the input (for example, the + * schema and the metadata entity) are not structurally valid. + */ + static applyExplicitTileMetadataSemanticOverrides( + tile: Tile, + schema: Schema + ) { + const metadata = tile.metadata; + if (!metadata) { + return; + } + let metadataEntityModel = undefined; + try { + metadataEntityModel = MetadataEntityModels.create(schema, metadata); + } catch (error) { + const message = `Error while traversing tileset: ${error}`; + throw new ImplicitTilingError(message); + } + MetadataSemanticOverrides.applyToTile(tile, metadataEntityModel); + } + + /** + * Perform the overrides of the properties of the given content that + * are given by metadata semantics. + * + * If the given content contains `MetadataEntity`, then the property + * values of that metadata entity are examined. The property values + * that have a semantic will be used to override the corresponding + * values in the given content. + * + * @param content - The content + * @param schema - The metadata schema + * @throws ImplicitTilingError If the input (for example, the + * schema and the metadata entity) are not structurally valid. + */ + static applyExplicitContentMetadataSemanticOverrides( + content: Content, + schema: Schema + ) { + const metadata = content.metadata; + if (!metadata) { + return; + } + + let metadataEntityModel = undefined; + try { + metadataEntityModel = MetadataEntityModels.create(schema, metadata); + } catch (error) { + const message = `Error while traversing tileset: ${error}`; + throw new ImplicitTilingError(message); + } + MetadataSemanticOverrides.applyToContent(content, metadataEntityModel); + } + + /** + * Perform the overrides of the properties of the given tile that + * are given by metadata semantics. + * + * If the given subtreeMetadataModel contains tile metadata, then + * the values of the metadata entity for the given tile are examined. + * The property values that have a semantic will be used to override + * the corresponding values in the given tile. + * + * @param tile - The tile + * @param tileIndex - The tile index (referring to availability indexing) + * @param subtreeMetadataModel - The `SubtreeMetadataModel` + * @throws ImplicitTilingError If the input (for example, the + * schema and the metadata entity) are not structurally valid. + */ + static applyImplicitTileMetadataSemanticOverrides( + tile: Tile, + tileIndex: number, + subtreeMetadataModel: SubtreeMetadataModel + ) { + if ( + subtreeMetadataModel.tileMetadataModel && + subtreeMetadataModel.tileIndexMapping + ) { + const metadataIndex = subtreeMetadataModel.tileIndexMapping[tileIndex]; + const tileMetadataModel = subtreeMetadataModel.tileMetadataModel; + const metadataEntityModel = + tileMetadataModel.getMetadataEntityModel(metadataIndex); + MetadataSemanticOverrides.applyToTile(tile, metadataEntityModel); + } + } + + /** + * Perform the overrides of the properties of the given content that + * are given by metadata semantics. + * + * If the given subtreeMetadataModel contains content metadata, then + * the values of the metadata entity for the given content are examined. + * The property values that have a semantic will be used to override + * the corresponding values in the given content. + * + * @param content - The content + * @param contentSetIndex - The content set index, which is `0` for + * single contents, `0` or `1` for two contents, and so on. + * @param tileIndex - The tile index (referring to availability indexing) + * @param subtreeMetadataModel - The `SubtreeMetadataModel` + * @throws ImplicitTilingError If the input (for example, the + * schema and the metadata entity) are not structurally valid. + */ + static applyImplicitContentMetadataSemanticOverrides( + content: Content, + contentSetIndex: number, + tileIndex: number, + subtreeMetadataModel: SubtreeMetadataModel + ) { + if ( + subtreeMetadataModel.contentMetadataModels && + subtreeMetadataModel.contentIndexMappings && + contentSetIndex < subtreeMetadataModel.contentMetadataModels.length + ) { + const metadataIndex = + subtreeMetadataModel.contentIndexMappings[contentSetIndex][tileIndex]; + const contentMetadataModel = + subtreeMetadataModel.contentMetadataModels[contentSetIndex]; + const metadataEntityModel = + contentMetadataModel.getMetadataEntityModel(metadataIndex); + MetadataSemanticOverrides.applyToContent(content, metadataEntityModel); + } + } + /** * Applies all overrides to the given tile, based in the property * values that are found in the given metadata entity model. @@ -18,8 +170,10 @@ export class MetadataSemanticOverrides { * @param tile - The tile that will be modified * @param metadataEntityModel - The `MetadataEntityModel` */ - static applyToTile(tile: Tile, metadataEntityModel: MetadataEntityModel) { - // Apply the semantic-based overrides from the metadata + private static applyToTile( + tile: Tile, + metadataEntityModel: MetadataEntityModel + ) { const semanticBoundingBox = metadataEntityModel.getPropertyValueBySemantic("TILE_BOUNDING_BOX"); if (semanticBoundingBox) { @@ -58,4 +212,56 @@ export class MetadataSemanticOverrides { tile.transform = semanticTransform; } } + + /** + * Applies all overrides to the given content, based in the property + * values that are found in the given metadata entity model. + * + * @param content - The content that will be modified + * @param metadataEntityModel - The `MetadataEntityModel` + */ + private static applyToContent( + content: Content, + metadataEntityModel: MetadataEntityModel + ) { + const semanticBoundingBox = metadataEntityModel.getPropertyValueBySemantic( + "CONTENT_BOUNDING_BOX" + ); + if (semanticBoundingBox) { + if (!content.boundingVolume) { + content.boundingVolume = {}; + } + content.boundingVolume.box = semanticBoundingBox; + } + + const semanticBoundingRegion = + metadataEntityModel.getPropertyValueBySemantic("CONTENT_BOUNDING_REGION"); + if (semanticBoundingRegion) { + if (!content.boundingVolume) { + content.boundingVolume = {}; + } + content.boundingVolume.region = semanticBoundingRegion; + } + + const semanticBoundingSphere = + metadataEntityModel.getPropertyValueBySemantic("CONTENT_BOUNDING_SPHERE"); + if (semanticBoundingSphere) { + if (!content.boundingVolume) { + content.boundingVolume = {}; + } + content.boundingVolume.sphere = semanticBoundingSphere; + } + + const semanticUri = + metadataEntityModel.getPropertyValueBySemantic("CONTENT_URI"); + if (defined(semanticUri)) { + content.uri = semanticUri as string; + } + + const semanticGroupId = + metadataEntityModel.getPropertyValueBySemantic("CONTENT_GROUP_ID"); + if (defined(semanticGroupId)) { + content.group = semanticGroupId as number; + } + } } diff --git a/src/traversal/SubtreeMetadataModel.ts b/src/traversal/SubtreeMetadataModel.ts index 6c4f0829..2a42b4ec 100644 --- a/src/traversal/SubtreeMetadataModel.ts +++ b/src/traversal/SubtreeMetadataModel.ts @@ -5,10 +5,14 @@ import { Schema } from "../structure/Metadata/Schema"; * An interface summarizing the metadata that may be associated * with a subtree. * - * (Note: One could consider to offer something like this in the + * Note: One could consider to offer something like this in the * `implicitTiling` package, alongside the `SubtreeInfo`. But * the structure of this interface is too much tailored for - * its use in the traversal right now) + * its use in the traversal right now. If it was supposed to + * be offered as a public interface, then the 'index mappings' + * should probably be hidden behind some function like + * m = getMetadataEntityModelForTile(tileIndex); + * that does the index lookup internally. * * @internal */ @@ -51,6 +55,15 @@ export interface SubtreeMetadataModel { * contentMetadataModels that contain the metadata for * the respective content. * + * The order is + * `contentIndexMappings[contentSetIndex][contentIndex]` + * where + * - the `contentSetIndex` is `0` for single contents, + * `0` or `1` for two contents, and so on + * - the `contentIndex` is `n` for the `n-th` available content + * + * (Yes, this should really be hidden behind an interface...) + * * (See SubtreeMetadataModels.computeAvailabilityIndexingMapping * for details) */ diff --git a/src/traversal/TraversedTile.ts b/src/traversal/TraversedTile.ts index 314707d4..f9a34591 100644 --- a/src/traversal/TraversedTile.ts +++ b/src/traversal/TraversedTile.ts @@ -112,7 +112,11 @@ export interface TraversedTile { * * @returns The contents */ - getContents(): Content[]; + getRawContents(): Content[]; + + // TODO Document or improve this - the same difference as between + // asRawTile and asFinalTile + getFinalContents(): Content[]; // TODO Some information has to be exposed here solely // for the validation. This should preferably not be @@ -121,4 +125,5 @@ export interface TraversedTile { getSubtreeUri(): string | undefined; getImplicitTiling(): TileImplicitTiling | undefined; getMetadata(): MetadataEntity | undefined; + resolveUri(uri: string): string; } From 61373581b973444eb617dd4ac6ad211bb42b8914 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Mon, 13 Mar 2023 21:57:08 +0100 Subject: [PATCH 13/26] Added specs (moved and extended from validator) --- .../data/TilesetWithFullMetadata/tileset.json | 3639 +++++++++++ specs/metadata/ArrayValuesSpec.ts | 635 ++ .../metadata/MetadataEntityModelBasicSpec.ts | 5546 +++++++++++++++++ specs/metadata/MetadataEntityModelSpec.ts | 253 + specs/metadata/PropertyTableModelsSpec.ts | 541 ++ specs/metadata/PropertyTableTestUtilities.ts | 579 ++ specs/metadata/genericEquals.ts | 58 + specs/metadata/readJsonUnchecked.ts | 27 + src/metadata/ArrayValues.ts | 354 +- src/metadata/ClassProperties.ts | 72 + src/metadata/DefaultMetadataEntityModel.ts | 4 +- src/metadata/MetadataValues.ts | 23 +- 12 files changed, 11594 insertions(+), 137 deletions(-) create mode 100644 specs/data/TilesetWithFullMetadata/tileset.json create mode 100644 specs/metadata/ArrayValuesSpec.ts create mode 100644 specs/metadata/MetadataEntityModelBasicSpec.ts create mode 100644 specs/metadata/MetadataEntityModelSpec.ts create mode 100644 specs/metadata/PropertyTableModelsSpec.ts create mode 100644 specs/metadata/PropertyTableTestUtilities.ts create mode 100644 specs/metadata/genericEquals.ts create mode 100644 specs/metadata/readJsonUnchecked.ts create mode 100644 src/metadata/ClassProperties.ts diff --git a/specs/data/TilesetWithFullMetadata/tileset.json b/specs/data/TilesetWithFullMetadata/tileset.json new file mode 100644 index 00000000..45e1dcd1 --- /dev/null +++ b/specs/data/TilesetWithFullMetadata/tileset.json @@ -0,0 +1,3639 @@ +{ + "asset" : { + "version" : "1.1" + }, + "schema" : { + "id": "TilesetWithFullMetadataSchema", + "classes" : { + "exampleClass" : { + "name" : "Full example metadata class", + "description" : "An example metadata class with all property types", + "properties" : { + "example_STRING" : { + "name" : "Example STRING property", + "description" : "An example property, with type STRING", + "type" : "STRING", + "array" : false + }, + "example_variable_length_STRING_array" : { + "name" : "Example variable-length STRING array property", + "description" : "An example variable length array property, with type STRING", + "type" : "STRING", + "array" : true + }, + "example_fixed_length_STRING_array" : { + "name" : "Example fixed-length STRING array property", + "description" : "An example fixed length array property, with type STRING", + "type" : "STRING", + "array" : true, + "count" : 5 + }, + "example_BOOLEAN" : { + "name" : "Example BOOLEAN property", + "description" : "An example property, with type BOOLEAN", + "type" : "BOOLEAN", + "array" : false + }, + "example_variable_length_BOOLEAN_array" : { + "name" : "Example variable-length BOOLEAN array property", + "description" : "An example variable length array property, with type BOOLEAN", + "type" : "BOOLEAN", + "array" : true + }, + "example_fixed_length_BOOLEAN_array" : { + "name" : "Example fixed-length BOOLEAN array property", + "description" : "An example fixed length array property, with type BOOLEAN", + "type" : "BOOLEAN", + "array" : true, + "count" : 5 + }, + "example_ENUM" : { + "name" : "Example ENUM property", + "description" : "An example property, with type ENUM, enum type exampleEnumType", + "type" : "ENUM", + "enumType" : "exampleEnumType", + "array" : false + }, + "example_variable_length_ENUM_array" : { + "name" : "Example variable-length ENUM array property", + "description" : "An example variable length array property, with type ENUM, enum type exampleEnumType", + "type" : "ENUM", + "enumType" : "exampleEnumType", + "array" : true + }, + "example_fixed_length_ENUM_array" : { + "name" : "Example fixed-length ENUM array property", + "description" : "An example fixed length array property, with type ENUM, enum type exampleEnumType", + "type" : "ENUM", + "enumType" : "exampleEnumType", + "array" : true, + "count" : 5 + }, + "example_INT8_SCALAR" : { + "name" : "Example SCALAR property with INT8 components", + "description" : "An example property, with type SCALAR, with component type INT8", + "type" : "SCALAR", + "componentType" : "INT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT8_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with INT8 components", + "description" : "An example variable length array property, with type SCALAR, with component type INT8", + "type" : "SCALAR", + "componentType" : "INT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT8_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with INT8 components", + "description" : "An example fixed length array property, with type SCALAR, with component type INT8", + "type" : "SCALAR", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT8_SCALAR" : { + "name" : "Example SCALAR property with normalized INT8 components", + "description" : "An example property, with type SCALAR, with component type INT8, normalized", + "type" : "SCALAR", + "componentType" : "INT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT8_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with normalized INT8 components", + "description" : "An example variable length array property, with type SCALAR, with component type INT8, normalized", + "type" : "SCALAR", + "componentType" : "INT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT8_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with normalized INT8 components", + "description" : "An example fixed length array property, with type SCALAR, with component type INT8, normalized", + "type" : "SCALAR", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT8_SCALAR" : { + "name" : "Example SCALAR property with UINT8 components", + "description" : "An example property, with type SCALAR, with component type UINT8", + "type" : "SCALAR", + "componentType" : "UINT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT8_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with UINT8 components", + "description" : "An example variable length array property, with type SCALAR, with component type UINT8", + "type" : "SCALAR", + "componentType" : "UINT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT8_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with UINT8 components", + "description" : "An example fixed length array property, with type SCALAR, with component type UINT8", + "type" : "SCALAR", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT8_SCALAR" : { + "name" : "Example SCALAR property with normalized UINT8 components", + "description" : "An example property, with type SCALAR, with component type UINT8, normalized", + "type" : "SCALAR", + "componentType" : "UINT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT8_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with normalized UINT8 components", + "description" : "An example variable length array property, with type SCALAR, with component type UINT8, normalized", + "type" : "SCALAR", + "componentType" : "UINT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT8_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with normalized UINT8 components", + "description" : "An example fixed length array property, with type SCALAR, with component type UINT8, normalized", + "type" : "SCALAR", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT16_SCALAR" : { + "name" : "Example SCALAR property with INT16 components", + "description" : "An example property, with type SCALAR, with component type INT16", + "type" : "SCALAR", + "componentType" : "INT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT16_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with INT16 components", + "description" : "An example variable length array property, with type SCALAR, with component type INT16", + "type" : "SCALAR", + "componentType" : "INT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT16_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with INT16 components", + "description" : "An example fixed length array property, with type SCALAR, with component type INT16", + "type" : "SCALAR", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT16_SCALAR" : { + "name" : "Example SCALAR property with normalized INT16 components", + "description" : "An example property, with type SCALAR, with component type INT16, normalized", + "type" : "SCALAR", + "componentType" : "INT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT16_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with normalized INT16 components", + "description" : "An example variable length array property, with type SCALAR, with component type INT16, normalized", + "type" : "SCALAR", + "componentType" : "INT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT16_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with normalized INT16 components", + "description" : "An example fixed length array property, with type SCALAR, with component type INT16, normalized", + "type" : "SCALAR", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT16_SCALAR" : { + "name" : "Example SCALAR property with UINT16 components", + "description" : "An example property, with type SCALAR, with component type UINT16", + "type" : "SCALAR", + "componentType" : "UINT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT16_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with UINT16 components", + "description" : "An example variable length array property, with type SCALAR, with component type UINT16", + "type" : "SCALAR", + "componentType" : "UINT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT16_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with UINT16 components", + "description" : "An example fixed length array property, with type SCALAR, with component type UINT16", + "type" : "SCALAR", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT16_SCALAR" : { + "name" : "Example SCALAR property with normalized UINT16 components", + "description" : "An example property, with type SCALAR, with component type UINT16, normalized", + "type" : "SCALAR", + "componentType" : "UINT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT16_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with normalized UINT16 components", + "description" : "An example variable length array property, with type SCALAR, with component type UINT16, normalized", + "type" : "SCALAR", + "componentType" : "UINT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT16_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with normalized UINT16 components", + "description" : "An example fixed length array property, with type SCALAR, with component type UINT16, normalized", + "type" : "SCALAR", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT32_SCALAR" : { + "name" : "Example SCALAR property with INT32 components", + "description" : "An example property, with type SCALAR, with component type INT32", + "type" : "SCALAR", + "componentType" : "INT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT32_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with INT32 components", + "description" : "An example variable length array property, with type SCALAR, with component type INT32", + "type" : "SCALAR", + "componentType" : "INT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT32_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with INT32 components", + "description" : "An example fixed length array property, with type SCALAR, with component type INT32", + "type" : "SCALAR", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT32_SCALAR" : { + "name" : "Example SCALAR property with normalized INT32 components", + "description" : "An example property, with type SCALAR, with component type INT32, normalized", + "type" : "SCALAR", + "componentType" : "INT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT32_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with normalized INT32 components", + "description" : "An example variable length array property, with type SCALAR, with component type INT32, normalized", + "type" : "SCALAR", + "componentType" : "INT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT32_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with normalized INT32 components", + "description" : "An example fixed length array property, with type SCALAR, with component type INT32, normalized", + "type" : "SCALAR", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT32_SCALAR" : { + "name" : "Example SCALAR property with UINT32 components", + "description" : "An example property, with type SCALAR, with component type UINT32", + "type" : "SCALAR", + "componentType" : "UINT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT32_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with UINT32 components", + "description" : "An example variable length array property, with type SCALAR, with component type UINT32", + "type" : "SCALAR", + "componentType" : "UINT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT32_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with UINT32 components", + "description" : "An example fixed length array property, with type SCALAR, with component type UINT32", + "type" : "SCALAR", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT32_SCALAR" : { + "name" : "Example SCALAR property with normalized UINT32 components", + "description" : "An example property, with type SCALAR, with component type UINT32, normalized", + "type" : "SCALAR", + "componentType" : "UINT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT32_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with normalized UINT32 components", + "description" : "An example variable length array property, with type SCALAR, with component type UINT32, normalized", + "type" : "SCALAR", + "componentType" : "UINT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT32_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with normalized UINT32 components", + "description" : "An example fixed length array property, with type SCALAR, with component type UINT32, normalized", + "type" : "SCALAR", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT64_SCALAR" : { + "name" : "Example SCALAR property with INT64 components", + "description" : "An example property, with type SCALAR, with component type INT64", + "type" : "SCALAR", + "componentType" : "INT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT64_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with INT64 components", + "description" : "An example variable length array property, with type SCALAR, with component type INT64", + "type" : "SCALAR", + "componentType" : "INT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT64_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with INT64 components", + "description" : "An example fixed length array property, with type SCALAR, with component type INT64", + "type" : "SCALAR", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT64_SCALAR" : { + "name" : "Example SCALAR property with normalized INT64 components", + "description" : "An example property, with type SCALAR, with component type INT64, normalized", + "type" : "SCALAR", + "componentType" : "INT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT64_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with normalized INT64 components", + "description" : "An example variable length array property, with type SCALAR, with component type INT64, normalized", + "type" : "SCALAR", + "componentType" : "INT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT64_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with normalized INT64 components", + "description" : "An example fixed length array property, with type SCALAR, with component type INT64, normalized", + "type" : "SCALAR", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT64_SCALAR" : { + "name" : "Example SCALAR property with UINT64 components", + "description" : "An example property, with type SCALAR, with component type UINT64", + "type" : "SCALAR", + "componentType" : "UINT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT64_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with UINT64 components", + "description" : "An example variable length array property, with type SCALAR, with component type UINT64", + "type" : "SCALAR", + "componentType" : "UINT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT64_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with UINT64 components", + "description" : "An example fixed length array property, with type SCALAR, with component type UINT64", + "type" : "SCALAR", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT64_SCALAR" : { + "name" : "Example SCALAR property with normalized UINT64 components", + "description" : "An example property, with type SCALAR, with component type UINT64, normalized", + "type" : "SCALAR", + "componentType" : "UINT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT64_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with normalized UINT64 components", + "description" : "An example variable length array property, with type SCALAR, with component type UINT64, normalized", + "type" : "SCALAR", + "componentType" : "UINT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT64_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with normalized UINT64 components", + "description" : "An example fixed length array property, with type SCALAR, with component type UINT64, normalized", + "type" : "SCALAR", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_FLOAT32_SCALAR" : { + "name" : "Example SCALAR property with FLOAT32 components", + "description" : "An example property, with type SCALAR, with component type FLOAT32", + "type" : "SCALAR", + "componentType" : "FLOAT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT32_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with FLOAT32 components", + "description" : "An example variable length array property, with type SCALAR, with component type FLOAT32", + "type" : "SCALAR", + "componentType" : "FLOAT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT32_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with FLOAT32 components", + "description" : "An example fixed length array property, with type SCALAR, with component type FLOAT32", + "type" : "SCALAR", + "componentType" : "FLOAT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_FLOAT64_SCALAR" : { + "name" : "Example SCALAR property with FLOAT64 components", + "description" : "An example property, with type SCALAR, with component type FLOAT64", + "type" : "SCALAR", + "componentType" : "FLOAT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT64_SCALAR_array" : { + "name" : "Example variable-length SCALAR array property with FLOAT64 components", + "description" : "An example variable length array property, with type SCALAR, with component type FLOAT64", + "type" : "SCALAR", + "componentType" : "FLOAT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT64_SCALAR_array" : { + "name" : "Example fixed-length SCALAR array property with FLOAT64 components", + "description" : "An example fixed length array property, with type SCALAR, with component type FLOAT64", + "type" : "SCALAR", + "componentType" : "FLOAT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_INT8_VEC2" : { + "name" : "Example VEC2 property with INT8 components", + "description" : "An example property, with type VEC2, with component type INT8", + "type" : "VEC2", + "componentType" : "INT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT8_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with INT8 components", + "description" : "An example variable length array property, with type VEC2, with component type INT8", + "type" : "VEC2", + "componentType" : "INT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT8_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with INT8 components", + "description" : "An example fixed length array property, with type VEC2, with component type INT8", + "type" : "VEC2", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT8_VEC2" : { + "name" : "Example VEC2 property with normalized INT8 components", + "description" : "An example property, with type VEC2, with component type INT8, normalized", + "type" : "VEC2", + "componentType" : "INT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT8_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with normalized INT8 components", + "description" : "An example variable length array property, with type VEC2, with component type INT8, normalized", + "type" : "VEC2", + "componentType" : "INT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT8_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with normalized INT8 components", + "description" : "An example fixed length array property, with type VEC2, with component type INT8, normalized", + "type" : "VEC2", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT8_VEC2" : { + "name" : "Example VEC2 property with UINT8 components", + "description" : "An example property, with type VEC2, with component type UINT8", + "type" : "VEC2", + "componentType" : "UINT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT8_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with UINT8 components", + "description" : "An example variable length array property, with type VEC2, with component type UINT8", + "type" : "VEC2", + "componentType" : "UINT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT8_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with UINT8 components", + "description" : "An example fixed length array property, with type VEC2, with component type UINT8", + "type" : "VEC2", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT8_VEC2" : { + "name" : "Example VEC2 property with normalized UINT8 components", + "description" : "An example property, with type VEC2, with component type UINT8, normalized", + "type" : "VEC2", + "componentType" : "UINT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT8_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with normalized UINT8 components", + "description" : "An example variable length array property, with type VEC2, with component type UINT8, normalized", + "type" : "VEC2", + "componentType" : "UINT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT8_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with normalized UINT8 components", + "description" : "An example fixed length array property, with type VEC2, with component type UINT8, normalized", + "type" : "VEC2", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT16_VEC2" : { + "name" : "Example VEC2 property with INT16 components", + "description" : "An example property, with type VEC2, with component type INT16", + "type" : "VEC2", + "componentType" : "INT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT16_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with INT16 components", + "description" : "An example variable length array property, with type VEC2, with component type INT16", + "type" : "VEC2", + "componentType" : "INT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT16_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with INT16 components", + "description" : "An example fixed length array property, with type VEC2, with component type INT16", + "type" : "VEC2", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT16_VEC2" : { + "name" : "Example VEC2 property with normalized INT16 components", + "description" : "An example property, with type VEC2, with component type INT16, normalized", + "type" : "VEC2", + "componentType" : "INT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT16_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with normalized INT16 components", + "description" : "An example variable length array property, with type VEC2, with component type INT16, normalized", + "type" : "VEC2", + "componentType" : "INT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT16_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with normalized INT16 components", + "description" : "An example fixed length array property, with type VEC2, with component type INT16, normalized", + "type" : "VEC2", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT16_VEC2" : { + "name" : "Example VEC2 property with UINT16 components", + "description" : "An example property, with type VEC2, with component type UINT16", + "type" : "VEC2", + "componentType" : "UINT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT16_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with UINT16 components", + "description" : "An example variable length array property, with type VEC2, with component type UINT16", + "type" : "VEC2", + "componentType" : "UINT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT16_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with UINT16 components", + "description" : "An example fixed length array property, with type VEC2, with component type UINT16", + "type" : "VEC2", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT16_VEC2" : { + "name" : "Example VEC2 property with normalized UINT16 components", + "description" : "An example property, with type VEC2, with component type UINT16, normalized", + "type" : "VEC2", + "componentType" : "UINT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT16_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with normalized UINT16 components", + "description" : "An example variable length array property, with type VEC2, with component type UINT16, normalized", + "type" : "VEC2", + "componentType" : "UINT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT16_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with normalized UINT16 components", + "description" : "An example fixed length array property, with type VEC2, with component type UINT16, normalized", + "type" : "VEC2", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT32_VEC2" : { + "name" : "Example VEC2 property with INT32 components", + "description" : "An example property, with type VEC2, with component type INT32", + "type" : "VEC2", + "componentType" : "INT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT32_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with INT32 components", + "description" : "An example variable length array property, with type VEC2, with component type INT32", + "type" : "VEC2", + "componentType" : "INT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT32_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with INT32 components", + "description" : "An example fixed length array property, with type VEC2, with component type INT32", + "type" : "VEC2", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT32_VEC2" : { + "name" : "Example VEC2 property with normalized INT32 components", + "description" : "An example property, with type VEC2, with component type INT32, normalized", + "type" : "VEC2", + "componentType" : "INT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT32_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with normalized INT32 components", + "description" : "An example variable length array property, with type VEC2, with component type INT32, normalized", + "type" : "VEC2", + "componentType" : "INT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT32_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with normalized INT32 components", + "description" : "An example fixed length array property, with type VEC2, with component type INT32, normalized", + "type" : "VEC2", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT32_VEC2" : { + "name" : "Example VEC2 property with UINT32 components", + "description" : "An example property, with type VEC2, with component type UINT32", + "type" : "VEC2", + "componentType" : "UINT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT32_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with UINT32 components", + "description" : "An example variable length array property, with type VEC2, with component type UINT32", + "type" : "VEC2", + "componentType" : "UINT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT32_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with UINT32 components", + "description" : "An example fixed length array property, with type VEC2, with component type UINT32", + "type" : "VEC2", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT32_VEC2" : { + "name" : "Example VEC2 property with normalized UINT32 components", + "description" : "An example property, with type VEC2, with component type UINT32, normalized", + "type" : "VEC2", + "componentType" : "UINT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT32_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with normalized UINT32 components", + "description" : "An example variable length array property, with type VEC2, with component type UINT32, normalized", + "type" : "VEC2", + "componentType" : "UINT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT32_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with normalized UINT32 components", + "description" : "An example fixed length array property, with type VEC2, with component type UINT32, normalized", + "type" : "VEC2", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT64_VEC2" : { + "name" : "Example VEC2 property with INT64 components", + "description" : "An example property, with type VEC2, with component type INT64", + "type" : "VEC2", + "componentType" : "INT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT64_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with INT64 components", + "description" : "An example variable length array property, with type VEC2, with component type INT64", + "type" : "VEC2", + "componentType" : "INT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT64_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with INT64 components", + "description" : "An example fixed length array property, with type VEC2, with component type INT64", + "type" : "VEC2", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT64_VEC2" : { + "name" : "Example VEC2 property with normalized INT64 components", + "description" : "An example property, with type VEC2, with component type INT64, normalized", + "type" : "VEC2", + "componentType" : "INT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT64_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with normalized INT64 components", + "description" : "An example variable length array property, with type VEC2, with component type INT64, normalized", + "type" : "VEC2", + "componentType" : "INT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT64_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with normalized INT64 components", + "description" : "An example fixed length array property, with type VEC2, with component type INT64, normalized", + "type" : "VEC2", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT64_VEC2" : { + "name" : "Example VEC2 property with UINT64 components", + "description" : "An example property, with type VEC2, with component type UINT64", + "type" : "VEC2", + "componentType" : "UINT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT64_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with UINT64 components", + "description" : "An example variable length array property, with type VEC2, with component type UINT64", + "type" : "VEC2", + "componentType" : "UINT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT64_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with UINT64 components", + "description" : "An example fixed length array property, with type VEC2, with component type UINT64", + "type" : "VEC2", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT64_VEC2" : { + "name" : "Example VEC2 property with normalized UINT64 components", + "description" : "An example property, with type VEC2, with component type UINT64, normalized", + "type" : "VEC2", + "componentType" : "UINT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT64_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with normalized UINT64 components", + "description" : "An example variable length array property, with type VEC2, with component type UINT64, normalized", + "type" : "VEC2", + "componentType" : "UINT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT64_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with normalized UINT64 components", + "description" : "An example fixed length array property, with type VEC2, with component type UINT64, normalized", + "type" : "VEC2", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_FLOAT32_VEC2" : { + "name" : "Example VEC2 property with FLOAT32 components", + "description" : "An example property, with type VEC2, with component type FLOAT32", + "type" : "VEC2", + "componentType" : "FLOAT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT32_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with FLOAT32 components", + "description" : "An example variable length array property, with type VEC2, with component type FLOAT32", + "type" : "VEC2", + "componentType" : "FLOAT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT32_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with FLOAT32 components", + "description" : "An example fixed length array property, with type VEC2, with component type FLOAT32", + "type" : "VEC2", + "componentType" : "FLOAT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_FLOAT64_VEC2" : { + "name" : "Example VEC2 property with FLOAT64 components", + "description" : "An example property, with type VEC2, with component type FLOAT64", + "type" : "VEC2", + "componentType" : "FLOAT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT64_VEC2_array" : { + "name" : "Example variable-length VEC2 array property with FLOAT64 components", + "description" : "An example variable length array property, with type VEC2, with component type FLOAT64", + "type" : "VEC2", + "componentType" : "FLOAT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT64_VEC2_array" : { + "name" : "Example fixed-length VEC2 array property with FLOAT64 components", + "description" : "An example fixed length array property, with type VEC2, with component type FLOAT64", + "type" : "VEC2", + "componentType" : "FLOAT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_INT8_VEC3" : { + "name" : "Example VEC3 property with INT8 components", + "description" : "An example property, with type VEC3, with component type INT8", + "type" : "VEC3", + "componentType" : "INT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT8_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with INT8 components", + "description" : "An example variable length array property, with type VEC3, with component type INT8", + "type" : "VEC3", + "componentType" : "INT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT8_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with INT8 components", + "description" : "An example fixed length array property, with type VEC3, with component type INT8", + "type" : "VEC3", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT8_VEC3" : { + "name" : "Example VEC3 property with normalized INT8 components", + "description" : "An example property, with type VEC3, with component type INT8, normalized", + "type" : "VEC3", + "componentType" : "INT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT8_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with normalized INT8 components", + "description" : "An example variable length array property, with type VEC3, with component type INT8, normalized", + "type" : "VEC3", + "componentType" : "INT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT8_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with normalized INT8 components", + "description" : "An example fixed length array property, with type VEC3, with component type INT8, normalized", + "type" : "VEC3", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT8_VEC3" : { + "name" : "Example VEC3 property with UINT8 components", + "description" : "An example property, with type VEC3, with component type UINT8", + "type" : "VEC3", + "componentType" : "UINT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT8_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with UINT8 components", + "description" : "An example variable length array property, with type VEC3, with component type UINT8", + "type" : "VEC3", + "componentType" : "UINT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT8_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with UINT8 components", + "description" : "An example fixed length array property, with type VEC3, with component type UINT8", + "type" : "VEC3", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT8_VEC3" : { + "name" : "Example VEC3 property with normalized UINT8 components", + "description" : "An example property, with type VEC3, with component type UINT8, normalized", + "type" : "VEC3", + "componentType" : "UINT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT8_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with normalized UINT8 components", + "description" : "An example variable length array property, with type VEC3, with component type UINT8, normalized", + "type" : "VEC3", + "componentType" : "UINT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT8_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with normalized UINT8 components", + "description" : "An example fixed length array property, with type VEC3, with component type UINT8, normalized", + "type" : "VEC3", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT16_VEC3" : { + "name" : "Example VEC3 property with INT16 components", + "description" : "An example property, with type VEC3, with component type INT16", + "type" : "VEC3", + "componentType" : "INT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT16_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with INT16 components", + "description" : "An example variable length array property, with type VEC3, with component type INT16", + "type" : "VEC3", + "componentType" : "INT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT16_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with INT16 components", + "description" : "An example fixed length array property, with type VEC3, with component type INT16", + "type" : "VEC3", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT16_VEC3" : { + "name" : "Example VEC3 property with normalized INT16 components", + "description" : "An example property, with type VEC3, with component type INT16, normalized", + "type" : "VEC3", + "componentType" : "INT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT16_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with normalized INT16 components", + "description" : "An example variable length array property, with type VEC3, with component type INT16, normalized", + "type" : "VEC3", + "componentType" : "INT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT16_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with normalized INT16 components", + "description" : "An example fixed length array property, with type VEC3, with component type INT16, normalized", + "type" : "VEC3", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT16_VEC3" : { + "name" : "Example VEC3 property with UINT16 components", + "description" : "An example property, with type VEC3, with component type UINT16", + "type" : "VEC3", + "componentType" : "UINT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT16_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with UINT16 components", + "description" : "An example variable length array property, with type VEC3, with component type UINT16", + "type" : "VEC3", + "componentType" : "UINT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT16_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with UINT16 components", + "description" : "An example fixed length array property, with type VEC3, with component type UINT16", + "type" : "VEC3", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT16_VEC3" : { + "name" : "Example VEC3 property with normalized UINT16 components", + "description" : "An example property, with type VEC3, with component type UINT16, normalized", + "type" : "VEC3", + "componentType" : "UINT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT16_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with normalized UINT16 components", + "description" : "An example variable length array property, with type VEC3, with component type UINT16, normalized", + "type" : "VEC3", + "componentType" : "UINT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT16_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with normalized UINT16 components", + "description" : "An example fixed length array property, with type VEC3, with component type UINT16, normalized", + "type" : "VEC3", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT32_VEC3" : { + "name" : "Example VEC3 property with INT32 components", + "description" : "An example property, with type VEC3, with component type INT32", + "type" : "VEC3", + "componentType" : "INT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT32_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with INT32 components", + "description" : "An example variable length array property, with type VEC3, with component type INT32", + "type" : "VEC3", + "componentType" : "INT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT32_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with INT32 components", + "description" : "An example fixed length array property, with type VEC3, with component type INT32", + "type" : "VEC3", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT32_VEC3" : { + "name" : "Example VEC3 property with normalized INT32 components", + "description" : "An example property, with type VEC3, with component type INT32, normalized", + "type" : "VEC3", + "componentType" : "INT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT32_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with normalized INT32 components", + "description" : "An example variable length array property, with type VEC3, with component type INT32, normalized", + "type" : "VEC3", + "componentType" : "INT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT32_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with normalized INT32 components", + "description" : "An example fixed length array property, with type VEC3, with component type INT32, normalized", + "type" : "VEC3", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT32_VEC3" : { + "name" : "Example VEC3 property with UINT32 components", + "description" : "An example property, with type VEC3, with component type UINT32", + "type" : "VEC3", + "componentType" : "UINT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT32_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with UINT32 components", + "description" : "An example variable length array property, with type VEC3, with component type UINT32", + "type" : "VEC3", + "componentType" : "UINT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT32_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with UINT32 components", + "description" : "An example fixed length array property, with type VEC3, with component type UINT32", + "type" : "VEC3", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT32_VEC3" : { + "name" : "Example VEC3 property with normalized UINT32 components", + "description" : "An example property, with type VEC3, with component type UINT32, normalized", + "type" : "VEC3", + "componentType" : "UINT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT32_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with normalized UINT32 components", + "description" : "An example variable length array property, with type VEC3, with component type UINT32, normalized", + "type" : "VEC3", + "componentType" : "UINT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT32_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with normalized UINT32 components", + "description" : "An example fixed length array property, with type VEC3, with component type UINT32, normalized", + "type" : "VEC3", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT64_VEC3" : { + "name" : "Example VEC3 property with INT64 components", + "description" : "An example property, with type VEC3, with component type INT64", + "type" : "VEC3", + "componentType" : "INT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT64_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with INT64 components", + "description" : "An example variable length array property, with type VEC3, with component type INT64", + "type" : "VEC3", + "componentType" : "INT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT64_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with INT64 components", + "description" : "An example fixed length array property, with type VEC3, with component type INT64", + "type" : "VEC3", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT64_VEC3" : { + "name" : "Example VEC3 property with normalized INT64 components", + "description" : "An example property, with type VEC3, with component type INT64, normalized", + "type" : "VEC3", + "componentType" : "INT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT64_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with normalized INT64 components", + "description" : "An example variable length array property, with type VEC3, with component type INT64, normalized", + "type" : "VEC3", + "componentType" : "INT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT64_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with normalized INT64 components", + "description" : "An example fixed length array property, with type VEC3, with component type INT64, normalized", + "type" : "VEC3", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT64_VEC3" : { + "name" : "Example VEC3 property with UINT64 components", + "description" : "An example property, with type VEC3, with component type UINT64", + "type" : "VEC3", + "componentType" : "UINT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT64_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with UINT64 components", + "description" : "An example variable length array property, with type VEC3, with component type UINT64", + "type" : "VEC3", + "componentType" : "UINT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT64_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with UINT64 components", + "description" : "An example fixed length array property, with type VEC3, with component type UINT64", + "type" : "VEC3", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT64_VEC3" : { + "name" : "Example VEC3 property with normalized UINT64 components", + "description" : "An example property, with type VEC3, with component type UINT64, normalized", + "type" : "VEC3", + "componentType" : "UINT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT64_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with normalized UINT64 components", + "description" : "An example variable length array property, with type VEC3, with component type UINT64, normalized", + "type" : "VEC3", + "componentType" : "UINT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT64_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with normalized UINT64 components", + "description" : "An example fixed length array property, with type VEC3, with component type UINT64, normalized", + "type" : "VEC3", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_FLOAT32_VEC3" : { + "name" : "Example VEC3 property with FLOAT32 components", + "description" : "An example property, with type VEC3, with component type FLOAT32", + "type" : "VEC3", + "componentType" : "FLOAT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT32_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with FLOAT32 components", + "description" : "An example variable length array property, with type VEC3, with component type FLOAT32", + "type" : "VEC3", + "componentType" : "FLOAT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT32_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with FLOAT32 components", + "description" : "An example fixed length array property, with type VEC3, with component type FLOAT32", + "type" : "VEC3", + "componentType" : "FLOAT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_FLOAT64_VEC3" : { + "name" : "Example VEC3 property with FLOAT64 components", + "description" : "An example property, with type VEC3, with component type FLOAT64", + "type" : "VEC3", + "componentType" : "FLOAT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT64_VEC3_array" : { + "name" : "Example variable-length VEC3 array property with FLOAT64 components", + "description" : "An example variable length array property, with type VEC3, with component type FLOAT64", + "type" : "VEC3", + "componentType" : "FLOAT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT64_VEC3_array" : { + "name" : "Example fixed-length VEC3 array property with FLOAT64 components", + "description" : "An example fixed length array property, with type VEC3, with component type FLOAT64", + "type" : "VEC3", + "componentType" : "FLOAT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_INT8_VEC4" : { + "name" : "Example VEC4 property with INT8 components", + "description" : "An example property, with type VEC4, with component type INT8", + "type" : "VEC4", + "componentType" : "INT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT8_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with INT8 components", + "description" : "An example variable length array property, with type VEC4, with component type INT8", + "type" : "VEC4", + "componentType" : "INT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT8_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with INT8 components", + "description" : "An example fixed length array property, with type VEC4, with component type INT8", + "type" : "VEC4", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT8_VEC4" : { + "name" : "Example VEC4 property with normalized INT8 components", + "description" : "An example property, with type VEC4, with component type INT8, normalized", + "type" : "VEC4", + "componentType" : "INT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT8_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with normalized INT8 components", + "description" : "An example variable length array property, with type VEC4, with component type INT8, normalized", + "type" : "VEC4", + "componentType" : "INT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT8_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with normalized INT8 components", + "description" : "An example fixed length array property, with type VEC4, with component type INT8, normalized", + "type" : "VEC4", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT8_VEC4" : { + "name" : "Example VEC4 property with UINT8 components", + "description" : "An example property, with type VEC4, with component type UINT8", + "type" : "VEC4", + "componentType" : "UINT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT8_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with UINT8 components", + "description" : "An example variable length array property, with type VEC4, with component type UINT8", + "type" : "VEC4", + "componentType" : "UINT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT8_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with UINT8 components", + "description" : "An example fixed length array property, with type VEC4, with component type UINT8", + "type" : "VEC4", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT8_VEC4" : { + "name" : "Example VEC4 property with normalized UINT8 components", + "description" : "An example property, with type VEC4, with component type UINT8, normalized", + "type" : "VEC4", + "componentType" : "UINT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT8_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with normalized UINT8 components", + "description" : "An example variable length array property, with type VEC4, with component type UINT8, normalized", + "type" : "VEC4", + "componentType" : "UINT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT8_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with normalized UINT8 components", + "description" : "An example fixed length array property, with type VEC4, with component type UINT8, normalized", + "type" : "VEC4", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT16_VEC4" : { + "name" : "Example VEC4 property with INT16 components", + "description" : "An example property, with type VEC4, with component type INT16", + "type" : "VEC4", + "componentType" : "INT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT16_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with INT16 components", + "description" : "An example variable length array property, with type VEC4, with component type INT16", + "type" : "VEC4", + "componentType" : "INT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT16_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with INT16 components", + "description" : "An example fixed length array property, with type VEC4, with component type INT16", + "type" : "VEC4", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT16_VEC4" : { + "name" : "Example VEC4 property with normalized INT16 components", + "description" : "An example property, with type VEC4, with component type INT16, normalized", + "type" : "VEC4", + "componentType" : "INT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT16_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with normalized INT16 components", + "description" : "An example variable length array property, with type VEC4, with component type INT16, normalized", + "type" : "VEC4", + "componentType" : "INT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT16_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with normalized INT16 components", + "description" : "An example fixed length array property, with type VEC4, with component type INT16, normalized", + "type" : "VEC4", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT16_VEC4" : { + "name" : "Example VEC4 property with UINT16 components", + "description" : "An example property, with type VEC4, with component type UINT16", + "type" : "VEC4", + "componentType" : "UINT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT16_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with UINT16 components", + "description" : "An example variable length array property, with type VEC4, with component type UINT16", + "type" : "VEC4", + "componentType" : "UINT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT16_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with UINT16 components", + "description" : "An example fixed length array property, with type VEC4, with component type UINT16", + "type" : "VEC4", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT16_VEC4" : { + "name" : "Example VEC4 property with normalized UINT16 components", + "description" : "An example property, with type VEC4, with component type UINT16, normalized", + "type" : "VEC4", + "componentType" : "UINT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT16_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with normalized UINT16 components", + "description" : "An example variable length array property, with type VEC4, with component type UINT16, normalized", + "type" : "VEC4", + "componentType" : "UINT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT16_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with normalized UINT16 components", + "description" : "An example fixed length array property, with type VEC4, with component type UINT16, normalized", + "type" : "VEC4", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT32_VEC4" : { + "name" : "Example VEC4 property with INT32 components", + "description" : "An example property, with type VEC4, with component type INT32", + "type" : "VEC4", + "componentType" : "INT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT32_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with INT32 components", + "description" : "An example variable length array property, with type VEC4, with component type INT32", + "type" : "VEC4", + "componentType" : "INT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT32_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with INT32 components", + "description" : "An example fixed length array property, with type VEC4, with component type INT32", + "type" : "VEC4", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT32_VEC4" : { + "name" : "Example VEC4 property with normalized INT32 components", + "description" : "An example property, with type VEC4, with component type INT32, normalized", + "type" : "VEC4", + "componentType" : "INT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT32_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with normalized INT32 components", + "description" : "An example variable length array property, with type VEC4, with component type INT32, normalized", + "type" : "VEC4", + "componentType" : "INT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT32_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with normalized INT32 components", + "description" : "An example fixed length array property, with type VEC4, with component type INT32, normalized", + "type" : "VEC4", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT32_VEC4" : { + "name" : "Example VEC4 property with UINT32 components", + "description" : "An example property, with type VEC4, with component type UINT32", + "type" : "VEC4", + "componentType" : "UINT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT32_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with UINT32 components", + "description" : "An example variable length array property, with type VEC4, with component type UINT32", + "type" : "VEC4", + "componentType" : "UINT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT32_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with UINT32 components", + "description" : "An example fixed length array property, with type VEC4, with component type UINT32", + "type" : "VEC4", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT32_VEC4" : { + "name" : "Example VEC4 property with normalized UINT32 components", + "description" : "An example property, with type VEC4, with component type UINT32, normalized", + "type" : "VEC4", + "componentType" : "UINT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT32_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with normalized UINT32 components", + "description" : "An example variable length array property, with type VEC4, with component type UINT32, normalized", + "type" : "VEC4", + "componentType" : "UINT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT32_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with normalized UINT32 components", + "description" : "An example fixed length array property, with type VEC4, with component type UINT32, normalized", + "type" : "VEC4", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT64_VEC4" : { + "name" : "Example VEC4 property with INT64 components", + "description" : "An example property, with type VEC4, with component type INT64", + "type" : "VEC4", + "componentType" : "INT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT64_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with INT64 components", + "description" : "An example variable length array property, with type VEC4, with component type INT64", + "type" : "VEC4", + "componentType" : "INT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT64_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with INT64 components", + "description" : "An example fixed length array property, with type VEC4, with component type INT64", + "type" : "VEC4", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT64_VEC4" : { + "name" : "Example VEC4 property with normalized INT64 components", + "description" : "An example property, with type VEC4, with component type INT64, normalized", + "type" : "VEC4", + "componentType" : "INT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT64_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with normalized INT64 components", + "description" : "An example variable length array property, with type VEC4, with component type INT64, normalized", + "type" : "VEC4", + "componentType" : "INT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT64_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with normalized INT64 components", + "description" : "An example fixed length array property, with type VEC4, with component type INT64, normalized", + "type" : "VEC4", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT64_VEC4" : { + "name" : "Example VEC4 property with UINT64 components", + "description" : "An example property, with type VEC4, with component type UINT64", + "type" : "VEC4", + "componentType" : "UINT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT64_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with UINT64 components", + "description" : "An example variable length array property, with type VEC4, with component type UINT64", + "type" : "VEC4", + "componentType" : "UINT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT64_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with UINT64 components", + "description" : "An example fixed length array property, with type VEC4, with component type UINT64", + "type" : "VEC4", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT64_VEC4" : { + "name" : "Example VEC4 property with normalized UINT64 components", + "description" : "An example property, with type VEC4, with component type UINT64, normalized", + "type" : "VEC4", + "componentType" : "UINT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT64_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with normalized UINT64 components", + "description" : "An example variable length array property, with type VEC4, with component type UINT64, normalized", + "type" : "VEC4", + "componentType" : "UINT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT64_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with normalized UINT64 components", + "description" : "An example fixed length array property, with type VEC4, with component type UINT64, normalized", + "type" : "VEC4", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_FLOAT32_VEC4" : { + "name" : "Example VEC4 property with FLOAT32 components", + "description" : "An example property, with type VEC4, with component type FLOAT32", + "type" : "VEC4", + "componentType" : "FLOAT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT32_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with FLOAT32 components", + "description" : "An example variable length array property, with type VEC4, with component type FLOAT32", + "type" : "VEC4", + "componentType" : "FLOAT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT32_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with FLOAT32 components", + "description" : "An example fixed length array property, with type VEC4, with component type FLOAT32", + "type" : "VEC4", + "componentType" : "FLOAT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_FLOAT64_VEC4" : { + "name" : "Example VEC4 property with FLOAT64 components", + "description" : "An example property, with type VEC4, with component type FLOAT64", + "type" : "VEC4", + "componentType" : "FLOAT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT64_VEC4_array" : { + "name" : "Example variable-length VEC4 array property with FLOAT64 components", + "description" : "An example variable length array property, with type VEC4, with component type FLOAT64", + "type" : "VEC4", + "componentType" : "FLOAT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT64_VEC4_array" : { + "name" : "Example fixed-length VEC4 array property with FLOAT64 components", + "description" : "An example fixed length array property, with type VEC4, with component type FLOAT64", + "type" : "VEC4", + "componentType" : "FLOAT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_INT8_MAT2" : { + "name" : "Example MAT2 property with INT8 components", + "description" : "An example property, with type MAT2, with component type INT8", + "type" : "MAT2", + "componentType" : "INT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT8_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with INT8 components", + "description" : "An example variable length array property, with type MAT2, with component type INT8", + "type" : "MAT2", + "componentType" : "INT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT8_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with INT8 components", + "description" : "An example fixed length array property, with type MAT2, with component type INT8", + "type" : "MAT2", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT8_MAT2" : { + "name" : "Example MAT2 property with normalized INT8 components", + "description" : "An example property, with type MAT2, with component type INT8, normalized", + "type" : "MAT2", + "componentType" : "INT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT8_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with normalized INT8 components", + "description" : "An example variable length array property, with type MAT2, with component type INT8, normalized", + "type" : "MAT2", + "componentType" : "INT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT8_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with normalized INT8 components", + "description" : "An example fixed length array property, with type MAT2, with component type INT8, normalized", + "type" : "MAT2", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT8_MAT2" : { + "name" : "Example MAT2 property with UINT8 components", + "description" : "An example property, with type MAT2, with component type UINT8", + "type" : "MAT2", + "componentType" : "UINT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT8_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with UINT8 components", + "description" : "An example variable length array property, with type MAT2, with component type UINT8", + "type" : "MAT2", + "componentType" : "UINT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT8_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with UINT8 components", + "description" : "An example fixed length array property, with type MAT2, with component type UINT8", + "type" : "MAT2", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT8_MAT2" : { + "name" : "Example MAT2 property with normalized UINT8 components", + "description" : "An example property, with type MAT2, with component type UINT8, normalized", + "type" : "MAT2", + "componentType" : "UINT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT8_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with normalized UINT8 components", + "description" : "An example variable length array property, with type MAT2, with component type UINT8, normalized", + "type" : "MAT2", + "componentType" : "UINT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT8_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with normalized UINT8 components", + "description" : "An example fixed length array property, with type MAT2, with component type UINT8, normalized", + "type" : "MAT2", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT16_MAT2" : { + "name" : "Example MAT2 property with INT16 components", + "description" : "An example property, with type MAT2, with component type INT16", + "type" : "MAT2", + "componentType" : "INT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT16_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with INT16 components", + "description" : "An example variable length array property, with type MAT2, with component type INT16", + "type" : "MAT2", + "componentType" : "INT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT16_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with INT16 components", + "description" : "An example fixed length array property, with type MAT2, with component type INT16", + "type" : "MAT2", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT16_MAT2" : { + "name" : "Example MAT2 property with normalized INT16 components", + "description" : "An example property, with type MAT2, with component type INT16, normalized", + "type" : "MAT2", + "componentType" : "INT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT16_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with normalized INT16 components", + "description" : "An example variable length array property, with type MAT2, with component type INT16, normalized", + "type" : "MAT2", + "componentType" : "INT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT16_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with normalized INT16 components", + "description" : "An example fixed length array property, with type MAT2, with component type INT16, normalized", + "type" : "MAT2", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT16_MAT2" : { + "name" : "Example MAT2 property with UINT16 components", + "description" : "An example property, with type MAT2, with component type UINT16", + "type" : "MAT2", + "componentType" : "UINT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT16_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with UINT16 components", + "description" : "An example variable length array property, with type MAT2, with component type UINT16", + "type" : "MAT2", + "componentType" : "UINT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT16_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with UINT16 components", + "description" : "An example fixed length array property, with type MAT2, with component type UINT16", + "type" : "MAT2", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT16_MAT2" : { + "name" : "Example MAT2 property with normalized UINT16 components", + "description" : "An example property, with type MAT2, with component type UINT16, normalized", + "type" : "MAT2", + "componentType" : "UINT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT16_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with normalized UINT16 components", + "description" : "An example variable length array property, with type MAT2, with component type UINT16, normalized", + "type" : "MAT2", + "componentType" : "UINT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT16_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with normalized UINT16 components", + "description" : "An example fixed length array property, with type MAT2, with component type UINT16, normalized", + "type" : "MAT2", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT32_MAT2" : { + "name" : "Example MAT2 property with INT32 components", + "description" : "An example property, with type MAT2, with component type INT32", + "type" : "MAT2", + "componentType" : "INT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT32_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with INT32 components", + "description" : "An example variable length array property, with type MAT2, with component type INT32", + "type" : "MAT2", + "componentType" : "INT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT32_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with INT32 components", + "description" : "An example fixed length array property, with type MAT2, with component type INT32", + "type" : "MAT2", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT32_MAT2" : { + "name" : "Example MAT2 property with normalized INT32 components", + "description" : "An example property, with type MAT2, with component type INT32, normalized", + "type" : "MAT2", + "componentType" : "INT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT32_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with normalized INT32 components", + "description" : "An example variable length array property, with type MAT2, with component type INT32, normalized", + "type" : "MAT2", + "componentType" : "INT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT32_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with normalized INT32 components", + "description" : "An example fixed length array property, with type MAT2, with component type INT32, normalized", + "type" : "MAT2", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT32_MAT2" : { + "name" : "Example MAT2 property with UINT32 components", + "description" : "An example property, with type MAT2, with component type UINT32", + "type" : "MAT2", + "componentType" : "UINT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT32_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with UINT32 components", + "description" : "An example variable length array property, with type MAT2, with component type UINT32", + "type" : "MAT2", + "componentType" : "UINT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT32_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with UINT32 components", + "description" : "An example fixed length array property, with type MAT2, with component type UINT32", + "type" : "MAT2", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT32_MAT2" : { + "name" : "Example MAT2 property with normalized UINT32 components", + "description" : "An example property, with type MAT2, with component type UINT32, normalized", + "type" : "MAT2", + "componentType" : "UINT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT32_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with normalized UINT32 components", + "description" : "An example variable length array property, with type MAT2, with component type UINT32, normalized", + "type" : "MAT2", + "componentType" : "UINT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT32_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with normalized UINT32 components", + "description" : "An example fixed length array property, with type MAT2, with component type UINT32, normalized", + "type" : "MAT2", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT64_MAT2" : { + "name" : "Example MAT2 property with INT64 components", + "description" : "An example property, with type MAT2, with component type INT64", + "type" : "MAT2", + "componentType" : "INT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT64_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with INT64 components", + "description" : "An example variable length array property, with type MAT2, with component type INT64", + "type" : "MAT2", + "componentType" : "INT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT64_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with INT64 components", + "description" : "An example fixed length array property, with type MAT2, with component type INT64", + "type" : "MAT2", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT64_MAT2" : { + "name" : "Example MAT2 property with normalized INT64 components", + "description" : "An example property, with type MAT2, with component type INT64, normalized", + "type" : "MAT2", + "componentType" : "INT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT64_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with normalized INT64 components", + "description" : "An example variable length array property, with type MAT2, with component type INT64, normalized", + "type" : "MAT2", + "componentType" : "INT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT64_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with normalized INT64 components", + "description" : "An example fixed length array property, with type MAT2, with component type INT64, normalized", + "type" : "MAT2", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT64_MAT2" : { + "name" : "Example MAT2 property with UINT64 components", + "description" : "An example property, with type MAT2, with component type UINT64", + "type" : "MAT2", + "componentType" : "UINT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT64_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with UINT64 components", + "description" : "An example variable length array property, with type MAT2, with component type UINT64", + "type" : "MAT2", + "componentType" : "UINT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT64_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with UINT64 components", + "description" : "An example fixed length array property, with type MAT2, with component type UINT64", + "type" : "MAT2", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT64_MAT2" : { + "name" : "Example MAT2 property with normalized UINT64 components", + "description" : "An example property, with type MAT2, with component type UINT64, normalized", + "type" : "MAT2", + "componentType" : "UINT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT64_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with normalized UINT64 components", + "description" : "An example variable length array property, with type MAT2, with component type UINT64, normalized", + "type" : "MAT2", + "componentType" : "UINT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT64_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with normalized UINT64 components", + "description" : "An example fixed length array property, with type MAT2, with component type UINT64, normalized", + "type" : "MAT2", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_FLOAT32_MAT2" : { + "name" : "Example MAT2 property with FLOAT32 components", + "description" : "An example property, with type MAT2, with component type FLOAT32", + "type" : "MAT2", + "componentType" : "FLOAT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT32_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with FLOAT32 components", + "description" : "An example variable length array property, with type MAT2, with component type FLOAT32", + "type" : "MAT2", + "componentType" : "FLOAT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT32_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with FLOAT32 components", + "description" : "An example fixed length array property, with type MAT2, with component type FLOAT32", + "type" : "MAT2", + "componentType" : "FLOAT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_FLOAT64_MAT2" : { + "name" : "Example MAT2 property with FLOAT64 components", + "description" : "An example property, with type MAT2, with component type FLOAT64", + "type" : "MAT2", + "componentType" : "FLOAT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT64_MAT2_array" : { + "name" : "Example variable-length MAT2 array property with FLOAT64 components", + "description" : "An example variable length array property, with type MAT2, with component type FLOAT64", + "type" : "MAT2", + "componentType" : "FLOAT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT64_MAT2_array" : { + "name" : "Example fixed-length MAT2 array property with FLOAT64 components", + "description" : "An example fixed length array property, with type MAT2, with component type FLOAT64", + "type" : "MAT2", + "componentType" : "FLOAT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_INT8_MAT3" : { + "name" : "Example MAT3 property with INT8 components", + "description" : "An example property, with type MAT3, with component type INT8", + "type" : "MAT3", + "componentType" : "INT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT8_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with INT8 components", + "description" : "An example variable length array property, with type MAT3, with component type INT8", + "type" : "MAT3", + "componentType" : "INT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT8_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with INT8 components", + "description" : "An example fixed length array property, with type MAT3, with component type INT8", + "type" : "MAT3", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT8_MAT3" : { + "name" : "Example MAT3 property with normalized INT8 components", + "description" : "An example property, with type MAT3, with component type INT8, normalized", + "type" : "MAT3", + "componentType" : "INT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT8_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with normalized INT8 components", + "description" : "An example variable length array property, with type MAT3, with component type INT8, normalized", + "type" : "MAT3", + "componentType" : "INT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT8_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with normalized INT8 components", + "description" : "An example fixed length array property, with type MAT3, with component type INT8, normalized", + "type" : "MAT3", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT8_MAT3" : { + "name" : "Example MAT3 property with UINT8 components", + "description" : "An example property, with type MAT3, with component type UINT8", + "type" : "MAT3", + "componentType" : "UINT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT8_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with UINT8 components", + "description" : "An example variable length array property, with type MAT3, with component type UINT8", + "type" : "MAT3", + "componentType" : "UINT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT8_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with UINT8 components", + "description" : "An example fixed length array property, with type MAT3, with component type UINT8", + "type" : "MAT3", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT8_MAT3" : { + "name" : "Example MAT3 property with normalized UINT8 components", + "description" : "An example property, with type MAT3, with component type UINT8, normalized", + "type" : "MAT3", + "componentType" : "UINT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT8_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with normalized UINT8 components", + "description" : "An example variable length array property, with type MAT3, with component type UINT8, normalized", + "type" : "MAT3", + "componentType" : "UINT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT8_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with normalized UINT8 components", + "description" : "An example fixed length array property, with type MAT3, with component type UINT8, normalized", + "type" : "MAT3", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT16_MAT3" : { + "name" : "Example MAT3 property with INT16 components", + "description" : "An example property, with type MAT3, with component type INT16", + "type" : "MAT3", + "componentType" : "INT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT16_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with INT16 components", + "description" : "An example variable length array property, with type MAT3, with component type INT16", + "type" : "MAT3", + "componentType" : "INT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT16_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with INT16 components", + "description" : "An example fixed length array property, with type MAT3, with component type INT16", + "type" : "MAT3", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT16_MAT3" : { + "name" : "Example MAT3 property with normalized INT16 components", + "description" : "An example property, with type MAT3, with component type INT16, normalized", + "type" : "MAT3", + "componentType" : "INT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT16_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with normalized INT16 components", + "description" : "An example variable length array property, with type MAT3, with component type INT16, normalized", + "type" : "MAT3", + "componentType" : "INT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT16_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with normalized INT16 components", + "description" : "An example fixed length array property, with type MAT3, with component type INT16, normalized", + "type" : "MAT3", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT16_MAT3" : { + "name" : "Example MAT3 property with UINT16 components", + "description" : "An example property, with type MAT3, with component type UINT16", + "type" : "MAT3", + "componentType" : "UINT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT16_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with UINT16 components", + "description" : "An example variable length array property, with type MAT3, with component type UINT16", + "type" : "MAT3", + "componentType" : "UINT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT16_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with UINT16 components", + "description" : "An example fixed length array property, with type MAT3, with component type UINT16", + "type" : "MAT3", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT16_MAT3" : { + "name" : "Example MAT3 property with normalized UINT16 components", + "description" : "An example property, with type MAT3, with component type UINT16, normalized", + "type" : "MAT3", + "componentType" : "UINT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT16_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with normalized UINT16 components", + "description" : "An example variable length array property, with type MAT3, with component type UINT16, normalized", + "type" : "MAT3", + "componentType" : "UINT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT16_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with normalized UINT16 components", + "description" : "An example fixed length array property, with type MAT3, with component type UINT16, normalized", + "type" : "MAT3", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT32_MAT3" : { + "name" : "Example MAT3 property with INT32 components", + "description" : "An example property, with type MAT3, with component type INT32", + "type" : "MAT3", + "componentType" : "INT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT32_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with INT32 components", + "description" : "An example variable length array property, with type MAT3, with component type INT32", + "type" : "MAT3", + "componentType" : "INT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT32_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with INT32 components", + "description" : "An example fixed length array property, with type MAT3, with component type INT32", + "type" : "MAT3", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT32_MAT3" : { + "name" : "Example MAT3 property with normalized INT32 components", + "description" : "An example property, with type MAT3, with component type INT32, normalized", + "type" : "MAT3", + "componentType" : "INT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT32_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with normalized INT32 components", + "description" : "An example variable length array property, with type MAT3, with component type INT32, normalized", + "type" : "MAT3", + "componentType" : "INT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT32_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with normalized INT32 components", + "description" : "An example fixed length array property, with type MAT3, with component type INT32, normalized", + "type" : "MAT3", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT32_MAT3" : { + "name" : "Example MAT3 property with UINT32 components", + "description" : "An example property, with type MAT3, with component type UINT32", + "type" : "MAT3", + "componentType" : "UINT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT32_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with UINT32 components", + "description" : "An example variable length array property, with type MAT3, with component type UINT32", + "type" : "MAT3", + "componentType" : "UINT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT32_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with UINT32 components", + "description" : "An example fixed length array property, with type MAT3, with component type UINT32", + "type" : "MAT3", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT32_MAT3" : { + "name" : "Example MAT3 property with normalized UINT32 components", + "description" : "An example property, with type MAT3, with component type UINT32, normalized", + "type" : "MAT3", + "componentType" : "UINT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT32_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with normalized UINT32 components", + "description" : "An example variable length array property, with type MAT3, with component type UINT32, normalized", + "type" : "MAT3", + "componentType" : "UINT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT32_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with normalized UINT32 components", + "description" : "An example fixed length array property, with type MAT3, with component type UINT32, normalized", + "type" : "MAT3", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT64_MAT3" : { + "name" : "Example MAT3 property with INT64 components", + "description" : "An example property, with type MAT3, with component type INT64", + "type" : "MAT3", + "componentType" : "INT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT64_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with INT64 components", + "description" : "An example variable length array property, with type MAT3, with component type INT64", + "type" : "MAT3", + "componentType" : "INT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT64_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with INT64 components", + "description" : "An example fixed length array property, with type MAT3, with component type INT64", + "type" : "MAT3", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT64_MAT3" : { + "name" : "Example MAT3 property with normalized INT64 components", + "description" : "An example property, with type MAT3, with component type INT64, normalized", + "type" : "MAT3", + "componentType" : "INT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT64_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with normalized INT64 components", + "description" : "An example variable length array property, with type MAT3, with component type INT64, normalized", + "type" : "MAT3", + "componentType" : "INT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT64_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with normalized INT64 components", + "description" : "An example fixed length array property, with type MAT3, with component type INT64, normalized", + "type" : "MAT3", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT64_MAT3" : { + "name" : "Example MAT3 property with UINT64 components", + "description" : "An example property, with type MAT3, with component type UINT64", + "type" : "MAT3", + "componentType" : "UINT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT64_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with UINT64 components", + "description" : "An example variable length array property, with type MAT3, with component type UINT64", + "type" : "MAT3", + "componentType" : "UINT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT64_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with UINT64 components", + "description" : "An example fixed length array property, with type MAT3, with component type UINT64", + "type" : "MAT3", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT64_MAT3" : { + "name" : "Example MAT3 property with normalized UINT64 components", + "description" : "An example property, with type MAT3, with component type UINT64, normalized", + "type" : "MAT3", + "componentType" : "UINT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT64_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with normalized UINT64 components", + "description" : "An example variable length array property, with type MAT3, with component type UINT64, normalized", + "type" : "MAT3", + "componentType" : "UINT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT64_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with normalized UINT64 components", + "description" : "An example fixed length array property, with type MAT3, with component type UINT64, normalized", + "type" : "MAT3", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_FLOAT32_MAT3" : { + "name" : "Example MAT3 property with FLOAT32 components", + "description" : "An example property, with type MAT3, with component type FLOAT32", + "type" : "MAT3", + "componentType" : "FLOAT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT32_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with FLOAT32 components", + "description" : "An example variable length array property, with type MAT3, with component type FLOAT32", + "type" : "MAT3", + "componentType" : "FLOAT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT32_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with FLOAT32 components", + "description" : "An example fixed length array property, with type MAT3, with component type FLOAT32", + "type" : "MAT3", + "componentType" : "FLOAT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_FLOAT64_MAT3" : { + "name" : "Example MAT3 property with FLOAT64 components", + "description" : "An example property, with type MAT3, with component type FLOAT64", + "type" : "MAT3", + "componentType" : "FLOAT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT64_MAT3_array" : { + "name" : "Example variable-length MAT3 array property with FLOAT64 components", + "description" : "An example variable length array property, with type MAT3, with component type FLOAT64", + "type" : "MAT3", + "componentType" : "FLOAT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT64_MAT3_array" : { + "name" : "Example fixed-length MAT3 array property with FLOAT64 components", + "description" : "An example fixed length array property, with type MAT3, with component type FLOAT64", + "type" : "MAT3", + "componentType" : "FLOAT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_INT8_MAT4" : { + "name" : "Example MAT4 property with INT8 components", + "description" : "An example property, with type MAT4, with component type INT8", + "type" : "MAT4", + "componentType" : "INT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT8_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with INT8 components", + "description" : "An example variable length array property, with type MAT4, with component type INT8", + "type" : "MAT4", + "componentType" : "INT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT8_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with INT8 components", + "description" : "An example fixed length array property, with type MAT4, with component type INT8", + "type" : "MAT4", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT8_MAT4" : { + "name" : "Example MAT4 property with normalized INT8 components", + "description" : "An example property, with type MAT4, with component type INT8, normalized", + "type" : "MAT4", + "componentType" : "INT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT8_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with normalized INT8 components", + "description" : "An example variable length array property, with type MAT4, with component type INT8, normalized", + "type" : "MAT4", + "componentType" : "INT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT8_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with normalized INT8 components", + "description" : "An example fixed length array property, with type MAT4, with component type INT8, normalized", + "type" : "MAT4", + "componentType" : "INT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT8_MAT4" : { + "name" : "Example MAT4 property with UINT8 components", + "description" : "An example property, with type MAT4, with component type UINT8", + "type" : "MAT4", + "componentType" : "UINT8", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT8_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with UINT8 components", + "description" : "An example variable length array property, with type MAT4, with component type UINT8", + "type" : "MAT4", + "componentType" : "UINT8", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT8_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with UINT8 components", + "description" : "An example fixed length array property, with type MAT4, with component type UINT8", + "type" : "MAT4", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT8_MAT4" : { + "name" : "Example MAT4 property with normalized UINT8 components", + "description" : "An example property, with type MAT4, with component type UINT8, normalized", + "type" : "MAT4", + "componentType" : "UINT8", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT8_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with normalized UINT8 components", + "description" : "An example variable length array property, with type MAT4, with component type UINT8, normalized", + "type" : "MAT4", + "componentType" : "UINT8", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT8_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with normalized UINT8 components", + "description" : "An example fixed length array property, with type MAT4, with component type UINT8, normalized", + "type" : "MAT4", + "componentType" : "UINT8", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT16_MAT4" : { + "name" : "Example MAT4 property with INT16 components", + "description" : "An example property, with type MAT4, with component type INT16", + "type" : "MAT4", + "componentType" : "INT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT16_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with INT16 components", + "description" : "An example variable length array property, with type MAT4, with component type INT16", + "type" : "MAT4", + "componentType" : "INT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT16_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with INT16 components", + "description" : "An example fixed length array property, with type MAT4, with component type INT16", + "type" : "MAT4", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT16_MAT4" : { + "name" : "Example MAT4 property with normalized INT16 components", + "description" : "An example property, with type MAT4, with component type INT16, normalized", + "type" : "MAT4", + "componentType" : "INT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT16_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with normalized INT16 components", + "description" : "An example variable length array property, with type MAT4, with component type INT16, normalized", + "type" : "MAT4", + "componentType" : "INT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT16_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with normalized INT16 components", + "description" : "An example fixed length array property, with type MAT4, with component type INT16, normalized", + "type" : "MAT4", + "componentType" : "INT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT16_MAT4" : { + "name" : "Example MAT4 property with UINT16 components", + "description" : "An example property, with type MAT4, with component type UINT16", + "type" : "MAT4", + "componentType" : "UINT16", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT16_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with UINT16 components", + "description" : "An example variable length array property, with type MAT4, with component type UINT16", + "type" : "MAT4", + "componentType" : "UINT16", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT16_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with UINT16 components", + "description" : "An example fixed length array property, with type MAT4, with component type UINT16", + "type" : "MAT4", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT16_MAT4" : { + "name" : "Example MAT4 property with normalized UINT16 components", + "description" : "An example property, with type MAT4, with component type UINT16, normalized", + "type" : "MAT4", + "componentType" : "UINT16", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT16_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with normalized UINT16 components", + "description" : "An example variable length array property, with type MAT4, with component type UINT16, normalized", + "type" : "MAT4", + "componentType" : "UINT16", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT16_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with normalized UINT16 components", + "description" : "An example fixed length array property, with type MAT4, with component type UINT16, normalized", + "type" : "MAT4", + "componentType" : "UINT16", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT32_MAT4" : { + "name" : "Example MAT4 property with INT32 components", + "description" : "An example property, with type MAT4, with component type INT32", + "type" : "MAT4", + "componentType" : "INT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT32_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with INT32 components", + "description" : "An example variable length array property, with type MAT4, with component type INT32", + "type" : "MAT4", + "componentType" : "INT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT32_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with INT32 components", + "description" : "An example fixed length array property, with type MAT4, with component type INT32", + "type" : "MAT4", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT32_MAT4" : { + "name" : "Example MAT4 property with normalized INT32 components", + "description" : "An example property, with type MAT4, with component type INT32, normalized", + "type" : "MAT4", + "componentType" : "INT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT32_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with normalized INT32 components", + "description" : "An example variable length array property, with type MAT4, with component type INT32, normalized", + "type" : "MAT4", + "componentType" : "INT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT32_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with normalized INT32 components", + "description" : "An example fixed length array property, with type MAT4, with component type INT32, normalized", + "type" : "MAT4", + "componentType" : "INT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT32_MAT4" : { + "name" : "Example MAT4 property with UINT32 components", + "description" : "An example property, with type MAT4, with component type UINT32", + "type" : "MAT4", + "componentType" : "UINT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT32_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with UINT32 components", + "description" : "An example variable length array property, with type MAT4, with component type UINT32", + "type" : "MAT4", + "componentType" : "UINT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT32_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with UINT32 components", + "description" : "An example fixed length array property, with type MAT4, with component type UINT32", + "type" : "MAT4", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT32_MAT4" : { + "name" : "Example MAT4 property with normalized UINT32 components", + "description" : "An example property, with type MAT4, with component type UINT32, normalized", + "type" : "MAT4", + "componentType" : "UINT32", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT32_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with normalized UINT32 components", + "description" : "An example variable length array property, with type MAT4, with component type UINT32, normalized", + "type" : "MAT4", + "componentType" : "UINT32", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT32_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with normalized UINT32 components", + "description" : "An example fixed length array property, with type MAT4, with component type UINT32, normalized", + "type" : "MAT4", + "componentType" : "UINT32", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_INT64_MAT4" : { + "name" : "Example MAT4 property with INT64 components", + "description" : "An example property, with type MAT4, with component type INT64", + "type" : "MAT4", + "componentType" : "INT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_INT64_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with INT64 components", + "description" : "An example variable length array property, with type MAT4, with component type INT64", + "type" : "MAT4", + "componentType" : "INT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_INT64_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with INT64 components", + "description" : "An example fixed length array property, with type MAT4, with component type INT64", + "type" : "MAT4", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_INT64_MAT4" : { + "name" : "Example MAT4 property with normalized INT64 components", + "description" : "An example property, with type MAT4, with component type INT64, normalized", + "type" : "MAT4", + "componentType" : "INT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_INT64_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with normalized INT64 components", + "description" : "An example variable length array property, with type MAT4, with component type INT64, normalized", + "type" : "MAT4", + "componentType" : "INT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_INT64_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with normalized INT64 components", + "description" : "An example fixed length array property, with type MAT4, with component type INT64, normalized", + "type" : "MAT4", + "componentType" : "INT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_UINT64_MAT4" : { + "name" : "Example MAT4 property with UINT64 components", + "description" : "An example property, with type MAT4, with component type UINT64", + "type" : "MAT4", + "componentType" : "UINT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_UINT64_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with UINT64 components", + "description" : "An example variable length array property, with type MAT4, with component type UINT64", + "type" : "MAT4", + "componentType" : "UINT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_UINT64_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with UINT64 components", + "description" : "An example fixed length array property, with type MAT4, with component type UINT64", + "type" : "MAT4", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_normalized_UINT64_MAT4" : { + "name" : "Example MAT4 property with normalized UINT64 components", + "description" : "An example property, with type MAT4, with component type UINT64, normalized", + "type" : "MAT4", + "componentType" : "UINT64", + "array" : false, + "normalized" : true + }, + "example_variable_length_normalized_UINT64_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with normalized UINT64 components", + "description" : "An example variable length array property, with type MAT4, with component type UINT64, normalized", + "type" : "MAT4", + "componentType" : "UINT64", + "array" : true, + "normalized" : true + }, + "example_fixed_length_normalized_UINT64_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with normalized UINT64 components", + "description" : "An example fixed length array property, with type MAT4, with component type UINT64, normalized", + "type" : "MAT4", + "componentType" : "UINT64", + "array" : true, + "count" : 5, + "normalized" : true + }, + "example_FLOAT32_MAT4" : { + "name" : "Example MAT4 property with FLOAT32 components", + "description" : "An example property, with type MAT4, with component type FLOAT32", + "type" : "MAT4", + "componentType" : "FLOAT32", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT32_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with FLOAT32 components", + "description" : "An example variable length array property, with type MAT4, with component type FLOAT32", + "type" : "MAT4", + "componentType" : "FLOAT32", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT32_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with FLOAT32 components", + "description" : "An example fixed length array property, with type MAT4, with component type FLOAT32", + "type" : "MAT4", + "componentType" : "FLOAT32", + "array" : true, + "count" : 5, + "normalized" : false + }, + "example_FLOAT64_MAT4" : { + "name" : "Example MAT4 property with FLOAT64 components", + "description" : "An example property, with type MAT4, with component type FLOAT64", + "type" : "MAT4", + "componentType" : "FLOAT64", + "array" : false, + "normalized" : false + }, + "example_variable_length_FLOAT64_MAT4_array" : { + "name" : "Example variable-length MAT4 array property with FLOAT64 components", + "description" : "An example variable length array property, with type MAT4, with component type FLOAT64", + "type" : "MAT4", + "componentType" : "FLOAT64", + "array" : true, + "normalized" : false + }, + "example_fixed_length_FLOAT64_MAT4_array" : { + "name" : "Example fixed-length MAT4 array property with FLOAT64 components", + "description" : "An example fixed length array property, with type MAT4, with component type FLOAT64", + "type" : "MAT4", + "componentType" : "FLOAT64", + "array" : true, + "count" : 5, + "normalized" : false + } + } + } + }, + "enums" : { + "exampleEnumType" : { + "values" : [ { + "name" : "ExampleEnumValueA", + "value" : 0 + }, { + "name" : "ExampleEnumValueB", + "value" : 1 + }, { + "name" : "ExampleEnumValueC", + "value" : 2 + } ] + } + } + }, + "metadata" : { + "class" : "exampleClass", + "properties" : { + "example_STRING" : "An example string", + "example_variable_length_STRING_array" : [ "This", "is", "an", "example" ], + "example_fixed_length_STRING_array" : [ "This", "is", "an", "example", "string" ], + "example_BOOLEAN" : true, + "example_variable_length_BOOLEAN_array" : [ true, false, true, false ], + "example_fixed_length_BOOLEAN_array" : [ true, false, true, false, true ], + "example_ENUM" : "ExampleEnumValueB", + "example_variable_length_ENUM_array" : [ "ExampleEnumValueA", "ExampleEnumValueB", "ExampleEnumValueC", "ExampleEnumValueA" ], + "example_fixed_length_ENUM_array" : [ "ExampleEnumValueA", "ExampleEnumValueB", "ExampleEnumValueC", "ExampleEnumValueA", "ExampleEnumValueB" ], + "example_INT8_SCALAR" : -128, + "example_variable_length_INT8_SCALAR_array" : [ -128, -43, 42, 127 ], + "example_fixed_length_INT8_SCALAR_array" : [ -128, -64, 0, 63, 127 ], + "example_normalized_INT8_SCALAR" : -128, + "example_variable_length_normalized_INT8_SCALAR_array" : [ -128, -43, 42, 127 ], + "example_fixed_length_normalized_INT8_SCALAR_array" : [ -128, -64, 0, 63, 127 ], + "example_UINT8_SCALAR" : 255, + "example_variable_length_UINT8_SCALAR_array" : [ 0, 84, 170, 255 ], + "example_fixed_length_UINT8_SCALAR_array" : [ 0, 63, 127, 191, 255 ], + "example_normalized_UINT8_SCALAR" : 255, + "example_variable_length_normalized_UINT8_SCALAR_array" : [ 0, 84, 170, 255 ], + "example_fixed_length_normalized_UINT8_SCALAR_array" : [ 0, 63, 127, 191, 255 ], + "example_INT16_SCALAR" : -32768, + "example_variable_length_INT16_SCALAR_array" : [ -32768, -10923, 10922, 32767 ], + "example_fixed_length_INT16_SCALAR_array" : [ -32768, -16384, 0, 16383, 32767 ], + "example_normalized_INT16_SCALAR" : -32768, + "example_variable_length_normalized_INT16_SCALAR_array" : [ -32768, -10923, 10922, 32767 ], + "example_fixed_length_normalized_INT16_SCALAR_array" : [ -32768, -16384, 0, 16383, 32767 ], + "example_UINT16_SCALAR" : 65535, + "example_variable_length_UINT16_SCALAR_array" : [ 0, 21844, 43690, 65535 ], + "example_fixed_length_UINT16_SCALAR_array" : [ 0, 16383, 32767, 49151, 65535 ], + "example_normalized_UINT16_SCALAR" : 65535, + "example_variable_length_normalized_UINT16_SCALAR_array" : [ 0, 21844, 43690, 65535 ], + "example_fixed_length_normalized_UINT16_SCALAR_array" : [ 0, 16383, 32767, 49151, 65535 ], + "example_INT32_SCALAR" : -2147483648, + "example_variable_length_INT32_SCALAR_array" : [ -2147483648, -715827883, 715827882, 2147483647 ], + "example_fixed_length_INT32_SCALAR_array" : [ -2147483648, -1073741824, 0, 1073741823, 2147483647 ], + "example_normalized_INT32_SCALAR" : -2147483648, + "example_variable_length_normalized_INT32_SCALAR_array" : [ -2147483648, -715827883, 715827882, 2147483647 ], + "example_fixed_length_normalized_INT32_SCALAR_array" : [ -2147483648, -1073741824, 0, 1073741823, 2147483647 ], + "example_UINT32_SCALAR" : 4294967295, + "example_variable_length_UINT32_SCALAR_array" : [ 0, 1431655764, 2863311530, 4294967295 ], + "example_fixed_length_UINT32_SCALAR_array" : [ 0, 1073741823, 2147483647, 3221225471, 4294967295 ], + "example_normalized_UINT32_SCALAR" : 4294967295, + "example_variable_length_normalized_UINT32_SCALAR_array" : [ 0, 1431655764, 2863311530, 4294967295 ], + "example_fixed_length_normalized_UINT32_SCALAR_array" : [ 0, 1073741823, 2147483647, 3221225471, 4294967295 ], + "example_INT64_SCALAR" : -9223372036854775808, + "example_variable_length_INT64_SCALAR_array" : [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], + "example_fixed_length_INT64_SCALAR_array" : [ -9223372036854775808, -4611686018427387904, 0, 4611686018427387903, 9223372036854775807 ], + "example_normalized_INT64_SCALAR" : -9223372036854775808, + "example_variable_length_normalized_INT64_SCALAR_array" : [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], + "example_fixed_length_normalized_INT64_SCALAR_array" : [ -9223372036854775808, -4611686018427387904, 0, 4611686018427387903, 9223372036854775807 ], + "example_UINT64_SCALAR" : 18446744073709551615, + "example_variable_length_UINT64_SCALAR_array" : [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], + "example_fixed_length_UINT64_SCALAR_array" : [ 0, 4611686018427387903, 9223372036854775807, 13835058055282163711, 18446744073709551615 ], + "example_normalized_UINT64_SCALAR" : 18446744073709551615, + "example_variable_length_normalized_UINT64_SCALAR_array" : [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], + "example_fixed_length_normalized_UINT64_SCALAR_array" : [ 0, 4611686018427387903, 9223372036854775807, 13835058055282163711, 18446744073709551615 ], + "example_FLOAT32_SCALAR" : 1.2, + "example_variable_length_FLOAT32_SCALAR_array" : [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], + "example_fixed_length_FLOAT32_SCALAR_array" : [ -1.0, -0.5, 0.0, 0.5, 1.0 ], + "example_FLOAT64_SCALAR" : 12.34, + "example_variable_length_FLOAT64_SCALAR_array" : [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], + "example_fixed_length_FLOAT64_SCALAR_array" : [ -1.0, -0.5, 0.0, 0.5, 1.0 ], + "example_INT8_VEC2" : [ -128, 127 ], + "example_variable_length_INT8_VEC2_array" : [ [ -128, 127 ], [ -128, 127 ], [ -128, 127 ], [ -128, 127 ] ], + "example_fixed_length_INT8_VEC2_array" : [ [ -128, 127 ], [ -128, 127 ], [ -128, 127 ], [ -128, 127 ], [ -128, 127 ] ], + "example_normalized_INT8_VEC2" : [ -128, 127 ], + "example_variable_length_normalized_INT8_VEC2_array" : [ [ -128, 127 ], [ -128, 127 ], [ -128, 127 ], [ -128, 127 ] ], + "example_fixed_length_normalized_INT8_VEC2_array" : [ [ -128, 127 ], [ -128, 127 ], [ -128, 127 ], [ -128, 127 ], [ -128, 127 ] ], + "example_UINT8_VEC2" : [ 0, 255 ], + "example_variable_length_UINT8_VEC2_array" : [ [ 0, 255 ], [ 0, 255 ], [ 0, 255 ], [ 0, 255 ] ], + "example_fixed_length_UINT8_VEC2_array" : [ [ 0, 255 ], [ 0, 255 ], [ 0, 255 ], [ 0, 255 ], [ 0, 255 ] ], + "example_normalized_UINT8_VEC2" : [ 0, 255 ], + "example_variable_length_normalized_UINT8_VEC2_array" : [ [ 0, 255 ], [ 0, 255 ], [ 0, 255 ], [ 0, 255 ] ], + "example_fixed_length_normalized_UINT8_VEC2_array" : [ [ 0, 255 ], [ 0, 255 ], [ 0, 255 ], [ 0, 255 ], [ 0, 255 ] ], + "example_INT16_VEC2" : [ -32768, 32767 ], + "example_variable_length_INT16_VEC2_array" : [ [ -32768, 32767 ], [ -32768, 32767 ], [ -32768, 32767 ], [ -32768, 32767 ] ], + "example_fixed_length_INT16_VEC2_array" : [ [ -32768, 32767 ], [ -32768, 32767 ], [ -32768, 32767 ], [ -32768, 32767 ], [ -32768, 32767 ] ], + "example_normalized_INT16_VEC2" : [ -32768, 32767 ], + "example_variable_length_normalized_INT16_VEC2_array" : [ [ -32768, 32767 ], [ -32768, 32767 ], [ -32768, 32767 ], [ -32768, 32767 ] ], + "example_fixed_length_normalized_INT16_VEC2_array" : [ [ -32768, 32767 ], [ -32768, 32767 ], [ -32768, 32767 ], [ -32768, 32767 ], [ -32768, 32767 ] ], + "example_UINT16_VEC2" : [ 0, 65535 ], + "example_variable_length_UINT16_VEC2_array" : [ [ 0, 65535 ], [ 0, 65535 ], [ 0, 65535 ], [ 0, 65535 ] ], + "example_fixed_length_UINT16_VEC2_array" : [ [ 0, 65535 ], [ 0, 65535 ], [ 0, 65535 ], [ 0, 65535 ], [ 0, 65535 ] ], + "example_normalized_UINT16_VEC2" : [ 0, 65535 ], + "example_variable_length_normalized_UINT16_VEC2_array" : [ [ 0, 65535 ], [ 0, 65535 ], [ 0, 65535 ], [ 0, 65535 ] ], + "example_fixed_length_normalized_UINT16_VEC2_array" : [ [ 0, 65535 ], [ 0, 65535 ], [ 0, 65535 ], [ 0, 65535 ], [ 0, 65535 ] ], + "example_INT32_VEC2" : [ -2147483648, 2147483647 ], + "example_variable_length_INT32_VEC2_array" : [ [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ] ], + "example_fixed_length_INT32_VEC2_array" : [ [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ] ], + "example_normalized_INT32_VEC2" : [ -2147483648, 2147483647 ], + "example_variable_length_normalized_INT32_VEC2_array" : [ [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ] ], + "example_fixed_length_normalized_INT32_VEC2_array" : [ [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ], [ -2147483648, 2147483647 ] ], + "example_UINT32_VEC2" : [ 0, 4294967295 ], + "example_variable_length_UINT32_VEC2_array" : [ [ 0, 4294967295 ], [ 0, 4294967295 ], [ 0, 4294967295 ], [ 0, 4294967295 ] ], + "example_fixed_length_UINT32_VEC2_array" : [ [ 0, 4294967295 ], [ 0, 4294967295 ], [ 0, 4294967295 ], [ 0, 4294967295 ], [ 0, 4294967295 ] ], + "example_normalized_UINT32_VEC2" : [ 0, 4294967295 ], + "example_variable_length_normalized_UINT32_VEC2_array" : [ [ 0, 4294967295 ], [ 0, 4294967295 ], [ 0, 4294967295 ], [ 0, 4294967295 ] ], + "example_fixed_length_normalized_UINT32_VEC2_array" : [ [ 0, 4294967295 ], [ 0, 4294967295 ], [ 0, 4294967295 ], [ 0, 4294967295 ], [ 0, 4294967295 ] ], + "example_INT64_VEC2" : [ -9223372036854775808, 9223372036854775807 ], + "example_variable_length_INT64_VEC2_array" : [ [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ] ], + "example_fixed_length_INT64_VEC2_array" : [ [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ] ], + "example_normalized_INT64_VEC2" : [ -9223372036854775808, 9223372036854775807 ], + "example_variable_length_normalized_INT64_VEC2_array" : [ [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ] ], + "example_fixed_length_normalized_INT64_VEC2_array" : [ [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ], [ -9223372036854775808, 9223372036854775807 ] ], + "example_UINT64_VEC2" : [ 0, 18446744073709551615 ], + "example_variable_length_UINT64_VEC2_array" : [ [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ] ], + "example_fixed_length_UINT64_VEC2_array" : [ [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ] ], + "example_normalized_UINT64_VEC2" : [ 0, 18446744073709551615 ], + "example_variable_length_normalized_UINT64_VEC2_array" : [ [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ] ], + "example_fixed_length_normalized_UINT64_VEC2_array" : [ [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ], [ 0, 18446744073709551615 ] ], + "example_FLOAT32_VEC2" : [ -1.0, 1.0 ], + "example_variable_length_FLOAT32_VEC2_array" : [ [ -1.0, 1.0 ], [ -1.0, 1.0 ], [ -1.0, 1.0 ], [ -1.0, 1.0 ] ], + "example_fixed_length_FLOAT32_VEC2_array" : [ [ -1.0, 1.0 ], [ -1.0, 1.0 ], [ -1.0, 1.0 ], [ -1.0, 1.0 ], [ -1.0, 1.0 ] ], + "example_FLOAT64_VEC2" : [ -1.0, 1.0 ], + "example_variable_length_FLOAT64_VEC2_array" : [ [ -1.0, 1.0 ], [ -1.0, 1.0 ], [ -1.0, 1.0 ], [ -1.0, 1.0 ] ], + "example_fixed_length_FLOAT64_VEC2_array" : [ [ -1.0, 1.0 ], [ -1.0, 1.0 ], [ -1.0, 1.0 ], [ -1.0, 1.0 ], [ -1.0, 1.0 ] ], + "example_INT8_VEC3" : [ -128, 0, 127 ], + "example_variable_length_INT8_VEC3_array" : [ [ -128, 0, 127 ], [ -128, 0, 127 ], [ -128, 0, 127 ], [ -128, 0, 127 ] ], + "example_fixed_length_INT8_VEC3_array" : [ [ -128, 0, 127 ], [ -128, 0, 127 ], [ -128, 0, 127 ], [ -128, 0, 127 ], [ -128, 0, 127 ] ], + "example_normalized_INT8_VEC3" : [ -128, 0, 127 ], + "example_variable_length_normalized_INT8_VEC3_array" : [ [ -128, 0, 127 ], [ -128, 0, 127 ], [ -128, 0, 127 ], [ -128, 0, 127 ] ], + "example_fixed_length_normalized_INT8_VEC3_array" : [ [ -128, 0, 127 ], [ -128, 0, 127 ], [ -128, 0, 127 ], [ -128, 0, 127 ], [ -128, 0, 127 ] ], + "example_UINT8_VEC3" : [ 0, 127, 255 ], + "example_variable_length_UINT8_VEC3_array" : [ [ 0, 127, 255 ], [ 0, 127, 255 ], [ 0, 127, 255 ], [ 0, 127, 255 ] ], + "example_fixed_length_UINT8_VEC3_array" : [ [ 0, 127, 255 ], [ 0, 127, 255 ], [ 0, 127, 255 ], [ 0, 127, 255 ], [ 0, 127, 255 ] ], + "example_normalized_UINT8_VEC3" : [ 0, 127, 255 ], + "example_variable_length_normalized_UINT8_VEC3_array" : [ [ 0, 127, 255 ], [ 0, 127, 255 ], [ 0, 127, 255 ], [ 0, 127, 255 ] ], + "example_fixed_length_normalized_UINT8_VEC3_array" : [ [ 0, 127, 255 ], [ 0, 127, 255 ], [ 0, 127, 255 ], [ 0, 127, 255 ], [ 0, 127, 255 ] ], + "example_INT16_VEC3" : [ -32768, 0, 32767 ], + "example_variable_length_INT16_VEC3_array" : [ [ -32768, 0, 32767 ], [ -32768, 0, 32767 ], [ -32768, 0, 32767 ], [ -32768, 0, 32767 ] ], + "example_fixed_length_INT16_VEC3_array" : [ [ -32768, 0, 32767 ], [ -32768, 0, 32767 ], [ -32768, 0, 32767 ], [ -32768, 0, 32767 ], [ -32768, 0, 32767 ] ], + "example_normalized_INT16_VEC3" : [ -32768, 0, 32767 ], + "example_variable_length_normalized_INT16_VEC3_array" : [ [ -32768, 0, 32767 ], [ -32768, 0, 32767 ], [ -32768, 0, 32767 ], [ -32768, 0, 32767 ] ], + "example_fixed_length_normalized_INT16_VEC3_array" : [ [ -32768, 0, 32767 ], [ -32768, 0, 32767 ], [ -32768, 0, 32767 ], [ -32768, 0, 32767 ], [ -32768, 0, 32767 ] ], + "example_UINT16_VEC3" : [ 0, 32767, 65535 ], + "example_variable_length_UINT16_VEC3_array" : [ [ 0, 32767, 65535 ], [ 0, 32767, 65535 ], [ 0, 32767, 65535 ], [ 0, 32767, 65535 ] ], + "example_fixed_length_UINT16_VEC3_array" : [ [ 0, 32767, 65535 ], [ 0, 32767, 65535 ], [ 0, 32767, 65535 ], [ 0, 32767, 65535 ], [ 0, 32767, 65535 ] ], + "example_normalized_UINT16_VEC3" : [ 0, 32767, 65535 ], + "example_variable_length_normalized_UINT16_VEC3_array" : [ [ 0, 32767, 65535 ], [ 0, 32767, 65535 ], [ 0, 32767, 65535 ], [ 0, 32767, 65535 ] ], + "example_fixed_length_normalized_UINT16_VEC3_array" : [ [ 0, 32767, 65535 ], [ 0, 32767, 65535 ], [ 0, 32767, 65535 ], [ 0, 32767, 65535 ], [ 0, 32767, 65535 ] ], + "example_INT32_VEC3" : [ -2147483648, 0, 2147483647 ], + "example_variable_length_INT32_VEC3_array" : [ [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ] ], + "example_fixed_length_INT32_VEC3_array" : [ [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ] ], + "example_normalized_INT32_VEC3" : [ -2147483648, 0, 2147483647 ], + "example_variable_length_normalized_INT32_VEC3_array" : [ [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ] ], + "example_fixed_length_normalized_INT32_VEC3_array" : [ [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ], [ -2147483648, 0, 2147483647 ] ], + "example_UINT32_VEC3" : [ 0, 2147483647, 4294967295 ], + "example_variable_length_UINT32_VEC3_array" : [ [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ] ], + "example_fixed_length_UINT32_VEC3_array" : [ [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ] ], + "example_normalized_UINT32_VEC3" : [ 0, 2147483647, 4294967295 ], + "example_variable_length_normalized_UINT32_VEC3_array" : [ [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ] ], + "example_fixed_length_normalized_UINT32_VEC3_array" : [ [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ], [ 0, 2147483647, 4294967295 ] ], + "example_INT64_VEC3" : [ -9223372036854775808, 0, 9223372036854775807 ], + "example_variable_length_INT64_VEC3_array" : [ [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ] ], + "example_fixed_length_INT64_VEC3_array" : [ [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ] ], + "example_normalized_INT64_VEC3" : [ -9223372036854775808, 0, 9223372036854775807 ], + "example_variable_length_normalized_INT64_VEC3_array" : [ [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ] ], + "example_fixed_length_normalized_INT64_VEC3_array" : [ [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ], [ -9223372036854775808, 0, 9223372036854775807 ] ], + "example_UINT64_VEC3" : [ 0, 9223372036854775807, 18446744073709551615 ], + "example_variable_length_UINT64_VEC3_array" : [ [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ] ], + "example_fixed_length_UINT64_VEC3_array" : [ [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ] ], + "example_normalized_UINT64_VEC3" : [ 0, 9223372036854775807, 18446744073709551615 ], + "example_variable_length_normalized_UINT64_VEC3_array" : [ [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ] ], + "example_fixed_length_normalized_UINT64_VEC3_array" : [ [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ], [ 0, 9223372036854775807, 18446744073709551615 ] ], + "example_FLOAT32_VEC3" : [ -1.0, 0.0, 1.0 ], + "example_variable_length_FLOAT32_VEC3_array" : [ [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ] ], + "example_fixed_length_FLOAT32_VEC3_array" : [ [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ] ], + "example_FLOAT64_VEC3" : [ -1.0, 0.0, 1.0 ], + "example_variable_length_FLOAT64_VEC3_array" : [ [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ] ], + "example_fixed_length_FLOAT64_VEC3_array" : [ [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ], [ -1.0, 0.0, 1.0 ] ], + "example_INT8_VEC4" : [ -128, -43, 42, 127 ], + "example_variable_length_INT8_VEC4_array" : [ [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ] ], + "example_fixed_length_INT8_VEC4_array" : [ [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ] ], + "example_normalized_INT8_VEC4" : [ -128, -43, 42, 127 ], + "example_variable_length_normalized_INT8_VEC4_array" : [ [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ] ], + "example_fixed_length_normalized_INT8_VEC4_array" : [ [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ] ], + "example_UINT8_VEC4" : [ 0, 84, 170, 255 ], + "example_variable_length_UINT8_VEC4_array" : [ [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ] ], + "example_fixed_length_UINT8_VEC4_array" : [ [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ] ], + "example_normalized_UINT8_VEC4" : [ 0, 84, 170, 255 ], + "example_variable_length_normalized_UINT8_VEC4_array" : [ [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ] ], + "example_fixed_length_normalized_UINT8_VEC4_array" : [ [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ] ], + "example_INT16_VEC4" : [ -32768, -10923, 10922, 32767 ], + "example_variable_length_INT16_VEC4_array" : [ [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ] ], + "example_fixed_length_INT16_VEC4_array" : [ [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ] ], + "example_normalized_INT16_VEC4" : [ -32768, -10923, 10922, 32767 ], + "example_variable_length_normalized_INT16_VEC4_array" : [ [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ] ], + "example_fixed_length_normalized_INT16_VEC4_array" : [ [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ] ], + "example_UINT16_VEC4" : [ 0, 21844, 43690, 65535 ], + "example_variable_length_UINT16_VEC4_array" : [ [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ] ], + "example_fixed_length_UINT16_VEC4_array" : [ [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ] ], + "example_normalized_UINT16_VEC4" : [ 0, 21844, 43690, 65535 ], + "example_variable_length_normalized_UINT16_VEC4_array" : [ [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ] ], + "example_fixed_length_normalized_UINT16_VEC4_array" : [ [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ] ], + "example_INT32_VEC4" : [ -2147483648, -715827883, 715827882, 2147483647 ], + "example_variable_length_INT32_VEC4_array" : [ [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ] ], + "example_fixed_length_INT32_VEC4_array" : [ [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ] ], + "example_normalized_INT32_VEC4" : [ -2147483648, -715827883, 715827882, 2147483647 ], + "example_variable_length_normalized_INT32_VEC4_array" : [ [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ] ], + "example_fixed_length_normalized_INT32_VEC4_array" : [ [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ] ], + "example_UINT32_VEC4" : [ 0, 1431655764, 2863311530, 4294967295 ], + "example_variable_length_UINT32_VEC4_array" : [ [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ] ], + "example_fixed_length_UINT32_VEC4_array" : [ [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ] ], + "example_normalized_UINT32_VEC4" : [ 0, 1431655764, 2863311530, 4294967295 ], + "example_variable_length_normalized_UINT32_VEC4_array" : [ [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ] ], + "example_fixed_length_normalized_UINT32_VEC4_array" : [ [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ] ], + "example_INT64_VEC4" : [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], + "example_variable_length_INT64_VEC4_array" : [ [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ] ], + "example_fixed_length_INT64_VEC4_array" : [ [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ] ], + "example_normalized_INT64_VEC4" : [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], + "example_variable_length_normalized_INT64_VEC4_array" : [ [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ] ], + "example_fixed_length_normalized_INT64_VEC4_array" : [ [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ] ], + "example_UINT64_VEC4" : [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], + "example_variable_length_UINT64_VEC4_array" : [ [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ] ], + "example_fixed_length_UINT64_VEC4_array" : [ [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ] ], + "example_normalized_UINT64_VEC4" : [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], + "example_variable_length_normalized_UINT64_VEC4_array" : [ [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ] ], + "example_fixed_length_normalized_UINT64_VEC4_array" : [ [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ] ], + "example_FLOAT32_VEC4" : [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], + "example_variable_length_FLOAT32_VEC4_array" : [ [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ] ], + "example_fixed_length_FLOAT32_VEC4_array" : [ [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ] ], + "example_FLOAT64_VEC4" : [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], + "example_variable_length_FLOAT64_VEC4_array" : [ [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ] ], + "example_fixed_length_FLOAT64_VEC4_array" : [ [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ] ], + "example_INT8_MAT2" : [ -128, -43, 42, 127 ], + "example_variable_length_INT8_MAT2_array" : [ [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ] ], + "example_fixed_length_INT8_MAT2_array" : [ [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ] ], + "example_normalized_INT8_MAT2" : [ -128, -43, 42, 127 ], + "example_variable_length_normalized_INT8_MAT2_array" : [ [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ] ], + "example_fixed_length_normalized_INT8_MAT2_array" : [ [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ], [ -128, -43, 42, 127 ] ], + "example_UINT8_MAT2" : [ 0, 84, 170, 255 ], + "example_variable_length_UINT8_MAT2_array" : [ [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ] ], + "example_fixed_length_UINT8_MAT2_array" : [ [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ] ], + "example_normalized_UINT8_MAT2" : [ 0, 84, 170, 255 ], + "example_variable_length_normalized_UINT8_MAT2_array" : [ [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ] ], + "example_fixed_length_normalized_UINT8_MAT2_array" : [ [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ], [ 0, 84, 170, 255 ] ], + "example_INT16_MAT2" : [ -32768, -10923, 10922, 32767 ], + "example_variable_length_INT16_MAT2_array" : [ [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ] ], + "example_fixed_length_INT16_MAT2_array" : [ [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ] ], + "example_normalized_INT16_MAT2" : [ -32768, -10923, 10922, 32767 ], + "example_variable_length_normalized_INT16_MAT2_array" : [ [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ] ], + "example_fixed_length_normalized_INT16_MAT2_array" : [ [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ], [ -32768, -10923, 10922, 32767 ] ], + "example_UINT16_MAT2" : [ 0, 21844, 43690, 65535 ], + "example_variable_length_UINT16_MAT2_array" : [ [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ] ], + "example_fixed_length_UINT16_MAT2_array" : [ [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ] ], + "example_normalized_UINT16_MAT2" : [ 0, 21844, 43690, 65535 ], + "example_variable_length_normalized_UINT16_MAT2_array" : [ [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ] ], + "example_fixed_length_normalized_UINT16_MAT2_array" : [ [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ], [ 0, 21844, 43690, 65535 ] ], + "example_INT32_MAT2" : [ -2147483648, -715827883, 715827882, 2147483647 ], + "example_variable_length_INT32_MAT2_array" : [ [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ] ], + "example_fixed_length_INT32_MAT2_array" : [ [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ] ], + "example_normalized_INT32_MAT2" : [ -2147483648, -715827883, 715827882, 2147483647 ], + "example_variable_length_normalized_INT32_MAT2_array" : [ [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ] ], + "example_fixed_length_normalized_INT32_MAT2_array" : [ [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ], [ -2147483648, -715827883, 715827882, 2147483647 ] ], + "example_UINT32_MAT2" : [ 0, 1431655764, 2863311530, 4294967295 ], + "example_variable_length_UINT32_MAT2_array" : [ [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ] ], + "example_fixed_length_UINT32_MAT2_array" : [ [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ] ], + "example_normalized_UINT32_MAT2" : [ 0, 1431655764, 2863311530, 4294967295 ], + "example_variable_length_normalized_UINT32_MAT2_array" : [ [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ] ], + "example_fixed_length_normalized_UINT32_MAT2_array" : [ [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ], [ 0, 1431655764, 2863311530, 4294967295 ] ], + "example_INT64_MAT2" : [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], + "example_variable_length_INT64_MAT2_array" : [ [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ] ], + "example_fixed_length_INT64_MAT2_array" : [ [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ] ], + "example_normalized_INT64_MAT2" : [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], + "example_variable_length_normalized_INT64_MAT2_array" : [ [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ] ], + "example_fixed_length_normalized_INT64_MAT2_array" : [ [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ], [ -9223372036854775808, -3074457346233150072, 3074457346233150071, 9223372036854775807 ] ], + "example_UINT64_MAT2" : [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], + "example_variable_length_UINT64_MAT2_array" : [ [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ] ], + "example_fixed_length_UINT64_MAT2_array" : [ [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ] ], + "example_normalized_UINT64_MAT2" : [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], + "example_variable_length_normalized_UINT64_MAT2_array" : [ [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ] ], + "example_fixed_length_normalized_UINT64_MAT2_array" : [ [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ], [ 0, 6148914690621625735, 12297829383087925879, 18446744073709551615 ] ], + "example_FLOAT32_MAT2" : [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], + "example_variable_length_FLOAT32_MAT2_array" : [ [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ] ], + "example_fixed_length_FLOAT32_MAT2_array" : [ [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ] ], + "example_FLOAT64_MAT2" : [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], + "example_variable_length_FLOAT64_MAT2_array" : [ [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ] ], + "example_fixed_length_FLOAT64_MAT2_array" : [ [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ], [ -1.0, -0.3333333334, 0.3333333334, 1.0 ] ], + "example_INT8_MAT3" : [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], + "example_variable_length_INT8_MAT3_array" : [ [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ] ], + "example_fixed_length_INT8_MAT3_array" : [ [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ] ], + "example_normalized_INT8_MAT3" : [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], + "example_variable_length_normalized_INT8_MAT3_array" : [ [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ] ], + "example_fixed_length_normalized_INT8_MAT3_array" : [ [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ], [ -128, -96, -64, -32, 0, 31, 63, 95, 127 ] ], + "example_UINT8_MAT3" : [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], + "example_variable_length_UINT8_MAT3_array" : [ [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ] ], + "example_fixed_length_UINT8_MAT3_array" : [ [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ] ], + "example_normalized_UINT8_MAT3" : [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], + "example_variable_length_normalized_UINT8_MAT3_array" : [ [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ] ], + "example_fixed_length_normalized_UINT8_MAT3_array" : [ [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ], [ 0, 31, 63, 95, 127, 159, 191, 223, 255 ] ], + "example_INT16_MAT3" : [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], + "example_variable_length_INT16_MAT3_array" : [ [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ] ], + "example_fixed_length_INT16_MAT3_array" : [ [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ] ], + "example_normalized_INT16_MAT3" : [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], + "example_variable_length_normalized_INT16_MAT3_array" : [ [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ] ], + "example_fixed_length_normalized_INT16_MAT3_array" : [ [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ], [ -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767 ] ], + "example_UINT16_MAT3" : [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], + "example_variable_length_UINT16_MAT3_array" : [ [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ] ], + "example_fixed_length_UINT16_MAT3_array" : [ [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ] ], + "example_normalized_UINT16_MAT3" : [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], + "example_variable_length_normalized_UINT16_MAT3_array" : [ [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ] ], + "example_fixed_length_normalized_UINT16_MAT3_array" : [ [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ], [ 0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535 ] ], + "example_INT32_MAT3" : [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], + "example_variable_length_INT32_MAT3_array" : [ [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ] ], + "example_fixed_length_INT32_MAT3_array" : [ [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ] ], + "example_normalized_INT32_MAT3" : [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], + "example_variable_length_normalized_INT32_MAT3_array" : [ [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ] ], + "example_fixed_length_normalized_INT32_MAT3_array" : [ [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ], [ -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, 1073741823, 1610612735, 2147483647 ] ], + "example_UINT32_MAT3" : [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], + "example_variable_length_UINT32_MAT3_array" : [ [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ] ], + "example_fixed_length_UINT32_MAT3_array" : [ [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ] ], + "example_normalized_UINT32_MAT3" : [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], + "example_variable_length_normalized_UINT32_MAT3_array" : [ [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ] ], + "example_fixed_length_normalized_UINT32_MAT3_array" : [ [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ], [ 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, 3758096383, 4294967295 ] ], + "example_INT64_MAT3" : [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], + "example_variable_length_INT64_MAT3_array" : [ [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ] ], + "example_fixed_length_INT64_MAT3_array" : [ [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ] ], + "example_normalized_INT64_MAT3" : [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], + "example_variable_length_normalized_INT64_MAT3_array" : [ [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ] ], + "example_fixed_length_normalized_INT64_MAT3_array" : [ [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ], [ -9223372036854775808, -6917529027641081856, -4611686018427387904, -2305843009213693952, 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807 ] ], + "example_UINT64_MAT3" : [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], + "example_variable_length_UINT64_MAT3_array" : [ [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ] ], + "example_fixed_length_UINT64_MAT3_array" : [ [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ] ], + "example_normalized_UINT64_MAT3" : [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], + "example_variable_length_normalized_UINT64_MAT3_array" : [ [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ] ], + "example_fixed_length_normalized_UINT64_MAT3_array" : [ [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ], [ 0, 2305843009213693951, 4611686018427387903, 6917529027641081855, 9223372036854775807, 11529215046068469759, 13835058055282163711, 16140901064495857663, 18446744073709551615 ] ], + "example_FLOAT32_MAT3" : [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], + "example_variable_length_FLOAT32_MAT3_array" : [ [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ] ], + "example_fixed_length_FLOAT32_MAT3_array" : [ [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ] ], + "example_FLOAT64_MAT3" : [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], + "example_variable_length_FLOAT64_MAT3_array" : [ [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ] ], + "example_fixed_length_FLOAT64_MAT3_array" : [ [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ], [ -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0 ] ], + "example_INT8_MAT4" : [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], + "example_variable_length_INT8_MAT4_array" : [ [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ] ], + "example_fixed_length_INT8_MAT4_array" : [ [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ] ], + "example_normalized_INT8_MAT4" : [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], + "example_variable_length_normalized_INT8_MAT4_array" : [ [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ] ], + "example_fixed_length_normalized_INT8_MAT4_array" : [ [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ], [ -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127 ] ], + "example_UINT8_MAT4" : [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], + "example_variable_length_UINT8_MAT4_array" : [ [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ] ], + "example_fixed_length_UINT8_MAT4_array" : [ [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ] ], + "example_normalized_UINT8_MAT4" : [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], + "example_variable_length_normalized_UINT8_MAT4_array" : [ [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ] ], + "example_fixed_length_normalized_UINT8_MAT4_array" : [ [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ], [ 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255 ] ], + "example_INT16_MAT4" : [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], + "example_variable_length_INT16_MAT4_array" : [ [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ] ], + "example_fixed_length_INT16_MAT4_array" : [ [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ] ], + "example_normalized_INT16_MAT4" : [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], + "example_variable_length_normalized_INT16_MAT4_array" : [ [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ] ], + "example_fixed_length_normalized_INT16_MAT4_array" : [ [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ], [ -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, 10922, 15290, 19660, 24029, 28397, 32767 ] ], + "example_UINT16_MAT4" : [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], + "example_variable_length_UINT16_MAT4_array" : [ [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ] ], + "example_fixed_length_UINT16_MAT4_array" : [ [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ] ], + "example_normalized_UINT16_MAT4" : [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], + "example_variable_length_normalized_UINT16_MAT4_array" : [ [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ] ], + "example_fixed_length_normalized_UINT16_MAT4_array" : [ [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ], [ 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, 48058, 52428, 56797, 61165, 65535 ] ], + "example_INT32_MAT4" : [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], + "example_variable_length_INT32_MAT4_array" : [ [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ] ], + "example_fixed_length_INT32_MAT4_array" : [ [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ] ], + "example_normalized_INT32_MAT4" : [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], + "example_variable_length_normalized_INT32_MAT4_array" : [ [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ] ], + "example_fixed_length_normalized_INT32_MAT4_array" : [ [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ], [ -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, 1002159034, 1288490188, 1574821341, 1861152493, 2147483647 ] ], + "example_UINT32_MAT4" : [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], + "example_variable_length_UINT32_MAT4_array" : [ [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ] ], + "example_fixed_length_UINT32_MAT4_array" : [ [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ] ], + "example_normalized_UINT32_MAT4" : [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], + "example_variable_length_normalized_UINT32_MAT4_array" : [ [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ] ], + "example_fixed_length_normalized_UINT32_MAT4_array" : [ [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ], [ 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, 3722304989, 4008636141, 4294967295 ] ], + "example_INT64_MAT4" : [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], + "example_variable_length_INT64_MAT4_array" : [ [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ] ], + "example_fixed_length_INT64_MAT4_array" : [ [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ] ], + "example_normalized_INT64_MAT4" : [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], + "example_variable_length_normalized_INT64_MAT4_array" : [ [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ] ], + "example_fixed_length_normalized_INT64_MAT4_array" : [ [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ], [ -9223372036854775808, -7993589097992580897, -6763806160975060395, -5534023222112865485, -4304240283250670574, -3074457346233150072, -1844674407370955162, -614891468508760251, 614891468508760250, 1844674407370955161, 3074457346233150071, 4304240283250670573, 5534023222112865484, 6763806160975060394, 7993589097992580896, 9223372036854775807 ] ], + "example_UINT64_MAT4" : [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], + "example_variable_length_UINT64_MAT4_array" : [ [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ] ], + "example_fixed_length_UINT64_MAT4_array" : [ [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ] ], + "example_normalized_UINT64_MAT4" : [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], + "example_variable_length_normalized_UINT64_MAT4_array" : [ [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ] ], + "example_fixed_length_normalized_UINT64_MAT4_array" : [ [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ], [ 0, 1229782938862194910, 2459565875879715412, 3689348814741910323, 4919131753604105233, 6148914690621625735, 7378697629483820646, 8608480568346015556, 9838263505363536058, 11068046444225730969, 12297829383087925879, 13527612320105446381, 14757395258967641292, 15987178197829836202, 17216961134847356704, 18446744073709551615 ] ], + "example_FLOAT32_MAT4" : [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], + "example_variable_length_FLOAT32_MAT4_array" : [ [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ] ], + "example_fixed_length_FLOAT32_MAT4_array" : [ [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ] ], + "example_FLOAT64_MAT4" : [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], + "example_variable_length_FLOAT64_MAT4_array" : [ [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ] ], + "example_fixed_length_FLOAT64_MAT4_array" : [ [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ], [ -1.0, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, 0.7333333334, 0.8666666666, 1.0 ] ] + } + }, + "geometricError" : 2.0, + "root" : { + "boundingVolume" : { + "box" : [ 0.5, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5 ] + }, + "geometricError" : 1.0 + } +} \ No newline at end of file diff --git a/specs/metadata/ArrayValuesSpec.ts b/specs/metadata/ArrayValuesSpec.ts new file mode 100644 index 00000000..b26ed432 --- /dev/null +++ b/specs/metadata/ArrayValuesSpec.ts @@ -0,0 +1,635 @@ +import { ArrayValues } from "../../src/metadata/ArrayValues"; + +describe("metadata/ArrayValues", function () { + //========================================================================== + // deepMultiply + + it("performs deepMultiply for number,number", function () { + const value = 2; + const factor = 3; + const expected = 2 * 3; + const actual = ArrayValues.deepMultiply(value, factor); + expect(actual).toEqual(expected); + }); + + it("performs deepMultiply for number[],number[]", function () { + const value = [2, 3]; + const factor = [3, 4]; + const expected = [2 * 3, 3 * 4]; + const actual = ArrayValues.deepMultiply(value, factor); + expect(actual).toEqual(expected); + }); + + it("performs deepMultiply for number[][],number[][]", function () { + const value = [ + [2, 3], + [4, 5], + ]; + const factor = [ + [3, 4], + [5, 6], + ]; + const expected = [ + [2 * 3, 3 * 4], + [4 * 5, 5 * 6], + ]; + const actual = ArrayValues.deepMultiply(value, factor); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // deepAdd + + it("performs deepAdd for number,number", function () { + const value = 2; + const factor = 3; + const expected = 2 + 3; + const actual = ArrayValues.deepAdd(value, factor); + expect(actual).toEqual(expected); + }); + + it("performs deepAdd for number[],number[]", function () { + const value = [2, 3]; + const factor = [3, 4]; + const expected = [2 + 3, 3 + 4]; + const actual = ArrayValues.deepAdd(value, factor); + expect(actual).toEqual(expected); + }); + + it("performs deepAdd for number[][],number[][]", function () { + const value = [ + [2, 3], + [4, 5], + ]; + const factor = [ + [3, 4], + [5, 6], + ]; + const expected = [ + [2 + 3, 3 + 4], + [4 + 5, 5 + 6], + ]; + const actual = ArrayValues.deepAdd(value, factor); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // deepMin number,number + + it("performs deepMin for number,number", function () { + const a = 2; + const b = 3; + const expected = 2; + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMin for number[],number[]", function () { + const a = [5, 2]; + const b = [3, 4]; + const expected = [3, 2]; + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMin for number[][],number[][]", function () { + const a = [ + [5, 2], + [1, 7], + ]; + const b = [ + [3, 4], + [5, 6], + ]; + const expected = [ + [3, 2], + [1, 6], + ]; + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // deepMin number,bigint + + it("performs deepMin for number,bigint", function () { + const a = 2; + const b = BigInt(3); + const expected = 2; + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMin for number[],bigint[]", function () { + const a = [5, 2]; + const b = [BigInt(3), BigInt(4)]; + const expected = [BigInt(3), 2]; + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMin for number[][],bigint[][]", function () { + const a = [ + [5, 2], + [1, 7], + ]; + const b = [ + [BigInt(3), BigInt(4)], + [BigInt(5), BigInt(6)], + ]; + const expected = [ + [BigInt(3), 2], + [1, BigInt(6)], + ]; + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // deepMin bigint,number + + it("performs deepMin for bigint,number", function () { + const a = BigInt(2); + const b = 3; + const expected = BigInt(2); + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMin for bigint[],number[]", function () { + const a = [BigInt(2), BigInt(3)]; + const b = [3, 4]; + const expected = [BigInt(2), BigInt(3)]; + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMin for bigint[][],number[][]", function () { + const a = [ + [BigInt(5), BigInt(2)], + [BigInt(1), BigInt(7)], + ]; + const b = [ + [3, 4], + [5, 6], + ]; + const expected = [ + [3, BigInt(2)], + [BigInt(1), 6], + ]; + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // deepMin bigint,bigint + + it("performs deepMin for bigint,bigint", function () { + const a = BigInt(2); + const b = BigInt(3); + const expected = BigInt(2); + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMin for bigint[],bigint[]", function () { + const a = [BigInt(5), BigInt(2)]; + const b = [BigInt(3), BigInt(4)]; + const expected = [BigInt(3), BigInt(2)]; + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMin for bigint[][],bigint[][]", function () { + const a = [ + [BigInt(5), BigInt(2)], + [BigInt(1), BigInt(7)], + ]; + const b = [ + [BigInt(3), BigInt(4)], + [BigInt(5), BigInt(6)], + ]; + const expected = [ + [BigInt(3), BigInt(2)], + [BigInt(1), BigInt(6)], + ]; + const actual = ArrayValues.deepMin(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // deepMax number,number + + it("performs deepMax for number,number", function () { + const a = 2; + const b = 3; + const expected = 3; + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMax for number[],number[]", function () { + const a = [5, 2]; + const b = [3, 4]; + const expected = [5, 4]; + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMax for number[][],number[][]", function () { + const a = [ + [5, 2], + [1, 7], + ]; + const b = [ + [3, 4], + [5, 6], + ]; + const expected = [ + [5, 4], + [5, 7], + ]; + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // deepMax number,bigint + + it("performs deepMax for number,bigint", function () { + const a = 2; + const b = BigInt(3); + const expected = BigInt(3); + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMax for number[],bigint[]", function () { + const a = [5, 2]; + const b = [BigInt(3), BigInt(4)]; + const expected = [5, BigInt(4)]; + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMax for number[][],bigint[][]", function () { + const a = [ + [5, 2], + [1, 7], + ]; + const b = [ + [BigInt(3), BigInt(4)], + [BigInt(5), BigInt(6)], + ]; + const expected = [ + [5, BigInt(4)], + [BigInt(5), 7], + ]; + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // deepMax bigint,number + + it("performs deepMax for bigint,number", function () { + const a = BigInt(2); + const b = 3; + const expected = 3; + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMax for bigint[],number[]", function () { + const a = [BigInt(5), BigInt(2)]; + const b = [3, 4]; + const expected = [BigInt(5), 4]; + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMax for bigint[][],number[][]", function () { + const a = [ + [BigInt(5), BigInt(2)], + [BigInt(1), BigInt(7)], + ]; + const b = [ + [3, 4], + [5, 6], + ]; + const expected = [ + [BigInt(5), 4], + [5, BigInt(7)], + ]; + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // deepMax bigint,bigint + + it("performs deepMax for bigint,bigint", function () { + const a = BigInt(2); + const b = BigInt(3); + const expected = BigInt(3); + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMax for bigint[],bigint[]", function () { + const a = [BigInt(5), BigInt(2)]; + const b = [BigInt(3), BigInt(4)]; + const expected = [BigInt(5), BigInt(4)]; + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + it("performs deepMax for bigint[][],bigint[][]", function () { + const a = [ + [BigInt(5), BigInt(2)], + [BigInt(1), BigInt(7)], + ]; + const b = [ + [BigInt(3), BigInt(4)], + [BigInt(5), BigInt(6)], + ]; + const expected = [ + [BigInt(5), BigInt(4)], + [BigInt(5), BigInt(7)], + ]; + const actual = ArrayValues.deepMax(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // anyDeepLessThan number, number + + it("performs anyDeepLessThan for number,number", function () { + const a = 4; + const b = 5; + const expected = true; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepLessThan for number,number", function () { + const a = 6; + const b = 5; + const expected = false; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepLessThan for number[],number[]", function () { + const a = [4, 5]; + const b = [5, 5]; + const expected = true; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + it("performs anyDeepLessThan for number[],number[]", function () { + const a = [6, 5]; + const b = [5, 5]; + const expected = false; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // anyDeepLessThan bigint, number + + it("performs anyDeepLessThan for bigint,number", function () { + const a = BigInt(4); + const b = 5; + const expected = true; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepLessThan for bigint,number", function () { + const a = BigInt(6); + const b = 5; + const expected = false; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepLessThan for bigint[],number[]", function () { + const a = [BigInt(4), BigInt(5)]; + const b = [5, 5]; + const expected = true; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + it("performs anyDeepLessThan for bigint[],number[]", function () { + const a = [BigInt(6), BigInt(5)]; + const b = [5, 5]; + const expected = false; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // anyDeepLessThan number, bigint + + it("performs anyDeepLessThan for number,bigint", function () { + const a = 4; + const b = BigInt(5); + const expected = true; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepLessThan for number,bigint", function () { + const a = 6; + const b = BigInt(5); + const expected = false; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepLessThan for number[],bigint[]", function () { + const a = [4, 5]; + const b = [BigInt(5), BigInt(5)]; + const expected = true; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + it("performs anyDeepLessThan for number[],bigint[]", function () { + const a = [6, 5]; + const b = [BigInt(5), BigInt(5)]; + const expected = false; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // anyDeepLessThan bigint,bigint + + it("performs anyDeepLessThan for bigint,bigint", function () { + const a = BigInt(4); + const b = BigInt(5); + const expected = true; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepLessThan for bigint,bigint", function () { + const a = BigInt(6); + const b = BigInt(5); + const expected = false; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepLessThan for bigint[],bigint[]", function () { + const a = [BigInt(4), BigInt(5)]; + const b = [BigInt(5), BigInt(5)]; + const expected = true; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + it("performs anyDeepLessThan for bigint[],bigint[]", function () { + const a = [BigInt(6), BigInt(5)]; + const b = [BigInt(5), BigInt(5)]; + const expected = false; + const actual = ArrayValues.anyDeepLessThan(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // anyDeepGreaterThan number, number + + it("performs anyDeepGreaterThan for number,number", function () { + const a = 4; + const b = 5; + const expected = false; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepGreaterThan for number,number", function () { + const a = 6; + const b = 5; + const expected = true; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepGreaterThan for number[],number[]", function () { + const a = [4, 5]; + const b = [5, 5]; + const expected = false; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + it("performs anyDeepGreaterThan for number[],number[]", function () { + const a = [6, 5]; + const b = [5, 5]; + const expected = true; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // anyDeepGreaterThan bigint, number + + it("performs anyDeepGreaterThan for bigint,number", function () { + const a = BigInt(4); + const b = 5; + const expected = false; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepGreaterThan for bigint,number", function () { + const a = BigInt(6); + const b = 5; + const expected = true; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepGreaterThan for bigint[],number[]", function () { + const a = [BigInt(4), BigInt(5)]; + const b = [5, 5]; + const expected = false; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + it("performs anyDeepGreaterThan for bigint[],number[]", function () { + const a = [BigInt(6), BigInt(5)]; + const b = [5, 5]; + const expected = true; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // anyDeepGreaterThan number, bigint + + it("performs anyDeepGreaterThan for number,bigint", function () { + const a = 4; + const b = BigInt(5); + const expected = false; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepGreaterThan for number,bigint", function () { + const a = 6; + const b = BigInt(5); + const expected = true; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepGreaterThan for number[],bigint[]", function () { + const a = [4, 5]; + const b = [BigInt(5), BigInt(5)]; + const expected = false; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + it("performs anyDeepGreaterThan for number[],bigint[]", function () { + const a = [6, 5]; + const b = [BigInt(5), BigInt(5)]; + const expected = true; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + //========================================================================== + // anyDeepGreaterThan bigint,bigint + + it("performs anyDeepGreaterThan for bigint,bigint", function () { + const a = BigInt(4); + const b = BigInt(5); + const expected = false; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepGreaterThan for bigint,bigint", function () { + const a = BigInt(6); + const b = BigInt(5); + const expected = true; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + + it("performs anyDeepGreaterThan for bigint[],bigint[]", function () { + const a = [BigInt(4), BigInt(5)]; + const b = [BigInt(5), BigInt(5)]; + const expected = false; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); + it("performs anyDeepGreaterThan for bigint[],bigint[]", function () { + const a = [BigInt(6), BigInt(5)]; + const b = [BigInt(5), BigInt(5)]; + const expected = true; + const actual = ArrayValues.anyDeepGreaterThan(a, b); + expect(actual).toEqual(expected); + }); +}); diff --git a/specs/metadata/MetadataEntityModelBasicSpec.ts b/specs/metadata/MetadataEntityModelBasicSpec.ts new file mode 100644 index 00000000..c824f41e --- /dev/null +++ b/specs/metadata/MetadataEntityModelBasicSpec.ts @@ -0,0 +1,5546 @@ +import { genericEquals } from "./genericEquals"; +import { readJsonUnchecked } from "./readJsonUnchecked"; + +import { MetadataEntityModel } from "../../src/metadata/MetadataEntityModel"; +import { MetadataEntityModels } from "../../src/metadata/MetadataEntityModels"; + +describe("metadata/MetadataEntityModelBasic", function () { + const epsilon = 0.000001; + let metadataEntityModel: MetadataEntityModel; + + beforeEach(async function () { + const tileset = await readJsonUnchecked( + "specs/data/TilesetWithFullMetadata/tileset.json" + ); + metadataEntityModel = MetadataEntityModels.create( + tileset.schema, + tileset.metadata + ); + }); + + it("obtains the right value for example_STRING", function () { + const propertyName = "example_STRING"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = "An example string"; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_STRING_array", function () { + const propertyName = "example_variable_length_STRING_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = ["This", "is", "an", "example"]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_STRING_array", function () { + const propertyName = "example_fixed_length_STRING_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = ["This", "is", "an", "example", "string"]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_BOOLEAN", function () { + const propertyName = "example_BOOLEAN"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = true; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_BOOLEAN_array", function () { + const propertyName = "example_variable_length_BOOLEAN_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [true, false, true, false]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_BOOLEAN_array", function () { + const propertyName = "example_fixed_length_BOOLEAN_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [true, false, true, false, true]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_ENUM", function () { + const propertyName = "example_ENUM"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = "ExampleEnumValueB"; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_ENUM_array", function () { + const propertyName = "example_variable_length_ENUM_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + "ExampleEnumValueA", + "ExampleEnumValueB", + "ExampleEnumValueC", + "ExampleEnumValueA", + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_ENUM_array", function () { + const propertyName = "example_fixed_length_ENUM_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + "ExampleEnumValueA", + "ExampleEnumValueB", + "ExampleEnumValueC", + "ExampleEnumValueA", + "ExampleEnumValueB", + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT8_SCALAR", function () { + const propertyName = "example_INT8_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = -128; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT8_SCALAR_array", function () { + const propertyName = "example_variable_length_INT8_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-128, -43, 42, 127]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT8_SCALAR_array", function () { + const propertyName = "example_fixed_length_INT8_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-128, -64, 0, 63, 127]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT8_SCALAR", function () { + const propertyName = "example_normalized_INT8_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = -1; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT8_SCALAR_array", function () { + const propertyName = "example_variable_length_normalized_INT8_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.33858267716535434, 0.33070866141732286, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT8_SCALAR_array", function () { + const propertyName = "example_fixed_length_normalized_INT8_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.5039370078740157, 0, 0.49606299212598426, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT8_SCALAR", function () { + const propertyName = "example_UINT8_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = 255; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT8_SCALAR_array", function () { + const propertyName = "example_variable_length_UINT8_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 84, 170, 255]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT8_SCALAR_array", function () { + const propertyName = "example_fixed_length_UINT8_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 63, 127, 191, 255]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT8_SCALAR", function () { + const propertyName = "example_normalized_UINT8_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = 1; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT8_SCALAR_array", function () { + const propertyName = + "example_variable_length_normalized_UINT8_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.32941176470588235, 0.6666666666666666, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT8_SCALAR_array", function () { + const propertyName = "example_fixed_length_normalized_UINT8_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 0.24705882352941178, 0.4980392156862745, 0.7490196078431373, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT16_SCALAR", function () { + const propertyName = "example_INT16_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = -32768; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT16_SCALAR_array", function () { + const propertyName = "example_variable_length_INT16_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-32768, -10923, 10922, 32767]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT16_SCALAR_array", function () { + const propertyName = "example_fixed_length_INT16_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-32768, -16384, 0, 16383, 32767]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT16_SCALAR", function () { + const propertyName = "example_normalized_INT16_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = -1; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT16_SCALAR_array", function () { + const propertyName = + "example_variable_length_normalized_INT16_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.33335367900631735, 0.33332316049684135, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT16_SCALAR_array", function () { + const propertyName = "example_fixed_length_normalized_INT16_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.500015259254738, 0, 0.499984740745262, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT16_SCALAR", function () { + const propertyName = "example_UINT16_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = 65535; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT16_SCALAR_array", function () { + const propertyName = "example_variable_length_UINT16_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 21844, 43690, 65535]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT16_SCALAR_array", function () { + const propertyName = "example_fixed_length_UINT16_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 16383, 32767, 49151, 65535]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT16_SCALAR", function () { + const propertyName = "example_normalized_UINT16_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = 1; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT16_SCALAR_array", function () { + const propertyName = + "example_variable_length_normalized_UINT16_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.3333180743114366, 0.6666666666666666, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT16_SCALAR_array", function () { + const propertyName = "example_fixed_length_normalized_UINT16_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 0.24998855573357748, 0.49999237048905165, 0.7499961852445258, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT32_SCALAR", function () { + const propertyName = "example_INT32_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = -2147483648; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT32_SCALAR_array", function () { + const propertyName = "example_variable_length_INT32_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-2147483648, -715827883, 715827882, 2147483647]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT32_SCALAR_array", function () { + const propertyName = "example_fixed_length_INT32_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-2147483648, -1073741824, 0, 1073741823, 2147483647]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT32_SCALAR", function () { + const propertyName = "example_normalized_INT32_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = -1; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT32_SCALAR_array", function () { + const propertyName = + "example_variable_length_normalized_INT32_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333336437742, 0.3333333331781129, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT32_SCALAR_array", function () { + const propertyName = "example_fixed_length_normalized_INT32_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.5000000002328306, 0, 0.49999999976716936, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT32_SCALAR", function () { + const propertyName = "example_UINT32_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = 4294967295; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT32_SCALAR_array", function () { + const propertyName = "example_variable_length_UINT32_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 1431655764, 2863311530, 4294967295]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT32_SCALAR_array", function () { + const propertyName = "example_fixed_length_UINT32_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 1073741823, 2147483647, 3221225471, 4294967295]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT32_SCALAR", function () { + const propertyName = "example_normalized_UINT32_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = 1; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT32_SCALAR_array", function () { + const propertyName = + "example_variable_length_normalized_UINT32_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.33333333310050267, 0.6666666666666666, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT32_SCALAR_array", function () { + const propertyName = "example_fixed_length_normalized_UINT32_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 0.24999999982537702, 0.4999999998835847, 0.7499999999417923, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT64_SCALAR", function () { + const propertyName = "example_INT64_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = -9223372036854776000n; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT64_SCALAR_array", function () { + const propertyName = "example_variable_length_INT64_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT64_SCALAR_array", function () { + const propertyName = "example_fixed_length_INT64_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -9223372036854776000n, + -4611686018427388000n, + 0, + 4611686018427388000n, + 9223372036854776000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT64_SCALAR", function () { + const propertyName = "example_normalized_INT64_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = -1; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT64_SCALAR_array", function () { + const propertyName = + "example_variable_length_normalized_INT64_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333334, 0.3333333334, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT64_SCALAR_array", function () { + const propertyName = "example_fixed_length_normalized_INT64_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.5, 0, 0.5, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT64_SCALAR", function () { + const propertyName = "example_UINT64_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = 18446744073709552000n; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT64_SCALAR_array", function () { + const propertyName = "example_variable_length_UINT64_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, + 6148914690621625000n, + 12297829383087925000n, + 18446744073709552000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT64_SCALAR_array", function () { + const propertyName = "example_fixed_length_UINT64_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, + 4611686018427388000n, + 9223372036854776000n, + 13835058055282164000n, + 18446744073709552000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT64_SCALAR", function () { + const propertyName = "example_normalized_UINT64_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = 1; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT64_SCALAR_array", function () { + const propertyName = + "example_variable_length_normalized_UINT64_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.3333333333, 0.6666666667, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT64_SCALAR_array", function () { + const propertyName = "example_fixed_length_normalized_UINT64_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.25, 0.5, 0.75, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT32_SCALAR", function () { + const propertyName = "example_FLOAT32_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = 1.2; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT32_SCALAR_array", function () { + const propertyName = "example_variable_length_FLOAT32_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333334, 0.3333333334, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT32_SCALAR_array", function () { + const propertyName = "example_fixed_length_FLOAT32_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.5, 0, 0.5, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT64_SCALAR", function () { + const propertyName = "example_FLOAT64_SCALAR"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = 12.34; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT64_SCALAR_array", function () { + const propertyName = "example_variable_length_FLOAT64_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333334, 0.3333333334, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT64_SCALAR_array", function () { + const propertyName = "example_fixed_length_FLOAT64_SCALAR_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.5, 0, 0.5, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT8_VEC2", function () { + const propertyName = "example_INT8_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-128, 127]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT8_VEC2_array", function () { + const propertyName = "example_variable_length_INT8_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-128, 127], + [-128, 127], + [-128, 127], + [-128, 127], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT8_VEC2_array", function () { + const propertyName = "example_fixed_length_INT8_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-128, 127], + [-128, 127], + [-128, 127], + [-128, 127], + [-128, 127], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT8_VEC2", function () { + const propertyName = "example_normalized_INT8_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT8_VEC2_array", function () { + const propertyName = "example_variable_length_normalized_INT8_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT8_VEC2_array", function () { + const propertyName = "example_fixed_length_normalized_INT8_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT8_VEC2", function () { + const propertyName = "example_UINT8_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 255]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT8_VEC2_array", function () { + const propertyName = "example_variable_length_UINT8_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 255], + [0, 255], + [0, 255], + [0, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT8_VEC2_array", function () { + const propertyName = "example_fixed_length_UINT8_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 255], + [0, 255], + [0, 255], + [0, 255], + [0, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT8_VEC2", function () { + const propertyName = "example_normalized_UINT8_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT8_VEC2_array", function () { + const propertyName = "example_variable_length_normalized_UINT8_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1], + [0, 1], + [0, 1], + [0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT8_VEC2_array", function () { + const propertyName = "example_fixed_length_normalized_UINT8_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1], + [0, 1], + [0, 1], + [0, 1], + [0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT16_VEC2", function () { + const propertyName = "example_INT16_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-32768, 32767]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT16_VEC2_array", function () { + const propertyName = "example_variable_length_INT16_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-32768, 32767], + [-32768, 32767], + [-32768, 32767], + [-32768, 32767], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT16_VEC2_array", function () { + const propertyName = "example_fixed_length_INT16_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-32768, 32767], + [-32768, 32767], + [-32768, 32767], + [-32768, 32767], + [-32768, 32767], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT16_VEC2", function () { + const propertyName = "example_normalized_INT16_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT16_VEC2_array", function () { + const propertyName = "example_variable_length_normalized_INT16_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT16_VEC2_array", function () { + const propertyName = "example_fixed_length_normalized_INT16_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT16_VEC2", function () { + const propertyName = "example_UINT16_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 65535]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT16_VEC2_array", function () { + const propertyName = "example_variable_length_UINT16_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 65535], + [0, 65535], + [0, 65535], + [0, 65535], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT16_VEC2_array", function () { + const propertyName = "example_fixed_length_UINT16_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 65535], + [0, 65535], + [0, 65535], + [0, 65535], + [0, 65535], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT16_VEC2", function () { + const propertyName = "example_normalized_UINT16_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT16_VEC2_array", function () { + const propertyName = "example_variable_length_normalized_UINT16_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1], + [0, 1], + [0, 1], + [0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT16_VEC2_array", function () { + const propertyName = "example_fixed_length_normalized_UINT16_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1], + [0, 1], + [0, 1], + [0, 1], + [0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT32_VEC2", function () { + const propertyName = "example_INT32_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-2147483648, 2147483647]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT32_VEC2_array", function () { + const propertyName = "example_variable_length_INT32_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-2147483648, 2147483647], + [-2147483648, 2147483647], + [-2147483648, 2147483647], + [-2147483648, 2147483647], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT32_VEC2_array", function () { + const propertyName = "example_fixed_length_INT32_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-2147483648, 2147483647], + [-2147483648, 2147483647], + [-2147483648, 2147483647], + [-2147483648, 2147483647], + [-2147483648, 2147483647], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT32_VEC2", function () { + const propertyName = "example_normalized_INT32_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT32_VEC2_array", function () { + const propertyName = "example_variable_length_normalized_INT32_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT32_VEC2_array", function () { + const propertyName = "example_fixed_length_normalized_INT32_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT32_VEC2", function () { + const propertyName = "example_UINT32_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 4294967295]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT32_VEC2_array", function () { + const propertyName = "example_variable_length_UINT32_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 4294967295], + [0, 4294967295], + [0, 4294967295], + [0, 4294967295], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT32_VEC2_array", function () { + const propertyName = "example_fixed_length_UINT32_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 4294967295], + [0, 4294967295], + [0, 4294967295], + [0, 4294967295], + [0, 4294967295], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT32_VEC2", function () { + const propertyName = "example_normalized_UINT32_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT32_VEC2_array", function () { + const propertyName = "example_variable_length_normalized_UINT32_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1], + [0, 1], + [0, 1], + [0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT32_VEC2_array", function () { + const propertyName = "example_fixed_length_normalized_UINT32_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1], + [0, 1], + [0, 1], + [0, 1], + [0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT64_VEC2", function () { + const propertyName = "example_INT64_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-9223372036854776000n, 9223372036854776000n]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT64_VEC2_array", function () { + const propertyName = "example_variable_length_INT64_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-9223372036854776000n, 9223372036854776000n], + [-9223372036854776000n, 9223372036854776000n], + [-9223372036854776000n, 9223372036854776000n], + [-9223372036854776000n, 9223372036854776000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT64_VEC2_array", function () { + const propertyName = "example_fixed_length_INT64_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-9223372036854776000n, 9223372036854776000n], + [-9223372036854776000n, 9223372036854776000n], + [-9223372036854776000n, 9223372036854776000n], + [-9223372036854776000n, 9223372036854776000n], + [-9223372036854776000n, 9223372036854776000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT64_VEC2", function () { + const propertyName = "example_normalized_INT64_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT64_VEC2_array", function () { + const propertyName = "example_variable_length_normalized_INT64_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT64_VEC2_array", function () { + const propertyName = "example_fixed_length_normalized_INT64_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT64_VEC2", function () { + const propertyName = "example_UINT64_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 18446744073709552000n]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT64_VEC2_array", function () { + const propertyName = "example_variable_length_UINT64_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 18446744073709552000n], + [0, 18446744073709552000n], + [0, 18446744073709552000n], + [0, 18446744073709552000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT64_VEC2_array", function () { + const propertyName = "example_fixed_length_UINT64_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 18446744073709552000n], + [0, 18446744073709552000n], + [0, 18446744073709552000n], + [0, 18446744073709552000n], + [0, 18446744073709552000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT64_VEC2", function () { + const propertyName = "example_normalized_UINT64_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT64_VEC2_array", function () { + const propertyName = "example_variable_length_normalized_UINT64_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1], + [0, 1], + [0, 1], + [0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT64_VEC2_array", function () { + const propertyName = "example_fixed_length_normalized_UINT64_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1], + [0, 1], + [0, 1], + [0, 1], + [0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT32_VEC2", function () { + const propertyName = "example_FLOAT32_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT32_VEC2_array", function () { + const propertyName = "example_variable_length_FLOAT32_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT32_VEC2_array", function () { + const propertyName = "example_fixed_length_FLOAT32_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT64_VEC2", function () { + const propertyName = "example_FLOAT64_VEC2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT64_VEC2_array", function () { + const propertyName = "example_variable_length_FLOAT64_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT64_VEC2_array", function () { + const propertyName = "example_fixed_length_FLOAT64_VEC2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + [-1, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT8_VEC3", function () { + const propertyName = "example_INT8_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-128, 0, 127]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT8_VEC3_array", function () { + const propertyName = "example_variable_length_INT8_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-128, 0, 127], + [-128, 0, 127], + [-128, 0, 127], + [-128, 0, 127], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT8_VEC3_array", function () { + const propertyName = "example_fixed_length_INT8_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-128, 0, 127], + [-128, 0, 127], + [-128, 0, 127], + [-128, 0, 127], + [-128, 0, 127], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT8_VEC3", function () { + const propertyName = "example_normalized_INT8_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 0, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT8_VEC3_array", function () { + const propertyName = "example_variable_length_normalized_INT8_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT8_VEC3_array", function () { + const propertyName = "example_fixed_length_normalized_INT8_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT8_VEC3", function () { + const propertyName = "example_UINT8_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 127, 255]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT8_VEC3_array", function () { + const propertyName = "example_variable_length_UINT8_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 127, 255], + [0, 127, 255], + [0, 127, 255], + [0, 127, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT8_VEC3_array", function () { + const propertyName = "example_fixed_length_UINT8_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 127, 255], + [0, 127, 255], + [0, 127, 255], + [0, 127, 255], + [0, 127, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT8_VEC3", function () { + const propertyName = "example_normalized_UINT8_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.4980392156862745, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT8_VEC3_array", function () { + const propertyName = "example_variable_length_normalized_UINT8_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.4980392156862745, 1], + [0, 0.4980392156862745, 1], + [0, 0.4980392156862745, 1], + [0, 0.4980392156862745, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT8_VEC3_array", function () { + const propertyName = "example_fixed_length_normalized_UINT8_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.4980392156862745, 1], + [0, 0.4980392156862745, 1], + [0, 0.4980392156862745, 1], + [0, 0.4980392156862745, 1], + [0, 0.4980392156862745, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT16_VEC3", function () { + const propertyName = "example_INT16_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-32768, 0, 32767]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT16_VEC3_array", function () { + const propertyName = "example_variable_length_INT16_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-32768, 0, 32767], + [-32768, 0, 32767], + [-32768, 0, 32767], + [-32768, 0, 32767], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT16_VEC3_array", function () { + const propertyName = "example_fixed_length_INT16_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-32768, 0, 32767], + [-32768, 0, 32767], + [-32768, 0, 32767], + [-32768, 0, 32767], + [-32768, 0, 32767], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT16_VEC3", function () { + const propertyName = "example_normalized_INT16_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 0, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT16_VEC3_array", function () { + const propertyName = "example_variable_length_normalized_INT16_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT16_VEC3_array", function () { + const propertyName = "example_fixed_length_normalized_INT16_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT16_VEC3", function () { + const propertyName = "example_UINT16_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 32767, 65535]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT16_VEC3_array", function () { + const propertyName = "example_variable_length_UINT16_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 32767, 65535], + [0, 32767, 65535], + [0, 32767, 65535], + [0, 32767, 65535], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT16_VEC3_array", function () { + const propertyName = "example_fixed_length_UINT16_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 32767, 65535], + [0, 32767, 65535], + [0, 32767, 65535], + [0, 32767, 65535], + [0, 32767, 65535], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT16_VEC3", function () { + const propertyName = "example_normalized_UINT16_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.49999237048905165, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT16_VEC3_array", function () { + const propertyName = "example_variable_length_normalized_UINT16_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.49999237048905165, 1], + [0, 0.49999237048905165, 1], + [0, 0.49999237048905165, 1], + [0, 0.49999237048905165, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT16_VEC3_array", function () { + const propertyName = "example_fixed_length_normalized_UINT16_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.49999237048905165, 1], + [0, 0.49999237048905165, 1], + [0, 0.49999237048905165, 1], + [0, 0.49999237048905165, 1], + [0, 0.49999237048905165, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT32_VEC3", function () { + const propertyName = "example_INT32_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-2147483648, 0, 2147483647]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT32_VEC3_array", function () { + const propertyName = "example_variable_length_INT32_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-2147483648, 0, 2147483647], + [-2147483648, 0, 2147483647], + [-2147483648, 0, 2147483647], + [-2147483648, 0, 2147483647], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT32_VEC3_array", function () { + const propertyName = "example_fixed_length_INT32_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-2147483648, 0, 2147483647], + [-2147483648, 0, 2147483647], + [-2147483648, 0, 2147483647], + [-2147483648, 0, 2147483647], + [-2147483648, 0, 2147483647], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT32_VEC3", function () { + const propertyName = "example_normalized_INT32_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 0, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT32_VEC3_array", function () { + const propertyName = "example_variable_length_normalized_INT32_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT32_VEC3_array", function () { + const propertyName = "example_fixed_length_normalized_INT32_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT32_VEC3", function () { + const propertyName = "example_UINT32_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 2147483647, 4294967295]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT32_VEC3_array", function () { + const propertyName = "example_variable_length_UINT32_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 2147483647, 4294967295], + [0, 2147483647, 4294967295], + [0, 2147483647, 4294967295], + [0, 2147483647, 4294967295], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT32_VEC3_array", function () { + const propertyName = "example_fixed_length_UINT32_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 2147483647, 4294967295], + [0, 2147483647, 4294967295], + [0, 2147483647, 4294967295], + [0, 2147483647, 4294967295], + [0, 2147483647, 4294967295], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT32_VEC3", function () { + const propertyName = "example_normalized_UINT32_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.4999999998835847, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT32_VEC3_array", function () { + const propertyName = "example_variable_length_normalized_UINT32_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.4999999998835847, 1], + [0, 0.4999999998835847, 1], + [0, 0.4999999998835847, 1], + [0, 0.4999999998835847, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT32_VEC3_array", function () { + const propertyName = "example_fixed_length_normalized_UINT32_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.4999999998835847, 1], + [0, 0.4999999998835847, 1], + [0, 0.4999999998835847, 1], + [0, 0.4999999998835847, 1], + [0, 0.4999999998835847, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT64_VEC3", function () { + const propertyName = "example_INT64_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-9223372036854776000n, 0, 9223372036854776000n]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT64_VEC3_array", function () { + const propertyName = "example_variable_length_INT64_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-9223372036854776000n, 0, 9223372036854776000n], + [-9223372036854776000n, 0, 9223372036854776000n], + [-9223372036854776000n, 0, 9223372036854776000n], + [-9223372036854776000n, 0, 9223372036854776000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT64_VEC3_array", function () { + const propertyName = "example_fixed_length_INT64_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-9223372036854776000n, 0, 9223372036854776000n], + [-9223372036854776000n, 0, 9223372036854776000n], + [-9223372036854776000n, 0, 9223372036854776000n], + [-9223372036854776000n, 0, 9223372036854776000n], + [-9223372036854776000n, 0, 9223372036854776000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT64_VEC3", function () { + const propertyName = "example_normalized_INT64_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 0, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT64_VEC3_array", function () { + const propertyName = "example_variable_length_normalized_INT64_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT64_VEC3_array", function () { + const propertyName = "example_fixed_length_normalized_INT64_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT64_VEC3", function () { + const propertyName = "example_UINT64_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 9223372036854776000n, 18446744073709552000n]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT64_VEC3_array", function () { + const propertyName = "example_variable_length_UINT64_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 9223372036854776000n, 18446744073709552000n], + [0, 9223372036854776000n, 18446744073709552000n], + [0, 9223372036854776000n, 18446744073709552000n], + [0, 9223372036854776000n, 18446744073709552000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT64_VEC3_array", function () { + const propertyName = "example_fixed_length_UINT64_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 9223372036854776000n, 18446744073709552000n], + [0, 9223372036854776000n, 18446744073709552000n], + [0, 9223372036854776000n, 18446744073709552000n], + [0, 9223372036854776000n, 18446744073709552000n], + [0, 9223372036854776000n, 18446744073709552000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT64_VEC3", function () { + const propertyName = "example_normalized_UINT64_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.5, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT64_VEC3_array", function () { + const propertyName = "example_variable_length_normalized_UINT64_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.5, 1], + [0, 0.5, 1], + [0, 0.5, 1], + [0, 0.5, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT64_VEC3_array", function () { + const propertyName = "example_fixed_length_normalized_UINT64_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.5, 1], + [0, 0.5, 1], + [0, 0.5, 1], + [0, 0.5, 1], + [0, 0.5, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT32_VEC3", function () { + const propertyName = "example_FLOAT32_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 0, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT32_VEC3_array", function () { + const propertyName = "example_variable_length_FLOAT32_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT32_VEC3_array", function () { + const propertyName = "example_fixed_length_FLOAT32_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT64_VEC3", function () { + const propertyName = "example_FLOAT64_VEC3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, 0, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT64_VEC3_array", function () { + const propertyName = "example_variable_length_FLOAT64_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT64_VEC3_array", function () { + const propertyName = "example_fixed_length_FLOAT64_VEC3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + [-1, 0, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT8_VEC4", function () { + const propertyName = "example_INT8_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-128, -43, 42, 127]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT8_VEC4_array", function () { + const propertyName = "example_variable_length_INT8_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-128, -43, 42, 127], + [-128, -43, 42, 127], + [-128, -43, 42, 127], + [-128, -43, 42, 127], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT8_VEC4_array", function () { + const propertyName = "example_fixed_length_INT8_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-128, -43, 42, 127], + [-128, -43, 42, 127], + [-128, -43, 42, 127], + [-128, -43, 42, 127], + [-128, -43, 42, 127], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT8_VEC4", function () { + const propertyName = "example_normalized_INT8_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.33858267716535434, 0.33070866141732286, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT8_VEC4_array", function () { + const propertyName = "example_variable_length_normalized_INT8_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT8_VEC4_array", function () { + const propertyName = "example_fixed_length_normalized_INT8_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT8_VEC4", function () { + const propertyName = "example_UINT8_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 84, 170, 255]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT8_VEC4_array", function () { + const propertyName = "example_variable_length_UINT8_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 84, 170, 255], + [0, 84, 170, 255], + [0, 84, 170, 255], + [0, 84, 170, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT8_VEC4_array", function () { + const propertyName = "example_fixed_length_UINT8_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 84, 170, 255], + [0, 84, 170, 255], + [0, 84, 170, 255], + [0, 84, 170, 255], + [0, 84, 170, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT8_VEC4", function () { + const propertyName = "example_normalized_UINT8_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.32941176470588235, 0.6666666666666666, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT8_VEC4_array", function () { + const propertyName = "example_variable_length_normalized_UINT8_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT8_VEC4_array", function () { + const propertyName = "example_fixed_length_normalized_UINT8_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT16_VEC4", function () { + const propertyName = "example_INT16_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-32768, -10923, 10922, 32767]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT16_VEC4_array", function () { + const propertyName = "example_variable_length_INT16_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT16_VEC4_array", function () { + const propertyName = "example_fixed_length_INT16_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT16_VEC4", function () { + const propertyName = "example_normalized_INT16_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.33335367900631735, 0.33332316049684135, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT16_VEC4_array", function () { + const propertyName = "example_variable_length_normalized_INT16_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT16_VEC4_array", function () { + const propertyName = "example_fixed_length_normalized_INT16_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT16_VEC4", function () { + const propertyName = "example_UINT16_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 21844, 43690, 65535]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT16_VEC4_array", function () { + const propertyName = "example_variable_length_UINT16_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT16_VEC4_array", function () { + const propertyName = "example_fixed_length_UINT16_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT16_VEC4", function () { + const propertyName = "example_normalized_UINT16_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.3333180743114366, 0.6666666666666666, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT16_VEC4_array", function () { + const propertyName = "example_variable_length_normalized_UINT16_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT16_VEC4_array", function () { + const propertyName = "example_fixed_length_normalized_UINT16_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT32_VEC4", function () { + const propertyName = "example_INT32_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-2147483648, -715827883, 715827882, 2147483647]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT32_VEC4_array", function () { + const propertyName = "example_variable_length_INT32_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT32_VEC4_array", function () { + const propertyName = "example_fixed_length_INT32_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT32_VEC4", function () { + const propertyName = "example_normalized_INT32_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333336437742, 0.3333333331781129, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT32_VEC4_array", function () { + const propertyName = "example_variable_length_normalized_INT32_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT32_VEC4_array", function () { + const propertyName = "example_fixed_length_normalized_INT32_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT32_VEC4", function () { + const propertyName = "example_UINT32_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 1431655764, 2863311530, 4294967295]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT32_VEC4_array", function () { + const propertyName = "example_variable_length_UINT32_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT32_VEC4_array", function () { + const propertyName = "example_fixed_length_UINT32_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT32_VEC4", function () { + const propertyName = "example_normalized_UINT32_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.33333333310050267, 0.6666666666666666, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT32_VEC4_array", function () { + const propertyName = "example_variable_length_normalized_UINT32_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT32_VEC4_array", function () { + const propertyName = "example_fixed_length_normalized_UINT32_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT64_VEC4", function () { + const propertyName = "example_INT64_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT64_VEC4_array", function () { + const propertyName = "example_variable_length_INT64_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT64_VEC4_array", function () { + const propertyName = "example_fixed_length_INT64_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT64_VEC4", function () { + const propertyName = "example_normalized_INT64_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333334, 0.3333333334, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT64_VEC4_array", function () { + const propertyName = "example_variable_length_normalized_INT64_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT64_VEC4_array", function () { + const propertyName = "example_fixed_length_normalized_INT64_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT64_VEC4", function () { + const propertyName = "example_UINT64_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, + 6148914690621625000n, + 12297829383087925000n, + 18446744073709552000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT64_VEC4_array", function () { + const propertyName = "example_variable_length_UINT64_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT64_VEC4_array", function () { + const propertyName = "example_fixed_length_UINT64_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT64_VEC4", function () { + const propertyName = "example_normalized_UINT64_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.3333333333, 0.6666666667, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT64_VEC4_array", function () { + const propertyName = "example_variable_length_normalized_UINT64_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT64_VEC4_array", function () { + const propertyName = "example_fixed_length_normalized_UINT64_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT32_VEC4", function () { + const propertyName = "example_FLOAT32_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333334, 0.3333333334, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT32_VEC4_array", function () { + const propertyName = "example_variable_length_FLOAT32_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT32_VEC4_array", function () { + const propertyName = "example_fixed_length_FLOAT32_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT64_VEC4", function () { + const propertyName = "example_FLOAT64_VEC4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333334, 0.3333333334, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT64_VEC4_array", function () { + const propertyName = "example_variable_length_FLOAT64_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT64_VEC4_array", function () { + const propertyName = "example_fixed_length_FLOAT64_VEC4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT8_MAT2", function () { + const propertyName = "example_INT8_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-128, -43, 42, 127]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT8_MAT2_array", function () { + const propertyName = "example_variable_length_INT8_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-128, -43, 42, 127], + [-128, -43, 42, 127], + [-128, -43, 42, 127], + [-128, -43, 42, 127], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT8_MAT2_array", function () { + const propertyName = "example_fixed_length_INT8_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-128, -43, 42, 127], + [-128, -43, 42, 127], + [-128, -43, 42, 127], + [-128, -43, 42, 127], + [-128, -43, 42, 127], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT8_MAT2", function () { + const propertyName = "example_normalized_INT8_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.33858267716535434, 0.33070866141732286, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT8_MAT2_array", function () { + const propertyName = "example_variable_length_normalized_INT8_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT8_MAT2_array", function () { + const propertyName = "example_fixed_length_normalized_INT8_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + [-1, -0.33858267716535434, 0.33070866141732286, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT8_MAT2", function () { + const propertyName = "example_UINT8_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 84, 170, 255]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT8_MAT2_array", function () { + const propertyName = "example_variable_length_UINT8_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 84, 170, 255], + [0, 84, 170, 255], + [0, 84, 170, 255], + [0, 84, 170, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT8_MAT2_array", function () { + const propertyName = "example_fixed_length_UINT8_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 84, 170, 255], + [0, 84, 170, 255], + [0, 84, 170, 255], + [0, 84, 170, 255], + [0, 84, 170, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT8_MAT2", function () { + const propertyName = "example_normalized_UINT8_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.32941176470588235, 0.6666666666666666, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT8_MAT2_array", function () { + const propertyName = "example_variable_length_normalized_UINT8_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT8_MAT2_array", function () { + const propertyName = "example_fixed_length_normalized_UINT8_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + [0, 0.32941176470588235, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT16_MAT2", function () { + const propertyName = "example_INT16_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-32768, -10923, 10922, 32767]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT16_MAT2_array", function () { + const propertyName = "example_variable_length_INT16_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT16_MAT2_array", function () { + const propertyName = "example_fixed_length_INT16_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + [-32768, -10923, 10922, 32767], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT16_MAT2", function () { + const propertyName = "example_normalized_INT16_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.33335367900631735, 0.33332316049684135, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT16_MAT2_array", function () { + const propertyName = "example_variable_length_normalized_INT16_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT16_MAT2_array", function () { + const propertyName = "example_fixed_length_normalized_INT16_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + [-1, -0.33335367900631735, 0.33332316049684135, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT16_MAT2", function () { + const propertyName = "example_UINT16_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 21844, 43690, 65535]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT16_MAT2_array", function () { + const propertyName = "example_variable_length_UINT16_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT16_MAT2_array", function () { + const propertyName = "example_fixed_length_UINT16_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + [0, 21844, 43690, 65535], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT16_MAT2", function () { + const propertyName = "example_normalized_UINT16_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.3333180743114366, 0.6666666666666666, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT16_MAT2_array", function () { + const propertyName = "example_variable_length_normalized_UINT16_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT16_MAT2_array", function () { + const propertyName = "example_fixed_length_normalized_UINT16_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + [0, 0.3333180743114366, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT32_MAT2", function () { + const propertyName = "example_INT32_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-2147483648, -715827883, 715827882, 2147483647]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT32_MAT2_array", function () { + const propertyName = "example_variable_length_INT32_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT32_MAT2_array", function () { + const propertyName = "example_fixed_length_INT32_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + [-2147483648, -715827883, 715827882, 2147483647], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT32_MAT2", function () { + const propertyName = "example_normalized_INT32_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333336437742, 0.3333333331781129, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT32_MAT2_array", function () { + const propertyName = "example_variable_length_normalized_INT32_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT32_MAT2_array", function () { + const propertyName = "example_fixed_length_normalized_INT32_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + [-1, -0.3333333336437742, 0.3333333331781129, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT32_MAT2", function () { + const propertyName = "example_UINT32_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 1431655764, 2863311530, 4294967295]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT32_MAT2_array", function () { + const propertyName = "example_variable_length_UINT32_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT32_MAT2_array", function () { + const propertyName = "example_fixed_length_UINT32_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + [0, 1431655764, 2863311530, 4294967295], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT32_MAT2", function () { + const propertyName = "example_normalized_UINT32_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.33333333310050267, 0.6666666666666666, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT32_MAT2_array", function () { + const propertyName = "example_variable_length_normalized_UINT32_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT32_MAT2_array", function () { + const propertyName = "example_fixed_length_normalized_UINT32_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + [0, 0.33333333310050267, 0.6666666666666666, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT64_MAT2", function () { + const propertyName = "example_INT64_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT64_MAT2_array", function () { + const propertyName = "example_variable_length_INT64_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT64_MAT2_array", function () { + const propertyName = "example_fixed_length_INT64_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -3074457346233150000n, + 3074457346233150000n, + 9223372036854776000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT64_MAT2", function () { + const propertyName = "example_normalized_INT64_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333334, 0.3333333334, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT64_MAT2_array", function () { + const propertyName = "example_variable_length_normalized_INT64_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT64_MAT2_array", function () { + const propertyName = "example_fixed_length_normalized_INT64_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT64_MAT2", function () { + const propertyName = "example_UINT64_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, + 6148914690621625000n, + 12297829383087925000n, + 18446744073709552000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT64_MAT2_array", function () { + const propertyName = "example_variable_length_UINT64_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT64_MAT2_array", function () { + const propertyName = "example_fixed_length_UINT64_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + [0, 6148914690621625000n, 12297829383087925000n, 18446744073709552000n], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT64_MAT2", function () { + const propertyName = "example_normalized_UINT64_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.3333333333, 0.6666666667, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT64_MAT2_array", function () { + const propertyName = "example_variable_length_normalized_UINT64_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT64_MAT2_array", function () { + const propertyName = "example_fixed_length_normalized_UINT64_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + [0, 0.3333333333, 0.6666666667, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT32_MAT2", function () { + const propertyName = "example_FLOAT32_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333334, 0.3333333334, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT32_MAT2_array", function () { + const propertyName = "example_variable_length_FLOAT32_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT32_MAT2_array", function () { + const propertyName = "example_fixed_length_FLOAT32_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT64_MAT2", function () { + const propertyName = "example_FLOAT64_MAT2"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.3333333334, 0.3333333334, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT64_MAT2_array", function () { + const propertyName = "example_variable_length_FLOAT64_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT64_MAT2_array", function () { + const propertyName = "example_fixed_length_FLOAT64_MAT2_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + [-1, -0.3333333334, 0.3333333334, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT8_MAT3", function () { + const propertyName = "example_INT8_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-128, -96, -64, -32, 0, 31, 63, 95, 127]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT8_MAT3_array", function () { + const propertyName = "example_variable_length_INT8_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-128, -96, -64, -32, 0, 31, 63, 95, 127], + [-128, -96, -64, -32, 0, 31, 63, 95, 127], + [-128, -96, -64, -32, 0, 31, 63, 95, 127], + [-128, -96, -64, -32, 0, 31, 63, 95, 127], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT8_MAT3_array", function () { + const propertyName = "example_fixed_length_INT8_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-128, -96, -64, -32, 0, 31, 63, 95, 127], + [-128, -96, -64, -32, 0, 31, 63, 95, 127], + [-128, -96, -64, -32, 0, 31, 63, 95, 127], + [-128, -96, -64, -32, 0, 31, 63, 95, 127], + [-128, -96, -64, -32, 0, 31, 63, 95, 127], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT8_MAT3", function () { + const propertyName = "example_normalized_INT8_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -1, -0.7559055118110236, -0.5039370078740157, -0.25196850393700787, 0, + 0.2440944881889764, 0.49606299212598426, 0.7480314960629921, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT8_MAT3_array", function () { + const propertyName = "example_variable_length_normalized_INT8_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.7559055118110236, -0.5039370078740157, -0.25196850393700787, 0, + 0.2440944881889764, 0.49606299212598426, 0.7480314960629921, 1, + ], + [ + -1, -0.7559055118110236, -0.5039370078740157, -0.25196850393700787, 0, + 0.2440944881889764, 0.49606299212598426, 0.7480314960629921, 1, + ], + [ + -1, -0.7559055118110236, -0.5039370078740157, -0.25196850393700787, 0, + 0.2440944881889764, 0.49606299212598426, 0.7480314960629921, 1, + ], + [ + -1, -0.7559055118110236, -0.5039370078740157, -0.25196850393700787, 0, + 0.2440944881889764, 0.49606299212598426, 0.7480314960629921, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT8_MAT3_array", function () { + const propertyName = "example_fixed_length_normalized_INT8_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.7559055118110236, -0.5039370078740157, -0.25196850393700787, 0, + 0.2440944881889764, 0.49606299212598426, 0.7480314960629921, 1, + ], + [ + -1, -0.7559055118110236, -0.5039370078740157, -0.25196850393700787, 0, + 0.2440944881889764, 0.49606299212598426, 0.7480314960629921, 1, + ], + [ + -1, -0.7559055118110236, -0.5039370078740157, -0.25196850393700787, 0, + 0.2440944881889764, 0.49606299212598426, 0.7480314960629921, 1, + ], + [ + -1, -0.7559055118110236, -0.5039370078740157, -0.25196850393700787, 0, + 0.2440944881889764, 0.49606299212598426, 0.7480314960629921, 1, + ], + [ + -1, -0.7559055118110236, -0.5039370078740157, -0.25196850393700787, 0, + 0.2440944881889764, 0.49606299212598426, 0.7480314960629921, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT8_MAT3", function () { + const propertyName = "example_UINT8_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 31, 63, 95, 127, 159, 191, 223, 255]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT8_MAT3_array", function () { + const propertyName = "example_variable_length_UINT8_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 31, 63, 95, 127, 159, 191, 223, 255], + [0, 31, 63, 95, 127, 159, 191, 223, 255], + [0, 31, 63, 95, 127, 159, 191, 223, 255], + [0, 31, 63, 95, 127, 159, 191, 223, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT8_MAT3_array", function () { + const propertyName = "example_fixed_length_UINT8_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 31, 63, 95, 127, 159, 191, 223, 255], + [0, 31, 63, 95, 127, 159, 191, 223, 255], + [0, 31, 63, 95, 127, 159, 191, 223, 255], + [0, 31, 63, 95, 127, 159, 191, 223, 255], + [0, 31, 63, 95, 127, 159, 191, 223, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT8_MAT3", function () { + const propertyName = "example_normalized_UINT8_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 0.12156862745098039, 0.24705882352941178, 0.37254901960784315, + 0.4980392156862745, 0.6235294117647059, 0.7490196078431373, + 0.8745098039215686, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT8_MAT3_array", function () { + const propertyName = "example_variable_length_normalized_UINT8_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.12156862745098039, 0.24705882352941178, 0.37254901960784315, + 0.4980392156862745, 0.6235294117647059, 0.7490196078431373, + 0.8745098039215686, 1, + ], + [ + 0, 0.12156862745098039, 0.24705882352941178, 0.37254901960784315, + 0.4980392156862745, 0.6235294117647059, 0.7490196078431373, + 0.8745098039215686, 1, + ], + [ + 0, 0.12156862745098039, 0.24705882352941178, 0.37254901960784315, + 0.4980392156862745, 0.6235294117647059, 0.7490196078431373, + 0.8745098039215686, 1, + ], + [ + 0, 0.12156862745098039, 0.24705882352941178, 0.37254901960784315, + 0.4980392156862745, 0.6235294117647059, 0.7490196078431373, + 0.8745098039215686, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT8_MAT3_array", function () { + const propertyName = "example_fixed_length_normalized_UINT8_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.12156862745098039, 0.24705882352941178, 0.37254901960784315, + 0.4980392156862745, 0.6235294117647059, 0.7490196078431373, + 0.8745098039215686, 1, + ], + [ + 0, 0.12156862745098039, 0.24705882352941178, 0.37254901960784315, + 0.4980392156862745, 0.6235294117647059, 0.7490196078431373, + 0.8745098039215686, 1, + ], + [ + 0, 0.12156862745098039, 0.24705882352941178, 0.37254901960784315, + 0.4980392156862745, 0.6235294117647059, 0.7490196078431373, + 0.8745098039215686, 1, + ], + [ + 0, 0.12156862745098039, 0.24705882352941178, 0.37254901960784315, + 0.4980392156862745, 0.6235294117647059, 0.7490196078431373, + 0.8745098039215686, 1, + ], + [ + 0, 0.12156862745098039, 0.24705882352941178, 0.37254901960784315, + 0.4980392156862745, 0.6235294117647059, 0.7490196078431373, + 0.8745098039215686, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT16_MAT3", function () { + const propertyName = "example_INT16_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT16_MAT3_array", function () { + const propertyName = "example_variable_length_INT16_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767], + [-32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767], + [-32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767], + [-32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT16_MAT3_array", function () { + const propertyName = "example_fixed_length_INT16_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767], + [-32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767], + [-32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767], + [-32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767], + [-32768, -24576, -16384, -8192, 0, 8191, 16383, 24575, 32767], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT16_MAT3", function () { + const propertyName = "example_normalized_INT16_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -1, -0.750022888882107, -0.500015259254738, -0.250007629627369, 0, + 0.249977111117893, 0.499984740745262, 0.749992370372631, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT16_MAT3_array", function () { + const propertyName = "example_variable_length_normalized_INT16_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.750022888882107, -0.500015259254738, -0.250007629627369, 0, + 0.249977111117893, 0.499984740745262, 0.749992370372631, 1, + ], + [ + -1, -0.750022888882107, -0.500015259254738, -0.250007629627369, 0, + 0.249977111117893, 0.499984740745262, 0.749992370372631, 1, + ], + [ + -1, -0.750022888882107, -0.500015259254738, -0.250007629627369, 0, + 0.249977111117893, 0.499984740745262, 0.749992370372631, 1, + ], + [ + -1, -0.750022888882107, -0.500015259254738, -0.250007629627369, 0, + 0.249977111117893, 0.499984740745262, 0.749992370372631, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT16_MAT3_array", function () { + const propertyName = "example_fixed_length_normalized_INT16_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.750022888882107, -0.500015259254738, -0.250007629627369, 0, + 0.249977111117893, 0.499984740745262, 0.749992370372631, 1, + ], + [ + -1, -0.750022888882107, -0.500015259254738, -0.250007629627369, 0, + 0.249977111117893, 0.499984740745262, 0.749992370372631, 1, + ], + [ + -1, -0.750022888882107, -0.500015259254738, -0.250007629627369, 0, + 0.249977111117893, 0.499984740745262, 0.749992370372631, 1, + ], + [ + -1, -0.750022888882107, -0.500015259254738, -0.250007629627369, 0, + 0.249977111117893, 0.499984740745262, 0.749992370372631, 1, + ], + [ + -1, -0.750022888882107, -0.500015259254738, -0.250007629627369, 0, + 0.249977111117893, 0.499984740745262, 0.749992370372631, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT16_MAT3", function () { + const propertyName = "example_UINT16_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT16_MAT3_array", function () { + const propertyName = "example_variable_length_UINT16_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535], + [0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535], + [0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535], + [0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT16_MAT3_array", function () { + const propertyName = "example_fixed_length_UINT16_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535], + [0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535], + [0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535], + [0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535], + [0, 8191, 16383, 24575, 32767, 40959, 49151, 57343, 65535], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT16_MAT3", function () { + const propertyName = "example_normalized_UINT16_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 0.12498664835584039, 0.24998855573357748, 0.37499046311131456, + 0.49999237048905165, 0.6249942778667887, 0.7499961852445258, + 0.8749980926222629, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT16_MAT3_array", function () { + const propertyName = "example_variable_length_normalized_UINT16_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.12498664835584039, 0.24998855573357748, 0.37499046311131456, + 0.49999237048905165, 0.6249942778667887, 0.7499961852445258, + 0.8749980926222629, 1, + ], + [ + 0, 0.12498664835584039, 0.24998855573357748, 0.37499046311131456, + 0.49999237048905165, 0.6249942778667887, 0.7499961852445258, + 0.8749980926222629, 1, + ], + [ + 0, 0.12498664835584039, 0.24998855573357748, 0.37499046311131456, + 0.49999237048905165, 0.6249942778667887, 0.7499961852445258, + 0.8749980926222629, 1, + ], + [ + 0, 0.12498664835584039, 0.24998855573357748, 0.37499046311131456, + 0.49999237048905165, 0.6249942778667887, 0.7499961852445258, + 0.8749980926222629, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT16_MAT3_array", function () { + const propertyName = "example_fixed_length_normalized_UINT16_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.12498664835584039, 0.24998855573357748, 0.37499046311131456, + 0.49999237048905165, 0.6249942778667887, 0.7499961852445258, + 0.8749980926222629, 1, + ], + [ + 0, 0.12498664835584039, 0.24998855573357748, 0.37499046311131456, + 0.49999237048905165, 0.6249942778667887, 0.7499961852445258, + 0.8749980926222629, 1, + ], + [ + 0, 0.12498664835584039, 0.24998855573357748, 0.37499046311131456, + 0.49999237048905165, 0.6249942778667887, 0.7499961852445258, + 0.8749980926222629, 1, + ], + [ + 0, 0.12498664835584039, 0.24998855573357748, 0.37499046311131456, + 0.49999237048905165, 0.6249942778667887, 0.7499961852445258, + 0.8749980926222629, 1, + ], + [ + 0, 0.12498664835584039, 0.24998855573357748, 0.37499046311131456, + 0.49999237048905165, 0.6249942778667887, 0.7499961852445258, + 0.8749980926222629, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT32_MAT3", function () { + const propertyName = "example_INT32_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, + 1073741823, 1610612735, 2147483647, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT32_MAT3_array", function () { + const propertyName = "example_variable_length_INT32_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, + 1073741823, 1610612735, 2147483647, + ], + [ + -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, + 1073741823, 1610612735, 2147483647, + ], + [ + -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, + 1073741823, 1610612735, 2147483647, + ], + [ + -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, + 1073741823, 1610612735, 2147483647, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT32_MAT3_array", function () { + const propertyName = "example_fixed_length_INT32_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, + 1073741823, 1610612735, 2147483647, + ], + [ + -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, + 1073741823, 1610612735, 2147483647, + ], + [ + -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, + 1073741823, 1610612735, 2147483647, + ], + [ + -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, + 1073741823, 1610612735, 2147483647, + ], + [ + -2147483648, -1610612736, -1073741824, -536870912, 0, 536870911, + 1073741823, 1610612735, 2147483647, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT32_MAT3", function () { + const propertyName = "example_normalized_INT32_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -1, -0.750000000349246, -0.5000000002328306, -0.2500000001164153, 0, + 0.24999999965075403, 0.49999999976716936, 0.7499999998835847, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT32_MAT3_array", function () { + const propertyName = "example_variable_length_normalized_INT32_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.750000000349246, -0.5000000002328306, -0.2500000001164153, 0, + 0.24999999965075403, 0.49999999976716936, 0.7499999998835847, 1, + ], + [ + -1, -0.750000000349246, -0.5000000002328306, -0.2500000001164153, 0, + 0.24999999965075403, 0.49999999976716936, 0.7499999998835847, 1, + ], + [ + -1, -0.750000000349246, -0.5000000002328306, -0.2500000001164153, 0, + 0.24999999965075403, 0.49999999976716936, 0.7499999998835847, 1, + ], + [ + -1, -0.750000000349246, -0.5000000002328306, -0.2500000001164153, 0, + 0.24999999965075403, 0.49999999976716936, 0.7499999998835847, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT32_MAT3_array", function () { + const propertyName = "example_fixed_length_normalized_INT32_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.750000000349246, -0.5000000002328306, -0.2500000001164153, 0, + 0.24999999965075403, 0.49999999976716936, 0.7499999998835847, 1, + ], + [ + -1, -0.750000000349246, -0.5000000002328306, -0.2500000001164153, 0, + 0.24999999965075403, 0.49999999976716936, 0.7499999998835847, 1, + ], + [ + -1, -0.750000000349246, -0.5000000002328306, -0.2500000001164153, 0, + 0.24999999965075403, 0.49999999976716936, 0.7499999998835847, 1, + ], + [ + -1, -0.750000000349246, -0.5000000002328306, -0.2500000001164153, 0, + 0.24999999965075403, 0.49999999976716936, 0.7499999998835847, 1, + ], + [ + -1, -0.750000000349246, -0.5000000002328306, -0.2500000001164153, 0, + 0.24999999965075403, 0.49999999976716936, 0.7499999998835847, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT32_MAT3", function () { + const propertyName = "example_UINT32_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, 3221225471, + 3758096383, 4294967295, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT32_MAT3_array", function () { + const propertyName = "example_variable_length_UINT32_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, + 3221225471, 3758096383, 4294967295, + ], + [ + 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, + 3221225471, 3758096383, 4294967295, + ], + [ + 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, + 3221225471, 3758096383, 4294967295, + ], + [ + 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, + 3221225471, 3758096383, 4294967295, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT32_MAT3_array", function () { + const propertyName = "example_fixed_length_UINT32_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, + 3221225471, 3758096383, 4294967295, + ], + [ + 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, + 3221225471, 3758096383, 4294967295, + ], + [ + 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, + 3221225471, 3758096383, 4294967295, + ], + [ + 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, + 3221225471, 3758096383, 4294967295, + ], + [ + 0, 536870911, 1073741823, 1610612735, 2147483647, 2684354559, + 3221225471, 3758096383, 4294967295, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT32_MAT3", function () { + const propertyName = "example_normalized_UINT32_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 0.12499999979627319, 0.24999999982537702, 0.37499999985448085, + 0.4999999998835847, 0.6249999999126885, 0.7499999999417923, + 0.8749999999708962, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT32_MAT3_array", function () { + const propertyName = "example_variable_length_normalized_UINT32_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.12499999979627319, 0.24999999982537702, 0.37499999985448085, + 0.4999999998835847, 0.6249999999126885, 0.7499999999417923, + 0.8749999999708962, 1, + ], + [ + 0, 0.12499999979627319, 0.24999999982537702, 0.37499999985448085, + 0.4999999998835847, 0.6249999999126885, 0.7499999999417923, + 0.8749999999708962, 1, + ], + [ + 0, 0.12499999979627319, 0.24999999982537702, 0.37499999985448085, + 0.4999999998835847, 0.6249999999126885, 0.7499999999417923, + 0.8749999999708962, 1, + ], + [ + 0, 0.12499999979627319, 0.24999999982537702, 0.37499999985448085, + 0.4999999998835847, 0.6249999999126885, 0.7499999999417923, + 0.8749999999708962, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT32_MAT3_array", function () { + const propertyName = "example_fixed_length_normalized_UINT32_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.12499999979627319, 0.24999999982537702, 0.37499999985448085, + 0.4999999998835847, 0.6249999999126885, 0.7499999999417923, + 0.8749999999708962, 1, + ], + [ + 0, 0.12499999979627319, 0.24999999982537702, 0.37499999985448085, + 0.4999999998835847, 0.6249999999126885, 0.7499999999417923, + 0.8749999999708962, 1, + ], + [ + 0, 0.12499999979627319, 0.24999999982537702, 0.37499999985448085, + 0.4999999998835847, 0.6249999999126885, 0.7499999999417923, + 0.8749999999708962, 1, + ], + [ + 0, 0.12499999979627319, 0.24999999982537702, 0.37499999985448085, + 0.4999999998835847, 0.6249999999126885, 0.7499999999417923, + 0.8749999999708962, 1, + ], + [ + 0, 0.12499999979627319, 0.24999999982537702, 0.37499999985448085, + 0.4999999998835847, 0.6249999999126885, 0.7499999999417923, + 0.8749999999708962, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT64_MAT3", function () { + const propertyName = "example_INT64_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -9223372036854776000n, + -6917529027641082000n, + -4611686018427388000n, + -2305843009213694000n, + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT64_MAT3_array", function () { + const propertyName = "example_variable_length_INT64_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -9223372036854776000n, + -6917529027641082000n, + -4611686018427388000n, + -2305843009213694000n, + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -6917529027641082000n, + -4611686018427388000n, + -2305843009213694000n, + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -6917529027641082000n, + -4611686018427388000n, + -2305843009213694000n, + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -6917529027641082000n, + -4611686018427388000n, + -2305843009213694000n, + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT64_MAT3_array", function () { + const propertyName = "example_fixed_length_INT64_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -9223372036854776000n, + -6917529027641082000n, + -4611686018427388000n, + -2305843009213694000n, + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -6917529027641082000n, + -4611686018427388000n, + -2305843009213694000n, + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -6917529027641082000n, + -4611686018427388000n, + -2305843009213694000n, + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -6917529027641082000n, + -4611686018427388000n, + -2305843009213694000n, + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -6917529027641082000n, + -4611686018427388000n, + -2305843009213694000n, + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT64_MAT3", function () { + const propertyName = "example_normalized_INT64_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT64_MAT3_array", function () { + const propertyName = "example_variable_length_normalized_INT64_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT64_MAT3_array", function () { + const propertyName = "example_fixed_length_normalized_INT64_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT64_MAT3", function () { + const propertyName = "example_UINT64_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + 11529215046068470000n, + 13835058055282164000n, + 16140901064495858000n, + 18446744073709552000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT64_MAT3_array", function () { + const propertyName = "example_variable_length_UINT64_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + 11529215046068470000n, + 13835058055282164000n, + 16140901064495858000n, + 18446744073709552000n, + ], + [ + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + 11529215046068470000n, + 13835058055282164000n, + 16140901064495858000n, + 18446744073709552000n, + ], + [ + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + 11529215046068470000n, + 13835058055282164000n, + 16140901064495858000n, + 18446744073709552000n, + ], + [ + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + 11529215046068470000n, + 13835058055282164000n, + 16140901064495858000n, + 18446744073709552000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT64_MAT3_array", function () { + const propertyName = "example_fixed_length_UINT64_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + 11529215046068470000n, + 13835058055282164000n, + 16140901064495858000n, + 18446744073709552000n, + ], + [ + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + 11529215046068470000n, + 13835058055282164000n, + 16140901064495858000n, + 18446744073709552000n, + ], + [ + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + 11529215046068470000n, + 13835058055282164000n, + 16140901064495858000n, + 18446744073709552000n, + ], + [ + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + 11529215046068470000n, + 13835058055282164000n, + 16140901064495858000n, + 18446744073709552000n, + ], + [ + 0, + 2305843009213694000n, + 4611686018427388000n, + 6917529027641082000n, + 9223372036854776000n, + 11529215046068470000n, + 13835058055282164000n, + 16140901064495858000n, + 18446744073709552000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT64_MAT3", function () { + const propertyName = "example_normalized_UINT64_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT64_MAT3_array", function () { + const propertyName = "example_variable_length_normalized_UINT64_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT64_MAT3_array", function () { + const propertyName = "example_fixed_length_normalized_UINT64_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT32_MAT3", function () { + const propertyName = "example_FLOAT32_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT32_MAT3_array", function () { + const propertyName = "example_variable_length_FLOAT32_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT32_MAT3_array", function () { + const propertyName = "example_fixed_length_FLOAT32_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT64_MAT3", function () { + const propertyName = "example_FLOAT64_MAT3"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT64_MAT3_array", function () { + const propertyName = "example_variable_length_FLOAT64_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT64_MAT3_array", function () { + const propertyName = "example_fixed_length_FLOAT64_MAT3_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT8_MAT4", function () { + const propertyName = "example_INT8_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, 127, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT8_MAT4_array", function () { + const propertyName = "example_variable_length_INT8_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, + 127, + ], + [ + -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, + 127, + ], + [ + -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, + 127, + ], + [ + -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, + 127, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT8_MAT4_array", function () { + const propertyName = "example_fixed_length_INT8_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, + 127, + ], + [ + -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, + 127, + ], + [ + -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, + 127, + ], + [ + -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, + 127, + ], + [ + -128, -110, -94, -77, -59, -43, -26, -8, 7, 25, 42, 58, 76, 93, 109, + 127, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT8_MAT4", function () { + const propertyName = "example_normalized_INT8_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -1, -0.8661417322834646, -0.7401574803149606, -0.6062992125984252, + -0.4645669291338583, -0.33858267716535434, -0.2047244094488189, + -0.06299212598425197, 0.05511811023622047, 0.1968503937007874, + 0.33070866141732286, 0.4566929133858268, 0.5984251968503937, + 0.7322834645669292, 0.8582677165354331, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT8_MAT4_array", function () { + const propertyName = "example_variable_length_normalized_INT8_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8661417322834646, -0.7401574803149606, -0.6062992125984252, + -0.4645669291338583, -0.33858267716535434, -0.2047244094488189, + -0.06299212598425197, 0.05511811023622047, 0.1968503937007874, + 0.33070866141732286, 0.4566929133858268, 0.5984251968503937, + 0.7322834645669292, 0.8582677165354331, 1, + ], + [ + -1, -0.8661417322834646, -0.7401574803149606, -0.6062992125984252, + -0.4645669291338583, -0.33858267716535434, -0.2047244094488189, + -0.06299212598425197, 0.05511811023622047, 0.1968503937007874, + 0.33070866141732286, 0.4566929133858268, 0.5984251968503937, + 0.7322834645669292, 0.8582677165354331, 1, + ], + [ + -1, -0.8661417322834646, -0.7401574803149606, -0.6062992125984252, + -0.4645669291338583, -0.33858267716535434, -0.2047244094488189, + -0.06299212598425197, 0.05511811023622047, 0.1968503937007874, + 0.33070866141732286, 0.4566929133858268, 0.5984251968503937, + 0.7322834645669292, 0.8582677165354331, 1, + ], + [ + -1, -0.8661417322834646, -0.7401574803149606, -0.6062992125984252, + -0.4645669291338583, -0.33858267716535434, -0.2047244094488189, + -0.06299212598425197, 0.05511811023622047, 0.1968503937007874, + 0.33070866141732286, 0.4566929133858268, 0.5984251968503937, + 0.7322834645669292, 0.8582677165354331, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT8_MAT4_array", function () { + const propertyName = "example_fixed_length_normalized_INT8_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8661417322834646, -0.7401574803149606, -0.6062992125984252, + -0.4645669291338583, -0.33858267716535434, -0.2047244094488189, + -0.06299212598425197, 0.05511811023622047, 0.1968503937007874, + 0.33070866141732286, 0.4566929133858268, 0.5984251968503937, + 0.7322834645669292, 0.8582677165354331, 1, + ], + [ + -1, -0.8661417322834646, -0.7401574803149606, -0.6062992125984252, + -0.4645669291338583, -0.33858267716535434, -0.2047244094488189, + -0.06299212598425197, 0.05511811023622047, 0.1968503937007874, + 0.33070866141732286, 0.4566929133858268, 0.5984251968503937, + 0.7322834645669292, 0.8582677165354331, 1, + ], + [ + -1, -0.8661417322834646, -0.7401574803149606, -0.6062992125984252, + -0.4645669291338583, -0.33858267716535434, -0.2047244094488189, + -0.06299212598425197, 0.05511811023622047, 0.1968503937007874, + 0.33070866141732286, 0.4566929133858268, 0.5984251968503937, + 0.7322834645669292, 0.8582677165354331, 1, + ], + [ + -1, -0.8661417322834646, -0.7401574803149606, -0.6062992125984252, + -0.4645669291338583, -0.33858267716535434, -0.2047244094488189, + -0.06299212598425197, 0.05511811023622047, 0.1968503937007874, + 0.33070866141732286, 0.4566929133858268, 0.5984251968503937, + 0.7322834645669292, 0.8582677165354331, 1, + ], + [ + -1, -0.8661417322834646, -0.7401574803149606, -0.6062992125984252, + -0.4645669291338583, -0.33858267716535434, -0.2047244094488189, + -0.06299212598425197, 0.05511811023622047, 0.1968503937007874, + 0.33070866141732286, 0.4566929133858268, 0.5984251968503937, + 0.7322834645669292, 0.8582677165354331, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT8_MAT4", function () { + const propertyName = "example_UINT8_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT8_MAT4_array", function () { + const propertyName = "example_variable_length_UINT8_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255], + [0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255], + [0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255], + [0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT8_MAT4_array", function () { + const propertyName = "example_fixed_length_UINT8_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255], + [0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255], + [0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255], + [0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255], + [0, 17, 33, 51, 68, 84, 102, 119, 135, 153, 170, 186, 204, 221, 237, 255], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT8_MAT4", function () { + const propertyName = "example_normalized_UINT8_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 0.06666666666666667, 0.12941176470588237, 0.2, 0.26666666666666666, + 0.32941176470588235, 0.4, 0.4666666666666667, 0.5294117647058824, 0.6, + 0.6666666666666666, 0.7294117647058823, 0.8, 0.8666666666666667, + 0.9294117647058824, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT8_MAT4_array", function () { + const propertyName = "example_variable_length_normalized_UINT8_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.06666666666666667, 0.12941176470588237, 0.2, 0.26666666666666666, + 0.32941176470588235, 0.4, 0.4666666666666667, 0.5294117647058824, 0.6, + 0.6666666666666666, 0.7294117647058823, 0.8, 0.8666666666666667, + 0.9294117647058824, 1, + ], + [ + 0, 0.06666666666666667, 0.12941176470588237, 0.2, 0.26666666666666666, + 0.32941176470588235, 0.4, 0.4666666666666667, 0.5294117647058824, 0.6, + 0.6666666666666666, 0.7294117647058823, 0.8, 0.8666666666666667, + 0.9294117647058824, 1, + ], + [ + 0, 0.06666666666666667, 0.12941176470588237, 0.2, 0.26666666666666666, + 0.32941176470588235, 0.4, 0.4666666666666667, 0.5294117647058824, 0.6, + 0.6666666666666666, 0.7294117647058823, 0.8, 0.8666666666666667, + 0.9294117647058824, 1, + ], + [ + 0, 0.06666666666666667, 0.12941176470588237, 0.2, 0.26666666666666666, + 0.32941176470588235, 0.4, 0.4666666666666667, 0.5294117647058824, 0.6, + 0.6666666666666666, 0.7294117647058823, 0.8, 0.8666666666666667, + 0.9294117647058824, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT8_MAT4_array", function () { + const propertyName = "example_fixed_length_normalized_UINT8_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.06666666666666667, 0.12941176470588237, 0.2, 0.26666666666666666, + 0.32941176470588235, 0.4, 0.4666666666666667, 0.5294117647058824, 0.6, + 0.6666666666666666, 0.7294117647058823, 0.8, 0.8666666666666667, + 0.9294117647058824, 1, + ], + [ + 0, 0.06666666666666667, 0.12941176470588237, 0.2, 0.26666666666666666, + 0.32941176470588235, 0.4, 0.4666666666666667, 0.5294117647058824, 0.6, + 0.6666666666666666, 0.7294117647058823, 0.8, 0.8666666666666667, + 0.9294117647058824, 1, + ], + [ + 0, 0.06666666666666667, 0.12941176470588237, 0.2, 0.26666666666666666, + 0.32941176470588235, 0.4, 0.4666666666666667, 0.5294117647058824, 0.6, + 0.6666666666666666, 0.7294117647058823, 0.8, 0.8666666666666667, + 0.9294117647058824, 1, + ], + [ + 0, 0.06666666666666667, 0.12941176470588237, 0.2, 0.26666666666666666, + 0.32941176470588235, 0.4, 0.4666666666666667, 0.5294117647058824, 0.6, + 0.6666666666666666, 0.7294117647058823, 0.8, 0.8666666666666667, + 0.9294117647058824, 1, + ], + [ + 0, 0.06666666666666667, 0.12941176470588237, 0.2, 0.26666666666666666, + 0.32941176470588235, 0.4, 0.4666666666666667, 0.5294117647058824, 0.6, + 0.6666666666666666, 0.7294117647058823, 0.8, 0.8666666666666667, + 0.9294117647058824, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT16_MAT4", function () { + const propertyName = "example_INT16_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, 6553, + 10922, 15290, 19660, 24029, 28397, 32767, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT16_MAT4_array", function () { + const propertyName = "example_variable_length_INT16_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, + 6553, 10922, 15290, 19660, 24029, 28397, 32767, + ], + [ + -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, + 6553, 10922, 15290, 19660, 24029, 28397, 32767, + ], + [ + -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, + 6553, 10922, 15290, 19660, 24029, 28397, 32767, + ], + [ + -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, + 6553, 10922, 15290, 19660, 24029, 28397, 32767, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT16_MAT4_array", function () { + const propertyName = "example_fixed_length_INT16_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, + 6553, 10922, 15290, 19660, 24029, 28397, 32767, + ], + [ + -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, + 6553, 10922, 15290, 19660, 24029, 28397, 32767, + ], + [ + -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, + 6553, 10922, 15290, 19660, 24029, 28397, 32767, + ], + [ + -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, + 6553, 10922, 15290, 19660, 24029, 28397, 32767, + ], + [ + -32768, -28398, -24030, -19661, -15291, -10923, -6554, -2184, 2183, + 6553, 10922, 15290, 19660, 24029, 28397, 32767, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT16_MAT4", function () { + const propertyName = "example_normalized_INT16_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -1, -0.8666646320993683, -0.7333597827082126, -0.6000244148075808, + -0.46665852839747307, -0.33335367900631735, -0.2000183111056856, + -0.06665242469557787, 0.06662190618610186, 0.1999877925962096, + 0.33332316049684135, 0.4666280098879971, 0.5999938962981048, + 0.7333292641987366, 0.8666341135898923, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT16_MAT4_array", function () { + const propertyName = "example_variable_length_normalized_INT16_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8666646320993683, -0.7333597827082126, -0.6000244148075808, + -0.46665852839747307, -0.33335367900631735, -0.2000183111056856, + -0.06665242469557787, 0.06662190618610186, 0.1999877925962096, + 0.33332316049684135, 0.4666280098879971, 0.5999938962981048, + 0.7333292641987366, 0.8666341135898923, 1, + ], + [ + -1, -0.8666646320993683, -0.7333597827082126, -0.6000244148075808, + -0.46665852839747307, -0.33335367900631735, -0.2000183111056856, + -0.06665242469557787, 0.06662190618610186, 0.1999877925962096, + 0.33332316049684135, 0.4666280098879971, 0.5999938962981048, + 0.7333292641987366, 0.8666341135898923, 1, + ], + [ + -1, -0.8666646320993683, -0.7333597827082126, -0.6000244148075808, + -0.46665852839747307, -0.33335367900631735, -0.2000183111056856, + -0.06665242469557787, 0.06662190618610186, 0.1999877925962096, + 0.33332316049684135, 0.4666280098879971, 0.5999938962981048, + 0.7333292641987366, 0.8666341135898923, 1, + ], + [ + -1, -0.8666646320993683, -0.7333597827082126, -0.6000244148075808, + -0.46665852839747307, -0.33335367900631735, -0.2000183111056856, + -0.06665242469557787, 0.06662190618610186, 0.1999877925962096, + 0.33332316049684135, 0.4666280098879971, 0.5999938962981048, + 0.7333292641987366, 0.8666341135898923, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT16_MAT4_array", function () { + const propertyName = "example_fixed_length_normalized_INT16_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8666646320993683, -0.7333597827082126, -0.6000244148075808, + -0.46665852839747307, -0.33335367900631735, -0.2000183111056856, + -0.06665242469557787, 0.06662190618610186, 0.1999877925962096, + 0.33332316049684135, 0.4666280098879971, 0.5999938962981048, + 0.7333292641987366, 0.8666341135898923, 1, + ], + [ + -1, -0.8666646320993683, -0.7333597827082126, -0.6000244148075808, + -0.46665852839747307, -0.33335367900631735, -0.2000183111056856, + -0.06665242469557787, 0.06662190618610186, 0.1999877925962096, + 0.33332316049684135, 0.4666280098879971, 0.5999938962981048, + 0.7333292641987366, 0.8666341135898923, 1, + ], + [ + -1, -0.8666646320993683, -0.7333597827082126, -0.6000244148075808, + -0.46665852839747307, -0.33335367900631735, -0.2000183111056856, + -0.06665242469557787, 0.06662190618610186, 0.1999877925962096, + 0.33332316049684135, 0.4666280098879971, 0.5999938962981048, + 0.7333292641987366, 0.8666341135898923, 1, + ], + [ + -1, -0.8666646320993683, -0.7333597827082126, -0.6000244148075808, + -0.46665852839747307, -0.33335367900631735, -0.2000183111056856, + -0.06665242469557787, 0.06662190618610186, 0.1999877925962096, + 0.33332316049684135, 0.4666280098879971, 0.5999938962981048, + 0.7333292641987366, 0.8666341135898923, 1, + ], + [ + -1, -0.8666646320993683, -0.7333597827082126, -0.6000244148075808, + -0.46665852839747307, -0.33335367900631735, -0.2000183111056856, + -0.06665242469557787, 0.06662190618610186, 0.1999877925962096, + 0.33332316049684135, 0.4666280098879971, 0.5999938962981048, + 0.7333292641987366, 0.8666341135898923, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT16_MAT4", function () { + const propertyName = "example_UINT16_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, + 48058, 52428, 56797, 61165, 65535, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT16_MAT4_array", function () { + const propertyName = "example_variable_length_UINT16_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, + 48058, 52428, 56797, 61165, 65535, + ], + [ + 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, + 48058, 52428, 56797, 61165, 65535, + ], + [ + 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, + 48058, 52428, 56797, 61165, 65535, + ], + [ + 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, + 48058, 52428, 56797, 61165, 65535, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT16_MAT4_array", function () { + const propertyName = "example_fixed_length_UINT16_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, + 48058, 52428, 56797, 61165, 65535, + ], + [ + 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, + 48058, 52428, 56797, 61165, 65535, + ], + [ + 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, + 48058, 52428, 56797, 61165, 65535, + ], + [ + 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, + 48058, 52428, 56797, 61165, 65535, + ], + [ + 0, 4369, 8737, 13107, 17476, 21844, 26214, 30583, 34951, 39321, 43690, + 48058, 52428, 56797, 61165, 65535, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT16_MAT4", function () { + const propertyName = "example_normalized_UINT16_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 0.06666666666666667, 0.13331807431143664, 0.2, 0.26666666666666666, + 0.3333180743114366, 0.4, 0.4666666666666667, 0.5333180743114366, 0.6, + 0.6666666666666666, 0.7333180743114366, 0.8, 0.8666666666666667, + 0.9333180743114367, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT16_MAT4_array", function () { + const propertyName = "example_variable_length_normalized_UINT16_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.06666666666666667, 0.13331807431143664, 0.2, 0.26666666666666666, + 0.3333180743114366, 0.4, 0.4666666666666667, 0.5333180743114366, 0.6, + 0.6666666666666666, 0.7333180743114366, 0.8, 0.8666666666666667, + 0.9333180743114367, 1, + ], + [ + 0, 0.06666666666666667, 0.13331807431143664, 0.2, 0.26666666666666666, + 0.3333180743114366, 0.4, 0.4666666666666667, 0.5333180743114366, 0.6, + 0.6666666666666666, 0.7333180743114366, 0.8, 0.8666666666666667, + 0.9333180743114367, 1, + ], + [ + 0, 0.06666666666666667, 0.13331807431143664, 0.2, 0.26666666666666666, + 0.3333180743114366, 0.4, 0.4666666666666667, 0.5333180743114366, 0.6, + 0.6666666666666666, 0.7333180743114366, 0.8, 0.8666666666666667, + 0.9333180743114367, 1, + ], + [ + 0, 0.06666666666666667, 0.13331807431143664, 0.2, 0.26666666666666666, + 0.3333180743114366, 0.4, 0.4666666666666667, 0.5333180743114366, 0.6, + 0.6666666666666666, 0.7333180743114366, 0.8, 0.8666666666666667, + 0.9333180743114367, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT16_MAT4_array", function () { + const propertyName = "example_fixed_length_normalized_UINT16_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.06666666666666667, 0.13331807431143664, 0.2, 0.26666666666666666, + 0.3333180743114366, 0.4, 0.4666666666666667, 0.5333180743114366, 0.6, + 0.6666666666666666, 0.7333180743114366, 0.8, 0.8666666666666667, + 0.9333180743114367, 1, + ], + [ + 0, 0.06666666666666667, 0.13331807431143664, 0.2, 0.26666666666666666, + 0.3333180743114366, 0.4, 0.4666666666666667, 0.5333180743114366, 0.6, + 0.6666666666666666, 0.7333180743114366, 0.8, 0.8666666666666667, + 0.9333180743114367, 1, + ], + [ + 0, 0.06666666666666667, 0.13331807431143664, 0.2, 0.26666666666666666, + 0.3333180743114366, 0.4, 0.4666666666666667, 0.5333180743114366, 0.6, + 0.6666666666666666, 0.7333180743114366, 0.8, 0.8666666666666667, + 0.9333180743114367, 1, + ], + [ + 0, 0.06666666666666667, 0.13331807431143664, 0.2, 0.26666666666666666, + 0.3333180743114366, 0.4, 0.4666666666666667, 0.5333180743114366, 0.6, + 0.6666666666666666, 0.7333180743114366, 0.8, 0.8666666666666667, + 0.9333180743114367, 1, + ], + [ + 0, 0.06666666666666667, 0.13331807431143664, 0.2, 0.26666666666666666, + 0.3333180743114366, 0.4, 0.4666666666666667, 0.5333180743114366, 0.6, + 0.6666666666666666, 0.7333180743114366, 0.8, 0.8666666666666667, + 0.9333180743114367, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT32_MAT4", function () { + const propertyName = "example_INT32_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, + -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, + 1002159034, 1288490188, 1574821341, 1861152493, 2147483647, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT32_MAT4_array", function () { + const propertyName = "example_variable_length_INT32_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, + -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, + 1002159034, 1288490188, 1574821341, 1861152493, 2147483647, + ], + [ + -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, + -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, + 1002159034, 1288490188, 1574821341, 1861152493, 2147483647, + ], + [ + -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, + -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, + 1002159034, 1288490188, 1574821341, 1861152493, 2147483647, + ], + [ + -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, + -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, + 1002159034, 1288490188, 1574821341, 1861152493, 2147483647, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT32_MAT4_array", function () { + const propertyName = "example_fixed_length_INT32_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, + -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, + 1002159034, 1288490188, 1574821341, 1861152493, 2147483647, + ], + [ + -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, + -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, + 1002159034, 1288490188, 1574821341, 1861152493, 2147483647, + ], + [ + -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, + -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, + 1002159034, 1288490188, 1574821341, 1861152493, 2147483647, + ], + [ + -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, + -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, + 1002159034, 1288490188, 1574821341, 1861152493, 2147483647, + ], + [ + -2147483648, -1861152494, -1574821342, -1288490189, -1002159035, + -715827883, -429496730, -143165576, 143165575, 429496729, 715827882, + 1002159034, 1288490188, 1574821341, 1861152493, 2147483647, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT32_MAT4", function () { + const propertyName = "example_normalized_INT32_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -1, -0.8666666666356225, -0.7333333337369065, -0.600000000372529, + -0.46666666654249034, -0.3333333336437742, -0.20000000027939677, + -0.06666666644935806, 0.06666666598369678, 0.19999999981373548, + 0.3333333331781129, 0.46666666607682905, 0.5999999999068677, + 0.7333333332712452, 0.8666666661699612, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT32_MAT4_array", function () { + const propertyName = "example_variable_length_normalized_INT32_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8666666666356225, -0.7333333337369065, -0.600000000372529, + -0.46666666654249034, -0.3333333336437742, -0.20000000027939677, + -0.06666666644935806, 0.06666666598369678, 0.19999999981373548, + 0.3333333331781129, 0.46666666607682905, 0.5999999999068677, + 0.7333333332712452, 0.8666666661699612, 1, + ], + [ + -1, -0.8666666666356225, -0.7333333337369065, -0.600000000372529, + -0.46666666654249034, -0.3333333336437742, -0.20000000027939677, + -0.06666666644935806, 0.06666666598369678, 0.19999999981373548, + 0.3333333331781129, 0.46666666607682905, 0.5999999999068677, + 0.7333333332712452, 0.8666666661699612, 1, + ], + [ + -1, -0.8666666666356225, -0.7333333337369065, -0.600000000372529, + -0.46666666654249034, -0.3333333336437742, -0.20000000027939677, + -0.06666666644935806, 0.06666666598369678, 0.19999999981373548, + 0.3333333331781129, 0.46666666607682905, 0.5999999999068677, + 0.7333333332712452, 0.8666666661699612, 1, + ], + [ + -1, -0.8666666666356225, -0.7333333337369065, -0.600000000372529, + -0.46666666654249034, -0.3333333336437742, -0.20000000027939677, + -0.06666666644935806, 0.06666666598369678, 0.19999999981373548, + 0.3333333331781129, 0.46666666607682905, 0.5999999999068677, + 0.7333333332712452, 0.8666666661699612, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT32_MAT4_array", function () { + const propertyName = "example_fixed_length_normalized_INT32_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8666666666356225, -0.7333333337369065, -0.600000000372529, + -0.46666666654249034, -0.3333333336437742, -0.20000000027939677, + -0.06666666644935806, 0.06666666598369678, 0.19999999981373548, + 0.3333333331781129, 0.46666666607682905, 0.5999999999068677, + 0.7333333332712452, 0.8666666661699612, 1, + ], + [ + -1, -0.8666666666356225, -0.7333333337369065, -0.600000000372529, + -0.46666666654249034, -0.3333333336437742, -0.20000000027939677, + -0.06666666644935806, 0.06666666598369678, 0.19999999981373548, + 0.3333333331781129, 0.46666666607682905, 0.5999999999068677, + 0.7333333332712452, 0.8666666661699612, 1, + ], + [ + -1, -0.8666666666356225, -0.7333333337369065, -0.600000000372529, + -0.46666666654249034, -0.3333333336437742, -0.20000000027939677, + -0.06666666644935806, 0.06666666598369678, 0.19999999981373548, + 0.3333333331781129, 0.46666666607682905, 0.5999999999068677, + 0.7333333332712452, 0.8666666661699612, 1, + ], + [ + -1, -0.8666666666356225, -0.7333333337369065, -0.600000000372529, + -0.46666666654249034, -0.3333333336437742, -0.20000000027939677, + -0.06666666644935806, 0.06666666598369678, 0.19999999981373548, + 0.3333333331781129, 0.46666666607682905, 0.5999999999068677, + 0.7333333332712452, 0.8666666661699612, 1, + ], + [ + -1, -0.8666666666356225, -0.7333333337369065, -0.600000000372529, + -0.46666666654249034, -0.3333333336437742, -0.20000000027939677, + -0.06666666644935806, 0.06666666598369678, 0.19999999981373548, + 0.3333333331781129, 0.46666666607682905, 0.5999999999068677, + 0.7333333332712452, 0.8666666661699612, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT32_MAT4", function () { + const propertyName = "example_UINT32_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, + 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, + 3722304989, 4008636141, 4294967295, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT32_MAT4_array", function () { + const propertyName = "example_variable_length_UINT32_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, + 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, + 3722304989, 4008636141, 4294967295, + ], + [ + 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, + 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, + 3722304989, 4008636141, 4294967295, + ], + [ + 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, + 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, + 3722304989, 4008636141, 4294967295, + ], + [ + 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, + 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, + 3722304989, 4008636141, 4294967295, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT32_MAT4_array", function () { + const propertyName = "example_fixed_length_UINT32_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, + 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, + 3722304989, 4008636141, 4294967295, + ], + [ + 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, + 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, + 3722304989, 4008636141, 4294967295, + ], + [ + 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, + 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, + 3722304989, 4008636141, 4294967295, + ], + [ + 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, + 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, + 3722304989, 4008636141, 4294967295, + ], + [ + 0, 286331153, 572662305, 858993459, 1145324612, 1431655764, 1717986918, + 2004318071, 2290649223, 2576980377, 2863311530, 3149642682, 3435973836, + 3722304989, 4008636141, 4294967295, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT32_MAT4", function () { + const propertyName = "example_normalized_UINT32_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 0.06666666666666667, 0.1333333331005027, 0.2, 0.26666666666666666, + 0.33333333310050267, 0.4, 0.4666666666666667, 0.5333333331005027, 0.6, + 0.6666666666666666, 0.7333333331005026, 0.8, 0.8666666666666667, + 0.9333333331005027, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT32_MAT4_array", function () { + const propertyName = "example_variable_length_normalized_UINT32_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.06666666666666667, 0.1333333331005027, 0.2, 0.26666666666666666, + 0.33333333310050267, 0.4, 0.4666666666666667, 0.5333333331005027, 0.6, + 0.6666666666666666, 0.7333333331005026, 0.8, 0.8666666666666667, + 0.9333333331005027, 1, + ], + [ + 0, 0.06666666666666667, 0.1333333331005027, 0.2, 0.26666666666666666, + 0.33333333310050267, 0.4, 0.4666666666666667, 0.5333333331005027, 0.6, + 0.6666666666666666, 0.7333333331005026, 0.8, 0.8666666666666667, + 0.9333333331005027, 1, + ], + [ + 0, 0.06666666666666667, 0.1333333331005027, 0.2, 0.26666666666666666, + 0.33333333310050267, 0.4, 0.4666666666666667, 0.5333333331005027, 0.6, + 0.6666666666666666, 0.7333333331005026, 0.8, 0.8666666666666667, + 0.9333333331005027, 1, + ], + [ + 0, 0.06666666666666667, 0.1333333331005027, 0.2, 0.26666666666666666, + 0.33333333310050267, 0.4, 0.4666666666666667, 0.5333333331005027, 0.6, + 0.6666666666666666, 0.7333333331005026, 0.8, 0.8666666666666667, + 0.9333333331005027, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT32_MAT4_array", function () { + const propertyName = "example_fixed_length_normalized_UINT32_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.06666666666666667, 0.1333333331005027, 0.2, 0.26666666666666666, + 0.33333333310050267, 0.4, 0.4666666666666667, 0.5333333331005027, 0.6, + 0.6666666666666666, 0.7333333331005026, 0.8, 0.8666666666666667, + 0.9333333331005027, 1, + ], + [ + 0, 0.06666666666666667, 0.1333333331005027, 0.2, 0.26666666666666666, + 0.33333333310050267, 0.4, 0.4666666666666667, 0.5333333331005027, 0.6, + 0.6666666666666666, 0.7333333331005026, 0.8, 0.8666666666666667, + 0.9333333331005027, 1, + ], + [ + 0, 0.06666666666666667, 0.1333333331005027, 0.2, 0.26666666666666666, + 0.33333333310050267, 0.4, 0.4666666666666667, 0.5333333331005027, 0.6, + 0.6666666666666666, 0.7333333331005026, 0.8, 0.8666666666666667, + 0.9333333331005027, 1, + ], + [ + 0, 0.06666666666666667, 0.1333333331005027, 0.2, 0.26666666666666666, + 0.33333333310050267, 0.4, 0.4666666666666667, 0.5333333331005027, 0.6, + 0.6666666666666666, 0.7333333331005026, 0.8, 0.8666666666666667, + 0.9333333331005027, 1, + ], + [ + 0, 0.06666666666666667, 0.1333333331005027, 0.2, 0.26666666666666666, + 0.33333333310050267, 0.4, 0.4666666666666667, 0.5333333331005027, 0.6, + 0.6666666666666666, 0.7333333331005026, 0.8, 0.8666666666666667, + 0.9333333331005027, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_INT64_MAT4", function () { + const propertyName = "example_INT64_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -9223372036854776000n, + -7993589097992581000n, + -6763806160975060000n, + -5534023222112865000n, + -4304240283250670600n, + -3074457346233150000n, + -1844674407370955300n, + -614891468508760200n, + 614891468508760200n, + 1844674407370955300n, + 3074457346233150000n, + 4304240283250670600n, + 5534023222112865000n, + 6763806160975060000n, + 7993589097992581000n, + 9223372036854776000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_INT64_MAT4_array", function () { + const propertyName = "example_variable_length_INT64_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -9223372036854776000n, + -7993589097992581000n, + -6763806160975060000n, + -5534023222112865000n, + -4304240283250670600n, + -3074457346233150000n, + -1844674407370955300n, + -614891468508760200n, + 614891468508760200n, + 1844674407370955300n, + 3074457346233150000n, + 4304240283250670600n, + 5534023222112865000n, + 6763806160975060000n, + 7993589097992581000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -7993589097992581000n, + -6763806160975060000n, + -5534023222112865000n, + -4304240283250670600n, + -3074457346233150000n, + -1844674407370955300n, + -614891468508760200n, + 614891468508760200n, + 1844674407370955300n, + 3074457346233150000n, + 4304240283250670600n, + 5534023222112865000n, + 6763806160975060000n, + 7993589097992581000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -7993589097992581000n, + -6763806160975060000n, + -5534023222112865000n, + -4304240283250670600n, + -3074457346233150000n, + -1844674407370955300n, + -614891468508760200n, + 614891468508760200n, + 1844674407370955300n, + 3074457346233150000n, + 4304240283250670600n, + 5534023222112865000n, + 6763806160975060000n, + 7993589097992581000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -7993589097992581000n, + -6763806160975060000n, + -5534023222112865000n, + -4304240283250670600n, + -3074457346233150000n, + -1844674407370955300n, + -614891468508760200n, + 614891468508760200n, + 1844674407370955300n, + 3074457346233150000n, + 4304240283250670600n, + 5534023222112865000n, + 6763806160975060000n, + 7993589097992581000n, + 9223372036854776000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_INT64_MAT4_array", function () { + const propertyName = "example_fixed_length_INT64_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -9223372036854776000n, + -7993589097992581000n, + -6763806160975060000n, + -5534023222112865000n, + -4304240283250670600n, + -3074457346233150000n, + -1844674407370955300n, + -614891468508760200n, + 614891468508760200n, + 1844674407370955300n, + 3074457346233150000n, + 4304240283250670600n, + 5534023222112865000n, + 6763806160975060000n, + 7993589097992581000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -7993589097992581000n, + -6763806160975060000n, + -5534023222112865000n, + -4304240283250670600n, + -3074457346233150000n, + -1844674407370955300n, + -614891468508760200n, + 614891468508760200n, + 1844674407370955300n, + 3074457346233150000n, + 4304240283250670600n, + 5534023222112865000n, + 6763806160975060000n, + 7993589097992581000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -7993589097992581000n, + -6763806160975060000n, + -5534023222112865000n, + -4304240283250670600n, + -3074457346233150000n, + -1844674407370955300n, + -614891468508760200n, + 614891468508760200n, + 1844674407370955300n, + 3074457346233150000n, + 4304240283250670600n, + 5534023222112865000n, + 6763806160975060000n, + 7993589097992581000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -7993589097992581000n, + -6763806160975060000n, + -5534023222112865000n, + -4304240283250670600n, + -3074457346233150000n, + -1844674407370955300n, + -614891468508760200n, + 614891468508760200n, + 1844674407370955300n, + 3074457346233150000n, + 4304240283250670600n, + 5534023222112865000n, + 6763806160975060000n, + 7993589097992581000n, + 9223372036854776000n, + ], + [ + -9223372036854776000n, + -7993589097992581000n, + -6763806160975060000n, + -5534023222112865000n, + -4304240283250670600n, + -3074457346233150000n, + -1844674407370955300n, + -614891468508760200n, + 614891468508760200n, + 1844674407370955300n, + 3074457346233150000n, + 4304240283250670600n, + 5534023222112865000n, + 6763806160975060000n, + 7993589097992581000n, + 9223372036854776000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_INT64_MAT4", function () { + const propertyName = "example_normalized_INT64_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_INT64_MAT4_array", function () { + const propertyName = "example_variable_length_normalized_INT64_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_INT64_MAT4_array", function () { + const propertyName = "example_fixed_length_normalized_INT64_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_UINT64_MAT4", function () { + const propertyName = "example_UINT64_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, + 1229782938862195000n, + 2459565875879715300n, + 3689348814741910500n, + 4919131753604105000n, + 6148914690621625000n, + 7378697629483821000n, + 8608480568346016000n, + 9838263505363536000n, + 11068046444225730000n, + 12297829383087925000n, + 13527612320105445000n, + 14757395258967642000n, + 15987178197829837000n, + 17216961134847357000n, + 18446744073709552000n, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_UINT64_MAT4_array", function () { + const propertyName = "example_variable_length_UINT64_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, + 1229782938862195000n, + 2459565875879715300n, + 3689348814741910500n, + 4919131753604105000n, + 6148914690621625000n, + 7378697629483821000n, + 8608480568346016000n, + 9838263505363536000n, + 11068046444225730000n, + 12297829383087925000n, + 13527612320105445000n, + 14757395258967642000n, + 15987178197829837000n, + 17216961134847357000n, + 18446744073709552000n, + ], + [ + 0, + 1229782938862195000n, + 2459565875879715300n, + 3689348814741910500n, + 4919131753604105000n, + 6148914690621625000n, + 7378697629483821000n, + 8608480568346016000n, + 9838263505363536000n, + 11068046444225730000n, + 12297829383087925000n, + 13527612320105445000n, + 14757395258967642000n, + 15987178197829837000n, + 17216961134847357000n, + 18446744073709552000n, + ], + [ + 0, + 1229782938862195000n, + 2459565875879715300n, + 3689348814741910500n, + 4919131753604105000n, + 6148914690621625000n, + 7378697629483821000n, + 8608480568346016000n, + 9838263505363536000n, + 11068046444225730000n, + 12297829383087925000n, + 13527612320105445000n, + 14757395258967642000n, + 15987178197829837000n, + 17216961134847357000n, + 18446744073709552000n, + ], + [ + 0, + 1229782938862195000n, + 2459565875879715300n, + 3689348814741910500n, + 4919131753604105000n, + 6148914690621625000n, + 7378697629483821000n, + 8608480568346016000n, + 9838263505363536000n, + 11068046444225730000n, + 12297829383087925000n, + 13527612320105445000n, + 14757395258967642000n, + 15987178197829837000n, + 17216961134847357000n, + 18446744073709552000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_UINT64_MAT4_array", function () { + const propertyName = "example_fixed_length_UINT64_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, + 1229782938862195000n, + 2459565875879715300n, + 3689348814741910500n, + 4919131753604105000n, + 6148914690621625000n, + 7378697629483821000n, + 8608480568346016000n, + 9838263505363536000n, + 11068046444225730000n, + 12297829383087925000n, + 13527612320105445000n, + 14757395258967642000n, + 15987178197829837000n, + 17216961134847357000n, + 18446744073709552000n, + ], + [ + 0, + 1229782938862195000n, + 2459565875879715300n, + 3689348814741910500n, + 4919131753604105000n, + 6148914690621625000n, + 7378697629483821000n, + 8608480568346016000n, + 9838263505363536000n, + 11068046444225730000n, + 12297829383087925000n, + 13527612320105445000n, + 14757395258967642000n, + 15987178197829837000n, + 17216961134847357000n, + 18446744073709552000n, + ], + [ + 0, + 1229782938862195000n, + 2459565875879715300n, + 3689348814741910500n, + 4919131753604105000n, + 6148914690621625000n, + 7378697629483821000n, + 8608480568346016000n, + 9838263505363536000n, + 11068046444225730000n, + 12297829383087925000n, + 13527612320105445000n, + 14757395258967642000n, + 15987178197829837000n, + 17216961134847357000n, + 18446744073709552000n, + ], + [ + 0, + 1229782938862195000n, + 2459565875879715300n, + 3689348814741910500n, + 4919131753604105000n, + 6148914690621625000n, + 7378697629483821000n, + 8608480568346016000n, + 9838263505363536000n, + 11068046444225730000n, + 12297829383087925000n, + 13527612320105445000n, + 14757395258967642000n, + 15987178197829837000n, + 17216961134847357000n, + 18446744073709552000n, + ], + [ + 0, + 1229782938862195000n, + 2459565875879715300n, + 3689348814741910500n, + 4919131753604105000n, + 6148914690621625000n, + 7378697629483821000n, + 8608480568346016000n, + 9838263505363536000n, + 11068046444225730000n, + 12297829383087925000n, + 13527612320105445000n, + 14757395258967642000n, + 15987178197829837000n, + 17216961134847357000n, + 18446744073709552000n, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_normalized_UINT64_MAT4", function () { + const propertyName = "example_normalized_UINT64_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + 0, 0.0666666667, 0.1333333333, 0.2, 0.2666666667, 0.3333333333, 0.4, + 0.4666666667, 0.5333333333, 0.6, 0.6666666667, 0.7333333333, 0.8, + 0.8666666667, 0.9333333333, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_normalized_UINT64_MAT4_array", function () { + const propertyName = "example_variable_length_normalized_UINT64_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.0666666667, 0.1333333333, 0.2, 0.2666666667, 0.3333333333, 0.4, + 0.4666666667, 0.5333333333, 0.6, 0.6666666667, 0.7333333333, 0.8, + 0.8666666667, 0.9333333333, 1, + ], + [ + 0, 0.0666666667, 0.1333333333, 0.2, 0.2666666667, 0.3333333333, 0.4, + 0.4666666667, 0.5333333333, 0.6, 0.6666666667, 0.7333333333, 0.8, + 0.8666666667, 0.9333333333, 1, + ], + [ + 0, 0.0666666667, 0.1333333333, 0.2, 0.2666666667, 0.3333333333, 0.4, + 0.4666666667, 0.5333333333, 0.6, 0.6666666667, 0.7333333333, 0.8, + 0.8666666667, 0.9333333333, 1, + ], + [ + 0, 0.0666666667, 0.1333333333, 0.2, 0.2666666667, 0.3333333333, 0.4, + 0.4666666667, 0.5333333333, 0.6, 0.6666666667, 0.7333333333, 0.8, + 0.8666666667, 0.9333333333, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_normalized_UINT64_MAT4_array", function () { + const propertyName = "example_fixed_length_normalized_UINT64_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + 0, 0.0666666667, 0.1333333333, 0.2, 0.2666666667, 0.3333333333, 0.4, + 0.4666666667, 0.5333333333, 0.6, 0.6666666667, 0.7333333333, 0.8, + 0.8666666667, 0.9333333333, 1, + ], + [ + 0, 0.0666666667, 0.1333333333, 0.2, 0.2666666667, 0.3333333333, 0.4, + 0.4666666667, 0.5333333333, 0.6, 0.6666666667, 0.7333333333, 0.8, + 0.8666666667, 0.9333333333, 1, + ], + [ + 0, 0.0666666667, 0.1333333333, 0.2, 0.2666666667, 0.3333333333, 0.4, + 0.4666666667, 0.5333333333, 0.6, 0.6666666667, 0.7333333333, 0.8, + 0.8666666667, 0.9333333333, 1, + ], + [ + 0, 0.0666666667, 0.1333333333, 0.2, 0.2666666667, 0.3333333333, 0.4, + 0.4666666667, 0.5333333333, 0.6, 0.6666666667, 0.7333333333, 0.8, + 0.8666666667, 0.9333333333, 1, + ], + [ + 0, 0.0666666667, 0.1333333333, 0.2, 0.2666666667, 0.3333333333, 0.4, + 0.4666666667, 0.5333333333, 0.6, 0.6666666667, 0.7333333333, 0.8, + 0.8666666667, 0.9333333333, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT32_MAT4", function () { + const propertyName = "example_FLOAT32_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT32_MAT4_array", function () { + const propertyName = "example_variable_length_FLOAT32_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT32_MAT4_array", function () { + const propertyName = "example_fixed_length_FLOAT32_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_FLOAT64_MAT4", function () { + const propertyName = "example_FLOAT64_MAT4"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_variable_length_FLOAT64_MAT4_array", function () { + const propertyName = "example_variable_length_FLOAT64_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); + + it("obtains the right value for example_fixed_length_FLOAT64_MAT4_array", function () { + const propertyName = "example_fixed_length_FLOAT64_MAT4_array"; + const value = metadataEntityModel.getPropertyValue(propertyName); + const expected = [ + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + [ + -1, -0.8666666666, -0.7333333334, -0.6, -0.4666666666, -0.3333333334, + -0.2, -0.0666666666, 0.0666666666, 0.2, 0.3333333334, 0.4666666666, 0.6, + 0.7333333334, 0.8666666666, 1, + ], + ]; + expect(genericEquals(value, expected, epsilon)).toBeTrue(); + }); +}); diff --git a/specs/metadata/MetadataEntityModelSpec.ts b/specs/metadata/MetadataEntityModelSpec.ts new file mode 100644 index 00000000..001a01c8 --- /dev/null +++ b/specs/metadata/MetadataEntityModelSpec.ts @@ -0,0 +1,253 @@ +import { MetadataEntityModels } from "../../src/metadata/MetadataEntityModels"; +import { MetadataClass } from "../../src/structure/Metadata/MetadataClass"; + +// NOTE: The tests here aim at testing the handling of default- and noData +// values, as well as the handling of offset/scale and the mechanism of +// overriding the offset/scale from the property definition with an +// offset/scale in the entity itself. The values are chosen to not +// require any "epslion" of the comparisons. + +describe("metadata/MetadataEntityModel", function () { + it("throws when the value of an unknown property is accessed", function () { + expect(function () { + const testMetadataClass: MetadataClass = { + properties: {}, + }; + const entityJson = { + testProperty: 1234, + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + entity.getPropertyValue("testProperty"); + }).toThrow(); + }); + + it("obtains a default value for a scalar int32 value", function () { + const testMetadataClass: MetadataClass = { + properties: { + testProperty: { + type: "SCALAR", + componentType: "INT32", + default: 1234, + }, + }, + }; + const entityJson = { + testProperty: undefined, + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + const value = entity.getPropertyValue("testProperty"); + expect(value).toBe(1234); + }); + + it("obtains a default value for a scalar int32 noData value", function () { + const testMetadataClass: MetadataClass = { + properties: { + testProperty: { + type: "SCALAR", + componentType: "INT32", + noData: 2345, + default: 1234, + }, + }, + }; + const entityJson = { + testProperty: 2345, + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + const value = entity.getPropertyValue("testProperty"); + expect(value).toBe(1234); + }); + + it("obtains a value for a vec3 float32 value with offset in property definition", function () { + const testMetadataClass: MetadataClass = { + properties: { + testProperty: { + type: "VEC3", + componentType: "FLOAT32", + offset: [1.0, 2.0, 3.0], + }, + }, + }; + const entityJson = { + testProperty: [3.0, 4.0, 5.0], + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + const value = entity.getPropertyValue("testProperty"); + const expected = [4.0, 6.0, 8.0]; + expect(value).toEqual(expected); + }); + + it("obtains a value for a vec3 float32 value with scale in property definition", function () { + const testMetadataClass: MetadataClass = { + properties: { + testProperty: { + type: "VEC3", + componentType: "FLOAT32", + scale: [2.0, 3.0, 4.0], + }, + }, + }; + const entityJson = { + testProperty: [3.0, 4.0, 5.0], + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + const value = entity.getPropertyValue("testProperty"); + const expected = [6.0, 12.0, 20.0]; + expect(value).toEqual(expected); + }); + + it("obtains a value for a vec3 float32 value with offset and scale in property definition", function () { + const testMetadataClass: MetadataClass = { + properties: { + testProperty: { + type: "VEC3", + componentType: "FLOAT32", + offset: [1.0, 2.0, 3.0], + scale: [2.0, 3.0, 4.0], + }, + }, + }; + const entityJson = { + testProperty: [3.0, 4.0, 5.0], + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + const value = entity.getPropertyValue("testProperty"); + const expected = [7.0, 14.0, 23.0]; + expect(value).toEqual(expected); + }); + + it("obtains a value for a vec3 float32 value with offset in property definition and scale in entity", function () { + const testMetadataClass: MetadataClass = { + properties: { + testProperty: { + type: "VEC3", + componentType: "FLOAT32", + offset: [1.0, 2.0, 3.0], + }, + }, + }; + const entityJson = { + testProperty: [3.0, 4.0, 5.0], + scale: [2.0, 3.0, 4.0], + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + const value = entity.getPropertyValue("testProperty"); + const expected = [7.0, 14.0, 23.0]; + expect(value).toEqual(expected); + }); + + it("obtains a value for a vec3 float32 value with offset in entity and scale in property definition", function () { + const testMetadataClass: MetadataClass = { + properties: { + testProperty: { + type: "VEC3", + componentType: "FLOAT32", + scale: [2.0, 3.0, 4.0], + }, + }, + }; + const entityJson = { + testProperty: [3.0, 4.0, 5.0], + offset: [1.0, 2.0, 3.0], + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + const value = entity.getPropertyValue("testProperty"); + const expected = [7.0, 14.0, 23.0]; + expect(value).toEqual(expected); + }); + + it("obtains a value for a vec3 float32 value with offset in property definition, and overriding offset in entity", function () { + const testMetadataClass: MetadataClass = { + properties: { + testProperty: { + type: "VEC3", + componentType: "FLOAT32", + offset: [100.0, 200.0, 300.0], + }, + }, + }; + const entityJson = { + testProperty: [3.0, 4.0, 5.0], + offset: [1.0, 2.0, 3.0], + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + const value = entity.getPropertyValue("testProperty"); + const expected = [4.0, 6.0, 8.0]; + expect(value).toEqual(expected); + }); + + it("obtains a value for a vec3 float32 value with scale in property definition, and overriding scale in entity", function () { + const testMetadataClass: MetadataClass = { + properties: { + testProperty: { + type: "VEC3", + componentType: "FLOAT32", + scale: [200.0, 300.0, 400.0], + }, + }, + }; + const entityJson = { + testProperty: [3.0, 4.0, 5.0], + scale: [2.0, 3.0, 4.0], + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + const value = entity.getPropertyValue("testProperty"); + const expected = [6.0, 12.0, 20.0]; + expect(value).toEqual(expected); + }); + + it("obtains a value for a vec3 float32 value with offset and scale in property definition, and overriding offset and scale in entity", function () { + const testMetadataClass: MetadataClass = { + properties: { + testProperty: { + type: "VEC3", + componentType: "FLOAT32", + offset: [100.0, 200.0, 300.0], + scale: [200.0, 300.0, 400.0], + }, + }, + }; + const entityJson = { + testProperty: [3.0, 4.0, 5.0], + offset: [1.0, 2.0, 3.0], + scale: [2.0, 3.0, 4.0], + }; + const entity = MetadataEntityModels.createFromClass( + testMetadataClass, + entityJson + ); + const value = entity.getPropertyValue("testProperty"); + const expected = [7.0, 14.0, 23.0]; + expect(value).toEqual(expected); + }); +}); diff --git a/specs/metadata/PropertyTableModelsSpec.ts b/specs/metadata/PropertyTableModelsSpec.ts new file mode 100644 index 00000000..b4e30538 --- /dev/null +++ b/specs/metadata/PropertyTableModelsSpec.ts @@ -0,0 +1,541 @@ +import { genericEquals } from "./genericEquals"; + +import { BinaryPropertyTables } from "../../src/metadata/binary/BinaryPropertyTables"; +import { BinaryPropertyTableModel } from "../../src/metadata/binary/BinaryPropertyTableModel"; + +import { ClassProperty } from "../../src/structure/Metadata/ClassProperty"; + +/** + * Test for the `PropertyTableModels` class. + * + * These tests just verify the "roundtrip": + * - They create a `PropertyTableModel` from a single property + * and its associated data + * - They obtain the `MetadataEntityModel` instances from the + * property table model + * - They check whether the elements of the input data and the + * values from the entity model are generically equal. + */ +describe("metadata/PropertyTableModelSpec", function () { + const epsilon = 0.000001; + + it("correctly represents example_INT16_SCALAR", function () { + const example_INT16_SCALAR: ClassProperty = { + type: "SCALAR", + componentType: "INT16", + }; + const example_INT16_SCALAR_values = [-32768, 32767]; + + const classProperty = example_INT16_SCALAR; + const values = example_INT16_SCALAR_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_variable_length_INT16_SCALAR_array", function () { + const example_variable_length_INT16_SCALAR_array: ClassProperty = { + type: "SCALAR", + componentType: "INT16", + array: true, + }; + const example_variable_length_INT16_SCALAR_array_values = [ + [-32768, 32767], + [-1, 0, 1], + ]; + + const classProperty = example_variable_length_INT16_SCALAR_array; + const values = example_variable_length_INT16_SCALAR_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_fixed_length_INT16_SCALAR_array", function () { + const example_fixed_length_INT16_SCALAR_array: ClassProperty = { + type: "SCALAR", + componentType: "INT16", + array: true, + count: 2, + }; + const example_fixed_length_INT16_SCALAR_array_values = [ + [-32768, 32767], + [-1, 1], + ]; + + const classProperty = example_fixed_length_INT16_SCALAR_array; + const values = example_fixed_length_INT16_SCALAR_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_BOOLEAN", function () { + const example_BOOLEAN: ClassProperty = { + type: "BOOLEAN", + }; + const example_BOOLEAN_values = [true, false]; + + const classProperty = example_BOOLEAN; + const values = example_BOOLEAN_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_variable_length_BOOLEAN_array", function () { + const example_variable_length_BOOLEAN_array: ClassProperty = { + type: "BOOLEAN", + array: true, + }; + const example_variable_length_BOOLEAN_array_values = [ + [true, false], + [false, true, false, true], + ]; + + const classProperty = example_variable_length_BOOLEAN_array; + const values = example_variable_length_BOOLEAN_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_fixed_length_BOOLEAN_array", function () { + const example_fixed_length_BOOLEAN_array: ClassProperty = { + type: "BOOLEAN", + array: true, + }; + const example_fixed_length_BOOLEAN_array_values = [ + [true, false, true], + [false, true, false], + ]; + + const classProperty = example_fixed_length_BOOLEAN_array; + const values = example_fixed_length_BOOLEAN_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_STRING", function () { + const example_STRING: ClassProperty = { + type: "STRING", + }; + const example_STRING_values = ["Test string", "Another string"]; + + const classProperty = example_STRING; + const values = example_STRING_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_variable_length_STRING_array", function () { + const example_variable_length_STRING_array: ClassProperty = { + type: "STRING", + array: true, + }; + const example_variable_length_STRING_array_values = [ + ["A0", "A1", "A2"], + ["B0", "B1"], + ]; + + const classProperty = example_variable_length_STRING_array; + const values = example_variable_length_STRING_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_fixed_length_STRING_array", function () { + const example_fixed_length_STRING_array: ClassProperty = { + type: "STRING", + array: true, + count: 3, + }; + const example_fixed_length_STRING_array_values = [ + ["A0", "A1", "A2"], + ["B0", "B1", "B2"], + ]; + + const classProperty = example_fixed_length_STRING_array; + const values = example_fixed_length_STRING_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_FLOAT32_VEC2", function () { + const example_FLOAT32_VEC2: ClassProperty = { + type: "VEC2", + componentType: "FLOAT32", + }; + const example_FLOAT32_VEC2_values = [ + [0.0, 1.0], + [2.0, 3.0], + ]; + + const classProperty = example_FLOAT32_VEC2; + const values = example_FLOAT32_VEC2_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_variable_length_UINT32_VEC2_array", function () { + const example_variable_length_UINT32_VEC2_array: ClassProperty = { + type: "VEC2", + componentType: "FLOAT32", + array: true, + }; + const example_variable_length_UINT32_VEC2_array_values = [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [4.0, 5.0], + [6.0, 7.0], + [8.0, 9.0], + ], + ]; + + const classProperty = example_variable_length_UINT32_VEC2_array; + const values = example_variable_length_UINT32_VEC2_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_fixed_length_UINT32_VEC2_array", function () { + const example_fixed_length_UINT32_VEC2_array: ClassProperty = { + type: "VEC2", + componentType: "FLOAT32", + array: true, + count: 2, + }; + const example_fixed_length_UINT32_VEC2_array_values = [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [4.0, 5.0], + [6.0, 7.0], + ], + ]; + + const classProperty = example_fixed_length_UINT32_VEC2_array; + const values = example_fixed_length_UINT32_VEC2_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_UINT64_MAT2", function () { + const example_UINT64_MAT2: ClassProperty = { + type: "MAT2", + componentType: "UINT64", + }; + const example_UINT64_MAT2_values = [ + [0n, 6148914690621625735n, 12297829383087925879n, 18446744073709551615n], + [18446744073709551615n, 12297829383087925879n, 6148914690621625735n, 0n], + ]; + + const classProperty = example_UINT64_MAT2; + const values = example_UINT64_MAT2_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = values[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); + + it("correctly represents example_normalized_UINT64_VEC2 with offset", function () { + const example_normalized_UINT64_VEC2: ClassProperty = { + type: "VEC2", + componentType: "UINT64", + normalized: true, + offset: [10, 20], + }; + const example_normalized_UINT64_VEC2_values = [ + [0n, 6148914690621625735n], + [6148914690621625735n, 12297829383087925879n], + ]; + const example_normalized_UINT64_VEC2_values_expected = [ + [10.0, 20.3333333333], + [10.3333333333, 20.6666666666], + ]; + + const classProperty = example_normalized_UINT64_VEC2; + const values = example_normalized_UINT64_VEC2_values; + const valuesExpected = example_normalized_UINT64_VEC2_values_expected; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + const propertyTableModel = new BinaryPropertyTableModel( + binaryPropertyTable + ); + const count = values.length; + for (let i = 0; i < count; i++) { + const entity = propertyTableModel.getMetadataEntityModel(i); + const expected = valuesExpected[i]; + const actual = entity.getPropertyValue("testProperty"); + expect(genericEquals(actual, expected, epsilon)).toBeTrue(); + } + }); +}); diff --git a/specs/metadata/PropertyTableTestUtilities.ts b/specs/metadata/PropertyTableTestUtilities.ts new file mode 100644 index 00000000..5b026a3b --- /dev/null +++ b/specs/metadata/PropertyTableTestUtilities.ts @@ -0,0 +1,579 @@ +import { BinaryPropertyTable } from "../../src/metadata/binary/BinaryPropertyTable"; +import { BinaryPropertyTables } from "../../src/metadata/binary/BinaryPropertyTables"; + +import { ClassProperty } from "../../src/structure/Metadata/ClassProperty"; +import { MetadataEnum } from "../../src/structure/Metadata/MetadataEnum"; + +/** + * Methods to create `BinaryPropertyTable` instances that are valid, + * and contain the data for a single property. + * + * These methods are used in the `BinaryPropertyTableValidationSpec.ts` + * to create valid `BinaryPropertyTable` instances, that are then made + * invalid in various ways in order to check whether these errors are + * detected asvalidation issues. + * + * @internal + */ +export class PropertyTableTestUtilities { + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_INT16_SCALAR(): BinaryPropertyTable { + const example_INT16_SCALAR: ClassProperty = { + type: "SCALAR", + componentType: "INT16", + }; + const example_INT16_SCALAR_values = [-32768, 32767]; + + const classProperty = example_INT16_SCALAR; + const values = example_INT16_SCALAR_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_variable_length_INT16_SCALAR_array(): BinaryPropertyTable { + const example_variable_length_INT16_SCALAR_array: ClassProperty = { + type: "SCALAR", + componentType: "INT16", + array: true, + }; + const example_variable_length_INT16_SCALAR_array_values = [ + [-32768, 32767], + [-1, 0, 1], + ]; + + const classProperty = example_variable_length_INT16_SCALAR_array; + const values = example_variable_length_INT16_SCALAR_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_fixed_length_INT16_SCALAR_array(): BinaryPropertyTable { + const example_fixed_length_INT16_SCALAR_array: ClassProperty = { + type: "SCALAR", + componentType: "INT16", + array: true, + count: 2, + }; + const example_fixed_length_INT16_SCALAR_array_values = [ + [-32768, 32767], + [-1, 1], + ]; + + const classProperty = example_fixed_length_INT16_SCALAR_array; + const values = example_fixed_length_INT16_SCALAR_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_BOOLEAN(): BinaryPropertyTable { + const example_BOOLEAN: ClassProperty = { + type: "BOOLEAN", + }; + const example_BOOLEAN_values = [true, false]; + + const classProperty = example_BOOLEAN; + const values = example_BOOLEAN_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_variable_length_BOOLEAN_array(): BinaryPropertyTable { + const example_variable_length_BOOLEAN_array: ClassProperty = { + type: "BOOLEAN", + array: true, + }; + const example_variable_length_BOOLEAN_array_values = [ + [true, false], + [false, true, false, true], + ]; + + const classProperty = example_variable_length_BOOLEAN_array; + const values = example_variable_length_BOOLEAN_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_fixed_length_BOOLEAN_array(): BinaryPropertyTable { + const example_fixed_length_BOOLEAN_array: ClassProperty = { + type: "BOOLEAN", + array: true, + }; + const example_fixed_length_BOOLEAN_array_values = [ + [true, false, true], + [false, true, false], + ]; + + const classProperty = example_fixed_length_BOOLEAN_array; + const values = example_fixed_length_BOOLEAN_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_STRING(): BinaryPropertyTable { + const example_STRING: ClassProperty = { + type: "STRING", + }; + const example_STRING_values = ["Test string", "Another string"]; + + const classProperty = example_STRING; + const values = example_STRING_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_variable_length_STRING_array(): BinaryPropertyTable { + const example_variable_length_STRING_array: ClassProperty = { + type: "STRING", + array: true, + }; + const example_variable_length_STRING_array_values = [ + ["A0", "A1", "A2"], + ["B0", "B1"], + ]; + + const classProperty = example_variable_length_STRING_array; + const values = example_variable_length_STRING_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_fixed_length_STRING_array(): BinaryPropertyTable { + const example_fixed_length_STRING_array: ClassProperty = { + type: "STRING", + array: true, + count: 3, + }; + const example_fixed_length_STRING_array_values = [ + ["A0", "A1", "A2"], + ["B0", "B1", "B2"], + ]; + + const classProperty = example_fixed_length_STRING_array; + const values = example_fixed_length_STRING_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_FLOAT32_VEC2(): BinaryPropertyTable { + const example_FLOAT32_VEC2: ClassProperty = { + type: "VEC2", + componentType: "FLOAT32", + }; + const example_FLOAT32_VEC2_values = [ + [0.0, 1.0], + [2.0, 3.0], + ]; + + const classProperty = example_FLOAT32_VEC2; + const values = example_FLOAT32_VEC2_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_variable_length_UINT32_VEC2_array(): BinaryPropertyTable { + const example_variable_length_UINT32_VEC2_array: ClassProperty = { + type: "VEC2", + componentType: "FLOAT32", + array: true, + }; + const example_variable_length_UINT32_VEC2_array_values = [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [4.0, 5.0], + [6.0, 7.0], + [8.0, 9.0], + ], + ]; + + const classProperty = example_variable_length_UINT32_VEC2_array; + const values = example_variable_length_UINT32_VEC2_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_fixed_length_UINT32_VEC2_array(): BinaryPropertyTable { + const example_fixed_length_UINT32_VEC2_array: ClassProperty = { + type: "VEC2", + componentType: "FLOAT32", + array: true, + count: 2, + }; + const example_fixed_length_UINT32_VEC2_array_values = [ + [ + [0.0, 1.0], + [2.0, 3.0], + ], + [ + [4.0, 5.0], + [6.0, 7.0], + ], + ]; + + const classProperty = example_fixed_length_UINT32_VEC2_array; + const values = example_fixed_length_UINT32_VEC2_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_variable_length_ENUM_array(): BinaryPropertyTable { + const example_variable_length_ENUM_array: ClassProperty = { + type: "ENUM", + enumType: "testMetadataEnum", + array: true, + }; + const example_example_variable_length_ENUM_array_values = [ + ["ExampleEnumValueA", "ExampleEnumValueB", "ExampleEnumValueC"], + ["ExampleEnumValueB", "ExampleEnumValueA"], + ]; + const testMetadataEnum: MetadataEnum = { + values: [ + { + name: "ExampleEnumValueA", + value: 0, + }, + { + name: "ExampleEnumValueB", + value: 1, + }, + { + name: "ExampleEnumValueC", + value: 2, + }, + ], + }; + + const classProperty = example_variable_length_ENUM_array; + const values = example_example_variable_length_ENUM_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + testMetadataEnum + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_ENUM_with_noData(): BinaryPropertyTable { + const example_ENUM: ClassProperty = { + type: "ENUM", + enumType: "testMetadataEnum", + noData: 9999, + default: 1, + }; + const example_ENUM_values = [ + "ExampleEnumValueA", + "ExampleEnumValueB", + "ExampleEnumValueC", + ]; + const testMetadataEnum: MetadataEnum = { + values: [ + { + name: "ExampleEnumValueA", + value: 0, + }, + { + name: "ExampleEnumValueB", + value: 1, + }, + { + name: "ExampleEnumValueC", + value: 2, + }, + ], + }; + + const classProperty = example_ENUM; + const values = example_ENUM_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + testMetadataEnum + ); + return binaryPropertyTable; + } + + /** + * Creates an unspecified valid default `BinaryPropertyTable`, containing + * a single property with the type indicated in the method name. + * + * @returns The `BinaryPropertyTable` + */ + static createDefaultBinaryPropertyTable_example_fixed_length_normalized_INT64_VEC2_array(): BinaryPropertyTable { + const example_fixed_length_normalized_INT64_VEC2_array: ClassProperty = { + type: "VEC2", + componentType: "INT64", + array: true, + normalized: true, + }; + // The normalized values here are + // [ + // [ 0, 0] + // [-1, 0] + // [ 0, -1] + // ], + // [ + // [ 0, 0] + // [ 1, 0] + // [ 0, 1] + // ] + const example_fixed_length_normalized_INT64_VEC2_array_values = [ + [ + [0, 0], + [-9223372036854775808n, 0], + [0, -9223372036854775808n], + ], + [ + [0, 0], + [9223372036854775807n, 0], + [0, 9223372036854775807n], + ], + ]; + + const classProperty = example_fixed_length_normalized_INT64_VEC2_array; + const values = example_fixed_length_normalized_INT64_VEC2_array_values; + + const arrayOffsetType = "UINT32"; + const stringOffsetType = "UINT32"; + const binaryPropertyTable = + BinaryPropertyTables.createBinaryPropertyTableFromProperty( + "testProperty", + classProperty, + values, + arrayOffsetType, + stringOffsetType, + undefined + ); + return binaryPropertyTable; + } +} diff --git a/specs/metadata/genericEquals.ts b/specs/metadata/genericEquals.ts new file mode 100644 index 00000000..d74b98df --- /dev/null +++ b/specs/metadata/genericEquals.ts @@ -0,0 +1,58 @@ +import { defaultValue } from "../../src/base/defaultValue"; + +/** + * Returns whether two numbers are equal, up to a certain epsilon + * + * @param left - The first value + * @param right - The second value + * @param relativeEpsilon - The maximum inclusive delta for the relative tolerance test. + * @param absoluteEpsilon - The maximum inclusive delta for the absolute tolerance test. + * @returns Whether the values are equal within the epsilon + */ +function equalsEpsilon( + left: number, + right: number, + relativeEpsilon: number, + absoluteEpsilon?: number +) { + relativeEpsilon = defaultValue(relativeEpsilon, 0.0); + absoluteEpsilon = defaultValue(absoluteEpsilon, relativeEpsilon); + const absDiff = Math.abs(left - right); + return ( + absDiff <= absoluteEpsilon || + absDiff <= relativeEpsilon * Math.max(Math.abs(left), Math.abs(right)) + ); +} + +/** + * A function for checking values for equality, taking into account the + * possibility that the values are (potentially multi- dimensional) + * arrays, and recursively comparing the elements in this case. + * If the eventual elements are numbers, they are compared for + * equality up to the given relative epsilon. + * + * This is ONLY used in the specs, to compare metadata values. + * + * @param a - The first element + * @param b - The second element + * @param epsilon - A relative epsilon + * @returns Whether the objects are equal + */ +export function genericEquals(a: any, b: any, epsilon: number): boolean { + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) { + return false; + } + + for (let i = 0; i < a.length; ++i) { + if (!genericEquals(a[i], b[i], epsilon)) { + return false; + } + } + return true; + } + if (typeof a === "number") { + return equalsEpsilon(a, Number(b), epsilon); + } + return a === b; +} diff --git a/specs/metadata/readJsonUnchecked.ts b/specs/metadata/readJsonUnchecked.ts new file mode 100644 index 00000000..6596ea65 --- /dev/null +++ b/specs/metadata/readJsonUnchecked.ts @@ -0,0 +1,27 @@ +import fs from "fs"; + +/** + * Only for internal use and basic tests: + * + * Reads a JSON file, parses it, and returns the result. + * If the file cannot be read or parsed, then an error + * message will be printed and `undefined` is returned. + * + * @param filePath - The path to the file + * @returns A promise that resolves with the result or `undefined` + */ +export async function readJsonUnchecked(filePath: string): Promise { + try { + const data = fs.readFileSync(filePath); + if (!data) { + console.error("Could not read " + filePath); + return undefined; + } + const jsonString = data.toString(); + const result = JSON.parse(jsonString); + return result; + } catch (error) { + console.error("Could not parse JSON", error); + return undefined; + } +} diff --git a/src/metadata/ArrayValues.ts b/src/metadata/ArrayValues.ts index f5737de9..2829f43f 100644 --- a/src/metadata/ArrayValues.ts +++ b/src/metadata/ArrayValues.ts @@ -1,10 +1,31 @@ -import { defined } from "../base/defined"; -import { defaultValue } from "../base/defaultValue"; +import { MetadataError } from "./MetadataError"; + +/** + * Type definition for numeric values (number or bigint) + */ +type NumericScalar = number | bigint; + +/** + * Type definition for numeric values or n-dimensional + * arrays of numeric values. + */ +type NumericND = NumericScalar | NumericND[]; + +/** + * A type definition for numbers + */ +type NumberScalar = number; + +/** + * A type definition for numbers or n-dimensional + * arrays of numbers + */ +type NumberND = NumberScalar | NumberND[]; /** * Utility functions for generic operations on values that - * may be numbers or bigints or (potentially multi-dimensional) - * arrays of numbers or bigints. + * may be numbers or (potentially multi-dimensional) arrays + * of numbers. * * These methods are mainly used for performing operations * on metadata values that have been found to be numeric @@ -13,198 +34,248 @@ import { defaultValue } from "../base/defaultValue"; * * When two values are involved, then the methods assume * that the values have the same structure, i.e. they are - * both numeric, or arrays with the same length. + * both numeric/numbers or arrays with the same length. If this + * is not the case, then a `MetadataError` will be thrown. */ export class ArrayValues { // Implementation note: - // The way how these functions are written is drilling a hole - // into the type checks that COULD be offered by TypeScript. - // However, defining these methods in a type-safe way is fairly - // non-trivial, and (more importantly) would require the proper - // type checks to be done by the caller. + // The methods here are supposed to be called in a context + // where no (compile-time) type information is available. + // Thes are offered to operate on "any" types, but usually + // delegate to "...Internal" methods with more specific + // type signatures. This does not imply any compile-time + // checks, but these specific signatures might be exposed + // as public methods at some point. /** - * Multiplies the given input value with the given factor. - * - * If the factor is undefined, then the original value - * is returned. + * Returns whether the given value is a Numeric scalar + * (i.e. number or bigint) * - * This considers the case that the values are numbers or - * (potentially multi-dimensional) arrays of numbers. + * @param value - The value + * @returns Whether the value is Numeric + */ + private static isNumericScalar(value: any): value is NumericScalar { + if (typeof value === "number") { + return true; + } + if (typeof value === "bigint") { + return true; + } + return true; + } + + /** + * Multiplies the given input value with the given factor. * * @param value - The input value - * @param factor - The optional factor + * @param factor - The factor * @returns The resulting value + * @throws MetadataError If the parameters have incompatible types */ static deepMultiply(value: any, factor: any): any { - if (!defined(factor)) { - return value; - } - const f = factor as any; - if (!Array.isArray(value)) { - return value * f; + return ArrayValues.deepMultiplyInternal(value, factor); + } + + /** + * Multiplies the given input value with the given factor. + * + * @param value - The input value + * @param factor - The factor + * @returns The resulting value + * @throws MetadataError If the parameters have incompatible types + */ + private static deepMultiplyInternal( + value: T, + factor: T + ): T { + if (Array.isArray(value) && Array.isArray(factor)) { + if (value.length != factor.length) { + throw new MetadataError( + `Values ${value} and ${factor} have different lengths` + ); + } + const result = value.slice(); + for (let i = 0; i < result.length; i++) { + result[i] = ArrayValues.deepMultiplyInternal(result[i], factor[i]); + } + return result as T; } - for (let i = 0; i < value.length; i++) { - value[i] = ArrayValues.deepMultiply(value[i], f[i]); + if (typeof value === "number" && typeof factor === "number") { + return (value * factor) as T; } - return value; + throw new MetadataError( + `Values ${value} and ${factor} have invalid ` + + `types ${typeof value} and ${typeof factor}` + ); } /** * Adds the given addend to the given input value. * - * If the addend is undefined, then the original value - * is returned. - * - * This considers the case that the values are numbers or - * (potentially multi-dimensional) arrays of numbers. - * * @param value - The input value * @param addend - The optional addend * @returns The resulting value + * @throws MetadataError If the parameters have incompatible types */ static deepAdd(value: any, addend: any): any { - if (!defined(addend)) { - return value; - } - if (!Array.isArray(value)) { - return value + addend; + return ArrayValues.deepAddInternal(value, addend); + } + + /** + * Adds the given addend to the given input value. + * + * @param value - The input value + * @param addend - The optional addend + * @returns The resulting value + * @throws MetadataError If the parameters have incompatible types + */ + private static deepAddInternal(value: T, addend: T): T { + if (Array.isArray(value) && Array.isArray(addend)) { + if (value.length != addend.length) { + throw new MetadataError( + `Values ${value} and ${addend} have different lengths` + ); + } + const result = value.slice(); + for (let i = 0; i < result.length; i++) { + result[i] = ArrayValues.deepAddInternal(result[i], addend[i]); + } + return result as T; } - const a = addend as any; - for (let i = 0; i < value.length; i++) { - value[i] = ArrayValues.deepAdd(value[i], a[i]); + if (typeof value === "number" && typeof addend === "number") { + return (value + addend) as T; } - return value; + throw new MetadataError( + `Values ${value} and ${addend} have invalid ` + + `types ${typeof value} and ${typeof addend}` + ); } /** * Computes the minimum of the given values. * - * This considers the case that the values are numbers or - * (potentially multi-dimensional) arrays of numbers, - * and computes the component-wise minimum. + * For arrays, it computes the component-wise minimum. * * @param a - The first value - * @param b - THe second value + * @param b - The second value * @returns The mimimum value + * @throws MetadataError If the parameters have incompatible types */ static deepMin(a: any, b: any): any { + return ArrayValues.deepMinInternal(a, b); + } + + /** + * Computes the minimum of the given values. + * + * For arrays, it computes the component-wise minimum. + * + * @param a - The first value + * @param b - The second value + * @returns The mimimum value + * @throws MetadataError If the parameters have incompatible types + */ + private static deepMinInternal(a: T, b: T): T { if (Array.isArray(a) && Array.isArray(b)) { + if (a.length != b.length) { + throw new MetadataError(`Values ${a} and ${b} have different lengths`); + } const result = a.slice(); for (let i = 0; i < a.length; ++i) { - result[i] = ArrayValues.deepMin(a[i], b[i]); + result[i] = ArrayValues.deepMinInternal(a[i], b[i]); } - return result; + return result as T; + } + if (ArrayValues.isNumericScalar(a) && ArrayValues.isNumericScalar(b)) { + return a < b ? a : b; } - return Math.min(a, b); + throw new MetadataError( + `Values ${a} and ${b} have invalid ` + `types ${typeof a} and ${typeof b}` + ); } /** * Computes the maximum of the given values. * - * This considers the case that the values are numbers or - * (potentially multi-dimensional) arrays of numbers, - * and computes the component-wise maximum. + * For arrays, it computes the component-wise maximum. * * @param a - The first value - * @param b - THe second value + * @param b - The second value * @returns The maximum value + * @throws MetadataError If the parameters have incompatible types */ static deepMax(a: any, b: any): any { - if (Array.isArray(a) && Array.isArray(b)) { - const result = a.slice(); - for (let i = 0; i < a.length; ++i) { - result[i] = ArrayValues.deepMax(a[i], b[i]); - } - return result; - } - return Math.max(a, b); + return ArrayValues.deepMaxInternal(a, b); } /** - * Returns whether the given values are equal up to the - * give relative epsilon. + * Computes the maximum of the given values. * - * This considers the case that the values are numbers or - * (potentially multi-dimensional) arrays of numbers. + * For arrays, it computes the component-wise maximum. * - * @param a - The first element - * @param b - The second element - * @param epsilon - A relative epsilon - * @returns Whether the objects are equal + * @param a - The first value + * @param b - The second value + * @returns The maximum value + * @throws MetadataError If the parameters have incompatible types */ - static deepEqualsEpsilon(a: any, b: any, epsilon: number): boolean { + private static deepMaxInternal(a: T, b: T): T { if (Array.isArray(a) && Array.isArray(b)) { - if (a.length !== b.length) { - return false; - } + const result = a.slice(); for (let i = 0; i < a.length; ++i) { - if (!ArrayValues.deepEqualsEpsilon(a[i], b[i], epsilon)) { - return false; - } + result[i] = ArrayValues.deepMaxInternal(a[i], b[i]); } - return true; + return result as T; } - return ArrayValues.equalsEpsilon(a, b, epsilon); + if (ArrayValues.isNumericScalar(a) && ArrayValues.isNumericScalar(b)) { + return a > b ? a : b; + } + throw new MetadataError( + `Values ${a} and ${b} have invalid ` + `types ${typeof a} and ${typeof b}` + ); } /** - * From CesiumJS: + * Checks whether two values are equal. * - * Returns whether two numbers are equal, up to a certain epsilon + * This is only supposed to be used for scalars (number or bigint) + * or (potentially multi-dimensional) arrays of scalars. * - * @param left - The first value - * @param right - The second value - * @param relativeEpsilon - The maximum inclusive delta for the relative tolerance test. - * @param absoluteEpsilon - The maximum inclusive delta for the absolute tolerance test. - * @returns Whether the values are equal within the epsilon + * @param a - The first value + * @param b - The second value + * @returns Whether the values are equal */ - private static equalsEpsilon( - left: number, - right: number, - relativeEpsilon: number, - absoluteEpsilon?: number - ) { - relativeEpsilon = defaultValue(relativeEpsilon, 0.0); - absoluteEpsilon = defaultValue(absoluteEpsilon, relativeEpsilon); - const absDiff = Math.abs(left - right); - return ( - absDiff <= absoluteEpsilon || - absDiff <= relativeEpsilon * Math.max(Math.abs(left), Math.abs(right)) - ); + static deepEquals(a: any, b: any): boolean { + return ArrayValues.deepEqualsInternal(a, b); } /** * Checks whether two values are equal. * - * This considers the case that the values are numbers or - * (potentially multi-dimensional) arrays of numbers. - * * @param a - The first value * @param b - The second value * @returns Whether the values are equal */ - static deepEquals(a: any, b: any) { + private static deepEqualsInternal(a: T, b: T): boolean { if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { - if (!ArrayValues.deepEquals(a[i], b[i])) { + if (!ArrayValues.deepEqualsInternal(a[i], b[i])) { return false; } } return true; } - return a === b; + // Do a loose comparison, for the case of mixing bigint and number: + return a == b; } /** * Returns a deep clone of the given value. * - * This considers the case that the values are (potentially - * multi-dimensional) arrays. Non-array values (including - * objects!) will be returned directly. + * When the value is an array, then its elements are + * deep-cloned. Otherwise, the value itself is returned. * * @param value - The input value * @returns The result value @@ -223,22 +294,43 @@ export class ArrayValues { /** * Returns whether one value is less than another. * - * This considers the case that the values are numbers or - * (potentially multi-dimensional) arrays of numbers. - * - * It returns whether the first number is smaller than - * the second number. For arrays, it recursively checks + * It returns whether the first value is smaller than + * the second value. For arrays, it recursively checks * whether ANY element of the first array is smaller - * than the corresponding element of the secon array. + * than the corresponding element of the second array. * * @param a - The first value * @param b - The second value * @returns Whether the first value is less than the second + * @throws MetadataError If the parameters have incompatible types */ static anyDeepLessThan(a: any, b: any): boolean { + return ArrayValues.anyDeepLessThanInternal(a, b); + } + + /** + * Returns whether one value is less than another. + * + * It returns whether the first value is smaller than + * the second value. For arrays, it recursively checks + * whether ANY element of the first array is smaller + * than the corresponding element of the second array. + * + * @param a - The first value + * @param b - The second value + * @returns Whether the first value is less than the second + * @throws MetadataError If the parameters have incompatible types + */ + private static anyDeepLessThanInternal( + a: T, + b: T + ): boolean { if (Array.isArray(a) && Array.isArray(b)) { + if (a.length != b.length) { + throw new MetadataError(`Values ${a} and ${b} have different lengths`); + } for (let i = 0; i < a.length; ++i) { - if (ArrayValues.anyDeepLessThan(a[i], b[i])) { + if (ArrayValues.anyDeepLessThanInternal(a[i], b[i])) { return true; } } @@ -246,25 +338,47 @@ export class ArrayValues { } return a < b; } + /** * Returns whether one value is greater than another. * - * This considers the case that the values are numbers or - * (potentially multi-dimensional) arrays of numbers. - * - * It returns whether the first number is greater than - * the second number. For arrays, it recursively checks + * It returns whether the first value is greater than + * the second value. For arrays, it recursively checks * whether ANY element of the first array is greater - * than the corresponding element of the secon array. + * than the corresponding element of the second array. * * @param a - The first value * @param b - The second value * @returns Whether the first value is greater than the second + * @throws MetadataError If the parameters have incompatible types */ static anyDeepGreaterThan(a: any, b: any): boolean { + return ArrayValues.anyDeepGreaterThanInternal(a, b); + } + + /** + * Returns whether one value is greater than another. + * + * It returns whether the first value is greater than + * the second value. For arrays, it recursively checks + * whether ANY element of the first array is greater + * than the corresponding element of the second array. + * + * @param a - The first value + * @param b - The second value + * @returns Whether the first value is greater than the second + * @throws MetadataError If the parameters have incompatible types + */ + private static anyDeepGreaterThanInternal( + a: T, + b: T + ): boolean { if (Array.isArray(a) && Array.isArray(b)) { + if (a.length != b.length) { + throw new MetadataError(`Values ${a} and ${b} have different lengths`); + } for (let i = 0; i < a.length; ++i) { - if (ArrayValues.anyDeepGreaterThan(a[i], b[i])) { + if (ArrayValues.anyDeepGreaterThanInternal(a[i], b[i])) { return true; } } diff --git a/src/metadata/ClassProperties.ts b/src/metadata/ClassProperties.ts new file mode 100644 index 00000000..f51a44a2 --- /dev/null +++ b/src/metadata/ClassProperties.ts @@ -0,0 +1,72 @@ +import { defined } from "../base/defined"; + +import { MetadataTypes } from "./MetadataTypes"; +import { MetadataComponentTypes } from "./MetadataComponentTypes"; + +import { ClassProperty } from "../structure/Metadata/ClassProperty"; + +/** + * Utility methods related to `ClassProperty` objects + */ +export class ClassProperties { + /** + * Returns whether the given property effectively describes a floating + * point type. + * + * These are the properties for which 'offset' and 'scale' may be defined. + * + * This means that the value has the type SCALAR, VECn, or MATn, and + * - either has the componentType FLOAT32 or FLOAT46 + * - or has an integer component type AND is 'normalized' + * + * @param property - The property + * @returns Whether the property is a floating point property + */ + static hasEffectivelyFloatingPointType(property: ClassProperty): boolean { + const type = property.type; + if (!MetadataTypes.numericTypes.includes(type)) { + return false; + } + const componentType = property.componentType; + if (!defined(componentType)) { + return false; + } + if (componentType === "FLOAT32" || componentType === "FLOAT64") { + return true; + } + if (MetadataComponentTypes.isIntegerComponentType(componentType)) { + const normalized = property.normalized; + if (!defined(normalized)) { + return false; + } + return normalized; + } + return false; + } + + /** + * Returns whether the given property describes a numeric type. + * + * These are the properties for which 'max' and 'min' may be defined. + * + * This means tha the value has the type SCALAR, VECn, or MATn, and + * one of the allowed component types. + * + * @param property - The property + * @returns Whether the property is a numeric property + */ + static hasNumericType(property: ClassProperty): boolean { + const type = property.type; + if (!MetadataTypes.numericTypes.includes(type)) { + return false; + } + const componentType = property.componentType; + if (!defined(componentType)) { + return false; + } + if (!MetadataComponentTypes.allComponentTypes.includes(componentType)) { + return false; + } + return true; + } +} diff --git a/src/metadata/DefaultMetadataEntityModel.ts b/src/metadata/DefaultMetadataEntityModel.ts index d36cc644..64a4eefa 100644 --- a/src/metadata/DefaultMetadataEntityModel.ts +++ b/src/metadata/DefaultMetadataEntityModel.ts @@ -43,7 +43,9 @@ export class DefaultMetadataEntityModel implements MetadataEntityModel { ); } const value = this._json[propertyId]; - return MetadataValues.processValue(property, undefined, undefined, value); + const offset = this._json.offset; + const scale = this._json.scale; + return MetadataValues.processValue(property, offset, scale, value); } /** {@inheritDoc MetadataEntityModel.getPropertyValueBySemantic} */ diff --git a/src/metadata/MetadataValues.ts b/src/metadata/MetadataValues.ts index 4292c6ef..a2afdbb9 100644 --- a/src/metadata/MetadataValues.ts +++ b/src/metadata/MetadataValues.ts @@ -66,7 +66,13 @@ export class MetadataValues { ? offsetOverride : classProperty.offset; const scale = defined(scaleOverride) ? scaleOverride : classProperty.scale; - value = MetadataValues.transform(value, offset, scale); + + if (defined(scale)) { + value = ArrayValues.deepMultiply(value, scale); + } + if (defined(offset)) { + value = ArrayValues.deepAdd(value, offset); + } return value; } @@ -88,19 +94,4 @@ export class MetadataValues { } return value; } - - /** - * Applies the given offset and scale to the given input value, if they - * are defined. - * - * @param value - The input value - * @param offset - The optional offset - * @param scale - The optional scale - * @returns The transformed value - */ - private static transform(value: any, offset: any, scale: any): any { - value = ArrayValues.deepMultiply(value, scale); - value = ArrayValues.deepAdd(value, offset); - return value; - } } From 95b1edb79d29804eb1e17ec97d9fc439a68e1117 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Mon, 13 Mar 2023 21:57:41 +0100 Subject: [PATCH 14/26] Add demo for collecting statistics in traversal --- demos/TraversalDemo.ts | 31 +------ demos/TraversalStatsDemo.ts | 180 ++++++++++++++++++++++++++++++++++++ demos/readJsonUnchecked.ts | 27 ++++++ 3 files changed, 210 insertions(+), 28 deletions(-) create mode 100644 demos/TraversalStatsDemo.ts create mode 100644 demos/readJsonUnchecked.ts diff --git a/demos/TraversalDemo.ts b/demos/TraversalDemo.ts index 9cedff38..d64624bd 100644 --- a/demos/TraversalDemo.ts +++ b/demos/TraversalDemo.ts @@ -1,34 +1,9 @@ -import fs from "fs"; import path from "path"; -import { ResourceResolvers } from "../src"; -import { TilesetTraverser } from "../src/traversal/TilesetTraverser"; +import { readJsonUnchecked } from "./readJsonUnchecked"; -/** - * Only for internal use and basic tests: - * - * Reads a JSON file, parses it, and returns the result. - * If the file cannot be read or parsed, then an error - * message will be printed and `undefined` is returned. - * - * @param filePath - The path to the file - * @returns A promise that resolves with the result or `undefined` - */ -async function readJsonUnchecked(filePath: string): Promise { - try { - const data = fs.readFileSync(filePath); - if (!data) { - console.error("Could not read " + filePath); - return undefined; - } - const jsonString = data.toString(); - const result = JSON.parse(jsonString); - return result; - } catch (error) { - console.error("Could not parse JSON", error); - return undefined; - } -} +import { ResourceResolvers } from "../src/io/ResourceResolvers"; +import { TilesetTraverser } from "../src/traversal/TilesetTraverser"; async function tilesetTraversalDemo(filePath: string) { const directory = path.dirname(filePath); diff --git a/demos/TraversalStatsDemo.ts b/demos/TraversalStatsDemo.ts new file mode 100644 index 00000000..9de0f4cd --- /dev/null +++ b/demos/TraversalStatsDemo.ts @@ -0,0 +1,180 @@ +import fs from "fs"; +import path from "path"; + +import { readJsonUnchecked } from "./readJsonUnchecked"; + +import { ResourceResolvers } from "../src/io/ResourceResolvers"; + +import { TilesetTraverser } from "../src/traversal/TilesetTraverser"; +import { TraversedTile } from "../src/traversal/TraversedTile"; + +// A small demo that traverses a tileset, passes all +// traversed tiles to a "StatsCollector" (defined below), +// and creates a short JSON summary of some statistics. + +async function tilesetTraversalDemo(filePath: string) { + // Read the tileset from the input path + const directory = path.dirname(filePath); + const resourceResolver = + ResourceResolvers.createFileResourceResolver(directory); + const tileset = await readJsonUnchecked(filePath); + // Note: External schemas are not considered here + const schema = tileset.schema; + + // Traverse the tileset, and pass each tile to + // the StatsCollector + console.log("Traversing tileset"); + const statsCollector = new StatsCollector(); + const depthFirst = false; + await TilesetTraverser.traverse( + tileset, + schema, + resourceResolver, + async (traversedTile) => { + statsCollector.accept(traversedTile); + return true; + }, + depthFirst + ); + console.log("Traversing tileset DONE"); + + // Print the statistics summary to the console + console.log("Stats:"); + const json = statsCollector.createJson(); + const jsonString = JSON.stringify(json, null, 2); + console.log(jsonString); +} + +// A simple class to collect statistical information about a tileset, +// from the tiles that are traversed with a TilesetTraverser +class StatsCollector { + private totalNumberOfTiles = 0; + private totalNumberOfSubtrees = 0; + + // A mapping from value names to statistical summaries + private readonly summaries: { + [key: string]: Summary; + } = {}; + + // Accept the given tile during traversal, and collect + // statistical information + accept(traversedTile: TraversedTile) { + this.totalNumberOfTiles++; + + // NOTE: This is a means of checking whether a tile + // is the root of an implicit tileset. This may be + // refactored at some point. + if (traversedTile.getImplicitTiling()) { + this.totalNumberOfSubtrees++; + } else { + // Obtain all content URIs, resolve them, and obtain + // the sizes of the corresponding files, storing them + // in the "tileFileSize" summary + const contentUris = traversedTile.getFinalContents().map((c) => c.uri); + for (const contentUri of contentUris) { + const resolvedContentUri = traversedTile.resolveUri(contentUri); + const stats = fs.statSync(resolvedContentUri); + const tileFileSizeInBytes = stats.size; + this.acceptEntry("tileFileSize", tileFileSizeInBytes); + } + } + + // Store the geometric error in the "geometricError" summary + const finalTile = traversedTile.asFinalTile(); + const geometricError = finalTile.geometricError; + this.acceptEntry("geometricError", geometricError); + } + + // Add one entry to a summary, creating it when necessary + private acceptEntry(name: string, value: number) { + let summary = this.summaries[name]; + if (!summary) { + summary = new Summary(); + this.summaries[name] = summary; + } + summary.accept(value); + } + + // Create a short JSON representation of the collected data + createJson(): any { + const json: any = {}; + json.totalNumberOfTiles = this.totalNumberOfTiles; + json.totalNumberOfSubtrees = this.totalNumberOfSubtrees; + for (const key of Object.keys(this.summaries)) { + const summary = this.summaries[key]; + json[key] = { + count: summary.getCount(), + sum: summary.getSum(), + min: summary.getMinimum(), + max: summary.getMaximum(), + avg: summary.getMean(), + stdDev: summary.getStandardDeviation(), + }; + } + return json; + } +} + +/** + * A class that can accept numbers, and collects statistical + * information for these numbers. + */ +class Summary { + private count: number; + private sum: number; + private min: number; + private max: number; + private varianceTracker: number; + + public constructor() { + this.count = 0; + this.sum = 0.0; + this.min = Number.POSITIVE_INFINITY; + this.max = Number.NEGATIVE_INFINITY; + this.varianceTracker = 0.0; + } + + accept(value: number) { + const deviation = value - this.getMean(); + this.sum += value; + this.min = Math.min(this.min, value); + this.max = Math.max(this.max, value); + this.count++; + if (this.count > 1) { + this.varianceTracker += + (deviation * deviation * (this.count - 1)) / this.count; + } + } + + getCount() { + return this.count; + } + + getSum() { + return this.sum; + } + + getMinimum() { + return this.min; + } + + getMaximum() { + return this.max; + } + + getMean() { + return this.sum / this.count; + } + + getStandardDeviation() { + return Math.sqrt(this.varianceTracker / this.count); + } +} + +async function runDemo() { + const tilesetFileName = + "../3d-tiles-samples/1.1/SparseImplicitQuadtree/tileset.json"; + await tilesetTraversalDemo(tilesetFileName); +} + +runDemo(); diff --git a/demos/readJsonUnchecked.ts b/demos/readJsonUnchecked.ts new file mode 100644 index 00000000..6596ea65 --- /dev/null +++ b/demos/readJsonUnchecked.ts @@ -0,0 +1,27 @@ +import fs from "fs"; + +/** + * Only for internal use and basic tests: + * + * Reads a JSON file, parses it, and returns the result. + * If the file cannot be read or parsed, then an error + * message will be printed and `undefined` is returned. + * + * @param filePath - The path to the file + * @returns A promise that resolves with the result or `undefined` + */ +export async function readJsonUnchecked(filePath: string): Promise { + try { + const data = fs.readFileSync(filePath); + if (!data) { + console.error("Could not read " + filePath); + return undefined; + } + const jsonString = data.toString(); + const result = JSON.parse(jsonString); + return result; + } catch (error) { + console.error("Could not parse JSON", error); + return undefined; + } +} From 9871b5560a2bb9083e100f0ebaeee4613c69b8ed Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 14 Mar 2023 18:50:38 +0100 Subject: [PATCH 15/26] Update API definition and documentation --- src/binary/BinaryBufferDataResolver.ts | 2 + src/binary/BinaryBufferStructure.ts | 2 + src/binary/BinaryDataError.ts | 2 + src/contentTypes/LazyContentData.ts | 2 +- src/implicitTiling/AvailabilityInfo.ts | 2 + .../BinarySubtreeDataResolver.ts | 2 +- src/implicitTiling/ImplicitTilingError.ts | 2 + src/implicitTiling/OctreeCoordinates.ts | 2 + src/implicitTiling/Octrees.ts | 5 ++ src/implicitTiling/QuadtreeCoordinates.ts | 2 + src/implicitTiling/Quadtrees.ts | 2 + src/implicitTiling/SubtreeInfo.ts | 2 + src/implicitTiling/SubtreeInfos.ts | 2 + src/implicitTiling/TemplateUris.ts | 2 + src/implicitTiling/TreeCoordinates.ts | 2 + src/index.ts | 53 +++++++++++++++++++ src/metadata/ArrayValues.ts | 2 + src/metadata/ClassProperties.ts | 2 + src/metadata/MetadataError.ts | 2 + src/traversal/ExplicitTraversedTile.ts | 8 +-- src/traversal/ImplicitTraversedTile.ts | 8 +-- src/traversal/SubtreeModel.ts | 2 + src/traversal/SubtreeModels.ts | 6 ++- 23 files changed, 104 insertions(+), 12 deletions(-) diff --git a/src/binary/BinaryBufferDataResolver.ts b/src/binary/BinaryBufferDataResolver.ts index 139d3bb5..44849b58 100644 --- a/src/binary/BinaryBufferDataResolver.ts +++ b/src/binary/BinaryBufferDataResolver.ts @@ -8,6 +8,8 @@ import { BinaryDataError } from "./BinaryDataError"; /** * A class for resolving binary buffer data. + * + * @internal */ export class BinaryBufferDataResolver { /** diff --git a/src/binary/BinaryBufferStructure.ts b/src/binary/BinaryBufferStructure.ts index dec62dfa..766efac5 100644 --- a/src/binary/BinaryBufferStructure.ts +++ b/src/binary/BinaryBufferStructure.ts @@ -5,6 +5,8 @@ import { BufferView } from "../structure/BufferView"; * A basic class holding information about the structure of * buffers that are split into buffer views, for example, * from a `Subtree` object. + * + * @internal */ export interface BinaryBufferStructure { buffers: BufferObject[]; diff --git a/src/binary/BinaryDataError.ts b/src/binary/BinaryDataError.ts index 412ef8ff..a6a28aba 100644 --- a/src/binary/BinaryDataError.ts +++ b/src/binary/BinaryDataError.ts @@ -1,5 +1,7 @@ /** * An error that indicates that binary data was structurally invalid. + * + * @internal */ export class BinaryDataError extends Error { constructor(message: string) { diff --git a/src/contentTypes/LazyContentData.ts b/src/contentTypes/LazyContentData.ts index 6cf5c92e..7e2da7fd 100644 --- a/src/contentTypes/LazyContentData.ts +++ b/src/contentTypes/LazyContentData.ts @@ -79,7 +79,7 @@ export class LazyContentData implements ContentData { * the given resource resolver. * * @param uri - The URI of the content data - * @param resourceResolver The `ResourceResolver` that will be + * @param resourceResolver - The `ResourceResolver` that will be * used for resolving the data from the given URI */ constructor(uri: string, resourceResolver: ResourceResolver) { diff --git a/src/implicitTiling/AvailabilityInfo.ts b/src/implicitTiling/AvailabilityInfo.ts index 4b850b3f..082fb6b0 100644 --- a/src/implicitTiling/AvailabilityInfo.ts +++ b/src/implicitTiling/AvailabilityInfo.ts @@ -2,6 +2,8 @@ * An interface that describes the availability information * in a subtree. This is used for tile, content, and child * subtree availability. + * + * @internal */ export interface AvailabilityInfo { /** diff --git a/src/implicitTiling/BinarySubtreeDataResolver.ts b/src/implicitTiling/BinarySubtreeDataResolver.ts index 426bf2f1..847392c1 100644 --- a/src/implicitTiling/BinarySubtreeDataResolver.ts +++ b/src/implicitTiling/BinarySubtreeDataResolver.ts @@ -110,7 +110,7 @@ export class BinarySubtreeDataResolver { * * @param subtree - The `Subtree` * @param binaryBuffer - The binary buffer of the subtree - * @param resourceResolver The resource resolver + * @param resourceResolver - The resource resolver * @returns A promise to the resolved binary subtree data */ static async resolveInternal( diff --git a/src/implicitTiling/ImplicitTilingError.ts b/src/implicitTiling/ImplicitTilingError.ts index c02a4c12..8c38c4e2 100644 --- a/src/implicitTiling/ImplicitTilingError.ts +++ b/src/implicitTiling/ImplicitTilingError.ts @@ -4,6 +4,8 @@ * * This may be thrown by methods that create the convenience classes * for this package, when the given resources are not valid. + * + * @internal */ export class ImplicitTilingError extends Error { constructor(message: string) { diff --git a/src/implicitTiling/OctreeCoordinates.ts b/src/implicitTiling/OctreeCoordinates.ts index a2651b5e..5c38e3c3 100644 --- a/src/implicitTiling/OctreeCoordinates.ts +++ b/src/implicitTiling/OctreeCoordinates.ts @@ -4,6 +4,8 @@ import { TreeCoordinates } from "./TreeCoordinates"; /** * An implementation of `TreeCoordinates` for octrees + * + * @internal */ export class OctreeCoordinates implements TreeCoordinates { private readonly _level: number; diff --git a/src/implicitTiling/Octrees.ts b/src/implicitTiling/Octrees.ts index b6b81311..70707e4f 100644 --- a/src/implicitTiling/Octrees.ts +++ b/src/implicitTiling/Octrees.ts @@ -1,5 +1,10 @@ import { OctreeCoordinates } from "./OctreeCoordinates"; +/** + * Methods related to octrees + * + * @internal + */ export class Octrees { /** * Computes the number of nodes of an octree with the given number of diff --git a/src/implicitTiling/QuadtreeCoordinates.ts b/src/implicitTiling/QuadtreeCoordinates.ts index 7358a0f5..aeea721f 100644 --- a/src/implicitTiling/QuadtreeCoordinates.ts +++ b/src/implicitTiling/QuadtreeCoordinates.ts @@ -4,6 +4,8 @@ import { TreeCoordinates } from "./TreeCoordinates"; /** * An implementation of `TreeCoordinates` for octrees + * + * @internal */ export class QuadtreeCoordinates implements TreeCoordinates { private readonly _level: number; diff --git a/src/implicitTiling/Quadtrees.ts b/src/implicitTiling/Quadtrees.ts index 3dc5610d..826069e5 100644 --- a/src/implicitTiling/Quadtrees.ts +++ b/src/implicitTiling/Quadtrees.ts @@ -2,6 +2,8 @@ import { QuadtreeCoordinates } from "./QuadtreeCoordinates"; /** * Methods related to quadtrees. + * + * @internal */ export class Quadtrees { /** diff --git a/src/implicitTiling/SubtreeInfo.ts b/src/implicitTiling/SubtreeInfo.ts index 23cea07b..02bc3a6a 100644 --- a/src/implicitTiling/SubtreeInfo.ts +++ b/src/implicitTiling/SubtreeInfo.ts @@ -6,6 +6,8 @@ import { AvailabilityInfo } from "./AvailabilityInfo"; * * It offers the availability information for tiles, child * subtrees, and contents, as `AvailabilityInfo` objects. + * + * @internal */ export interface SubtreeInfo { /** diff --git a/src/implicitTiling/SubtreeInfos.ts b/src/implicitTiling/SubtreeInfos.ts index 13f1ccaf..ca8555fc 100644 --- a/src/implicitTiling/SubtreeInfos.ts +++ b/src/implicitTiling/SubtreeInfos.ts @@ -10,6 +10,8 @@ import { TileImplicitTiling } from "../structure/TileImplicitTiling"; /** * Methods to create `SubtreeInfo` instances. + * + * @internal */ export class SubtreeInfos { /** diff --git a/src/implicitTiling/TemplateUris.ts b/src/implicitTiling/TemplateUris.ts index 1542217f..a70e9c71 100644 --- a/src/implicitTiling/TemplateUris.ts +++ b/src/implicitTiling/TemplateUris.ts @@ -3,6 +3,8 @@ import { QuadtreeCoordinates } from "./QuadtreeCoordinates"; /** * Method related to template URIs for implicit tiling. + * + * @internal */ export class TemplateUris { /** diff --git a/src/implicitTiling/TreeCoordinates.ts b/src/implicitTiling/TreeCoordinates.ts index 903eb83e..041abfee 100644 --- a/src/implicitTiling/TreeCoordinates.ts +++ b/src/implicitTiling/TreeCoordinates.ts @@ -1,5 +1,7 @@ /** * An interface for coordinates within a tree structure. + * + * @internal */ export interface TreeCoordinates { /** diff --git a/src/index.ts b/src/index.ts index 7cc75141..3b722941 100644 --- a/src/index.ts +++ b/src/index.ts @@ -63,3 +63,56 @@ export * from "./io/UnzippingResourceResolver"; export * from "./contentTypes/ContentData"; export * from "./contentTypes/ContentDataTypeRegistry"; +export * from "./contentTypes/LazyContentData"; + +export * from "./binary/BinaryBufferData"; +export * from "./binary/BinaryBufferDataResolver"; +export * from "./binary/BinaryBuffers"; +export * from "./binary/BinaryBufferStructure"; +export * from "./binary/BinaryDataError"; + +export * from "./implicitTiling/AvailabilityInfo"; +export * from "./implicitTiling/AvailabilityInfos"; +export * from "./implicitTiling/BinarySubtreeData"; +export * from "./implicitTiling/BinarySubtreeDataResolver"; +export * from "./implicitTiling/ImplicitTilingError"; +export * from "./implicitTiling/ImplicitTilings"; +export * from "./implicitTiling/OctreeCoordinates"; +export * from "./implicitTiling/Octrees"; +export * from "./implicitTiling/QuadtreeCoordinates"; +export * from "./implicitTiling/Quadtrees"; +export * from "./implicitTiling/SubtreeInfo"; +export * from "./implicitTiling/SubtreeInfos"; +export * from "./implicitTiling/TemplateUris"; +export * from "./implicitTiling/TreeCoordinates"; + +export * from "./metadata/ArrayValues"; +export * from "./metadata/ClassProperties"; +export * from "./metadata/DefaultMetadataEntityModel"; +export * from "./metadata/MetadataComponentTypes"; +export * from "./metadata/MetadataUtilities"; +export * from "./metadata/MetadataEntityModel"; +export * from "./metadata/MetadataEntityModels"; +export * from "./metadata/MetadataError"; +export * from "./metadata/MetadataTypes"; +export * from "./metadata/MetadataUtilities"; +export * from "./metadata/MetadataValues"; +export * from "./metadata/PropertyModel"; +export * from "./metadata/PropertyTableModel"; + +export * from "./metadata/binary/BinaryPropertyTable"; +export * from "./metadata/binary/BinaryPropertyTables"; +export * from "./metadata/binary/BinaryPropertyTableModel"; +export * from "./metadata/binary/NumericBuffers"; + +export * from "./traversal/ExplicitTraversedTile"; +export * from "./traversal/ExplicitTraversedTiles"; +export * from "./traversal/ImplicitTraversedTile"; +export * from "./traversal/SubtreeMetadataModel"; +export * from "./traversal/SubtreeMetadataModels"; +export * from "./traversal/SubtreeModel"; +export * from "./traversal/SubtreeModels"; +export * from "./traversal/TilesetTraverser"; +export * from "./traversal/TraversedTile"; +export * from "./traversal/TraversalCallback"; + diff --git a/src/metadata/ArrayValues.ts b/src/metadata/ArrayValues.ts index 2829f43f..03327422 100644 --- a/src/metadata/ArrayValues.ts +++ b/src/metadata/ArrayValues.ts @@ -36,6 +36,8 @@ type NumberND = NumberScalar | NumberND[]; * that the values have the same structure, i.e. they are * both numeric/numbers or arrays with the same length. If this * is not the case, then a `MetadataError` will be thrown. + * + * @internal */ export class ArrayValues { // Implementation note: diff --git a/src/metadata/ClassProperties.ts b/src/metadata/ClassProperties.ts index f51a44a2..35355aca 100644 --- a/src/metadata/ClassProperties.ts +++ b/src/metadata/ClassProperties.ts @@ -7,6 +7,8 @@ import { ClassProperty } from "../structure/Metadata/ClassProperty"; /** * Utility methods related to `ClassProperty` objects + * + * @internal */ export class ClassProperties { /** diff --git a/src/metadata/MetadataError.ts b/src/metadata/MetadataError.ts index 4c76f990..2acbefee 100644 --- a/src/metadata/MetadataError.ts +++ b/src/metadata/MetadataError.ts @@ -3,6 +3,8 @@ * * This may be thrown by methods that create the convenience classes * for this package, when the given inputs are not valid. + * + * @internal */ export class MetadataError extends Error { constructor(message: string) { diff --git a/src/traversal/ExplicitTraversedTile.ts b/src/traversal/ExplicitTraversedTile.ts index f8d45187..5d014ab7 100644 --- a/src/traversal/ExplicitTraversedTile.ts +++ b/src/traversal/ExplicitTraversedTile.ts @@ -198,7 +198,7 @@ export class ExplicitTraversedTile implements TraversedTile { return finalContents; } - /** {@inheritDoc TraversedTile.getSubtreeUri} - PRELIMINARY */ + /** {@inheritDoc TraversedTile.getSubtreeUri} */ getSubtreeUri(): string | undefined { const implicitTiling = this._tile.implicitTiling; if (!implicitTiling) { @@ -214,17 +214,17 @@ export class ExplicitTraversedTile implements TraversedTile { return subtreeUri; } - /** {@inheritDoc TraversedTile.getImplicitTiling} - PRELIMINARY */ + /** {@inheritDoc TraversedTile.getImplicitTiling} */ getImplicitTiling(): TileImplicitTiling | undefined { return this._tile.implicitTiling; } - /** {@inheritDoc TraversedTile.getMetadata} - PRELIMINARY */ + /** {@inheritDoc TraversedTile.getMetadata} */ getMetadata(): MetadataEntity | undefined { return this._tile.metadata; } - /** {@inheritDoc TraversedTile.resolveUri} - PRELIMINARY */ + /** {@inheritDoc TraversedTile.resolveUri} */ resolveUri(uri: string): string { return this._resourceResolver.resolveUri(uri); } diff --git a/src/traversal/ImplicitTraversedTile.ts b/src/traversal/ImplicitTraversedTile.ts index 287b6f11..f7fecdd5 100644 --- a/src/traversal/ImplicitTraversedTile.ts +++ b/src/traversal/ImplicitTraversedTile.ts @@ -377,7 +377,7 @@ export class ImplicitTraversedTile implements TraversedTile { return contents; } - /** {@inheritDoc TraversedTile.getSubtreeUri} PRELIMINARY */ + /** {@inheritDoc TraversedTile.getSubtreeUri} */ getSubtreeUri(): string | undefined { const localCoordinate = this._localCoordinate; if (localCoordinate.level === 0) { @@ -393,7 +393,7 @@ export class ImplicitTraversedTile implements TraversedTile { return undefined; } - /** {@inheritDoc TraversedTile.getImplicitTiling} PRELIMINARY */ + /** {@inheritDoc TraversedTile.getImplicitTiling} */ getImplicitTiling(): TileImplicitTiling | undefined { const localCoordinate = this._localCoordinate; if (localCoordinate.level === 0) { @@ -401,12 +401,12 @@ export class ImplicitTraversedTile implements TraversedTile { } } - /** {@inheritDoc TraversedTile.getMetadata} PRELIMINARY */ + /** {@inheritDoc TraversedTile.getMetadata} */ getMetadata(): MetadataEntity | undefined { return undefined; } - /** {@inheritDoc TraversedTile.resolveUri} - PRELIMINARY */ + /** {@inheritDoc TraversedTile.resolveUri} */ resolveUri(uri: string): string { return this._resourceResolver.resolveUri(uri); } diff --git a/src/traversal/SubtreeModel.ts b/src/traversal/SubtreeModel.ts index b4bc7734..6bfc1aea 100644 --- a/src/traversal/SubtreeModel.ts +++ b/src/traversal/SubtreeModel.ts @@ -3,6 +3,8 @@ import { SubtreeMetadataModel } from "./SubtreeMetadataModel"; /** * An interface that summarizes the information for a subtree. + * + * @internal */ export interface SubtreeModel { /** diff --git a/src/traversal/SubtreeModels.ts b/src/traversal/SubtreeModels.ts index 0fa85987..2837075a 100644 --- a/src/traversal/SubtreeModels.ts +++ b/src/traversal/SubtreeModels.ts @@ -25,6 +25,8 @@ import { SubtreeMetadataModels } from "./SubtreeMetadataModels"; * The methods will resolve the data for a subtree, based on the template * URI from the implicit tiling and the root coordinates of the subtree, * and offer this information as `SubtreeModel` objects. + * + * @internal */ export class SubtreeModels { /** @@ -135,8 +137,8 @@ export class SubtreeModels { * Creates the `SubtreeModel` from the given binary subtree data * * @param binarySubtreeData - The binary subtree data - * @param implicitTiling The `TileImplicitTiling` - * @param schema The optional metadata schema + * @param implicitTiling - The `TileImplicitTiling` + * @param schema - The optional metadata schema * @returns The `SubtreeModel` * @throws ImplicitTilingError If the input was structurally invalid */ From 70ce72a47e37dbcbf05d140c68f94b8450b8749e Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 14 Mar 2023 18:51:13 +0100 Subject: [PATCH 16/26] Removed unused file --- specs/metadata/PropertyTableTestUtilities.ts | 579 ------------------- 1 file changed, 579 deletions(-) delete mode 100644 specs/metadata/PropertyTableTestUtilities.ts diff --git a/specs/metadata/PropertyTableTestUtilities.ts b/specs/metadata/PropertyTableTestUtilities.ts deleted file mode 100644 index 5b026a3b..00000000 --- a/specs/metadata/PropertyTableTestUtilities.ts +++ /dev/null @@ -1,579 +0,0 @@ -import { BinaryPropertyTable } from "../../src/metadata/binary/BinaryPropertyTable"; -import { BinaryPropertyTables } from "../../src/metadata/binary/BinaryPropertyTables"; - -import { ClassProperty } from "../../src/structure/Metadata/ClassProperty"; -import { MetadataEnum } from "../../src/structure/Metadata/MetadataEnum"; - -/** - * Methods to create `BinaryPropertyTable` instances that are valid, - * and contain the data for a single property. - * - * These methods are used in the `BinaryPropertyTableValidationSpec.ts` - * to create valid `BinaryPropertyTable` instances, that are then made - * invalid in various ways in order to check whether these errors are - * detected asvalidation issues. - * - * @internal - */ -export class PropertyTableTestUtilities { - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_INT16_SCALAR(): BinaryPropertyTable { - const example_INT16_SCALAR: ClassProperty = { - type: "SCALAR", - componentType: "INT16", - }; - const example_INT16_SCALAR_values = [-32768, 32767]; - - const classProperty = example_INT16_SCALAR; - const values = example_INT16_SCALAR_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_variable_length_INT16_SCALAR_array(): BinaryPropertyTable { - const example_variable_length_INT16_SCALAR_array: ClassProperty = { - type: "SCALAR", - componentType: "INT16", - array: true, - }; - const example_variable_length_INT16_SCALAR_array_values = [ - [-32768, 32767], - [-1, 0, 1], - ]; - - const classProperty = example_variable_length_INT16_SCALAR_array; - const values = example_variable_length_INT16_SCALAR_array_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_fixed_length_INT16_SCALAR_array(): BinaryPropertyTable { - const example_fixed_length_INT16_SCALAR_array: ClassProperty = { - type: "SCALAR", - componentType: "INT16", - array: true, - count: 2, - }; - const example_fixed_length_INT16_SCALAR_array_values = [ - [-32768, 32767], - [-1, 1], - ]; - - const classProperty = example_fixed_length_INT16_SCALAR_array; - const values = example_fixed_length_INT16_SCALAR_array_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_BOOLEAN(): BinaryPropertyTable { - const example_BOOLEAN: ClassProperty = { - type: "BOOLEAN", - }; - const example_BOOLEAN_values = [true, false]; - - const classProperty = example_BOOLEAN; - const values = example_BOOLEAN_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_variable_length_BOOLEAN_array(): BinaryPropertyTable { - const example_variable_length_BOOLEAN_array: ClassProperty = { - type: "BOOLEAN", - array: true, - }; - const example_variable_length_BOOLEAN_array_values = [ - [true, false], - [false, true, false, true], - ]; - - const classProperty = example_variable_length_BOOLEAN_array; - const values = example_variable_length_BOOLEAN_array_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_fixed_length_BOOLEAN_array(): BinaryPropertyTable { - const example_fixed_length_BOOLEAN_array: ClassProperty = { - type: "BOOLEAN", - array: true, - }; - const example_fixed_length_BOOLEAN_array_values = [ - [true, false, true], - [false, true, false], - ]; - - const classProperty = example_fixed_length_BOOLEAN_array; - const values = example_fixed_length_BOOLEAN_array_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_STRING(): BinaryPropertyTable { - const example_STRING: ClassProperty = { - type: "STRING", - }; - const example_STRING_values = ["Test string", "Another string"]; - - const classProperty = example_STRING; - const values = example_STRING_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_variable_length_STRING_array(): BinaryPropertyTable { - const example_variable_length_STRING_array: ClassProperty = { - type: "STRING", - array: true, - }; - const example_variable_length_STRING_array_values = [ - ["A0", "A1", "A2"], - ["B0", "B1"], - ]; - - const classProperty = example_variable_length_STRING_array; - const values = example_variable_length_STRING_array_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_fixed_length_STRING_array(): BinaryPropertyTable { - const example_fixed_length_STRING_array: ClassProperty = { - type: "STRING", - array: true, - count: 3, - }; - const example_fixed_length_STRING_array_values = [ - ["A0", "A1", "A2"], - ["B0", "B1", "B2"], - ]; - - const classProperty = example_fixed_length_STRING_array; - const values = example_fixed_length_STRING_array_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_FLOAT32_VEC2(): BinaryPropertyTable { - const example_FLOAT32_VEC2: ClassProperty = { - type: "VEC2", - componentType: "FLOAT32", - }; - const example_FLOAT32_VEC2_values = [ - [0.0, 1.0], - [2.0, 3.0], - ]; - - const classProperty = example_FLOAT32_VEC2; - const values = example_FLOAT32_VEC2_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_variable_length_UINT32_VEC2_array(): BinaryPropertyTable { - const example_variable_length_UINT32_VEC2_array: ClassProperty = { - type: "VEC2", - componentType: "FLOAT32", - array: true, - }; - const example_variable_length_UINT32_VEC2_array_values = [ - [ - [0.0, 1.0], - [2.0, 3.0], - ], - [ - [4.0, 5.0], - [6.0, 7.0], - [8.0, 9.0], - ], - ]; - - const classProperty = example_variable_length_UINT32_VEC2_array; - const values = example_variable_length_UINT32_VEC2_array_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_fixed_length_UINT32_VEC2_array(): BinaryPropertyTable { - const example_fixed_length_UINT32_VEC2_array: ClassProperty = { - type: "VEC2", - componentType: "FLOAT32", - array: true, - count: 2, - }; - const example_fixed_length_UINT32_VEC2_array_values = [ - [ - [0.0, 1.0], - [2.0, 3.0], - ], - [ - [4.0, 5.0], - [6.0, 7.0], - ], - ]; - - const classProperty = example_fixed_length_UINT32_VEC2_array; - const values = example_fixed_length_UINT32_VEC2_array_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_variable_length_ENUM_array(): BinaryPropertyTable { - const example_variable_length_ENUM_array: ClassProperty = { - type: "ENUM", - enumType: "testMetadataEnum", - array: true, - }; - const example_example_variable_length_ENUM_array_values = [ - ["ExampleEnumValueA", "ExampleEnumValueB", "ExampleEnumValueC"], - ["ExampleEnumValueB", "ExampleEnumValueA"], - ]; - const testMetadataEnum: MetadataEnum = { - values: [ - { - name: "ExampleEnumValueA", - value: 0, - }, - { - name: "ExampleEnumValueB", - value: 1, - }, - { - name: "ExampleEnumValueC", - value: 2, - }, - ], - }; - - const classProperty = example_variable_length_ENUM_array; - const values = example_example_variable_length_ENUM_array_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - testMetadataEnum - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_ENUM_with_noData(): BinaryPropertyTable { - const example_ENUM: ClassProperty = { - type: "ENUM", - enumType: "testMetadataEnum", - noData: 9999, - default: 1, - }; - const example_ENUM_values = [ - "ExampleEnumValueA", - "ExampleEnumValueB", - "ExampleEnumValueC", - ]; - const testMetadataEnum: MetadataEnum = { - values: [ - { - name: "ExampleEnumValueA", - value: 0, - }, - { - name: "ExampleEnumValueB", - value: 1, - }, - { - name: "ExampleEnumValueC", - value: 2, - }, - ], - }; - - const classProperty = example_ENUM; - const values = example_ENUM_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - testMetadataEnum - ); - return binaryPropertyTable; - } - - /** - * Creates an unspecified valid default `BinaryPropertyTable`, containing - * a single property with the type indicated in the method name. - * - * @returns The `BinaryPropertyTable` - */ - static createDefaultBinaryPropertyTable_example_fixed_length_normalized_INT64_VEC2_array(): BinaryPropertyTable { - const example_fixed_length_normalized_INT64_VEC2_array: ClassProperty = { - type: "VEC2", - componentType: "INT64", - array: true, - normalized: true, - }; - // The normalized values here are - // [ - // [ 0, 0] - // [-1, 0] - // [ 0, -1] - // ], - // [ - // [ 0, 0] - // [ 1, 0] - // [ 0, 1] - // ] - const example_fixed_length_normalized_INT64_VEC2_array_values = [ - [ - [0, 0], - [-9223372036854775808n, 0], - [0, -9223372036854775808n], - ], - [ - [0, 0], - [9223372036854775807n, 0], - [0, 9223372036854775807n], - ], - ]; - - const classProperty = example_fixed_length_normalized_INT64_VEC2_array; - const values = example_fixed_length_normalized_INT64_VEC2_array_values; - - const arrayOffsetType = "UINT32"; - const stringOffsetType = "UINT32"; - const binaryPropertyTable = - BinaryPropertyTables.createBinaryPropertyTableFromProperty( - "testProperty", - classProperty, - values, - arrayOffsetType, - stringOffsetType, - undefined - ); - return binaryPropertyTable; - } -} From cb2208ffaa760fa9523a7a9832e54643e09670c3 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 14 Mar 2023 18:51:29 +0100 Subject: [PATCH 17/26] Change argument type in "defined" --- src/base/defined.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base/defined.ts b/src/base/defined.ts index 463f464f..c612a46e 100644 --- a/src/base/defined.ts +++ b/src/base/defined.ts @@ -28,6 +28,6 @@ * @returns `true` if the value is not `undefined` and not `null` * @internal */ -export function defined(value: any): value is NonNullable { +export function defined(value: T): value is NonNullable { return value !== undefined && value !== null; } From 2c80a429b221d51de2b836d8f17383aa3aac280a Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 14 Mar 2023 18:51:44 +0100 Subject: [PATCH 18/26] Exclude demos in build --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 688a9221..6bffe9dd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,6 @@ "*": [ "./src/*.d.ts"] } }, - "include": ["src", "demos" ], + "include": ["src" ], "exclude": ["node_modules"] } From e2488aa58f75074f4481329e87583c4d97d02b2b Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 14 Mar 2023 18:52:19 +0100 Subject: [PATCH 19/26] Update files generated during build --- ThirdParty.json | 2 +- etc/3d-tiles-tools.api.md | 540 +++++++++++++++++++++++++++++++++++--- 2 files changed, 507 insertions(+), 35 deletions(-) diff --git a/ThirdParty.json b/ThirdParty.json index 0fced04c..6d691cd4 100644 --- a/ThirdParty.json +++ b/ThirdParty.json @@ -28,7 +28,7 @@ "license": [ "Apache-2.0" ], - "version": "1.102.0", + "version": "1.103.0", "url": "https://www.npmjs.com/package/cesium" }, { diff --git a/etc/3d-tiles-tools.api.md b/etc/3d-tiles-tools.api.md index c28a04d8..b0acc469 100644 --- a/etc/3d-tiles-tools.api.md +++ b/etc/3d-tiles-tools.api.md @@ -28,6 +28,20 @@ export class ArchiveFunctions3tz { static zipIndexFind(zipIndex: IndexEntry[], searchHash: Buffer): number; } +// Warning: (ae-internal-missing-underscore) The name "ArrayValues" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class ArrayValues { + static anyDeepGreaterThan(a: any, b: any): boolean; + static anyDeepLessThan(a: any, b: any): boolean; + static deepAdd(value: any, addend: any): any; + static deepClone(value: any): any; + static deepEquals(a: any, b: any): boolean; + static deepMax(a: any, b: any): any; + static deepMin(a: any, b: any): any; + static deepMultiply(value: any, factor: any): any; +} + // Warning: (ae-internal-missing-underscore) The name "Asset" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -50,6 +64,113 @@ export interface Availability extends RootProperty { constant?: number; } +// Warning: (ae-internal-missing-underscore) The name "AvailabilityInfo" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface AvailabilityInfo { + isAvailable(index: number): boolean; + get length(): number; +} + +// Warning: (ae-internal-missing-underscore) The name "AvailabilityInfos" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class AvailabilityInfos { + static createChildSubtree(availability: Availability, bufferViewDatas: Buffer[], implicitTiling: TileImplicitTiling): AvailabilityInfo; + static createTileOrContent(availability: Availability, bufferViewDatas: Buffer[], implicitTiling: TileImplicitTiling): AvailabilityInfo; +} + +// Warning: (ae-internal-missing-underscore) The name "BinaryBufferData" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface BinaryBufferData { + buffersData: Buffer[]; + bufferViewsData: Buffer[]; +} + +// Warning: (ae-internal-missing-underscore) The name "BinaryBufferDataResolver" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class BinaryBufferDataResolver { + static resolve(binaryBufferStructure: BinaryBufferStructure, binaryBuffer: Buffer | undefined, resourceResolver: ResourceResolver): Promise; +} + +// Warning: (ae-internal-missing-underscore) The name "BinaryBuffers" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class BinaryBuffers { + static createBinaryBufferStructure(binaryBufferData: BinaryBufferData, newBufferViewsData: Buffer[]): BinaryBufferStructure; +} + +// Warning: (ae-internal-missing-underscore) The name "BinaryBufferStructure" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface BinaryBufferStructure { + // (undocumented) + buffers: BufferObject[]; + // (undocumented) + bufferViews: BufferView[]; +} + +// Warning: (ae-internal-missing-underscore) The name "BinaryDataError" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class BinaryDataError extends Error { + constructor(message: string); + // (undocumented) + toString: () => string; +} + +// Warning: (ae-internal-missing-underscore) The name "BinaryPropertyTable" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface BinaryPropertyTable { + binaryBufferData: BinaryBufferData; + binaryBufferStructure: BinaryBufferStructure; + // Warning: (ae-forgotten-export) The symbol "BinaryEnumInfo" needs to be exported by the entry point index.d.ts + binaryEnumInfo: BinaryEnumInfo; + metadataClass: MetadataClass; + propertyTable: PropertyTable; +} + +// Warning: (ae-internal-missing-underscore) The name "BinaryPropertyTableModel" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class BinaryPropertyTableModel implements PropertyTableModel { + constructor(binaryPropertyTable: BinaryPropertyTable); + getClassProperty(propertyId: string): ClassProperty | undefined; + getMetadataEntityModel(index: number): MetadataEntityModel; + getPropertyModel(propertyId: string): PropertyModel | undefined; + getPropertyTableProperty(propertyId: string): PropertyTableProperty | undefined; +} + +// Warning: (ae-internal-missing-underscore) The name "BinaryPropertyTables" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class BinaryPropertyTables { + static createBinaryPropertyTable(schema: Schema, className: string, propertyName: string, values: any, arrayOffsetType: string | undefined, stringOffsetType: string | undefined): BinaryPropertyTable; + static createBinaryPropertyTableFromProperty(propertyName: string, classProperty: ClassProperty, values: any, arrayOffsetType: string | undefined, stringOffsetType: string | undefined, metadataEnum: MetadataEnum | undefined): BinaryPropertyTable; + static createSchemaFromClassProperty(propertyName: string, classProperty: ClassProperty, metadataEnum: MetadataEnum | undefined): Schema; +} + +// Warning: (ae-internal-missing-underscore) The name "BinarySubtreeData" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface BinarySubtreeData { + binaryBufferData: BinaryBufferData; + binaryBufferStructure: BinaryBufferStructure; + subtree: Subtree; +} + +// Warning: (ae-internal-missing-underscore) The name "BinarySubtreeDataResolver" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class BinarySubtreeDataResolver { + static resolveFromBuffer(input: Buffer, resourceResolver: ResourceResolver): Promise; + static resolveFromJson(subtree: Subtree, resourceResolver: ResourceResolver): Promise; + static resolveInternal(subtree: Subtree, binaryBuffer: Buffer | undefined, resourceResolver: ResourceResolver): Promise; +} + // Warning: (ae-internal-missing-underscore) The name "BoundingVolume" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -78,11 +199,14 @@ export interface BufferObject extends RootProperty { // // @internal export class Buffers { + static createBinaryString(buffer: Buffer): string; static getBufferPadded(buffer: Buffer, byteOffset?: number): Buffer; static getJson(buffer: Buffer): any; static getJsonBufferPadded(json: any, byteOffset?: number): Buffer; static getMagic(buffer: Buffer, byteOffset?: number): string; static getUnicodeBOMDescription(buffer: Buffer): string | undefined; + static gunzip(inputBuffer: Buffer): Buffer; + static gzip(inputBuffer: Buffer): Buffer; static isGzipped(buffer: Buffer): boolean; static isProbablyJson(buffer: Buffer): boolean; } @@ -101,6 +225,14 @@ export interface BufferView extends RootProperty { name?: string; } +// Warning: (ae-internal-missing-underscore) The name "ClassProperties" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class ClassProperties { + static hasEffectivelyFloatingPointType(property: ClassProperty): boolean; + static hasNumericType(property: ClassProperty): boolean; +} + // Warning: (ae-internal-missing-underscore) The name "ClassProperty" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -172,6 +304,17 @@ export class ContentDataTypeRegistry { static findContentDataType(contentData: ContentData): Promise; } +// Warning: (ae-internal-missing-underscore) The name "DefaultMetadataEntityModel" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class DefaultMetadataEntityModel implements MetadataEntityModel { + constructor(metadataClass: MetadataClass, semanticToPropertyId: { + [key: string]: string; + }, json: any); + getPropertyValue(propertyId: string): any; + getPropertyValueBySemantic(semantic: string): any; +} + // Warning: (ae-internal-missing-underscore) The name "defaultValue" should be prefixed with an underscore because the declaration is marked as @internal // // @internal @@ -180,7 +323,7 @@ export function defaultValue(a: T | undefined, b: T): T; // Warning: (ae-internal-missing-underscore) The name "defined" should be prefixed with an underscore because the declaration is marked as @internal // // @internal -export function defined(value: any): value is NonNullable; +export function defined(value: T): value is NonNullable; // Warning: (ae-internal-missing-underscore) The name "DeveloperError" should be prefixed with an underscore because the declaration is marked as @internal // @@ -203,18 +346,47 @@ export interface EnumValue extends RootProperty { value: number; } +// Warning: (ae-internal-missing-underscore) The name "ExplicitTraversedTile" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class ExplicitTraversedTile implements TraversedTile { + constructor(tile: Tile, path: string, level: number, parent: TraversedTile | undefined, schema: Schema | undefined, resourceResolver: ResourceResolver); + asFinalTile(): Tile; + asRawTile(): Tile; + getChildren(): Promise; + // (undocumented) + getFinalContents(): Content[]; + // (undocumented) + getImplicitTiling(): TileImplicitTiling | undefined; + // (undocumented) + getMetadata(): MetadataEntity | undefined; + getParent(): TraversedTile | undefined; + getRawContents(): Content[]; + // (undocumented) + getSubtreeUri(): string | undefined; + get level(): number; + get path(): string; + // (undocumented) + resolveUri(uri: string): string; + // (undocumented) + toString: () => string; +} + +// Warning: (ae-internal-missing-underscore) The name "ExplicitTraversedTiles" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class ExplicitTraversedTiles { + static createTraversedChildren(implicitTiling: TileImplicitTiling, schema: Schema | undefined, parent: ExplicitTraversedTile, resourceResolver: ResourceResolver): Promise; +} + // Warning: (ae-internal-missing-underscore) The name "FileResourceResolver" should be prefixed with an underscore because the declaration is marked as @internal // // @internal export class FileResourceResolver implements ResourceResolver { constructor(basePath: string); - // (undocumented) derive(uri: string): ResourceResolver; - // (undocumented) resolveData(uri: string): Promise; - // (undocumented) resolveDataPartial(uri: string, maxBytes: number): Promise; - // (undocumented) resolveUri(uri: string): string; } @@ -224,6 +396,56 @@ export class FileResourceResolver implements ResourceResolver { export interface Group extends MetadataEntity { } +// Warning: (ae-internal-missing-underscore) The name "ImplicitTilingError" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class ImplicitTilingError extends Error { + constructor(message: string); + // (undocumented) + toString: () => string; +} + +// Warning: (ae-internal-missing-underscore) The name "ImplicitTilings" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class ImplicitTilings { + static computeNumberOfNodesInLevel(implicitTiling: TileImplicitTiling, level: number): number; + static computeNumberOfNodesPerSubtree(implicitTiling: TileImplicitTiling): number; + static createRootCoordinates(implicitTiling: TileImplicitTiling): TreeCoordinates; + static createString(coordinates: TreeCoordinates): string; + static createSubtreeCoordinatesIterator(implicitTiling: TileImplicitTiling): IterableIterator; + static globalizeCoordinates(implicitTiling: TileImplicitTiling, rootCoordinates: TreeCoordinates, coordinates: TreeCoordinates): TreeCoordinates; + static substituteTemplateUri(subdivisionScheme: string, templateUri: string, coordinates: TreeCoordinates): string; +} + +// Warning: (ae-internal-missing-underscore) The name "ImplicitTraversedTile" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class ImplicitTraversedTile implements TraversedTile { + constructor(implicitTiling: TileImplicitTiling, resourceResolver: ResourceResolver, root: TraversedTile, path: string, subtreeModel: SubtreeModel, globalLevel: number, globalCoordinate: TreeCoordinates, rootCoordinate: TreeCoordinates, localCoordinate: TreeCoordinates, parent: TraversedTile); + asFinalTile(): Tile; + asRawTile(): Tile; + getChildren(): Promise; + // (undocumented) + getFinalContents(): Content[]; + getGlobalCoordinate(): TreeCoordinates; + // (undocumented) + getImplicitTiling(): TileImplicitTiling | undefined; + getLocalCoordinate(): TreeCoordinates; + // (undocumented) + getMetadata(): MetadataEntity | undefined; + getParent(): TraversedTile | undefined; + getRawContents(): Content[]; + // (undocumented) + getSubtreeUri(): string | undefined; + get level(): number; + get path(): string; + // (undocumented) + resolveUri(uri: string): string; + // (undocumented) + toString: () => string; +} + // Warning: (ae-internal-missing-underscore) The name "IndexBuilder" should be prefixed with an underscore because the declaration is marked as @internal // // @internal @@ -261,6 +483,19 @@ export class Iterables { static overFiles(directory: string | PathLike, recurse?: boolean): IterableIterator; } +// Warning: (ae-internal-missing-underscore) The name "LazyContentData" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class LazyContentData implements ContentData { + constructor(uri: string, resourceResolver: ResourceResolver); + exists(): Promise; + get extension(): string; + getData(): Promise; + getMagic(): Promise; + getParsedObject(): Promise; + get uri(): string; +} + // Warning: (ae-internal-missing-underscore) The name "MetadataClass" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -275,6 +510,20 @@ export interface MetadataClass extends RootProperty { }; } +// Warning: (ae-internal-missing-underscore) The name "MetadataComponentTypes" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class MetadataComponentTypes { + static allComponentTypes: string[]; + static byteSizeForComponentType(componentType: string): number; + static integerComponentTypes: string[]; + static isIntegerComponentType(componentType: string | undefined): boolean; + static isUnsignedComponentType(componentType: string | undefined): boolean; + // (undocumented) + static normalize(value: number, componentType: string | undefined): number; + static unsignedComponentTypes: string[]; +} + // Warning: (ae-internal-missing-underscore) The name "MetadataEntity" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -287,6 +536,27 @@ export interface MetadataEntity extends RootProperty { }; } +// Warning: (ae-internal-missing-underscore) The name "MetadataEntityModel" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface MetadataEntityModel { + getPropertyValue(propertyId: string): any; + getPropertyValueBySemantic(semantic: string): any; +} + +// Warning: (ae-internal-missing-underscore) The name "MetadataEntityModels" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class MetadataEntityModels { + static computeSemanticToPropertyIdMapping(metadataClass: MetadataClass): { + [key: string]: string; + }; + static create(schema: Schema, entity: MetadataEntity): MetadataEntityModel; + static createFromClass(metadataClass: MetadataClass, entityProperties: { + [key: string]: any; + }): DefaultMetadataEntityModel; +} + // Warning: (ae-internal-missing-underscore) The name "MetadataEnum" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -301,6 +571,80 @@ export interface MetadataEnum extends RootProperty { valueType?: string; } +// Warning: (ae-internal-missing-underscore) The name "MetadataError" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class MetadataError extends Error { + constructor(message: string); + // (undocumented) + toString: () => string; +} + +// Warning: (ae-internal-missing-underscore) The name "MetadataTypes" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class MetadataTypes { + static allTypes: string[]; + static componentCountForType(type: string): number; + static isNumericType(type: string): boolean; + static numericTypes: string[]; +} + +// Warning: (ae-internal-missing-underscore) The name "MetadataUtilities" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class MetadataUtilities { + static computeBinaryEnumInfo(schema: Schema): BinaryEnumInfo; + static obtainEnumValueNames(classProperty: ClassProperty, schema: Schema): string[]; +} + +// Warning: (ae-internal-missing-underscore) The name "MetadataValues" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class MetadataValues { + static processValue(classProperty: ClassProperty, offsetOverride: any, scaleOverride: any, value: any): any; +} + +// Warning: (ae-internal-missing-underscore) The name "NumericBuffers" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class NumericBuffers { + static getNumericArrayFromBuffer(buffer: Buffer, index: number, arrayLength: number, componentType: string): any; + static getNumericBufferAsArray(buffer: Buffer, componentType: string): any; + static getNumericFromBuffer(buffer: Buffer, index: number, componentType: string): any; +} + +// Warning: (ae-internal-missing-underscore) The name "OctreeCoordinates" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class OctreeCoordinates implements TreeCoordinates { + constructor(level: number, x: number, y: number, z: number); + children(): IterableIterator; + descendants(maxLevelInclusive: number, depthFirst: boolean): IterableIterator; + get level(): number; + parent(): OctreeCoordinates | null; + toArray(): number[]; + toIndex(): number; + toIndexInLevel(): number; + // (undocumented) + toString: () => string; + // (undocumented) + get x(): number; + // (undocumented) + get y(): number; + // (undocumented) + get z(): number; +} + +// Warning: (ae-internal-missing-underscore) The name "Octrees" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class Octrees { + static computeNumberOfNodesForLevels(levels: number): number; + static coordinatesForLevel(level: number): Generator; + static isValid(c: OctreeCoordinates): boolean; +} + // Warning: (ae-internal-missing-underscore) The name "Paths" should be prefixed with an underscore because the declaration is marked as @internal // // @internal @@ -324,6 +668,13 @@ export interface Properties extends RootProperty { minimum: number; } +// Warning: (ae-internal-missing-underscore) The name "PropertyModel" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface PropertyModel { + getPropertyValue(index: number): any; +} + // Warning: (ae-internal-missing-underscore) The name "PropertyTable" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -340,6 +691,16 @@ export interface PropertyTable extends RootProperty { }; } +// Warning: (ae-internal-missing-underscore) The name "PropertyTableModel" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface PropertyTableModel { + getClassProperty(propertyId: string): ClassProperty | undefined; + getMetadataEntityModel(index: number): MetadataEntityModel; + getPropertyModel(propertyId: string): PropertyModel | undefined; + getPropertyTableProperty(propertyId: string): PropertyTableProperty | undefined; +} + // Warning: (ae-internal-missing-underscore) The name "PropertyTableProperty" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -364,6 +725,35 @@ export interface PropertyTableProperty extends RootProperty { values: number; } +// Warning: (ae-internal-missing-underscore) The name "QuadtreeCoordinates" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class QuadtreeCoordinates implements TreeCoordinates { + constructor(level: number, x: number, y: number); + children(): IterableIterator; + descendants(maxLevelInclusive: number, depthFirst: boolean): IterableIterator; + get level(): number; + parent(): QuadtreeCoordinates | null; + toArray(): number[]; + toIndex(): number; + toIndexInLevel(): number; + // (undocumented) + toString: () => string; + // (undocumented) + get x(): number; + // (undocumented) + get y(): number; +} + +// Warning: (ae-internal-missing-underscore) The name "Quadtrees" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class Quadtrees { + static computeNumberOfNodesForLevels(levels: number): number; + static coordinatesForLevel(level: number): Generator; + static isValid(c: QuadtreeCoordinates): boolean; +} + // Warning: (ae-internal-missing-underscore) The name "ResourceResolver" should be prefixed with an underscore because the declaration is marked as @internal // // @internal @@ -489,6 +879,57 @@ export interface Subtree extends RootProperty { tileMetadata?: number; } +// Warning: (ae-internal-missing-underscore) The name "SubtreeInfo" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface SubtreeInfo { + childSubtreeAvailabilityInfo: AvailabilityInfo; + contentAvailabilityInfos: AvailabilityInfo[]; + tileAvailabilityInfo: AvailabilityInfo; +} + +// Warning: (ae-internal-missing-underscore) The name "SubtreeInfos" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class SubtreeInfos { + static create(binarySubtreeData: BinarySubtreeData, implicitTiling: TileImplicitTiling): SubtreeInfo; + static createFromBuffer(input: Buffer, implicitTiling: TileImplicitTiling, resourceResolver: ResourceResolver): Promise; + static createFromJson(subtree: Subtree, implicitTiling: TileImplicitTiling, resourceResolver: ResourceResolver): Promise; +} + +// Warning: (ae-internal-missing-underscore) The name "SubtreeMetadataModel" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface SubtreeMetadataModel { + contentIndexMappings: number[][]; + contentMetadataModels: PropertyTableModel[]; + schema: Schema; + tileIndexMapping: number[] | undefined; + tileMetadataModel: PropertyTableModel | undefined; +} + +// Warning: (ae-internal-missing-underscore) The name "SubtreeMetadataModels" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class SubtreeMetadataModels { + static create(binarySubtreeData: BinarySubtreeData, subtreeInfo: SubtreeInfo, schema: Schema): SubtreeMetadataModel; +} + +// Warning: (ae-internal-missing-underscore) The name "SubtreeModel" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface SubtreeModel { + subtreeInfo: SubtreeInfo; + subtreeMetadataModel: SubtreeMetadataModel | undefined; +} + +// Warning: (ae-internal-missing-underscore) The name "SubtreeModels" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class SubtreeModels { + static resolve(implicitTiling: TileImplicitTiling, schema: Schema | undefined, resourceResolver: ResourceResolver, coordinates: TreeCoordinates): Promise; +} + // Warning: (ae-internal-missing-underscore) The name "Subtrees" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -497,6 +938,16 @@ export interface Subtrees extends RootProperty { uri: string; } +// Warning: (ae-internal-missing-underscore) The name "TemplateUris" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class TemplateUris { + static substituteOctree(templateUri: string, coordinates: OctreeCoordinates): string; + static substituteOctreeInternal(templateUri: string, level: number, x: number, y: number, z: number): string; + static substituteQuadtree(templateUri: string, coordinates: QuadtreeCoordinates): string; + static substituteQuadtreeInternal(templateUri: string, level: number, x: number, y: number): string; +} + // Warning: (ae-internal-missing-underscore) The name "Tile" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -597,13 +1048,9 @@ export interface TilesetSource { // @internal export class TilesetSource3dtiles implements TilesetSource { constructor(); - // (undocumented) close(): void; - // (undocumented) getKeys(): IterableIterator; - // (undocumented) getValue(key: string): Buffer | undefined; - // (undocumented) open(fullInputName: string): void; } @@ -612,15 +1059,11 @@ export class TilesetSource3dtiles implements TilesetSource { // @internal export class TilesetSource3tz implements TilesetSource { constructor(); - // (undocumented) close(): void; - // (undocumented) getKeys(): IterableIterator; - // (undocumented) getValue(key: string): Buffer | undefined; // (undocumented) getZipIndex(): IndexEntry[] | undefined; - // (undocumented) open(fullInputName: string): void; } @@ -629,13 +1072,9 @@ export class TilesetSource3tz implements TilesetSource { // @internal export class TilesetSourceFs implements TilesetSource { constructor(); - // (undocumented) close(): void; - // (undocumented) getKeys(): IterableIterator; - // (undocumented) getValue(key: string): Buffer | undefined; - // (undocumented) open(fullInputName: string): void; } @@ -644,13 +1083,9 @@ export class TilesetSourceFs implements TilesetSource { // @internal export class TilesetSourceResourceResolver implements ResourceResolver { constructor(basePath: string, tilesetSourceFileName: string, tilesetSource: TilesetSource); - // (undocumented) derive(uri: string): ResourceResolver; - // (undocumented) resolveData(uri: string): Promise; - // (undocumented) resolveDataPartial(uri: string, maxBytes: number): Promise; - // (undocumented) resolveUri(uri: string): string; } @@ -677,11 +1112,8 @@ export interface TilesetTarget { // @internal export class TilesetTarget3dtiles implements TilesetTarget { constructor(); - // (undocumented) addEntry(key: string, content: Buffer): void; - // (undocumented) begin(fullOutputName: string, overwrite: boolean): void; - // (undocumented) end(): Promise; } @@ -690,11 +1122,8 @@ export class TilesetTarget3dtiles implements TilesetTarget { // @internal export class TilesetTarget3tz implements TilesetTarget { constructor(); - // (undocumented) addEntry(key: string, content: Buffer): void; - // (undocumented) begin(fullOutputName: string, overwrite: boolean): void; - // (undocumented) end(): Promise; } @@ -703,11 +1132,8 @@ export class TilesetTarget3tz implements TilesetTarget { // @internal export class TilesetTargetFs implements TilesetTarget { constructor(); - // (undocumented) addEntry(key: string, content: Buffer): void; - // (undocumented) begin(fullOutputName: string, overwrite: boolean): void; - // (undocumented) end(): Promise; } @@ -720,18 +1146,64 @@ export class TilesetTargets { static putEntries(tilesetTarget: TilesetTarget, entries: IterableIterator): void; } +// Warning: (ae-internal-missing-underscore) The name "TilesetTraverser" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class TilesetTraverser { + static traverse(tileset: Tileset, schema: Schema | undefined, resourceResolver: ResourceResolver, traversalCallback: TraversalCallback, depthFirst: boolean): Promise; +} + +// Warning: (ae-internal-missing-underscore) The name "TraversalCallback" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface TraversalCallback { + (traversedTile: TraversedTile): Promise; +} + +// Warning: (ae-internal-missing-underscore) The name "TraversedTile" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface TraversedTile { + asFinalTile(): Tile; + asRawTile(): Tile; + getChildren(): Promise; + // (undocumented) + getFinalContents(): Content[]; + // (undocumented) + getImplicitTiling(): TileImplicitTiling | undefined; + // (undocumented) + getMetadata(): MetadataEntity | undefined; + getParent(): TraversedTile | undefined; + getRawContents(): Content[]; + // (undocumented) + getSubtreeUri(): string | undefined; + get level(): number; + get path(): string; + // (undocumented) + resolveUri(uri: string): string; +} + +// Warning: (ae-internal-missing-underscore) The name "TreeCoordinates" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export interface TreeCoordinates { + children(): IterableIterator; + descendants(maxLevelInclusive: number, depthFirst: boolean): IterableIterator; + get level(): number; + parent(): TreeCoordinates | null; + toArray(): number[]; + toIndex(): number; + toIndexInLevel(): number; +} + // Warning: (ae-internal-missing-underscore) The name "UnzippingResourceResolver" should be prefixed with an underscore because the declaration is marked as @internal // // @internal export class UnzippingResourceResolver implements ResourceResolver { constructor(delegate: ResourceResolver); - // (undocumented) derive(uri: string): ResourceResolver; - // (undocumented) resolveData(uri: string): Promise; - // (undocumented) resolveDataPartial(uri: string, maxBytes: number): Promise; - // (undocumented) resolveUri(uri: string): string; } From e6e691c422afebf832650f57e477e310b0fdf3e0 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sun, 19 Mar 2023 16:13:54 +0100 Subject: [PATCH 20/26] Improve error handling in base classes --- src/base/Buffers.ts | 22 +++++++++++++++++++--- src/base/DataError.ts | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/base/DataError.ts diff --git a/src/base/Buffers.ts b/src/base/Buffers.ts index 35ce67a7..cdc82c47 100644 --- a/src/base/Buffers.ts +++ b/src/base/Buffers.ts @@ -2,6 +2,9 @@ import zlib from "zlib"; import { defined } from "./defined"; +import { DataError } from "./DataError"; + + /** * Methods related to buffers. * @@ -31,11 +34,18 @@ export class Buffers { * * @param inputBuffer - The input buffer * @returns The resulting buffer + * @throws DataError If the buffer looked like GZIPped + * data, but could not be decompressed. */ static gunzip(inputBuffer: Buffer): Buffer { let outputBuffer: Buffer; if (Buffers.isGzipped(inputBuffer)) { - outputBuffer = zlib.gunzipSync(inputBuffer); + try { + outputBuffer = zlib.gunzipSync(inputBuffer); + } catch (e) { + const message = `Could not gunzip buffer: ${e}`; + throw new DataError(message); + } } else { outputBuffer = inputBuffer; } @@ -113,13 +123,19 @@ export class Buffers { * * @param buffer - The buffer * @returns The parsed object - * @throws Possible errors from `JSON:parse` + * @throws DataError If the JSON could not be parsed */ static getJson(buffer: Buffer): any { if (buffer.length === 0) { return {}; } - return JSON.parse(buffer.toString("utf8")); + try { + return JSON.parse(buffer.toString("utf8")); + } catch (e) { + const message = `Could not parse JSON from buffer: ${e}`; + throw new DataError(message); + } + } /** diff --git a/src/base/DataError.ts b/src/base/DataError.ts new file mode 100644 index 00000000..083afffe --- /dev/null +++ b/src/base/DataError.ts @@ -0,0 +1,24 @@ +/** + * An error that may be thrown to indicate that input + * data was invalid. + * + * This may refer to buffers that have been expected to + * contain a certain type of data, but did not. For + * example, a buffer that looked like a GZIpped buffer, + * but turned out to be invalid, or a buffer that + * looked like it contained valid JSON, but did not. + * + * @internal + */ +export class DataError extends Error { + constructor(message: string) { + super(message); + // See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes + // #extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, DataError.prototype); + } + + override toString = (): string => { + return `${this.name}: ${this.message}`; + }; +} From eb09f329c6af50a4c536c83f9dbd756bfee05807 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sun, 19 Mar 2023 16:14:04 +0100 Subject: [PATCH 21/26] Removed unused field --- src/io/TilesetSourceResourceResolver.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/io/TilesetSourceResourceResolver.ts b/src/io/TilesetSourceResourceResolver.ts index 4d389ef2..fd059166 100644 --- a/src/io/TilesetSourceResourceResolver.ts +++ b/src/io/TilesetSourceResourceResolver.ts @@ -14,16 +14,13 @@ import { ResourceResolver } from "./ResourceResolver"; */ export class TilesetSourceResourceResolver implements ResourceResolver { private readonly _basePath: string; - private readonly _tilesetSourceFileName: string; private readonly _tilesetSource: TilesetSource; constructor( basePath: string, - tilesetSourceFileName: string, tilesetSource: TilesetSource ) { this._basePath = basePath; - this._tilesetSourceFileName = tilesetSourceFileName; this._tilesetSource = tilesetSource; } @@ -64,7 +61,6 @@ export class TilesetSourceResourceResolver implements ResourceResolver { const resolved = Paths.join(this._basePath, decodeURIComponent(uri)); const result = new TilesetSourceResourceResolver( resolved, - this._tilesetSourceFileName, this._tilesetSource ); return result; From ac03ad86af0884798ddd544baeca65eddf83f13b Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sun, 19 Mar 2023 16:14:32 +0100 Subject: [PATCH 22/26] Minor generalization of StatsCollector in demo --- demos/TraversalStatsDemo.ts | 115 ++++++++++++++++++++++++------------ 1 file changed, 76 insertions(+), 39 deletions(-) diff --git a/demos/TraversalStatsDemo.ts b/demos/TraversalStatsDemo.ts index 9de0f4cd..fad50e03 100644 --- a/demos/TraversalStatsDemo.ts +++ b/demos/TraversalStatsDemo.ts @@ -24,14 +24,14 @@ async function tilesetTraversalDemo(filePath: string) { // Traverse the tileset, and pass each tile to // the StatsCollector console.log("Traversing tileset"); - const statsCollector = new StatsCollector(); + const tilesetStatsCollector = new TilesetStatsCollector(); const depthFirst = false; await TilesetTraverser.traverse( tileset, schema, resourceResolver, async (traversedTile) => { - statsCollector.accept(traversedTile); + tilesetStatsCollector.accept(traversedTile); return true; }, depthFirst @@ -40,53 +40,25 @@ async function tilesetTraversalDemo(filePath: string) { // Print the statistics summary to the console console.log("Stats:"); - const json = statsCollector.createJson(); + const json = tilesetStatsCollector.createJson(); const jsonString = JSON.stringify(json, null, 2); console.log(jsonString); } -// A simple class to collect statistical information about a tileset, -// from the tiles that are traversed with a TilesetTraverser +// A simple class to collect statistical information class StatsCollector { - private totalNumberOfTiles = 0; - private totalNumberOfSubtrees = 0; + // A mapping from value names to counters + private readonly counters: { + [key: string]: Counter; + } = {}; // A mapping from value names to statistical summaries private readonly summaries: { [key: string]: Summary; } = {}; - // Accept the given tile during traversal, and collect - // statistical information - accept(traversedTile: TraversedTile) { - this.totalNumberOfTiles++; - - // NOTE: This is a means of checking whether a tile - // is the root of an implicit tileset. This may be - // refactored at some point. - if (traversedTile.getImplicitTiling()) { - this.totalNumberOfSubtrees++; - } else { - // Obtain all content URIs, resolve them, and obtain - // the sizes of the corresponding files, storing them - // in the "tileFileSize" summary - const contentUris = traversedTile.getFinalContents().map((c) => c.uri); - for (const contentUri of contentUris) { - const resolvedContentUri = traversedTile.resolveUri(contentUri); - const stats = fs.statSync(resolvedContentUri); - const tileFileSizeInBytes = stats.size; - this.acceptEntry("tileFileSize", tileFileSizeInBytes); - } - } - - // Store the geometric error in the "geometricError" summary - const finalTile = traversedTile.asFinalTile(); - const geometricError = finalTile.geometricError; - this.acceptEntry("geometricError", geometricError); - } - // Add one entry to a summary, creating it when necessary - private acceptEntry(name: string, value: number) { + acceptEntry(name: string, value: number) { let summary = this.summaries[name]; if (!summary) { summary = new Summary(); @@ -95,11 +67,23 @@ class StatsCollector { summary.accept(value); } + // Increment a counter, creating it when necessary + increment(name: string) { + let counter = this.counters[name]; + if (!counter) { + counter = new Counter(); + this.counters[name] = counter; + } + counter.increment(); + } + // Create a short JSON representation of the collected data createJson(): any { const json: any = {}; - json.totalNumberOfTiles = this.totalNumberOfTiles; - json.totalNumberOfSubtrees = this.totalNumberOfSubtrees; + for (const key of Object.keys(this.counters)) { + const counter = this.counters[key]; + json[key] = counter.getCount(); + } for (const key of Object.keys(this.summaries)) { const summary = this.summaries[key]; json[key] = { @@ -115,6 +99,25 @@ class StatsCollector { } } +/** + * A class that serves as a counter in the `StatsCollector` + */ +class Counter { + private count: number; + + public constructor() { + this.count = 0; + } + + increment() { + this.count++; + } + + getCount() { + return this.count; + } +} + /** * A class that can accept numbers, and collects statistical * information for these numbers. @@ -171,6 +174,40 @@ class Summary { } } +// A specialization of the `StatsColleror` that collects +// information about the tiles that are traversed with +// a TilesetTraverser +class TilesetStatsCollector extends StatsCollector { + // Accept the given tile during traversal, and collect + // statistical information + accept(traversedTile: TraversedTile) { + this.increment("totalNumberOfTiles"); + + // NOTE: This is a means of checking whether a tile + // is the root of an implicit tileset. This may be + // refactored at some point. + if (traversedTile.getImplicitTiling()) { + this.increment("totalNumberOfSubtres"); + } else { + // Obtain all content URIs, resolve them, and obtain + // the sizes of the corresponding files, storing them + // in the "tileFileSize" summary + const contentUris = traversedTile.getFinalContents().map((c) => c.uri); + for (const contentUri of contentUris) { + const resolvedContentUri = traversedTile.resolveUri(contentUri); + const stats = fs.statSync(resolvedContentUri); + const tileFileSizeInBytes = stats.size; + this.acceptEntry("tileFileSize", tileFileSizeInBytes); + } + } + + // Store the geometric error in the "geometricError" summary + const finalTile = traversedTile.asFinalTile(); + const geometricError = finalTile.geometricError; + this.acceptEntry("geometricError", geometricError); + } +} + async function runDemo() { const tilesetFileName = "../3d-tiles-samples/1.1/SparseImplicitQuadtree/tileset.json"; From 1bb03cfcb9a38b2260ed30d8c50a6a24022a859d Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 22 Mar 2023 18:47:51 +0100 Subject: [PATCH 23/26] Renamed directory, moved some classes to own directory --- demos/TileFormatsDemoConversions.ts | 2 +- src/ToolsMain.ts | 4 +- src/base/Buffers.ts | 2 - src/base/DataError.ts | 6 +- .../ContentOps.ts | 0 .../GltfPipelineLegacy.ts | 0 .../GtlfUtilities.ts | 0 src/implicitTiling/AvailabilityInfo.ts | 2 +- src/implicitTiling/AvailabilityInfos.ts | 9 +- src/implicitTiling/ImplicitTilingError.ts | 2 +- src/implicitTiling/ImplicitTilings.ts | 12 +- src/implicitTiling/MortonOrder.ts | 105 ------------------ src/implicitTiling/SubtreeInfo.ts | 2 +- src/implicitTiling/SubtreeInfos.ts | 2 +- src/implicitTiling/TemplateUris.ts | 6 +- src/index.ts | 12 +- src/io/TilesetSourceResourceResolver.ts | 5 +- src/metadata/ArrayValues.ts | 2 +- src/metadata/ClassProperties.ts | 2 +- src/metadata/MetadataError.ts | 2 +- .../OctreeCoordinates.ts | 2 +- src/{implicitTiling => spatial}/Octrees.ts | 2 +- .../QuadtreeCoordinates.ts | 2 +- src/{implicitTiling => spatial}/Quadtrees.ts | 2 +- .../TreeCoordinates.ts | 2 +- src/tilesetProcessing/TilesetUpgrader.ts | 2 +- src/traversal/ImplicitTraversedTile.ts | 3 +- src/traversal/SubtreeModel.ts | 2 +- src/traversal/SubtreeModels.ts | 5 +- 29 files changed, 46 insertions(+), 153 deletions(-) rename src/{contentOperations => contentProcessing}/ContentOps.ts (100%) rename src/{contentOperations => contentProcessing}/GltfPipelineLegacy.ts (100%) rename src/{contentOperations => contentProcessing}/GtlfUtilities.ts (100%) delete mode 100644 src/implicitTiling/MortonOrder.ts rename src/{implicitTiling => spatial}/OctreeCoordinates.ts (99%) rename src/{implicitTiling => spatial}/Octrees.ts (99%) rename src/{implicitTiling => spatial}/QuadtreeCoordinates.ts (99%) rename src/{implicitTiling => spatial}/Quadtrees.ts (99%) rename src/{implicitTiling => spatial}/TreeCoordinates.ts (99%) diff --git a/demos/TileFormatsDemoConversions.ts b/demos/TileFormatsDemoConversions.ts index 22983f10..80110419 100644 --- a/demos/TileFormatsDemoConversions.ts +++ b/demos/TileFormatsDemoConversions.ts @@ -2,7 +2,7 @@ import fs from "fs"; import { Paths } from "../src/base/Paths"; -import { GltfUtilities } from "../src/contentOperations/GtlfUtilities"; +import { GltfUtilities } from "../src/contentProcessing/GtlfUtilities"; import { TileFormats } from "../src/tileFormats/TileFormats"; function glbToB3dm(inputFileName: string, outputFileName: string) { diff --git a/src/ToolsMain.ts b/src/ToolsMain.ts index c3d18a82..698d5aae 100644 --- a/src/ToolsMain.ts +++ b/src/ToolsMain.ts @@ -7,8 +7,8 @@ import { Tilesets } from "./tilesets/Tilesets"; import { TileFormats } from "./tileFormats/TileFormats"; -import { ContentOps } from "./contentOperations/ContentOps"; -import { GltfUtilities } from "./contentOperations/GtlfUtilities"; +import { ContentOps } from "./contentProcessing/ContentOps"; +import { GltfUtilities } from "./contentProcessing/GtlfUtilities"; import { ContentDataTypes } from "./contentTypes/ContentDataTypes"; diff --git a/src/base/Buffers.ts b/src/base/Buffers.ts index cdc82c47..a77d45c7 100644 --- a/src/base/Buffers.ts +++ b/src/base/Buffers.ts @@ -4,7 +4,6 @@ import { defined } from "./defined"; import { DataError } from "./DataError"; - /** * Methods related to buffers. * @@ -135,7 +134,6 @@ export class Buffers { const message = `Could not parse JSON from buffer: ${e}`; throw new DataError(message); } - } /** diff --git a/src/base/DataError.ts b/src/base/DataError.ts index 083afffe..7583154f 100644 --- a/src/base/DataError.ts +++ b/src/base/DataError.ts @@ -1,11 +1,11 @@ /** * An error that may be thrown to indicate that input - * data was invalid. - * + * data was invalid. + * * This may refer to buffers that have been expected to * contain a certain type of data, but did not. For * example, a buffer that looked like a GZIpped buffer, - * but turned out to be invalid, or a buffer that + * but turned out to be invalid, or a buffer that * looked like it contained valid JSON, but did not. * * @internal diff --git a/src/contentOperations/ContentOps.ts b/src/contentProcessing/ContentOps.ts similarity index 100% rename from src/contentOperations/ContentOps.ts rename to src/contentProcessing/ContentOps.ts diff --git a/src/contentOperations/GltfPipelineLegacy.ts b/src/contentProcessing/GltfPipelineLegacy.ts similarity index 100% rename from src/contentOperations/GltfPipelineLegacy.ts rename to src/contentProcessing/GltfPipelineLegacy.ts diff --git a/src/contentOperations/GtlfUtilities.ts b/src/contentProcessing/GtlfUtilities.ts similarity index 100% rename from src/contentOperations/GtlfUtilities.ts rename to src/contentProcessing/GtlfUtilities.ts diff --git a/src/implicitTiling/AvailabilityInfo.ts b/src/implicitTiling/AvailabilityInfo.ts index 082fb6b0..5680ea23 100644 --- a/src/implicitTiling/AvailabilityInfo.ts +++ b/src/implicitTiling/AvailabilityInfo.ts @@ -2,7 +2,7 @@ * An interface that describes the availability information * in a subtree. This is used for tile, content, and child * subtree availability. - * + * * @internal */ export interface AvailabilityInfo { diff --git a/src/implicitTiling/AvailabilityInfos.ts b/src/implicitTiling/AvailabilityInfos.ts index 8ee9873a..da874208 100644 --- a/src/implicitTiling/AvailabilityInfos.ts +++ b/src/implicitTiling/AvailabilityInfos.ts @@ -1,12 +1,13 @@ +import { defined } from "../base/defined"; + import { AvailabilityInfo } from "./AvailabilityInfo"; import { BufferAvailabilityInfo } from "./BufferAvailabilityInfo"; import { ConstantAvailabilityInfo } from "./ConstantAvailabilityInfo"; import { ImplicitTilings } from "./ImplicitTilings"; +import { ImplicitTilingError } from "./ImplicitTilingError"; import { Availability } from "../structure/Availability"; import { TileImplicitTiling } from "../structure/TileImplicitTiling"; -import { defined } from "../base/defined"; -import { MetadataError } from "../metadata/MetadataError"; /** * Methods for creating `AvailabilityInfo` instances @@ -66,7 +67,7 @@ export class AvailabilityInfos { * @param bufferViewDatas - The `BufferView` data chunks * @param length - The length of the availability info * @returns The `AvailabilityInfo` object - * @throws MetadataError If the data is structurally invalid + * @throws ImplicitTilingError If the data is structurally invalid */ private static create( availability: Availability, @@ -81,7 +82,7 @@ export class AvailabilityInfos { // The bitstream MUST be defined when constant is undefined const bitstream = availability.bitstream; if (!defined(bitstream)) { - throw new MetadataError( + throw new ImplicitTilingError( "The availability neither defines a constant nor a bitstream" ); } diff --git a/src/implicitTiling/ImplicitTilingError.ts b/src/implicitTiling/ImplicitTilingError.ts index 8c38c4e2..b3453f93 100644 --- a/src/implicitTiling/ImplicitTilingError.ts +++ b/src/implicitTiling/ImplicitTilingError.ts @@ -4,7 +4,7 @@ * * This may be thrown by methods that create the convenience classes * for this package, when the given resources are not valid. - * + * * @internal */ export class ImplicitTilingError extends Error { diff --git a/src/implicitTiling/ImplicitTilings.ts b/src/implicitTiling/ImplicitTilings.ts index 7d069313..6f3f7595 100644 --- a/src/implicitTiling/ImplicitTilings.ts +++ b/src/implicitTiling/ImplicitTilings.ts @@ -1,10 +1,10 @@ -import { TreeCoordinates } from "./TreeCoordinates"; -import { Quadtrees } from "./Quadtrees"; -import { QuadtreeCoordinates } from "./QuadtreeCoordinates"; -import { Octrees } from "./Octrees"; -import { OctreeCoordinates } from "./OctreeCoordinates"; -import { TemplateUris } from "./TemplateUris"; +import { TreeCoordinates } from "../spatial/TreeCoordinates"; +import { Quadtrees } from "../spatial/Quadtrees"; +import { QuadtreeCoordinates } from "../spatial/QuadtreeCoordinates"; +import { Octrees } from "../spatial/Octrees"; +import { OctreeCoordinates } from "../spatial/OctreeCoordinates"; +import { TemplateUris } from "./TemplateUris"; import { ImplicitTilingError } from "./ImplicitTilingError"; import { TileImplicitTiling } from "../structure/TileImplicitTiling"; diff --git a/src/implicitTiling/MortonOrder.ts b/src/implicitTiling/MortonOrder.ts deleted file mode 100644 index 3509dfd8..00000000 --- a/src/implicitTiling/MortonOrder.ts +++ /dev/null @@ -1,105 +0,0 @@ -// Ported from https://github.com/CesiumGS/cesium/blob/4b333bc145fa9f7aed0c7ad7e0f46cb001a94ddd/Source/Core/MortonOrder.js - -/** - * Morton Order (aka Z-Order Curve) helper functions. - * @see {@link https://en.wikipedia.org/wiki/Z-order_curve} - * - * @internal - */ -export class MortonOrder { - /** - * Inserts one 0 bit of spacing between a number's bits. This is the opposite of removeOneSpacing. - * - * Example: - * input: 6 - * input (binary): 110 - * output (binary): 10100 - * ^ ^ (added) - * output: 20 - * - * @param v - A 16-bit unsigned integer. - * @returns A 32-bit unsigned integer. - * @see {@link https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/} - */ - private static insertOneSpacing(v: number): number { - v = (v ^ (v << 8)) & 0x00ff00ff; - v = (v ^ (v << 4)) & 0x0f0f0f0f; - v = (v ^ (v << 2)) & 0x33333333; - v = (v ^ (v << 1)) & 0x55555555; - return v; - } - - /** - * Inserts two 0 bits of spacing between a number's bits. This is the opposite of removeTwoSpacing. - * - * Example: - * input: 6 - * input (binary): 110 - * output (binary): 1001000 - * ^^ ^^ (added) - * output: 72 - * - * @internal - * @param v - A 10-bit unsigned integer. - * @returns A 30-bit unsigned integer. - * @see {@link https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/} - */ - private static insertTwoSpacing(v: number): number { - v = (v ^ (v << 16)) & 0x030000ff; - v = (v ^ (v << 8)) & 0x0300f00f; - v = (v ^ (v << 4)) & 0x030c30c3; - v = (v ^ (v << 2)) & 0x09249249; - return v; - } - - /** - * Computes the Morton index from 2D coordinates. This is equivalent to interleaving their bits. - * The inputs must be 16-bit unsigned integers (resulting in 32-bit Morton index) due to 32-bit bitwise operator limitation in JavaScript. - * - * @param x - The X coordinate in the range [0, (2^16)-1]. - * @param y - The Y coordinate in the range [0, (2^16)-1]. - * @returns The Morton index. - * @internal - */ - static encode2D(x: number, y: number): number { - //>>includeStart('debug', pragmas.debug); - if (x < 0 || x > 65535 || y < 0 || y > 65535) { - throw new Error("inputs must be 16-bit unsigned integers"); - } - //>>includeEnd('debug'); - - // Note: JavaScript bitwise operations return signed 32-bit integers, so the - // final result needs to be reintepreted as an unsigned integer using >>> 0. - // This is not needed for encode3D because the result is guaranteed to be at most - // 30 bits and thus will always be interpreted as an unsigned value. - return ( - (MortonOrder.insertOneSpacing(x) | - (MortonOrder.insertOneSpacing(y) << 1)) >>> - 0 - ); - } - - /** - * Computes the Morton index from 3D coordinates. This is equivalent to interleaving their bits. - * The inputs must be 10-bit unsigned integers (resulting in 30-bit Morton index) due to 32-bit bitwise operator limitation in JavaScript. - * - * @param x - The X coordinate in the range [0, (2^10)-1]. - * @param y - The Y coordinate in the range [0, (2^10)-1]. - * @param z - The Z coordinate in the range [0, (2^10)-1]. - * @returns The Morton index. - * @internal - */ - static encode3D(x: number, y: number, z: number): number { - //>>includeStart('debug', pragmas.debug); - if (x < 0 || x > 1023 || y < 0 || y > 1023 || z < 0 || z > 1023) { - throw new Error("inputs must be 10-bit unsigned integers"); - } - //>>includeEnd('debug'); - - return ( - MortonOrder.insertTwoSpacing(x) | - (MortonOrder.insertTwoSpacing(y) << 1) | - (MortonOrder.insertTwoSpacing(z) << 2) - ); - } -} diff --git a/src/implicitTiling/SubtreeInfo.ts b/src/implicitTiling/SubtreeInfo.ts index 02bc3a6a..0887d619 100644 --- a/src/implicitTiling/SubtreeInfo.ts +++ b/src/implicitTiling/SubtreeInfo.ts @@ -6,7 +6,7 @@ import { AvailabilityInfo } from "./AvailabilityInfo"; * * It offers the availability information for tiles, child * subtrees, and contents, as `AvailabilityInfo` objects. - * + * * @internal */ export interface SubtreeInfo { diff --git a/src/implicitTiling/SubtreeInfos.ts b/src/implicitTiling/SubtreeInfos.ts index ca8555fc..906fbea1 100644 --- a/src/implicitTiling/SubtreeInfos.ts +++ b/src/implicitTiling/SubtreeInfos.ts @@ -10,7 +10,7 @@ import { TileImplicitTiling } from "../structure/TileImplicitTiling"; /** * Methods to create `SubtreeInfo` instances. - * + * * @internal */ export class SubtreeInfos { diff --git a/src/implicitTiling/TemplateUris.ts b/src/implicitTiling/TemplateUris.ts index a70e9c71..e10b08fa 100644 --- a/src/implicitTiling/TemplateUris.ts +++ b/src/implicitTiling/TemplateUris.ts @@ -1,9 +1,9 @@ -import { OctreeCoordinates } from "./OctreeCoordinates"; -import { QuadtreeCoordinates } from "./QuadtreeCoordinates"; +import { OctreeCoordinates } from "../spatial/OctreeCoordinates"; +import { QuadtreeCoordinates } from "../spatial/QuadtreeCoordinates"; /** * Method related to template URIs for implicit tiling. - * + * * @internal */ export class TemplateUris { diff --git a/src/index.ts b/src/index.ts index 3b722941..ca45c409 100644 --- a/src/index.ts +++ b/src/index.ts @@ -77,14 +77,15 @@ export * from "./implicitTiling/BinarySubtreeData"; export * from "./implicitTiling/BinarySubtreeDataResolver"; export * from "./implicitTiling/ImplicitTilingError"; export * from "./implicitTiling/ImplicitTilings"; -export * from "./implicitTiling/OctreeCoordinates"; -export * from "./implicitTiling/Octrees"; -export * from "./implicitTiling/QuadtreeCoordinates"; -export * from "./implicitTiling/Quadtrees"; export * from "./implicitTiling/SubtreeInfo"; export * from "./implicitTiling/SubtreeInfos"; export * from "./implicitTiling/TemplateUris"; -export * from "./implicitTiling/TreeCoordinates"; + +export * from "./spatial/OctreeCoordinates"; +export * from "./spatial/Octrees"; +export * from "./spatial/QuadtreeCoordinates"; +export * from "./spatial/Quadtrees"; +export * from "./spatial/TreeCoordinates"; export * from "./metadata/ArrayValues"; export * from "./metadata/ClassProperties"; @@ -115,4 +116,3 @@ export * from "./traversal/SubtreeModels"; export * from "./traversal/TilesetTraverser"; export * from "./traversal/TraversedTile"; export * from "./traversal/TraversalCallback"; - diff --git a/src/io/TilesetSourceResourceResolver.ts b/src/io/TilesetSourceResourceResolver.ts index fd059166..4aa44548 100644 --- a/src/io/TilesetSourceResourceResolver.ts +++ b/src/io/TilesetSourceResourceResolver.ts @@ -16,10 +16,7 @@ export class TilesetSourceResourceResolver implements ResourceResolver { private readonly _basePath: string; private readonly _tilesetSource: TilesetSource; - constructor( - basePath: string, - tilesetSource: TilesetSource - ) { + constructor(basePath: string, tilesetSource: TilesetSource) { this._basePath = basePath; this._tilesetSource = tilesetSource; } diff --git a/src/metadata/ArrayValues.ts b/src/metadata/ArrayValues.ts index 03327422..a218a4ba 100644 --- a/src/metadata/ArrayValues.ts +++ b/src/metadata/ArrayValues.ts @@ -36,7 +36,7 @@ type NumberND = NumberScalar | NumberND[]; * that the values have the same structure, i.e. they are * both numeric/numbers or arrays with the same length. If this * is not the case, then a `MetadataError` will be thrown. - * + * * @internal */ export class ArrayValues { diff --git a/src/metadata/ClassProperties.ts b/src/metadata/ClassProperties.ts index 35355aca..91913ca8 100644 --- a/src/metadata/ClassProperties.ts +++ b/src/metadata/ClassProperties.ts @@ -7,7 +7,7 @@ import { ClassProperty } from "../structure/Metadata/ClassProperty"; /** * Utility methods related to `ClassProperty` objects - * + * * @internal */ export class ClassProperties { diff --git a/src/metadata/MetadataError.ts b/src/metadata/MetadataError.ts index 2acbefee..40da61c3 100644 --- a/src/metadata/MetadataError.ts +++ b/src/metadata/MetadataError.ts @@ -3,7 +3,7 @@ * * This may be thrown by methods that create the convenience classes * for this package, when the given inputs are not valid. - * + * * @internal */ export class MetadataError extends Error { diff --git a/src/implicitTiling/OctreeCoordinates.ts b/src/spatial/OctreeCoordinates.ts similarity index 99% rename from src/implicitTiling/OctreeCoordinates.ts rename to src/spatial/OctreeCoordinates.ts index 5c38e3c3..27dfaf23 100644 --- a/src/implicitTiling/OctreeCoordinates.ts +++ b/src/spatial/OctreeCoordinates.ts @@ -4,7 +4,7 @@ import { TreeCoordinates } from "./TreeCoordinates"; /** * An implementation of `TreeCoordinates` for octrees - * + * * @internal */ export class OctreeCoordinates implements TreeCoordinates { diff --git a/src/implicitTiling/Octrees.ts b/src/spatial/Octrees.ts similarity index 99% rename from src/implicitTiling/Octrees.ts rename to src/spatial/Octrees.ts index 70707e4f..e2f0012d 100644 --- a/src/implicitTiling/Octrees.ts +++ b/src/spatial/Octrees.ts @@ -2,7 +2,7 @@ import { OctreeCoordinates } from "./OctreeCoordinates"; /** * Methods related to octrees - * + * * @internal */ export class Octrees { diff --git a/src/implicitTiling/QuadtreeCoordinates.ts b/src/spatial/QuadtreeCoordinates.ts similarity index 99% rename from src/implicitTiling/QuadtreeCoordinates.ts rename to src/spatial/QuadtreeCoordinates.ts index aeea721f..5292a45e 100644 --- a/src/implicitTiling/QuadtreeCoordinates.ts +++ b/src/spatial/QuadtreeCoordinates.ts @@ -4,7 +4,7 @@ import { TreeCoordinates } from "./TreeCoordinates"; /** * An implementation of `TreeCoordinates` for octrees - * + * * @internal */ export class QuadtreeCoordinates implements TreeCoordinates { diff --git a/src/implicitTiling/Quadtrees.ts b/src/spatial/Quadtrees.ts similarity index 99% rename from src/implicitTiling/Quadtrees.ts rename to src/spatial/Quadtrees.ts index 826069e5..4a6a8e98 100644 --- a/src/implicitTiling/Quadtrees.ts +++ b/src/spatial/Quadtrees.ts @@ -2,7 +2,7 @@ import { QuadtreeCoordinates } from "./QuadtreeCoordinates"; /** * Methods related to quadtrees. - * + * * @internal */ export class Quadtrees { diff --git a/src/implicitTiling/TreeCoordinates.ts b/src/spatial/TreeCoordinates.ts similarity index 99% rename from src/implicitTiling/TreeCoordinates.ts rename to src/spatial/TreeCoordinates.ts index 041abfee..50018d41 100644 --- a/src/implicitTiling/TreeCoordinates.ts +++ b/src/spatial/TreeCoordinates.ts @@ -1,6 +1,6 @@ /** * An interface for coordinates within a tree structure. - * + * * @internal */ export interface TreeCoordinates { diff --git a/src/tilesetProcessing/TilesetUpgrader.ts b/src/tilesetProcessing/TilesetUpgrader.ts index c5696ea4..eb7c1309 100644 --- a/src/tilesetProcessing/TilesetUpgrader.ts +++ b/src/tilesetProcessing/TilesetUpgrader.ts @@ -19,7 +19,7 @@ import { Tilesets } from "../tilesets/Tilesets"; import { TileFormats } from "../tileFormats/TileFormats"; -import { GltfUtilities } from "../contentOperations/GtlfUtilities"; +import { GltfUtilities } from "../contentProcessing/GtlfUtilities"; /** * The options for the upgrade. This is only used internally, diff --git a/src/traversal/ImplicitTraversedTile.ts b/src/traversal/ImplicitTraversedTile.ts index f7fecdd5..32bbd144 100644 --- a/src/traversal/ImplicitTraversedTile.ts +++ b/src/traversal/ImplicitTraversedTile.ts @@ -11,7 +11,8 @@ import { Content } from "../structure/Content"; import { MetadataEntity } from "../structure/MetadataEntity"; import { TileImplicitTiling } from "../structure/TileImplicitTiling"; -import { TreeCoordinates } from "../implicitTiling/TreeCoordinates"; +import { TreeCoordinates } from "../spatial/TreeCoordinates"; + import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; import { ImplicitTilings } from "../implicitTiling/ImplicitTilings"; diff --git a/src/traversal/SubtreeModel.ts b/src/traversal/SubtreeModel.ts index 6bfc1aea..b29719b1 100644 --- a/src/traversal/SubtreeModel.ts +++ b/src/traversal/SubtreeModel.ts @@ -3,7 +3,7 @@ import { SubtreeMetadataModel } from "./SubtreeMetadataModel"; /** * An interface that summarizes the information for a subtree. - * + * * @internal */ export interface SubtreeModel { diff --git a/src/traversal/SubtreeModels.ts b/src/traversal/SubtreeModels.ts index 2837075a..8c90170e 100644 --- a/src/traversal/SubtreeModels.ts +++ b/src/traversal/SubtreeModels.ts @@ -10,7 +10,8 @@ import { BinarySubtreeDataResolver } from "../implicitTiling/BinarySubtreeDataRe import { ImplicitTilingError } from "../implicitTiling/ImplicitTilingError"; import { ImplicitTilings } from "../implicitTiling/ImplicitTilings"; import { SubtreeInfos } from "../implicitTiling/SubtreeInfos"; -import { TreeCoordinates } from "../implicitTiling/TreeCoordinates"; + +import { TreeCoordinates } from "../spatial/TreeCoordinates"; import { Subtree } from "../structure/Subtree"; import { TileImplicitTiling } from "../structure/TileImplicitTiling"; @@ -25,7 +26,7 @@ import { SubtreeMetadataModels } from "./SubtreeMetadataModels"; * The methods will resolve the data for a subtree, based on the template * URI from the implicit tiling and the root coordinates of the subtree, * and offer this information as `SubtreeModel` objects. - * + * * @internal */ export class SubtreeModels { From 47ff5a6460f6b1afa845540e0443a5dca150650c Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 22 Mar 2023 18:48:09 +0100 Subject: [PATCH 24/26] Update implementation notes --- IMPLEMENTATION.md | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index 0171c3aa..caf4601a 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -9,29 +9,51 @@ Parts of the current implementation may still change. This page is only a short - `./src/base`: Generic, low-level utility functions. Most of the functions here are grouped into classes. - `Buffers`: Padding, detecting GZIPpedness, obtaining "magic" bytes... + - `DataError`: An error indicating that input data was invalid on a low level (e.g. invalid binary data, or unparseable JSON) - `DeveloperError`: An error that was caused by the _developer_ (i.e. someone used the API in a wrong way) - `Iterables`: Iterating over files, filtering, mapping... - `Paths`: Resolving, checking/changing file extensions, ... - `Uris`: Detect data URIs or absolute URIs - Special cases: `defined` and `defaultValue`. These have some documentation explaining why they should rarely be used in TypeScript. -- `./src/contentOperations`: Operations that are applied to tile content - - `ContentOps`: Functions like ZIP/unZIP, conversions like `glbToB3dm`, _always_ operating on buffers: "Buffer in - Buffer out" +- `./src/contentProcessing`: Operations that are applied to tile content + - `ContentOps`: Functions like `glbToB3dm`, _always_ operating on buffers: "Buffer in - Buffer out" - `GltfUtilites`/`GltfPipelineLegacy`: Wrappers around `gltf-pipeline` (e.g. for optimizing/upgrading the GLB in a B3DM) - `./src/contentTypes`: Classes for determining the type of content data - `ContentData` as the main interface, implemented as `BufferedContentData` (to be created from a buffer that already exists in memory), or `LazyContentData` (that resolves "as little data as possible" to determine the content type) - - `ContentDataTypeRegistry`: Receives a `ContentData` object and returns a string that indicates the type, like `CONTENT_TYPE_B3DM` or `CONTENT_TYPE_TILESET`. + - `ContentDataTypes`: A set of strings representing different content data types, like `CONTENT_TYPE_B3DM` or `CONTENT_TYPE_TILESET`. + - `ContentDataTypeRegistry`: Receives a `ContentData` object and returns one of the `ContentDataTypes` strings - `ContentDataTypeChecks`: Offers methods to create predicates that check for certain `included/excluded` content types +- `./src/implicitTiling/`: Classes that represent the structure and information of implicit tilesets + - `AvailabilityInfo`: A simple interface for representing information about the availability of tiles, content, or child subtrees in implicit tiling. This is accessed with an _index_. Instances of classes implementing this interface can be created with the `AvailabilityInfos` class. + - `SubtreeInfo`: A structure that combines the `AvailabilityInfo` for tiles, content, and child subtrees (as it is defined in the input data). Instances of this structure can be created from a subtree JSON file or from binary subtree data, using the `SubtreeInfos` class. + - `BinarySubtreeData`: A simple structure from which the `SubtreeInfo` is created. It combines the data that represents a 'subtree' in implicit tiling, in its 'raw' form: It contains the `Subtree` JSON object, as well as a `BinaryBufferStructure`/`BinaryBufferData` that was created from the `buffers/bufferViews` and the resolved binary data + - `BinarySubtreeDataResolver`: A class that receives a `Subtree` JSON object, and returns the `BinarySubtreeData`, resolving all external `buffer.uri` references + - `ImplicitTilingError`: An error indicating that implicit tiling data was structurally invalid + - `ImplicitTilings`: Methods that try to hide the difference between `QUADTREE` and `OCTREE` tilings. They usually receive a `TileImplicitTiling` JSON object, and perform operations that _depend_ on the `subdivisionScheme`, but can be applied _agnostically_ of the subvision scheme. (Note: Some of this could be done in a cleaner and more generic way, involving ... generics (sic). This _does_ already exist (in a different language), but carrying type parameters along, as in `Availability>` can look obscure and "overengineered" at the first glance. I only hope that the current solution here does not turn out to be _underengineered_ ...) + - `TemplateUris`: Internal method to substitute quadtree- or octree coordinates into template URIs + - `./src/io`: Classes for "loading data" in a very generic way, from different sources - `ResourceResolver` is the main interface, with the core functionality of receiving a URI and returning the data for that URI, with implementations to obtain that data e.g. from a file system or from a 3D Tiles Package. +- `./src/metadata/`: Classes for an implementation of the 3D Metadata Specification + - Utilities for dealing with the JSON representations of metadata objects `ClassProperties`/`MetadataTypes`/`MetadataComponentTypes`... + - Internal utilities for processing metadata values (e.g. normalization, `offset` and `scale` etc.), in `MetadataValues` and `ArrayValues`. + - The `PropertyTableModel`, `MetadataEntityModel` and `PropertyModel` interfaces offer a very thin and simple abstraction layer for 3D Metadata. The structure of these classes is shown here: + ![PropertyTable](figures/PropertyTable.png) + - Implementations of these interfaces exist: + - For the JSON-based representation of metadata entities, metadata entity model instances can be created with `MetadataEntityModels` + - `./src/metadata/binary` contains implementations of the metadata interfaces for _binary_ data, with `BinaryPropertyTableModel` being the top-level class, implementing the `PropertyTableModel` interface. + - `./src/packages`: Classes for reading or creating 3D Tiles Package files - These are implementations of the `TilesetSource` and `TilesetTarget` interface (see `./src/tilesetData`), based on 3TZ or 3DTILES - `./src/pipelines`: **Preliminary** classes for modeling "processing pipelines" for tilesets +- `./src/spatial`: Basic classes for dealing with tree structures, specifically with quadtrees and octrees + - `./src/structure`: Plain old data objects for the elements of a Tileset JSON - E.g. `Tileset`, `Tile`, `Content`, ... - The goal is to have a _typed_ representation of a tileset JSON (assuming that the input JSON was indeed structurally valid - there are no validations or actual type checks during deserialization) @@ -56,6 +78,11 @@ Parts of the current implementation may still change. This page is only a short - `Tiles` for traversing (explicit!) tile hierarchies - `Tilesets` offering convenience functions for `merge/combine/upgrade` +- `./src/traversal`: Classes for traversing tilesets + - NOTE: The `SubtreeModel`/`SubtreeMetadataModel` interfaces _might_ at some point be moved into `implicitTiling`, but are currently tailored for the use in the traversal classes, and should be considered to be an "implementation detail" here. + - The `TilesetTraverser` class is the entry point for the traversal. It allows traversing a tileset, and offer each traversed tile as a `TraversedTile` instance. The `TraversedTile` describes a tile during traversal (e.g. with a parent, and semantic-based overrides) + + ## Demos The `./demos/` folder contains demos that show how to use various parts of the API @@ -71,6 +98,8 @@ The `./demos/` folder contains demos that show how to use various parts of the A - `TileFormatsDemoConversions`: Demos showing how to use the `TileFormats` class for conversions (like extracting GLB from B3DM etc.) - `TilesetProcessingDemos`: Demos for the `combine/merge/upgrade` functions - `TilesetUpgraderDemos`: More fine-grained demos for the upgrade functionality +- `TraversalDemo`: A basic example showing how to traverse a tileset +- `TraversalStatsDemo`: A basic example showing how to traverse a tileset and collect statistical information in the process. The `./demos/benchmarks` folder contains very basic benchmarks for creating/reading different 3D Tiles package formats. From 486f57b8e8e00aea382876a9af01f9d5bf2b3607 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 22 Mar 2023 18:50:40 +0100 Subject: [PATCH 25/26] Added figure for property table --- figures/PropertyTable.png | Bin 0 -> 205368 bytes figures/PropertyTable.svg | 591 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 591 insertions(+) create mode 100644 figures/PropertyTable.png create mode 100644 figures/PropertyTable.svg diff --git a/figures/PropertyTable.png b/figures/PropertyTable.png new file mode 100644 index 0000000000000000000000000000000000000000..a9db2917966a6a08f34c4a3b2436604cd81ef843 GIT binary patch literal 205368 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

IVKR%C(!#BtvB^p-GP& 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 + + + + + + + + + + + + + index + 0 + "red" + "green" + "blue" + "yellow" + 1 + 2 + 2 + 1994 + 2003 + 1987 + 2012 + 3 + 1 + 2 + 3 + color + year + stories + + + + + + + + + + + PropertyTableModel + MetadataEntityModel + PropertyModel + + + + + + getPropertyModel(propertyId: string) + getMetadataEntityModel(index: number) + getPropertyValue(index: number) + Represents one property - that is, one column of the table. The values in thiscolumn can be accessed with an index. + Each column is a PropertyModel, that can be accessed with the property ID (column name). + Represents a property table with rows and columns. + Each row is a MetadataEntityModel, that canbe accessed with the row index. + Represents one metadata entity - that is,one row of the table. The values in thisentity can be accessed with a property ID. + Example: + getPropertyValue(propertyId: string) + m.getPropertyValue("color") === "green" + Example: + m.getPropertyValue(1) === 2003 + + From 49f8dca010814cdc3e18904e00a9bc28788c0e12 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 22 Mar 2023 18:51:05 +0100 Subject: [PATCH 26/26] Added class that was missed while moving --- src/spatial/MortonOrder.ts | 105 +++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/spatial/MortonOrder.ts diff --git a/src/spatial/MortonOrder.ts b/src/spatial/MortonOrder.ts new file mode 100644 index 00000000..3509dfd8 --- /dev/null +++ b/src/spatial/MortonOrder.ts @@ -0,0 +1,105 @@ +// Ported from https://github.com/CesiumGS/cesium/blob/4b333bc145fa9f7aed0c7ad7e0f46cb001a94ddd/Source/Core/MortonOrder.js + +/** + * Morton Order (aka Z-Order Curve) helper functions. + * @see {@link https://en.wikipedia.org/wiki/Z-order_curve} + * + * @internal + */ +export class MortonOrder { + /** + * Inserts one 0 bit of spacing between a number's bits. This is the opposite of removeOneSpacing. + * + * Example: + * input: 6 + * input (binary): 110 + * output (binary): 10100 + * ^ ^ (added) + * output: 20 + * + * @param v - A 16-bit unsigned integer. + * @returns A 32-bit unsigned integer. + * @see {@link https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/} + */ + private static insertOneSpacing(v: number): number { + v = (v ^ (v << 8)) & 0x00ff00ff; + v = (v ^ (v << 4)) & 0x0f0f0f0f; + v = (v ^ (v << 2)) & 0x33333333; + v = (v ^ (v << 1)) & 0x55555555; + return v; + } + + /** + * Inserts two 0 bits of spacing between a number's bits. This is the opposite of removeTwoSpacing. + * + * Example: + * input: 6 + * input (binary): 110 + * output (binary): 1001000 + * ^^ ^^ (added) + * output: 72 + * + * @internal + * @param v - A 10-bit unsigned integer. + * @returns A 30-bit unsigned integer. + * @see {@link https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/} + */ + private static insertTwoSpacing(v: number): number { + v = (v ^ (v << 16)) & 0x030000ff; + v = (v ^ (v << 8)) & 0x0300f00f; + v = (v ^ (v << 4)) & 0x030c30c3; + v = (v ^ (v << 2)) & 0x09249249; + return v; + } + + /** + * Computes the Morton index from 2D coordinates. This is equivalent to interleaving their bits. + * The inputs must be 16-bit unsigned integers (resulting in 32-bit Morton index) due to 32-bit bitwise operator limitation in JavaScript. + * + * @param x - The X coordinate in the range [0, (2^16)-1]. + * @param y - The Y coordinate in the range [0, (2^16)-1]. + * @returns The Morton index. + * @internal + */ + static encode2D(x: number, y: number): number { + //>>includeStart('debug', pragmas.debug); + if (x < 0 || x > 65535 || y < 0 || y > 65535) { + throw new Error("inputs must be 16-bit unsigned integers"); + } + //>>includeEnd('debug'); + + // Note: JavaScript bitwise operations return signed 32-bit integers, so the + // final result needs to be reintepreted as an unsigned integer using >>> 0. + // This is not needed for encode3D because the result is guaranteed to be at most + // 30 bits and thus will always be interpreted as an unsigned value. + return ( + (MortonOrder.insertOneSpacing(x) | + (MortonOrder.insertOneSpacing(y) << 1)) >>> + 0 + ); + } + + /** + * Computes the Morton index from 3D coordinates. This is equivalent to interleaving their bits. + * The inputs must be 10-bit unsigned integers (resulting in 30-bit Morton index) due to 32-bit bitwise operator limitation in JavaScript. + * + * @param x - The X coordinate in the range [0, (2^10)-1]. + * @param y - The Y coordinate in the range [0, (2^10)-1]. + * @param z - The Z coordinate in the range [0, (2^10)-1]. + * @returns The Morton index. + * @internal + */ + static encode3D(x: number, y: number, z: number): number { + //>>includeStart('debug', pragmas.debug); + if (x < 0 || x > 1023 || y < 0 || y > 1023 || z < 0 || z > 1023) { + throw new Error("inputs must be 10-bit unsigned integers"); + } + //>>includeEnd('debug'); + + return ( + MortonOrder.insertTwoSpacing(x) | + (MortonOrder.insertTwoSpacing(y) << 1) | + (MortonOrder.insertTwoSpacing(z) << 2) + ); + } +}