(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./shaders/point-shaders.js", "./point-texture-layer.js", "./cpu-pipeline/point-pipeline.js", "./constants.js"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.GpuPointLayer = void 0;
    const point_shaders_js_1 = require("./shaders/point-shaders.js");
    const point_texture_layer_js_1 = require("./point-texture-layer.js");
    const point_pipeline_js_1 = require("./cpu-pipeline/point-pipeline.js");
    const constants_js_1 = require("./constants.js");
    /**
     * Generic GPU point layer with optional rotation and optional motion extrapolation.
     */
    class GpuPointLayer {
        constructor(points = [], options) {
            this.playbackStartSeconds = performance.now() / 1000;
            this.motionAnchorSeconds = 0.0001;
            this.drawOrder = options.drawOrder ?? 0;
            const resolvedDescriptor = this.resolveDescriptor(options);
            this.defaultAltitudeMeters = options.defaultAltitudeMeters ?? constants_js_1.DEFAULT_POINT_ALTITUDE_METERS;
            this.defaultHeadingRadians = options.defaultHeadingRadians ?? constants_js_1.DEFAULT_POINT_HEADING_RADIANS;
            this.enableAnimation = options.enableAnimation ?? true;
            this.cullDotThreshold =
                options.cullDotThreshold ?? constants_js_1.DEFAULT_POINT_CULL_DOT_THRESHOLD;
            this.pointLayer = this.createPointLayer(resolvedDescriptor, options);
            this.pointLayer.setRecords(points);
            this.primitive = this.pointLayer.primitive;
        }
        setRecords(points) {
            this.pointLayer.setRecords(points);
        }
        setSprite(sprite) {
            this.pointLayer.setSprite(this.normalizeSpriteInput(sprite));
        }
        setVisiblePointIds(visiblePointIds) {
            this.pointLayer.setVisiblePointIds(visiblePointIds);
        }
        destroy() {
            this.pointLayer.destroy();
        }
        createPointLayer(descriptor, options) {
            const enableAnimation = options.enableAnimation ?? true;
            const resolvedAttributeName = descriptor.attributeName ?? `a_${(0, point_pipeline_js_1.normalizeTextureName)(options.textureName ?? options.name ?? 'point')}${constants_js_1.DEFAULT_ATTRIBUTE_NAME_SUFFIX}`;
            const shaderConfig = (0, point_pipeline_js_1.resolveShaderConfig)(descriptor.shaderConfig);
            const shaders = descriptor.shaders ??
                (0, point_shaders_js_1.buildPointShaders)({
                    attributeName: resolvedAttributeName,
                    dataTextureUniform: shaderConfig.dataTextureUniform,
                    dataTextureDimensionsUniform: shaderConfig.dataTextureDimensionsUniform,
                    spriteTextureUniform: shaderConfig.spriteTextureUniform,
                    headingOffsetRadians: descriptor.headingOffsetRadians ?? 0,
                    alignWithGround: options.alignWithGround,
                    hasMotionExtrapolation: enableAnimation,
                    motionTextureUniform: shaderConfig.motionTextureUniform,
                    nowSecondsUniform: shaderConfig.nowSecondsUniform,
                    maxExtrapolationSecondsUniform: shaderConfig.maxExtrapolationSecondsUniform,
                });
            const cesiumUniforms = {
                dataTexture: shaderConfig.dataTextureUniform,
                dataTextureDimensions: shaderConfig.dataTextureDimensionsUniform,
                motionTexture: shaderConfig.motionTextureUniform,
                nowSeconds: shaderConfig.nowSecondsUniform,
                maxExtrapolationSeconds: shaderConfig.maxExtrapolationSecondsUniform,
                spriteTexture: shaderConfig.spriteTextureUniform,
                rotationEnabled: shaderConfig.rotationEnabledUniform,
            };
            const layerDescriptor = {
                name: descriptor.name ?? constants_js_1.DEFAULT_LAYER_NAME,
                shaders,
                uniforms: cesiumUniforms,
                indexAttributeName: resolvedAttributeName,
                indexAttributeLocation: descriptor.indexAttributeLocation ?? constants_js_1.DEFAULT_ATTRIBUTE_INDEX,
                boundingSphere: descriptor.boundingSphere ??
                    new Cesium.BoundingSphere(Cesium.Cartesian3.ZERO, Cesium.Ellipsoid.WGS84.maximumRadius + 1000000),
                options: {
                    pointScale: options.pointScale ?? constants_js_1.DEFAULT_POINT_SCALE,
                    minPointSize: options.minPointSize ?? constants_js_1.DEFAULT_MIN_POINT_SIZE,
                    maxPointSize: options.maxPointSize ?? constants_js_1.DEFAULT_MAX_POINT_SIZE,
                    maxExtrapolationSeconds: options.maxExtrapolationSeconds ?? constants_js_1.DEFAULT_MAX_EXTRAPOLATION_SECONDS,
                    sprite: options.sprite,
                    rotationEnabled: options.rotationEnabled,
                    alignWithGround: options.alignWithGround,
                    depthTest: options.depthTest,
                    depthMask: options.depthMask,
                },
                cullDotThreshold: this.cullDotThreshold,
                prepareRecord: (point) => this.preparePointForRendering(point),
                packMainData: (point, output, valueOffset) => {
                    output[valueOffset] = point.longitude;
                    output[valueOffset + 1] = point.latitude;
                    output[valueOffset + 2] = point.altitudeMeters;
                    output[valueOffset + 3] = point.headingRadians;
                },
                packMotionData: enableAnimation
                    ? (point, output, valueOffset) => {
                        output[valueOffset] = point.speedMetersPerSecond;
                        output[valueOffset + 1] = point.directionX;
                        output[valueOffset + 2] = point.directionY;
                        output[valueOffset + 3] = point.timestampSeconds;
                    }
                    : undefined,
                getNowSeconds: (frameState) => {
                    void frameState;
                    return performance.now() / 1000 - this.playbackStartSeconds;
                },
            };
            return new point_texture_layer_js_1.CesiumPointTextureLayer(layerDescriptor);
        }
        normalizeSpriteInput(sprite) {
            return {
                width: sprite.width,
                height: sprite.height,
                pixels: sprite.pixels,
            };
        }
        preparePointForRendering(point) {
            const altitudeMeters = (0, point_pipeline_js_1.isFiniteNumber)(point.altitudeMeters)
                ? point.altitudeMeters
                : this.defaultAltitudeMeters;
            const hasRotationRadians = (0, point_pipeline_js_1.isFiniteNumber)(point.rotationRadians);
            // Marker rotation defaults when not explicitly set.
            const rawRotationRadians = hasRotationRadians
                ? point.rotationRadians
                : this.defaultHeadingRadians;
            const normalizedRotationRadians = (0, point_pipeline_js_1.isFiniteNumber)(rawRotationRadians)
                ? rawRotationRadians
                : this.defaultHeadingRadians;
            const headingRadians = Cesium.Math.zeroToTwoPi(normalizedRotationRadians);
            if (!(0, point_pipeline_js_1.isFiniteNumber)(point.longitude) ||
                !(0, point_pipeline_js_1.isFiniteNumber)(point.latitude) ||
                !(0, point_pipeline_js_1.isFiniteNumber)(altitudeMeters) ||
                !(0, point_pipeline_js_1.isFiniteNumber)(headingRadians)) {
                return null;
            }
            const directionFromEarthCenter = Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, altitudeMeters, Cesium.Ellipsoid.WGS84, new Cesium.Cartesian3());
            Cesium.Cartesian3.normalize(directionFromEarthCenter, directionFromEarthCenter);
            const speedMetersPerSecond = this.enableAnimation && (0, point_pipeline_js_1.isFiniteNumber)(point.speedMetersPerSecond)
                ? Math.max(point.speedMetersPerSecond, 0)
                : 0;
            // If movement direction is not explicitly provided, inherit marker rotation for motion.
            const hasMovementRadians = (0, point_pipeline_js_1.isFiniteNumber)(point.movementDirectionRadians) || hasRotationRadians;
            const rawMovementRadians = (0, point_pipeline_js_1.isFiniteNumber)(point.movementDirectionRadians)
                ? point.movementDirectionRadians
                : headingRadians;
            const movementRadians = hasMovementRadians
                ? Cesium.Math.zeroToTwoPi(rawMovementRadians)
                : 0;
            const directionX = speedMetersPerSecond > 0 && hasMovementRadians ? Math.cos(movementRadians) : 0;
            const directionY = speedMetersPerSecond > 0 && hasMovementRadians ? Math.sin(movementRadians) : 0;
            return {
                id: point.id,
                longitude: point.longitude,
                latitude: point.latitude,
                altitudeMeters,
                headingRadians,
                speedMetersPerSecond,
                directionX,
                directionY,
                directionFromEarthCenter,
                timestampSeconds: this.motionAnchorSeconds,
            };
        }
        resolveDescriptor(options) {
            const normalizedTextureName = (0, point_pipeline_js_1.normalizeTextureName)(options.textureName ?? options.name ?? 'point');
            const prefix = `u_${normalizedTextureName}`;
            return {
                name: options.name ?? constants_js_1.DEFAULT_LAYER_NAME,
                attributeName: options.attributeName ?? `a_${normalizedTextureName}${constants_js_1.DEFAULT_ATTRIBUTE_NAME_SUFFIX}`,
                indexAttributeLocation: options.indexAttributeLocation ?? constants_js_1.DEFAULT_ATTRIBUTE_INDEX,
                boundingSphere: options.boundingSphere ?? constants_js_1.DEFAULT_BOUNDING_SPHERE,
                cullDotThreshold: options.cullDotThreshold ?? constants_js_1.DEFAULT_POINT_CULL_DOT_THRESHOLD,
                headingOffsetRadians: options.headingOffsetRadians,
                shaderConfig: {
                    ...(0, point_pipeline_js_1.resolveShaderConfig)(options.shaderConfig),
                    dataTextureUniform: options.shaderConfig?.dataTextureUniform ?? `${prefix}Texture`,
                    dataTextureDimensionsUniform: options.shaderConfig?.dataTextureDimensionsUniform ?? `${prefix}TextureDimensions`,
                    spriteTextureUniform: options.shaderConfig?.spriteTextureUniform ??
                        point_pipeline_js_1.DEFAULT_POINT_SHADER_CONFIG.spriteTextureUniform,
                    motionTextureUniform: options.shaderConfig?.motionTextureUniform ?? `${prefix}MotionTexture`,
                    nowSecondsUniform: options.shaderConfig?.nowSecondsUniform ??
                        point_pipeline_js_1.DEFAULT_POINT_SHADER_CONFIG.nowSecondsUniform,
                    maxExtrapolationSecondsUniform: options.shaderConfig?.maxExtrapolationSecondsUniform ??
                        point_pipeline_js_1.DEFAULT_POINT_SHADER_CONFIG.maxExtrapolationSecondsUniform,
                    rotationEnabledUniform: options.shaderConfig?.rotationEnabledUniform ??
                        point_pipeline_js_1.DEFAULT_POINT_SHADER_CONFIG.rotationEnabledUniform,
                },
                shaders: options.shaders ?? undefined,
            };
        }
    }
    exports.GpuPointLayer = GpuPointLayer;
});
