"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 Bspline
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.InterpolationCurve3d = exports.InterpolationCurve3dOptions = void 0;
const BSplineCurveOps_1 = require("../bspline/BSplineCurveOps");
const ProxyCurve_1 = require("../curve/ProxyCurve");
const Geometry_1 = require("../Geometry");
const Point3dVector3d_1 = require("../geometry3d/Point3dVector3d");
const PointHelpers_1 = require("../geometry3d/PointHelpers");
const BSplineCurve_1 = require("./BSplineCurve");
/**
 * fitPoints and end condition data for [[InterpolationCurve3d]]
 * * This is a "typed object" version of the serializer-friendly [[InterpolationCurve3dProps]]
 * * Typical use cases rarely require all parameters, so the constructor does not itemize them as parameters.
 * @public
 */
class InterpolationCurve3dOptions {
    /**
     * Constructor.
     * @param fitPoints points to CAPTURE
     * @param knots array to CAPTURE
     */
    constructor(fitPoints, knots) {
        this._fitPoints = fitPoints ? fitPoints : [];
        this._knots = knots;
    }
    _order;
    _closed;
    _isChordLenKnots;
    _isColinearTangents;
    _isChordLenTangents;
    _isNaturalTangents;
    _startTangent;
    _endTangent;
    _fitPoints;
    _knots;
    /** `order` as property */
    get order() { return Geometry_1.Geometry.resolveNumber(this._order, 4); }
    set order(val) { this._order = val; }
    /** `closed` as property */
    get closed() { return Geometry_1.Geometry.resolveValue(this._closed, false); }
    set closed(val) { this._closed = val; }
    /** `isChordLenKnots` as property */
    get isChordLenKnots() { return Geometry_1.Geometry.resolveNumber(this._isChordLenKnots, 0); }
    set isChordLenKnots(val) { this._isChordLenKnots = val; }
    /** `isColinearTangents` as property */
    get isColinearTangents() { return Geometry_1.Geometry.resolveNumber(this._isColinearTangents, 0); }
    set isColinearTangents(val) { this._isColinearTangents = val; }
    /** `isChordLenTangents` as property */
    get isChordLenTangents() { return Geometry_1.Geometry.resolveNumber(this._isChordLenTangents, 0); }
    set isChordLenTangents(val) { this._isChordLenTangents = val; }
    /** `isNaturalTangents` as property */
    get isNaturalTangents() { return Geometry_1.Geometry.resolveNumber(this._isNaturalTangents, 0); }
    set isNaturalTangents(val) { this._isNaturalTangents = val; }
    /** access POSSIBLY UNDEFINED start tangent. Setter CAPTURES. */
    get startTangent() { return this._startTangent; }
    set startTangent(val) { this._startTangent = val; }
    /** access POSSIBLY UNDEFINED end tangent. Setter CAPTURES. */
    get endTangent() { return this._endTangent; }
    set endTangent(val) { this._endTangent = val; }
    /** access POINTER TO fit points. Setter CAPTURES. */
    get fitPoints() { return this._fitPoints; }
    set fitPoints(val) { this._fitPoints = val; }
    /** access POSSIBLY UNDEFINED knots array. Setter CAPTURES. */
    get knots() { return this._knots; }
    set knots(val) { this._knots = val; }
    /** One step setup of properties not named in constructor.
     * * CAPTURE pointers to tangents.
     * * OPTIONALLY downgrade "0" values to undefined.
     */
    captureOptionalProps(order, closed, isChordLenKnots, isColinearTangents, isChordLenTangent, isNaturalTangents, startTangent, endTangent) {
        this._order = Geometry_1.Geometry.resolveToUndefined(order, 0);
        this._closed = Geometry_1.Geometry.resolveToUndefined(closed, false);
        this._isChordLenKnots = Geometry_1.Geometry.resolveToUndefined(isChordLenKnots, 0);
        this._isColinearTangents = Geometry_1.Geometry.resolveToUndefined(isColinearTangents, 0);
        this._isChordLenTangents = Geometry_1.Geometry.resolveToUndefined(isChordLenTangent, 0);
        this._isNaturalTangents = Geometry_1.Geometry.resolveToUndefined(isNaturalTangents, 0);
        this._startTangent = startTangent;
        this._endTangent = endTangent;
    }
    /** Clone with strongly typed members reduced to simple json, with "undefined" members omitted */
    cloneAsInterpolationCurve3dProps() {
        const props = {
            fitPoints: PointHelpers_1.Point3dArray.cloneDeepJSONNumberArrays(this.fitPoints),
            knots: this._knots?.slice(),
        };
        if (this._order !== undefined)
            props.order = this._order;
        if (this._closed !== undefined)
            props.closed = this._closed;
        if (this._isChordLenKnots !== undefined)
            props.isChordLenKnots = this._isChordLenKnots;
        if (this._isColinearTangents !== undefined)
            props.isColinearTangents = this._isColinearTangents;
        if (this._isChordLenTangents !== undefined)
            props.isChordLenTangents = this._isChordLenTangents;
        if (this._isNaturalTangents !== undefined)
            props.isNaturalTangents = this._isNaturalTangents;
        if (this._startTangent !== undefined)
            props.startTangent = this._startTangent?.toArray();
        if (this._endTangent !== undefined)
            props.endTangent = this._endTangent?.toArray();
        return props;
    }
    /** Clone with strongly typed members reduced to simple json. */
    clone() {
        const clone = new InterpolationCurve3dOptions(PointHelpers_1.Point3dArray.clonePoint3dArray(this.fitPoints), this.knots?.slice());
        clone._order = this.order;
        clone._closed = this.closed;
        clone._isChordLenKnots = this.isChordLenKnots;
        clone._isColinearTangents = this.isColinearTangents;
        clone._isChordLenTangents = this.isChordLenTangents;
        clone._isNaturalTangents = this.isNaturalTangents;
        clone._startTangent = this._startTangent?.clone();
        clone._endTangent = this._endTangent?.clone();
        return clone;
    }
    /** Clone with strongly typed members reduced to simple json. */
    static create(source) {
        const result = new InterpolationCurve3dOptions(PointHelpers_1.Point3dArray.clonePoint3dArray(source.fitPoints), source.knots?.slice());
        result._order = source.order;
        result._closed = source.closed;
        result._isChordLenKnots = source.isChordLenKnots;
        result._isColinearTangents = source.isColinearTangents;
        result._isChordLenTangents = source.isChordLenTangents;
        result._isNaturalTangents = source.isNaturalTangents;
        result._startTangent = source.startTangent ? Point3dVector3d_1.Vector3d.fromJSON(source.startTangent) : undefined;
        result._endTangent = source.endTangent ? Point3dVector3d_1.Vector3d.fromJSON(source.endTangent) : undefined;
        return result;
    }
    // ugh.
    // vector equality test with awkward rule that 000 matches undefined.
    static areAlmostEqualAllow000AsUndefined(a, b) {
        if (a !== undefined && a.maxAbs() === 0)
            a = undefined;
        if (b !== undefined && b.maxAbs() === 0)
            b = undefined;
        if (a !== undefined && b !== undefined)
            return a.isAlmostEqual(b);
        return a === undefined && b === undefined;
    }
    static areAlmostEqual(dataA, dataB) {
        if (dataA === undefined && dataB === undefined)
            return true;
        if (dataA !== undefined && dataB !== undefined) {
            if (Geometry_1.Geometry.areEqualAllowUndefined(dataA.order, dataB.order)
                && Geometry_1.Geometry.areEqualAllowUndefined(dataA.closed, dataB.closed)
                && Geometry_1.Geometry.areEqualAllowUndefined(dataA.isChordLenKnots, dataB.isChordLenKnots)
                && Geometry_1.Geometry.areEqualAllowUndefined(dataA.isColinearTangents, dataB.isColinearTangents)
                && Geometry_1.Geometry.areEqualAllowUndefined(dataA.isNaturalTangents, dataB.isNaturalTangents)
                && this.areAlmostEqualAllow000AsUndefined(dataA.startTangent, dataB.startTangent)
                && this.areAlmostEqualAllow000AsUndefined(dataA.endTangent, dataB.endTangent)
                && Geometry_1.Geometry.almostEqualArrays(dataA.fitPoints, dataB.fitPoints, (a, b) => a.isAlmostEqual(b))) {
                if (Geometry_1.Geometry.almostEqualNumberArrays(dataA.knots, dataB.knots, (a, b) => a === b))
                    return true;
                if (dataA.knots === undefined && dataB.knots === undefined)
                    return true;
                /* alas .. need to allow tricky mismatch of end replication */
                let knotsA = dataA.knots, knotsB = dataB.knots;
                if (dataA.knots === undefined) // construct undefined knots to compare against defined knots
                    knotsA = BSplineCurveOps_1.BSplineCurveOps.C2CubicFit.constructFitParametersFromPoints(dataA.fitPoints, dataA.isChordLenKnots, dataA.closed);
                else if (dataB.knots === undefined)
                    knotsB = BSplineCurveOps_1.BSplineCurveOps.C2CubicFit.constructFitParametersFromPoints(dataB.fitPoints, dataB.isChordLenKnots, dataB.closed);
                knotsA = BSplineCurveOps_1.BSplineCurveOps.C2CubicFit.convertCubicKnotVectorToFitParams(knotsA, dataA.fitPoints.length, false);
                knotsB = BSplineCurveOps_1.BSplineCurveOps.C2CubicFit.convertCubicKnotVectorToFitParams(knotsB, dataB.fitPoints.length, false);
                return Geometry_1.Geometry.almostEqualNumberArrays(knotsA, knotsB, (a, b) => Geometry_1.Geometry.isAlmostEqualNumber(a, b));
            }
        }
        return false;
    }
    /** reverse the order or sense of all start-to-end related properties. */
    reverseInPlace() {
        this.fitPoints.reverse();
        if (this.knots)
            this.knots.reverse();
        // Swap pointers to tangents. They don't need to be negated because they point into the curve.
        const oldStart = this._startTangent;
        this._startTangent = this.endTangent;
        this._endTangent = oldStart;
    }
}
exports.InterpolationCurve3dOptions = InterpolationCurve3dOptions;
/**
 * Interpolating curve.
 * * Derive from [[ProxyCurve]]
 * * Use a [[BSplineCurve3d]] as the proxy
 * *
 * @public
 */
