"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 Geometry
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.QPoint3dBufferBuilder = exports.QPoint2dBufferBuilder = exports.QPoint3dList = exports.QPoint3dBuffer = exports.QPoint3d = exports.QParams3d = exports.QPoint2dList = exports.QPoint2dBuffer = exports.QPoint2d = exports.QParams2d = exports.Quantization = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const core_geometry_1 = require("@itwin/core-geometry");
/**
 * Provides facilities for quantizing floating point values within a specified range into 16-bit unsigned integers.
 * This is a lossy compression technique.
 * Given a floating point range [min, max], a floating point value `x` within that range is quantized by subtracting
 * `min`, scaling the result according to `max`, and truncating the result to an integer.
 * Therefore min quantizes to 0, max to 0xffff, (min+max)/2 to 0x7fff, and so on.
 * These routines are chiefly used by classes like [[QPoint2d]] and [[QPoint3d]] to reduce the space required to store
 * coordinate values for [RenderGraphic]($frontend)s.
 * @public
 * @extensions
 */
var Quantization;
(function (Quantization) {
    Quantization.rangeScale16 = 0xffff;
    Quantization.rangeScale8 = 0xff;
    /** Compute the scale factor required to quantize `extent` to `rangeScale` discrete values. */
    function computeScale(extent, rangeScale = Quantization.rangeScale16) {
        return 0.0 === extent ? extent : rangeScale / extent;
    }
    Quantization.computeScale = computeScale;
    /** Returns true if the quantized value `qpos` fits within the specified range. */
    function isInRange(qpos, rangeScale = Quantization.rangeScale16) {
        return qpos >= 0.0 && qpos < rangeScale + 1.0;
    }
    Quantization.isInRange = isInRange;
    /** Return `pos` quantized to the range [`origin`, `origin + rangeScale`].
     * @see [[Quantization.unquantize]] for the inverse operation.
     */
    function quantize(pos, origin, scale, rangeScale = Quantization.rangeScale16) {
        return Math.floor(Math.max(0.0, Math.min(rangeScale, 0.5 + (pos - origin) * scale)));
    }
    Quantization.quantize = quantize;
    /** Returns true if the value `pos` can be quantized to the specified range. */
    function isQuantizable(pos, origin, scale, rangeScale = Quantization.rangeScale16) {
        return isInRange(quantize(pos, origin, scale, rangeScale));
    }
    Quantization.isQuantizable = isQuantizable;
    /** Give `qpos` quantized to the range [`origin`, `origin + rangeScale`], return the unquantized value.
     * @see [[Quantization.quantize]] for the inverse operation.
     */
    function unquantize(qpos, origin, scale) {
        return 0.0 === scale ? origin : origin + qpos / scale;
    }
    Quantization.unquantize = unquantize;
    /** Returns true if `qpos` is a valid quantized 16-bit value. */
    function isQuantized(qpos) {
        return isInRange(qpos) && qpos === Math.floor(qpos);
    }
    Quantization.isQuantized = isQuantized;
})(Quantization || (exports.Quantization = Quantization = {}));
/** Parameters used for [[Quantization]] of 2d points such that the `x` and `y` components are each quantized to 16-bit unsigned integers.
 * @see [[QPoint2d]] for the quantized representation of a [Point2d]($core-geometry).
 * @see [[QPoint2dList]] for a list of [[QPoint2d]]s quantized using a [[QParams2d]].
 * @public
 * @extensions
 */
