"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Rendering
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.MeshList = exports.Mesh = exports.createMeshArgs = exports.MeshArgsEdges = exports.createPolylineArgs = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const core_geometry_1 = require("@itwin/core-geometry");
const core_common_1 = require("@itwin/core-common");
const ColorMap_1 = require("./ColorMap");
const DisplayParams_1 = require("./DisplayParams");
const MeshPrimitive_1 = require("./MeshPrimitive");
const Primitives_1 = require("./Primitives");
function createPolylineArgs(mesh) {
    if (!mesh.polylines || mesh.polylines.length === 0)
        return undefined;
    const polylines = [];
    for (const polyline of mesh.polylines)
        if (polyline.indices.length > 0)
            polylines.push(polyline.indices);
    if (polylines.length === 0)
        return undefined;
    const flags = {
        is2d: mesh.is2d,
        isPlanar: mesh.isPlanar,
        isDisjoint: mesh.type === MeshPrimitive_1.MeshPrimitiveType.Point,
    };
    if (mesh.displayParams.regionEdgeType === DisplayParams_1.DisplayParams.RegionEdgeType.Outline) {
        // This polyline is behaving as the edges of a region surface.
        if (!mesh.displayParams.gradient || mesh.displayParams.gradient.isOutlined)
            flags.type = core_common_1.PolylineTypeFlags.Edge;
        else
            flags.type = core_common_1.PolylineTypeFlags.Outline; // edges only displayed if fill undisplayed
    }
    const colors = new core_common_1.ColorIndex();
    mesh.colorMap.toColorIndex(colors, mesh.colors);
    const features = new core_common_1.FeatureIndex();
    mesh.toFeatureIndex(features);
    return {
        width: mesh.displayParams.width,
        linePixels: mesh.displayParams.linePixels,
        flags,
        polylines,
        points: mesh.points,
        colors,
        features,
    };
}
exports.createPolylineArgs = createPolylineArgs;
/** The vertices of the edges are shared with those of the surface. */
class MeshArgsEdges {
    constructor() {
        this.edges = new core_common_1.EdgeArgs();
        this.silhouettes = new core_common_1.SilhouetteEdgeArgs();
        this.polylines = new core_common_1.PolylineEdgeArgs();
        this.width = 0;
        this.linePixels = core_common_1.LinePixels.Solid;
    }
    clear() {
        this.edges.clear();
        this.silhouettes.clear();
        this.polylines.clear();
        this.width = 0;
        this.linePixels = core_common_1.LinePixels.Solid;
    }
    get isValid() { return this.edges.isValid || this.silhouettes.isValid || this.polylines.isValid; }
}
exports.MeshArgsEdges = MeshArgsEdges;
function createMeshArgs(mesh) {
    if (!mesh.triangles || mesh.triangles.isEmpty || mesh.points.length === 0)
        return undefined;
    const texture = mesh.displayParams.textureMapping?.texture;
    const textureMapping = texture && mesh.uvParams.length > 0 ? { texture, uvParams: mesh.uvParams } : undefined;
    const colors = new core_common_1.ColorIndex();
    mesh.colorMap.toColorIndex(colors, mesh.colors);
    const features = new core_common_1.FeatureIndex();
    mesh.toFeatureIndex(features);
    let edges;
    if (mesh.edges) {
        edges = new MeshArgsEdges();
        edges.width = mesh.displayParams.width;
        edges.linePixels = mesh.displayParams.linePixels;
        edges.edges.init(mesh.edges);
        edges.silhouettes.init(mesh.edges);
        const polylines = [];
        for (const meshPolyline of mesh.edges.polylines)
            if (meshPolyline.indices.length > 0)
                polylines.push(meshPolyline.indices);
        edges.polylines.init(polylines);
    }
    return {
        vertIndices: mesh.triangles.indices,
        points: mesh.points,
        normals: !mesh.displayParams.ignoreLighting && mesh.normals.length > 0 ? mesh.normals : undefined,
        textureMapping,
        colors,
        features,
        material: mesh.displayParams.material,
        fillFlags: mesh.displayParams.fillFlags,
        isPlanar: mesh.isPlanar,
        is2d: mesh.is2d,
        hasBakedLighting: true === mesh.hasBakedLighting,
        isVolumeClassifier: true === mesh.isVolumeClassifier,
        edges,
        auxChannels: mesh.auxChannels,
    };
}
exports.createMeshArgs = createMeshArgs;
class Mesh {
    constructor(props) {
        this.normals = [];
        this.uvParams = [];
        this.colorMap = new ColorMap_1.ColorMap(); // used to be called ColorTable
        this.colors = [];
        const { displayParams, features, type, range, is2d, isPlanar } = props;
        this._data = MeshPrimitive_1.MeshPrimitiveType.Mesh === type ? new Primitives_1.TriangleList() : new core_common_1.MeshPolylineList();
        this.displayParams = displayParams;
        this.features = features ? new Mesh.Features(features) : undefined;
        this.type = type;
        this.is2d = is2d;
        this.isPlanar = isPlanar;
        this.hasBakedLighting = (true === props.hasBakedLighting);
        this.isVolumeClassifier = (true === props.isVolumeClassifier);
        if (props.quantizePositions) {
            this.points = new core_common_1.QPoint3dList(core_common_1.QParams3d.fromRange(range));
        }
        else {
            const points = [];
            points.range = range;
            const center = range.center;
            points.add = (pt) => {
                // assert(range.containsPoint(pt)); rounding error triggers this sometimes...
                points.push(pt.minus(center));
            };
            this.points = points;
        }
    }
    static create(props) { return new Mesh(props); }
    get triangles() {
        return MeshPrimitive_1.MeshPrimitiveType.Mesh === this.type ? this._data : undefined;
    }
    get polylines() {
        return MeshPrimitive_1.MeshPrimitiveType.Mesh !== this.type ? this._data : undefined;
    }
    get auxChannels() {
        return this._auxChannels;
    }
    addAuxChannels(channels, srcIndex) {
        // The native version of this function appears to assume that all polyfaces added to the Mesh will have
        // the same number + type of aux channels.
        // ###TODO We should really produce a separate Mesh for each unique combination. For now just bail on mismatch.
        if (this._auxChannels) {
            if (this._auxChannels.length !== channels.length)
                return;
            for (let i = 0; i < channels.length; i++) {
                const src = channels[i];
                const dst = this._auxChannels[i];
                if (src.dataType !== dst.dataType || src.name !== dst.name || src.inputName !== dst.inputName)
                    return;
            }
        }
        if (!this._auxChannels) {
            // Copy the channels, leaving each AuxData's values array empty.
            this._auxChannels = channels.map((x) => new core_geometry_1.AuxChannel(x.data.map((y) => new core_geometry_1.AuxChannelData(y.input, [])), x.dataType, x.name, x.inputName));
        }
        // Append the value at srcIndex from each source channel's data to our channels.
        for (let channelIndex = 0; channelIndex < channels.length; channelIndex++) {
            const srcChannel = channels[channelIndex];
            const dstChannel = this._auxChannels[channelIndex];
            const dstIndex = dstChannel.valueCount;
            for (let dataIndex = 0; dataIndex < srcChannel.data.length; dataIndex++) {
                const dstData = dstChannel.data[dataIndex];
                dstData.copyValues(srcChannel.data[dataIndex], dstIndex, srcIndex, dstChannel.entriesPerValue);
            }
        }
    }
    toFeatureIndex(index) {
        if (undefined !== this.features)
            this.features.toFeatureIndex(index);
    }
    toMeshArgs() {
        return createMeshArgs(this);
    }
    toPolylineArgs() {
        return createPolylineArgs(this);
    }
    addPolyline(poly) {
        const { type, polylines } = this;
        (0, core_bentley_1.assert)(MeshPrimitive_1.MeshPrimitiveType.Polyline === type || MeshPrimitive_1.MeshPrimitiveType.Point === type);
        (0, core_bentley_1.assert)(undefined !== polylines);
        if (MeshPrimitive_1.MeshPrimitiveType.Polyline === type && poly.indices.length < 2)
            return;
        if (undefined !== polylines)
            polylines.push(poly);
    }
    addTriangle(triangle) {
        const { triangles, type } = this;
        (0, core_bentley_1.assert)(MeshPrimitive_1.MeshPrimitiveType.Mesh === type);
        (0, core_bentley_1.assert)(undefined !== triangles);
        if (undefined !== triangles)
            triangles.addTriangle(triangle);
    }
    addVertex(props) {
        const { feature, position, normal, uvParam, fillColor } = props;
        this.points.add(position);
        if (undefined !== normal)
            this.normals.push(normal);
        if (undefined !== uvParam)
            this.uvParams.push(uvParam);
        if (feature) {
            (0, core_bentley_1.assert)(undefined !== this.features);
            this.features.add(feature, this.points.length);
        }
        // Don't allocate color indices until we have non-uniform colors
        if (0 === this.colorMap.length) {
            this.colorMap.insert(fillColor);
            (0, core_bentley_1.assert)(this.colorMap.isUniform);
            (0, core_bentley_1.assert)(0 === this.colorMap.indexOf(fillColor));
        }
        else if (!this.colorMap.isUniform || !this.colorMap.hasColor(fillColor)) {
            // Back-fill uniform value (index=0) for existing vertices if previously uniform
            if (0 === this.colors.length)
                this.colors.length = this.points.length - 1;
            this.colors.push(this.colorMap.insert(fillColor));
            (0, core_bentley_1.assert)(!this.colorMap.isUniform);
        }
        return this.points.length - 1;
    }
}
exports.Mesh = Mesh;
(function (Mesh) {
    class Features {
        constructor(table) {
            this.indices = [];
            this.uniform = 0;
            this.initialized = false;
            this.table = table;
        }
        add(feat, numVerts) {
            const index = this.table.insert(feat);
            if (!this.initialized) {
                // First feature - uniform.
                this.uniform = index;
                this.initialized = true;
            }
            else if (0 < this.indices.length) {
                // Already non-uniform
                this.indices.push(index);
            }
            else {
                // Second feature - back-fill uniform for existing verts
                while (this.indices.length < numVerts - 1)
                    this.indices.push(this.uniform);
                this.indices.push(index);
            }
        }
        setIndices(indices) {
            this.indices.length = 0;
            this.uniform = 0;
            this.initialized = 0 < indices.length;
            (0, core_bentley_1.assert)(0 < indices.length);
            if (1 === indices.length)
                this.uniform = indices[0];
            else if (1 < indices.length)
                this.indices = indices;
        }
        toFeatureIndex(output) {
            const index = output ?? new core_common_1.FeatureIndex();
            if (!this.initialized) {
                index.type = core_common_1.FeatureIndexType.Empty;
            }
            else if (this.indices.length === 0) {
                index.type = core_common_1.FeatureIndexType.Uniform;
                index.featureID = this.uniform;
            }
            else {
                index.type = core_common_1.FeatureIndexType.NonUniform;
                index.featureIDs = new Uint32Array(this.indices);
            }
            return index;
        }
    }
    Mesh.Features = Features;
})(Mesh || (exports.Mesh = Mesh = {}));
class MeshList extends Array {
    constructor(features, range) {
        super();
        this.features = features;
        this.range = range;
    }
}
exports.MeshList = MeshList;
//# sourceMappingURL=MeshPrimitives.js.map