"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.upsampleRealityMeshParams = upsampleRealityMeshParams;
const core_geometry_1 = require("@itwin/core-geometry");
const core_common_1 = require("@itwin/core-common");
const RealityMeshParams_1 = require("../../render/RealityMeshParams");
const core_bentley_1 = require("@itwin/core-bentley");
class UpsampleIndexMap extends Map {
    _next = 0;
    indices = new Array();
    addTriangle(indices) {
        for (const index of indices) {
            let mapIndex = this.get(index);
            if (undefined === mapIndex)
                this.set(index, mapIndex = this._next++);
            this.indices.push(mapIndex);
        }
    }
}
class ClipAxis {
    vertical;
    lessThan;
    value;
    constructor(vertical, lessThan, value) {
        this.vertical = vertical;
        this.lessThan = lessThan;
        this.value = value;
    }
}
function upsampleRealityMeshParams(params, uvSampleRange) {
    const indexMap = new UpsampleIndexMap();
    const uvParams = core_common_1.QParams2d.fromZeroToOne();
    const uvLow = core_common_1.QPoint2d.create(uvSampleRange.low, uvParams);
    const uvHigh = core_common_1.QPoint2d.create(uvSampleRange.high, uvParams);
    const uvRange = core_geometry_1.Range2d.createXYXY(uvLow.x, uvLow.y, uvHigh.x, uvHigh.y);
    const clipAxes = new Array();
    const addedPoints = new Array(), addedParams = new Array(), addedNormals = new Array();
    if (uvLow.x > 0)
        clipAxes.push(new ClipAxis(true, false, uvLow.x));
    if (uvHigh.x < core_common_1.Quantization.rangeScale16)
        clipAxes.push(new ClipAxis(true, true, uvHigh.x));
    if (uvLow.y > 0)
        clipAxes.push(new ClipAxis(false, false, uvLow.y));
    if (uvHigh.y < core_common_1.Quantization.rangeScale16)
        clipAxes.push(new ClipAxis(false, true, uvHigh.y));
    const triangleRange = core_geometry_1.Range2d.createNull();
    for (let i = 0; i < params.indices.length;) {
        const triangleIndices = [params.indices[i++], params.indices[i++], params.indices[i++]];
        core_geometry_1.Range2d.createNull(triangleRange);
        for (const index of triangleIndices) {
            const paramIndex = 2 * index;
            triangleRange.extendXY(params.uvs.points[paramIndex], params.uvs.points[paramIndex + 1]);
        }
        if (uvRange.intersectsRange(triangleRange)) {
            if (uvRange.containsRange(triangleRange)) {
                indexMap.addTriangle(triangleIndices);
            }
            else {
                addClipped(params, triangleIndices, indexMap, clipAxes, 0, addedPoints, addedParams, addedNormals);
            }
        }
    }
    const parentPoints = params.positions;
    const parentParams = params.uvs;
    const parentNormals = params.normals;
    const parentPointCount = parentPoints.points.length / 3;
    const zRange = core_geometry_1.Range1d.createNull();
    const builder = new RealityMeshParams_1.RealityMeshParamsBuilder({
        positionRange: parentPoints.params.computeRange(),
        initialVertexCapacity: indexMap.size,
        initialIndexCapacity: indexMap.indices.length,
        wantNormals: parentNormals !== undefined,
    });
    const pos = new core_common_1.QPoint3d();
    const uv = new core_common_1.QPoint2d();
    for (const entry of indexMap.entries()) {
        const parentIndex = entry[0];
        let normal;
        if (parentIndex < parentPointCount) {
            const pointIndex = 3 * parentIndex;
            pos.setFromScalars(parentPoints.points[pointIndex], parentPoints.points[pointIndex + 1], parentPoints.points[pointIndex + 2]);
            const paramIndex = 2 * parentIndex;
            uv.setFromScalars(parentParams.points[paramIndex], parentParams.points[paramIndex + 1]);
            if (parentNormals)
                normal = parentNormals[parentIndex];
        }
        else {
            const addedIndex = parentIndex - parentPointCount;
            addedPoints[addedIndex].clone(pos);
            addedParams[addedIndex].clone(uv);
            if (addedNormals.length > 0)
                normal = addedNormals[addedIndex];
        }
        builder.addQuantizedVertex(pos, uv, normal);
        zRange.extendX(pos.z);
    }
    builder.addIndices(indexMap.indices);
    const mesh = builder.finish();
    const qParams = mesh.positions.params;
    return {
        mesh: builder.finish(),
        heightRange: core_geometry_1.Range1d.createXX(core_common_1.Quantization.unquantize(zRange.low, qParams.origin.z, qParams.scale.z), core_common_1.Quantization.unquantize(zRange.high, qParams.origin.z, qParams.scale.z)),
    };
}
function interpolate(value0, value1, fraction) {
    return value0 + (value1 - value0) * fraction;
}
function interpolateInt(value0, value1, fraction) {
    return Math.floor(.5 + interpolate(value0, value1, fraction));
}
function interpolateQPoint3d(qPoint, qNext, fraction) {
    return core_common_1.QPoint3d.fromScalars(interpolateInt(qPoint.x, qNext.x, fraction), interpolateInt(qPoint.y, qNext.y, fraction), interpolateInt(qPoint.z, qNext.z, fraction));
}
function interpolateQPoint2d(qPoint, qNext, fraction) {
    return core_common_1.QPoint2d.fromScalars(interpolateInt(qPoint.x, qNext.x, fraction), interpolateInt(qPoint.y, qNext.y, fraction));
}
function interpolateOctEncodedNormal(normal0, normal1, fraction) {
    const n0 = core_common_1.OctEncodedNormal.decodeValue(normal0);
    const n1 = core_common_1.OctEncodedNormal.decodeValue(normal1);
    if (undefined !== n0 && undefined !== n1) {
        const n = core_geometry_1.Vector3d.create(interpolate(n0.x, n1.x, fraction), interpolate(n0.y, n1.y, fraction), interpolate(n0.z, n1.z, fraction));
        n.normalizeInPlace();
        return core_common_1.OctEncodedNormal.encode(n);
    }
    else {
        return core_common_1.OctEncodedNormal.encode(core_geometry_1.Vector3d.create(0, 0, 1));
    }
}
function addClipped(params, triangleIndices, indexMap, clipAxes, clipIndex, addedPoints, addedParams, addedNormals) {
    if (clipIndex === clipAxes.length) {
        indexMap.addTriangle(triangleIndices);
        return;
    }
    const inside = [false, false, false];
    const values = [0, 0, 0];
    const clipOutput = [];
    const parentPoints = params.positions.points;
    const parentParams = params.uvs.points;
    const parentNormals = params.normals;
    const clipAxis = clipAxes[clipIndex++];
    const clipValue = clipAxis.value;
    const parentPointCount = parentPoints.length / 3;
    const scratchQPoint3d = new core_common_1.QPoint3d(), scratchQPoint3d1 = new core_common_1.QPoint3d();
    const scratchQPoint2d = new core_common_1.QPoint2d(), scratchQPoint2d1 = new core_common_1.QPoint2d();
    const getPoint = (index, result) => {
        if (index < parentPointCount) {
            const pointIndex = index * 3;
            result.setFromScalars(parentPoints[pointIndex], parentPoints[pointIndex + 1], parentPoints[pointIndex + 2]);
        }
        else {
            addedPoints[index - parentPointCount].clone(result);
        }
        return result;
    };
    const getParam = (index, result) => {
        if (index < parentPointCount) {
            const pointIndex = index * 2;
            result.setFromScalars(parentParams[pointIndex], parentParams[pointIndex + 1]);
        }
        else {
            addedParams[index - parentPointCount].clone(result);
        }
        return result;
    };
    const getNormal = (index) => {
        if (!parentNormals)
            return undefined;
        return (index < parentPointCount) ? parentNormals[index] : addedNormals[index - parentPointCount];
    };
    for (let i = 0; i < 3; i++) {
        const index = triangleIndices[i];
        const thisParam = getParam(index, scratchQPoint2d);
        const thisValue = clipAxis.vertical ? thisParam.x : thisParam.y;
        values[i] = thisValue;
        inside[i] = clipAxis.lessThan ? (thisValue < clipValue) : (thisValue > clipValue);
    }
    for (let i = 0; i < 3; i++) {
        const index = triangleIndices[i];
        const next = (i + 1) % 3;
        if (inside[i])
            clipOutput.push(index);
        if (inside[i] !== inside[next]) {
            const nextIndex = triangleIndices[next];
            const fraction = (clipValue - values[i]) / (values[next] - values[i]);
            clipOutput.push(parentPointCount + addedPoints.length);
            addedPoints.push(interpolateQPoint3d(getPoint(index, scratchQPoint3d), getPoint(nextIndex, scratchQPoint3d1), fraction));
            addedParams.push(interpolateQPoint2d(getParam(index, scratchQPoint2d), getParam(nextIndex, scratchQPoint2d1), fraction));
            if (parentNormals)
                addedNormals.push(interpolateOctEncodedNormal((0, core_bentley_1.expectDefined)(getNormal(index)), (0, core_bentley_1.expectDefined)(getNormal(nextIndex)), fraction));
        }
    }
    if (clipOutput.length > 2) {
        addClipped(params, clipOutput.slice(0, 3), indexMap, clipAxes, clipIndex, addedPoints, addedParams, addedNormals);
        if (clipOutput.length > 3)
            addClipped(params, [clipOutput[0], clipOutput[2], clipOutput[3]], indexMap, clipAxes, clipIndex, addedPoints, addedParams, addedNormals);
    }
}
//# sourceMappingURL=UpsampleRealityMeshParams.js.map