class QParams2d {
    /** The origin of the quantization range. */
    origin = new core_geometry_1.Point2d();
    /** The scale applied to coordinates to quantize them. */
    scale = new core_geometry_1.Point2d();
    constructor(ox = 0, oy = 0, sx = 0, sy = 0) { this.setFrom(ox, oy, sx, sy); }
    setFrom(ox, oy, sx, sy) {
        this.origin.x = ox;
        this.origin.y = oy;
        this.scale.x = sx;
        this.scale.y = sy;
    }
    /** Set [[origin]] and [[scale]] from `src`. */
    copyFrom(src) {
        this.setFrom(src.origin.x, src.origin.y, src.scale.x, src.scale.y);
    }
    /** Create a copy of these params.
     * @param out If supplied, these QParams2d will be modified and returned; otherwise a new QParams2d object will be created and returned.
     */
    clone(out) {
        const result = undefined !== out ? out : new QParams2d();
        result.copyFrom(this);
        return result;
    }
    /** Initialize these parameters to support quantization of values within the specified range. */
    setFromRange(range, rangeScale = Quantization.rangeScale16) {
        if (!range.isNull) {
            this.setFrom(range.low.x, range.low.y, Quantization.computeScale(range.high.x - range.low.x, rangeScale), Quantization.computeScale(range.high.y - range.low.y, rangeScale));
        }
        else {
            this.origin.x = this.origin.y = this.scale.x = this.scale.y = 0;
        }
    }
    /** Create parameters to support quantization of values within the specified range. */
    static fromRange(range, out, rangeScale = Quantization.rangeScale16) {
        const params = undefined !== out ? out : new QParams2d();
        params.setFromRange(range, rangeScale);
        return params;
    }
    /** Return the unquantized point for the input `x` and `y` components. If `out` is supplied, it will be modified to hold the result and returned. */
    unquantize(x, y, out) {
        out = out ?? new core_geometry_1.Point2d();
        out.x = Quantization.unquantize(x, this.origin.x, this.scale.x);
        out.y = Quantization.unquantize(y, this.origin.y, this.scale.y);
        return out;
    }
    /** Creates parameters supporting quantization of values within the range [-1.0, 1.0], appropriate for normalized 2d vectors. */
    static fromNormalizedRange(rangeScale = Quantization.rangeScale16) {
        return QParams2d.fromRange(core_geometry_1.Range2d.createArray([core_geometry_1.Point2d.create(-1, -1), core_geometry_1.Point2d.create(1, 1)]), undefined, rangeScale);
    }
    /** Create parameters supporting quantization of values within the range [0.0, 1.0]. */
    static fromZeroToOne(rangeScale = Quantization.rangeScale16) {
        return QParams2d.fromRange(core_geometry_1.Range2d.createArray([core_geometry_1.Point2d.create(0, 0), core_geometry_1.Point2d.create(1, 1)]), undefined, rangeScale);
    }
    /** Create parameters from origin and scale components */
    static fromOriginAndScale(originX, originY, scaleX, scaleY) {
        return new QParams2d(originX, originY, scaleX, scaleY);
    }
    /** The diagonal of the unquantized range represented by these parameters. */
    get rangeDiagonal() {
        return core_geometry_1.Vector2d.createFrom({ x: 0 === this.scale.x ? 0 : Quantization.rangeScale16 / this.scale.x, y: 0 === this.scale.y ? 0 : Quantization.rangeScale16 / this.scale.y });
    }
    /** Return true if the point point is quantizable using these parameters. */
    isQuantizable(point) {
        return Quantization.isQuantizable(point.x, this.origin.x, this.scale.x) && Quantization.isQuantizable(point.y, this.origin.y, this.scale.y);
    }
    /** @alpha */
    toJSON() {
        return {
            origin: { x: this.origin.x, y: this.origin.y },
            scale: { x: this.scale.x, y: this.scale.y },
        };
    }
    /** @alpha */
    static fromJSON(src) {
        return this.fromOriginAndScale(src.origin.x, src.origin.y, src.scale.x, src.scale.y);
    }
}
exports.QParams2d = QParams2d;
/** Represents a [Point2d]($core-geometry) compressed such that each component `x` and `y` is quantized to the 16-bit integer range [0, 0xffff].
 * These are primarily used to reduce the space required for coordinates used by [RenderGraphic]($frontend)s.
 * @see [[QParams2d]] to define quantization parameters for a range of points.
 * @see [[QPoint2dList]] for a list of points all quantized to the same range.
 * @public
 * @extensions
 */
