/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module ExportGraphics
 */
import { assert } from "@itwin/core-bentley";
import { IndexedPolyface, PolyfaceData } from "@itwin/core-geometry";
/** Provides utility functions for working with data generated by [IModelDb.exportGraphics]($core-backend)
 * @public
 */
export var ExportGraphics;
(function (ExportGraphics) {
    /** Test if ExportPartDisplayInfos have exactly the same values.
     * @public
     */
    function arePartDisplayInfosEqual(lhs, rhs) {
        if (lhs.categoryId !== rhs.categoryId)
            return false;
        if (lhs.subCategoryId !== rhs.subCategoryId)
            return false;
        if (lhs.materialId !== rhs.materialId)
            return false;
        if (lhs.elmTransparency !== rhs.elmTransparency)
            return false;
        if (lhs.lineColor !== rhs.lineColor)
            return false;
        return true;
    }
    ExportGraphics.arePartDisplayInfosEqual = arePartDisplayInfosEqual;
    /**
     * Convert an ExportGraphicsMesh to an IndexedPolyface usable by the geometry API.
     * @note The resulting IndexedPolyface may have duplicate points, normals and params. If problematic, call [PolyfaceData.compress]($core-geometry)
     * @public
     */
    function convertToIndexedPolyface(mesh) {
        const polyface = IndexedPolyface.create(true, true, false, mesh.isTwoSided);
        const p = mesh.points;
        for (let i = 0; i < p.length; i += 3)
            polyface.data.point.pushXYZ(p[i], p[i + 1], p[i + 2]);
        const n = mesh.normals;
        assert(undefined !== polyface.data.normal);
        for (let i = 0; i < n.length; i += 3)
            polyface.data.normal.pushXYZ(n[i], n[i + 1], n[i + 2]);
        const uv = mesh.params;
        assert(undefined !== polyface.data.param);
        for (let i = 0; i < uv.length; i += 2)
            polyface.data.param.pushXY(uv[i], uv[i + 1]);
        const indices = mesh.indices;
        const addIndex = (idx) => {
            polyface.addPointIndex(idx, true);
            polyface.addNormalIndex(idx);
            polyface.addParamIndex(idx);
        };
        for (let i = 0; i < indices.length; i += 3) {
            addIndex(indices[i]);
            addIndex(indices[i + 1]);
            addIndex(indices[i + 2]);
            polyface.terminateFacet(false);
        }
        return polyface;
    }
    ExportGraphics.convertToIndexedPolyface = convertToIndexedPolyface;
})(ExportGraphics || (ExportGraphics = {}));
/**
 * * Iterator to walk the facets of an ExportGraphicsMesh and present them to the world as if visiting a Polyface.
 * * Because the ExportGraphicsMesh has limited data:
 *   * There is no auxData in this visitor.
 *   * There is no color in this visitor.
 *   * All edgeVisible are true.
 * @public
 */
