/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Curve
 */
import { Geometry } from "../../Geometry";
import { Plane3dByOriginAndVectors } from "../../geometry3d/Plane3dByOriginAndVectors";
import { Point3d } from "../../geometry3d/Point3dVector3d";
import { Ray3d } from "../../geometry3d/Ray3d";
import { Quadrature } from "../../numerics/Quadrature";
import { SimpleNewton } from "../../numerics/Newton";
/**
 * XYCurveEvaluator is an abstract with methods for evaluating X and Y parts of a curve parameterized by a fraction.
 * * The required methods call for independent X and Y evaluation.
 * * Base class methods package those (multiple) calls into point, ray, and plane structures.
 *    * An implementation that has evaluation substantial cost that can be shared among x,y parts or between
 *      primary functions and derivatives might choose to implement the point and derivative methods directly.
 * @internal
 */
export class XYCurveEvaluator {
    /** Evaluate both X and Y at fractional coordinate, return bundled as a point. */
    fractionToPoint(fraction, result) {
        return Point3d.create(this.fractionToX(fraction), this.fractionToY(fraction), 0.0, result);
    }
    /**
     * Evaluate both X and Y and their first derivatives at fractional coordinate, return bundled as origin and
     * (non-unit) direction vector.
     */
    fractionToPointAndDerivative(fraction, result) {
        return Ray3d.createXYZUVW(this.fractionToX(fraction), this.fractionToY(fraction), 0.0, this.fractionToDX(fraction), this.fractionToDY(fraction), 0, result);
    }
    /**
     * Evaluate both X and Y and their second derivatives at fractional coordinate, return bundled as origin and
     * (non-unit) vectorU an vectorV.
     */
    fractionToPointAnd2Derivatives(fraction, result) {
        return Plane3dByOriginAndVectors.createOriginAndVectorsXYZ(this.fractionToX(fraction), this.fractionToY(fraction), 0.0, this.fractionToDX(fraction), this.fractionToDY(fraction), 0, this.fractionToDDX(fraction), this.fractionToDDY(fraction), 0, result);
    }
    /**
     * Return the magnitude of the tangent vector at fraction.
     * @param fraction fractional position along the curve
     */
    fractionToTangentMagnitude(fraction) {
        const u = this.fractionToDX(fraction);
        const v = this.fractionToDY(fraction);
        return Geometry.hypotenuseXY(u, v);
    }
    static _gaussX;
    static _gaussWeight;
    static _gaussMapper;
    /**
     * Initialize class level work arrays for 5 point Gaussian quadrature.
     * * These static variables are reused on calls to [[integrateDistanceBetweenFractions]].
     */
    static initWorkSpace() {
        XYCurveEvaluator._gaussX = new Float64Array(5);
        XYCurveEvaluator._gaussWeight = new Float64Array(5);
        XYCurveEvaluator._gaussMapper = (xA, xB, xMapped, wMapped) => Quadrature.setupGauss5(xA, xB, xMapped, wMapped);
    }
    /**
     * Integrate between nominal fractions with default gauss rule.
     * * The caller is expected to choose nearby fractions so that the single gauss interval accuracy is good.
     */
    integrateDistanceBetweenFractions(fraction0, fraction1) {
        const gaussX = XYCurveEvaluator._gaussX;
        const gaussWeight = XYCurveEvaluator._gaussWeight;
        const numEval = XYCurveEvaluator._gaussMapper(fraction0, fraction1, gaussX, gaussWeight);
        let sum = 0;
        for (let k = 0; k < numEval; k++) {
            sum += gaussWeight[k] * this.fractionToTangentMagnitude(gaussX[k]);
        }
        return sum;
    }
    /**
     * Inverse integrated distance.
     * @param fraction0 start of fraction interval.
     * @param fraction1 end of fraction interval.
     * @param distance0 distance at start.
     * @param distance1 distance at end.
     * @param targetDistance intermediate distance.
     */
    inverseDistanceFraction(fraction0, fraction1, distance0, distance1, targetDistance) {
        const startFraction = Geometry.inverseInterpolate(fraction0, distance0, fraction1, distance1, targetDistance);
        if (startFraction !== undefined) {
            return SimpleNewton.runNewton1D(startFraction, (fraction) => {
                const d = this.integrateDistanceBetweenFractions(fraction0, fraction);
                return distance0 + d - targetDistance;
            }, (fraction) => this.fractionToTangentMagnitude(fraction));
        }
        return undefined;
    }
    /**
     * Get the point and its derivatives at a given fraction.
     * @param fraction fractional position along x axis
     * @param xy xy coordinates of point on the curve
     * @param d1xy first derivative vector
     * @param d2xy second derivative vector
     * @param d3xy third derivative vector
     */
    fractionToPointAnd3Derivatives(fraction, xy, d1xy, d2xy, d3xy) {
        xy.set(this.fractionToX(fraction), this.fractionToY(fraction), 0);
        if (d1xy)
            d1xy.set(this.fractionToDX(fraction), this.fractionToDY(fraction), 0);
        if (d2xy)
            d2xy.set(this.fractionToDDX(fraction), this.fractionToDDY(fraction), 0);
        if (d3xy)
            d3xy.set(this.fractionToD3X(fraction), this.fractionToD3Y(fraction), 0);
    }
}
// at load time, initialize gauss quadrature workspace
XYCurveEvaluator.initWorkSpace();
//# sourceMappingURL=XYCurveEvaluator.js.map