/*---------------------------------------------------------------------------------------------
* 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 { RecurseToCurvesGeometryHandler } from "../../geometry3d/GeometryHandler";
import { Point3d, Vector3d } from "../../geometry3d/Point3dVector3d";
import { LineString3d } from "../LineString3d";
import { StrokeOptions } from "../StrokeOptions";
/**
 * Context for computing geometry range around an axis.
 * * The publicly called method is `computeZRRange (ray, geometry)
 */
export class CylindricalRangeQuery extends RecurseToCurvesGeometryHandler {
    // private geometry0: GeometryQuery;  <-- Never used
    _perpVector;
    _maxDistance;
    _localToWorld;
    /** capture ray and initialize evolving ranges. */
    constructor(ray) {
        super();
        this._perpVector = Vector3d.createZero();
        this._maxDistance = 0.0;
        this._localToWorld = ray.toRigidZFrame();
    }
    _localPoint = Point3d.create();
    _worldPoint = Point3d.create();
    announcePoint(xyz) {
        this._localToWorld.multiplyInversePoint3d(xyz, this._localPoint);
        const distance = this._localPoint.magnitudeXY();
        if (distance >= this._maxDistance) {
            this._maxDistance = distance;
            this._perpVector.setFromPoint3d(this._localPoint);
            this._perpVector.z = 0.0;
            this._localToWorld.matrix.multiplyXY(this._localPoint.x, this._localPoint.y, this._perpVector);
        }
    }
    handleLineSegment3d(segment0) {
        this.announcePoint(segment0.startPoint(this._worldPoint));
        this.announcePoint(segment0.endPoint(this._worldPoint));
    }
    handleLineString3d(ls0) {
        for (let i = 0; i < ls0.numPoints(); i++) {
            ls0.pointAt(i, this._worldPoint);
            this.announcePoint(this._worldPoint);
        }
    }
    handleArc3d(arc0) {
        // humbug .. just stroke it ..
        // exact solution is:
        //   project the arc to the z=0 plane of the local system.
        //   find max distance to origin.
        const numStroke = StrokeOptions.applyAngleTol(undefined, 3, arc0.sweep.sweepRadians, 0.1);
        const df = 1.0 / numStroke;
        for (let i = 0; i <= numStroke; i++) {
            arc0.fractionToPoint(i * df, this._worldPoint);
            this.announcePoint(this._worldPoint);
        }
        return undefined;
    }
    /**
     * Compute the largest vector perpendicular to a ray and ending on the geometry.
     * @param geometry0 geometry to search
     * @returns vector from ray to geometry.
     */
    static computeMaxVectorFromRay(ray, geometry) {
        const accumulator = new CylindricalRangeQuery(ray);
        geometry.dispatchToGeometryHandler(accumulator);
        return accumulator._perpVector.clone();
    }
    /**
     * Recurse through `geometry.children` to find linestrings.
     * For each linestring, compute and store the normal of the rotational surface resulting from sweeping the
     * geometry around `axis` through a positive angle, using:
     *  * the curve tangent stored in the linestring
     *  * the axis of rotation
     *  * a default V vector to be used when the linestring point is close to the axis
     * @param geometry profile curve (e.g., linestring, parity region). The orientation of the curve should be such that
     * the computed normal lies in the same half-space as the rotational sweep direction.
     * @param axis rotational axis
     * @param defaultVectorFromAxis default vector perpendicular to `axis` (e.g., sweepVector)
     */
    static buildRotationalNormalsInLineStrings(geometry, axis, defaultVectorFromAxis) {
        if (geometry instanceof LineString3d) {
            const points = geometry.packedPoints;
            const derivatives = geometry.packedDerivatives;
            const normals = geometry.ensureEmptySurfaceNormals();
            if (derivatives && normals) {
                const vectorU = Vector3d.create();
                const vectorV = Vector3d.create();
                const xyz = Point3d.create();
                const n = points.length;
                for (let i = 0; i < n; i++) {
                    points.getPoint3dAtUncheckedPointIndex(i, xyz);
                    axis.perpendicularPartOfVectorToTarget(xyz, vectorU);
                    // compute the positive sweep direction. ASSUME: the rotational sweep angle is positive!
                    if (vectorU.isAlmostZero)
                        axis.direction.crossProduct(defaultVectorFromAxis, vectorV);
                    else
                        axis.direction.crossProduct(vectorU, vectorV);
                    // ASSUME: orientation-based profile normal is in the same half-space as vectorV
                    geometry.packedDerivatives.getVector3dAtCheckedVectorIndex(i, vectorU); // reuse vectorU
                    vectorU.crossProduct(vectorV, vectorV); // reuse vectorV
                    vectorV.normalizeInPlace();
                    normals.push(vectorV);
                }
            }
        }
        else if (geometry.children) {
            const children = geometry.children;
            for (const child of children) {
                this.buildRotationalNormalsInLineStrings(child, axis, defaultVectorFromAxis);
            }
        }
    }
}
//# sourceMappingURL=CylindricalRange.js.map