export class ExportGraphicsMeshVisitor extends PolyfaceData {
    _currentFacetIndex;
    _nextFacetIndex;
    _numWrap;
    _polyface;
    constructor(facets, numWrap) {
        super(facets.normals.length > 0, facets.params.length > 0, false, facets.isTwoSided);
        this._polyface = facets;
        this._numWrap = numWrap;
        this._nextFacetIndex = 0;
        this._currentFacetIndex = -1;
        this.reset();
    }
    /** Create a visitor for iterating the facets of `polyface`, with indicated number of points to be added to each facet to produce closed point arrays
     * Typical wrap counts are:
     * * 0 -- leave the point arrays with "missing final edge" (default)
     * * 1 -- add point 0 as closure point
     * * 2 -- add points 0 and 1 as closure and wrap point.  This is useful when vertex visit requires two adjacent vectors, e.g. for cross products.
     */
    static create(polyface, numWrap = 0) {
        return new ExportGraphicsMeshVisitor(polyface, numWrap);
    }
    /** Restart the visitor at the first facet. */
    reset() {
        this.moveToReadIndex(0);
        this._nextFacetIndex = 0; // so immediate moveToNextFacet stays here
    }
    /** Select a facet by simple index. */
    moveToReadIndex(facetIndex) {
        if (facetIndex < 0 || 2 + facetIndex * 3 >= this._polyface.indices.length)
            return false;
        if (this._currentFacetIndex !== facetIndex || 3 + this._numWrap !== this.point.length) {
            this._currentFacetIndex = facetIndex;
            this.point.length = 0;
            this.pointIndex.length = 0;
            this.edgeVisible.length = 0;
            const sourcePoints = this._polyface.points;
            const indices = this._polyface.indices;
            const i0 = 3 * facetIndex;
            for (let i = i0; i < i0 + 3; i++) {
                const k = 3 * indices[i];
                this.pointIndex.push(indices[i]);
                this.point.pushXYZ(sourcePoints[k], sourcePoints[k + 1], sourcePoints[k + 2]);
                this.edgeVisible.push(true);
            }
            for (let i = 0; i < this._numWrap; i++) {
                this.point.pushFromGrowableXYZArray(this.point, i);
            }
            const sourceParams = this._polyface.params;
            if (sourceParams.length > 0 && this.paramIndex && this.param) {
                this.paramIndex.length = 0;
                this.param.length = 0;
                for (let i = i0; i < i0 + 3; i++) {
                    const k = 2 * indices[i];
                    this.paramIndex.push(indices[i]);
                    this.param.pushXY(sourceParams[k], sourceParams[k + 1]);
                }
                for (let i = 0; i < this._numWrap; i++) {
                    this.param.pushFromGrowableXYArray(this.param, i);
                }
            }
            const sourceNormals = this._polyface.normals;
            if (sourceNormals.length > 0 && this.normalIndex && this.normal) {
                this.normalIndex.length = 0;
                this.normal.length = 0;
                for (let i = i0; i < i0 + 3; i++) {
                    const k = 3 * indices[i];
                    this.normalIndex.push(indices[i]);
                    this.normal.pushXYZ(sourceNormals[k], sourceNormals[k + 1], sourceNormals[k + 2]);
                }
                for (let i = 0; i < this._numWrap; i++) {
                    this.normal.pushFromGrowableXYZArray(this.normal, i);
                }
            }
        }
        this._nextFacetIndex = facetIndex + 1;
        return true;
    }
    /** Load data for the next facet. */
    moveToNextFacet() {
        if (this._nextFacetIndex !== this._currentFacetIndex)
            return this.moveToReadIndex(this._nextFacetIndex);
        this._nextFacetIndex++;
        return true;
    }
    /** Set the number of vertices to replicate in visitor arrays. */
    setNumWrap(numWrap) {
        this._numWrap = numWrap;
    }
    /** Return the index (in the client polyface) of the current facet */
    currentReadIndex() {
        return this._currentFacetIndex;
    }
    /** Return the point index of vertex i within the currently loaded facet */
    clientPointIndex(i) {
        return this.pointIndex[i];
    }
    /** Return the param index of vertex i within the currently loaded facet.
     * Use the artificial paramIndex, which matches pointIndex.
     */
    clientParamIndex(i) {
        return this.paramIndex ? this.paramIndex[i] : -1;
    }
    /** Return the normal index of vertex i within the currently loaded facet.
     * Use the artificial paramIndex, which matches pointIndex.
     */
    clientNormalIndex(i) {
        return this.normalIndex ? this.normalIndex[i] : -1;
    }
    /** Always returns -1 since we never have colors. */
    clientColorIndex(_i) {
        return -1;
    }
    /** Always returns -1 since we never have auxiliary data. */
    clientAuxIndex(_i) {
        return -1;
    }
    /** return the client polyface */
    clientPolyface() {
        return undefined;
    }
    /** clear the contents of all arrays.  Use this along with transferDataFrom methods to build up new facets */
    clearArrays() {
        if (this.point !== undefined)
            this.point.length = 0;
        if (this.param !== undefined)
            this.param.length = 0;
        if (this.normal !== undefined)
            this.normal.length = 0;
        // ignore color and aux -- they never exist.
    }
    /** transfer interpolated data from the other visitor.
     * * all data values are interpolated at `fraction` between `other` values at index0 and index1.
     */
    pushInterpolatedDataFrom(other, index0, fraction, index1) {
        this.point.pushInterpolatedFromGrowableXYZArray(other.point, index0, fraction, index1);
        if (this.param && other.param && index0 < other.param.length && index1 < other.param.length)
            this.param.pushInterpolatedFromGrowableXYArray(other.param, index0, fraction, index1);
        if (this.normal && other.normal && index0 < other.normal.length && index1 < other.normal.length)
            this.normal.pushInterpolatedFromGrowableXYZArray(other.normal, index0, fraction, index1);
    }
    /** transfer data from a specified index of the other visitor as new data in this visitor. */
    pushDataFrom(other, index) {
        this.point.pushFromGrowableXYZArray(other.point, index);
        if (this.param && other.param && index < other.param.length)
            this.param.pushFromGrowableXYArray(other.param, index);
        if (this.normal && other.normal && index < other.normal.length)
            this.normal.pushFromGrowableXYZArray(other.normal, index);
        // ignore color and aux -- they never exist.
    }
    /** Return the number of facets this visitor is able to visit */
    getVisitableFacetCount() {
        return Math.floor(this._polyface.indices.length / 3);
    }
    /** Create a visitor for a subset of the facets visitable by the instance. */
    createSubsetVisitor(facetIndices, numWrap = 0) {
        return ExportGraphicsMeshSubsetVisitor.createSubsetVisitor(this._polyface, facetIndices, numWrap);
    }
}
/**
 * An `ExportGraphicsMeshSubsetVisitor` is an `ExportGraphicsMeshVisitor` which only visits a subset of the facets.
 * * The subset is defined by an array of facet indices provided when this visitor is created.
 * * Input indices (e.g., for `moveToReadIndex`) are understood to be indices into the subset array.
 * @public
 */
