UNPKG

@cesium/engine

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

397 lines (384 loc) 13.8 kB
import Cartesian2 from "../Core/Cartesian2.js"; import Cartesian3 from "../Core/Cartesian3.js"; import Cartesian4 from "../Core/Cartesian4.js"; import defined from "../Core/defined.js"; import Matrix2 from "../Core/Matrix2.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import RuntimeError from "../Core/RuntimeError.js"; import MetadataClassProperty from "./MetadataClassProperty.js"; import MetadataComponentType from "./MetadataComponentType.js"; import MetadataType from "./MetadataType.js"; /** * Utility functions for metadata picking. * * These are used by the `Picking.pickMetadata` function to decode * the metadata values that have been read from the frame buffer * into the actual metadata values, according to the structure * defined by the `MetadataClassProperty`. * * @private */ const MetadataPicking = {}; /** * Returns the value at the specified index of the given data view, * interpreting the data to have the given component type. * * @param {MetadataComponentType} componentType The `MetadataComponentType` * @param {DataView} dataView The data view * @param {number} index The index (byte offset) * @returns {number|bigint|undefined} The value * @throws RuntimeError If the given component type is not a valid * `MetadataComponentType` * @throws RangeError If reading the data from the given data view would * cause an out-of-bounds access * * @private */ MetadataPicking.decodeRawMetadataValue = function ( componentType, dataView, index, ) { switch (componentType) { case MetadataComponentType.INT8: return dataView.getInt8(index); case MetadataComponentType.UINT8: return dataView.getUint8(index); case MetadataComponentType.INT16: return dataView.getInt16(index, true); case MetadataComponentType.UINT16: return dataView.getUint16(index, true); case MetadataComponentType.INT32: return dataView.getInt32(index, true); case MetadataComponentType.UINT32: return dataView.getUint32(index, true); case MetadataComponentType.INT64: return dataView.getBigInt64(index, true); case MetadataComponentType.UINT64: return dataView.getBigUint64(index, true); case MetadataComponentType.FLOAT32: return dataView.getFloat32(index, true); case MetadataComponentType.FLOAT64: return dataView.getFloat64(index, true); } throw new RuntimeError(`Invalid component type: ${componentType}`); }; /** * Decodes one component of a metadata value with the given property type * from the given data view. * * This will decode one component (e.g. one entry of a SCALAR array, * or one component of a VEC2 element). * * This will apply normalization to the raw component value if the given * class property is 'normalized'. * * @param {MetadataClassProperty} classProperty The class property * @param {DataView} dataView The data view containing the raw metadata values * @param {number} dataViewOffset The byte offset within the data view from * which the component should be read * @returns {number|bigint|undefined} The metadata value component * @throws RuntimeError If the component of the given property is not * a valid `MetadataComponentType` * @throws RangeError If reading the data from the given data view would * cause an out-of-bounds access */ MetadataPicking.decodeRawMetadataValueComponent = function ( classProperty, dataView, dataViewOffset, ) { const componentType = classProperty.componentType; const component = MetadataPicking.decodeRawMetadataValue( componentType, dataView, dataViewOffset, ); if (classProperty.normalized) { return MetadataComponentType.normalize(component, componentType); } return component; }; /** * Decodes one element of a metadata value with the given property type * from the given data view. * * When the given class property is vector- or matrix typed, then the * result will be an array, with a length that corresponds to the * number of vector- or matrix components. * * Otherwise, it will be a single value. * * In any case, the return value will be the "raw" value, which does * take into account normalization, but does NOT take into account * default/noData value handling. * * @param {MetadataClassProperty} classProperty The metadata class property * @param {DataView} dataView The data view containing the raw metadata values * @param {number} elementIndex The index of the element. This is the index * inside the array for array-typed properties, and 0 for non-array types. * @returns {number|number[]|bigint|bigint[]|undefined} The decoded metadata value element * @throws RuntimeError If the component of the given property is not * a valid `MetadataComponentType` * @throws RangeError If reading the data from the given data view would * cause an out-of-bounds access * */ MetadataPicking.decodeRawMetadataValueElement = function ( classProperty, dataView, elementIndex, ) { const componentType = classProperty.componentType; const componentSizeInBytes = MetadataComponentType.getSizeInBytes(componentType); const type = classProperty.type; const componentCount = MetadataType.getComponentCount(type); const elementSizeInBytes = componentSizeInBytes * componentCount; if (componentCount > 1) { const result = Array(componentCount); for (let i = 0; i < componentCount; i++) { const offset = elementIndex * elementSizeInBytes + i * componentSizeInBytes; const component = MetadataPicking.decodeRawMetadataValueComponent( classProperty, dataView, offset, ); result[i] = component; } return result; } const offset = elementIndex * elementSizeInBytes; const result = MetadataPicking.decodeRawMetadataValueComponent( classProperty, dataView, offset, ); return result; }; /** * Decode the given raw values into the raw (array-based) form of * a metadata property value. * * (For decoding to types like `CartesianN`, the `decodeMetadataValues` * function can be used) * * The given values are a `Uint8Array` containing the RGBA * values that have been read from the metadata picking * frame buffer. They are assumed to contain the value for * the given class property, as encoded by the * `MetadataPickingPipelineStage` for metadata picking. * * When the given class property is an array, then (it has to be * a fixed-length array, and) the result will be an array with * the respective length. * * When the given class property is vector- or matrix typed, * then the result will be an array, with a length that corresponds * to the number of vector- or matrix components. * * (The case that the property is an array of vector- or matrix * elements is not supported on the side of the general metadata * shader infrastructure, but handled here nevertheless. For such * an input, the result would be an array of arrays, with each * element representing one of the vectors or matrices). * * In any case, the return value will be the "raw" value, which does * take into account normalization, but does NOT take into account * any offset/scale, or default/noData value handling. * * @param {MetadataClassProperty} classProperty The `MetadataClassProperty` * @param {Uint8Array} rawPixelValues The raw values * @returns {number|bigint|number[]|bigint[]|undefined} The value * @throws RuntimeError If the class property has an invalid component type * * @private */ MetadataPicking.decodeRawMetadataValues = function ( classProperty, rawPixelValues, ) { const dataView = new DataView( rawPixelValues.buffer, rawPixelValues.byteOffset, rawPixelValues.byteLength, ); if (classProperty.isArray) { const arrayLength = classProperty.arrayLength; const result = Array(arrayLength); for (let i = 0; i < arrayLength; i++) { const element = MetadataPicking.decodeRawMetadataValueElement( classProperty, dataView, i, ); result[i] = element; } return result; } const result = MetadataPicking.decodeRawMetadataValueElement( classProperty, dataView, 0, ); return result; }; /** * Converts the given type into an object representation where appropriate. * * When the given type is `SCALAR`, `STRING`, `BOOLEAN`, or `ENUM`, or * when the given value is `undefined`, then the given value will be * returned. * * Otherwise, for the `VECn/MATn` types, the given value is assumed to be * a numeric array, and is converted into the matching `CartesianN/MatrixN` * value. * * @param {string} type The `ClassProperty` type * @param {number|bigint|number[]|bigint[]|undefined} value The input value * @returns {undefined|number|bigint|string|boolean|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4} The object representation * @throws RuntimeError If the type is not a valid `MetadataType` */ MetadataPicking.convertToObjectType = function (type, value) { if (!defined(value)) { return value; } if ( type === MetadataType.SCALAR || type === MetadataType.STRING || type === MetadataType.BOOLEAN || type === MetadataType.ENUM ) { return value; } const numbers = value.map((n) => Number(n)); switch (type) { case MetadataType.VEC2: return Cartesian2.unpack(numbers, 0, new Cartesian2()); case MetadataType.VEC3: return Cartesian3.unpack(numbers, 0, new Cartesian3()); case MetadataType.VEC4: return Cartesian4.unpack(numbers, 0, new Cartesian4()); case MetadataType.MAT2: return Matrix2.unpack(numbers, 0, new Matrix2()); case MetadataType.MAT3: return Matrix3.unpack(numbers, 0, new Matrix3()); case MetadataType.MAT4: return Matrix4.unpack(numbers, 0, new Matrix4()); } // Should never happen: throw new RuntimeError(`Invalid metadata object type: ${type}`); }; /** * Converts the given type into a raw value or array representation. * * For `VECn/MATn` types, the given value is converted into an array. * For other types, the value is returned directly * * @param {string} type The `ClassProperty` type * @param {undefined|number|bigint|string|boolean|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4} value The input value * @returns {undefined|number|bigint|string|boolean|number[]} The array representation * @throws RuntimeError If the type is not a valid `MetadataType` */ MetadataPicking.convertFromObjectType = function (type, value) { if (!defined(value)) { return value; } if ( type === MetadataType.SCALAR || type === MetadataType.STRING || type === MetadataType.BOOLEAN || type === MetadataType.ENUM ) { return value; } switch (type) { case MetadataType.VEC2: return Cartesian2.pack(value, Array(2)); case MetadataType.VEC3: return Cartesian3.pack(value, Array(3)); case MetadataType.VEC4: return Cartesian4.pack(value, Array(4)); case MetadataType.MAT2: return Matrix2.pack(value, Array(4)); case MetadataType.MAT3: return Matrix3.pack(value, Array(9)); case MetadataType.MAT4: return Matrix4.pack(value, Array(16)); } // Should never happen: throw new RuntimeError(`Invalid metadata object type: ${type}`); }; /** * Decode the given raw values into a metadata property value. * * This applies the value transform (offset/scale) to the result * of `decodeRawMetadataValues`, and converts this from array-based * types into object types like `CartesianN`. * * @param {MetadataClassProperty} classProperty The `MetadataClassProperty` * @param {object} metadataProperty The * `PropertyTextureProperty` or `PropertyAttributeProperty` * @param {Uint8Array} rawPixelValues The raw values * @returns {MetadataValue} The value * @throws RuntimeError If the class property has an invalid type * or component type * @throws RangeError If the given pixel values do not have sufficient * size to contain the expected value type * * @private */ MetadataPicking.decodeMetadataValues = function ( classProperty, metadataProperty, rawPixelValues, ) { let arrayBasedResult = MetadataPicking.decodeRawMetadataValues( classProperty, rawPixelValues, ); if (metadataProperty.hasValueTransform) { // In the MetadataClassProperty, these offset/scale are always in // their array-based form (e.g. a number[3] for `VEC3`). But for // the PropertyTextureProperty and PropertyAttributeProperty, // the type of the offset/scale is defined to be // number|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4 // So these types are converted into their array-based form here, before // applying them with `MetadataClassProperty.valueTransformInPlace` const offset = MetadataPicking.convertFromObjectType( classProperty.type, metadataProperty.offset, ); const scale = MetadataPicking.convertFromObjectType( classProperty.type, metadataProperty.scale, ); arrayBasedResult = MetadataClassProperty.valueTransformInPlace( arrayBasedResult, offset, scale, MetadataComponentType.applyValueTransform, ); } if (classProperty.isArray) { const arrayLength = classProperty.arrayLength; const result = Array(arrayLength); for (let i = 0; i < arrayLength; i++) { const arrayBasedValue = arrayBasedResult[i]; const objectBasedValue = MetadataPicking.convertToObjectType( classProperty.type, arrayBasedValue, ); result[i] = objectBasedValue; } return result; } const objectResult = MetadataPicking.convertToObjectType( classProperty.type, arrayBasedResult, ); return objectResult; }; export default Object.freeze(MetadataPicking);