@itwin/core-geometry 4.9.0-dev.2 → 4.9.0-dev.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -1
- package/lib/cjs/Geometry.d.ts +57 -46
- package/lib/cjs/Geometry.d.ts.map +1 -1
- package/lib/cjs/Geometry.js +73 -53
- package/lib/cjs/Geometry.js.map +1 -1
- package/lib/cjs/curve/Arc3d.d.ts +128 -34
- package/lib/cjs/curve/Arc3d.d.ts.map +1 -1
- package/lib/cjs/curve/Arc3d.js +174 -20
- package/lib/cjs/curve/Arc3d.js.map +1 -1
- package/lib/cjs/curve/CurveChainWithDistanceIndex.d.ts +11 -6
- package/lib/cjs/curve/CurveChainWithDistanceIndex.d.ts.map +1 -1
- package/lib/cjs/curve/CurveChainWithDistanceIndex.js +12 -10
- package/lib/cjs/curve/CurveChainWithDistanceIndex.js.map +1 -1
- package/lib/cjs/curve/CurveCollection.d.ts +2 -1
- package/lib/cjs/curve/CurveCollection.d.ts.map +1 -1
- package/lib/cjs/curve/CurveCollection.js +2 -1
- package/lib/cjs/curve/CurveCollection.js.map +1 -1
- package/lib/cjs/curve/CurveCurve.d.ts +11 -9
- package/lib/cjs/curve/CurveCurve.d.ts.map +1 -1
- package/lib/cjs/curve/CurveCurve.js +11 -9
- package/lib/cjs/curve/CurveCurve.js.map +1 -1
- package/lib/cjs/curve/CurveFactory.d.ts +4 -3
- package/lib/cjs/curve/CurveFactory.d.ts.map +1 -1
- package/lib/cjs/curve/CurveFactory.js +4 -3
- package/lib/cjs/curve/CurveFactory.js.map +1 -1
- package/lib/cjs/curve/CurveLocationDetail.d.ts +19 -1
- package/lib/cjs/curve/CurveLocationDetail.d.ts.map +1 -1
- package/lib/cjs/curve/CurveLocationDetail.js +39 -0
- package/lib/cjs/curve/CurveLocationDetail.js.map +1 -1
- package/lib/cjs/curve/CurveOps.d.ts +4 -4
- package/lib/cjs/curve/CurveOps.d.ts.map +1 -1
- package/lib/cjs/curve/CurveOps.js +6 -6
- package/lib/cjs/curve/CurveOps.js.map +1 -1
- package/lib/cjs/curve/CurvePrimitive.d.ts +1 -1
- package/lib/cjs/curve/CurvePrimitive.js.map +1 -1
- package/lib/cjs/curve/LineString3d.d.ts +7 -5
- package/lib/cjs/curve/LineString3d.d.ts.map +1 -1
- package/lib/cjs/curve/LineString3d.js +8 -6
- package/lib/cjs/curve/LineString3d.js.map +1 -1
- package/lib/cjs/curve/Loop.d.ts.map +1 -1
- package/lib/cjs/curve/Loop.js +6 -6
- package/lib/cjs/curve/Loop.js.map +1 -1
- package/lib/cjs/curve/OffsetOptions.d.ts +1 -1
- package/lib/cjs/curve/OffsetOptions.js +1 -1
- package/lib/cjs/curve/OffsetOptions.js.map +1 -1
- package/lib/cjs/curve/Path.d.ts.map +1 -1
- package/lib/cjs/curve/Path.js +5 -6
- package/lib/cjs/curve/Path.js.map +1 -1
- package/lib/cjs/curve/Query/ConsolidateAdjacentPrimitivesContext.d.ts.map +1 -1
- package/lib/cjs/curve/Query/ConsolidateAdjacentPrimitivesContext.js +3 -4
- package/lib/cjs/curve/Query/ConsolidateAdjacentPrimitivesContext.js.map +1 -1
- package/lib/cjs/curve/Query/CylindricalRange.js.map +1 -1
- package/lib/cjs/curve/RegionOps.d.ts +4 -3
- package/lib/cjs/curve/RegionOps.d.ts.map +1 -1
- package/lib/cjs/curve/RegionOps.js +4 -3
- package/lib/cjs/curve/RegionOps.js.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.d.ts +23 -7
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.d.ts.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.js +43 -35
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.js.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.d.ts +22 -8
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.d.ts.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.js +127 -52
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.js.map +1 -1
- package/lib/cjs/curve/internalContexts/EllipticalArcApproximationContext.d.ts +211 -0
- package/lib/cjs/curve/internalContexts/EllipticalArcApproximationContext.d.ts.map +1 -0
- package/lib/cjs/curve/internalContexts/EllipticalArcApproximationContext.js +1000 -0
- package/lib/cjs/curve/internalContexts/EllipticalArcApproximationContext.js.map +1 -0
- package/lib/cjs/curve/internalContexts/MultiChainCollector.d.ts +11 -8
- package/lib/cjs/curve/internalContexts/MultiChainCollector.d.ts.map +1 -1
- package/lib/cjs/curve/internalContexts/MultiChainCollector.js +7 -4
- package/lib/cjs/curve/internalContexts/MultiChainCollector.js.map +1 -1
- package/lib/cjs/geometry3d/Angle.d.ts +18 -5
- package/lib/cjs/geometry3d/Angle.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Angle.js +23 -7
- package/lib/cjs/geometry3d/Angle.js.map +1 -1
- package/lib/cjs/geometry3d/AngleSweep.d.ts +14 -1
- package/lib/cjs/geometry3d/AngleSweep.d.ts.map +1 -1
- package/lib/cjs/geometry3d/AngleSweep.js +47 -12
- package/lib/cjs/geometry3d/AngleSweep.js.map +1 -1
- package/lib/cjs/geometry3d/FrameBuilder.d.ts +2 -1
- package/lib/cjs/geometry3d/FrameBuilder.d.ts.map +1 -1
- package/lib/cjs/geometry3d/FrameBuilder.js +12 -10
- package/lib/cjs/geometry3d/FrameBuilder.js.map +1 -1
- package/lib/cjs/geometry3d/GeometryHandler.d.ts.map +1 -1
- package/lib/cjs/geometry3d/GeometryHandler.js +1 -7
- package/lib/cjs/geometry3d/GeometryHandler.js.map +1 -1
- package/lib/cjs/geometry3d/Matrix3d.d.ts +6 -4
- package/lib/cjs/geometry3d/Matrix3d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Matrix3d.js +6 -4
- package/lib/cjs/geometry3d/Matrix3d.js.map +1 -1
- package/lib/cjs/geometry3d/Point3dVector3d.d.ts +2 -3
- package/lib/cjs/geometry3d/Point3dVector3d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Point3dVector3d.js +2 -3
- package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
- package/lib/cjs/geometry3d/PointHelpers.d.ts +6 -5
- package/lib/cjs/geometry3d/PointHelpers.d.ts.map +1 -1
- package/lib/cjs/geometry3d/PointHelpers.js +11 -10
- package/lib/cjs/geometry3d/PointHelpers.js.map +1 -1
- package/lib/cjs/geometry3d/PolygonOps.d.ts +4 -4
- package/lib/cjs/geometry3d/PolygonOps.d.ts.map +1 -1
- package/lib/cjs/geometry3d/PolygonOps.js +7 -11
- package/lib/cjs/geometry3d/PolygonOps.js.map +1 -1
- package/lib/cjs/geometry3d/PolylineCompressionByEdgeOffset.d.ts +1 -1
- package/lib/cjs/geometry3d/PolylineCompressionByEdgeOffset.d.ts.map +1 -1
- package/lib/cjs/geometry3d/PolylineCompressionByEdgeOffset.js +3 -3
- package/lib/cjs/geometry3d/PolylineCompressionByEdgeOffset.js.map +1 -1
- package/lib/cjs/geometry3d/Range.d.ts +6 -1
- package/lib/cjs/geometry3d/Range.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Range.js +9 -3
- package/lib/cjs/geometry3d/Range.js.map +1 -1
- package/lib/cjs/geometry3d/Ray3d.d.ts +1 -1
- package/lib/cjs/geometry3d/Ray3d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Ray3d.js.map +1 -1
- package/lib/cjs/geometry3d/Transform.d.ts +1 -1
- package/lib/cjs/geometry3d/Transform.js +1 -1
- package/lib/cjs/geometry3d/Transform.js.map +1 -1
- package/lib/cjs/numerics/Newton.d.ts +3 -3
- package/lib/cjs/numerics/Newton.d.ts.map +1 -1
- package/lib/cjs/numerics/Newton.js +14 -16
- package/lib/cjs/numerics/Newton.js.map +1 -1
- package/lib/cjs/numerics/Polynomials.d.ts +2 -2
- package/lib/cjs/numerics/Polynomials.js +2 -2
- package/lib/cjs/numerics/Polynomials.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceBuilder.d.ts +7 -4
- package/lib/cjs/polyface/PolyfaceBuilder.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceBuilder.js +11 -6
- package/lib/cjs/polyface/PolyfaceBuilder.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceClip.d.ts +13 -10
- package/lib/cjs/polyface/PolyfaceClip.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceClip.js +17 -14
- package/lib/cjs/polyface/PolyfaceClip.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceQuery.d.ts +11 -14
- package/lib/cjs/polyface/PolyfaceQuery.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceQuery.js +59 -52
- package/lib/cjs/polyface/PolyfaceQuery.js.map +1 -1
- package/lib/cjs/polyface/multiclip/BuildAverageNormalsContext.d.ts.map +1 -1
- package/lib/cjs/polyface/multiclip/BuildAverageNormalsContext.js +13 -6
- package/lib/cjs/polyface/multiclip/BuildAverageNormalsContext.js.map +1 -1
- package/lib/cjs/serialization/BGFBReader.js.map +1 -1
- package/lib/cjs/serialization/BGFBWriter.js +2 -2
- package/lib/cjs/serialization/BGFBWriter.js.map +1 -1
- package/lib/cjs/serialization/GeometrySamples.js.map +1 -1
- package/lib/cjs/solid/SweepContour.d.ts.map +1 -1
- package/lib/cjs/solid/SweepContour.js +0 -4
- package/lib/cjs/solid/SweepContour.js.map +1 -1
- package/lib/cjs/topology/Graph.d.ts +1 -1
- package/lib/cjs/topology/Graph.js +2 -2
- package/lib/cjs/topology/Graph.js.map +1 -1
- package/lib/cjs/topology/HalfEdgeNodeXYZUV.d.ts +1 -1
- package/lib/cjs/topology/HalfEdgeNodeXYZUV.js +1 -1
- package/lib/cjs/topology/HalfEdgeNodeXYZUV.js.map +1 -1
- package/lib/cjs/topology/HalfEdgePointInGraphSearch.d.ts +57 -15
- package/lib/cjs/topology/HalfEdgePointInGraphSearch.d.ts.map +1 -1
- package/lib/cjs/topology/HalfEdgePointInGraphSearch.js +168 -127
- package/lib/cjs/topology/HalfEdgePointInGraphSearch.js.map +1 -1
- package/lib/cjs/topology/HalfEdgePositionDetail.d.ts +35 -35
- package/lib/cjs/topology/HalfEdgePositionDetail.d.ts.map +1 -1
- package/lib/cjs/topology/HalfEdgePositionDetail.js +63 -41
- package/lib/cjs/topology/HalfEdgePositionDetail.js.map +1 -1
- package/lib/cjs/topology/InsertAndRetriangulateContext.d.ts +64 -12
- package/lib/cjs/topology/InsertAndRetriangulateContext.d.ts.map +1 -1
- package/lib/cjs/topology/InsertAndRetriangulateContext.js +174 -75
- package/lib/cjs/topology/InsertAndRetriangulateContext.js.map +1 -1
- package/lib/cjs/topology/Merging.d.ts +2 -2
- package/lib/cjs/topology/Merging.js +2 -2
- package/lib/cjs/topology/Merging.js.map +1 -1
- package/lib/cjs/topology/Triangulation.d.ts +16 -10
- package/lib/cjs/topology/Triangulation.d.ts.map +1 -1
- package/lib/cjs/topology/Triangulation.js +23 -30
- package/lib/cjs/topology/Triangulation.js.map +1 -1
- package/lib/esm/Geometry.d.ts +57 -46
- package/lib/esm/Geometry.d.ts.map +1 -1
- package/lib/esm/Geometry.js +73 -53
- package/lib/esm/Geometry.js.map +1 -1
- package/lib/esm/curve/Arc3d.d.ts +128 -34
- package/lib/esm/curve/Arc3d.d.ts.map +1 -1
- package/lib/esm/curve/Arc3d.js +172 -19
- package/lib/esm/curve/Arc3d.js.map +1 -1
- package/lib/esm/curve/CurveChainWithDistanceIndex.d.ts +11 -6
- package/lib/esm/curve/CurveChainWithDistanceIndex.d.ts.map +1 -1
- package/lib/esm/curve/CurveChainWithDistanceIndex.js +12 -10
- package/lib/esm/curve/CurveChainWithDistanceIndex.js.map +1 -1
- package/lib/esm/curve/CurveCollection.d.ts +2 -1
- package/lib/esm/curve/CurveCollection.d.ts.map +1 -1
- package/lib/esm/curve/CurveCollection.js +2 -1
- package/lib/esm/curve/CurveCollection.js.map +1 -1
- package/lib/esm/curve/CurveCurve.d.ts +11 -9
- package/lib/esm/curve/CurveCurve.d.ts.map +1 -1
- package/lib/esm/curve/CurveCurve.js +11 -9
- package/lib/esm/curve/CurveCurve.js.map +1 -1
- package/lib/esm/curve/CurveFactory.d.ts +4 -3
- package/lib/esm/curve/CurveFactory.d.ts.map +1 -1
- package/lib/esm/curve/CurveFactory.js +4 -3
- package/lib/esm/curve/CurveFactory.js.map +1 -1
- package/lib/esm/curve/CurveLocationDetail.d.ts +19 -1
- package/lib/esm/curve/CurveLocationDetail.d.ts.map +1 -1
- package/lib/esm/curve/CurveLocationDetail.js +39 -0
- package/lib/esm/curve/CurveLocationDetail.js.map +1 -1
- package/lib/esm/curve/CurveOps.d.ts +4 -4
- package/lib/esm/curve/CurveOps.d.ts.map +1 -1
- package/lib/esm/curve/CurveOps.js +6 -6
- package/lib/esm/curve/CurveOps.js.map +1 -1
- package/lib/esm/curve/CurvePrimitive.d.ts +1 -1
- package/lib/esm/curve/CurvePrimitive.js.map +1 -1
- package/lib/esm/curve/LineString3d.d.ts +7 -5
- package/lib/esm/curve/LineString3d.d.ts.map +1 -1
- package/lib/esm/curve/LineString3d.js +8 -6
- package/lib/esm/curve/LineString3d.js.map +1 -1
- package/lib/esm/curve/Loop.d.ts.map +1 -1
- package/lib/esm/curve/Loop.js +6 -6
- package/lib/esm/curve/Loop.js.map +1 -1
- package/lib/esm/curve/OffsetOptions.d.ts +1 -1
- package/lib/esm/curve/OffsetOptions.js +1 -1
- package/lib/esm/curve/OffsetOptions.js.map +1 -1
- package/lib/esm/curve/Path.d.ts.map +1 -1
- package/lib/esm/curve/Path.js +5 -6
- package/lib/esm/curve/Path.js.map +1 -1
- package/lib/esm/curve/Query/ConsolidateAdjacentPrimitivesContext.d.ts.map +1 -1
- package/lib/esm/curve/Query/ConsolidateAdjacentPrimitivesContext.js +3 -4
- package/lib/esm/curve/Query/ConsolidateAdjacentPrimitivesContext.js.map +1 -1
- package/lib/esm/curve/Query/CylindricalRange.js.map +1 -1
- package/lib/esm/curve/RegionOps.d.ts +4 -3
- package/lib/esm/curve/RegionOps.d.ts.map +1 -1
- package/lib/esm/curve/RegionOps.js +4 -3
- package/lib/esm/curve/RegionOps.js.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.d.ts +23 -7
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.d.ts.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.js +43 -35
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.js.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.d.ts +22 -8
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.d.ts.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.js +127 -52
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.js.map +1 -1
- package/lib/esm/curve/internalContexts/EllipticalArcApproximationContext.d.ts +211 -0
- package/lib/esm/curve/internalContexts/EllipticalArcApproximationContext.d.ts.map +1 -0
- package/lib/esm/curve/internalContexts/EllipticalArcApproximationContext.js +995 -0
- package/lib/esm/curve/internalContexts/EllipticalArcApproximationContext.js.map +1 -0
- package/lib/esm/curve/internalContexts/MultiChainCollector.d.ts +11 -8
- package/lib/esm/curve/internalContexts/MultiChainCollector.d.ts.map +1 -1
- package/lib/esm/curve/internalContexts/MultiChainCollector.js +7 -4
- package/lib/esm/curve/internalContexts/MultiChainCollector.js.map +1 -1
- package/lib/esm/geometry3d/Angle.d.ts +18 -5
- package/lib/esm/geometry3d/Angle.d.ts.map +1 -1
- package/lib/esm/geometry3d/Angle.js +23 -7
- package/lib/esm/geometry3d/Angle.js.map +1 -1
- package/lib/esm/geometry3d/AngleSweep.d.ts +14 -1
- package/lib/esm/geometry3d/AngleSweep.d.ts.map +1 -1
- package/lib/esm/geometry3d/AngleSweep.js +47 -12
- package/lib/esm/geometry3d/AngleSweep.js.map +1 -1
- package/lib/esm/geometry3d/FrameBuilder.d.ts +2 -1
- package/lib/esm/geometry3d/FrameBuilder.d.ts.map +1 -1
- package/lib/esm/geometry3d/FrameBuilder.js +12 -10
- package/lib/esm/geometry3d/FrameBuilder.js.map +1 -1
- package/lib/esm/geometry3d/GeometryHandler.d.ts.map +1 -1
- package/lib/esm/geometry3d/GeometryHandler.js +1 -7
- package/lib/esm/geometry3d/GeometryHandler.js.map +1 -1
- package/lib/esm/geometry3d/Matrix3d.d.ts +6 -4
- package/lib/esm/geometry3d/Matrix3d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Matrix3d.js +6 -4
- package/lib/esm/geometry3d/Matrix3d.js.map +1 -1
- package/lib/esm/geometry3d/Point3dVector3d.d.ts +2 -3
- package/lib/esm/geometry3d/Point3dVector3d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Point3dVector3d.js +2 -3
- package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
- package/lib/esm/geometry3d/PointHelpers.d.ts +6 -5
- package/lib/esm/geometry3d/PointHelpers.d.ts.map +1 -1
- package/lib/esm/geometry3d/PointHelpers.js +11 -10
- package/lib/esm/geometry3d/PointHelpers.js.map +1 -1
- package/lib/esm/geometry3d/PolygonOps.d.ts +4 -4
- package/lib/esm/geometry3d/PolygonOps.d.ts.map +1 -1
- package/lib/esm/geometry3d/PolygonOps.js +7 -11
- package/lib/esm/geometry3d/PolygonOps.js.map +1 -1
- package/lib/esm/geometry3d/PolylineCompressionByEdgeOffset.d.ts +1 -1
- package/lib/esm/geometry3d/PolylineCompressionByEdgeOffset.d.ts.map +1 -1
- package/lib/esm/geometry3d/PolylineCompressionByEdgeOffset.js +3 -3
- package/lib/esm/geometry3d/PolylineCompressionByEdgeOffset.js.map +1 -1
- package/lib/esm/geometry3d/Range.d.ts +6 -1
- package/lib/esm/geometry3d/Range.d.ts.map +1 -1
- package/lib/esm/geometry3d/Range.js +9 -3
- package/lib/esm/geometry3d/Range.js.map +1 -1
- package/lib/esm/geometry3d/Ray3d.d.ts +1 -1
- package/lib/esm/geometry3d/Ray3d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Ray3d.js.map +1 -1
- package/lib/esm/geometry3d/Transform.d.ts +1 -1
- package/lib/esm/geometry3d/Transform.js +1 -1
- package/lib/esm/geometry3d/Transform.js.map +1 -1
- package/lib/esm/numerics/Newton.d.ts +3 -3
- package/lib/esm/numerics/Newton.d.ts.map +1 -1
- package/lib/esm/numerics/Newton.js +14 -16
- package/lib/esm/numerics/Newton.js.map +1 -1
- package/lib/esm/numerics/Polynomials.d.ts +2 -2
- package/lib/esm/numerics/Polynomials.js +2 -2
- package/lib/esm/numerics/Polynomials.js.map +1 -1
- package/lib/esm/polyface/PolyfaceBuilder.d.ts +7 -4
- package/lib/esm/polyface/PolyfaceBuilder.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceBuilder.js +11 -6
- package/lib/esm/polyface/PolyfaceBuilder.js.map +1 -1
- package/lib/esm/polyface/PolyfaceClip.d.ts +13 -10
- package/lib/esm/polyface/PolyfaceClip.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceClip.js +17 -14
- package/lib/esm/polyface/PolyfaceClip.js.map +1 -1
- package/lib/esm/polyface/PolyfaceQuery.d.ts +11 -14
- package/lib/esm/polyface/PolyfaceQuery.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceQuery.js +59 -52
- package/lib/esm/polyface/PolyfaceQuery.js.map +1 -1
- package/lib/esm/polyface/multiclip/BuildAverageNormalsContext.d.ts.map +1 -1
- package/lib/esm/polyface/multiclip/BuildAverageNormalsContext.js +13 -6
- package/lib/esm/polyface/multiclip/BuildAverageNormalsContext.js.map +1 -1
- package/lib/esm/serialization/BGFBReader.js.map +1 -1
- package/lib/esm/serialization/BGFBWriter.js +2 -2
- package/lib/esm/serialization/BGFBWriter.js.map +1 -1
- package/lib/esm/serialization/GeometrySamples.js.map +1 -1
- package/lib/esm/solid/SweepContour.d.ts.map +1 -1
- package/lib/esm/solid/SweepContour.js +0 -4
- package/lib/esm/solid/SweepContour.js.map +1 -1
- package/lib/esm/topology/Graph.d.ts +1 -1
- package/lib/esm/topology/Graph.js +2 -2
- package/lib/esm/topology/Graph.js.map +1 -1
- package/lib/esm/topology/HalfEdgeNodeXYZUV.d.ts +1 -1
- package/lib/esm/topology/HalfEdgeNodeXYZUV.js +1 -1
- package/lib/esm/topology/HalfEdgeNodeXYZUV.js.map +1 -1
- package/lib/esm/topology/HalfEdgePointInGraphSearch.d.ts +57 -15
- package/lib/esm/topology/HalfEdgePointInGraphSearch.d.ts.map +1 -1
- package/lib/esm/topology/HalfEdgePointInGraphSearch.js +168 -127
- package/lib/esm/topology/HalfEdgePointInGraphSearch.js.map +1 -1
- package/lib/esm/topology/HalfEdgePositionDetail.d.ts +35 -35
- package/lib/esm/topology/HalfEdgePositionDetail.d.ts.map +1 -1
- package/lib/esm/topology/HalfEdgePositionDetail.js +63 -41
- package/lib/esm/topology/HalfEdgePositionDetail.js.map +1 -1
- package/lib/esm/topology/InsertAndRetriangulateContext.d.ts +64 -12
- package/lib/esm/topology/InsertAndRetriangulateContext.d.ts.map +1 -1
- package/lib/esm/topology/InsertAndRetriangulateContext.js +173 -74
- package/lib/esm/topology/InsertAndRetriangulateContext.js.map +1 -1
- package/lib/esm/topology/Merging.d.ts +2 -2
- package/lib/esm/topology/Merging.js +2 -2
- package/lib/esm/topology/Merging.js.map +1 -1
- package/lib/esm/topology/Triangulation.d.ts +16 -10
- package/lib/esm/topology/Triangulation.d.ts.map +1 -1
- package/lib/esm/topology/Triangulation.js +24 -31
- package/lib/esm/topology/Triangulation.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,1000 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.EllipticalArcApproximationContext = exports.QuadrantFractions = void 0;
|
|
8
|
+
const core_bentley_1 = require("@itwin/core-bentley");
|
|
9
|
+
const Geometry_1 = require("../../Geometry");
|
|
10
|
+
const Angle_1 = require("../../geometry3d/Angle");
|
|
11
|
+
const AngleSweep_1 = require("../../geometry3d/AngleSweep");
|
|
12
|
+
const Point3dVector3d_1 = require("../../geometry3d/Point3dVector3d");
|
|
13
|
+
const Range_1 = require("../../geometry3d/Range");
|
|
14
|
+
const Ray3d_1 = require("../../geometry3d/Ray3d");
|
|
15
|
+
const Transform_1 = require("../../geometry3d/Transform");
|
|
16
|
+
const Arc3d_1 = require("../Arc3d");
|
|
17
|
+
const Loop_1 = require("../Loop");
|
|
18
|
+
const Path_1 = require("../Path");
|
|
19
|
+
const CurveCurveCloseApproachXY_1 = require("./CurveCurveCloseApproachXY");
|
|
20
|
+
/** @packageDocumentation
|
|
21
|
+
* @module Curve
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Comparison callback to sort fractions in increasing order with suitable tolerance for equality.
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
function compareFractionsIncreasing(f0, f1) {
|
|
28
|
+
if (Geometry_1.Geometry.isAlmostEqualNumber(f0, f1, Geometry_1.Geometry.smallFraction))
|
|
29
|
+
return 0;
|
|
30
|
+
return f0 < f1 ? -1 : 1;
|
|
31
|
+
}
|
|
32
|
+
;
|
|
33
|
+
/**
|
|
34
|
+
* Comparison callback to sort fractions in decreasing order with suitable tolerance for equality.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
function compareFractionsDecreasing(f0, f1) {
|
|
38
|
+
if (Geometry_1.Geometry.isAlmostEqualNumber(f0, f1, Geometry_1.Geometry.smallFraction))
|
|
39
|
+
return 0;
|
|
40
|
+
return f0 < f1 ? 1 : -1;
|
|
41
|
+
}
|
|
42
|
+
;
|
|
43
|
+
/**
|
|
44
|
+
* Structured data carrier used by the elliptical arc sampler.
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
class QuadrantFractions {
|
|
48
|
+
constructor(quadrant, fractions, interpolateStartTangent, interpolateEndTangent) {
|
|
49
|
+
this._quadrant = quadrant;
|
|
50
|
+
this._fractions = fractions;
|
|
51
|
+
this._interpolateStartTangent = interpolateStartTangent;
|
|
52
|
+
this._interpolateEndTangent = interpolateEndTangent;
|
|
53
|
+
this._averageAdded = false;
|
|
54
|
+
}
|
|
55
|
+
/** Constructor, captures the array. */
|
|
56
|
+
static create(quadrant, fractions = [], interpolateStartTangent = true, interpolateEndTangent = true) {
|
|
57
|
+
return new QuadrantFractions(quadrant, fractions, interpolateStartTangent, interpolateEndTangent);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Quadrant of the full ellipse containing the samples.
|
|
61
|
+
* * Quadrants are labeled proceeding in counterclockwise angular sweeps of length pi/2 starting at vector0.
|
|
62
|
+
* * For example, Quadrant 1 starts at vector0 and ends at vector90, and Quadrant 4 ends at vector0.
|
|
63
|
+
* * For purposes of angle classification, quadrants are half-open intervals, closed at their start angle,
|
|
64
|
+
* as determined by the ellipse's sweep direction.
|
|
65
|
+
*/
|
|
66
|
+
get quadrant() {
|
|
67
|
+
return this._quadrant;
|
|
68
|
+
}
|
|
69
|
+
/** Sample locations in this quadrant of the elliptical arc, as fractions of its sweep. */
|
|
70
|
+
get fractions() {
|
|
71
|
+
return this._fractions;
|
|
72
|
+
}
|
|
73
|
+
set fractions(f) {
|
|
74
|
+
this._fractions = f;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Whether to interpolate the elliptical arc tangent at the first fraction.
|
|
78
|
+
* * If true (default), the first approximating arc is computed from the first two fractions and approximates the
|
|
79
|
+
* elliptical arc between the first and second fractions, interpolating both point and tangent at the first fraction,
|
|
80
|
+
* and point at the second fraction.
|
|
81
|
+
* * If false, the first approximating arc is computed from the first three fractions and approximates the
|
|
82
|
+
* elliptical arc between the second and third fractions, where it interpolates position only.
|
|
83
|
+
*/
|
|
84
|
+
get interpolateStartTangent() {
|
|
85
|
+
return this._interpolateStartTangent;
|
|
86
|
+
}
|
|
87
|
+
set interpolateStartTangent(interpolate) {
|
|
88
|
+
this._interpolateStartTangent = interpolate;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Whether to interpolate the elliptical arc tangent at the last fraction.
|
|
92
|
+
* * If true (default), the last approximating arc is computed from the last two fractions and approximates the
|
|
93
|
+
* elliptical arc between the penultimate and last fractions, interpolating point at the penultimate fraction, and
|
|
94
|
+
* both point and tangent at the last fraction.
|
|
95
|
+
* * If false, the last approximating arc is computed from the last three fractions and approximates the
|
|
96
|
+
* elliptical arc between the penultimate and last fractions, where it interpolates position only.
|
|
97
|
+
*/
|
|
98
|
+
get interpolateEndTangent() {
|
|
99
|
+
return this._interpolateEndTangent;
|
|
100
|
+
}
|
|
101
|
+
set interpolateEndTangent(interpolate) {
|
|
102
|
+
this._interpolateEndTangent = interpolate;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Whether the average of the first and last fractions was added to satisfy a minimum fractions array length of three.
|
|
106
|
+
* * There are always at least two fractions per quadrant, but three are needed to interpolate both end tangents
|
|
107
|
+
* with circular arcs.
|
|
108
|
+
* * This flag is set if a given sample method/arc yielded only two fractions, so their average was inserted in the
|
|
109
|
+
* fractions array to meet this minimum three-sample requirement.
|
|
110
|
+
*/
|
|
111
|
+
get averageAdded() {
|
|
112
|
+
return this._fractions.length === 3 ? this._averageAdded : false;
|
|
113
|
+
}
|
|
114
|
+
set averageAdded(added) {
|
|
115
|
+
this._averageAdded = added;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Compute quadrant data for the given angles.
|
|
119
|
+
* @param radians0 first radian angle
|
|
120
|
+
* @param radians1 second radian angle
|
|
121
|
+
* @return quadrant number and start/end radian angles for the quadrant that contains both input angles, or
|
|
122
|
+
* undefined if no such quadrant.
|
|
123
|
+
* * The returned sweep is always counterclockwise: angle0 < angle1.
|
|
124
|
+
*/
|
|
125
|
+
static getQuadrantRadians(radians0, radians1) {
|
|
126
|
+
if (AngleSweep_1.AngleSweep.isRadiansInStartEnd(radians0, 0, Angle_1.Angle.piOver2Radians)
|
|
127
|
+
&& AngleSweep_1.AngleSweep.isRadiansInStartEnd(radians1, 0, Angle_1.Angle.piOver2Radians))
|
|
128
|
+
return { quadrant: 1, angle0: 0, angle1: Angle_1.Angle.piOver2Radians };
|
|
129
|
+
if (AngleSweep_1.AngleSweep.isRadiansInStartEnd(radians0, Angle_1.Angle.piOver2Radians, Angle_1.Angle.piRadians)
|
|
130
|
+
&& AngleSweep_1.AngleSweep.isRadiansInStartEnd(radians1, Angle_1.Angle.piOver2Radians, Angle_1.Angle.piRadians))
|
|
131
|
+
return { quadrant: 2, angle0: Angle_1.Angle.piOver2Radians, angle1: Angle_1.Angle.piRadians };
|
|
132
|
+
if (AngleSweep_1.AngleSweep.isRadiansInStartEnd(radians0, Angle_1.Angle.piRadians, Angle_1.Angle.pi3Over2Radians)
|
|
133
|
+
&& AngleSweep_1.AngleSweep.isRadiansInStartEnd(radians1, Angle_1.Angle.piRadians, Angle_1.Angle.pi3Over2Radians))
|
|
134
|
+
return { quadrant: 3, angle0: Angle_1.Angle.piRadians, angle1: Angle_1.Angle.pi3Over2Radians };
|
|
135
|
+
if (AngleSweep_1.AngleSweep.isRadiansInStartEnd(radians0, Angle_1.Angle.pi3Over2Radians, Angle_1.Angle.pi2Radians)
|
|
136
|
+
&& AngleSweep_1.AngleSweep.isRadiansInStartEnd(radians1, Angle_1.Angle.pi3Over2Radians, Angle_1.Angle.pi2Radians))
|
|
137
|
+
return { quadrant: 4, angle0: Angle_1.Angle.pi3Over2Radians, angle1: Angle_1.Angle.pi2Radians };
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
/** Compute the fractional range of Quadrant 1 for the given sweep. */
|
|
141
|
+
static getQ1FractionalRange(sweep) {
|
|
142
|
+
const angle0 = 0;
|
|
143
|
+
const angle90 = 0.5 * Math.PI;
|
|
144
|
+
let f0 = sweep.radiansToSignedPeriodicFraction(angle0);
|
|
145
|
+
let f1 = sweep.radiansToSignedPeriodicFraction(angle90);
|
|
146
|
+
if (!sweep.isCCW)
|
|
147
|
+
[f0, f1] = [f1, f0];
|
|
148
|
+
if (f1 < f0)
|
|
149
|
+
f1 += 1;
|
|
150
|
+
return Range_1.Range1d.createXX(f0, f1);
|
|
151
|
+
}
|
|
152
|
+
/** Reverse the fractions array and flags. */
|
|
153
|
+
reverse() {
|
|
154
|
+
this._fractions.reverse();
|
|
155
|
+
[this._interpolateStartTangent, this._interpolateEndTangent] = [this._interpolateEndTangent, this._interpolateStartTangent];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.QuadrantFractions = QuadrantFractions;
|
|
159
|
+
;
|
|
160
|
+
/**
|
|
161
|
+
* Base class specifying callbacks for processing samples of an elliptical arc.
|
|
162
|
+
* @internal
|
|
163
|
+
*/
|
|
164
|
+
class QuadrantFractionsProcessor {
|
|
165
|
+
/**
|
|
166
|
+
* Announce the beginning of processing for quadrant `q`.
|
|
167
|
+
* @param _reversed whether `q.reverse()` was invoked before this call for symmetry reasons. If so, arcs will be
|
|
168
|
+
* announced in the opposite order and with the opposite orientation.
|
|
169
|
+
* @return whether to process `q`
|
|
170
|
+
*/
|
|
171
|
+
announceQuadrantBegin(_q, _reversed) { return true; }
|
|
172
|
+
/**
|
|
173
|
+
* Announce a circular arc approximating the elliptical arc E between the given fractions.
|
|
174
|
+
* * The given fractions are different. If `announceQuadrantBegin` was invoked with `reversed === false` then
|
|
175
|
+
* `fPrev < f0 < f1`; otherwise, `fPrev > f0 > f1`.
|
|
176
|
+
* @param _arc circular arc that interpolates E at f0 and f1. Processor can capture `arc`; it is unused afterwards.
|
|
177
|
+
* @param _fPrev fractional parameter of E used to define the 3-point parent circle through the points at `fPrev`,
|
|
178
|
+
* `f0`, and `f1` from which `arc` was constructed. If undefined, `arc` was generated from the circle defined by
|
|
179
|
+
* the points at `f0` and `f1` and one of their tangents.
|
|
180
|
+
* @param _f0 fractional parameter of E at which point `arc` starts
|
|
181
|
+
* @param _f1 fractional parameter of E at which point `arc` ends
|
|
182
|
+
*/
|
|
183
|
+
announceArc(_arc, _fPrev, _f0, _f1) { }
|
|
184
|
+
/**
|
|
185
|
+
* Announce the end of processing for quadrant `q`.
|
|
186
|
+
* @param _reversed whether `q.reverse()` was invoked before processing (see [[announceQuadrantBegin]]). If so,
|
|
187
|
+
* after this call `q.reverse()` is invoked again.
|
|
188
|
+
*/
|
|
189
|
+
announceQuadrantEnd(_q, _reversed) { }
|
|
190
|
+
}
|
|
191
|
+
;
|
|
192
|
+
/**
|
|
193
|
+
* Processor for computing the error of a sample-based arc chain approximation.
|
|
194
|
+
* @internal
|
|
195
|
+
*/
|
|
196
|
+
class ArcChainErrorProcessor extends QuadrantFractionsProcessor {
|
|
197
|
+
constructor(ellipticalArc) {
|
|
198
|
+
super();
|
|
199
|
+
this._ellipticalArc = ellipticalArc;
|
|
200
|
+
this._maxPerpendicular = undefined;
|
|
201
|
+
}
|
|
202
|
+
static create(ellipticalArc) {
|
|
203
|
+
return new ArcChainErrorProcessor(ellipticalArc);
|
|
204
|
+
}
|
|
205
|
+
get ellipticalArc() {
|
|
206
|
+
return this._ellipticalArc;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Compute the maximum xy-distance between an elliptical arc and its approximation.
|
|
210
|
+
* * Inputs should be in horizontal plane(s), as z-coordinates are ignored.
|
|
211
|
+
* @param circularArc circular arc approximant. Assumed to start and end on the elliptical arc.
|
|
212
|
+
* @param ellipticalArc elliptical arc being approximated.
|
|
213
|
+
* For best results, `f0` and `f1` should correspond to the start/end of `circularArc`
|
|
214
|
+
* @param f0 optional `ellipticalArc` start fraction to restrict its sweep
|
|
215
|
+
* @param f1 optional `ellipticalArc` end fraction to restrict its sweep
|
|
216
|
+
* @return details of the perpendicular measuring the max approximation error, or undefined if no such perpendicular.
|
|
217
|
+
* For each of `detailA` (refers to `circularArc`) and `detailB` (refers to unrestricted `ellipticalArc`):
|
|
218
|
+
* * `point` is the end of the perpendicular on each curve
|
|
219
|
+
* * `fraction` is the curve parameter of the point
|
|
220
|
+
* * `a` is the distance between the points
|
|
221
|
+
*/
|
|
222
|
+
static computePrimitiveErrorXY(circularArc, ellipticalArc, f0, f1) {
|
|
223
|
+
const handler = new CurveCurveCloseApproachXY_1.CurveCurveCloseApproachXY();
|
|
224
|
+
handler.maxDistanceToAccept = circularArc.quickLength() / 2;
|
|
225
|
+
const trimEllipse = undefined !== f0 && undefined !== f1;
|
|
226
|
+
const trimmedEllipticalArc = trimEllipse ? ellipticalArc.clonePartialCurve(f0, f1) : ellipticalArc;
|
|
227
|
+
// We expect only one perpendicular, not near an endpoint.
|
|
228
|
+
handler.allPerpendicularsArcArcBounded(circularArc, trimmedEllipticalArc);
|
|
229
|
+
let maxPerp;
|
|
230
|
+
for (const perp of handler.grabPairedResults()) {
|
|
231
|
+
if (Geometry_1.Geometry.isAlmostEqualEitherNumber(perp.detailA.fraction, 0, 1, Geometry_1.Geometry.smallFraction))
|
|
232
|
+
continue; // rule out perpendiculars on circular arc ends
|
|
233
|
+
if (Geometry_1.Geometry.isAlmostEqualEitherNumber(perp.detailB.fraction, 0, 1, Geometry_1.Geometry.smallFraction))
|
|
234
|
+
continue; // rule out perpendiculars on elliptical arc ends
|
|
235
|
+
const error = perp.detailA.point.distanceXY(perp.detailB.point);
|
|
236
|
+
if (!maxPerp || maxPerp.detailA.a < error) {
|
|
237
|
+
if (trimEllipse) { // reset ellipticalArc fraction to unrestricted range
|
|
238
|
+
perp.detailB.fraction = Geometry_1.Geometry.interpolate(f0, perp.detailB.fraction, f1);
|
|
239
|
+
perp.detailB.setCurve(ellipticalArc);
|
|
240
|
+
}
|
|
241
|
+
perp.detailA.a = perp.detailB.a = error;
|
|
242
|
+
maxPerp = perp;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return maxPerp;
|
|
246
|
+
}
|
|
247
|
+
get maxPerpendicular() {
|
|
248
|
+
return this._maxPerpendicular;
|
|
249
|
+
}
|
|
250
|
+
set maxPerpendicular(newMaxPerp) {
|
|
251
|
+
this._maxPerpendicular = newMaxPerp;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Update the chain approximation error for a given chain child that approximates the elliptical arc between the
|
|
255
|
+
* given fractions.
|
|
256
|
+
* * Fractional sweep [f0, f1] of the elliptical arc is the smaller of the cyclic sweeps.
|
|
257
|
+
*/
|
|
258
|
+
updateMaxPerpendicular(childApproximation, f0, f1) {
|
|
259
|
+
const childPerp = ArcChainErrorProcessor.computePrimitiveErrorXY(childApproximation, this.ellipticalArc, f0, f1);
|
|
260
|
+
if (childPerp && (!this.maxPerpendicular || this.maxPerpendicular.detailA.a < childPerp.detailA.a))
|
|
261
|
+
this.maxPerpendicular = childPerp;
|
|
262
|
+
}
|
|
263
|
+
;
|
|
264
|
+
announceArc(arc, _fPrev, f0, f1) {
|
|
265
|
+
this.updateMaxPerpendicular(arc, f0, f1);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Processor for refining a single Q1 ordered interval [f0,f1] by perturbing an interior fraction f.
|
|
270
|
+
* * This processor expects to repeatedly process a QuadrantFractions `q` wth `q.quadrant` = 1 and fractions array
|
|
271
|
+
* [fPrev, f0, f, f1], where fPrev is from the previously processed (possibly refined) adjacent interval; however, if
|
|
272
|
+
* `q.interpolateStartTangent === true`, then no fPrev is necessary and [f0, f, f1] is expected.
|
|
273
|
+
* * This is enough info to compute the two circular arcs spanning [f0,f] and [f,f1] and compare their approximation
|
|
274
|
+
* errors.
|
|
275
|
+
* * The basic idea is to perturb f so that the difference in the two arcs' errors is minimized.
|
|
276
|
+
* * This processor keeps track of a bracket containing f so that when the caller repeatedly processes `q` via
|
|
277
|
+
* [[EllipticalArcApproximationContext.processQuadrantFractions]], a bisection algorithm plays out, informed by the
|
|
278
|
+
* heuristic that moving f toward one end of its bracket decreases the error of the approximating arc on that side of f.
|
|
279
|
+
* @internal
|
|
280
|
+
*/
|
|
281
|
+
class AdaptiveSubdivisionQ1IntervalErrorProcessor extends QuadrantFractionsProcessor {
|
|
282
|
+
constructor(fullEllipseXY, f0, f, f1) {
|
|
283
|
+
super();
|
|
284
|
+
this._fullEllipseXY = fullEllipseXY;
|
|
285
|
+
this._bracket0 = f0;
|
|
286
|
+
this._f = f;
|
|
287
|
+
this._bracket1 = f1;
|
|
288
|
+
this._error0 = this._error1 = Geometry_1.Geometry.largeCoordinateResult;
|
|
289
|
+
}
|
|
290
|
+
static create(fullEllipseXY, f0, f, f1) {
|
|
291
|
+
return new AdaptiveSubdivisionQ1IntervalErrorProcessor(fullEllipseXY, f0, f, f1);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* The arc to approximate, transformed to local coordinates, and with full sweep.
|
|
295
|
+
* * Local coordinates allows us to ignore z in determining approximation error.
|
|
296
|
+
* * Full sweep guarantees we have the first quadrant in which to do our computations.
|
|
297
|
+
*/
|
|
298
|
+
get fullEllipseXY() {
|
|
299
|
+
return this._fullEllipseXY;
|
|
300
|
+
}
|
|
301
|
+
get f() {
|
|
302
|
+
return this._f;
|
|
303
|
+
}
|
|
304
|
+
get isConverged() {
|
|
305
|
+
if (Geometry_1.Geometry.isSmallMetricDistance(this._error0 - this._error1))
|
|
306
|
+
return true;
|
|
307
|
+
if (Geometry_1.Geometry.isSmallRelative(this._bracket0 - this._bracket1))
|
|
308
|
+
return true;
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
/** Remember the initial value of the fraction f to be perturbed. */
|
|
312
|
+
announceQuadrantBegin(q, reversed) {
|
|
313
|
+
(0, core_bentley_1.assert)(q.quadrant === 1);
|
|
314
|
+
(0, core_bentley_1.assert)(!reversed); // ASSUME bracket and q.fractions have same ordering
|
|
315
|
+
// the first fraction might be an extra point for computing the first 3-pt arc.
|
|
316
|
+
(0, core_bentley_1.assert)(q.fractions.length === 4 || (q.fractions.length === 3 && q.interpolateStartTangent));
|
|
317
|
+
this._error0 = this._error1 = Geometry_1.Geometry.largeCoordinateResult;
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
/** Compute approximation error over the interval adjacent to f. */
|
|
321
|
+
announceArc(arc, _fPrev, f0, f1) {
|
|
322
|
+
if (Geometry_1.Geometry.isAlmostEqualEitherNumber(this.f, f0, f1, 0)) {
|
|
323
|
+
const perp = ArcChainErrorProcessor.computePrimitiveErrorXY(arc, this.fullEllipseXY, f0, f1);
|
|
324
|
+
if (perp) {
|
|
325
|
+
if (this.f === f1)
|
|
326
|
+
this._error0 = perp.detailA.a; // first arc error
|
|
327
|
+
else // f === f0
|
|
328
|
+
this._error1 = perp.detailA.a; // second arc error
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/** Update `q.fractions` with a perturbed value of f that is expected to decrease error delta. */
|
|
333
|
+
announceQuadrantEnd(q, _reversed) {
|
|
334
|
+
if (Geometry_1.Geometry.isLargeCoordinateResult(this._error0) || Geometry_1.Geometry.isLargeCoordinateResult(this._error1))
|
|
335
|
+
return;
|
|
336
|
+
if (this.isConverged)
|
|
337
|
+
return;
|
|
338
|
+
// set up for next call to processQuadrantFractions
|
|
339
|
+
const n = q.fractions.length;
|
|
340
|
+
if (this._error0 < this._error1)
|
|
341
|
+
this._bracket0 = this._f; // HEURISTIC: move f toward f1 to decrease e1
|
|
342
|
+
else
|
|
343
|
+
this._bracket1 = this._f; // HEURISTIC: move f toward f0 to decrease e0
|
|
344
|
+
this._f = q.fractions[n - 2] = Geometry_1.Geometry.interpolate(this._bracket0, 0.5, this._bracket1);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Processor for computing samples in Q1 for a subdivision-based arc chain approximation.
|
|
349
|
+
* * The basic idea is to build a refinement of `q.fractions` for a QuadrantFractions q with q.quadrant = 1.
|
|
350
|
+
* * Start off the refinement with a copy of `q.fractions`.
|
|
351
|
+
* * When an announced arc exceeds a given maximum approximation error, compute a fraction f in the span
|
|
352
|
+
* such that the error of arcs on either side of f would be almost equal, then add f to the refinement.
|
|
353
|
+
* * If the announced arc does not exceed the maxError, its associated fraction span remains unchanged---no
|
|
354
|
+
* additional samples are needed to decrease approximation error.
|
|
355
|
+
* * After `q` processing completes, `q.fractions` is updated in place with the computed refinement.
|
|
356
|
+
* * The caller typically re-processes `q` until `isRefined` returns false, at which point construction of an
|
|
357
|
+
* approximation that is guaranteed not to exceed the desired error can commence.
|
|
358
|
+
* @internal
|
|
359
|
+
*/
|
|
360
|
+
class AdaptiveSubdivisionQ1ErrorProcessor extends QuadrantFractionsProcessor {
|
|
361
|
+
constructor(fullEllipseXY, maxError) {
|
|
362
|
+
super();
|
|
363
|
+
this._fullEllipseXY = fullEllipseXY;
|
|
364
|
+
this._fractionRangeQ1 = QuadrantFractions.getQ1FractionalRange(fullEllipseXY.sweep);
|
|
365
|
+
this._maxError = maxError > 0 ? maxError : Arc3d_1.EllipticalArcApproximationOptions.defaultMaxError;
|
|
366
|
+
this._originalRefinementCount = 0;
|
|
367
|
+
}
|
|
368
|
+
static create(fullEllipseXY, maxError) {
|
|
369
|
+
return new AdaptiveSubdivisionQ1ErrorProcessor(fullEllipseXY, maxError);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* The arc to approximate, transformed to local coordinates, and with full sweep.
|
|
373
|
+
* * Local coordinates allows us to ignore z in determining approximation error.
|
|
374
|
+
* * Full sweep guarantees we have the first quadrant in which to do our computations.
|
|
375
|
+
*/
|
|
376
|
+
get fullEllipseXY() {
|
|
377
|
+
return this._fullEllipseXY;
|
|
378
|
+
}
|
|
379
|
+
/** Whether the processor refined the current `QuadrantFractions` fractions array to decrease approximation error. */
|
|
380
|
+
get isRefined() {
|
|
381
|
+
if (undefined === this._refinement || 0 === this._refinement.length)
|
|
382
|
+
return false;
|
|
383
|
+
return this._originalRefinementCount < this._refinement.length;
|
|
384
|
+
}
|
|
385
|
+
/** Initialize the refinement from the quadrant fractions array. */
|
|
386
|
+
announceQuadrantBegin(q, reversed) {
|
|
387
|
+
(0, core_bentley_1.assert)(q.quadrant === 1);
|
|
388
|
+
this._refinement = new core_bentley_1.SortedArray(reversed ? compareFractionsDecreasing : compareFractionsIncreasing, false);
|
|
389
|
+
for (const f of q.fractions) {
|
|
390
|
+
if (this._fractionRangeQ1.containsX(f))
|
|
391
|
+
this._refinement.insert(f);
|
|
392
|
+
}
|
|
393
|
+
return 2 <= (this._originalRefinementCount = this._refinement.length);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Return the adjacent fraction from the previously refined interval.
|
|
397
|
+
* * This is used to refine the interval of an inner arc, which depends on the most recent refinement of the
|
|
398
|
+
* previous interval.
|
|
399
|
+
*/
|
|
400
|
+
getPreviousFraction(f0) {
|
|
401
|
+
if (undefined === this._refinement)
|
|
402
|
+
return undefined;
|
|
403
|
+
const iPrev = this._refinement.indexOf(f0);
|
|
404
|
+
return (iPrev >= 1) ? this._refinement.get(iPrev - 1) : undefined;
|
|
405
|
+
}
|
|
406
|
+
/** If this arc needs to be refined, add a refinement point. */
|
|
407
|
+
announceArc(arc, fPrev, f0, f1) {
|
|
408
|
+
if (undefined === this._refinement)
|
|
409
|
+
return;
|
|
410
|
+
if (this._originalRefinementCount > 2) { // no early out for a single interval; it gets refined below
|
|
411
|
+
const perp = ArcChainErrorProcessor.computePrimitiveErrorXY(arc, this.fullEllipseXY, f0, f1);
|
|
412
|
+
if (!perp || perp.detailA.a <= this._maxError)
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
// throughout this function, f0 and f1 may be in either order
|
|
416
|
+
const f = Geometry_1.Geometry.interpolate(f0, 0.5, f1);
|
|
417
|
+
const interpolateStartTangent = Geometry_1.Geometry.isAlmostEqualEitherNumber(f0, this._fractionRangeQ1.low, this._fractionRangeQ1.high, 0);
|
|
418
|
+
const interpolateEndTangent = Geometry_1.Geometry.isAlmostEqualEitherNumber(f1, this._fractionRangeQ1.low, this._fractionRangeQ1.high, 0);
|
|
419
|
+
if (!interpolateStartTangent && undefined === fPrev)
|
|
420
|
+
fPrev = this.getPreviousFraction(f0); // createLastArc caller doesn't supply fPrev
|
|
421
|
+
const fractions = (undefined === fPrev) ? [f0, f, f1] : [fPrev, f0, f, f1];
|
|
422
|
+
const q1 = [QuadrantFractions.create(1, fractions, interpolateStartTangent, interpolateEndTangent)];
|
|
423
|
+
const processor = AdaptiveSubdivisionQ1IntervalErrorProcessor.create(this.fullEllipseXY, f0, f, f1);
|
|
424
|
+
let iter = 0;
|
|
425
|
+
do { // bisect to refine f (starting at avg) to balance the approx error of the arcs on either side
|
|
426
|
+
EllipticalArcApproximationContext.processQuadrantFractions(this.fullEllipseXY, q1, processor);
|
|
427
|
+
} while (iter++ < AdaptiveSubdivisionQ1ErrorProcessor._maxIters && !processor.isConverged);
|
|
428
|
+
this._refinement.insert(processor.f);
|
|
429
|
+
}
|
|
430
|
+
/** Update the quadrant fractions array with the current refinement. */
|
|
431
|
+
announceQuadrantEnd(q, _reversed) {
|
|
432
|
+
if (this._refinement)
|
|
433
|
+
q.fractions = [...this._refinement];
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Compute radian angles for the fractions in the current refinement that are strictly inside Q1.
|
|
437
|
+
* @param result optional preallocated array to clear and populate
|
|
438
|
+
* @return angles suitable for output from [[EllipticalArcSampler.computeRadiansStrictlyInsideQuadrant1]].
|
|
439
|
+
*/
|
|
440
|
+
getRefinedInteriorQ1Angles(result) {
|
|
441
|
+
if (!result)
|
|
442
|
+
result = [];
|
|
443
|
+
else
|
|
444
|
+
result.length = 0;
|
|
445
|
+
if (this._refinement) {
|
|
446
|
+
for (const f of this._refinement) {
|
|
447
|
+
if (this._fractionRangeQ1.containsXOpen(f))
|
|
448
|
+
result.push(this.fullEllipseXY.sweep.fractionToRadians(f));
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return result;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
AdaptiveSubdivisionQ1ErrorProcessor._maxIters = 50;
|
|
455
|
+
;
|
|
456
|
+
/**
|
|
457
|
+
* Implementation for method `EllipticalArcSampleMethod.UniformParameter`
|
|
458
|
+
* @internal
|
|
459
|
+
*/
|
|
460
|
+
class UniformParameterSampler {
|
|
461
|
+
constructor(c, o) {
|
|
462
|
+
this._context = c;
|
|
463
|
+
this._options = o;
|
|
464
|
+
}
|
|
465
|
+
static create(context, options) {
|
|
466
|
+
return new UniformParameterSampler(context, options);
|
|
467
|
+
}
|
|
468
|
+
computeRadiansStrictlyInsideQuadrant1(result) {
|
|
469
|
+
if (!result)
|
|
470
|
+
result = [];
|
|
471
|
+
if (this._context.isValidEllipticalArc) {
|
|
472
|
+
const aDelta = Angle_1.Angle.piOver2Radians / (this._options.numSamplesInQuadrant - 1);
|
|
473
|
+
for (let i = 1; i < this._options.numSamplesInQuadrant - 1; ++i)
|
|
474
|
+
result.push(i * aDelta);
|
|
475
|
+
}
|
|
476
|
+
return result;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
;
|
|
480
|
+
/**
|
|
481
|
+
* Implementation for method `EllipticalArcSampleMethod.NonUniformCurvature`
|
|
482
|
+
* @internal
|
|
483
|
+
*/
|
|
484
|
+
class NonUniformCurvatureSampler {
|
|
485
|
+
constructor(c, o) {
|
|
486
|
+
this._context = c;
|
|
487
|
+
this._options = o;
|
|
488
|
+
this._xMag2 = c.ellipticalArc.matrixRef.columnXMagnitudeSquared();
|
|
489
|
+
this._yMag2 = c.ellipticalArc.matrixRef.columnYMagnitudeSquared();
|
|
490
|
+
// extreme curvatures occur at the ellipse's axis points because its axes are perpendicular
|
|
491
|
+
this._curvatureRange = Range_1.Range1d.createXX(Math.sqrt(this._xMag2) / this._yMag2, Math.sqrt(this._yMag2) / this._xMag2);
|
|
492
|
+
}
|
|
493
|
+
static create(context, options) {
|
|
494
|
+
return new NonUniformCurvatureSampler(context, options);
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Compute the angle corresponding to the point in the ellipse's first quadrant with the given curvature.
|
|
498
|
+
* * The elliptical arc is assumed to be non-circular and have perpendicular axes of positive length; its sweep is ignored.
|
|
499
|
+
* * This is a scaled inverse of [[Arc3d.fractionToCurvature]] restricted to fractions in [0, 1/4].
|
|
500
|
+
* @return radian angle in [0, pi/2] or undefined if the ellipse is invalid, or does not attain the given curvature.
|
|
501
|
+
*/
|
|
502
|
+
curvatureToRadians(curvature) {
|
|
503
|
+
/*
|
|
504
|
+
Let the elliptical arc be parameterized with axes u,v of different length and u.v = 0:
|
|
505
|
+
f(t) = c + u cos(t) + v sin(t),
|
|
506
|
+
f'(t) = -u sin(t) + v cos(t),
|
|
507
|
+
f"(t) = -u cos(t) - v sin(t)
|
|
508
|
+
We seek a formula for t(K), the inverse of the standard curvature formula
|
|
509
|
+
K(t) := ||f'(t) x f"(t)|| / ||f'(t)||^3
|
|
510
|
+
for a parametric function f(t):R->R^3. We'll restrict K to Q1 (i.e., t in [0, pi/2]), where K is monotonic.
|
|
511
|
+
By linearity of the cross product and the above formulas, the numerator of K(t) reduces to ||u x v||, and so:
|
|
512
|
+
cbrt(||u x v||/K) = ||f'(t)|| = sqrt(f'(t).f'(t))
|
|
513
|
+
Leveraging u,v perpendicularity we can define:
|
|
514
|
+
lambda(K) := (||u x v||/K)^(2/3) = (||u|| ||v|| / K)^(2/3) = cbrt(u.u v.v / K^2)
|
|
515
|
+
Then substituting and using perpendicularity again:
|
|
516
|
+
lambda(K) = f'(t).f'(t)
|
|
517
|
+
= sin^2(t)u.u + cos^2(t)v.v - 2sin(t)cos(t)u.v
|
|
518
|
+
= u.u + cos^2(t)(v.v - u.u)
|
|
519
|
+
Taking the positive root because cos(t)>=0 in Q1, and relying on u,v having different lengths:
|
|
520
|
+
cos(t) = sqrt((lambda(K) - u.u)/(v.v - u.u))
|
|
521
|
+
Solving for t yields the formula for t(K).
|
|
522
|
+
*/
|
|
523
|
+
if (!this._curvatureRange.containsX(curvature))
|
|
524
|
+
return undefined; // ellipse does not attain this curvature
|
|
525
|
+
const lambda = Math.cbrt((this._xMag2 * this._yMag2) / (curvature * curvature));
|
|
526
|
+
const cosTheta = Math.sqrt(Math.abs((lambda - this._xMag2) / (this._yMag2 - this._xMag2)));
|
|
527
|
+
return Math.acos(cosTheta);
|
|
528
|
+
}
|
|
529
|
+
computeRadiansStrictlyInsideQuadrant1(result) {
|
|
530
|
+
if (!result)
|
|
531
|
+
result = [];
|
|
532
|
+
if (this._context.isValidEllipticalArc) {
|
|
533
|
+
const tDelta = 1.0 / (this._options.numSamplesInQuadrant - 1);
|
|
534
|
+
for (let i = 1; i < this._options.numSamplesInQuadrant - 1; ++i) {
|
|
535
|
+
const j = this._options.remapFunction(i * tDelta);
|
|
536
|
+
const curvature = (1 - j) * this._curvatureRange.low + j * this._curvatureRange.high;
|
|
537
|
+
const angle = this.curvatureToRadians(curvature);
|
|
538
|
+
if (undefined !== angle)
|
|
539
|
+
result.push(angle);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return result;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
;
|
|
546
|
+
/**
|
|
547
|
+
* Implementation for method `EllipticalArcSampleMethod.UniformCurvature`.
|
|
548
|
+
* * Basically this is just `NonUniformCurvature` method with uniformity preserved via identity remap function.
|
|
549
|
+
* @internal
|
|
550
|
+
*/
|
|
551
|
+
class UniformCurvatureSampler extends NonUniformCurvatureSampler {
|
|
552
|
+
constructor(c, o) {
|
|
553
|
+
super(c, o.clone());
|
|
554
|
+
this._options.remapFunction = (x) => x; // identity map
|
|
555
|
+
}
|
|
556
|
+
static create(context, options) {
|
|
557
|
+
return new UniformCurvatureSampler(context, options);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
;
|
|
561
|
+
/**
|
|
562
|
+
* Implementation for method `EllipticalArcSampleMethod.AdaptiveSubdivision`
|
|
563
|
+
* @internal
|
|
564
|
+
*/
|
|
565
|
+
class AdaptiveSubdivisionSampler {
|
|
566
|
+
constructor(c, o) {
|
|
567
|
+
this._context = c;
|
|
568
|
+
this._options = o;
|
|
569
|
+
this._fullEllipseXY = c.cloneLocalArc(true) ?? Arc3d_1.Arc3d.createUnitCircle();
|
|
570
|
+
}
|
|
571
|
+
static create(context, options) {
|
|
572
|
+
return new AdaptiveSubdivisionSampler(context, options);
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Return a copy of the arc to approximate, transformed to local coordinates, and with full sweep.
|
|
576
|
+
* * Local coordinates allows us to ignore z in determining approximation error.
|
|
577
|
+
* * Full sweep guarantees we have the first quadrant in which to do our computations.
|
|
578
|
+
*/
|
|
579
|
+
get fullEllipseXY() {
|
|
580
|
+
return this._fullEllipseXY;
|
|
581
|
+
}
|
|
582
|
+
computeRadiansStrictlyInsideQuadrant1(result) {
|
|
583
|
+
if (!this._context.isValidEllipticalArc)
|
|
584
|
+
return [];
|
|
585
|
+
const rangeQ1 = QuadrantFractions.getQ1FractionalRange(this.fullEllipseXY.sweep);
|
|
586
|
+
const q1 = [QuadrantFractions.create(1, [rangeQ1.low, rangeQ1.high], true, true)];
|
|
587
|
+
const processor = AdaptiveSubdivisionQ1ErrorProcessor.create(this.fullEllipseXY, this._options.maxError);
|
|
588
|
+
do {
|
|
589
|
+
EllipticalArcApproximationContext.processQuadrantFractions(this.fullEllipseXY, q1, processor);
|
|
590
|
+
} while (processor.isRefined);
|
|
591
|
+
return processor.getRefinedInteriorQ1Angles(result);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
;
|
|
595
|
+
/**
|
|
596
|
+
* Processor for constructing a sample-based circular arc chain approximation.
|
|
597
|
+
* @internal
|
|
598
|
+
*/
|
|
599
|
+
class ArcChainConstructionProcessor extends QuadrantFractionsProcessor {
|
|
600
|
+
constructor(ellipticalArc, forcePath) {
|
|
601
|
+
super();
|
|
602
|
+
this._chain = (ellipticalArc.sweep.isFullCircle && !forcePath) ? Loop_1.Loop.create() : Path_1.Path.create();
|
|
603
|
+
}
|
|
604
|
+
static create(ellipticalArc, forcePath = false) {
|
|
605
|
+
return new ArcChainConstructionProcessor(ellipticalArc, forcePath);
|
|
606
|
+
}
|
|
607
|
+
get chain() {
|
|
608
|
+
return this._chain.children.length > 0 ? this._chain : undefined;
|
|
609
|
+
}
|
|
610
|
+
announceQuadrantBegin(_q, _reversed) {
|
|
611
|
+
this._quadrantChain = undefined;
|
|
612
|
+
return true;
|
|
613
|
+
}
|
|
614
|
+
announceArc(arc, _fPrev, _f0, _f1) {
|
|
615
|
+
if (!this._quadrantChain)
|
|
616
|
+
this._quadrantChain = Path_1.Path.create(); // the arc chain in a quadrant is always open
|
|
617
|
+
this._quadrantChain.tryAddChild(arc); // captured!
|
|
618
|
+
}
|
|
619
|
+
announceQuadrantEnd(_q, reversed) {
|
|
620
|
+
if (this._quadrantChain) {
|
|
621
|
+
if (reversed)
|
|
622
|
+
this._quadrantChain.reverseChildrenInPlace();
|
|
623
|
+
for (const child of this._quadrantChain.children)
|
|
624
|
+
this._chain.tryAddChild(child); // captured!
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
;
|
|
629
|
+
/**
|
|
630
|
+
* Context for sampling a non-circular Arc3d and for constructing an approximation to it based on interpolation
|
|
631
|
+
* of the samples.
|
|
632
|
+
* * [[EllipticalArcApproximationContext.constructCircularArcChainApproximation]] constructs a `CurveChain`
|
|
633
|
+
* approximation consisting of circular arcs.
|
|
634
|
+
* * Various sample methods are supported, cf. [[EllipticalArcApproximationOptions]].
|
|
635
|
+
* @internal
|
|
636
|
+
*/
|
|
637
|
+
class EllipticalArcApproximationContext {
|
|
638
|
+
/** Constructor, captures input */
|
|
639
|
+
constructor(ellipticalArc) {
|
|
640
|
+
this._isValidEllipticalArc = false;
|
|
641
|
+
const data = ellipticalArc.toScaledMatrix3d();
|
|
642
|
+
this._ellipticalArc = Arc3d_1.Arc3d.createScaledXYColumns(data.center, data.axes, data.r0, data.r90, data.sweep);
|
|
643
|
+
this._localToWorld = Transform_1.Transform.createRefs(data.center, data.axes);
|
|
644
|
+
if (this._localToWorld.matrix.isSingular())
|
|
645
|
+
return;
|
|
646
|
+
if (this._ellipticalArc.sweep.isEmpty)
|
|
647
|
+
return; // ellipse must have a nonzero sweep
|
|
648
|
+
const xMag2 = ellipticalArc.matrixRef.columnXMagnitudeSquared();
|
|
649
|
+
const yMag2 = ellipticalArc.matrixRef.columnYMagnitudeSquared();
|
|
650
|
+
if (Geometry_1.Geometry.isSmallMetricDistanceSquared(xMag2) || Geometry_1.Geometry.isSmallMetricDistanceSquared(yMag2))
|
|
651
|
+
return; // ellipse must have positive radii
|
|
652
|
+
if (Geometry_1.Geometry.isSameCoordinateSquared(xMag2, yMag2))
|
|
653
|
+
return; // ellipse must not be circular
|
|
654
|
+
this._isValidEllipticalArc = true;
|
|
655
|
+
}
|
|
656
|
+
/** Constructor, clones input. */
|
|
657
|
+
static create(ellipticalArc) {
|
|
658
|
+
return new EllipticalArcApproximationContext(ellipticalArc);
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* The arc to be sampled.
|
|
662
|
+
* * Its axes are forced to be perpendicular.
|
|
663
|
+
* * It is stored in world coordinates.
|
|
664
|
+
*/
|
|
665
|
+
get ellipticalArc() {
|
|
666
|
+
return this._ellipticalArc;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* The rigid transformation that maps `ellipticalArc` from local coordinates to world coordinates.
|
|
670
|
+
* * In local coordinates, the arc center lies at the origin and its (perpendicular) axes of symmetry lie along
|
|
671
|
+
* the positive x- and y-axes.
|
|
672
|
+
*/
|
|
673
|
+
get localToWorld() {
|
|
674
|
+
return this._localToWorld;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Whether the elliptical arc is amenable to sampling.
|
|
678
|
+
* * The arc is valid if it is non-circular, has nonzero sweep, and has positive radii (nonsingular matrix).
|
|
679
|
+
*/
|
|
680
|
+
get isValidEllipticalArc() {
|
|
681
|
+
return this._isValidEllipticalArc;
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Create a clone of the context's arc in local coordinates.
|
|
685
|
+
* @param fullSweep Optionally set full sweep on the returned local arc. Start angle is preserved.
|
|
686
|
+
* @returns local arc, or undefined if the arc is invalid
|
|
687
|
+
*/
|
|
688
|
+
cloneLocalArc(fullSweep) {
|
|
689
|
+
if (!this.isValidEllipticalArc)
|
|
690
|
+
return undefined;
|
|
691
|
+
const worldToLocal = this.localToWorld.inverse();
|
|
692
|
+
if (!worldToLocal)
|
|
693
|
+
return undefined;
|
|
694
|
+
const arcXY = this.ellipticalArc.cloneTransformed(worldToLocal);
|
|
695
|
+
if (fullSweep) {
|
|
696
|
+
let sweep = 2 * Math.PI;
|
|
697
|
+
if (!arcXY.sweep.isCCW)
|
|
698
|
+
sweep = -sweep;
|
|
699
|
+
arcXY.sweep.setStartEndRadians(arcXY.sweep.startRadians, arcXY.sweep.startRadians + sweep);
|
|
700
|
+
}
|
|
701
|
+
return arcXY;
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Process structured sample data for the given elliptical arc.
|
|
705
|
+
* * Circular arcs are announced to the processor for each sample interval in each quadrant.
|
|
706
|
+
* * Each quadrant is processed separately to allow the elliptical arc's axis points and tangents to be interpolated.
|
|
707
|
+
* * A 2-point plus tangent construction is used to create the first and last circular arc in each quadrant.
|
|
708
|
+
* * Symmetry of the announced circular arcs matching that of a multi-quadrant spanning elliptical arc is ensured by
|
|
709
|
+
* processing the samples consistently, starting along the elliptical arc's major axis in each quadrant.
|
|
710
|
+
* @param ellipticalArc source arc to approximate
|
|
711
|
+
* @param quadrants structured samples, may be temporarily reversed for symmetry
|
|
712
|
+
* @param processor callbacks for handling the constructed arcs
|
|
713
|
+
* @internal
|
|
714
|
+
*/
|
|
715
|
+
static processQuadrantFractions(ellipticalArc, quadrants, processor) {
|
|
716
|
+
const pt0 = this.workPt0;
|
|
717
|
+
const pt1 = this.workPt1;
|
|
718
|
+
const pt2 = this.workPt2;
|
|
719
|
+
const ray = this.workRay;
|
|
720
|
+
const arcBetween2Samples = (arcStart, arcEnd, reverse) => {
|
|
721
|
+
// assume non-colinear inputs
|
|
722
|
+
const myArc = Arc3d_1.Arc3d.createCircularStartTangentEnd(arcStart.origin, arcStart.direction, arcEnd);
|
|
723
|
+
if (!(myArc instanceof Arc3d_1.Arc3d))
|
|
724
|
+
return undefined;
|
|
725
|
+
if (reverse)
|
|
726
|
+
myArc.reverseInPlace();
|
|
727
|
+
return myArc;
|
|
728
|
+
};
|
|
729
|
+
const arcBetweenLast2Of3Samples = (p0, arcStart, arcEnd) => {
|
|
730
|
+
// assume non-colinear inputs; initial arc starts at p0, ends at arcEnd
|
|
731
|
+
const arc = Arc3d_1.Arc3d.createCircularStartMiddleEnd(p0, arcStart, arcEnd);
|
|
732
|
+
if (!(arc instanceof Arc3d_1.Arc3d))
|
|
733
|
+
return undefined; // colinear?
|
|
734
|
+
const startAngle = arc.vector0.signedAngleTo(Point3dVector3d_1.Vector3d.createStartEnd(arc.center, arcStart), arc.matrixRef.columnZ());
|
|
735
|
+
arc.sweep.setStartEndRadians(startAngle.radians, arc.sweep.endRadians);
|
|
736
|
+
return arc; // returned arc starts at arcStart, ends at arcEnd
|
|
737
|
+
};
|
|
738
|
+
const createFirstArc = (f0, f1, reverse) => {
|
|
739
|
+
// This arc starts at the first sample f0 and ends at f1.
|
|
740
|
+
ellipticalArc.fractionToPointAndDerivative(f0, ray);
|
|
741
|
+
ellipticalArc.fractionToPoint(f1, pt1);
|
|
742
|
+
if (reverse)
|
|
743
|
+
ray.direction.scaleInPlace(-1);
|
|
744
|
+
const arc = arcBetween2Samples(ray, pt1, false);
|
|
745
|
+
if (arc)
|
|
746
|
+
processor.announceArc(arc, undefined, f0, f1);
|
|
747
|
+
};
|
|
748
|
+
const createInnerArc = (f0, f1, f2) => {
|
|
749
|
+
let fPrev = f0;
|
|
750
|
+
if (processor.getPreviousFraction)
|
|
751
|
+
fPrev = processor.getPreviousFraction(f1) ?? f0;
|
|
752
|
+
ellipticalArc.fractionToPoint(fPrev, pt0);
|
|
753
|
+
ellipticalArc.fractionToPoint(f1, pt1);
|
|
754
|
+
ellipticalArc.fractionToPoint(f2, pt2);
|
|
755
|
+
const arc = arcBetweenLast2Of3Samples(pt0, pt1, pt2);
|
|
756
|
+
if (arc)
|
|
757
|
+
processor.announceArc(arc, fPrev, f1, f2);
|
|
758
|
+
};
|
|
759
|
+
const createLastArc = (f0, f1, reverse) => {
|
|
760
|
+
// This arc starts at f0 and ends at the last sample f1. It is the only arc to use f1.
|
|
761
|
+
ellipticalArc.fractionToPoint(f0, pt0);
|
|
762
|
+
ellipticalArc.fractionToPointAndDerivative(f1, ray);
|
|
763
|
+
if (!reverse)
|
|
764
|
+
ray.direction.scaleInPlace(-1);
|
|
765
|
+
const arc = arcBetween2Samples(ray, pt0, true);
|
|
766
|
+
if (arc)
|
|
767
|
+
processor.announceArc(arc, undefined, f0, f1);
|
|
768
|
+
};
|
|
769
|
+
const reverseFractionsForSymmetry = (q) => {
|
|
770
|
+
// If q interpolates an axis, we process q.fractions in a consistent direction (increasing or decreasing) so that the
|
|
771
|
+
// approximating arc chain exhibits fourfold axial symmetry. We do this by ensuring q.fractions starts along the
|
|
772
|
+
// major axis (or ends along the minor axis). This choice is arbitrary, but consistently made across all quadrants.
|
|
773
|
+
if (!q.interpolateStartTangent && !q.interpolateEndTangent)
|
|
774
|
+
return false;
|
|
775
|
+
const n = q.fractions.length;
|
|
776
|
+
if (n < 2)
|
|
777
|
+
return false;
|
|
778
|
+
const xAxisIsMajor = ellipticalArc.vector0.magnitudeSquared() > ellipticalArc.vector90.magnitudeSquared();
|
|
779
|
+
const processCCWQuadrantInReverse = xAxisIsMajor ? (q.quadrant === 2 || q.quadrant === 4) : (q.quadrant === 1 || q.quadrant === 3);
|
|
780
|
+
const isAlreadyReversed = q.fractions[0] > q.fractions[n - 1];
|
|
781
|
+
const doReverse = !isAlreadyReversed && (processCCWQuadrantInReverse === ellipticalArc.sweep.isCCW);
|
|
782
|
+
if (doReverse)
|
|
783
|
+
q.reverse(); // for symmetry we sometimes process decreasing fractions. This creates slightly different arcs.
|
|
784
|
+
return doReverse;
|
|
785
|
+
};
|
|
786
|
+
for (const q of quadrants) {
|
|
787
|
+
const n = q.fractions.length;
|
|
788
|
+
if (n < 2)
|
|
789
|
+
continue;
|
|
790
|
+
const reversed = reverseFractionsForSymmetry(q);
|
|
791
|
+
if (!processor.announceQuadrantBegin(q, reversed))
|
|
792
|
+
continue;
|
|
793
|
+
if (q.interpolateStartTangent)
|
|
794
|
+
createFirstArc(q.fractions[0], q.fractions[1], reversed);
|
|
795
|
+
// the first inner arc approximates the ellipse over [f[1],f[2]]; the last inner arc, over [f[n-3],f[n-2]]
|
|
796
|
+
for (let i = 0; i + 2 < n - 1; ++i)
|
|
797
|
+
createInnerArc(q.fractions[i], q.fractions[i + 1], q.fractions[i + 2]);
|
|
798
|
+
if (n > 2) { // the final arc approximates [f[n-2],f[n-1]]
|
|
799
|
+
if (q.interpolateEndTangent)
|
|
800
|
+
createLastArc(q.fractions[n - 2], q.fractions[n - 1], reversed);
|
|
801
|
+
else
|
|
802
|
+
createInnerArc(q.fractions[n - 3], q.fractions[n - 2], q.fractions[n - 1]);
|
|
803
|
+
}
|
|
804
|
+
processor.announceQuadrantEnd(q, reversed);
|
|
805
|
+
if (reversed)
|
|
806
|
+
q.reverse(); // undo the reverse above
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Compute the maximum error of the circular arc chain approximation determined by the given samples.
|
|
811
|
+
* * This is measured by the longest perpendicular between the elliptical arc and its approximation.
|
|
812
|
+
* @param samples structured sample data from the instance's elliptical arc.
|
|
813
|
+
* @return details of the perpendicular measuring the max approximation error, or undefined if no such perpendicular.
|
|
814
|
+
* For each of `detailA` and `detailB`:
|
|
815
|
+
* * `point` is the end of the perpendicular on each curve
|
|
816
|
+
* * `fraction` is the curve parameter of the point
|
|
817
|
+
* * `a` is the distance between the points.
|
|
818
|
+
* @internal
|
|
819
|
+
*/
|
|
820
|
+
computeApproximationError(samples) {
|
|
821
|
+
const arcXY = this.cloneLocalArc();
|
|
822
|
+
if (!arcXY)
|
|
823
|
+
return undefined;
|
|
824
|
+
const processor = ArcChainErrorProcessor.create(arcXY);
|
|
825
|
+
EllipticalArcApproximationContext.processQuadrantFractions(arcXY, samples, processor);
|
|
826
|
+
const maxError = processor.maxPerpendicular;
|
|
827
|
+
return (maxError && maxError.tryTransformInPlace(this.localToWorld)) ? maxError : undefined;
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Compute samples for the elliptical arc as fraction parameters.
|
|
831
|
+
* * This method houses the sampling framework for all sampling methods, which are customized via implementations
|
|
832
|
+
* of the [[EllipticalArcSampler]] interface.
|
|
833
|
+
* * Note that the returned samples are fractions in the parameterization of the context's arc (whose axes have been
|
|
834
|
+
* forced to be perpendicular), not the input arc passed into the context's constructor.
|
|
835
|
+
* @param options options that determine how the elliptical arc is sampled.
|
|
836
|
+
* @param structuredOutput flag indicating output format as follows:
|
|
837
|
+
* * If false (default), return all fractions in one sorted (increasing), deduplicated array (a full ellipse includes
|
|
838
|
+
* both 0 and 1).
|
|
839
|
+
* * If true, fractions are assembled by quadrants:
|
|
840
|
+
* * Each [[QuadrantFractions]] object holds at least three sorted (increasing), deduplicated fractions in a
|
|
841
|
+
* specified quadrant of the arc.
|
|
842
|
+
* * If only two fractions would be computed for a given `QuadrantFractions`, their midpoint is inserted to enable
|
|
843
|
+
* tangent interpolation at both ends. Such a quadrant `q` is marked with `q.averageAdded = true`.
|
|
844
|
+
* * The `QuadrantFractions` objects themselves are sorted by increasing order of the fractions they contain.
|
|
845
|
+
* * If the arc sweep spans adjacent quadrants, the fraction bordering the quadrants appears in both `QuadrantFractions`.
|
|
846
|
+
* * If the arc starts and ends in the same quadrant, two `QuadrantFractions` objects can be returned.
|
|
847
|
+
* * This means there are between 1 and 5 objects in the `QuadrantFractions` array.
|
|
848
|
+
* @internal
|
|
849
|
+
*/
|
|
850
|
+
computeSampleFractions(options, structuredOutput = false) {
|
|
851
|
+
if (!this.isValidEllipticalArc)
|
|
852
|
+
return [];
|
|
853
|
+
const compareRadiansIncreasing = (a0, a1) => {
|
|
854
|
+
if (Geometry_1.Geometry.isAlmostEqualNumber(a0, a1, Geometry_1.Geometry.smallAngleRadians))
|
|
855
|
+
return 0;
|
|
856
|
+
return a0 < a1 ? -1 : 1;
|
|
857
|
+
};
|
|
858
|
+
const compareRadiansDecreasing = (a0, a1) => {
|
|
859
|
+
if (Geometry_1.Geometry.isAlmostEqualNumber(a0, a1, Geometry_1.Geometry.smallAngleRadians))
|
|
860
|
+
return 0;
|
|
861
|
+
return a0 < a1 ? 1 : -1;
|
|
862
|
+
};
|
|
863
|
+
const compareQuadrantFractions = (q0, q1) => {
|
|
864
|
+
// ASSUME QuadrantFractions.fractions arrays are sorted (increasing) and have only trivial overlap
|
|
865
|
+
if (compareFractionsIncreasing(q0.fractions[q0.fractions.length - 1], q1.fractions[0]) <= 0)
|
|
866
|
+
return -1;
|
|
867
|
+
if (compareFractionsIncreasing(q1.fractions[q1.fractions.length - 1], q0.fractions[0]) <= 0)
|
|
868
|
+
return 1;
|
|
869
|
+
return 0;
|
|
870
|
+
};
|
|
871
|
+
const shiftRadiansToSweep = (angle, sweep) => {
|
|
872
|
+
const inSweep = sweep.isRadiansInSweep(angle, true);
|
|
873
|
+
if (inSweep) {
|
|
874
|
+
const fraction = sweep.radiansToSignedPeriodicFraction(angle);
|
|
875
|
+
if (Geometry_1.Geometry.isIn01(fraction))
|
|
876
|
+
angle = sweep.fractionToRadians(fraction);
|
|
877
|
+
}
|
|
878
|
+
return { angle, inSweep };
|
|
879
|
+
};
|
|
880
|
+
const convertAndAddRadiansToFractionInRange = (dest, radians, sweep, f0, f1) => {
|
|
881
|
+
if (undefined === f0)
|
|
882
|
+
f0 = 0;
|
|
883
|
+
if (undefined === f1)
|
|
884
|
+
f1 = 1;
|
|
885
|
+
if (f0 > f1)
|
|
886
|
+
return convertAndAddRadiansToFractionInRange(dest, radians, sweep, f1, f0);
|
|
887
|
+
const fraction = sweep.radiansToSignedPeriodicFraction(radians);
|
|
888
|
+
if (fraction < (f0 - Geometry_1.Geometry.smallFraction) || (f1 + Geometry_1.Geometry.smallFraction) < fraction)
|
|
889
|
+
return undefined; // angle is outside sweep
|
|
890
|
+
Geometry_1.Geometry.restrictToInterval(fraction, 0, 1);
|
|
891
|
+
dest.add(fraction);
|
|
892
|
+
return fraction;
|
|
893
|
+
};
|
|
894
|
+
const convertQ1RadiansInSweepToQuadrantFractions = (anglesInQ1, angle0, angle1, sweep) => {
|
|
895
|
+
if (angle0 > angle1)
|
|
896
|
+
return convertQ1RadiansInSweepToQuadrantFractions(anglesInQ1, angle1, angle0, sweep);
|
|
897
|
+
if (Angle_1.Angle.isAlmostEqualRadiansNoPeriodShift(angle0, angle1))
|
|
898
|
+
return undefined; // empty sweep
|
|
899
|
+
const qData = QuadrantFractions.getQuadrantRadians(angle0, angle1);
|
|
900
|
+
if (undefined === qData)
|
|
901
|
+
return undefined; // no containing quadrant
|
|
902
|
+
const qFractions = new core_bentley_1.OrderedSet(compareFractionsIncreasing);
|
|
903
|
+
const f0 = convertAndAddRadiansToFractionInRange(qFractions, angle0, sweep);
|
|
904
|
+
const f1 = convertAndAddRadiansToFractionInRange(qFractions, angle1, sweep);
|
|
905
|
+
if (undefined === f0 || undefined === f1)
|
|
906
|
+
return undefined;
|
|
907
|
+
for (const a0 of anglesInQ1) {
|
|
908
|
+
let angle = a0;
|
|
909
|
+
if (2 === qData.quadrant)
|
|
910
|
+
angle = Angle_1.Angle.piRadians - angle;
|
|
911
|
+
else if (3 === qData.quadrant)
|
|
912
|
+
angle = Angle_1.Angle.piRadians + angle;
|
|
913
|
+
else if (4 === qData.quadrant)
|
|
914
|
+
angle = Angle_1.Angle.pi2Radians - angle;
|
|
915
|
+
convertAndAddRadiansToFractionInRange(qFractions, angle, sweep, f0, f1);
|
|
916
|
+
}
|
|
917
|
+
const qf = QuadrantFractions.create(qData.quadrant, [...qFractions]);
|
|
918
|
+
const n = qf.fractions.length;
|
|
919
|
+
if (2 === n) { // e.g. elliptical arc is so small it contains no interior samples in this quadrant
|
|
920
|
+
qf.fractions.splice(1, 0, Geometry_1.Geometry.interpolate(qf.fractions[0], 0.5, qf.fractions[1]));
|
|
921
|
+
qf.averageAdded = true;
|
|
922
|
+
}
|
|
923
|
+
return qf;
|
|
924
|
+
};
|
|
925
|
+
const computeStructuredOutput = (anglesInQ1, arcSweep) => {
|
|
926
|
+
const qEndAngles = new core_bentley_1.OrderedSet(arcSweep.isCCW ? compareRadiansIncreasing : compareRadiansDecreasing);
|
|
927
|
+
qEndAngles.add(arcSweep.endRadians);
|
|
928
|
+
for (const qAngle of [0, Angle_1.Angle.piOver2Radians, Angle_1.Angle.piRadians, Angle_1.Angle.pi3Over2Radians, Angle_1.Angle.pi2Radians]) {
|
|
929
|
+
const shifted = shiftRadiansToSweep(qAngle, arcSweep);
|
|
930
|
+
if (shifted.inSweep)
|
|
931
|
+
qEndAngles.add(shifted.angle);
|
|
932
|
+
}
|
|
933
|
+
const quadrants = new core_bentley_1.OrderedSet(compareQuadrantFractions);
|
|
934
|
+
let a0 = arcSweep.startRadians;
|
|
935
|
+
for (const a1 of qEndAngles) {
|
|
936
|
+
const quadrant = convertQ1RadiansInSweepToQuadrantFractions(anglesInQ1, a0, a1, arcSweep);
|
|
937
|
+
if (quadrant)
|
|
938
|
+
quadrants.add(quadrant);
|
|
939
|
+
a0 = a1;
|
|
940
|
+
}
|
|
941
|
+
return [...quadrants];
|
|
942
|
+
};
|
|
943
|
+
const computeFlatOutput = (anglesInQ1, arcSweep) => {
|
|
944
|
+
// first add the quadrant fractions so the set prefers them over nearby interior fractions
|
|
945
|
+
const fractions = new core_bentley_1.OrderedSet(compareFractionsIncreasing);
|
|
946
|
+
fractions.add(0);
|
|
947
|
+
fractions.add(1);
|
|
948
|
+
for (const angle of [0, Angle_1.Angle.piOver2Radians, Angle_1.Angle.piRadians, Angle_1.Angle.pi3Over2Radians])
|
|
949
|
+
convertAndAddRadiansToFractionInRange(fractions, angle, arcSweep);
|
|
950
|
+
// add interior Q1 fractions, reflect to the other quadrants, filter by sweep and extant entry
|
|
951
|
+
for (const angle0 of anglesInQ1) {
|
|
952
|
+
for (const angle of [angle0, Angle_1.Angle.piRadians - angle0, Angle_1.Angle.piRadians + angle0, Angle_1.Angle.pi2Radians - angle0])
|
|
953
|
+
convertAndAddRadiansToFractionInRange(fractions, angle, arcSweep);
|
|
954
|
+
}
|
|
955
|
+
return [...fractions];
|
|
956
|
+
};
|
|
957
|
+
// sample the (full) ellipse as angles in strict interior of Quadrant 1
|
|
958
|
+
const radiansQ1 = []; // unordered
|
|
959
|
+
switch (options.sampleMethod) {
|
|
960
|
+
case Arc3d_1.EllipticalArcSampleMethod.UniformParameter: {
|
|
961
|
+
UniformParameterSampler.create(this, options).computeRadiansStrictlyInsideQuadrant1(radiansQ1);
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
case Arc3d_1.EllipticalArcSampleMethod.UniformCurvature: {
|
|
965
|
+
UniformCurvatureSampler.create(this, options).computeRadiansStrictlyInsideQuadrant1(radiansQ1);
|
|
966
|
+
break;
|
|
967
|
+
}
|
|
968
|
+
case Arc3d_1.EllipticalArcSampleMethod.NonUniformCurvature: {
|
|
969
|
+
NonUniformCurvatureSampler.create(this, options).computeRadiansStrictlyInsideQuadrant1(radiansQ1);
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
case Arc3d_1.EllipticalArcSampleMethod.AdaptiveSubdivision: {
|
|
973
|
+
AdaptiveSubdivisionSampler.create(this, options).computeRadiansStrictlyInsideQuadrant1(radiansQ1);
|
|
974
|
+
break;
|
|
975
|
+
}
|
|
976
|
+
default:
|
|
977
|
+
break;
|
|
978
|
+
}
|
|
979
|
+
return structuredOutput ?
|
|
980
|
+
computeStructuredOutput(radiansQ1, this.ellipticalArc.sweep) :
|
|
981
|
+
computeFlatOutput(radiansQ1, this.ellipticalArc.sweep);
|
|
982
|
+
}
|
|
983
|
+
/** Construct a circular arc chain approximation to the elliptical arc. */
|
|
984
|
+
constructCircularArcChainApproximation(options) {
|
|
985
|
+
if (!this.isValidEllipticalArc)
|
|
986
|
+
return undefined;
|
|
987
|
+
if (!options)
|
|
988
|
+
options = Arc3d_1.EllipticalArcApproximationOptions.create();
|
|
989
|
+
const processor = ArcChainConstructionProcessor.create(this.ellipticalArc, options.forcePath);
|
|
990
|
+
const samples = this.computeSampleFractions(options, true);
|
|
991
|
+
EllipticalArcApproximationContext.processQuadrantFractions(this.ellipticalArc, samples, processor);
|
|
992
|
+
return processor.chain;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
exports.EllipticalArcApproximationContext = EllipticalArcApproximationContext;
|
|
996
|
+
EllipticalArcApproximationContext.workPt0 = Point3dVector3d_1.Point3d.createZero();
|
|
997
|
+
EllipticalArcApproximationContext.workPt1 = Point3dVector3d_1.Point3d.createZero();
|
|
998
|
+
EllipticalArcApproximationContext.workPt2 = Point3dVector3d_1.Point3d.createZero();
|
|
999
|
+
EllipticalArcApproximationContext.workRay = Ray3d_1.Ray3d.createZero();
|
|
1000
|
+
//# sourceMappingURL=EllipticalArcApproximationContext.js.map
|