var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { isEqual } from "lodash";
import { CompositeLayer } from "@deck.gl/core";
import workerpool from "workerpool";
import PrivateLayer from "./privateGrid3dLayer";
import { makeFullMesh } from "./webworker";
import config from "../../SubsurfaceConfig.json";
import { findConfig, loadDataArray } from "../../utils";
// init workerpool
const workerPoolConfig = findConfig(config, "config/workerpool", "config/layer/Grid3DLayer/workerpool");
const pool = workerpool.pool(Object.assign({
    maxWorkers: 10,
    workerType: "web",
}, workerPoolConfig));
function onTerminateWorker() {
    const stats = pool.stats();
    if (stats.busyWorkers === 0 && stats.pendingTasks === 0) {
        pool.terminate();
    }
}
function GetBBox(points) {
    let xmax = -99999999;
    let ymax = -99999999;
    let zmax = -99999999;
    let xmin = 99999999;
    let ymin = 99999999;
    let zmin = 99999999;
    for (let i = 0; i < points.length / 3; i++) {
        xmax = points[3 * i + 0] > xmax ? points[3 * i + 0] : xmax;
        xmin = points[3 * i + 0] < xmin ? points[3 * i + 0] : xmin;
        ymax = points[3 * i + 1] > ymax ? points[3 * i + 1] : ymax;
        ymin = points[3 * i + 1] < ymin ? points[3 * i + 1] : ymin;
        zmax = points[3 * i + 2] > zmax ? points[3 * i + 2] : zmax;
        zmin = points[3 * i + 2] < zmin ? points[3 * i + 2] : zmin;
    }
    return [xmin, ymin, zmin, xmax, ymax, zmax];
}
function loadData(data, type) {
    return __awaiter(this, void 0, void 0, function* () {
        const result = yield loadDataArray(data, type);
        if (result !== null) {
            return result;
        }
        return Promise.reject("Grid3DLayer: Unsupported type of input data");
    });
}
function loadPropertiesData(propertiesData) {
    return __awaiter(this, void 0, void 0, function* () {
        const isPropertiesDiscrete = propertiesData instanceof Uint16Array;
        const result = isPropertiesDiscrete
            ? yield loadDataArray(propertiesData, Uint16Array)
            : yield loadDataArray(propertiesData, Float32Array);
        if (result !== null) {
            return result;
        }
        return Promise.reject("Grid3DLayer: Unsupported type of input data");
    });
}
function load_data(pointsData, polysData, propertiesData, loadProperties) {
    return __awaiter(this, void 0, void 0, function* () {
        const points = yield loadData(pointsData, Float32Array);
        const polys = yield loadData(polysData, Uint32Array);
        const properties = loadProperties && propertiesData
            ? yield loadPropertiesData(propertiesData)
            : undefined;
        return Promise.all([points, polys, properties]);
    });
}
/**
 * Enumerates possible coloring modes of Grid3D Layer.
 */
export var TGrid3DColoringMode;
(function (TGrid3DColoringMode) {
    /** A continuous property is provided to colorize the cells. */
    TGrid3DColoringMode[TGrid3DColoringMode["Property"] = 0] = "Property";
    /** A discrete property is provided to colorize the cells. */
    TGrid3DColoringMode[TGrid3DColoringMode["DiscreteProperty"] = 1] = "DiscreteProperty";
    /** Coloring using the X value of the cell nodes. */
    TGrid3DColoringMode[TGrid3DColoringMode["X"] = 2] = "X";
    /** Coloring using the Y value of the cell nodes. */
    TGrid3DColoringMode[TGrid3DColoringMode["Y"] = 3] = "Y";
    /** Coloring using the Z value of the cell nodes. */
    TGrid3DColoringMode[TGrid3DColoringMode["Z"] = 4] = "Z";
})(TGrid3DColoringMode || (TGrid3DColoringMode = {}));
/**
 * Returns true if the coloring mode is geometric, that is based on the X, Y or Z coordinates
 * of the cell nodes. In that case no explicit property data is required.
 * @param coloringMode coloring mode.
 * @returns true if the coloring mode is geometric.
 */
export function isGeometricProperty(coloringMode) {
    return (coloringMode === TGrid3DColoringMode.X ||
        coloringMode === TGrid3DColoringMode.Y ||
        coloringMode === TGrid3DColoringMode.Z);
}
/**
 * Returns true if the coloring mode is discrete. In that case an explicit property data is required,
 * with integer values ranging from 0 to N, where N is the number of discrete property values.
 * @param coloringMode coloring mode
 * @returns true if the property values are discrete.
 */