class QPoint2d {
    _x = 0;
    _y = 0;
    /** The quantized x component. */
    get x() { return this._x; }
    set x(x) {
        (0, core_bentley_1.assert)(Quantization.isQuantized(x));
        this._x = x;
    }
    /** The quantized y component. */
    get y() { return this._y; }
    set y(y) {
        (0, core_bentley_1.assert)(Quantization.isQuantized(y));
        this._y = y;
    }
    /** Construct with `x` and `y` initialized to zero. */
    constructor() { }
    /** Initialize this point by quantizing the supplied { x, y } using the specified params */
    init(pos, params) {
        this.x = Quantization.quantize(pos.x, params.origin.x, params.scale.x);
        this.y = Quantization.quantize(pos.y, params.origin.y, params.scale.y);
    }
    /** Create a quantized point from the supplied Point2d using the specified params */
    static create(pos, params) {
        const qpt = new QPoint2d();
        qpt.init(pos, params);
        return qpt;
    }
    /** Initialize `x` and `y` from `src`. */
    copyFrom(src) {
        this.x = src.x;
        this.y = src.y;
    }
    /** Create a copy of this point.
     * @param out If supplied, it will be modified in-place and returned; otherwise a new QPoint2d will be allocated and returned.
     */
    clone(out) {
        const result = undefined !== out ? out : new QPoint2d();
        result.copyFrom(this);
        return result;
    }
    /**
     * Set the x and y components directly.
     * @param x Must be an integer in the range [0, 0xffff]
     * @param y Must be an integer in the range [0, 0xffff]
     */
    setFromScalars(x, y) {
        this.x = x;
        this.y = y;
    }
    /**
     * Create a QPoint2d directly from x and y components.
     * @param x Must be an integer in the range [0, 0xffff]
     * @param y Must be an integer in the range [0, 0xffff]
     */
    static fromScalars(x, y) {
        const pt = new QPoint2d();
        pt.setFromScalars(x, y);
        return pt;
    }
    /** Return a Point2d unquantized according to the supplied `params`. If `out` is supplied, it will be modified in-place and returned. */
    unquantize(params, out) {
        const pt = undefined !== out ? out : new core_geometry_1.Point2d();
        pt.x = Quantization.unquantize(this.x, params.origin.x, params.scale.x);
        pt.y = Quantization.unquantize(this.y, params.origin.y, params.scale.y);
        return pt;
    }
}
exports.QPoint2d = QPoint2d;
/**
 * @public
 * @extensions
 */
var QPoint2dBuffer;
(function (QPoint2dBuffer) {
    const scratchQPoint2d = new QPoint2d();
    /** Extracts the point at the specified index from a buffer.
     * @param points The buffer in which each consecutive pair of integers is a 2d quantized point.
     * @param pointIndex The index of the point to extract, ranging from zero to one less than the number of points in the buffer.
     * @param result If supplied, a preallocated [[QPoint2d]] to initialize with the result and return.
     * @returns The point at `pointIndex`.
     * @throws Error if `pointIndex` is out of bounds.
     */
    function getQPoint(points, pointIndex, result) {
        const index = pointIndex * 2;
        const x = points[index + 0];
        const y = points[index + 1];
        if (undefined === x || undefined === y)
            throw new Error("Index out of range");
        result = result ?? new QPoint2d();
        result.setFromScalars(x, y);
        return result;
    }
    QPoint2dBuffer.getQPoint = getQPoint;
    /** Extracts and unquantizes the point at the specified index from a buffer.
     * @param buffer The array of points and the quantization parameters.
     * @param The index of the point to extract, ranging from zero to one less than the number of points in the buffer.
     * @param result If supplied, a preallocated [Point2d]($core-geometry) to initialize with the result and return.
     * @returns The point at `pointIndex`.
     * @throws Error if `pointIndex` is out of bounds.
     */
    function unquantizePoint(buffer, pointIndex, result) {
        const qpt = getQPoint(buffer.points, pointIndex, scratchQPoint2d);
        return qpt.unquantize(buffer.params, result);
    }
    QPoint2dBuffer.unquantizePoint = unquantizePoint;
})(QPoint2dBuffer || (exports.QPoint2dBuffer = QPoint2dBuffer = {}));
/** A list of [[QPoint2d]]s all quantized to the same range.
 * @public
 * @extensions
 */
