"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.createEdgeParams = exports.calculateEdgeTableParams = void 0;
const VertexIndices_1 = require("./VertexIndices");
const PolylineParams_1 = require("./PolylineParams");
const core_bentley_1 = require("@itwin/core-bentley");
/** @internal */
function calculateEdgeTableParams(numSegmentEdges, numSilhouettes, maxSize) {
    // Each segment edge requires 2 24-bit indices = 6 bytes = 1.5 RGBA values.
    // Each silhouette requires the same as segment edge plus 2 16-bit oct-encoded normals = 10 bytes = 2.5 RGBA values.
    let nRgbaRequired = Math.ceil(1.5 * numSegmentEdges + 2.5 * numSilhouettes);
    const silhouetteStartByteIndex = numSegmentEdges * 6;
    let silhouettePadding = 0;
    let width = nRgbaRequired;
    let height = 1;
    if (nRgbaRequired >= maxSize) {
        // Make roughly square to reduce unused space in last row.
        width = Math.ceil(Math.sqrt(nRgbaRequired));
        // Each entry's data must fit on the same row. 15 RGBA = 60 bytes = lowest common multiple of 6, 10, and 4.
        const remainder = width % 15;
        if (0 !== remainder)
            width += 15 - remainder;
        // If the table contains both segments and silhouettes, there may be one row containing a mix of the two where padding
        // is required between them.
        if (numSilhouettes > 0 && numSegmentEdges > 0) {
            const silOffset = silhouetteStartByteIndex % 60; // some multiple of 6.
            silhouettePadding = (60 - silOffset) % 10;
            nRgbaRequired += Math.ceil(silhouettePadding / 4);
        }
        height = Math.ceil(nRgbaRequired / width);
        if (width * height < nRgbaRequired)
            height++;
    }
    return {
        width,
        height,
        silhouettePadding,
        silhouetteStartByteIndex,
    };
}
exports.calculateEdgeTableParams = calculateEdgeTableParams;
function convertPolylinesAndEdges(polylines, edges) {
    let numIndices = undefined !== edges ? edges.length : 0;
    if (undefined !== polylines)
        for (const pd of polylines)
            numIndices += (pd.length - 1);
    if (0 === numIndices)
        return undefined;
    numIndices *= 6;
    const indexBytes = new Uint8Array(numIndices * 3);
    const endPointAndQuadIndexBytes = new Uint8Array(numIndices * 4);
    let ndx = 0;
    let ndx2 = 0;
    const addPoint = (p0, p1, quadIndex) => {
        VertexIndices_1.VertexIndices.encodeIndex(p0, indexBytes, ndx);
        ndx += 3;
        VertexIndices_1.VertexIndices.encodeIndex(p1, endPointAndQuadIndexBytes, ndx2);
        endPointAndQuadIndexBytes[ndx2 + 3] = quadIndex;
        ndx2 += 4;
    };
    if (undefined !== polylines) {
        for (const pd of polylines) {
            const num = pd.length - 1;
            for (let i = 0; i < num; ++i) {
                let p0 = pd[i];
                let p1 = pd[i + 1];
                if (p1 < p0) { // swap so that lower index is first.
                    p0 = p1;
                    p1 = pd[i];
                }
                addPoint(p0, p1, 0);
                addPoint(p1, p0, 2);
                addPoint(p0, p1, 1);
                addPoint(p0, p1, 1);
                addPoint(p1, p0, 2);
                addPoint(p1, p0, 3);
            }
        }
    }
    if (undefined !== edges) {
        for (const meshEdge of edges) {
            const p0 = meshEdge.indices[0];
            const p1 = meshEdge.indices[1];
            addPoint(p0, p1, 0);
            addPoint(p1, p0, 2);
            addPoint(p0, p1, 1);
            addPoint(p0, p1, 1);
            addPoint(p1, p0, 2);
            addPoint(p1, p0, 3);
        }
    }
    return {
        indices: new VertexIndices_1.VertexIndices(indexBytes),
        endPointAndQuadIndices: endPointAndQuadIndexBytes,
    };
}
function convertSilhouettes(edges, normalPairs) {
    const base = convertPolylinesAndEdges(undefined, edges);
    if (undefined === base)
        return undefined;
    const normalPairBytes = new Uint8Array(normalPairs.length * 6 * 4);
    const normalPair16 = new Uint16Array(normalPairBytes.buffer);
    let ndx = 0;
    for (const pair of normalPairs) {
        for (let i = 0; i < 6; i++) {
            normalPair16[ndx++] = pair.first.value;
            normalPair16[ndx++] = pair.second.value;
        }
    }
    return {
        indices: base.indices,
        endPointAndQuadIndices: base.endPointAndQuadIndices,
        normalPairs: normalPairBytes,
    };
}
function buildIndexedEdges(args, doPolylines, maxSize) {
    const hardEdges = args.edges?.edges;
    const silhouettes = args.silhouettes;
    const polylines = doPolylines ? args.polylines?.lines : undefined;
    const numHardEdges = hardEdges?.length ?? 0;
    const numSilhouettes = silhouettes?.edges?.length ?? 0;
    const numPolylines = polylines ? polylines.reduce((count, pd) => count + Math.max(0, pd.length - 1), 0) : 0;
    const numSegmentEdges = numHardEdges + numPolylines;
    const numTotalEdges = numSegmentEdges + numSilhouettes;
    if (numTotalEdges === 0)
        return undefined;
    // Each edge is a quad consisting of six vertices. Each vertex is an identical 24-bit index into the lookup table.
    const indices = new VertexIndices_1.VertexIndices(new Uint8Array(numTotalEdges * 6 * 3));
    for (let i = 0; i < numTotalEdges; i++)
        for (let j = 0; j < 6; j++)
            indices.setNthIndex(i * 6 + j, i);
    const { width, height, silhouettePadding, silhouetteStartByteIndex } = calculateEdgeTableParams(numSegmentEdges, numSilhouettes, maxSize);
    const data = new Uint8Array(width * height * 4);
    function setUint24(byteIndex, value) {
        data[byteIndex + 0] = value & 0x0000ff;
        data[byteIndex + 1] = (value & 0x00ff00) >>> 8;
        data[byteIndex + 2] = (value & 0xff0000) >>> 16;
    }
    function setEdge(index, startPointIndex, endPointIndex) {
        const byteIndex = index * 6;
        setUint24(byteIndex, startPointIndex);
        setUint24(byteIndex + 3, endPointIndex);
    }
    let curIndex = 0;
    if (hardEdges)
        for (const edge of hardEdges)
            setEdge(curIndex++, edge.indices[0], edge.indices[1]);
    if (polylines) {
        for (const pd of polylines) {
            const num = pd.length - 1;
            for (let i = 0; i < num; i++) {
                const p0 = pd[i];
                const p1 = pd[i + 1];
                // Ensure lower index is first.
                if (p0 < p1)
                    setEdge(curIndex++, p0, p1);
                else
                    setEdge(curIndex++, p1, p0);
            }
        }
    }
    if (silhouettes?.edges) {
        (0, core_bentley_1.assert)(undefined !== silhouettes.normals);
        (0, core_bentley_1.assert)(silhouettes.normals.length === silhouettes.edges.length);
        function setSilhouette(index, start, end, normals) {
            const byteIndex = silhouetteStartByteIndex + silhouettePadding + index * 10;
            setUint24(byteIndex, start);
            setUint24(byteIndex + 3, end);
            data[byteIndex + 6] = normals.first.value & 0xff;
            data[byteIndex + 7] = (normals.first.value & 0xff00) >>> 8;
            data[byteIndex + 8] = normals.second.value & 0xff;
            data[byteIndex + 9] = (normals.second.value & 0xff00) >>> 8;
        }
        curIndex = 0;
        for (let i = 0; i < silhouettes.edges.length; i++)
            setSilhouette(curIndex++, silhouettes.edges[i].indices[0], silhouettes.edges[i].indices[1], silhouettes.normals[i]);
    }
    return {
        indices,
        edges: {
            data,
            width,
            height,
            numSegments: numSegmentEdges,
            silhouettePadding,
        },
    };
}
/** @internal */
function createEdgeParams(args) {
    const { meshArgs, maxWidth, createIndexed } = args;
    const edgeArgs = meshArgs.edges;
    if (!edgeArgs)
        return undefined;
    const doJoints = (0, PolylineParams_1.wantJointTriangles)(edgeArgs.width, true === meshArgs.is2d);
    const polylines = doJoints ? (0, PolylineParams_1.tesselatePolylineFromMesh)(meshArgs) : undefined;
    let segments;
    let silhouettes;
    let indexed;
    if (createIndexed) {
        indexed = buildIndexedEdges(edgeArgs, !doJoints, maxWidth);
    }
    else {
        segments = convertPolylinesAndEdges(undefined, edgeArgs.edges.edges);
        silhouettes = edgeArgs.silhouettes.edges && edgeArgs.silhouettes.normals ? convertSilhouettes(edgeArgs.silhouettes.edges, edgeArgs.silhouettes.normals) : undefined;
    }
    if (!segments && !silhouettes && !polylines && !indexed)
        return undefined;
    return {
        weight: edgeArgs.width,
        linePixels: edgeArgs.linePixels,
        segments,
        silhouettes,
        polylines,
        indexed,
    };
}
exports.createEdgeParams = createEdgeParams;
//# sourceMappingURL=EdgeParams.js.map