/*---------------------------------------------------------------------------------------------
* 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 { Point3d } from "../geometry3d/Point3dVector3d";
import { Point3dArray } from "../geometry3d/PointHelpers";
import { GeometryQuery } from "./GeometryQuery";
/**
 * A PointString3d is an array of points.
 * * PointString3D is first class (displayable, possibly persistent) geometry derived from the GeometryQuery base class.
 * * The various points in the PointString3d are NOT connected by line segments for display or other calculations.
 * @public
 */
export class PointString3d extends GeometryQuery {
    /** String name for schema properties */
    geometryCategory = "pointCollection";
    /** Test if `other` is a PointString3d */
    isSameGeometryClass(other) {
        return other instanceof PointString3d;
    }
    _points;
    /** Return a clone of the points array. */
    get points() {
        return this._points;
    }
    constructor() {
        super();
        this._points = [];
    }
    /** Clone and apply a transform. */
    cloneTransformed(transform) {
        const c = this.clone();
        c.tryTransformInPlace(transform); // we know tryTransformInPlace succeeds
        return c;
    }
    /**
     * Turn any array (possibly nested) into a "flat" array of objects that are not arrays. This allows processing
     * the objects without recursion into nested arrays.
     */
    static flattenArray(arr) {
        return arr.reduce(
        // a callback function to execute for each element in the array. Its return value becomes
        // the value of the "flat" parameter on the next invocation of the callback function.
        (flat, toFlatten) => {
            return flat.concat(Array.isArray(toFlatten) ? PointString3d.flattenArray(toFlatten) : toFlatten);
        }, []);
    }
    /** Create a PointString3d from points. */
    static create(...points) {
        const result = new PointString3d();
        result.addPoints(points);
        return result;
    }
    /** Add multiple points to the PointString3d. */
    addPoints(...points) {
        const toAdd = PointString3d.flattenArray(points);
        for (const p of toAdd) {
            if (p instanceof Point3d)
                this._points.push(p);
        }
    }
    /** Add a single point to the PointString3d. */
    addPoint(point) {
        this._points.push(point);
    }
    /** Remove the last point added to the PointString3d. */
    popPoint() {
        this._points.pop();
    }
    /** Replace this PointString3d's point array by a clone of the array in `other`. */
    setFrom(other) {
        this._points = Point3dArray.clonePoint3dArray(other._points);
    }
    /** Create from an array of Point3d. */
    static createPoints(points) {
        const ps = new PointString3d();
        ps._points = Point3dArray.clonePoint3dArray(points);
        return ps;
    }
    /** Create a PointString3d from xyz coordinates packed in a Float64Array. */
    static createFloat64Array(xyzData) {
        const ps = new PointString3d();
        for (let i = 0; i + 3 <= xyzData.length; i += 3)
            ps._points.push(Point3d.create(xyzData[i], xyzData[i + 1], xyzData[i + 2]));
        return ps;
    }
    /** Return a deep clone. */
    clone() {
        const retVal = new PointString3d();
        retVal.setFrom(this);
        return retVal;
    }
    /** Replace this instance's points by those from a json array, e.g. `[[1,2,3], [4,5,6]]`. */
    setFromJSON(json) {
        this._points.length = 0;
        if (Array.isArray(json)) {
            let xyz;
            for (xyz of json)
                this._points.push(Point3d.fromJSON(xyz));
        }
    }
    /**
     * Convert an PointString3d to a JSON object.
     * @return {*} e.g., `[[1,2,3], [4,5,6]]`.
     */
    toJSON() {
        const value = [];
        for (const p of this._points)
            value.push(p.toJSON());
        return value;
    }
    /** Create a PointString3d from a json array, e.g. `[[1,2,3], [4,5,6]]`. */
    static fromJSON(json) {
        const ps = new PointString3d();
        ps.setFromJSON(json);
        return ps;
    }
    /** Access a single point by index. */
    pointAt(i, result) {
        if (i >= 0 && i < this._points.length) {
            if (result) {
                result.setFrom(this._points[i]);
                return result;
            }
            return this._points[i].clone();
        }
        return undefined;
    }
    /** Return the number of points. */
    numPoints() {
        return this._points.length;
    }
    /** Reverse the point order */
    reverseInPlace() {
        if (this._points.length >= 2) {
            let i0 = 0;
            let i1 = this._points.length - 1;
            while (i0 < i1) {
                const a = this._points[i1];
                this._points[i1] = this._points[i0];
                this._points[i0] = a;
                i0++;
                i1--;
            }
        }
    }
    /** Apply transform on points in place. */
    tryTransformInPlace(transform) {
        transform.multiplyPoint3dArrayInPlace(this._points);
        return true;
    }
    /** Return the index and coordinates of the closest point to spacePoint. */
    closestPoint(spacePoint) {
        const result = { index: -1, xyz: Point3d.create() };
        const index = Point3dArray.closestPointIndex(this._points, spacePoint);
        if (index >= 0) {
            result.index = index;
            result.xyz.setFrom(this._points[index]);
        }
        return result;
    }
    /** Return true if all points are in the given plane. */
    isInPlane(plane) {
        return Point3dArray.isCloseToPlane(this._points, plane, Geometry.smallMetricDistance);
    }
    /** Extend a range to include the points in this PointString3d (optionally transformed). */
    extendRange(rangeToExtend, transform) {
        rangeToExtend.extendArray(this._points, transform);
    }
    /** Return true if corresponding points are almost equal. */
    isAlmostEqual(other) {
        if (!(other instanceof PointString3d))
            return false;
        return Point3dArray.isAlmostEqual(this._points, other._points);
    }
    /** Reduce to empty set of points. */
    clear() {
        this._points.length = 0;
    }
    /** Second step of double dispatch: call `handler.handlePointString(this)` */
    dispatchToGeometryHandler(handler) {
        return handler.handlePointString3d(this);
    }
}
//# sourceMappingURL=PointString3d.js.map