class QPoint2dList {
    /** Parameters used to quantize the points. */
    params;
    _list = new Array();
    /** The list of quantized points. */
    get list() {
        return this._list;
    }
    /** Construct an empty list set up to use the supplied quantization parameters. */
    constructor(params) {
        this.params = params.clone();
    }
    /** Removes all points from the list. */
    clear() {
        this._list.length = 0;
    }
    /** Removes all points from the list and change the quantization parameters. */
    reset(params) {
        this.clear();
        this.params.copyFrom(params);
    }
    /** Quantizes the supplied Point2d to this list's range and appends it to the list. */
    add(pt) {
        this._list.push(QPoint2d.create(pt, this.params));
    }
    /** Adds a previously-quantized point to this list. */
    push(qpt) {
        this._list.push(qpt.clone());
    }
    /** The number of points in the list. */
    get length() {
        return this._list.length;
    }
    /** Returns the unquantized value of the point at the specified index in the list. */
    unquantize(index, out) {
        (0, core_bentley_1.assert)(index < this.length);
        if (index < this.length) {
            return this._list[index].unquantize(this.params, out);
        }
        else {
            return undefined !== out ? out : new core_geometry_1.Point2d();
        }
    }
    /** Changes the quantization parameters and requantizes all points in the list to the new range.
     * @note The loss of precision is compounded each time the points are requantized to a new range.
     */
    requantize(params) {
        for (let i = 0; i < this.length; i++) {
            const pt = this.unquantize(i);
            this._list[i].init(pt, params);
        }
        this.params.copyFrom(params);
    }
    /** Extracts the current contents of the list as a Uint16Array such that the first element of the array corresponds to the first point's `x` component,
     * the second to the first point's `y` component, and so on.
     */
    toTypedArray() {
        const array = new Uint16Array(this.length * 2);
        const pts = this._list;
        for (let i = 0; i < this.length; i++) {
            const pt = pts[i];
            array[i * 2] = pt.x;
            array[i * 2 + 1] = pt.y;
        }
        return array;
    }
    /**  Create from a Uint16Array laid out such that `array[0]` corresponds to the first point's `x` component, `array[1]` to the first point's `y` component, and so on. */
    fromTypedArray(range, array) {
        this.params.setFromRange(range);
        this._list.length = array.length / 2;
        for (let i = 0, j = 0; i < this.list.length; i++)
            this._list[i] = QPoint2d.fromScalars(array[j++], array[j++]);
    }
    /** Construct a QPoint2dList containing all points in the supplied list, quantized to the range of those points. */
    static fromPoints(points, out) {
        let qPoints;
        const qParams = QParams2d.fromRange(core_geometry_1.Range2d.createArray(points));
        if (out) {
            qPoints = out;
            qPoints.reset(qParams);
        }
        else {
            qPoints = new QPoint2dList(qParams);
        }
        for (const point of points)
            qPoints.add(point);
        return qPoints;
    }
}
exports.QPoint2dList = QPoint2dList;
/** Parameters used for [[Quantization]] of 3d points such that the `x`, `y`, and `z` components are each quantized to 16-bit unsigned integers.
 * @see [[QPoint3d]] for the quantized representation of a [Point3d]($core-geometry).
 * @see [[QPoint3dList]] for a list of [[QPoint3d]]s quantized using a [[QParams3d]].
 * @public
 * @extensions
 */