export class ExportGraphicsMeshSubsetVisitor extends ExportGraphicsMeshVisitor {
    _facetIndices;
    _currentSubsetIndex; // index within _facetIndices
    _nextSubsetIndex; // index within _facetIndices
    constructor(polyface, facetIndices, numWrap) {
        super(polyface, numWrap);
        this._facetIndices = facetIndices.slice();
        this._currentSubsetIndex = -1;
        this._nextSubsetIndex = 0;
        this.reset();
    }
    isValidSubsetIndex(index) {
        return index >= 0 && index < this._facetIndices.length;
    }
    /**
     * Create a visitor for iterating a subset of the facets of `polyface`.
     * @param polyface reference to the client polyface, supplying facets
     * @param facetIndices array of indices of facets in the client polyface to visit. This array is cloned.
     * @param numWrap number of vertices replicated in the visitor arrays to facilitate simpler caller code. Default is zero.
     */
    static createSubsetVisitor(polyface, facetIndices, numWrap = 0) {
        return new ExportGraphicsMeshSubsetVisitor(polyface, facetIndices, numWrap);
    }
    /**
     * Advance the iterator to a particular facet in the subset of client polyface facets.
     * @param subsetIndex index into the subset array, not to be confused with the client facet index.
     * @return whether the iterator was successfully moved.
     */
    moveToReadIndex(subsetIndex) {
        if (this.isValidSubsetIndex(subsetIndex)) {
            this._currentSubsetIndex = subsetIndex;
            this._nextSubsetIndex = subsetIndex + 1;
            return super.moveToReadIndex(this._facetIndices[subsetIndex]);
        }
        return false;
    }
    /**
     * Advance the iterator to the next facet in the subset of client polyface facets.
     * @return whether the iterator was successfully moved.
     */
    moveToNextFacet() {
        if (this._nextSubsetIndex !== this._currentSubsetIndex)
            return this.moveToReadIndex(this._nextSubsetIndex);
        this._nextSubsetIndex++;
        return true;
    }
    /** Restart the visitor at the first facet. */
    reset() {
        if (this._facetIndices) { // avoid crash during super ctor when we aren't yet initialized
            this.moveToReadIndex(0);
            this._nextSubsetIndex = 0; // so immediate moveToNextFacet stays here
        }
    }
    /**
     * Return the client polyface facet index (aka "readIndex") for the given subset index.
     * @param subsetIndex index into the subset array. Default is the subset index of the currently visited facet.
     * @return valid client polyface facet index, or `undefined` if invalid subset index.
     */
    parentFacetIndex(subsetIndex) {
        if (undefined === subsetIndex)
            subsetIndex = this._currentSubsetIndex;
        return this.isValidSubsetIndex(subsetIndex) ? this._facetIndices[subsetIndex] : undefined;
    }
    /** Return the number of facets this visitor is able to visit. */
    getVisitableFacetCount() {
        return this._facetIndices.length;
    }
}
//# sourceMappingURL=ExportGraphics.js.map