/*---------------------------------------------------------------------------------------------
* 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
 */
import { IModelStatus } from "@itwin/core-bentley";
import { Angle, Constant, Matrix3d, Point2d, Point3d, Range2d, Range3d, Transform, Vector3d, YawPitchRollAngles, } from "@itwin/core-geometry";
import { Frustum } from "../Frustum";
import { IModelError } from "../IModelError";
/** The placement of a GeometricElement3d. This includes the origin, orientation, and size (bounding box) of the element.
 * All geometry of a GeometricElement are relative to its placement.
 * @public
 */
export class Placement3d {
    constructor(origin, angles, bbox) {
        this.origin = origin;
        this.angles = angles;
        this.bbox = bbox;
    }
    /** Get the rotation from local coordinates of this placement to world coordinates. */
    get rotation() { return this.angles.toMatrix3d(); }
    /** Get the transform from local coordinates of this placement to world coordinates. */
    get transform() { return Transform.createOriginAndMatrix(this.origin, this.rotation); }
    /** determine if this is 3d placement */
    get is3d() { return true; }
    /** Create a new Placement3d from a Placement3dProps. */
    static fromJSON(json) {
        const props = json ? json : {};
        return new Placement3d(Point3d.fromJSON(props.origin), YawPitchRollAngles.fromJSON(props.angles), Range3d.fromJSON(props.bbox));
    }
    /** Get the 8 corners, in world coordinates, of this placement. */
    getWorldCorners(out) {
        const frust = Frustum.fromRange(this.bbox, out);
        frust.multiply(this.transform);
        return frust;
    }
    /** Set the contents of this Placement3d from another Placement3d */
    setFrom(other) {
        this.origin.setFrom(other.origin);
        this.angles.setFrom(other.angles);
        this.bbox.setFrom(other.bbox);
    }
    /** Determine whether this Placement3d is valid. */
    get isValid() { return !this.bbox.isNull && Math.max(this.origin.maxAbs(), this.bbox.maxAbs()) < Constant.circumferenceOfEarth; }
    /** Calculate the axis-aligned bounding box for this placement. */
    calculateRange() {
        const range = new Range3d();
        if (!this.isValid)
            return range;
        this.transform.multiplyRange(this.bbox, range);
        // low and high are not allowed to be equal
        range.ensureMinLengths();
        return range;
    }
    /** Multiply the Transform of this Placement3d by the specified *other* Transform.
     * * Specifically `this.angles` is set to the rotation specified by `other.matrix * this.transform.matrix`
     * and `this.origin` is set to the origin of `other * this.transform`.
     * * Since the placement transform is local-to-world, this means `other` is a world-to-world transform.
     * @throws [[IModelError]] if the Transform is invalid for a GeometricElement3d.
     */
    multiplyTransform(other) {
        const transform = other.multiplyTransformTransform(this.transform);
        const angles = YawPitchRollAngles.createFromMatrix3d(transform.matrix);
        if (undefined === angles)
            throw new IModelError(IModelStatus.BadRequest, "Invalid Transform");
        this.angles = angles;
        this.origin.setFrom(transform.origin);
    }
}
/** The placement of a GeometricElement2d. This includes the origin, rotation, and size (bounding box) of the element.
 * @public
 */
export class Placement2d {
    constructor(origin, angle, bbox) {
        this.origin = origin;
        this.angle = angle;
        this.bbox = bbox;
    }
    /** Get the rotation from local coordinates of this placement to world coordinates. */
    get rotation() { return Matrix3d.createRotationAroundVector(Vector3d.unitZ(), this.angle); }
    /** Get the transform from local coordinates of this placement to world coordinates. */
    get transform() { return Transform.createOriginAndMatrix(Point3d.createFrom(this.origin), this.rotation); }
    /** Create a new Placement2d from a Placement2dProps. */
    static fromJSON(json) {
        const props = json ? json : {};
        return new Placement2d(Point2d.fromJSON(props.origin), Angle.fromJSON(props.angle), Range2d.fromJSON(props.bbox));
    }
    /** determine if this is 3d placement */
    get is3d() { return false; }
    /** Get the 8 corners, in world coordinates, of this placement. */
    getWorldCorners(out) {
        const frust = Frustum.fromRange(this.bbox, out);
        frust.multiply(this.transform);
        return frust;
    }
    /** Determine whether this Placement2d is valid. */
    get isValid() { return !this.bbox.isNull && Math.max(this.origin.maxAbs(), this.bbox.maxAbs()) < Constant.circumferenceOfEarth; }
    /** Set the contents of this Placement2d from another Placement2d */
    setFrom(other) {
        this.origin.setFrom(other.origin);
        this.angle.setFrom(other.angle);
        this.bbox.setFrom(other.bbox);
    }
    /** Calculate the axis-aligned bounding box for this placement. */
    calculateRange() {
        const range = new Range3d();
        if (!this.isValid)
            return range;
        this.transform.multiplyRange(Range3d.createRange2d(this.bbox, 0), range);
        // low and high are not allowed to be equal
        range.ensureMinLengths();
        range.low.z = -1.0; // is the 2dFrustumDepth, which === 1 meter
        range.high.z = 1.0;
        return range;
    }
    /** Multiply the Transform of this Placement2d by the specified *other* Transform.
     * @throws [[IModelError]] if the Transform is invalid for a GeometricElement2d.
     */
    multiplyTransform(other) {
        const transform = other.multiplyTransformTransform(this.transform);
        const angles = YawPitchRollAngles.createFromMatrix3d(transform.matrix);
        if ((undefined === angles) || !angles.pitch.isAlmostZero || !angles.roll.isAlmostZero)
            throw new IModelError(IModelStatus.BadRequest, "Invalid Transform");
        this.angle = angles.yaw;
        this.origin.setFrom(transform.origin);
    }
}
//# sourceMappingURL=Placement.js.map