class QParams3d {
    /** The origin of the quantization range. */
    origin = new core_geometry_1.Point3d();
    /** The scale applied to coordinates to quantize them. */
    scale = new core_geometry_1.Point3d();
    constructor(ox = 0, oy = 0, oz = 0, sx = 0, sy = 0, sz = 0) {
        this.setFrom(ox, oy, oz, sx, sy, sz);
    }
    setFrom(ox, oy, oz, sx, sy, sz) {
        this.origin.x = ox;
        this.origin.y = oy;
        this.origin.z = oz;
        this.scale.x = sx;
        this.scale.y = sy;
        this.scale.z = sz;
    }
    /** Set `x`, `y`, and `z` from `src. */
    copyFrom(src) {
        this.setFrom(src.origin.x, src.origin.y, src.origin.z, src.scale.x, src.scale.y, src.scale.z);
    }
    /** Create a copy of these parameters.
     * @param out If supplied, it will be modified in-place and returned instead of allocating a new QParams3d.
     */
    clone(out) {
        const result = undefined !== out ? out : new QParams3d();
        result.copyFrom(this);
        return result;
    }
    /** Initialize from origin and scale */
    setFromOriginAndScale(origin, scale) {
        this.setFrom(origin.x, origin.y, origin.z, scale.x, scale.y, scale.z);
    }
    /** Initialize these parameters to support quantization of values within the specified range. */
    setFromRange(range, rangeScale = Quantization.rangeScale16) {
        if (!range.isNull) {
            this.setFrom(range.low.x, range.low.y, range.low.z, Quantization.computeScale(range.high.x - range.low.x, rangeScale), Quantization.computeScale(range.high.y - range.low.y, rangeScale), Quantization.computeScale(range.high.z - range.low.z, rangeScale));
        }
        else {
            this.origin.x = this.origin.y = this.origin.z = 0;
            this.scale.x = this.scale.y = this.scale.z = 0;
        }
    }
    /** Return the unquantized point for the input components.
     * @param out If supplied, it will be modified in-place and returned instead of allocating a new Point3d.
     */
    unquantize(x, y, z, out) {
        const pt = undefined !== out ? out : new core_geometry_1.Point3d();
        pt.x = Quantization.unquantize(x, this.origin.x, this.scale.x);
        pt.y = Quantization.unquantize(y, this.origin.y, this.scale.y);
        pt.z = Quantization.unquantize(z, this.origin.z, this.scale.z);
        return pt;
    }
    /** Creates parameters to support quantization of values within the specified range.
     * If `out` is supplied, it will be modified in-place and returned instead of allocating a new QParams3d.
     */
    static fromRange(range, out, rangeScale = Quantization.rangeScale16) {
        const params = undefined !== out ? out : new QParams3d();
        params.setFromRange(range, rangeScale);
        return params;
    }
    /** Creates parameters supporting quantization of values within the range [-1.0, 1.0].
     * If `out` is supplied, it will be modified in-place and returned instead of allocating a new QParams3d.
     */
    static fromOriginAndScale(origin, scale, out) {
        const params = undefined !== out ? out : new QParams3d();
        params.setFromOriginAndScale(origin, scale);
        return params;
    }
    /** Creates parameters supporting quantization of values within the range [-1.0, 1.0]. */
    static fromNormalizedRange(rangeScale = Quantization.rangeScale16) {
        return QParams3d.fromRange(core_geometry_1.Range3d.createArray([core_geometry_1.Point3d.create(-1, -1, -1), core_geometry_1.Point3d.create(1, 1, 1)]), undefined, rangeScale);
    }
    /** Creates parameters supporting quantization of values within the range [0.0, 1.0]. */
    static fromZeroToOne(rangeScale = Quantization.rangeScale16) {
        return QParams3d.fromRange(core_geometry_1.Range3d.createArray([core_geometry_1.Point3d.create(0, 0, 0), core_geometry_1.Point3d.create(1, 1, 1)]), undefined, rangeScale);
    }
    /** The diagonal of the unquantized range represented by these parameters. */
    get rangeDiagonal() {
        return core_geometry_1.Vector3d.createFrom({
            x: this.scale.x === 0 ? 0 : Quantization.rangeScale16 / this.scale.x,
            y: this.scale.y === 0 ? 0 : Quantization.rangeScale16 / this.scale.y,
            z: this.scale.z === 0 ? 0 : Quantization.rangeScale16 / this.scale.z,
        });
    }
    /** Return true if the point point is quantizable using these parameters. */
    isQuantizable(point) {
        return Quantization.isQuantizable(point.x, this.origin.x, this.scale.x) && Quantization.isQuantizable(point.y, this.origin.y, this.scale.y) && Quantization.isQuantizable(point.z, this.origin.z, this.scale.z);
    }
    /** Compute the range to which these parameters quantize. */
    computeRange(out) {
        const range = core_geometry_1.Range3d.createNull(out);
        range.extendPoint(this.origin);
        range.extendPoint(this.origin.plus(this.rangeDiagonal));
        return range;
    }
    /** @alpha */
    toJSON() {
        return {
            origin: { x: this.origin.x, y: this.origin.y, z: this.origin.z },
            scale: { x: this.scale.x, y: this.scale.y, z: this.scale.z },
        };
    }
    /** @alpha */
    static fromJSON(src, out) {
        return this.fromOriginAndScale(core_geometry_1.Point3d.fromJSON(src.origin), core_geometry_1.Point3d.fromJSON(src.scale), out);
    }
}
exports.QParams3d = QParams3d;
/** Represents a [Point3d]($core-geometry) compressed such that each component `x`, `y`, and `z` is quantized to the 16-bit integer range [0, 0xffff].
 * These are primarily used to reduce the space required for coordinates used by [RenderGraphic]($frontend)s.
 * @see [[QParams3d]] to define quantization parameters for a range of points.
 * @see [[QPoint3dList]] for a list of points all quantized to the same range.
 * @public
 * @extensions
 */