class InterpolationCurve3d extends ProxyCurve_1.ProxyCurve {
    curvePrimitiveType = "interpolationCurve";
    _options;
    /** CAPTURE properties and proxy curve. */
    constructor(properties, proxyCurve) {
        super(proxyCurve);
        this._options = properties;
    }
    /** Second step of double dispatch: call `handler.handleInterpolationCurve3d(this)` */
    dispatchToGeometryHandler(handler) {
        let result = handler.handleInterpolationCurve3d(this);
        if (undefined === result) // if handler doesn't specialize on interpolation curves, default to proxy
            result = this._proxyCurve.dispatchToGeometryHandler(handler);
        return result;
    }
    /**
     * Create an [[InterpolationCurve3d]] based on points, knots, and other properties in the [[InterpolationCurve3dProps]] or [[InterpolationCurve3dOptions]].
     * * This saves a COPY OF the options or props.
     * * Use createCapture () if the options or props can be used without copy
     */
    static create(options) {
        let optionsCopy;
        if (options instanceof InterpolationCurve3dOptions) {
            optionsCopy = options.clone();
        }
        else {
            optionsCopy = InterpolationCurve3dOptions.create(options);
        }
        return InterpolationCurve3d.createCapture(optionsCopy);
    }
    /**
     * Create an [[InterpolationCurve3d]]
     * * The options object is captured into the new curve object (not copied)
     */
    static createCapture(options) {
        const proxyCurve = BSplineCurve_1.BSplineCurve3d.createFromInterpolationCurve3dOptions(options);
        if (proxyCurve)
            return new InterpolationCurve3d(options, proxyCurve);
        return undefined;
    }
    /** Return a (copy of) the defining points, packed as a Float64Array */
    copyFitPointsFloat64Array() {
        return PointHelpers_1.Point3dArray.cloneXYZPropsAsFloat64Array(this._options.fitPoints);
    }
    /**
     * Return json key-value pairs for for this [[InterpolationCurve3d]].
     * @returns
     */
    toJSON() {
        return this._options.cloneAsInterpolationCurve3dProps();
    }
    /** Clone the [[InterpolationCurve3dProps]] object in this [[InterpolationCurve3d]] */
    cloneProps() {
        return this._options.cloneAsInterpolationCurve3dProps();
    }
    /** return the options pointer */
    get options() { return this._options; }
    /**
     * Reverse the curve direction.
     * * This updates both the defining properties and the proxy bspline.
     */
    reverseInPlace() {
        this._proxyCurve.reverseInPlace();
        this._options.reverseInPlace();
    }
    /**
     * Transform this [[InterpolationCurve3d]] and its defining data in place
     */
    tryTransformInPlace(transform) {
        const proxyOk = this._proxyCurve.tryTransformInPlace(transform);
        if (proxyOk) {
            transform.multiplyPoint3dArrayInPlace(this._options.fitPoints);
            if (this._options.startTangent)
                transform.multiplyVectorInPlace(this._options.startTangent);
            if (this._options.endTangent)
                transform.multiplyVectorInPlace(this._options.endTangent);
        }
        return proxyOk;
    }
    /**
     * Find intervals of this CurvePrimitive that are interior to a clipper.
     * * This implementation simply passes the call to the proxy curve.
     * @param clipper clip structure (e.g. clip planes).
     * @param announce (optional) function to be called announcing fractional intervals of the input curve.
     * @returns true if any "in" segments are announced.
     */
    announceClipIntervals(clipper, announce) {
        return this._proxyCurve.announceClipIntervals(clipper, announce);
    }
    /** Return a deep clone */
    clone() {
        return new InterpolationCurve3d(this._options.clone(), this._proxyCurve.clone());
    }
    isAlmostEqual(other) {
        if (other instanceof InterpolationCurve3d) {
            return InterpolationCurve3dOptions.areAlmostEqual(this._options, other._options);
        }
        return false;
    }
    /** Test if `other` is also an [[InterpolationCurve3d]] */
    isSameGeometryClass(other) { return other instanceof InterpolationCurve3d; }
}
exports.InterpolationCurve3d = InterpolationCurve3d;
//# sourceMappingURL=InterpolationCurve3d.js.map