export function isDiscreteProperty(coloringMode) {
    return coloringMode === TGrid3DColoringMode.DiscreteProperty;
}
const defaultProps = {
    "@@type": "Grid3DLayer",
    name: "Grid 3D",
    id: "grid-3d-layer",
    visible: true,
    material: true,
    coloringMode: TGrid3DColoringMode.Property,
    colorMapName: "",
    gridLines: true,
    propertyValueRange: [0.0, 1.0],
    depthTest: true,
    ZIncreasingDownwards: true,
};
export default class Grid3DLayer extends CompositeLayer {
    get isLoaded() {
        var _a, _b;
        const subLayers = this.getSubLayers();
        const isLoaded = super.isLoaded &&
            subLayers.length > 0 &&
            subLayers.every((layer) => layer.isLoaded);
        const isFinished = (_b = (_a = this.state) === null || _a === void 0 ? void 0 : _a["isFinishedLoading"]) !== null && _b !== void 0 ? _b : false;
        return isLoaded && isFinished;
    }
    rebuildData(reportBoundingBox) {
        var _a;
        const p = load_data(this.props.pointsData, this.props.polysData, this.props.propertiesData, !isGeometricProperty((_a = this.props.coloringMode) !== null && _a !== void 0 ? _a : TGrid3DColoringMode.Property));
        p.then(([points, polys, properties]) => {
            const bbox = GetBBox(points);
            // Using inline web worker for calculating the triangle mesh from
            // loaded input data so not to halt the GUI thread.
            const webworkerParams = {
                points,
                polys,
                properties,
                undefinedValue: this.getUndefinedPropertyValue(),
            };
            pool.exec(makeFullMesh, [{ data: webworkerParams }]).then((e) => {
                var _a;
                const [mesh, mesh_lines, propertyValueRange] = e;
                const legend = {
                    discrete: false,
                    valueRange: (_a = this.props.colorMapRange) !== null && _a !== void 0 ? _a : propertyValueRange,
                    colorName: this.props.colorMapName,
                    title: "MapLayer",
                    colormapFunction: this.props.colorMapFunction,
                };
                this.setState({
                    mesh,
                    mesh_lines,
                    propertyValueRange,
                    legend,
                    bbox,
                });
                if (typeof this.props.reportBoundingBox !== "undefined" &&
                    reportBoundingBox) {
                    this.props.reportBoundingBox({ layerBoundingBox: bbox });
                }
                this.setState(Object.assign(Object.assign({}, this.state), { isFinishedLoading: true }));
                onTerminateWorker();
            });
        });
    }
    initializeState() {
        this.setState(Object.assign(Object.assign({}, this.state), { isFinishedLoading: false }));
        const reportBoundingBox = true;
        this.rebuildData(reportBoundingBox);
    }
    updateState({ props, oldProps, }) {
        const needs_reload = !isEqual(props.pointsData, oldProps.pointsData) ||
            !isEqual(props.polysData, oldProps.polysData) ||
            !isEqual(props.propertiesData, oldProps.propertiesData) ||
            !isEqual(props.gridLines, oldProps.gridLines) ||
            !isEqual(props.ZIncreasingDownwards, oldProps.ZIncreasingDownwards);
        if (needs_reload) {
            this.setState(Object.assign(Object.assign({}, this.state), { isFinishedLoading: false }));
            const reportBoundingBox = false;
            this.rebuildData(reportBoundingBox);
        }
    }
    renderLayers() {
        if (Object.keys(this.state).length === 1) {
            // isFinishedLoading only in state
            return [];
        }
        const undefinedColor = this.getUndefinedPropertyColor();
        const undefinedValue = this.getUndefinedPropertyValue();
        const enableLighting = this.props.material !== false;
        const layer = new PrivateLayer(this.getSubLayerProps({
            mesh: this.state["mesh"],
            meshLines: this.state["mesh_lines"],
            pickable: this.props.pickable,
            colormapName: this.props.colorMapName,
            colormapRange: this.props.colorMapRange,
            colormapClampColor: this.props.colorMapClampColor,
            undefinedPropertyValue: undefinedValue,
            undefinedPropertyColor: undefinedColor,
            colormapFunction: this.props.colorMapFunction,
            coloringMode: this.props.coloringMode,
            gridLines: this.props.gridLines,
            propertyValueRange: this.getPropertyValueRange(),
            discretePropertyValueNames: this.props.discretePropertyValueNames,
            material: this.props.material,
            depthTest: this.props.depthTest,
            ZIncreasingDownwards: this.props.ZIncreasingDownwards,
            enableLighting,
        }));
        return [layer];
    }
    getPropertyValueRange() {
        const bbox = this.state["bbox"];
        switch (this.props.coloringMode) {
            case TGrid3DColoringMode.X:
                return [bbox[0], bbox[3]];
            case TGrid3DColoringMode.Y:
                return [bbox[1], bbox[4]];
            case TGrid3DColoringMode.Z:
                return this.props.ZIncreasingDownwards
                    ? [-bbox[5], -bbox[2]]
                    : [bbox[2], bbox[5]];
            default:
                return this.state["propertyValueRange"];
        }
    }
    getUndefinedPropertyValue() {
        if (typeof this.props.undefinedPropertyValue === "number") {
            //Precision should be reduced to single for correct comparing property values
            // with undefined property value in the webworker.
            return new Float32Array([this.props.undefinedPropertyValue])[0];
        }
        if (this.props.propertiesData instanceof Uint16Array) {
            return 0xffff;
        }
        return Number.NaN;
    }
    getUndefinedPropertyColor() {
        var _a, _b;
        const colorFunc = this.props.colorMapFunction;
        if (((_a = this.props.propertiesData) === null || _a === void 0 ? void 0 : _a.length) === 0 &&
            this.isColormapFunctionConstantColor(colorFunc)) {
            return [colorFunc[0], colorFunc[1], colorFunc[2]];
        }
        return (_b = this.props.undefinedPropertyColor) !== null && _b !== void 0 ? _b : [204, 204, 204];
    }
    isColormapFunctionConstantColor(colorFunc) {
        return ((Array.isArray(colorFunc) || colorFunc instanceof Uint8Array) &&
            colorFunc.length >= 3);
    }
}
Grid3DLayer.layerName = "Grid3DLayer";
Grid3DLayer.defaultProps = defaultProps;
//# sourceMappingURL=grid3dLayer.js.map