class QPoint3d {
    _x = 0;
    _y = 0;
    _z = 0;
    /** The quantized x component. */
    get x() { return this._x; }
    set x(x) {
        (0, core_bentley_1.assert)(Quantization.isQuantized(x));
        this._x = x;
    }
    /** The quantized y component. */
    get y() { return this._y; }
    set y(y) {
        (0, core_bentley_1.assert)(Quantization.isQuantized(y));
        this._y = y;
    }
    /** The quantized z component. */
    get z() { return this._z; }
    set z(z) {
        (0, core_bentley_1.assert)(Quantization.isQuantized(z));
        this._z = z;
    }
    /** Construct with all components initialized to zero. */
    constructor() { }
    /** Initialize this point by quantizing the supplied { x, y, z } using the specified params */
    init(pos, params) {
        this.x = Quantization.quantize(pos.x, params.origin.x, params.scale.x);
        this.y = Quantization.quantize(pos.y, params.origin.y, params.scale.y);
        this.z = Quantization.quantize(pos.z, params.origin.z, params.scale.z);
    }
    /** Creates a quantized point from the supplied Point3d using the specified params */
    static create(pos, params) {
        const qpt = new QPoint3d();
        qpt.init(pos, params);
        return qpt;
    }
    /** Set this points components from `src`. */
    copyFrom(src) {
        this.x = src.x;
        this.y = src.y;
        this.z = src.z;
    }
    /** Create a copy of this point.
     * @param out If supplied, it will be modified in-place instead of allocating a new QPoint3d.
     */
    clone(out) {
        const result = undefined !== out ? out : new QPoint3d();
        result.copyFrom(this);
        return result;
    }
    /**
     * Sets the x, y, and z components directly.
     * @param x Must be an integer in the range [0, 0xffff]
     * @param y Must be an integer in the range [0, 0xffff]
     * @param z Must be an integer in the range [0, 0xffff]
     */
    setFromScalars(x, y, z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    /**
     * Creates a QPoint3d directly from x, y, and z components.
     * @param x Must be an integer in the range [0, 0xffff]
     * @param y Must be an integer in the range [0, 0xffff]
     * @param z Must be an integer in the range [0, 0xffff]
     * @param out If supplied, it will be modified in-place instead of allocating a new QPoint3d.
     */
    static fromScalars(x, y, z, out) {
        const pt = undefined === out ? new QPoint3d() : out;
        pt.setFromScalars(x, y, z);
        return pt;
    }
    /** Returns a Point3d unquantized according to the supplied params.
     * If `out` is supplied, it will be modified in-place instead of allocating a new Point3d.
     */
    unquantize(params, out) {
        const pt = undefined !== out ? out : new core_geometry_1.Point3d();
        pt.x = Quantization.unquantize(this.x, params.origin.x, params.scale.x);
        pt.y = Quantization.unquantize(this.y, params.origin.y, params.scale.y);
        pt.z = Quantization.unquantize(this.z, params.origin.z, params.scale.z);
        return pt;
    }
    /** Return true if this point's components are identical to the other point's components. */
    equals(other) {
        return this.x === other.x && this.y === other.y && this.z === other.z;
    }
    /** Perform ordinal comparison to another point. The function returns:
     *  - Zero if this point is identical to `rhs`; or
     *  - A number less than zero if this point is ordered before `rhs`; or
     *  - A number greater than zero if this point is ordered after `rhs`.
     * @see [OrderedComparator]($core-bentley).
     */
    compare(rhs) {
        let diff = this.x - rhs.x;
        if (0 === diff) {
            diff = this.y - rhs.y;
            if (0 === diff) {
                diff = this.z - rhs.z;
            }
        }
        return diff;
    }
}
exports.QPoint3d = QPoint3d;
/** @public
 * @extensions
 */
var QPoint3dBuffer;
(function (QPoint3dBuffer) {
    const scratchQPoint3d = new QPoint3d();
    /** Extracts the point at the specified index from a buffer.
     * @param points The buffer in which each consecutive pair of integers is a 3d quantized point.
     * @param pointIndex The index of the point to extract, ranging from zero to one less than the number of points in the buffer.
     * @param result If supplied, a preallocated [[QPoint3d]] to initialize with the result and return.
     * @returns The point at `pointIndex`.
     * @throws Error if `pointIndex` is out of bounds.
     */
    function getQPoint(points, pointIndex, result) {
        const index = pointIndex * 3;
        const x = points[index + 0];
        const y = points[index + 1];
        const z = points[index + 2];
        if (undefined === x || undefined === y || undefined === z)
            throw new Error("Index out of range");
        result = result ?? new QPoint3d();
        result.setFromScalars(x, y, z);
        return result;
    }
    QPoint3dBuffer.getQPoint = getQPoint;
    /** Extracts and unquantizes the point at the specified index from a buffer.
     * @param buffer The array of points and the quantization parameters.
     * @param buffer The index of the point to extract, ranging from zero to one less than the number of points in the buffer.
     * @param result If supplied, a preallocated [Point3d]($core-geometry) to initialize with the result and return.
     * @returns The point at `pointIndex`.
     * @throws Error if `pointIndex` is out of bounds.
     */
    function unquantizePoint(buffer, pointIndex, result) {
        const qpt = getQPoint(buffer.points, pointIndex, scratchQPoint3d);
        return qpt.unquantize(buffer.params, result);
    }
    QPoint3dBuffer.unquantizePoint = unquantizePoint;
})(QPoint3dBuffer || (exports.QPoint3dBuffer = QPoint3dBuffer = {}));
/** A list of [[QPoint3d]]s all quantized to the same range.
 * @public
 * @extensions
 */
class QPoint3dList {
    /** Parameters used to quantize the points. */
    params;
    _list = [];
    /** The list of quantized points. */
    get list() {
        return this._list;
    }
    /** Construct an empty list set up to quantize to the supplied range.
     * @param params The quantization parameters. If omitted, a null range will be used.
     */
    constructor(params) {
        this.params = params ? params.clone() : QParams3d.fromRange(core_geometry_1.Range3d.createNull());
    }
    /** Construct a QPoint3dList containing all points in the supplied list, quantized to the range of those points.
     * @param points The points to quantize and add to the list.
     * @param out If supplied, it will be cleared, its parameters recomputed, and the points will be added to it; otherwise, a new QPoint3dList will be created and returned.
     */
    static fromPoints(points, out) {
        let qPoints;
        const qParams = QParams3d.fromRange(core_geometry_1.Range3d.createArray(points));
        if (out) {
            qPoints = out;
            qPoints.reset(qParams);
        }
        else {
            qPoints = new QPoint3dList(qParams);
        }
        for (const point of points)
            qPoints.add(point);
        return qPoints;
    }
    /** Removes all points from the list. */
    clear() {
        this._list.length = 0;
    }
    /** Clears out the contents of the list and changes the quantization parameters. */
    reset(params) {
        this.clear();
        this.params.copyFrom(params);
    }
    /** Quantizes the supplied Point3d to this list's range and appends it to the list. */
    add(pt) {
        this._list.push(QPoint3d.create(pt, this.params));
    }
    /** Adds a previously-quantized point to this list. */
    push(qpt) {
        this._list.push(qpt.clone());
    }
    /** The number of points in the list. */
    get length() {
        return this._list.length;
    }
    /** Returns the unquantized value of the point at the specified index in the list. */
    unquantize(index, out) {
        (0, core_bentley_1.assert)(index < this.length);
        if (index < this.length) {
            return this._list[index].unquantize(this.params, out);
        }
        else {
            return undefined !== out ? out : new core_geometry_1.Point3d();
        }
    }
    /** Changes the quantization parameters and requantizes all points in the list to the new range.
     * @note The loss of precision is compounded each time the points are requantized to a new range.
     */
    requantize(params) {
        for (let i = 0; i < this.length; i++) {
            const pt = this.unquantize(i);
            this._list[i].init(pt, params);
        }
        this.params.copyFrom(params);
    }
    /** Extracts the current contents of the list as a Uint16Array such that the first 3 elements contain the first point's x, y, and z components,
     * the second three elements contain the second point's components, and so on.
     */
    toTypedArray() {
        const array = new Uint16Array(this.length * 3);
        const pts = this._list;
        for (let i = 0; i < this.length; i++) {
            const pt = pts[i];
            array[i * 3 + 0] = pt.x;
            array[i * 3 + 1] = pt.y;
            array[i * 3 + 2] = pt.z;
        }
        return array;
    }
    /** Reinitialize from a Uint16Array in which the first three elements specify the x, y, and z components of the first point, the second three elements specify the components
     * of the second point, and so on.
     */
    fromTypedArray(range, array) {
        this.params.setFromRange(range);
        this._list.length = array.length / 3;
        for (let i = 0, j = 0; i < this.list.length; i++)
            this._list[i] = QPoint3d.fromScalars(array[j++], array[j++], array[j++]);
    }
    /** Construct a list containing all points in the supplied list, quantized using the supplied parameters. */
    static createFrom(points, params) {
        const list = new QPoint3dList(params);
        for (const point of points)
            list.add(point);
        return list;
    }
    /** An iterator over the points in the list. */
    [Symbol.iterator]() {
        return this.list[Symbol.iterator]();
    }
}
exports.QPoint3dList = QPoint3dList;
/** Constructs a [[QPoint2dBuffer]] using a [Uint16ArrayBuilder]($bentley).
 * @public
 * @extensions
 */
class QPoint2dBufferBuilder {
    _scratchQPoint2d = new QPoint2d();
    /** The parameters used to quantize the points in the [[buffer]]. */
    params;
    /** The buffer that holds the points. */
    buffer;
    /** Construct a new buffer with a [[length]] of zero. */
    constructor(options) {
        this.params = QParams2d.fromRange(options.range);
        const initialCapacity = options.initialCapacity ?? 0;
        this.buffer = new core_bentley_1.Uint16ArrayBuilder({
            growthFactor: options.growthFactor,
            initialCapacity: 2 * initialCapacity,
        });
    }
    /** Append a point with the specified quantized coordinates. */
    pushXY(x, y) {
        this.buffer.push(x);
        this.buffer.push(y);
    }
    /** Append a point with the specified quantized coordinates. */
    push(pt) {
        this.pushXY(pt.x, pt.y);
    }
    /** The number of points currently in the [[buffer]]. */
    get length() {
        const len = this.buffer.length;
        (0, core_bentley_1.assert)(len % 2 === 0);
        return len / 2;
    }
    /** Returns the quantized point at the specified index in [[buffer]].
     * @param pointIndex The index of the point of interest, ranging from zero to one minus the number of points currently in the [[buffer]].
     * @param result If supplied, a [[QPoint2d]] to initialize with the result and return.
     * @returns The quantized point at the specified index in [[buffer]].
     * @throws Error if `pointIndex` is out of bounds.
     */
    get(pointIndex, result) {
        return QPoint2dBuffer.getQPoint(this.buffer.toTypedArray(), pointIndex, result);
    }
    /** Returns the unquantized point at the specified index in [[buffer]].
     * @param pointIndex The index of the point of interest, ranging from zero to one minus the number of points currently in the [[buffer]].
     * @param result If supplied, a [Point2d]($core-geometry) to initialize with the result and return.
     * @returns The unquantized point at the specified index in [[buffer]].
     * @throws Error if `pointIndex` is out of bounds.
     */
    unquantize(pointIndex, result) {
        return this.get(pointIndex, this._scratchQPoint2d).unquantize(this.params, result);
    }
    /** Obtain a [[QPoint2dBuffer]] containing all of the points that have been appended by this builder. */
    finish() {
        return {
            params: this.params,
            points: this.buffer.toTypedArray(),
        };
    }
}
exports.QPoint2dBufferBuilder = QPoint2dBufferBuilder;
/** Constructs a [[QPoint3dBuffer]] using a [Uint16ArrayBuilder]($bentley).
 * @public
 * @extensions
 */
class QPoint3dBufferBuilder {
    _scratchQPoint3d = new QPoint3d();
    /** The parameters used to quantize the points in the [[buffer]]. */
    params;
    /** The buffer that holds the points. */
    buffer;
    /** Construct a new buffer with a [[length]] of zero. */
    constructor(options) {
        this.params = QParams3d.fromRange(options.range);
        const initialCapacity = options.initialCapacity ?? 0;
        this.buffer = new core_bentley_1.Uint16ArrayBuilder({
            growthFactor: options.growthFactor,
            initialCapacity: 3 * initialCapacity,
        });
    }
    /** Append a point with the specified quantized coordinates. */
    pushXYZ(x, y, z) {
        this.buffer.push(x);
        this.buffer.push(y);
        this.buffer.push(z);
    }
    /** Append a point with the specified quantized coordinates. */
    push(pt) {
        this.pushXYZ(pt.x, pt.y, pt.z);
    }
    /** The number of points currently in the [[buffer]]. */
    get length() {
        const len = this.buffer.length;
        (0, core_bentley_1.assert)(len % 3 === 0);
        return len / 3;
    }
    /** Returns the quantized point at the specified index in [[buffer]].
     * @param pointIndex The index of the point of interest, ranging from zero to one minus the number of points currently in the [[buffer]].
     * @param result If supplied, a [[QPoint3d]] to initialize with the result and return.
     * @returns The quantized point at the specified index in [[buffer]].
     * @throws Error if `pointIndex` is out of bounds.
     */
    get(pointIndex, result) {
        return QPoint3dBuffer.getQPoint(this.buffer.toTypedArray(), pointIndex, result);
    }
    /** Returns the unquantized point at the specified index in [[buffer]].
     * @param pointIndex The index of the point of interest, ranging from zero to one minus the number of points currently in the [[buffer]].
     * @param result If supplied, a [Point3d]($core-geometry) to initialize with the result and return.
     * @returns The unquantized point at the specified index in [[buffer]].
     * @throws Error if `pointIndex` is out of bounds.
     */
    unquantize(pointIndex, result) {
        return this.get(pointIndex, this._scratchQPoint3d).unquantize(this.params, result);
    }
    /** Obtain a [[QPoint3dBuffer]] containing all of the points that have been appended by this builder. */
    finish() {
        return {
            params: this.params,
            points: this.buffer.toTypedArray(),
        };
    }
}
exports.QPoint3dBufferBuilder = QPoint3dBufferBuilder;
//# sourceMappingURL=QPoint.js.map