@itwin/core-geometry 4.0.0-dev.4 → 4.0.0-dev.41
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 +17 -3
- package/lib/cjs/Geometry.d.ts +56 -16
- package/lib/cjs/Geometry.d.ts.map +1 -1
- package/lib/cjs/Geometry.js +87 -30
- package/lib/cjs/Geometry.js.map +1 -1
- package/lib/cjs/bspline/BSplineCurveOps.js +4 -5
- package/lib/cjs/bspline/BSplineCurveOps.js.map +1 -1
- package/lib/cjs/bspline/InterpolationCurve3d.js +7 -10
- package/lib/cjs/bspline/InterpolationCurve3d.js.map +1 -1
- package/lib/cjs/clipping/ClipPrimitive.js +2 -2
- package/lib/cjs/clipping/ClipPrimitive.js.map +1 -1
- package/lib/cjs/clipping/ClipUtils.js +4 -4
- package/lib/cjs/clipping/ClipUtils.js.map +1 -1
- package/lib/cjs/clipping/internalContexts/LineStringOffsetClipperContext.js +2 -2
- package/lib/cjs/clipping/internalContexts/LineStringOffsetClipperContext.js.map +1 -1
- package/lib/cjs/curve/CurveChainWithDistanceIndex.d.ts.map +1 -1
- package/lib/cjs/curve/CurveChainWithDistanceIndex.js +20 -13
- package/lib/cjs/curve/CurveChainWithDistanceIndex.js.map +1 -1
- package/lib/cjs/curve/CurveCollection.js +1 -1
- package/lib/cjs/curve/CurveCollection.js.map +1 -1
- package/lib/cjs/curve/CurveCurve.d.ts +11 -8
- package/lib/cjs/curve/CurveCurve.d.ts.map +1 -1
- package/lib/cjs/curve/CurveCurve.js +16 -12
- package/lib/cjs/curve/CurveCurve.js.map +1 -1
- package/lib/cjs/curve/CurveCurveIntersectXY.d.ts +5 -1
- package/lib/cjs/curve/CurveCurveIntersectXY.d.ts.map +1 -1
- package/lib/cjs/curve/CurveCurveIntersectXY.js +11 -10
- package/lib/cjs/curve/CurveCurveIntersectXY.js.map +1 -1
- package/lib/cjs/curve/Loop.d.ts +2 -2
- package/lib/cjs/curve/Loop.d.ts.map +1 -1
- package/lib/cjs/curve/Loop.js +6 -0
- package/lib/cjs/curve/Loop.js.map +1 -1
- package/lib/cjs/curve/RegionOps.d.ts +10 -10
- package/lib/cjs/curve/RegionOps.d.ts.map +1 -1
- package/lib/cjs/curve/RegionOps.js +11 -11
- package/lib/cjs/curve/RegionOps.js.map +1 -1
- package/lib/cjs/curve/RegionOpsClassificationSweeps.d.ts +2 -1
- package/lib/cjs/curve/RegionOpsClassificationSweeps.d.ts.map +1 -1
- package/lib/cjs/curve/RegionOpsClassificationSweeps.js +5 -2
- package/lib/cjs/curve/RegionOpsClassificationSweeps.js.map +1 -1
- package/lib/cjs/curve/StrokeOptions.js +1 -2
- package/lib/cjs/curve/StrokeOptions.js.map +1 -1
- package/lib/cjs/curve/spiral/DirectSpiral3d.js +1 -2
- package/lib/cjs/curve/spiral/DirectSpiral3d.js.map +1 -1
- package/lib/cjs/curve/spiral/IntegratedSpiral3d.js +1 -2
- package/lib/cjs/curve/spiral/IntegratedSpiral3d.js.map +1 -1
- package/lib/cjs/geometry3d/Angle.d.ts +19 -0
- package/lib/cjs/geometry3d/Angle.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Angle.js +39 -0
- package/lib/cjs/geometry3d/Angle.js.map +1 -1
- package/lib/cjs/geometry3d/AngleSweep.d.ts +1 -0
- package/lib/cjs/geometry3d/AngleSweep.d.ts.map +1 -1
- package/lib/cjs/geometry3d/AngleSweep.js +1 -0
- package/lib/cjs/geometry3d/AngleSweep.js.map +1 -1
- package/lib/cjs/geometry3d/BarycentricTriangle.d.ts +195 -8
- package/lib/cjs/geometry3d/BarycentricTriangle.d.ts.map +1 -1
- package/lib/cjs/geometry3d/BarycentricTriangle.js +459 -11
- package/lib/cjs/geometry3d/BarycentricTriangle.js.map +1 -1
- package/lib/cjs/geometry3d/CoincidentGeometryOps.d.ts +1 -0
- package/lib/cjs/geometry3d/CoincidentGeometryOps.d.ts.map +1 -1
- package/lib/cjs/geometry3d/CoincidentGeometryOps.js +3 -0
- package/lib/cjs/geometry3d/CoincidentGeometryOps.js.map +1 -1
- package/lib/cjs/geometry3d/GrowableFloat64Array.js +2 -2
- package/lib/cjs/geometry3d/GrowableFloat64Array.js.map +1 -1
- package/lib/cjs/geometry3d/GrowableXYArray.d.ts +1 -1
- package/lib/cjs/geometry3d/GrowableXYArray.d.ts.map +1 -1
- package/lib/cjs/geometry3d/GrowableXYArray.js +2 -2
- package/lib/cjs/geometry3d/GrowableXYArray.js.map +1 -1
- package/lib/cjs/geometry3d/GrowableXYZArray.js +1 -1
- package/lib/cjs/geometry3d/GrowableXYZArray.js.map +1 -1
- package/lib/cjs/geometry3d/IndexedXYCollection.d.ts +22 -7
- package/lib/cjs/geometry3d/IndexedXYCollection.d.ts.map +1 -1
- package/lib/cjs/geometry3d/IndexedXYCollection.js +41 -5
- package/lib/cjs/geometry3d/IndexedXYCollection.js.map +1 -1
- package/lib/cjs/geometry3d/IndexedXYZCollection.d.ts +58 -4
- package/lib/cjs/geometry3d/IndexedXYZCollection.d.ts.map +1 -1
- package/lib/cjs/geometry3d/IndexedXYZCollection.js +102 -4
- package/lib/cjs/geometry3d/IndexedXYZCollection.js.map +1 -1
- package/lib/cjs/geometry3d/Matrix3d.d.ts +479 -265
- package/lib/cjs/geometry3d/Matrix3d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Matrix3d.js +996 -784
- package/lib/cjs/geometry3d/Matrix3d.js.map +1 -1
- package/lib/cjs/geometry3d/OrderedRotationAngles.d.ts +1 -0
- package/lib/cjs/geometry3d/OrderedRotationAngles.d.ts.map +1 -1
- package/lib/cjs/geometry3d/OrderedRotationAngles.js +1 -0
- package/lib/cjs/geometry3d/OrderedRotationAngles.js.map +1 -1
- package/lib/cjs/geometry3d/Point2dArrayCarrier.d.ts +10 -0
- package/lib/cjs/geometry3d/Point2dArrayCarrier.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Point2dArrayCarrier.js +14 -0
- package/lib/cjs/geometry3d/Point2dArrayCarrier.js.map +1 -1
- package/lib/cjs/geometry3d/Point2dVector2d.js +4 -6
- package/lib/cjs/geometry3d/Point2dVector2d.js.map +1 -1
- package/lib/cjs/geometry3d/Point3dArrayCarrier.d.ts +0 -6
- package/lib/cjs/geometry3d/Point3dArrayCarrier.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Point3dArrayCarrier.js +0 -6
- package/lib/cjs/geometry3d/Point3dArrayCarrier.js.map +1 -1
- package/lib/cjs/geometry3d/Point3dVector3d.d.ts +57 -57
- package/lib/cjs/geometry3d/Point3dVector3d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Point3dVector3d.js +63 -65
- package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
- package/lib/cjs/geometry3d/PointHelpers.d.ts +14 -1
- package/lib/cjs/geometry3d/PointHelpers.d.ts.map +1 -1
- package/lib/cjs/geometry3d/PointHelpers.js +33 -1
- package/lib/cjs/geometry3d/PointHelpers.js.map +1 -1
- package/lib/cjs/geometry3d/PolygonOps.d.ts +127 -19
- package/lib/cjs/geometry3d/PolygonOps.d.ts.map +1 -1
- package/lib/cjs/geometry3d/PolygonOps.js +420 -22
- package/lib/cjs/geometry3d/PolygonOps.js.map +1 -1
- package/lib/cjs/geometry3d/Ray3d.js +1 -1
- package/lib/cjs/geometry3d/Ray3d.js.map +1 -1
- package/lib/cjs/geometry3d/Segment1d.d.ts +1 -1
- package/lib/cjs/geometry3d/Segment1d.js +1 -1
- package/lib/cjs/geometry3d/Segment1d.js.map +1 -1
- package/lib/cjs/numerics/Polynomials.d.ts +12 -0
- package/lib/cjs/numerics/Polynomials.d.ts.map +1 -1
- package/lib/cjs/numerics/Polynomials.js +14 -0
- package/lib/cjs/numerics/Polynomials.js.map +1 -1
- package/lib/cjs/polyface/AuxData.js +1 -1
- package/lib/cjs/polyface/AuxData.js.map +1 -1
- package/lib/cjs/polyface/FacetLocationDetail.d.ts +264 -0
- package/lib/cjs/polyface/FacetLocationDetail.d.ts.map +1 -0
- package/lib/cjs/polyface/FacetLocationDetail.js +376 -0
- package/lib/cjs/polyface/FacetLocationDetail.js.map +1 -0
- package/lib/cjs/polyface/IndexedPolyfaceVisitor.d.ts +2 -5
- package/lib/cjs/polyface/IndexedPolyfaceVisitor.d.ts.map +1 -1
- package/lib/cjs/polyface/IndexedPolyfaceVisitor.js +5 -2
- package/lib/cjs/polyface/IndexedPolyfaceVisitor.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceBuilder.d.ts +24 -14
- package/lib/cjs/polyface/PolyfaceBuilder.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceBuilder.js +74 -23
- package/lib/cjs/polyface/PolyfaceBuilder.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceClip.js +6 -7
- package/lib/cjs/polyface/PolyfaceClip.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceData.d.ts +1 -1
- package/lib/cjs/polyface/PolyfaceData.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceData.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceQuery.d.ts +76 -1
- package/lib/cjs/polyface/PolyfaceQuery.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceQuery.js +123 -3
- package/lib/cjs/polyface/PolyfaceQuery.js.map +1 -1
- package/lib/cjs/polyface/multiclip/OffsetMeshContext.d.ts +202 -0
- package/lib/cjs/polyface/multiclip/OffsetMeshContext.d.ts.map +1 -0
- package/lib/cjs/polyface/multiclip/OffsetMeshContext.js +1038 -0
- package/lib/cjs/polyface/multiclip/OffsetMeshContext.js.map +1 -0
- package/lib/cjs/serialization/BGFBReader.js +4 -4
- package/lib/cjs/serialization/BGFBReader.js.map +1 -1
- package/lib/cjs/serialization/GeometrySamples.d.ts +8 -6
- package/lib/cjs/serialization/GeometrySamples.d.ts.map +1 -1
- package/lib/cjs/serialization/GeometrySamples.js +26 -19
- package/lib/cjs/serialization/GeometrySamples.js.map +1 -1
- package/lib/cjs/serialization/IModelJsonSchema.js +1 -2
- package/lib/cjs/serialization/IModelJsonSchema.js.map +1 -1
- package/lib/cjs/solid/Sphere.d.ts +5 -5
- package/lib/cjs/solid/Sphere.js +5 -5
- package/lib/cjs/solid/Sphere.js.map +1 -1
- package/lib/cjs/solid/SweepContour.d.ts.map +1 -1
- package/lib/cjs/solid/SweepContour.js +8 -1
- package/lib/cjs/solid/SweepContour.js.map +1 -1
- package/lib/cjs/topology/Graph.d.ts +113 -7
- package/lib/cjs/topology/Graph.d.ts.map +1 -1
- package/lib/cjs/topology/Graph.js +185 -7
- package/lib/cjs/topology/Graph.js.map +1 -1
- package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts +38 -0
- package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts.map +1 -0
- package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.js +82 -0
- package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.js.map +1 -0
- package/lib/cjs/topology/HalfEdgeGraphSearch.d.ts +2 -1
- package/lib/cjs/topology/HalfEdgeGraphSearch.d.ts.map +1 -1
- package/lib/cjs/topology/HalfEdgeGraphSearch.js +1 -0
- package/lib/cjs/topology/HalfEdgeGraphSearch.js.map +1 -1
- package/lib/cjs/topology/Triangulation.js +1 -1
- package/lib/cjs/topology/Triangulation.js.map +1 -1
- package/lib/esm/Geometry.d.ts +56 -16
- package/lib/esm/Geometry.d.ts.map +1 -1
- package/lib/esm/Geometry.js +86 -29
- package/lib/esm/Geometry.js.map +1 -1
- package/lib/esm/bspline/BSplineCurveOps.js +4 -5
- package/lib/esm/bspline/BSplineCurveOps.js.map +1 -1
- package/lib/esm/bspline/InterpolationCurve3d.js +7 -10
- package/lib/esm/bspline/InterpolationCurve3d.js.map +1 -1
- package/lib/esm/clipping/ClipPrimitive.js +2 -2
- package/lib/esm/clipping/ClipPrimitive.js.map +1 -1
- package/lib/esm/clipping/ClipUtils.js +4 -4
- package/lib/esm/clipping/ClipUtils.js.map +1 -1
- package/lib/esm/clipping/internalContexts/LineStringOffsetClipperContext.js +2 -2
- package/lib/esm/clipping/internalContexts/LineStringOffsetClipperContext.js.map +1 -1
- package/lib/esm/curve/CurveChainWithDistanceIndex.d.ts.map +1 -1
- package/lib/esm/curve/CurveChainWithDistanceIndex.js +21 -14
- package/lib/esm/curve/CurveChainWithDistanceIndex.js.map +1 -1
- package/lib/esm/curve/CurveCollection.js +1 -1
- package/lib/esm/curve/CurveCollection.js.map +1 -1
- package/lib/esm/curve/CurveCurve.d.ts +11 -8
- package/lib/esm/curve/CurveCurve.d.ts.map +1 -1
- package/lib/esm/curve/CurveCurve.js +16 -12
- package/lib/esm/curve/CurveCurve.js.map +1 -1
- package/lib/esm/curve/CurveCurveIntersectXY.d.ts +5 -1
- package/lib/esm/curve/CurveCurveIntersectXY.d.ts.map +1 -1
- package/lib/esm/curve/CurveCurveIntersectXY.js +11 -10
- package/lib/esm/curve/CurveCurveIntersectXY.js.map +1 -1
- package/lib/esm/curve/Loop.d.ts +2 -2
- package/lib/esm/curve/Loop.d.ts.map +1 -1
- package/lib/esm/curve/Loop.js +6 -0
- package/lib/esm/curve/Loop.js.map +1 -1
- package/lib/esm/curve/RegionOps.d.ts +10 -10
- package/lib/esm/curve/RegionOps.d.ts.map +1 -1
- package/lib/esm/curve/RegionOps.js +11 -11
- package/lib/esm/curve/RegionOps.js.map +1 -1
- package/lib/esm/curve/RegionOpsClassificationSweeps.d.ts +2 -1
- package/lib/esm/curve/RegionOpsClassificationSweeps.d.ts.map +1 -1
- package/lib/esm/curve/RegionOpsClassificationSweeps.js +5 -2
- package/lib/esm/curve/RegionOpsClassificationSweeps.js.map +1 -1
- package/lib/esm/curve/StrokeOptions.js +1 -2
- package/lib/esm/curve/StrokeOptions.js.map +1 -1
- package/lib/esm/curve/spiral/DirectSpiral3d.js +1 -2
- package/lib/esm/curve/spiral/DirectSpiral3d.js.map +1 -1
- package/lib/esm/curve/spiral/IntegratedSpiral3d.js +1 -2
- package/lib/esm/curve/spiral/IntegratedSpiral3d.js.map +1 -1
- package/lib/esm/geometry3d/Angle.d.ts +19 -0
- package/lib/esm/geometry3d/Angle.d.ts.map +1 -1
- package/lib/esm/geometry3d/Angle.js +39 -0
- package/lib/esm/geometry3d/Angle.js.map +1 -1
- package/lib/esm/geometry3d/AngleSweep.d.ts +1 -0
- package/lib/esm/geometry3d/AngleSweep.d.ts.map +1 -1
- package/lib/esm/geometry3d/AngleSweep.js +1 -0
- package/lib/esm/geometry3d/AngleSweep.js.map +1 -1
- package/lib/esm/geometry3d/BarycentricTriangle.d.ts +195 -8
- package/lib/esm/geometry3d/BarycentricTriangle.d.ts.map +1 -1
- package/lib/esm/geometry3d/BarycentricTriangle.js +459 -12
- package/lib/esm/geometry3d/BarycentricTriangle.js.map +1 -1
- package/lib/esm/geometry3d/CoincidentGeometryOps.d.ts +1 -0
- package/lib/esm/geometry3d/CoincidentGeometryOps.d.ts.map +1 -1
- package/lib/esm/geometry3d/CoincidentGeometryOps.js +3 -0
- package/lib/esm/geometry3d/CoincidentGeometryOps.js.map +1 -1
- package/lib/esm/geometry3d/GrowableFloat64Array.js +2 -2
- package/lib/esm/geometry3d/GrowableFloat64Array.js.map +1 -1
- package/lib/esm/geometry3d/GrowableXYArray.d.ts +1 -1
- package/lib/esm/geometry3d/GrowableXYArray.d.ts.map +1 -1
- package/lib/esm/geometry3d/GrowableXYArray.js +2 -2
- package/lib/esm/geometry3d/GrowableXYArray.js.map +1 -1
- package/lib/esm/geometry3d/GrowableXYZArray.js +1 -1
- package/lib/esm/geometry3d/GrowableXYZArray.js.map +1 -1
- package/lib/esm/geometry3d/IndexedXYCollection.d.ts +22 -7
- package/lib/esm/geometry3d/IndexedXYCollection.d.ts.map +1 -1
- package/lib/esm/geometry3d/IndexedXYCollection.js +41 -5
- package/lib/esm/geometry3d/IndexedXYCollection.js.map +1 -1
- package/lib/esm/geometry3d/IndexedXYZCollection.d.ts +58 -4
- package/lib/esm/geometry3d/IndexedXYZCollection.d.ts.map +1 -1
- package/lib/esm/geometry3d/IndexedXYZCollection.js +103 -5
- package/lib/esm/geometry3d/IndexedXYZCollection.js.map +1 -1
- package/lib/esm/geometry3d/Matrix3d.d.ts +479 -265
- package/lib/esm/geometry3d/Matrix3d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Matrix3d.js +996 -784
- package/lib/esm/geometry3d/Matrix3d.js.map +1 -1
- package/lib/esm/geometry3d/OrderedRotationAngles.d.ts +1 -0
- package/lib/esm/geometry3d/OrderedRotationAngles.d.ts.map +1 -1
- package/lib/esm/geometry3d/OrderedRotationAngles.js +1 -0
- package/lib/esm/geometry3d/OrderedRotationAngles.js.map +1 -1
- package/lib/esm/geometry3d/Point2dArrayCarrier.d.ts +10 -0
- package/lib/esm/geometry3d/Point2dArrayCarrier.d.ts.map +1 -1
- package/lib/esm/geometry3d/Point2dArrayCarrier.js +14 -0
- package/lib/esm/geometry3d/Point2dArrayCarrier.js.map +1 -1
- package/lib/esm/geometry3d/Point2dVector2d.js +4 -6
- package/lib/esm/geometry3d/Point2dVector2d.js.map +1 -1
- package/lib/esm/geometry3d/Point3dArrayCarrier.d.ts +0 -6
- package/lib/esm/geometry3d/Point3dArrayCarrier.d.ts.map +1 -1
- package/lib/esm/geometry3d/Point3dArrayCarrier.js +0 -6
- package/lib/esm/geometry3d/Point3dArrayCarrier.js.map +1 -1
- package/lib/esm/geometry3d/Point3dVector3d.d.ts +57 -57
- package/lib/esm/geometry3d/Point3dVector3d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Point3dVector3d.js +63 -65
- package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
- package/lib/esm/geometry3d/PointHelpers.d.ts +14 -1
- package/lib/esm/geometry3d/PointHelpers.d.ts.map +1 -1
- package/lib/esm/geometry3d/PointHelpers.js +33 -1
- package/lib/esm/geometry3d/PointHelpers.js.map +1 -1
- package/lib/esm/geometry3d/PolygonOps.d.ts +127 -19
- package/lib/esm/geometry3d/PolygonOps.d.ts.map +1 -1
- package/lib/esm/geometry3d/PolygonOps.js +419 -22
- package/lib/esm/geometry3d/PolygonOps.js.map +1 -1
- package/lib/esm/geometry3d/Ray3d.js +1 -1
- package/lib/esm/geometry3d/Ray3d.js.map +1 -1
- package/lib/esm/geometry3d/Segment1d.d.ts +1 -1
- package/lib/esm/geometry3d/Segment1d.js +1 -1
- package/lib/esm/geometry3d/Segment1d.js.map +1 -1
- package/lib/esm/numerics/Polynomials.d.ts +12 -0
- package/lib/esm/numerics/Polynomials.d.ts.map +1 -1
- package/lib/esm/numerics/Polynomials.js +14 -0
- package/lib/esm/numerics/Polynomials.js.map +1 -1
- package/lib/esm/polyface/AuxData.js +1 -1
- package/lib/esm/polyface/AuxData.js.map +1 -1
- package/lib/esm/polyface/FacetLocationDetail.d.ts +264 -0
- package/lib/esm/polyface/FacetLocationDetail.d.ts.map +1 -0
- package/lib/esm/polyface/FacetLocationDetail.js +369 -0
- package/lib/esm/polyface/FacetLocationDetail.js.map +1 -0
- package/lib/esm/polyface/IndexedPolyfaceVisitor.d.ts +2 -5
- package/lib/esm/polyface/IndexedPolyfaceVisitor.d.ts.map +1 -1
- package/lib/esm/polyface/IndexedPolyfaceVisitor.js +5 -2
- package/lib/esm/polyface/IndexedPolyfaceVisitor.js.map +1 -1
- package/lib/esm/polyface/PolyfaceBuilder.d.ts +24 -14
- package/lib/esm/polyface/PolyfaceBuilder.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceBuilder.js +74 -23
- package/lib/esm/polyface/PolyfaceBuilder.js.map +1 -1
- package/lib/esm/polyface/PolyfaceClip.js +6 -7
- package/lib/esm/polyface/PolyfaceClip.js.map +1 -1
- package/lib/esm/polyface/PolyfaceData.d.ts +1 -1
- package/lib/esm/polyface/PolyfaceData.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceData.js.map +1 -1
- package/lib/esm/polyface/PolyfaceQuery.d.ts +76 -1
- package/lib/esm/polyface/PolyfaceQuery.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceQuery.js +121 -2
- package/lib/esm/polyface/PolyfaceQuery.js.map +1 -1
- package/lib/esm/polyface/multiclip/OffsetMeshContext.d.ts +202 -0
- package/lib/esm/polyface/multiclip/OffsetMeshContext.d.ts.map +1 -0
- package/lib/esm/polyface/multiclip/OffsetMeshContext.js +1032 -0
- package/lib/esm/polyface/multiclip/OffsetMeshContext.js.map +1 -0
- package/lib/esm/serialization/BGFBReader.js +4 -4
- package/lib/esm/serialization/BGFBReader.js.map +1 -1
- package/lib/esm/serialization/GeometrySamples.d.ts +8 -6
- package/lib/esm/serialization/GeometrySamples.d.ts.map +1 -1
- package/lib/esm/serialization/GeometrySamples.js +26 -19
- package/lib/esm/serialization/GeometrySamples.js.map +1 -1
- package/lib/esm/serialization/IModelJsonSchema.js +1 -2
- package/lib/esm/serialization/IModelJsonSchema.js.map +1 -1
- package/lib/esm/solid/Sphere.d.ts +5 -5
- package/lib/esm/solid/Sphere.js +5 -5
- package/lib/esm/solid/Sphere.js.map +1 -1
- package/lib/esm/solid/SweepContour.d.ts.map +1 -1
- package/lib/esm/solid/SweepContour.js +8 -1
- package/lib/esm/solid/SweepContour.js.map +1 -1
- package/lib/esm/topology/Graph.d.ts +113 -7
- package/lib/esm/topology/Graph.d.ts.map +1 -1
- package/lib/esm/topology/Graph.js +185 -7
- package/lib/esm/topology/Graph.js.map +1 -1
- package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts +38 -0
- package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts.map +1 -0
- package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.js +78 -0
- package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.js.map +1 -0
- package/lib/esm/topology/HalfEdgeGraphSearch.d.ts +2 -1
- package/lib/esm/topology/HalfEdgeGraphSearch.d.ts.map +1 -1
- package/lib/esm/topology/HalfEdgeGraphSearch.js +1 -0
- package/lib/esm/topology/HalfEdgeGraphSearch.js.map +1 -1
- package/lib/esm/topology/Triangulation.js +1 -1
- package/lib/esm/topology/Triangulation.js.map +1 -1
- package/package.json +5 -5
|
@@ -12,6 +12,7 @@ import { Point2d } from "./Point2dVector2d";
|
|
|
12
12
|
import { Point3d, Vector3d } from "./Point3dVector3d";
|
|
13
13
|
import { Transform } from "./Transform";
|
|
14
14
|
/* eslint-disable @itwin/prefer-get */
|
|
15
|
+
// cSpell:words XXYZ YXYZ ZXYZ SaeedTorabi arctan newcommand diagonalization
|
|
15
16
|
/**
|
|
16
17
|
* PackedMatrix3dOps contains static methods for matrix operations where the matrix is a Float64Array.
|
|
17
18
|
* * The Float64Array contains the matrix entries in row-major order
|
|
@@ -307,7 +308,7 @@ export class Matrix3d {
|
|
|
307
308
|
/**
|
|
308
309
|
* Here we rotate this.columnX() around this.columnZ() by "angle" and expect to get other.columnX().
|
|
309
310
|
* Then we rotate this.columnY() around this.columnZ() by the same "angle" and if we get other.columnY(),
|
|
310
|
-
* that means this` and `other` have X and Y columns differing only by a rotation around
|
|
311
|
+
* that means `this` and `other` have X and Y columns differing only by a rotation around column Z.
|
|
311
312
|
*/
|
|
312
313
|
let column = Vector3d.createRotateVectorAroundVector(columnX, columnZ, angle);
|
|
313
314
|
if (other.isAlmostEqualColumnXYZ(0, column.x, column.y, column.z, tol)) {
|
|
@@ -421,9 +422,20 @@ export class Matrix3d {
|
|
|
421
422
|
return result;
|
|
422
423
|
}
|
|
423
424
|
/**
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
425
|
+
* Create the inverseCoffs member (filled with zeros)
|
|
426
|
+
* This is for use by matrix * matrix multiplications which need to be sure the member is there to be
|
|
427
|
+
* filled with method-specific content.
|
|
428
|
+
*/
|
|
429
|
+
createInverseCoffsWithZeros() {
|
|
430
|
+
if (!this.inverseCoffs) {
|
|
431
|
+
this.inverseState = InverseMatrixState.unknown;
|
|
432
|
+
this.inverseCoffs = new Float64Array(9);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Copy the transpose of the coffs to the inverseCoffs.
|
|
437
|
+
* * Mark the matrix as inverseStored.
|
|
438
|
+
*/
|
|
427
439
|
setupInverseTranspose() {
|
|
428
440
|
const coffs = this.coffs;
|
|
429
441
|
this.inverseState = InverseMatrixState.inverseStored;
|
|
@@ -639,8 +651,10 @@ export class Matrix3d {
|
|
|
639
651
|
return undefined;
|
|
640
652
|
}
|
|
641
653
|
/**
|
|
642
|
-
* Construct a rigid matrix using vectorA and its 2 perpendicular.
|
|
654
|
+
* Construct a rigid matrix (orthogonal matrix with +1 determinant) using vectorA and its 2 perpendicular.
|
|
655
|
+
* * If axisOrder is not passed then `AxisOrder = AxisOrder.ZXY` is used as default.
|
|
643
656
|
* * This function internally uses createPerpendicularVectorFavorXYPlane and createRigidFromColumns.
|
|
657
|
+
* * Visualization can be found at https://www.itwinjs.org/sandbox/SaeedTorabi/2PerpendicularVectorsTo1Vector
|
|
644
658
|
*/
|
|
645
659
|
static createRigidHeadsUp(vectorA, axisOrder = AxisOrder.ZXY, result) {
|
|
646
660
|
const vectorB = Matrix3d.createPerpendicularVectorFavorXYPlane(vectorA);
|
|
@@ -651,7 +665,14 @@ export class Matrix3d {
|
|
|
651
665
|
}
|
|
652
666
|
return Matrix3d.createIdentity(result);
|
|
653
667
|
}
|
|
654
|
-
/**
|
|
668
|
+
/**
|
|
669
|
+
* Return the matrix for rotation of `angle` around desired `axis`
|
|
670
|
+
* * Visualization can be found at https://www.itwinjs.org/sandbox/SaeedTorabi/CubeRotationAroundAnAxis
|
|
671
|
+
* @param axis the axis of rotation
|
|
672
|
+
* @param angle the angle of rotation
|
|
673
|
+
* @param result caller-allocated matrix (optional)
|
|
674
|
+
* @returns the `rotation matrix` or `undefined` (if axis magnitude is near zero).
|
|
675
|
+
*/
|
|
655
676
|
static createRotationAroundVector(axis, angle, result) {
|
|
656
677
|
// Rodriguez formula (matrix form), https://mathworld.wolfram.com/RodriguesRotationFormula.html
|
|
657
678
|
const c = angle.cos();
|
|
@@ -665,7 +686,7 @@ export class Matrix3d {
|
|
|
665
686
|
}
|
|
666
687
|
return undefined;
|
|
667
688
|
}
|
|
668
|
-
/** Returns a rotation of specified angle around
|
|
689
|
+
/** Returns a rotation of specified angle around one of the main axis (X,Y,Z).
|
|
669
690
|
* @param axisIndex index of axis (AxisIndex.X, AxisIndex.Y, AxisIndex.Z) kept fixed by the rotation.
|
|
670
691
|
* @param angle angle of rotation
|
|
671
692
|
* @param result optional result matrix.
|
|
@@ -688,14 +709,33 @@ export class Matrix3d {
|
|
|
688
709
|
return myResult;
|
|
689
710
|
}
|
|
690
711
|
/**
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
712
|
+
* Replace current rows Ui and Uj with (c*Ui + s*Uj) and (c*Uj - s*Ui).
|
|
713
|
+
* * There is no checking for i,j being 0,1,2.
|
|
714
|
+
* @param i first row index. **must be 0,1,2** (unchecked)
|
|
715
|
+
* @param j second row index. **must be 0,1,2** (unchecked)
|
|
716
|
+
* @param c fist coefficient
|
|
717
|
+
* @param s second coefficient
|
|
718
|
+
*/
|
|
719
|
+
applyGivensRowOp(i, j, c, s) {
|
|
720
|
+
let ii = 3 * i;
|
|
721
|
+
let jj = 3 * j;
|
|
722
|
+
const limit = ii + 3;
|
|
723
|
+
for (; ii < limit; ii++, jj++) {
|
|
724
|
+
const a = this.coffs[ii];
|
|
725
|
+
const b = this.coffs[jj];
|
|
726
|
+
this.coffs[ii] = a * c + b * s;
|
|
727
|
+
this.coffs[jj] = -a * s + b * c;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Replace current columns Ui and Uj with (c*Ui + s*Uj) and (c*Uj - s*Ui).
|
|
732
|
+
* * There is no checking for i,j being 0,1,2.
|
|
733
|
+
* * This is used in compute intensive inner loops
|
|
734
|
+
* @param i first row index. **must be 0,1,2** (unchecked)
|
|
735
|
+
* @param j second row index. **must be 0,1,2** (unchecked)
|
|
736
|
+
* @param c fist coefficient
|
|
737
|
+
* @param s second coefficient
|
|
738
|
+
*/
|
|
699
739
|
applyGivensColumnOp(i, j, c, s) {
|
|
700
740
|
const limit = i + 9;
|
|
701
741
|
for (; i < limit; i += 3, j += 3) {
|
|
@@ -706,12 +746,12 @@ export class Matrix3d {
|
|
|
706
746
|
}
|
|
707
747
|
}
|
|
708
748
|
/**
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
749
|
+
* Create a matrix from column vectors.
|
|
750
|
+
* ```
|
|
751
|
+
* equation
|
|
752
|
+
* \begin{bmatrix}U_x & V_x & W_x \\ U_y & V_y & W_y \\ U_z & V_z & W_z \end{bmatrix}
|
|
753
|
+
* ```
|
|
754
|
+
*/
|
|
715
755
|
static createColumns(vectorU, vectorV, vectorW, result) {
|
|
716
756
|
return Matrix3d.createRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, vectorU.z, vectorV.z, vectorW.z, result);
|
|
717
757
|
}
|
|
@@ -724,29 +764,36 @@ export class Matrix3d {
|
|
|
724
764
|
static createColumnsXYW(vectorU, u, vectorV, v, vectorW, w, result) {
|
|
725
765
|
return Matrix3d.createRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, u, v, w, result);
|
|
726
766
|
}
|
|
727
|
-
/**
|
|
728
|
-
*
|
|
729
|
-
* *
|
|
767
|
+
/**
|
|
768
|
+
* Create a matrix from "as viewed" right and up vectors.
|
|
769
|
+
* * ColumnX points in the rightVector direction.
|
|
770
|
+
* * ColumnY points in the upVector direction.
|
|
730
771
|
* * ColumnZ is a unit cross product of ColumnX and ColumnY.
|
|
731
|
-
* * Optionally rotate
|
|
732
|
-
* * Optionally rotate
|
|
733
|
-
*
|
|
772
|
+
* * Optionally rotate by 45 degrees around `upVector` to bring its left or right vertical edge to center.
|
|
773
|
+
* * Optionally rotate by arctan(1/sqrt(2)) ~ 35.264 degrees around `rightVector` to bring the top or bottom
|
|
774
|
+
* horizontal edge of the view to the center (for isometric views).
|
|
775
|
+
*
|
|
776
|
+
* This is expected to be used with various principal unit vectors that are perpendicular to each other.
|
|
734
777
|
* * STANDARD TOP VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitY(), 0, 0)
|
|
735
778
|
* * STANDARD FRONT VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitZ(), 0, 0)
|
|
736
779
|
* * STANDARD BACK VIEW: createViewedAxes(Vector3d.unitX(-1), Vector3d.unitZ(), 0, 0)
|
|
737
780
|
* * STANDARD RIGHT VIEW: createViewedAxes(Vector3d.unitY(), Vector3d.unitZ(), 0, 0)
|
|
738
781
|
* * STANDARD LEFT VIEW: createViewedAxes(Vector3d.unitY(-1), Vector3d.unitZ(), 0, 0)
|
|
739
782
|
* * STANDARD BOTTOM VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitY(-1), 0, 0)
|
|
783
|
+
* * STANDARD ISO VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitZ(), -1, 1)
|
|
784
|
+
* * STANDARD RIGHT ISO VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitZ(), 1, 1)
|
|
785
|
+
* * Front, right, back, left, top, and bottom standard views are views from faces of the cube
|
|
786
|
+
* and iso and right iso standard views are views from corners of the cube.
|
|
740
787
|
* * Note: createViewedAxes is column-based so always returns local to world
|
|
741
788
|
*
|
|
742
789
|
* @param rightVector ColumnX of the returned matrix. Expected to be perpendicular to upVector.
|
|
743
790
|
* @param upVector ColumnY of the returned matrix. Expected to be perpendicular to rightVector.
|
|
744
|
-
* @param leftNoneRight Specifies the ccw rotation around
|
|
745
|
-
* "-1" indicates rotation by 45 degrees to bring the left vertical edge to center, "0" means no rotation,
|
|
791
|
+
* @param leftNoneRight Specifies the ccw rotation around `upVector` axis. Normally one of "-1", "0", and "1",
|
|
792
|
+
* where "-1" indicates rotation by 45 degrees to bring the left vertical edge to center, "0" means no rotation,
|
|
746
793
|
* and "1" indicates rotation by 45 degrees to bring the right vertical edge to center. Other numbers are
|
|
747
794
|
* used as multiplier for this 45 degree rotation.
|
|
748
|
-
* @param topNoneBottom Specifies the ccw rotation around
|
|
749
|
-
* "-1" indicates isometric rotation (35.264 degrees) to bring the bottom upward, "0" means no rotation,
|
|
795
|
+
* @param topNoneBottom Specifies the ccw rotation around `rightVector` axis. Normally one of "-1", "0", and "1",
|
|
796
|
+
* where "-1" indicates isometric rotation (35.264 degrees) to bring the bottom upward, "0" means no rotation,
|
|
750
797
|
* and "1" indicates isometric rotation (35.264 degrees) to bring the top downward. Other numbers are
|
|
751
798
|
* used as multiplier for the 35.264 degree rotation.
|
|
752
799
|
* @returns matrix = [rightVector, upVector, rightVector cross upVector] with the applied rotations specified
|
|
@@ -784,9 +831,11 @@ export class Matrix3d {
|
|
|
784
831
|
* * Default is TOP view (`local X = world X`, `local Y = world Y`, `local Z = world Z`).
|
|
785
832
|
* * To change view from the TOP to one of the other 7 standard views, we need to multiply "world data" to
|
|
786
833
|
* the corresponding matrix1 provided by `createStandardWorldToView(index, false)` and then
|
|
787
|
-
* `matrix1.multiply(world data)` will
|
|
834
|
+
* `matrix1.multiply(world data)` will return "local data".
|
|
788
835
|
* * To change view back to the TOP, we need to multiply "local data" to the corresponding matrix2 provided
|
|
789
836
|
* by `createStandardWorldToView(index, true)` and then `matrix2.multiply(local data)` will returns "world data".
|
|
837
|
+
* * Note: No matter how you rotate the world axis, local X is always pointing right, local Y is always pointing up,
|
|
838
|
+
* and local Z is always pointing toward you.
|
|
790
839
|
*
|
|
791
840
|
* @param index standard view index `StandardViewIndex.Top, Bottom, Left, Right, Front, Back, Iso, RightIso`
|
|
792
841
|
* @param invert if false (default), the return matrix is world to local (view) and if true, the the return
|
|
@@ -795,200 +844,388 @@ export class Matrix3d {
|
|
|
795
844
|
*/
|
|
796
845
|
static createStandardWorldToView(index, invert = false, result) {
|
|
797
846
|
switch (index) {
|
|
798
|
-
//
|
|
847
|
+
// Start with TOP view, ccw rotation by 180 degrees around X
|
|
799
848
|
case StandardViewIndex.Bottom:
|
|
800
849
|
result = Matrix3d.createRowValues(1, 0, 0, 0, -1, 0, 0, 0, -1);
|
|
801
850
|
break;
|
|
802
|
-
//
|
|
851
|
+
// Start with TOP view, ccw rotation by -90 degrees around X and by 90 degrees around Z
|
|
803
852
|
case StandardViewIndex.Left:
|
|
804
853
|
result = Matrix3d.createRowValues(0, -1, 0, 0, 0, 1, -1, 0, 0);
|
|
805
854
|
break;
|
|
806
|
-
//
|
|
855
|
+
// Start with TOP view, ccw rotation by -90 degrees around X and by -90 degrees around Z
|
|
807
856
|
case StandardViewIndex.Right:
|
|
808
857
|
result = Matrix3d.createRowValues(0, 1, 0, 0, 0, 1, 1, 0, 0);
|
|
809
858
|
break;
|
|
810
|
-
//
|
|
859
|
+
// Start with TOP view, ccw rotation by -90 degrees around X
|
|
811
860
|
case StandardViewIndex.Front:
|
|
812
861
|
result = Matrix3d.createRowValues(1, 0, 0, 0, 0, 1, 0, -1, 0);
|
|
813
862
|
break;
|
|
814
|
-
//
|
|
863
|
+
// Start with TOP view, ccw rotation by -90 degrees around X and by 180 degrees around Z
|
|
815
864
|
case StandardViewIndex.Back:
|
|
816
865
|
result = Matrix3d.createRowValues(-1, 0, 0, 0, 0, 1, 0, 1, 0);
|
|
817
866
|
break;
|
|
867
|
+
/**
|
|
868
|
+
* Isometric view
|
|
869
|
+
* Start with FRONT view, ccw rotation by -45 degrees around Y and by arctan(1/sqrt(2)) ~ 35.264 degrees around X
|
|
870
|
+
* cos(45) = 1/sqrt(2) = 0.70710678118 and sin(45) = 1/sqrt(2) = 0.70710678118
|
|
871
|
+
* cos(35.264) = 2/sqrt(6) = 0.81649658092 and sin(35.264) = 1/sqrt(3) = 0.57735026919
|
|
872
|
+
* More info: https://en.wikipedia.org/wiki/Isometric_projection
|
|
873
|
+
*/
|
|
818
874
|
case StandardViewIndex.Iso:
|
|
819
|
-
// start with FRONT view, ccw rotation by -45 degrees around Y and by 35.264 degrees around X
|
|
820
875
|
result = Matrix3d.createRowValues(0.707106781186548, -0.70710678118654757, 0.00000000000000000, 0.408248290463863, 0.40824829046386302, 0.81649658092772603, -0.577350269189626, -0.57735026918962573, 0.57735026918962573);
|
|
821
876
|
break;
|
|
877
|
+
// Start with FRONT view, ccw rotation by 45 degrees around Y and by 35.264 degrees around X
|
|
822
878
|
case StandardViewIndex.RightIso:
|
|
823
879
|
result = Matrix3d.createRowValues(0.707106781186548, 0.70710678118654757, 0.00000000000000000, -0.408248290463863, 0.40824829046386302, 0.81649658092772603, 0.577350269189626, -0.57735026918962573, 0.57735026918962573);
|
|
824
880
|
break;
|
|
825
|
-
|
|
881
|
+
// no rotation
|
|
882
|
+
case StandardViewIndex.Top:
|
|
826
883
|
default:
|
|
827
884
|
result = Matrix3d.createIdentity(result);
|
|
828
885
|
}
|
|
829
886
|
if (invert)
|
|
830
|
-
result.transposeInPlace();
|
|
887
|
+
result.transposeInPlace(); // matrix is rigid so transpose and inverse are the same
|
|
831
888
|
return result;
|
|
832
889
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
890
|
+
/**
|
|
891
|
+
* Apply (in place) a jacobi eigenvalue algorithm.
|
|
892
|
+
* @param i row index of zeroed member
|
|
893
|
+
* @param j column index of zeroed member
|
|
894
|
+
* @param leftEigenvectors a matrix that its columns will be filled by the left eigenvectors of `this` Matrix3d
|
|
895
|
+
* (allocated by caller, computed and filled by this function). Note that columns of leftEigenVectors will be
|
|
896
|
+
* mutually perpendicular because `this` matrix is symmetric.
|
|
897
|
+
* @param lambda a matrix that its diagonal entries will be filled by eigenvalues and its non-diagonal elements
|
|
898
|
+
* converge to 0 (allocated by caller, computed and filled by this function).
|
|
899
|
+
*/
|
|
900
|
+
applySymmetricJacobi(i, j, leftEigenvectors, lambda) {
|
|
901
|
+
const sii = lambda.at(i, i);
|
|
902
|
+
const sjj = lambda.at(j, j);
|
|
903
|
+
const sij = lambda.at(i, j);
|
|
904
|
+
if (Math.abs(sij) < 1.0e-15 * (sii + sjj))
|
|
905
|
+
return 0.0;
|
|
906
|
+
const jacobi = Angle.trigValuesToHalfAngleTrigValues(sii - sjj, 2.0 * sij);
|
|
907
|
+
const c = jacobi.c;
|
|
908
|
+
const s = jacobi.s;
|
|
909
|
+
/**
|
|
910
|
+
* The following check does not exist in applyFastSymmetricJacobi because here if we don't return
|
|
911
|
+
* early, the matrix remains untouched. However, applyFastSymmetricJacobi zeroes-out elements ij
|
|
912
|
+
* and ji. Therefore, if we return early in applyFastSymmetricJacobi, zeroing-out wont happen.
|
|
913
|
+
*/
|
|
914
|
+
if (Math.abs(s) < 2.0e-15)
|
|
915
|
+
return 0.0;
|
|
916
|
+
/**
|
|
917
|
+
* If you apply the following 2 lines to a symmetric matrix, you get same lines used in
|
|
918
|
+
* applyFastSymmetricJacobi. There are 2 differences which make applyFastSymmetricJacobi
|
|
919
|
+
* more efficient. First, we directly set elements ij and ji equal to zero rather than
|
|
920
|
+
* calculation them. Second, we copy symmetric elements from upper triangle to lower
|
|
921
|
+
* instead of calculating them.
|
|
922
|
+
*/
|
|
923
|
+
lambda.applyGivensRowOp(i, j, c, s);
|
|
924
|
+
lambda.applyGivensColumnOp(i, j, c, s);
|
|
925
|
+
leftEigenvectors.applyGivensColumnOp(i, j, c, s);
|
|
926
|
+
return Math.abs(sij);
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Factor `this` matrix as a product `U * lambda * UT` where `U` is an orthogonal matrix and `lambda`
|
|
930
|
+
* is a diagonal matrix.
|
|
931
|
+
*
|
|
932
|
+
* * **Note 1:** You must apply this function to a `symmetric` matrix. Otherwise, the lower triangle is ignored
|
|
933
|
+
* and the upper triangle is mirrored to the lower triangle to enforce symmetry.
|
|
934
|
+
* * **Note 2:** This function is replaced by a faster method called `fastSymmetricEigenvalues` so consider
|
|
935
|
+
* using the fast version instead.
|
|
936
|
+
* @param leftEigenvectors a matrix that its columns will be filled by the left eigenvectors of `this` Matrix3d
|
|
937
|
+
* (allocated by caller, computed and filled by this function). Note that columns of leftEigenVectors will be
|
|
938
|
+
* mutually perpendicular because `this` matrix is symmetric.
|
|
939
|
+
* @param lambda a vector that its entries will be filled by eigenvalues of `this` Matrix3d (allocated by
|
|
940
|
+
* caller, computed and filled by this function).
|
|
941
|
+
*/
|
|
942
|
+
symmetricEigenvalues(leftEigenvectors, lambda) {
|
|
943
|
+
const matrix = this.clone();
|
|
944
|
+
leftEigenvectors.setIdentity();
|
|
945
|
+
matrix.coffs[3] = matrix.coffs[1];
|
|
946
|
+
matrix.coffs[6] = matrix.coffs[2];
|
|
947
|
+
matrix.coffs[7] = matrix.coffs[5];
|
|
948
|
+
const tolerance = 1.0e-12 * this.sumSquares();
|
|
949
|
+
const numberOfIterations = 7;
|
|
950
|
+
for (let iteration = 0; iteration < numberOfIterations; iteration++) {
|
|
951
|
+
const sum = this.applySymmetricJacobi(0, 1, leftEigenvectors, matrix)
|
|
952
|
+
+ this.applySymmetricJacobi(0, 2, leftEigenvectors, matrix)
|
|
953
|
+
+ this.applySymmetricJacobi(1, 2, leftEigenvectors, matrix);
|
|
954
|
+
if (sum < tolerance) {
|
|
955
|
+
lambda.set(matrix.at(0, 0), matrix.at(1, 1), matrix.at(2, 2));
|
|
956
|
+
return true;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return false;
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Apply (in place) a jacobi eigenvalue algorithm that diagonalize `this` matrix, i.e., zeros out this.at(i,j).
|
|
963
|
+
* * During diagonalization, the upper triangle is mirrored to lower triangle to enforce symmetry.
|
|
964
|
+
* * Math details can be found at docs/learning/geometry/Matrix.md
|
|
965
|
+
* @param i row index of zeroed member.
|
|
966
|
+
* @param j column index of zeroed member.
|
|
967
|
+
* @param k other row/column index (different from i and j).
|
|
968
|
+
* @param leftEigenVectors a matrix that its columns will be filled by the left eigenvectors of `this` Matrix3d
|
|
969
|
+
* (allocated by caller, computed and filled by this function). Note that columns of leftEigenVectors will be
|
|
970
|
+
* mutually perpendicular because `this` matrix is symmetric.
|
|
971
|
+
*/
|
|
972
|
+
applyFastSymmetricJacobi(i, j, k, leftEigenVectors) {
|
|
973
|
+
const indexII = 4 * i;
|
|
974
|
+
const indexJJ = 4 * j;
|
|
975
|
+
const indexIJ = 3 * i + j;
|
|
976
|
+
const indexJI = 3 * j + i;
|
|
977
|
+
const indexIK = 3 * i + k;
|
|
978
|
+
const indexKI = 3 * k + i;
|
|
979
|
+
const indexJK = 3 * j + k;
|
|
980
|
+
const indexKJ = 3 * k + j;
|
|
981
|
+
const sii = this.coffs[indexII];
|
|
982
|
+
const sjj = this.coffs[indexJJ];
|
|
983
|
+
const sij = this.coffs[indexIJ];
|
|
984
|
+
if (Math.abs(sij) < 1.0e-15 * (sii + sjj))
|
|
985
|
+
return 0.0;
|
|
986
|
+
const jacobi = Angle.trigValuesToHalfAngleTrigValues(sii - sjj, 2.0 * sij);
|
|
987
|
+
const c = jacobi.c;
|
|
988
|
+
const s = jacobi.s;
|
|
989
|
+
const cc = c * c;
|
|
990
|
+
const ss = s * s;
|
|
991
|
+
const sc2 = 2.0 * c * s;
|
|
992
|
+
this.coffs[indexII] = cc * sii + sc2 * sij + ss * sjj;
|
|
993
|
+
this.coffs[indexJJ] = ss * sii - sc2 * sij + cc * sjj;
|
|
994
|
+
this.coffs[indexIJ] = 0.0;
|
|
995
|
+
this.coffs[indexJI] = 0.0;
|
|
996
|
+
const a = this.coffs[indexIK];
|
|
997
|
+
const b = this.coffs[indexJK];
|
|
998
|
+
this.coffs[indexIK] = c * a + s * b;
|
|
999
|
+
this.coffs[indexJK] = -s * a + c * b;
|
|
1000
|
+
this.coffs[indexKI] = this.coffs[indexIK];
|
|
1001
|
+
this.coffs[indexKJ] = this.coffs[indexJK];
|
|
1002
|
+
leftEigenVectors.applyGivensColumnOp(i, j, c, s);
|
|
1003
|
+
return Math.abs(sij);
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Factor `this` matrix as a product `U * lambda * UT` where `U` is an orthogonal matrix and `lambda`
|
|
1007
|
+
* is a diagonal matrix.
|
|
1008
|
+
*
|
|
1009
|
+
* * **Note:** You must apply this function to a `symmetric` matrix. Otherwise, the lower triangle is ignored
|
|
1010
|
+
* and the upper triangle is mirrored to the lower triangle to enforce symmetry.
|
|
1011
|
+
* * Math details can be found at docs/learning/geometry/Matrix.md
|
|
1012
|
+
* @param leftEigenvectors a matrix that its columns will be filled by the left eigenvectors of `this` Matrix3d
|
|
1013
|
+
* (allocated by caller, computed and filled by this function). Note that columns of leftEigenVectors will be
|
|
1014
|
+
* mutually perpendicular because `this` matrix is symmetric.
|
|
1015
|
+
* @param lambda a vector that its entries will be filled by eigenvalues of `this` Matrix3d (allocated by
|
|
1016
|
+
* caller, computed and filled by this function).
|
|
1017
|
+
*/
|
|
1018
|
+
fastSymmetricEigenvalues(leftEigenvectors, lambda) {
|
|
1019
|
+
const matrix = this.clone();
|
|
1020
|
+
leftEigenvectors.setIdentity();
|
|
1021
|
+
const tolerance = 1.0e-12 * this.sumSquares();
|
|
1022
|
+
const numberOfIterations = 7;
|
|
1023
|
+
for (let iteration = 0; iteration < numberOfIterations; iteration++) {
|
|
1024
|
+
const sum = matrix.applyFastSymmetricJacobi(0, 1, 2, leftEigenvectors)
|
|
1025
|
+
+ matrix.applyFastSymmetricJacobi(0, 2, 1, leftEigenvectors)
|
|
1026
|
+
+ matrix.applyFastSymmetricJacobi(1, 2, 0, leftEigenvectors);
|
|
1027
|
+
if (sum < tolerance) {
|
|
1028
|
+
lambda.set(matrix.at(0, 0), matrix.at(1, 1), matrix.at(2, 2));
|
|
1029
|
+
return true;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
return false;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Compute the (unit vector) axis and angle for the rotation generated by `this` Matrix3d.
|
|
1036
|
+
* * Math details can be found at docs/learning/geometry/Angle.md
|
|
902
1037
|
* @returns Returns axis and angle of rotation with result.ok === true when the conversion succeeded.
|
|
903
1038
|
*/
|
|
904
1039
|
getAxisAndAngleOfRotation() {
|
|
905
1040
|
const trace = this.coffs[0] + this.coffs[4] + this.coffs[8];
|
|
906
|
-
|
|
907
|
-
const
|
|
908
|
-
const
|
|
909
|
-
|
|
910
|
-
const c = (trace - 1.0) / 2.0;
|
|
911
|
-
const s = Geometry.hypotenuseXYZ(skewXY, skewYZ, skewZX) / 2.0;
|
|
912
|
-
const e = c * c + s * s - 1.0;
|
|
1041
|
+
const skewXY = this.coffs[3] - this.coffs[1]; // 2*z*sin
|
|
1042
|
+
const skewYZ = this.coffs[7] - this.coffs[5]; // 2*y*sin
|
|
1043
|
+
const skewZX = this.coffs[2] - this.coffs[6]; // 2*x*sin
|
|
1044
|
+
// trace = (m00^2 + m11^2 + m22^2) * (1-cos) + 3cos = (1-cos) + 3cos = 1 + 2cos ==> cos = (trace-1) / 2
|
|
1045
|
+
const c = (trace - 1.0) / 2.0; // cosine
|
|
1046
|
+
const s = Geometry.hypotenuseXYZ(skewXY, skewYZ, skewZX) / 2.0; // sine
|
|
1047
|
+
const e = c * c + s * s - 1.0; // s^2 + c^2 = 1
|
|
1048
|
+
// if s^2 + c^2 != 1 then we have a bad matrix so return false
|
|
913
1049
|
if (Math.abs(e) > Geometry.smallAngleRadians) {
|
|
914
|
-
// the sine and cosine are not a unit circle point. bad matrix . ..
|
|
915
1050
|
return { axis: Vector3d.create(0, 0, 1), angle: Angle.createRadians(0), ok: false };
|
|
916
1051
|
}
|
|
1052
|
+
// sin is close to 0 then we got to special cases (angle 0 or 180) which needs to be handled differently
|
|
917
1053
|
if (Math.abs(s) < Geometry.smallAngleRadians) {
|
|
918
|
-
//
|
|
919
|
-
// The matrix is symmetric
|
|
920
|
-
// So it has simple eigenvalues -- either (1,1,1) or (1,-1,-1).
|
|
921
|
-
if (c > 0) // no rotation
|
|
1054
|
+
if (c > 0) // sin = 0 and cos = 1 so angle = 0 (i.e., no rotation)
|
|
922
1055
|
return { axis: Vector3d.create(0, 0, 1), angle: Angle.createRadians(0), ok: true };
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1056
|
+
/**
|
|
1057
|
+
* If sin = 0 and cos = -1 then angle = 180 (i.e., 180 degree rotation around some axis)
|
|
1058
|
+
* then the rotation matrix becomes
|
|
1059
|
+
* 2x^2-1 2xy 2xz
|
|
1060
|
+
* 2xy 2y^2-1 2yz
|
|
1061
|
+
* 2xz 2yz 2z^2-1
|
|
1062
|
+
* Note that the matrix is "symmetric".
|
|
1063
|
+
* If rotation is around one the standard basis then non-diagonal entries become 0 and we
|
|
1064
|
+
* have one 1 and two -1s on the diagonal.
|
|
1065
|
+
* If rotation is around an axis other than standard basis, then the axis is the eigenvector
|
|
1066
|
+
* of the rotation matrix with eigenvalue = 1.
|
|
1067
|
+
*/
|
|
926
1068
|
const axx = this.coffs[0];
|
|
927
1069
|
const ayy = this.coffs[4];
|
|
928
1070
|
const azz = this.coffs[8];
|
|
929
|
-
|
|
930
|
-
// Look for principal axis flips as a special case . ..
|
|
1071
|
+
// Look for a pair of "-1" entries on the diagonal (for rotation around the basis X,Y,Z axis)
|
|
931
1072
|
if (Geometry.isAlmostEqualNumber(-1.0, ayy) && Geometry.isAlmostEqualNumber(-1, azz)) {
|
|
932
|
-
|
|
933
|
-
return { axis: Vector3d.create(1, 0, 0), angle: theta180, ok: true };
|
|
1073
|
+
return { axis: Vector3d.create(1, 0, 0), angle: Angle.createDegrees(180), ok: true };
|
|
934
1074
|
}
|
|
935
1075
|
else if (Geometry.isAlmostEqualNumber(-1.0, axx) && Geometry.isAlmostEqualNumber(-1, azz)) {
|
|
936
|
-
return { axis: Vector3d.create(0, 1, 0), angle:
|
|
1076
|
+
return { axis: Vector3d.create(0, 1, 0), angle: Angle.createDegrees(180), ok: true };
|
|
937
1077
|
}
|
|
938
1078
|
else if (Geometry.isAlmostEqualNumber(-1.0, axx) && Geometry.isAlmostEqualNumber(-1, ayy)) {
|
|
939
|
-
return { axis: Vector3d.create(0, 0, 1), angle:
|
|
1079
|
+
return { axis: Vector3d.create(0, 0, 1), angle: Angle.createDegrees(180), ok: true };
|
|
940
1080
|
}
|
|
941
|
-
//
|
|
942
|
-
// eigenvalues will have 1.0 once, -1.0 twice.
|
|
943
|
-
// These cases look for each place (x,y,z) that the 1.0 might appear.
|
|
944
|
-
// But fastSymmetricEigenvalues reliably always seems to put the 1.0 as the x eigenvalue.
|
|
945
|
-
// so only the getColumn(0) return seems reachable in unit tests.
|
|
1081
|
+
// Look for eigenvector with eigenvalue = 1
|
|
946
1082
|
const eigenvectors = Matrix3d.createIdentity();
|
|
947
1083
|
const eigenvalues = Vector3d.create(0, 0, 0);
|
|
948
|
-
if (this.fastSymmetricEigenvalues(eigenvectors, eigenvalues)) {
|
|
1084
|
+
if (this.fastSymmetricEigenvalues(eigenvectors, eigenvalues)) { // note: this matrix is "symmetric"
|
|
949
1085
|
for (let axisIndex = 0; axisIndex < 2; axisIndex++) {
|
|
950
1086
|
const lambda = eigenvalues.at(axisIndex);
|
|
951
1087
|
if (Geometry.isAlmostEqualNumber(1, lambda))
|
|
952
|
-
return { axis: eigenvectors.getColumn(axisIndex), angle:
|
|
1088
|
+
return { axis: eigenvectors.getColumn(axisIndex), angle: Angle.createDegrees(180), ok: true };
|
|
953
1089
|
}
|
|
954
|
-
//
|
|
1090
|
+
// if no eigenvalue = 1 was found return false
|
|
955
1091
|
return { axis: Vector3d.create(0, 0, 1), angle: Angle.createRadians(0), ok: false };
|
|
956
1092
|
}
|
|
1093
|
+
// if no axis was found return false
|
|
957
1094
|
return { axis: Vector3d.create(0, 0, 1), angle: Angle.createRadians(0), ok: false };
|
|
958
1095
|
}
|
|
1096
|
+
// good matrix and non-zero sine
|
|
959
1097
|
const a = 1.0 / (2.0 * s);
|
|
960
|
-
const result = {
|
|
1098
|
+
const result = {
|
|
1099
|
+
axis: Vector3d.create(skewYZ * a, skewZX * a, skewXY * a),
|
|
1100
|
+
angle: Angle.createAtan2(s, c),
|
|
1101
|
+
ok: true,
|
|
1102
|
+
};
|
|
961
1103
|
return result;
|
|
962
1104
|
}
|
|
963
1105
|
/**
|
|
964
|
-
*
|
|
1106
|
+
* Rotate columns i and j of `this` matrix to make them perpendicular using the angle that zero-out
|
|
1107
|
+
* `thisTranspose * this`.
|
|
1108
|
+
* @param i row index of zeroed member.
|
|
1109
|
+
* @param j column index of zeroed member.
|
|
1110
|
+
* @param matrixU a matrix that its columns will be filled by the right eigenvectors of `thisTranspose * this`
|
|
1111
|
+
* (allocated by caller, computed and filled by this function). Note that columns of matrixU will be mutually
|
|
1112
|
+
* perpendicular because `thisTranspose * this` matrix is symmetric.
|
|
965
1113
|
*/
|
|
966
|
-
|
|
967
|
-
|
|
1114
|
+
applyJacobiColumnRotation(i, j, matrixU) {
|
|
1115
|
+
const uDotU = this.coffs[i] * this.coffs[i]
|
|
1116
|
+
+ this.coffs[i + 3] * this.coffs[i + 3]
|
|
1117
|
+
+ this.coffs[i + 6] * this.coffs[i + 6];
|
|
1118
|
+
const vDotV = this.coffs[j] * this.coffs[j]
|
|
1119
|
+
+ this.coffs[j + 3] * this.coffs[j + 3]
|
|
1120
|
+
+ this.coffs[j + 6] * this.coffs[j + 6];
|
|
1121
|
+
const uDotV = this.coffs[i] * this.coffs[j]
|
|
1122
|
+
+ this.coffs[i + 3] * this.coffs[j + 3]
|
|
1123
|
+
+ this.coffs[i + 6] * this.coffs[j + 6];
|
|
1124
|
+
const jacobi = Angle.trigValuesToHalfAngleTrigValues(uDotU - vDotV, 2.0 * uDotV);
|
|
1125
|
+
const c = jacobi.c;
|
|
1126
|
+
const s = jacobi.s;
|
|
1127
|
+
if (Math.abs(s) < 2.0e-15)
|
|
1128
|
+
return 0.0;
|
|
1129
|
+
this.applyGivensColumnOp(i, j, c, s); // make columns i and j of `this` matrix perpendicular
|
|
1130
|
+
matrixU.applyGivensRowOp(i, j, c, s); // right eigenvalues of `thisTranspose * this`
|
|
1131
|
+
return Math.abs(uDotV);
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Factor `this` matrix as a product `VD * U` where `VD` has mutually perpendicular columns and `U` is orthogonal.
|
|
1135
|
+
* @param matrixVD a matrix that its columns will be filled by rotating columns of `this` to make them mutually
|
|
1136
|
+
* perpendicular (allocated by caller, computed and filled by this function).
|
|
1137
|
+
* @param matrixU a matrix that its columns will be filled by the right eigenvectors of `thisTranspose * this`
|
|
1138
|
+
* (allocated by caller, computed and filled by this function). Note that columns of matrixU will be mutually
|
|
1139
|
+
* perpendicular because `thisTranspose * this` matrix is symmetric.
|
|
1140
|
+
*/
|
|
1141
|
+
factorPerpendicularColumns(matrixVD, matrixU) {
|
|
1142
|
+
matrixVD.setFrom(this);
|
|
1143
|
+
matrixU.setIdentity();
|
|
1144
|
+
const tolerance = 1.0e-12 * this.sumSquares();
|
|
1145
|
+
const numberOfIterations = 7;
|
|
1146
|
+
for (let iteration = 0; iteration < numberOfIterations; iteration++) {
|
|
1147
|
+
const sum = matrixVD.applyJacobiColumnRotation(0, 1, matrixU)
|
|
1148
|
+
+ matrixVD.applyJacobiColumnRotation(0, 2, matrixU)
|
|
1149
|
+
+ matrixVD.applyJacobiColumnRotation(1, 2, matrixU);
|
|
1150
|
+
if (sum < tolerance) {
|
|
1151
|
+
return true;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return false;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Factor `this` matrix as a product `V * D * U` where `V` and `U` are orthogonal and `D` is diagonal with
|
|
1158
|
+
* positive entries.
|
|
1159
|
+
* * This is formally known as the `Singular Value Decomposition` or `SVD`.
|
|
1160
|
+
* @param matrixV an orthogonal matrix that its columns will be filled by the left eigenvectors of
|
|
1161
|
+
* `thisTranspose * this` (allocated by caller, computed and filled by this function).
|
|
1162
|
+
* @param scale singular values of `this` (allocated by caller, computed and filled by this function).
|
|
1163
|
+
* The singular values in the `scale` are non-negative and decreasing.
|
|
1164
|
+
* @param matrixU an orthogonal matrix that its columns will be filled by the right eigenvectors of
|
|
1165
|
+
* `thisTranspose * this` (allocated by caller, computed and filled by this function).
|
|
1166
|
+
*/
|
|
1167
|
+
factorOrthogonalScaleOrthogonal(matrixV, scale, matrixU) {
|
|
1168
|
+
const matrixVD = Matrix3d.createZero();
|
|
1169
|
+
if (!this.factorPerpendicularColumns(matrixVD, matrixU))
|
|
1170
|
+
return false;
|
|
1171
|
+
const column = [];
|
|
1172
|
+
column.push(matrixVD.getColumn(0));
|
|
1173
|
+
column.push(matrixVD.getColumn(1));
|
|
1174
|
+
column.push(matrixVD.getColumn(2));
|
|
1175
|
+
scale.set(column[0].magnitude(), column[1].magnitude(), column[2].magnitude()); // singular values of `this`
|
|
1176
|
+
const det = matrixVD.determinant();
|
|
1177
|
+
if (det < 0)
|
|
1178
|
+
scale.z = -scale.z;
|
|
1179
|
+
const almostZero = 1.0e-15;
|
|
1180
|
+
const scaleXIsZero = Math.abs(scale.x) < almostZero;
|
|
1181
|
+
const scaleYIsZero = Math.abs(scale.y) < almostZero;
|
|
1182
|
+
const scaleZIsZero = Math.abs(scale.z) < almostZero;
|
|
1183
|
+
// NOTE: We assume any zero-magnitude column(s) of matrixVD are last
|
|
1184
|
+
if (!scaleXIsZero && !scaleYIsZero && !scaleZIsZero) { // full rank
|
|
1185
|
+
matrixV = matrixVD.scaleColumns(1 / scale.x, 1 / scale.y, 1 / scale.z, matrixV);
|
|
1186
|
+
}
|
|
1187
|
+
else if (!scaleXIsZero && !scaleYIsZero) { // rank 2
|
|
1188
|
+
column[0].scaleInPlace(1 / scale.x);
|
|
1189
|
+
column[1].scaleInPlace(1 / scale.y);
|
|
1190
|
+
column[2] = column[0].unitCrossProduct(column[1], column[2]);
|
|
1191
|
+
matrixV.setColumns(column[0], column[1], column[2]);
|
|
1192
|
+
}
|
|
1193
|
+
else if (!scaleXIsZero) { // rank 1
|
|
1194
|
+
matrixV = Matrix3d.createRigidHeadsUp(column[0], AxisOrder.XYZ, matrixV); // preserve column0
|
|
1195
|
+
}
|
|
1196
|
+
else { // rank 0
|
|
1197
|
+
matrixV.setIdentity();
|
|
1198
|
+
}
|
|
1199
|
+
return true;
|
|
968
1200
|
}
|
|
969
1201
|
/**
|
|
970
1202
|
* Return a matrix that rotates a fraction of the angular sweep from vectorA to vectorB.
|
|
971
1203
|
* @param vectorA initial vector position
|
|
972
|
-
* @param fraction fractional rotation
|
|
1204
|
+
* @param fraction fractional rotation (1 means rotate all the way)
|
|
973
1205
|
* @param vectorB final vector position
|
|
974
1206
|
* @param result optional result matrix.
|
|
975
1207
|
*/
|
|
976
1208
|
static createPartialRotationVectorToVector(vectorA, fraction, vectorB, result) {
|
|
977
1209
|
let upVector = vectorA.unitCrossProduct(vectorB);
|
|
978
|
-
|
|
1210
|
+
// the usual case (both vectors and also their cross product is non-zero)
|
|
1211
|
+
if (upVector) {
|
|
979
1212
|
return Matrix3d.createRotationAroundVector(upVector, Angle.createRadians(fraction * vectorA.planarAngleTo(vectorB, upVector).radians));
|
|
980
1213
|
}
|
|
981
|
-
//
|
|
1214
|
+
// if either vector is zero
|
|
982
1215
|
if (Geometry.isSmallMetricDistance(vectorA.magnitude())
|
|
983
1216
|
|| Geometry.isSmallMetricDistance(vectorB.magnitude()))
|
|
984
1217
|
return undefined;
|
|
985
|
-
//
|
|
1218
|
+
// aligned vectors (cross product = 0, dot product > 0)
|
|
986
1219
|
if (vectorA.dotProduct(vectorB) > 0.0)
|
|
987
1220
|
return Matrix3d.createIdentity(result);
|
|
988
|
-
//
|
|
1221
|
+
// opposing vectors (cross product = 0, dot product < 0)
|
|
989
1222
|
upVector = Matrix3d.createPerpendicularVectorFavorPlaneContainingZ(vectorA, upVector);
|
|
990
1223
|
return Matrix3d.createRotationAroundVector(upVector, Angle.createRadians(fraction * Math.PI));
|
|
991
1224
|
}
|
|
1225
|
+
/** Returns a matrix that rotates from vectorA to vectorB. */
|
|
1226
|
+
static createRotationVectorToVector(vectorA, vectorB, result) {
|
|
1227
|
+
return this.createPartialRotationVectorToVector(vectorA, 1.0, vectorB, result);
|
|
1228
|
+
}
|
|
992
1229
|
/** Create a 90 degree rotation around a principal axis */
|
|
993
1230
|
static create90DegreeRotationAroundAxis(axisIndex) {
|
|
994
1231
|
axisIndex = Geometry.cyclic3dAxis(axisIndex);
|
|
@@ -1009,43 +1246,78 @@ export class Matrix3d {
|
|
|
1009
1246
|
}
|
|
1010
1247
|
}
|
|
1011
1248
|
/** Return (a copy of) the X column */
|
|
1012
|
-
columnX(result) {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
/** Return (a copy of)the
|
|
1016
|
-
|
|
1249
|
+
columnX(result) {
|
|
1250
|
+
return Vector3d.create(this.coffs[0], this.coffs[3], this.coffs[6], result);
|
|
1251
|
+
}
|
|
1252
|
+
/** Return (a copy of) the Y column */
|
|
1253
|
+
columnY(result) {
|
|
1254
|
+
return Vector3d.create(this.coffs[1], this.coffs[4], this.coffs[7], result);
|
|
1255
|
+
}
|
|
1256
|
+
/** Return (a copy of) the Z column */
|
|
1257
|
+
columnZ(result) {
|
|
1258
|
+
return Vector3d.create(this.coffs[2], this.coffs[5], this.coffs[8], result);
|
|
1259
|
+
}
|
|
1017
1260
|
/** Return the X column magnitude squared */
|
|
1018
|
-
columnXMagnitudeSquared() {
|
|
1261
|
+
columnXMagnitudeSquared() {
|
|
1262
|
+
return Geometry.hypotenuseSquaredXYZ(this.coffs[0], this.coffs[3], this.coffs[6]);
|
|
1263
|
+
}
|
|
1019
1264
|
/** Return the Y column magnitude squared */
|
|
1020
|
-
columnYMagnitudeSquared() {
|
|
1265
|
+
columnYMagnitudeSquared() {
|
|
1266
|
+
return Geometry.hypotenuseSquaredXYZ(this.coffs[1], this.coffs[4], this.coffs[7]);
|
|
1267
|
+
}
|
|
1021
1268
|
/** Return the Z column magnitude squared */
|
|
1022
|
-
columnZMagnitudeSquared() {
|
|
1269
|
+
columnZMagnitudeSquared() {
|
|
1270
|
+
return Geometry.hypotenuseSquaredXYZ(this.coffs[2], this.coffs[5], this.coffs[8]);
|
|
1271
|
+
}
|
|
1023
1272
|
/** Return the X column magnitude */
|
|
1024
|
-
columnXMagnitude() {
|
|
1273
|
+
columnXMagnitude() {
|
|
1274
|
+
return Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[3], this.coffs[6]);
|
|
1275
|
+
}
|
|
1025
1276
|
/** Return the Y column magnitude */
|
|
1026
|
-
columnYMagnitude() {
|
|
1277
|
+
columnYMagnitude() {
|
|
1278
|
+
return Geometry.hypotenuseXYZ(this.coffs[1], this.coffs[4], this.coffs[7]);
|
|
1279
|
+
}
|
|
1027
1280
|
/** Return the Z column magnitude */
|
|
1028
|
-
columnZMagnitude() {
|
|
1281
|
+
columnZMagnitude() {
|
|
1282
|
+
return Geometry.hypotenuseXYZ(this.coffs[2], this.coffs[5], this.coffs[8]);
|
|
1283
|
+
}
|
|
1029
1284
|
/** Return magnitude of columnX cross columnY. */
|
|
1030
1285
|
columnXYCrossProductMagnitude() {
|
|
1031
1286
|
return Geometry.crossProductMagnitude(this.coffs[0], this.coffs[3], this.coffs[6], this.coffs[1], this.coffs[4], this.coffs[7]);
|
|
1032
1287
|
}
|
|
1033
|
-
/** Return the X row magnitude
|
|
1034
|
-
rowXMagnitude() {
|
|
1288
|
+
/** Return the X row magnitude */
|
|
1289
|
+
rowXMagnitude() {
|
|
1290
|
+
return Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[1], this.coffs[2]);
|
|
1291
|
+
}
|
|
1035
1292
|
/** Return the Y row magnitude */
|
|
1036
|
-
rowYMagnitude() {
|
|
1293
|
+
rowYMagnitude() {
|
|
1294
|
+
return Geometry.hypotenuseXYZ(this.coffs[3], this.coffs[4], this.coffs[5]);
|
|
1295
|
+
}
|
|
1037
1296
|
/** Return the Z row magnitude */
|
|
1038
|
-
rowZMagnitude() {
|
|
1039
|
-
|
|
1297
|
+
rowZMagnitude() {
|
|
1298
|
+
return Geometry.hypotenuseXYZ(this.coffs[6], this.coffs[7], this.coffs[8]);
|
|
1299
|
+
}
|
|
1040
1300
|
/** Return the dot product of column X with column Y */
|
|
1041
1301
|
columnXDotColumnY() {
|
|
1042
1302
|
return this.coffs[0] * this.coffs[1]
|
|
1043
1303
|
+ this.coffs[3] * this.coffs[4]
|
|
1044
1304
|
+ this.coffs[6] * this.coffs[7];
|
|
1045
1305
|
}
|
|
1306
|
+
/** Return the dot product of column X with column Z */
|
|
1307
|
+
columnXDotColumnZ() {
|
|
1308
|
+
return this.coffs[0] * this.coffs[2]
|
|
1309
|
+
+ this.coffs[3] * this.coffs[5]
|
|
1310
|
+
+ this.coffs[6] * this.coffs[8];
|
|
1311
|
+
}
|
|
1312
|
+
/** Return the dot product of column Y with column Z */
|
|
1313
|
+
columnYDotColumnZ() {
|
|
1314
|
+
return this.coffs[1] * this.coffs[2]
|
|
1315
|
+
+ this.coffs[4] * this.coffs[5]
|
|
1316
|
+
+ this.coffs[7] * this.coffs[8];
|
|
1317
|
+
}
|
|
1046
1318
|
/**
|
|
1047
1319
|
* Dot product of an indexed column with a vector given as x,y,z
|
|
1048
|
-
* @param columnIndex index of column.
|
|
1320
|
+
* @param columnIndex index of column. Must be 0,1,2.
|
|
1049
1321
|
* @param x x component of vector
|
|
1050
1322
|
* @param y y component of vector
|
|
1051
1323
|
* @param z z component of vector
|
|
@@ -1054,285 +1326,58 @@ export class Matrix3d {
|
|
|
1054
1326
|
return this.coffs[columnIndex] * x + this.coffs[columnIndex + 3] * y + this.coffs[columnIndex + 6] * z;
|
|
1055
1327
|
}
|
|
1056
1328
|
/** Return (a copy of) the X row */
|
|
1057
|
-
rowX(result) {
|
|
1329
|
+
rowX(result) {
|
|
1330
|
+
return Vector3d.create(this.coffs[0], this.coffs[1], this.coffs[2], result);
|
|
1331
|
+
}
|
|
1058
1332
|
/** Return (a copy of) the Y row */
|
|
1059
|
-
rowY(result) {
|
|
1333
|
+
rowY(result) {
|
|
1334
|
+
return Vector3d.create(this.coffs[3], this.coffs[4], this.coffs[5], result);
|
|
1335
|
+
}
|
|
1060
1336
|
/** Return (a copy of) the Z row */
|
|
1061
|
-
rowZ(result) {
|
|
1337
|
+
rowZ(result) {
|
|
1338
|
+
return Vector3d.create(this.coffs[6], this.coffs[7], this.coffs[8], result);
|
|
1339
|
+
}
|
|
1062
1340
|
/** Return the dot product of the vector parameter with the X column. */
|
|
1063
|
-
dotColumnX(vector) {
|
|
1064
|
-
|
|
1065
|
-
dotColumnY(vector) { return vector.x * this.coffs[1] + vector.y * this.coffs[4] + vector.z * this.coffs[7]; }
|
|
1066
|
-
/** Return the dot product of the vector parameter with the Z column. */
|
|
1067
|
-
dotColumnZ(vector) { return vector.x * this.coffs[2] + vector.y * this.coffs[5] + vector.z * this.coffs[8]; }
|
|
1068
|
-
/** Return the dot product of the vector parameter with the X row. */
|
|
1069
|
-
dotRowX(vector) { return vector.x * this.coffs[0] + vector.y * this.coffs[1] + vector.z * this.coffs[2]; }
|
|
1070
|
-
/** Return the dot product of the vector parameter with the Y row. */
|
|
1071
|
-
dotRowY(vector) { return vector.x * this.coffs[3] + vector.y * this.coffs[4] + vector.z * this.coffs[5]; }
|
|
1072
|
-
/** Return the dot product of the vector parameter with the Z row. */
|
|
1073
|
-
dotRowZ(vector) { return vector.x * this.coffs[6] + vector.y * this.coffs[7] + vector.z * this.coffs[8]; }
|
|
1074
|
-
// cSpell:words XXYZ YXYZ ZXYZ XYZAs Eigen
|
|
1075
|
-
/** Return the dot product of the x,y,z with the X row. */
|
|
1076
|
-
dotRowXXYZ(x, y, z) { return x * this.coffs[0] + y * this.coffs[1] + z * this.coffs[2]; }
|
|
1077
|
-
/** Return the dot product of the x,y,z with the Y row. */
|
|
1078
|
-
dotRowYXYZ(x, y, z) { return x * this.coffs[3] + y * this.coffs[4] + z * this.coffs[5]; }
|
|
1079
|
-
/** Return the dot product of the x,y,z with the Z row. */
|
|
1080
|
-
dotRowZXYZ(x, y, z) { return x * this.coffs[6] + y * this.coffs[7] + z * this.coffs[8]; }
|
|
1081
|
-
/** Return the (vector) cross product of the Z column with the vector parameter. */
|
|
1082
|
-
columnZCrossVector(vector, result) {
|
|
1083
|
-
return Geometry.crossProductXYZXYZ(this.coffs[2], this.coffs[5], this.coffs[8], vector.x, vector.y, vector.z, result);
|
|
1341
|
+
dotColumnX(vector) {
|
|
1342
|
+
return vector.x * this.coffs[0] + vector.y * this.coffs[3] + vector.z * this.coffs[6];
|
|
1084
1343
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
* @param j second row index. must be 0,1,2 (unchecked)
|
|
1089
|
-
* @param c fist coefficient
|
|
1090
|
-
* @param s second coefficient
|
|
1091
|
-
*/
|
|
1092
|
-
applyGivensRowOp(i, j, c, s) {
|
|
1093
|
-
let ii = 3 * i;
|
|
1094
|
-
let jj = 3 * j;
|
|
1095
|
-
const limit = ii + 3;
|
|
1096
|
-
for (; ii < limit; ii++, jj++) {
|
|
1097
|
-
const a = this.coffs[ii];
|
|
1098
|
-
const b = this.coffs[jj];
|
|
1099
|
-
this.coffs[ii] = a * c + b * s;
|
|
1100
|
-
this.coffs[jj] = -a * s + b * c;
|
|
1101
|
-
}
|
|
1344
|
+
/** Return the dot product of the vector parameter with the Y column. */
|
|
1345
|
+
dotColumnY(vector) {
|
|
1346
|
+
return vector.x * this.coffs[1] + vector.y * this.coffs[4] + vector.z * this.coffs[7];
|
|
1102
1347
|
}
|
|
1103
|
-
/**
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
* * column x is perpendicular and in the xy plane
|
|
1107
|
-
* * column y is perpendicular to both. It is the "up" vector on the view plane.
|
|
1108
|
-
* * Multiplying a world vector times the transpose of this matrix transforms into the view xy
|
|
1109
|
-
* * Multiplying the matrix times the an in-view vector transforms the vector to world.
|
|
1110
|
-
* @param x eye x coordinate
|
|
1111
|
-
* @param y eye y coordinate
|
|
1112
|
-
* @param z eye z coordinate
|
|
1113
|
-
* @param result
|
|
1114
|
-
*/
|
|
1115
|
-
static createRigidViewAxesZTowardsEye(x, y, z, result) {
|
|
1116
|
-
result = Matrix3d.createIdentity(result);
|
|
1117
|
-
const rxy = Geometry.hypotenuseXY(x, y);
|
|
1118
|
-
if (Geometry.isSmallMetricDistance(rxy)) {
|
|
1119
|
-
// special case for top or bottom view.
|
|
1120
|
-
if (z < 0.0)
|
|
1121
|
-
result.scaleColumnsInPlace(1.0, -1, -1.0);
|
|
1122
|
-
}
|
|
1123
|
-
else {
|
|
1124
|
-
// const d = Geometry.hypotenuseSquaredXYZ(x, y, z);
|
|
1125
|
-
const c = x / rxy;
|
|
1126
|
-
const s = y / rxy;
|
|
1127
|
-
result.setRowValues(-s, 0, c, c, 0, s, 0, 1, 0);
|
|
1128
|
-
if (z !== 0.0) {
|
|
1129
|
-
const r = Geometry.hypotenuseXYZ(x, y, z);
|
|
1130
|
-
const s1 = z / r;
|
|
1131
|
-
const c1 = rxy / r;
|
|
1132
|
-
result.applyGivensColumnOp(1, 2, c1, -s1);
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
return result;
|
|
1348
|
+
/** Return the dot product of the vector parameter with the Z column. */
|
|
1349
|
+
dotColumnZ(vector) {
|
|
1350
|
+
return vector.x * this.coffs[2] + vector.y * this.coffs[5] + vector.z * this.coffs[8];
|
|
1136
1351
|
}
|
|
1137
|
-
/**
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
const vDotV = this.coffs[j] * this.coffs[j] + this.coffs[j + 3] * this.coffs[j + 3] + this.coffs[j + 6] * this.coffs[j + 6];
|
|
1141
|
-
const uDotV = this.coffs[i] * this.coffs[j] + this.coffs[i + 3] * this.coffs[j + 3] + this.coffs[i + 6] * this.coffs[j + 6];
|
|
1142
|
-
// const c2 = uDotU - vDotV;
|
|
1143
|
-
// const s2 = 2.0 * uDotV;
|
|
1144
|
-
const jacobi = Angle.trigValuesToHalfAngleTrigValues(uDotU - vDotV, 2.0 * uDotV);
|
|
1145
|
-
// const h = Math.hypot(c2, s2);
|
|
1146
|
-
// console.log(" c2 s2", c2 / h, s2 / h);
|
|
1147
|
-
// console.log(" C S ", Math.cos(2 * jacobi.radians), Math.sin(2 * jacobi.radians));
|
|
1148
|
-
// console.log("i j uDotV", i, j, uDotV);
|
|
1149
|
-
if (Math.abs(jacobi.s) < 2.0e-15)
|
|
1150
|
-
return 0.0;
|
|
1151
|
-
this.applyGivensColumnOp(i, j, jacobi.c, jacobi.s);
|
|
1152
|
-
matrixU.applyGivensRowOp(i, j, jacobi.c, jacobi.s);
|
|
1153
|
-
// const BTB = this.multiplyMatrixTransposeMatrix(this);
|
|
1154
|
-
// console.log("BTB", BTB.at(0, 0), BTB.at(1, 1), BTB.at(2, 2), " off", BTB.at(0, 1), BTB.at(0, 2), BTB.at(1, 2), " at(i,j)", BTB.at(i, j));
|
|
1155
|
-
return Math.abs(uDotV);
|
|
1352
|
+
/** Return the dot product of the vector parameter with the X row. */
|
|
1353
|
+
dotRowX(vector) {
|
|
1354
|
+
return vector.x * this.coffs[0] + vector.y * this.coffs[1] + vector.z * this.coffs[2];
|
|
1156
1355
|
}
|
|
1157
|
-
/**
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
* @param matrixC (allocate by caller, computed here)
|
|
1161
|
-
* @param matrixU (allocate by caller, computed here)
|
|
1162
|
-
*/
|
|
1163
|
-
factorPerpendicularColumns(matrixC, matrixU) {
|
|
1164
|
-
matrixC.setFrom(this);
|
|
1165
|
-
matrixU.setIdentity();
|
|
1166
|
-
const ss = this.sumSquares();
|
|
1167
|
-
const tolerance = 1.0e-12 * ss;
|
|
1168
|
-
for (let iteration = 0; iteration < 7; iteration++) {
|
|
1169
|
-
const sum = matrixC.applyJacobiColumnRotation(0, 1, matrixU)
|
|
1170
|
-
+ matrixC.applyJacobiColumnRotation(0, 2, matrixU)
|
|
1171
|
-
+ matrixC.applyJacobiColumnRotation(1, 2, matrixU);
|
|
1172
|
-
// console.log (" sum", sum);
|
|
1173
|
-
if (sum < tolerance) {
|
|
1174
|
-
// console.log("jacobi iterations", iteration);
|
|
1175
|
-
return true;
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
return false;
|
|
1356
|
+
/** Return the dot product of the vector parameter with the Y row. */
|
|
1357
|
+
dotRowY(vector) {
|
|
1358
|
+
return vector.x * this.coffs[3] + vector.y * this.coffs[4] + vector.z * this.coffs[5];
|
|
1179
1359
|
}
|
|
1180
|
-
/**
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
* @param scale diagonal entries of D (allocate by caller, computed here)
|
|
1184
|
-
* @param matrixU right orthogonal factor (allocate by caller, computed here)
|
|
1185
|
-
*/
|
|
1186
|
-
factorOrthogonalScaleOrthogonal(matrixV, scale, matrixU) {
|
|
1187
|
-
const matrixVD = Matrix3d.createZero();
|
|
1188
|
-
if (!this.factorPerpendicularColumns(matrixVD, matrixU))
|
|
1189
|
-
return false;
|
|
1190
|
-
const column = [];
|
|
1191
|
-
column.push(matrixVD.getColumn(0));
|
|
1192
|
-
column.push(matrixVD.getColumn(1));
|
|
1193
|
-
column.push(matrixVD.getColumn(2));
|
|
1194
|
-
scale.set(column[0].magnitude(), column[1].magnitude(), column[2].magnitude());
|
|
1195
|
-
const det = matrixVD.determinant();
|
|
1196
|
-
if (det < 0)
|
|
1197
|
-
scale.z = -scale.z;
|
|
1198
|
-
const almostZero = 1.0e-15;
|
|
1199
|
-
const scaleXIsZero = Math.abs(scale.x) < almostZero;
|
|
1200
|
-
const scaleYIsZero = Math.abs(scale.y) < almostZero;
|
|
1201
|
-
const scaleZIsZero = Math.abs(scale.z) < almostZero;
|
|
1202
|
-
// ASSUME: any zero-magnitude column(s) of matrixVD are last
|
|
1203
|
-
if (!scaleXIsZero && !scaleYIsZero && !scaleZIsZero) { // full rank
|
|
1204
|
-
matrixV = matrixVD.scaleColumns(1 / scale.x, 1 / scale.y, 1 / scale.z, matrixV);
|
|
1205
|
-
}
|
|
1206
|
-
else if (!scaleXIsZero && !scaleYIsZero) { // rank 2
|
|
1207
|
-
column[0].scaleInPlace(1 / scale.x);
|
|
1208
|
-
column[1].scaleInPlace(1 / scale.y);
|
|
1209
|
-
column[2] = column[0].unitCrossProduct(column[1], column[2]);
|
|
1210
|
-
matrixV.setColumns(column[0], column[1], column[2]);
|
|
1211
|
-
}
|
|
1212
|
-
else if (!scaleXIsZero) { // rank 1
|
|
1213
|
-
matrixV = Matrix3d.createRigidHeadsUp(column[0], AxisOrder.XYZ, matrixV); // preserve column0
|
|
1214
|
-
}
|
|
1215
|
-
else { // rank 0
|
|
1216
|
-
matrixV.setIdentity();
|
|
1217
|
-
}
|
|
1218
|
-
return true;
|
|
1360
|
+
/** Return the dot product of the vector parameter with the Z row. */
|
|
1361
|
+
dotRowZ(vector) {
|
|
1362
|
+
return vector.x * this.coffs[6] + vector.y * this.coffs[7] + vector.z * this.coffs[8];
|
|
1219
1363
|
}
|
|
1220
|
-
/**
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
const vDotV = lambda.at(j, j);
|
|
1224
|
-
const uDotV = lambda.at(i, j);
|
|
1225
|
-
if (Math.abs(uDotV) < 1.0e-15 * (uDotU + vDotV))
|
|
1226
|
-
return 0.0;
|
|
1227
|
-
// const c2 = uDotU - vDotV;
|
|
1228
|
-
// const s2 = 2.0 * uDotV;
|
|
1229
|
-
const jacobi = Angle.trigValuesToHalfAngleTrigValues(uDotU - vDotV, 2.0 * uDotV);
|
|
1230
|
-
// const h = Math.hypot(c2, s2);
|
|
1231
|
-
// console.log(" c2 s2", c2 / h, s2 / h);
|
|
1232
|
-
// console.log(" C S ", Math.cos(2 * jacobi.radians), Math.sin(2 * jacobi.radians));
|
|
1233
|
-
// console.log("i j uDotV", i, j, uDotV);
|
|
1234
|
-
if (Math.abs(jacobi.s) < 2.0e-15)
|
|
1235
|
-
return 0.0;
|
|
1236
|
-
// Factored form is this *lambda * thisTranspose
|
|
1237
|
-
// Let Q be the rotation matrix. Q*QT is inserted, viz
|
|
1238
|
-
// this*Q * QT * lambda * Q*thisTranspose
|
|
1239
|
-
this.applyGivensColumnOp(i, j, jacobi.c, jacobi.s);
|
|
1240
|
-
lambda.applyGivensRowOp(i, j, jacobi.c, jacobi.s);
|
|
1241
|
-
lambda.applyGivensColumnOp(i, j, jacobi.c, jacobi.s);
|
|
1242
|
-
// const BTB = this.multiplyMatrixTransposeMatrix(this);
|
|
1243
|
-
// console.log("BTB", BTB.at(0, 0), BTB.at(1, 1), BTB.at(2, 2), " off", BTB.at(0, 1), BTB.at(0, 2), BTB.at(1, 2), " at(i,j)", BTB.at(i, j));
|
|
1244
|
-
return Math.abs(uDotV);
|
|
1364
|
+
/** Return the dot product of the x,y,z with the X row. */
|
|
1365
|
+
dotRowXXYZ(x, y, z) {
|
|
1366
|
+
return x * this.coffs[0] + y * this.coffs[1] + z * this.coffs[2];
|
|
1245
1367
|
}
|
|
1246
|
-
/**
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
* @param matrixC (allocate by caller, computed here)
|
|
1250
|
-
* @param factor (allocate by caller, computed here)
|
|
1251
|
-
*/
|
|
1252
|
-
symmetricEigenvalues(leftEigenvectors, lambda) {
|
|
1253
|
-
const matrix = this.clone();
|
|
1254
|
-
leftEigenvectors.setIdentity();
|
|
1255
|
-
matrix.coffs[3] = matrix.coffs[1];
|
|
1256
|
-
matrix.coffs[6] = matrix.coffs[2];
|
|
1257
|
-
matrix.coffs[7] = matrix.coffs[5];
|
|
1258
|
-
const ss = this.sumSquares();
|
|
1259
|
-
const tolerance = 1.0e-12 * ss;
|
|
1260
|
-
for (let iteration = 0; iteration < 7; iteration++) {
|
|
1261
|
-
const sum = leftEigenvectors.applySymmetricJacobi(0, 1, matrix)
|
|
1262
|
-
+ leftEigenvectors.applySymmetricJacobi(0, 2, matrix)
|
|
1263
|
-
+ leftEigenvectors.applySymmetricJacobi(1, 2, matrix);
|
|
1264
|
-
// console.log("symmetric sum", sum);
|
|
1265
|
-
// console.log (" sum", sum);
|
|
1266
|
-
if (sum < tolerance) {
|
|
1267
|
-
// console.log("symmetric iterations", iteration);
|
|
1268
|
-
lambda.set(matrix.at(0, 0), matrix.at(1, 1), matrix.at(2, 2));
|
|
1269
|
-
return true;
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
return false;
|
|
1368
|
+
/** Return the dot product of the x,y,z with the Y row. */
|
|
1369
|
+
dotRowYXYZ(x, y, z) {
|
|
1370
|
+
return x * this.coffs[3] + y * this.coffs[4] + z * this.coffs[5];
|
|
1273
1371
|
}
|
|
1274
|
-
/**
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
applyFastSymmetricJacobiUpdate(i, // row index of zeroed member
|
|
1278
|
-
j, // column index of zeroed member
|
|
1279
|
-
k, // other row/column index (different from i and j)
|
|
1280
|
-
leftEigenVectors) {
|
|
1281
|
-
const indexII = 4 * i;
|
|
1282
|
-
const indexJJ = 4 * j;
|
|
1283
|
-
const indexIJ = 3 * i + j;
|
|
1284
|
-
const indexIK = 3 * i + k;
|
|
1285
|
-
const indexJK = 3 * j + k;
|
|
1286
|
-
const dotUU = this.coffs[indexII];
|
|
1287
|
-
const dotVV = this.coffs[indexJJ];
|
|
1288
|
-
const dotUV = this.coffs[indexIJ];
|
|
1289
|
-
const jacobi = Angle.trigValuesToHalfAngleTrigValues(dotUU - dotVV, 2.0 * dotUV);
|
|
1290
|
-
if (Math.abs(dotUV) < 1.0e-15 * (dotUU + dotVV))
|
|
1291
|
-
return 0.0;
|
|
1292
|
-
const c = jacobi.c;
|
|
1293
|
-
const s = jacobi.s;
|
|
1294
|
-
const cc = c * c;
|
|
1295
|
-
const ss = s * s;
|
|
1296
|
-
const sc2 = 2.0 * c * s;
|
|
1297
|
-
this.coffs[indexII] = cc * dotUU + sc2 * dotUV + ss * dotVV;
|
|
1298
|
-
this.coffs[indexJJ] = ss * dotUU - sc2 * dotUV + cc * dotVV;
|
|
1299
|
-
this.coffs[indexIJ] = 0.0;
|
|
1300
|
-
const a = this.coffs[indexIK];
|
|
1301
|
-
const b = this.coffs[indexJK];
|
|
1302
|
-
this.coffs[indexIK] = a * c + b * s;
|
|
1303
|
-
this.coffs[indexJK] = -s * a + c * b;
|
|
1304
|
-
this.coffs[3 * j + i] = 0.0;
|
|
1305
|
-
this.coffs[3 * k + i] = this.coffs[indexIK];
|
|
1306
|
-
this.coffs[3 * k + j] = this.coffs[indexJK];
|
|
1307
|
-
leftEigenVectors.applyGivensColumnOp(i, j, c, s);
|
|
1308
|
-
return Math.abs(dotUV);
|
|
1372
|
+
/** Return the dot product of the x,y,z with the Z row. */
|
|
1373
|
+
dotRowZXYZ(x, y, z) {
|
|
1374
|
+
return x * this.coffs[6] + y * this.coffs[7] + z * this.coffs[8];
|
|
1309
1375
|
}
|
|
1310
|
-
/**
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
* @param matrixC (allocate by caller, computed here)
|
|
1314
|
-
* @param factor (allocate by caller, computed here)
|
|
1315
|
-
*/
|
|
1316
|
-
fastSymmetricEigenvalues(leftEigenvectors, lambda) {
|
|
1317
|
-
const matrix = this.clone();
|
|
1318
|
-
leftEigenvectors.setIdentity();
|
|
1319
|
-
const ss = this.sumSquares();
|
|
1320
|
-
const tolerance = 1.0e-12 * ss;
|
|
1321
|
-
for (let iteration = 0; iteration < 7; iteration++) {
|
|
1322
|
-
const sum = matrix.applyFastSymmetricJacobiUpdate(0, 1, 2, leftEigenvectors)
|
|
1323
|
-
+ matrix.applyFastSymmetricJacobiUpdate(0, 2, 1, leftEigenvectors)
|
|
1324
|
-
+ matrix.applyFastSymmetricJacobiUpdate(1, 2, 0, leftEigenvectors);
|
|
1325
|
-
// console.log("symmetric sum", sum);
|
|
1326
|
-
// console.log (" sum", sum);
|
|
1327
|
-
if (sum < tolerance) {
|
|
1328
|
-
// console.log("symmetric iterations", iteration);
|
|
1329
|
-
lambda.set(matrix.at(0, 0), matrix.at(1, 1), matrix.at(2, 2));
|
|
1330
|
-
return true;
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
return false;
|
|
1376
|
+
/** Return the cross product of the Z column with the vector parameter. */
|
|
1377
|
+
columnZCrossVector(vector, result) {
|
|
1378
|
+
return Geometry.crossProductXYZXYZ(this.coffs[2], this.coffs[5], this.coffs[8], vector.x, vector.y, vector.z, result);
|
|
1334
1379
|
}
|
|
1335
|
-
/**
|
|
1380
|
+
/** Set data from xyz parts of Point4d (w part of Point4d ignored) */
|
|
1336
1381
|
setColumnsPoint4dXYZ(vectorU, vectorV, vectorW) {
|
|
1337
1382
|
this.inverseState = InverseMatrixState.unknown;
|
|
1338
1383
|
this.setRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, vectorU.z, vectorV.z, vectorW.z);
|
|
@@ -1356,16 +1401,22 @@ export class Matrix3d {
|
|
|
1356
1401
|
this.coffs[index + 6] = 0.0;
|
|
1357
1402
|
}
|
|
1358
1403
|
}
|
|
1359
|
-
/**
|
|
1404
|
+
/**
|
|
1405
|
+
* Set all columns of the matrix. Any undefined vector is zeros.
|
|
1406
|
+
* @param vectorX values for column 0
|
|
1407
|
+
* @param vectorY values for column 1
|
|
1408
|
+
* @param vectorZ optional values for column 2 (it's optional in case column 2 is 000, which is a
|
|
1409
|
+
* projection onto the xy-plane)
|
|
1410
|
+
*/
|
|
1360
1411
|
setColumns(vectorX, vectorY, vectorZ) {
|
|
1361
1412
|
this.setColumn(0, vectorX);
|
|
1362
1413
|
this.setColumn(1, vectorY);
|
|
1363
1414
|
this.setColumn(2, vectorZ);
|
|
1364
1415
|
}
|
|
1365
1416
|
/**
|
|
1366
|
-
*
|
|
1367
|
-
* @param rowIndex row index.
|
|
1368
|
-
* @param value x,
|
|
1417
|
+
* Set entries in one row of the matrix.
|
|
1418
|
+
* @param rowIndex row index. This is interpreted cyclically (using Geometry.cyclic3dAxis).
|
|
1419
|
+
* @param value x,y,z values for row.
|
|
1369
1420
|
*/
|
|
1370
1421
|
setRow(rowIndex, value) {
|
|
1371
1422
|
const index = 3 * Geometry.cyclic3dAxis(rowIndex);
|
|
@@ -1374,21 +1425,26 @@ export class Matrix3d {
|
|
|
1374
1425
|
this.coffs[index + 2] = value.z;
|
|
1375
1426
|
this.inverseState = InverseMatrixState.unknown;
|
|
1376
1427
|
}
|
|
1377
|
-
/**
|
|
1378
|
-
*
|
|
1428
|
+
/**
|
|
1429
|
+
* Return (a copy of) a column of the matrix.
|
|
1430
|
+
* @param i column index. This is interpreted cyclically (using Geometry.cyclic3dAxis).
|
|
1431
|
+
* @param result optional preallocated result.
|
|
1379
1432
|
*/
|
|
1380
1433
|
getColumn(columnIndex, result) {
|
|
1381
1434
|
const index = Geometry.cyclic3dAxis(columnIndex);
|
|
1382
1435
|
return Vector3d.create(this.coffs[index], this.coffs[index + 3], this.coffs[index + 6], result);
|
|
1383
1436
|
}
|
|
1384
|
-
/**
|
|
1385
|
-
*
|
|
1437
|
+
/**
|
|
1438
|
+
* Return a (copy of) a row of the matrix.
|
|
1439
|
+
* @param i row index. This is interpreted cyclically (using Geometry.cyclic3dAxis).
|
|
1440
|
+
* @param result optional preallocated result.
|
|
1386
1441
|
*/
|
|
1387
1442
|
getRow(columnIndex, result) {
|
|
1388
1443
|
const index = 3 * Geometry.cyclic3dAxis(columnIndex);
|
|
1389
1444
|
return Vector3d.create(this.coffs[index], this.coffs[index + 1], this.coffs[index + 2], result);
|
|
1390
1445
|
}
|
|
1391
|
-
/**
|
|
1446
|
+
/**
|
|
1447
|
+
* Create a matrix from row vectors.
|
|
1392
1448
|
* ```
|
|
1393
1449
|
* equation
|
|
1394
1450
|
* \begin{bmatrix}U_x & U_y & U_z \\ V_x & V_y & V_z \\ W_x & W_y & W_z \end{bmatrix}
|
|
@@ -1397,14 +1453,20 @@ export class Matrix3d {
|
|
|
1397
1453
|
static createRows(vectorU, vectorV, vectorW, result) {
|
|
1398
1454
|
return Matrix3d.createRowValues(vectorU.x, vectorU.y, vectorU.z, vectorV.x, vectorV.y, vectorV.z, vectorW.x, vectorW.y, vectorW.z, result);
|
|
1399
1455
|
}
|
|
1400
|
-
/**
|
|
1401
|
-
*
|
|
1402
|
-
*
|
|
1456
|
+
/**
|
|
1457
|
+
* Create a matrix that scales along a specified `direction`. This means if you multiply the returned matrix
|
|
1458
|
+
* by a `vector`, you get `directional scale` of that `vector`. Suppose `plane` is the plane perpendicular
|
|
1459
|
+
* to the `direction`. When scale = 0, `directional scale` is projection of the `vector` to the `plane`.
|
|
1460
|
+
* When scale = 1, `directional scale` is the `vector` itself. When scale = -1, `directional scale` is
|
|
1461
|
+
* mirror of the `vector` across the `plane`. In general, When scale != 0, the result is computed by first
|
|
1462
|
+
* projecting the `vector` to the `plane`, then translating that projection along the `direction` (if scale > 0)
|
|
1463
|
+
* or in opposite direction (if scale < 0).
|
|
1403
1464
|
* ```
|
|
1404
1465
|
* equation
|
|
1405
|
-
* \text{The matrix is } I
|
|
1406
|
-
* \\ \text{with }
|
|
1466
|
+
* \text{The matrix is } I + (s-1) D D^T
|
|
1467
|
+
* \\ \text{with }D\text{ being the normalized direction vector and }s\text{ being the scale.}
|
|
1407
1468
|
* ```
|
|
1469
|
+
* * Visualization can be found at itwinjs.org/sandbox/SaeedTorabi/DirectionalScale
|
|
1408
1470
|
*/
|
|
1409
1471
|
static createDirectionalScale(direction, scale, result) {
|
|
1410
1472
|
const unit = direction.normalize();
|
|
@@ -1412,19 +1474,13 @@ export class Matrix3d {
|
|
|
1412
1474
|
const x = unit.x;
|
|
1413
1475
|
const y = unit.y;
|
|
1414
1476
|
const z = unit.z;
|
|
1415
|
-
const a =
|
|
1477
|
+
const a = scale - 1;
|
|
1416
1478
|
return Matrix3d.createRowValues(1 + a * x * x, a * x * y, a * x * z, a * y * x, 1 + a * y * y, a * y * z, a * z * x, a * z * y, 1 + a * z * z, result);
|
|
1417
1479
|
}
|
|
1418
1480
|
return Matrix3d.createUniformScale(scale);
|
|
1419
1481
|
}
|
|
1420
|
-
|
|
1421
|
-
* *
|
|
1422
|
-
* * If the direction vector is not close to Z, the "next" column ((axisIndex + 1) mod 3) will be in the XY plane in the direction of (direction cross Z)
|
|
1423
|
-
* * If the direction vector is close to Z, the "next" column ((axisIndex + 1) mode 3) will be in the direction of (direction cross Y)
|
|
1424
|
-
*/
|
|
1425
|
-
// static create1Vector(direction: Vector3d, axisIndex: number): Matrix3d;
|
|
1426
|
-
// static createFromXYVectors(vectorX: Vector3d, vectorY: Vector3d, axisIndex: number): Matrix3d;
|
|
1427
|
-
/** Multiply the matrix * vector, treating the vector is a column vector on the right.
|
|
1482
|
+
/**
|
|
1483
|
+
* Multiply `matrix * vector`, treating the vector is a column vector on the right.
|
|
1428
1484
|
* ```
|
|
1429
1485
|
* equation
|
|
1430
1486
|
* \matrixXY{A}\columnSubXYZ{U}
|
|
@@ -1435,36 +1491,38 @@ export class Matrix3d {
|
|
|
1435
1491
|
const x = vectorU.x;
|
|
1436
1492
|
const y = vectorU.y;
|
|
1437
1493
|
const z = vectorU.z;
|
|
1438
|
-
return Vector3d.create(
|
|
1494
|
+
return Vector3d.create(this.coffs[0] * x + this.coffs[1] * y + this.coffs[2] * z, this.coffs[3] * x + this.coffs[4] * y + this.coffs[5] * z, this.coffs[6] * x + this.coffs[7] * y + this.coffs[8] * z, result);
|
|
1439
1495
|
}
|
|
1440
|
-
/**
|
|
1441
|
-
*
|
|
1496
|
+
/**
|
|
1497
|
+
* Multiply `matrix * vector` in place for vector in the array, i.e. treating the vector is a column
|
|
1498
|
+
* vector on the right.
|
|
1499
|
+
* * Each `vector` is updated to be `matrix * vector`
|
|
1442
1500
|
*/
|
|
1443
1501
|
multiplyVectorArrayInPlace(data) {
|
|
1444
1502
|
for (const v of data)
|
|
1445
|
-
v.set(
|
|
1503
|
+
v.set(this.coffs[0] * v.x + this.coffs[1] * v.y + this.coffs[2] * v.z, this.coffs[3] * v.x + this.coffs[4] * v.y + this.coffs[5] * v.z, this.coffs[6] * v.x + this.coffs[7] * v.y + this.coffs[8] * v.z);
|
|
1446
1504
|
}
|
|
1447
|
-
/**
|
|
1505
|
+
/** Compute `origin - matrix * vector` */
|
|
1448
1506
|
static xyzMinusMatrixTimesXYZ(origin, matrix, vector, result) {
|
|
1449
1507
|
const x = vector.x;
|
|
1450
1508
|
const y = vector.y;
|
|
1451
1509
|
const z = vector.z;
|
|
1452
1510
|
return Point3d.create(origin.x - (matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z), origin.y - (matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z), origin.z - (matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z), result);
|
|
1453
1511
|
}
|
|
1454
|
-
/**
|
|
1512
|
+
/** Compute `origin + matrix * vector` using only the xy parts of the inputs. */
|
|
1455
1513
|
static xyPlusMatrixTimesXY(origin, matrix, vector, result) {
|
|
1456
1514
|
const x = vector.x;
|
|
1457
1515
|
const y = vector.y;
|
|
1458
1516
|
return Point2d.create(origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y, origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y, result);
|
|
1459
1517
|
}
|
|
1460
|
-
/**
|
|
1518
|
+
/** Compute `origin + matrix * vector` using all xyz parts of the inputs. */
|
|
1461
1519
|
static xyzPlusMatrixTimesXYZ(origin, matrix, vector, result) {
|
|
1462
1520
|
const x = vector.x;
|
|
1463
1521
|
const y = vector.y;
|
|
1464
1522
|
const z = vector.z;
|
|
1465
1523
|
return Point3d.create(origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z, origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z, origin.z + matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z, result);
|
|
1466
1524
|
}
|
|
1467
|
-
/**
|
|
1525
|
+
/** Updates vector to be `origin + matrix * vector` using all xyz parts of the inputs. */
|
|
1468
1526
|
static xyzPlusMatrixTimesXYZInPlace(origin, matrix, vector) {
|
|
1469
1527
|
const x = vector.x;
|
|
1470
1528
|
const y = vector.y;
|
|
@@ -1473,65 +1531,76 @@ export class Matrix3d {
|
|
|
1473
1531
|
vector.y = origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z;
|
|
1474
1532
|
vector.z = origin.z + matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z;
|
|
1475
1533
|
}
|
|
1476
|
-
/**
|
|
1534
|
+
/** Compute `origin + matrix * vector` where the final vector is given as direct x,y,z coordinates */
|
|
1477
1535
|
static xyzPlusMatrixTimesCoordinates(origin, matrix, x, y, z, result) {
|
|
1478
1536
|
return Point3d.create(origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z, origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z, origin.z + matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z, result);
|
|
1479
1537
|
}
|
|
1480
1538
|
/**
|
|
1481
1539
|
* Treat the 3x3 matrix and origin as upper 3x4 part of a 4x4 matrix, with 0001 as the final row.
|
|
1482
|
-
* Multiply
|
|
1540
|
+
* Multiply the 4x4 matrix by `[x,y,z,w]`
|
|
1541
|
+
* ```
|
|
1542
|
+
* equation
|
|
1543
|
+
* \begin{bmatrix}M_0 & M_1 & M_2 & Ox \\ M_3 & M_4 & M_5 & Oy \\ M_6 & M_7 & M_8 & Oz \\ 0 & 0 & 0 & 1\end{bmatrix} * \begin{bmatrix}x \\ y \\ z \\ w\end{bmatrix}
|
|
1544
|
+
* ```
|
|
1483
1545
|
* @param origin translation part (xyz in column 3)
|
|
1484
1546
|
* @param matrix matrix part (leading 3x3)
|
|
1485
1547
|
* @param x x part of multiplied point
|
|
1486
1548
|
* @param y y part of multiplied point
|
|
1487
1549
|
* @param z z part of multiplied point
|
|
1488
1550
|
* @param w w part of multiplied point
|
|
1489
|
-
* @param result optional result.
|
|
1551
|
+
* @param result optional preallocated result.
|
|
1490
1552
|
*/
|
|
1491
1553
|
static xyzPlusMatrixTimesWeightedCoordinates(origin, matrix, x, y, z, w, result) {
|
|
1492
|
-
return Point4d.create(
|
|
1554
|
+
return Point4d.create(matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z + origin.x * w, matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z + origin.y * w, matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z + origin.z * w, w, result);
|
|
1493
1555
|
}
|
|
1494
1556
|
/**
|
|
1495
1557
|
* Treat the 3x3 matrix and origin as upper 3x4 part of a 4x4 matrix, with 0001 as the final row.
|
|
1496
|
-
* Multiply
|
|
1558
|
+
* Multiply the 4x4 matrix by `[x,y,z,w]`
|
|
1559
|
+
* ```
|
|
1560
|
+
* equation
|
|
1561
|
+
* \begin{bmatrix}M_0 & M_1 & M_2 & Ox \\ M_3 & M_4 & M_5 & Oy \\ M_6 & M_7 & M_8 & Oz \\ 0 & 0 & 0 & 1\end{bmatrix} * \begin{bmatrix}x \\ y \\ z \\ w\end{bmatrix}
|
|
1562
|
+
* ```
|
|
1497
1563
|
* @param origin translation part (xyz in column 3)
|
|
1498
1564
|
* @param matrix matrix part (leading 3x3)
|
|
1499
1565
|
* @param x x part of multiplied point
|
|
1500
1566
|
* @param y y part of multiplied point
|
|
1501
1567
|
* @param z z part of multiplied point
|
|
1502
1568
|
* @param w w part of multiplied point
|
|
1503
|
-
* @param result optional result.
|
|
1569
|
+
* @param result optional preallocated result.
|
|
1504
1570
|
*/
|
|
1505
1571
|
static xyzPlusMatrixTimesWeightedCoordinatesToFloat64Array(origin, matrix, x, y, z, w, result) {
|
|
1506
1572
|
if (!result)
|
|
1507
1573
|
result = new Float64Array(4);
|
|
1508
|
-
result[0] =
|
|
1509
|
-
result[1] =
|
|
1510
|
-
result[2] =
|
|
1574
|
+
result[0] = matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z + origin.x * w;
|
|
1575
|
+
result[1] = matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z + origin.y * w;
|
|
1576
|
+
result[2] = matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z + origin.z * w;
|
|
1511
1577
|
result[3] = w;
|
|
1512
1578
|
return result;
|
|
1513
1579
|
}
|
|
1514
1580
|
/**
|
|
1515
|
-
* Treat the 3x3 matrix and origin as
|
|
1516
|
-
* Multiply
|
|
1581
|
+
* Treat the 3x3 matrix and origin as a 3x4 matrix.
|
|
1582
|
+
* * Multiply the 3x4 matrix by `[x,y,z,1]`
|
|
1583
|
+
* ```
|
|
1584
|
+
* equation
|
|
1585
|
+
* \begin{bmatrix}M_0 & M_1 & M_2 & Ox \\ M_3 & M_4 & M_5 & Oy \\ M_6 & M_7 & M_8 & Oz\end{bmatrix} * \begin{bmatrix}x \\ y \\ z \\ 1\end{bmatrix}
|
|
1586
|
+
* ```
|
|
1517
1587
|
* @param origin translation part (xyz in column 3)
|
|
1518
1588
|
* @param matrix matrix part (leading 3x3)
|
|
1519
1589
|
* @param x x part of multiplied point
|
|
1520
1590
|
* @param y y part of multiplied point
|
|
1521
1591
|
* @param z z part of multiplied point
|
|
1522
|
-
* @param
|
|
1523
|
-
* @param result optional result.
|
|
1592
|
+
* @param result optional preallocated result.
|
|
1524
1593
|
*/
|
|
1525
1594
|
static xyzPlusMatrixTimesCoordinatesToFloat64Array(origin, matrix, x, y, z, result) {
|
|
1526
1595
|
if (!result)
|
|
1527
1596
|
result = new Float64Array(3);
|
|
1528
|
-
result[0] =
|
|
1529
|
-
result[1] =
|
|
1530
|
-
result[2] =
|
|
1597
|
+
result[0] = matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z + origin.x;
|
|
1598
|
+
result[1] = matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z + origin.y;
|
|
1599
|
+
result[2] = matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z + origin.z;
|
|
1531
1600
|
return result;
|
|
1532
1601
|
}
|
|
1533
1602
|
/**
|
|
1534
|
-
* Multiply transpose
|
|
1603
|
+
* Multiply the transpose matrix times a vector.
|
|
1535
1604
|
* * This produces the same x,y,z as treating the vector as a row on the left of the (un-transposed) matrix.
|
|
1536
1605
|
* ```
|
|
1537
1606
|
* equation
|
|
@@ -1540,99 +1609,112 @@ export class Matrix3d {
|
|
|
1540
1609
|
* \text{Treating U as a row to the left of untransposed matrix\: return row}&\rowSubXYZ{V}&=&\rowSubXYZ{U}\matrixXY{A}
|
|
1541
1610
|
* \end{matrix}
|
|
1542
1611
|
* ```
|
|
1543
|
-
* @
|
|
1612
|
+
* @param result the vector result (optional)
|
|
1544
1613
|
*/
|
|
1545
1614
|
multiplyTransposeVector(vector, result) {
|
|
1546
1615
|
result = result ? result : new Vector3d();
|
|
1547
1616
|
const x = vector.x;
|
|
1548
1617
|
const y = vector.y;
|
|
1549
1618
|
const z = vector.z;
|
|
1550
|
-
result.x =
|
|
1551
|
-
result.y =
|
|
1552
|
-
result.z =
|
|
1619
|
+
result.x = this.coffs[0] * x + this.coffs[3] * y + this.coffs[6] * z;
|
|
1620
|
+
result.y = this.coffs[1] * x + this.coffs[4] * y + this.coffs[7] * z;
|
|
1621
|
+
result.z = this.coffs[2] * x + this.coffs[5] * y + this.coffs[8] * z;
|
|
1553
1622
|
return result;
|
|
1554
1623
|
}
|
|
1555
|
-
/**
|
|
1556
|
-
*
|
|
1624
|
+
/**
|
|
1625
|
+
* Multiply the matrix * [x,y,z], i.e. the vector [x,y,z] is a column vector on the right.
|
|
1626
|
+
* @param result the vector result (optional)
|
|
1557
1627
|
*/
|
|
1558
1628
|
multiplyXYZ(x, y, z, result) {
|
|
1559
1629
|
result = result ? result : new Vector3d();
|
|
1560
|
-
result.x =
|
|
1561
|
-
result.y =
|
|
1562
|
-
result.z =
|
|
1630
|
+
result.x = this.coffs[0] * x + this.coffs[1] * y + this.coffs[2] * z;
|
|
1631
|
+
result.y = this.coffs[3] * x + this.coffs[4] * y + this.coffs[5] * z;
|
|
1632
|
+
result.z = this.coffs[6] * x + this.coffs[7] * y + this.coffs[8] * z;
|
|
1563
1633
|
return result;
|
|
1564
1634
|
}
|
|
1565
|
-
/**
|
|
1566
|
-
*
|
|
1567
|
-
*
|
|
1635
|
+
/**
|
|
1636
|
+
* Multiply the matrix * xyz, place result in (required) return value.
|
|
1637
|
+
* @param xyz right side
|
|
1638
|
+
* @param result the result.
|
|
1568
1639
|
*/
|
|
1569
1640
|
multiplyXYZtoXYZ(xyz, result) {
|
|
1570
1641
|
const x = xyz.x;
|
|
1571
1642
|
const y = xyz.y;
|
|
1572
1643
|
const z = xyz.z;
|
|
1573
|
-
result.x =
|
|
1574
|
-
result.y =
|
|
1575
|
-
result.z =
|
|
1644
|
+
result.x = this.coffs[0] * x + this.coffs[1] * y + this.coffs[2] * z;
|
|
1645
|
+
result.y = this.coffs[3] * x + this.coffs[4] * y + this.coffs[5] * z;
|
|
1646
|
+
result.z = this.coffs[6] * x + this.coffs[7] * y + this.coffs[8] * z;
|
|
1576
1647
|
return result;
|
|
1577
1648
|
}
|
|
1578
|
-
/**
|
|
1579
|
-
*
|
|
1649
|
+
/**
|
|
1650
|
+
* Multiply the matrix * [x,y,0], i.e. the vector [x,y,0] is a column vector on the right.
|
|
1651
|
+
* @param result the vector result (optional)
|
|
1580
1652
|
*/
|
|
1581
1653
|
multiplyXY(x, y, result) {
|
|
1582
1654
|
result = result ? result : new Vector3d();
|
|
1583
|
-
result.x =
|
|
1584
|
-
result.y =
|
|
1585
|
-
result.z =
|
|
1655
|
+
result.x = this.coffs[0] * x + this.coffs[1] * y;
|
|
1656
|
+
result.y = this.coffs[3] * x + this.coffs[4] * y;
|
|
1657
|
+
result.z = this.coffs[6] * x + this.coffs[7] * y;
|
|
1586
1658
|
return result;
|
|
1587
1659
|
}
|
|
1588
|
-
/**
|
|
1660
|
+
/**
|
|
1661
|
+
* Compute origin + the matrix * [x,y,0].
|
|
1662
|
+
* @param result the Point3d result (optional)
|
|
1663
|
+
*/
|
|
1589
1664
|
originPlusMatrixTimesXY(origin, x, y, result) {
|
|
1590
1665
|
return Point3d.create(origin.x + this.coffs[0] * x + this.coffs[1] * y, origin.y + this.coffs[3] * x + this.coffs[4] * y, origin.z + this.coffs[6] * x + this.coffs[7] * y, result);
|
|
1591
1666
|
}
|
|
1592
|
-
/**
|
|
1667
|
+
/**
|
|
1668
|
+
* Multiply the matrix * (x,y,z) in place, i.e. the vector (x,y,z) is a column vector on the right and
|
|
1669
|
+
* the multiplication updates the vector values.
|
|
1670
|
+
* @param xyzData the vector data.
|
|
1671
|
+
*/
|
|
1593
1672
|
multiplyVectorInPlace(xyzData) {
|
|
1594
1673
|
const x = xyzData.x;
|
|
1595
1674
|
const y = xyzData.y;
|
|
1596
1675
|
const z = xyzData.z;
|
|
1597
|
-
|
|
1598
|
-
xyzData.
|
|
1599
|
-
xyzData.
|
|
1600
|
-
xyzData.z = (coffs[6] * x + coffs[7] * y + coffs[8] * z);
|
|
1676
|
+
xyzData.x = this.coffs[0] * x + this.coffs[1] * y + this.coffs[2] * z;
|
|
1677
|
+
xyzData.y = this.coffs[3] * x + this.coffs[4] * y + this.coffs[5] * z;
|
|
1678
|
+
xyzData.z = this.coffs[6] * x + this.coffs[7] * y + this.coffs[8] * z;
|
|
1601
1679
|
}
|
|
1602
|
-
/**
|
|
1603
|
-
*
|
|
1680
|
+
/**
|
|
1681
|
+
* Multiply the transpose matrix times [x,y,z] in place, i.e. the vector [x,y,z] is a column vector on
|
|
1682
|
+
* the right and the multiplication updates the vector values.
|
|
1683
|
+
* * This is equivalent to `multiplyTransposeVector` but always returns the result directly in the input.
|
|
1684
|
+
* @param vectorU the vector data
|
|
1604
1685
|
*/
|
|
1605
1686
|
multiplyTransposeVectorInPlace(vectorU) {
|
|
1606
1687
|
const x = vectorU.x;
|
|
1607
1688
|
const y = vectorU.y;
|
|
1608
1689
|
const z = vectorU.z;
|
|
1609
|
-
|
|
1610
|
-
vectorU.
|
|
1611
|
-
vectorU.
|
|
1612
|
-
vectorU.z = (coffs[2] * x + coffs[5] * y + coffs[8] * z);
|
|
1690
|
+
vectorU.x = this.coffs[0] * x + this.coffs[3] * y + this.coffs[6] * z;
|
|
1691
|
+
vectorU.y = this.coffs[1] * x + this.coffs[4] * y + this.coffs[7] * z;
|
|
1692
|
+
vectorU.z = this.coffs[2] * x + this.coffs[5] * y + this.coffs[8] * z;
|
|
1613
1693
|
}
|
|
1614
|
-
/**
|
|
1615
|
-
*
|
|
1694
|
+
/**
|
|
1695
|
+
* Multiply the transpose matrix times column using individual numeric inputs.
|
|
1696
|
+
* * This produces the same x,y,z as treating the vector as a row on the left of the (un-transposed) matrix.
|
|
1616
1697
|
* ```
|
|
1617
1698
|
* equation
|
|
1618
1699
|
* \begin{matrix}
|
|
1619
|
-
* \text{treating the input as a column } \columnXYZ{x}{y}{z}\text{ compute }&\columnSubXYZ{V} &= &A^T \columnXYZ{x}{y}{z} \\
|
|
1620
|
-
* \text{or row vector } \rowXYZ{x}{y}{z} \text{ compute }&\rowSubXYZ{V} &= &\rowXYZ{x}{y}{z} A \\
|
|
1700
|
+
* \text{treating the input as a column vector } \columnXYZ{x}{y}{z}\text{ compute }&\columnSubXYZ{V} &= &A^T \columnXYZ{x}{y}{z} \\
|
|
1701
|
+
* \text{or as a row vector } \rowXYZ{x}{y}{z} \text{ compute }&\rowSubXYZ{V} &= &\rowXYZ{x}{y}{z} A \\
|
|
1621
1702
|
* \phantom{8888}\text{and return V as a Vector3d} & & &
|
|
1622
1703
|
* \end{matrix}
|
|
1623
1704
|
* ````
|
|
1624
|
-
* @
|
|
1705
|
+
* @param result the vector result (optional)
|
|
1625
1706
|
*/
|
|
1626
1707
|
multiplyTransposeXYZ(x, y, z, result) {
|
|
1627
1708
|
result = result ? result : new Vector3d();
|
|
1628
|
-
result.x =
|
|
1629
|
-
result.y =
|
|
1630
|
-
result.z =
|
|
1709
|
+
result.x = this.coffs[0] * x + this.coffs[3] * y + this.coffs[6] * z;
|
|
1710
|
+
result.y = this.coffs[1] * x + this.coffs[4] * y + this.coffs[7] * z;
|
|
1711
|
+
result.z = this.coffs[2] * x + this.coffs[5] * y + this.coffs[8] * z;
|
|
1631
1712
|
return result;
|
|
1632
1713
|
}
|
|
1633
|
-
/**
|
|
1714
|
+
/**
|
|
1715
|
+
* Solve `matrix * result = vector` for an unknown `result`.
|
|
1634
1716
|
* * This is equivalent to multiplication `result = matrixInverse * vector`.
|
|
1635
|
-
* * Result is undefined if the matrix is singular (e.g. has parallel or zero
|
|
1717
|
+
* * Result is `undefined` if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
|
|
1636
1718
|
*/
|
|
1637
1719
|
multiplyInverse(vector, result) {
|
|
1638
1720
|
this.computeCachedInverse(true);
|
|
@@ -1640,13 +1722,14 @@ export class Matrix3d {
|
|
|
1640
1722
|
const x = vector.x;
|
|
1641
1723
|
const y = vector.y;
|
|
1642
1724
|
const z = vector.z;
|
|
1643
|
-
return Vector3d.create(
|
|
1725
|
+
return Vector3d.create(this.inverseCoffs[0] * x + this.inverseCoffs[1] * y + this.inverseCoffs[2] * z, this.inverseCoffs[3] * x + this.inverseCoffs[4] * y + this.inverseCoffs[5] * z, this.inverseCoffs[6] * x + this.inverseCoffs[7] * y + this.inverseCoffs[8] * z, result);
|
|
1644
1726
|
}
|
|
1645
1727
|
return undefined;
|
|
1646
1728
|
}
|
|
1647
|
-
/**
|
|
1729
|
+
/**
|
|
1730
|
+
* Solve `matrixTranspose * result = vector` for an unknown `result`.
|
|
1648
1731
|
* * This is equivalent to multiplication `result = matrixInverseTranspose * vector`.
|
|
1649
|
-
* * Result is undefined if the matrix is singular (e.g. has parallel or zero
|
|
1732
|
+
* * Result is `undefined` if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
|
|
1650
1733
|
*/
|
|
1651
1734
|
multiplyInverseTranspose(vector, result) {
|
|
1652
1735
|
this.computeCachedInverse(true);
|
|
@@ -1654,87 +1737,91 @@ export class Matrix3d {
|
|
|
1654
1737
|
const x = vector.x;
|
|
1655
1738
|
const y = vector.y;
|
|
1656
1739
|
const z = vector.z;
|
|
1657
|
-
return Vector3d.create(
|
|
1740
|
+
return Vector3d.create(this.inverseCoffs[0] * x + this.inverseCoffs[3] * y + this.inverseCoffs[6] * z, this.inverseCoffs[1] * x + this.inverseCoffs[4] * y + this.inverseCoffs[7] * z, this.inverseCoffs[2] * x + this.inverseCoffs[5] * y + this.inverseCoffs[8] * z, result);
|
|
1658
1741
|
}
|
|
1659
1742
|
return undefined;
|
|
1660
1743
|
}
|
|
1661
1744
|
/**
|
|
1662
|
-
*
|
|
1663
|
-
* *
|
|
1664
|
-
* *
|
|
1745
|
+
* Multiply `matrixInverse * [x,y,z]`.
|
|
1746
|
+
* * This is equivalent to solving `matrix * result = [x,y,z]` for an unknown `result`.
|
|
1747
|
+
* * Result is `undefined` if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
|
|
1748
|
+
* @return result as a Vector3d or undefined (if the matrix is singular).
|
|
1665
1749
|
*/
|
|
1666
1750
|
multiplyInverseXYZAsVector3d(x, y, z, result) {
|
|
1667
1751
|
this.computeCachedInverse(true);
|
|
1668
1752
|
if (this.inverseCoffs) {
|
|
1669
|
-
return Vector3d.create(
|
|
1753
|
+
return Vector3d.create(this.inverseCoffs[0] * x + this.inverseCoffs[1] * y + this.inverseCoffs[2] * z, this.inverseCoffs[3] * x + this.inverseCoffs[4] * y + this.inverseCoffs[5] * z, this.inverseCoffs[6] * x + this.inverseCoffs[7] * y + this.inverseCoffs[8] * z, result);
|
|
1670
1754
|
}
|
|
1671
1755
|
return undefined;
|
|
1672
1756
|
}
|
|
1673
1757
|
/**
|
|
1674
|
-
*
|
|
1675
|
-
* *
|
|
1676
|
-
* *
|
|
1677
|
-
*
|
|
1758
|
+
* Multiply `matrixInverse * [x,y,z]` and return result as `Point4d` with given weight.
|
|
1759
|
+
* * Equivalent to solving `matrix * result = [x,y,z]` for an unknown `result`.
|
|
1760
|
+
* * Result is `undefined` if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
|
|
1761
|
+
* @return result as a Point4d with the same weight.
|
|
1678
1762
|
*/
|
|
1679
1763
|
multiplyInverseXYZW(x, y, z, w, result) {
|
|
1680
1764
|
this.computeCachedInverse(true);
|
|
1681
1765
|
if (this.inverseCoffs) {
|
|
1682
|
-
return Point4d.create(
|
|
1766
|
+
return Point4d.create(this.inverseCoffs[0] * x + this.inverseCoffs[1] * y + this.inverseCoffs[2] * z, this.inverseCoffs[3] * x + this.inverseCoffs[4] * y + this.inverseCoffs[5] * z, this.inverseCoffs[6] * x + this.inverseCoffs[7] * y + this.inverseCoffs[8] * z, w, result);
|
|
1683
1767
|
}
|
|
1684
1768
|
return undefined;
|
|
1685
1769
|
}
|
|
1686
1770
|
/**
|
|
1687
|
-
*
|
|
1688
|
-
* *
|
|
1689
|
-
*
|
|
1690
|
-
* * return as a Point3d.
|
|
1771
|
+
* Multiply `matrixInverse * [x,y,z]` and return result as `Point3d`.
|
|
1772
|
+
* * Equivalent to solving `matrix * result = [x,y,z]` for an unknown `result`.
|
|
1773
|
+
* @return result as a Point3d or `undefined` (if the matrix is singular).
|
|
1691
1774
|
*/
|
|
1692
1775
|
multiplyInverseXYZAsPoint3d(x, y, z, result) {
|
|
1693
1776
|
this.computeCachedInverse(true);
|
|
1694
1777
|
if (this.inverseCoffs) {
|
|
1695
|
-
return Point3d.create(
|
|
1778
|
+
return Point3d.create(this.inverseCoffs[0] * x + this.inverseCoffs[1] * y + this.inverseCoffs[2] * z, this.inverseCoffs[3] * x + this.inverseCoffs[4] * y + this.inverseCoffs[5] * z, this.inverseCoffs[6] * x + this.inverseCoffs[7] * y + this.inverseCoffs[8] * z, result);
|
|
1696
1779
|
}
|
|
1697
1780
|
return undefined;
|
|
1698
1781
|
}
|
|
1699
1782
|
/**
|
|
1700
|
-
*
|
|
1701
|
-
* * set
|
|
1702
|
-
*
|
|
1783
|
+
* Invoke a given matrix*matrix operation to compute the inverse matrix and set this.inverseCoffs
|
|
1784
|
+
* * If either input coffA or coffB is `undefined`, set state to `InverseMatrixState.unknown` but
|
|
1785
|
+
* leave the inverseCoffs untouched.
|
|
1786
|
+
* @param f the given matrix*matrix operation that is called by this function to compute the inverse.
|
|
1787
|
+
* `f` must be a matrix*matrix operation. Otherwise, the function does not generate the inverse properly.
|
|
1703
1788
|
*/
|
|
1704
1789
|
finishInverseCoffs(f, coffA, coffB) {
|
|
1705
1790
|
if (coffA && coffB) {
|
|
1706
1791
|
this.createInverseCoffsWithZeros();
|
|
1707
1792
|
this.inverseState = InverseMatrixState.inverseStored;
|
|
1708
|
-
f(coffA, coffB, this.inverseCoffs);
|
|
1793
|
+
f(coffA, coffB, this.inverseCoffs); // call function f (which is provided by user) to compute the inverse.
|
|
1709
1794
|
}
|
|
1710
1795
|
else {
|
|
1711
1796
|
this.inverseState = InverseMatrixState.unknown;
|
|
1712
1797
|
}
|
|
1713
1798
|
}
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
* @return the matrix product A * B
|
|
1799
|
+
// Notes on inverse of matrix products:
|
|
1800
|
+
// 1) M = A * B ===> MInverse = BInverse * AInverse
|
|
1801
|
+
// 2) M = A * BInverse ===> MInverse = B * AInverse
|
|
1802
|
+
// 3) M = AInverse * B ===> MInverse = BInverse * A
|
|
1803
|
+
// 4) M = A * BTranspose ===> MInverse = BInverseTranspose * AInverse
|
|
1804
|
+
// 5) M = ATranspose * B ===> MInverse = BInverse * AInverseTranspose
|
|
1805
|
+
/**
|
|
1806
|
+
* Multiply `this` matrix times `other` matrix
|
|
1807
|
+
* @return the matrix result: this*other
|
|
1724
1808
|
*/
|
|
1725
1809
|
multiplyMatrixMatrix(other, result) {
|
|
1726
1810
|
result = result ? result : new Matrix3d();
|
|
1727
1811
|
PackedMatrix3dOps.multiplyMatrixMatrix(this.coffs, other.coffs, result.coffs);
|
|
1728
|
-
if (this.inverseState === InverseMatrixState.inverseStored
|
|
1812
|
+
if (this.inverseState === InverseMatrixState.inverseStored
|
|
1813
|
+
&& other.inverseState === InverseMatrixState.inverseStored)
|
|
1729
1814
|
result.finishInverseCoffs(PackedMatrix3dOps.multiplyMatrixMatrix, other.inverseCoffs, this.inverseCoffs);
|
|
1730
|
-
else if (this.inverseState === InverseMatrixState.singular
|
|
1815
|
+
else if (this.inverseState === InverseMatrixState.singular
|
|
1816
|
+
|| other.inverseState === InverseMatrixState.singular)
|
|
1731
1817
|
result.inverseState = InverseMatrixState.singular;
|
|
1732
1818
|
else
|
|
1733
1819
|
result.inverseState = InverseMatrixState.unknown;
|
|
1734
1820
|
return result;
|
|
1735
1821
|
}
|
|
1736
|
-
/**
|
|
1737
|
-
*
|
|
1822
|
+
/**
|
|
1823
|
+
* Multiply `this` matrix times `inverse of other` matrix
|
|
1824
|
+
* @return the matrix result: this*otherInverse
|
|
1738
1825
|
*/
|
|
1739
1826
|
multiplyMatrixMatrixInverse(other, result) {
|
|
1740
1827
|
if (!other.computeCachedInverse(true))
|
|
@@ -1748,8 +1835,9 @@ export class Matrix3d {
|
|
|
1748
1835
|
PackedMatrix3dOps.copy(Matrix3d._productBuffer, result.coffs);
|
|
1749
1836
|
return result;
|
|
1750
1837
|
}
|
|
1751
|
-
/**
|
|
1752
|
-
*
|
|
1838
|
+
/**
|
|
1839
|
+
* Multiply `inverse of this` matrix times `other` matrix
|
|
1840
|
+
* @return the matrix result: thisInverse*other
|
|
1753
1841
|
*/
|
|
1754
1842
|
multiplyMatrixInverseMatrix(other, result) {
|
|
1755
1843
|
if (!this.computeCachedInverse(true))
|
|
@@ -1763,30 +1851,32 @@ export class Matrix3d {
|
|
|
1763
1851
|
PackedMatrix3dOps.copy(Matrix3d._productBuffer, result.coffs);
|
|
1764
1852
|
return result;
|
|
1765
1853
|
}
|
|
1766
|
-
/**
|
|
1854
|
+
/**
|
|
1855
|
+
* Multiply `this` matrix times the transpose of `other` matrix
|
|
1767
1856
|
* ```
|
|
1768
1857
|
* equation
|
|
1769
|
-
* \text{for instance matrix }A\text{ and
|
|
1858
|
+
* \text{for instance matrix }A\text{ and matrix }B\text{ return matrix }C{\text where }\\\matrixXY{C}=\matrixXY{A}\matrixTransposeSubXY{B}
|
|
1770
1859
|
* ```
|
|
1771
|
-
* @return the matrix result
|
|
1860
|
+
* @return the matrix result: this*otherTranspose
|
|
1772
1861
|
*/
|
|
1773
|
-
multiplyMatrixMatrixTranspose(
|
|
1862
|
+
multiplyMatrixMatrixTranspose(other, result) {
|
|
1774
1863
|
result = result ? result : new Matrix3d();
|
|
1775
|
-
PackedMatrix3dOps.multiplyMatrixMatrixTranspose(this.coffs,
|
|
1776
|
-
if (this.inverseState === InverseMatrixState.inverseStored &&
|
|
1777
|
-
result.finishInverseCoffs(PackedMatrix3dOps.multiplyMatrixTransposeMatrix,
|
|
1778
|
-
else if (this.inverseState === InverseMatrixState.singular ||
|
|
1864
|
+
PackedMatrix3dOps.multiplyMatrixMatrixTranspose(this.coffs, other.coffs, result.coffs);
|
|
1865
|
+
if (this.inverseState === InverseMatrixState.inverseStored && other.inverseState === InverseMatrixState.inverseStored)
|
|
1866
|
+
result.finishInverseCoffs(PackedMatrix3dOps.multiplyMatrixTransposeMatrix, other.inverseCoffs, this.inverseCoffs);
|
|
1867
|
+
else if (this.inverseState === InverseMatrixState.singular || other.inverseState === InverseMatrixState.singular)
|
|
1779
1868
|
result.inverseState = InverseMatrixState.singular;
|
|
1780
1869
|
else
|
|
1781
1870
|
result.inverseState = InverseMatrixState.unknown;
|
|
1782
1871
|
return result;
|
|
1783
1872
|
}
|
|
1784
|
-
/**
|
|
1873
|
+
/**
|
|
1874
|
+
* Multiply the transpose of `this` matrix times `other` matrix
|
|
1785
1875
|
* ```
|
|
1786
1876
|
* equation
|
|
1787
1877
|
* \matrixXY{result}=\matrixXY{\text{this}}\matrixTransposeSubXY{\text{other}}
|
|
1788
1878
|
* ```
|
|
1789
|
-
*
|
|
1879
|
+
* @return the matrix result: thisTranspose*other
|
|
1790
1880
|
*/
|
|
1791
1881
|
multiplyMatrixTransposeMatrix(other, result) {
|
|
1792
1882
|
result = result ? result : new Matrix3d();
|
|
@@ -1799,30 +1889,31 @@ export class Matrix3d {
|
|
|
1799
1889
|
result.inverseState = InverseMatrixState.unknown;
|
|
1800
1890
|
return result;
|
|
1801
1891
|
}
|
|
1802
|
-
/**
|
|
1892
|
+
/**
|
|
1893
|
+
* Multiply `this` Matrix3d (considered as a Transform with 0 translation) times `other` Transform.
|
|
1803
1894
|
* ```
|
|
1804
1895
|
* equation
|
|
1805
1896
|
* \begin{matrix}
|
|
1806
|
-
*
|
|
1807
|
-
*
|
|
1897
|
+
* \text{This matrix }\bold{A}\text{ promoted to block transform} & \blockTransform{A}{0} \\
|
|
1898
|
+
* \text{other transform with matrix part }\bold{B}\text{ and translation }\bold{b} & \blockTransform{B}{b}\\
|
|
1808
1899
|
* \text{product}& \blockTransform{A}{0}\blockTransform{B}{b}=\blockTransform{AB}{Ab}
|
|
1809
1900
|
* \end{matrix}
|
|
1810
1901
|
* ```
|
|
1811
|
-
* @param other
|
|
1812
|
-
* @param result
|
|
1902
|
+
* @param other Right hand Matrix3d for multiplication.
|
|
1903
|
+
* @param result the Transform result (optional)
|
|
1813
1904
|
*/
|
|
1814
1905
|
multiplyMatrixTransform(other, result) {
|
|
1815
1906
|
if (!result)
|
|
1816
1907
|
return Transform.createRefs(this.multiplyXYZ(other.origin.x, other.origin.y, other.origin.z), this.multiplyMatrixMatrix(other.matrix));
|
|
1817
|
-
// be sure to do the point multiplication first before aliasing changes the matrix
|
|
1908
|
+
// be sure to do the point multiplication first before aliasing changes the matrix
|
|
1818
1909
|
this.multiplyXYZtoXYZ(other.origin, result.origin);
|
|
1819
1910
|
this.multiplyMatrixMatrix(other.matrix, result.matrix);
|
|
1820
1911
|
return result;
|
|
1821
1912
|
}
|
|
1822
1913
|
/**
|
|
1823
1914
|
* Return the transpose of `this` matrix.
|
|
1824
|
-
* If `result` is passed as argument, then the function copies the transpose of `this` into `result
|
|
1825
|
-
* `this` is not changed unless also passed as the result, i.e., this.transpose(this) transposes `this` in place
|
|
1915
|
+
* * If `result` is passed as argument, then the function copies the transpose of `this` into `result`.
|
|
1916
|
+
* * `this` is not changed unless also passed as the result, i.e., `this.transpose(this)` transposes `this` in place.
|
|
1826
1917
|
*/
|
|
1827
1918
|
transpose(result) {
|
|
1828
1919
|
if (!result)
|
|
@@ -1844,18 +1935,22 @@ export class Matrix3d {
|
|
|
1844
1935
|
transposeInPlace() {
|
|
1845
1936
|
PackedMatrix3dOps.transposeInPlace(this.coffs);
|
|
1846
1937
|
if (this.inverseCoffs)
|
|
1847
|
-
PackedMatrix3dOps.transposeInPlace(this.inverseCoffs);
|
|
1938
|
+
PackedMatrix3dOps.transposeInPlace(this.inverseCoffs); // inverse of transpose is equal to transpose of inverse
|
|
1848
1939
|
}
|
|
1849
|
-
/**
|
|
1850
|
-
*
|
|
1851
|
-
*
|
|
1852
|
-
* *
|
|
1940
|
+
/**
|
|
1941
|
+
* Return the inverse matrix.
|
|
1942
|
+
* The return is undefined if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
|
|
1943
|
+
* * If `result == this`, then content of inverse of `this` matrix is copied into `this`. Otherwise, inverse
|
|
1944
|
+
* of `this` is stored in `result`.
|
|
1945
|
+
* * **Note:** Each Matrix3d object caches its own inverse (`this.inverseCoffs`) and has methods to multiply
|
|
1946
|
+
* the inverse times matrices and vectors (e.g., `multiplyMatrixInverseMatrix`, `multiplyMatrixMatrixInverse`,
|
|
1947
|
+
* `multiplyInverse`). Hence explicitly constructing this new inverse object is rarely necessary.
|
|
1853
1948
|
*/
|
|
1854
1949
|
inverse(result) {
|
|
1855
1950
|
if (!this.computeCachedInverse(true))
|
|
1856
1951
|
return undefined;
|
|
1857
1952
|
if (result === this) {
|
|
1858
|
-
// swap the contents
|
|
1953
|
+
// swap the contents of this.coffs and this.inverseCoffs
|
|
1859
1954
|
PackedMatrix3dOps.copy(this.coffs, Matrix3d._productBuffer);
|
|
1860
1955
|
PackedMatrix3dOps.copy(this.inverseCoffs, this.coffs);
|
|
1861
1956
|
PackedMatrix3dOps.copy(Matrix3d._productBuffer, this.inverseCoffs);
|
|
@@ -1870,33 +1965,50 @@ export class Matrix3d {
|
|
|
1870
1965
|
result.inverseState = this.inverseState;
|
|
1871
1966
|
return result;
|
|
1872
1967
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1968
|
+
/**
|
|
1969
|
+
* Take the dot product of a row (specified by `rowStartA`) of `coffA` and `columnStartB` of `coffB`.
|
|
1970
|
+
* * **Note:** We don't validate row/column numbers. Pass 0/3/6 for row 0/1/2 and pass 0/1/2 for column 0/1/2.
|
|
1971
|
+
*/
|
|
1972
|
+
static rowColumnDot(coffA, rowStartA, coffB, columnStartB) {
|
|
1973
|
+
return coffA[rowStartA] * coffB[columnStartB] +
|
|
1974
|
+
coffA[rowStartA + 1] * coffB[columnStartB + 3] +
|
|
1975
|
+
coffA[rowStartA + 2] * coffB[columnStartB + 6];
|
|
1976
|
+
}
|
|
1977
|
+
/**
|
|
1978
|
+
* Take the cross product of 2 rows (specified by `rowStart0` and `rowStart1`) of `source` and store the result
|
|
1979
|
+
* in `columnStart` of `dest`.
|
|
1980
|
+
* * **Note:** We don't validate row/column numbers. Pass 0/3/6 for row 0/1/2 and pass 0/1/2 for column 0/1/2.
|
|
1981
|
+
*/
|
|
1877
1982
|
static indexedRowCrossProduct(source, rowStart0, rowStart1, dest, columnStart) {
|
|
1878
1983
|
dest[columnStart] = source[rowStart0 + 1] * source[rowStart1 + 2] - source[rowStart0 + 2] * source[rowStart1 + 1];
|
|
1879
1984
|
dest[columnStart + 3] = source[rowStart0 + 2] * source[rowStart1] - source[rowStart0] * source[rowStart1 + 2];
|
|
1880
1985
|
dest[columnStart + 6] = source[rowStart0] * source[rowStart1 + 1] - source[rowStart0 + 1] * source[rowStart1];
|
|
1881
1986
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1987
|
+
/**
|
|
1988
|
+
* Take the cross product of 2 columns (i.e., `colStart0` and `colStart1`) of `this` matrix and store the
|
|
1989
|
+
* result in `colStart2` of the same matrix.
|
|
1990
|
+
* * **Note:** We don't validate column numbers. Pass 0/1/2 for column 0/1/2.
|
|
1991
|
+
*/
|
|
1885
1992
|
indexedColumnCrossProductInPlace(colStart0, colStart1, colStart2) {
|
|
1886
1993
|
const coffs = this.coffs;
|
|
1887
1994
|
coffs[colStart2] = coffs[colStart0 + 3] * coffs[colStart1 + 6] - coffs[colStart0 + 6] * coffs[colStart1 + 3];
|
|
1888
1995
|
coffs[colStart2 + 3] = coffs[colStart0 + 6] * coffs[colStart1] - coffs[colStart0] * coffs[colStart1 + 6];
|
|
1889
1996
|
coffs[colStart2 + 6] = coffs[colStart0] * coffs[colStart1 + 3] - coffs[colStart0 + 3] * coffs[colStart1];
|
|
1890
1997
|
}
|
|
1891
|
-
/**
|
|
1892
|
-
*
|
|
1893
|
-
*
|
|
1998
|
+
/**
|
|
1999
|
+
* Form cross products among columns in axisOrder.
|
|
2000
|
+
* For axis order ABC:
|
|
2001
|
+
* * form cross product of column A and B, store in C.
|
|
1894
2002
|
* * form cross product of column C and A, store in B.
|
|
2003
|
+
* * [A B C] ===> [A B AxB] ===> [A (AxB)xA AxB]
|
|
2004
|
+
*
|
|
1895
2005
|
* This means that in the final matrix:
|
|
1896
|
-
* * column
|
|
1897
|
-
* * column
|
|
1898
|
-
* * column
|
|
1899
|
-
* * original column C does not participate in the result.
|
|
2006
|
+
* * first column is same as original column A.
|
|
2007
|
+
* * second column is linear combination of original A and B (i.e., is in the plane of original A and B).
|
|
2008
|
+
* * third column is perpendicular to first and second columns of both the original and final.
|
|
2009
|
+
* * original column C is overwritten and does not participate in the result.
|
|
2010
|
+
*
|
|
2011
|
+
* The final matrix will have 3 orthogonal columns.
|
|
1900
2012
|
*/
|
|
1901
2013
|
axisOrderCrossProductsInPlace(axisOrder) {
|
|
1902
2014
|
switch (axisOrder) {
|
|
@@ -1932,41 +2044,44 @@ export class Matrix3d {
|
|
|
1932
2044
|
}
|
|
1933
2045
|
}
|
|
1934
2046
|
}
|
|
1935
|
-
/**
|
|
1936
|
-
*
|
|
1937
|
-
* @
|
|
1938
|
-
* @
|
|
2047
|
+
/**
|
|
2048
|
+
* Normalize each column in place.
|
|
2049
|
+
* @param originalColumnMagnitudes optional vector to store original column magnitudes.
|
|
2050
|
+
* @returns return true if all columns have non-zero lengths. Otherwise, return false.
|
|
2051
|
+
* * If false is returned, the magnitudes are stored in the `originalColumnMagnitudes` vector but no columns
|
|
2052
|
+
* are altered.
|
|
1939
2053
|
*/
|
|
1940
|
-
normalizeColumnsInPlace(
|
|
2054
|
+
normalizeColumnsInPlace(originalColumnMagnitudes) {
|
|
1941
2055
|
const ax = this.columnXMagnitude();
|
|
1942
2056
|
const ay = this.columnYMagnitude();
|
|
1943
2057
|
const az = this.columnZMagnitude();
|
|
1944
|
-
if (
|
|
1945
|
-
|
|
2058
|
+
if (originalColumnMagnitudes)
|
|
2059
|
+
originalColumnMagnitudes.set(ax, ay, az);
|
|
1946
2060
|
if (Geometry.isSmallMetricDistance(ax) || Geometry.isSmallMetricDistance(ay) || Geometry.isSmallMetricDistance(az))
|
|
1947
2061
|
return false;
|
|
1948
2062
|
this.scaleColumns(1.0 / ax, 1.0 / ay, 1.0 / az, this);
|
|
1949
2063
|
return true;
|
|
1950
2064
|
}
|
|
1951
|
-
/**
|
|
1952
|
-
|
|
2065
|
+
/**
|
|
2066
|
+
* Normalize each row in place.
|
|
2067
|
+
* @param originalRowMagnitudes optional vector to store original row magnitudes.
|
|
2068
|
+
* @returns return true if all rows have non-zero lengths. Otherwise, return false.
|
|
2069
|
+
* * If false is returned, the magnitudes are stored in the `originalRowMagnitudes` vector but no rows
|
|
2070
|
+
* are altered.
|
|
2071
|
+
*/
|
|
2072
|
+
normalizeRowsInPlace(originalRowMagnitudes) {
|
|
1953
2073
|
const ax = this.rowXMagnitude();
|
|
1954
2074
|
const ay = this.rowYMagnitude();
|
|
1955
2075
|
const az = this.rowZMagnitude();
|
|
1956
|
-
if (
|
|
1957
|
-
|
|
2076
|
+
if (originalRowMagnitudes)
|
|
2077
|
+
originalRowMagnitudes.set(ax, ay, az);
|
|
1958
2078
|
if (Geometry.isSmallMetricDistance(ax) || Geometry.isSmallMetricDistance(ay) || Geometry.isSmallMetricDistance(az))
|
|
1959
2079
|
return false;
|
|
1960
2080
|
this.scaleRows(1.0 / ax, 1.0 / ay, 1.0 / az, this);
|
|
1961
2081
|
return true;
|
|
1962
2082
|
}
|
|
1963
|
-
// take the cross product of two rows of source.
|
|
1964
|
-
// store as a column of dest.
|
|
1965
|
-
static rowColumnDot(coffA, rowStartA, coffB, columnStartB) {
|
|
1966
|
-
return coffA[rowStartA] * coffB[columnStartB] + coffA[rowStartA + 1] * coffB[columnStartB + 3] + coffA[rowStartA + 2] * coffB[columnStartB + 6];
|
|
1967
|
-
}
|
|
1968
2083
|
/**
|
|
1969
|
-
* Returns true if the matrix is singular
|
|
2084
|
+
* Returns true if the matrix is singular.
|
|
1970
2085
|
*/
|
|
1971
2086
|
isSingular() {
|
|
1972
2087
|
return !this.computeCachedInverse(true);
|
|
@@ -1978,18 +2093,10 @@ export class Matrix3d {
|
|
|
1978
2093
|
this.inverseState = InverseMatrixState.singular;
|
|
1979
2094
|
}
|
|
1980
2095
|
/**
|
|
1981
|
-
*
|
|
1982
|
-
*
|
|
1983
|
-
*
|
|
1984
|
-
|
|
1985
|
-
createInverseCoffsWithZeros() {
|
|
1986
|
-
if (!this.inverseCoffs) {
|
|
1987
|
-
this.inverseState = InverseMatrixState.unknown;
|
|
1988
|
-
this.inverseCoffs = new Float64Array(9);
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
/** compute the inverse of this Matrix3d. The inverse is stored for later use.
|
|
1992
|
-
* @returns Return true if the inverse computed. (False if the columns collapse to a point, line or plane.)
|
|
2096
|
+
* Compute the inverse of `this` Matrix3d. The inverse is stored in `this.inverseCoffs` for later use.
|
|
2097
|
+
* @param useCacheIfAvailable if `true`, use the previously computed inverse if available. If `false`,
|
|
2098
|
+
* recompute the inverse.
|
|
2099
|
+
* @returns return `true` if the inverse is computed. Return `false` if matrix is singular.
|
|
1993
2100
|
*/
|
|
1994
2101
|
computeCachedInverse(useCacheIfAvailable) {
|
|
1995
2102
|
if (useCacheIfAvailable && Matrix3d.useCachedInverse && this.inverseState !== InverseMatrixState.unknown) {
|
|
@@ -2000,71 +2107,46 @@ export class Matrix3d {
|
|
|
2000
2107
|
this.createInverseCoffsWithZeros();
|
|
2001
2108
|
const coffs = this.coffs;
|
|
2002
2109
|
const inverseCoffs = this.inverseCoffs;
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2110
|
+
/**
|
|
2111
|
+
* We calculate the inverse using cross products.
|
|
2112
|
+
* Math details can be found at docs/learning/matrix/Matrix.md
|
|
2113
|
+
* [ A ]
|
|
2114
|
+
* In summary, if M = [ B ] then inverse of M = (1/det)[BxC CxA AxB] where
|
|
2115
|
+
* [ C ]
|
|
2116
|
+
* det is the determinant of matrix M (which is equal to "A dot BxC").
|
|
2117
|
+
*/
|
|
2118
|
+
Matrix3d.indexedRowCrossProduct(coffs, 3, 6, inverseCoffs, 0); // BxC
|
|
2119
|
+
Matrix3d.indexedRowCrossProduct(coffs, 6, 0, inverseCoffs, 1); // CxA
|
|
2120
|
+
Matrix3d.indexedRowCrossProduct(coffs, 0, 3, inverseCoffs, 2); // AxB
|
|
2006
2121
|
Matrix3d.numComputeCache++;
|
|
2007
|
-
const
|
|
2008
|
-
if (
|
|
2122
|
+
const det = Matrix3d.rowColumnDot(coffs, 0, inverseCoffs, 0); // A dot BxC
|
|
2123
|
+
if (det === 0.0) {
|
|
2009
2124
|
this.inverseState = InverseMatrixState.singular;
|
|
2010
2125
|
this.inverseCoffs = undefined;
|
|
2011
2126
|
return false;
|
|
2012
2127
|
}
|
|
2013
|
-
const f = 1.0 /
|
|
2128
|
+
const f = 1.0 / det;
|
|
2014
2129
|
for (let i = 0; i < 9; i++)
|
|
2015
2130
|
inverseCoffs[i] *= f;
|
|
2016
2131
|
this.inverseState = InverseMatrixState.inverseStored;
|
|
2017
|
-
// verify inverse
|
|
2018
|
-
// const p = new Float64Array(9);
|
|
2019
|
-
// for (let i = 0; i < 9; i += 3)
|
|
2020
|
-
// for (let j = 0; j < 3; j++)
|
|
2021
|
-
// p[i + j] = Matrix3d.rowColumnDot (coffs, i, inverseCoffs, j);
|
|
2022
2132
|
return true;
|
|
2023
2133
|
}
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
private static crossXY: Vector3d = Vector3d.create();
|
|
2029
|
-
private static crossZX: Vector3d = Vector3d.create();
|
|
2030
|
-
private static crossYZ: Vector3d = Vector3d.create();
|
|
2031
|
-
private computeCachedInverse(useCacheIfAvailable: boolean) {
|
|
2032
|
-
if (useCacheIfAvailable && Matrix3d.useCachedInverse && this.inverseState !== InverseMatrixState.unknown) {
|
|
2033
|
-
Matrix3d.numUseCache++;
|
|
2034
|
-
return this.inverseState === InverseMatrixState.inverseStored;
|
|
2035
|
-
}
|
|
2036
|
-
this.inverseState = InverseMatrixState.unknown;
|
|
2037
|
-
Matrix3d.numComputeCache++;
|
|
2038
|
-
const rowX = this.rowX(Matrix3d.rowX);
|
|
2039
|
-
const rowY = this.rowY(Matrix3d.rowY);
|
|
2040
|
-
const rowZ = this.rowZ(Matrix3d.rowZ);
|
|
2041
|
-
const crossXY = rowX.crossProduct(rowY, Matrix3d.crossXY);
|
|
2042
|
-
const crossYZ = rowY.crossProduct(rowZ, Matrix3d.crossYZ);
|
|
2043
|
-
const crossZX = rowZ.crossProduct(rowX, Matrix3d.crossZX);
|
|
2044
|
-
const d = rowX.dotProduct(crossYZ); // that's the determinant
|
|
2045
|
-
if (d === 0.0) { // better test?
|
|
2046
|
-
this.inverseState = InverseMatrixState.singular;
|
|
2047
|
-
this.inverseCoffs = undefined;
|
|
2048
|
-
return false;
|
|
2049
|
-
}
|
|
2050
|
-
const f = 1.0 / d;
|
|
2051
|
-
this.inverseState = InverseMatrixState.inverseStored; // Currently just lists that the inverse has been stored... singular case not handled
|
|
2052
|
-
this.inverseCoffs = Float64Array.from([crossYZ.x * f, crossZX.x * f, crossXY.x * f,
|
|
2053
|
-
crossYZ.y * f, crossZX.y * f, crossXY.y * f,
|
|
2054
|
-
crossYZ.z * f, crossZX.z * f, crossXY.z * f]);
|
|
2055
|
-
return true;
|
|
2056
|
-
}
|
|
2057
|
-
*/
|
|
2058
|
-
/** convert a (row,column) index pair to the single index within flattened array of 9 numbers in row-major-order */
|
|
2134
|
+
/**
|
|
2135
|
+
* Convert a (row,column) index pair to the single index within flattened array of 9 numbers in row-major-order
|
|
2136
|
+
* * **Note:** Out of range row/column is interpreted cyclically.
|
|
2137
|
+
*/
|
|
2059
2138
|
static flatIndexOf(row, column) {
|
|
2060
2139
|
return 3 * Geometry.cyclic3dAxis(row) + Geometry.cyclic3dAxis(column);
|
|
2061
2140
|
}
|
|
2062
|
-
/**
|
|
2141
|
+
/**
|
|
2142
|
+
* Get elements of column `index` packaged as a Point4d with given `weight`.
|
|
2143
|
+
* * **Note:** Out of range index is interpreted cyclically.
|
|
2144
|
+
*/
|
|
2063
2145
|
indexedColumnWithWeight(index, weight, result) {
|
|
2064
2146
|
index = Geometry.cyclic3dAxis(index);
|
|
2065
2147
|
return Point4d.create(this.coffs[index], this.coffs[index + 3], this.coffs[index + 6], weight, result);
|
|
2066
2148
|
}
|
|
2067
|
-
/**
|
|
2149
|
+
/** Return the entry at specific row and column */
|
|
2068
2150
|
at(row, column) {
|
|
2069
2151
|
return this.coffs[Matrix3d.flatIndexOf(row, column)];
|
|
2070
2152
|
}
|
|
@@ -2073,19 +2155,30 @@ export class Matrix3d {
|
|
|
2073
2155
|
this.coffs[Matrix3d.flatIndexOf(row, column)] = value;
|
|
2074
2156
|
this.inverseState = InverseMatrixState.unknown;
|
|
2075
2157
|
}
|
|
2076
|
-
/**
|
|
2077
|
-
*
|
|
2078
|
-
* @param
|
|
2079
|
-
* @param scaleZ scale factor for column z
|
|
2158
|
+
/**
|
|
2159
|
+
* Create a Matrix3d whose values are uniformly scaled from `this` Matrix3d.
|
|
2160
|
+
* @param scale scale factor to apply.
|
|
2080
2161
|
* @param result optional result.
|
|
2162
|
+
* @returns return the scaled matrix.
|
|
2163
|
+
*/
|
|
2164
|
+
scale(scale, result) {
|
|
2165
|
+
return Matrix3d.createRowValues(this.coffs[0] * scale, this.coffs[1] * scale, this.coffs[2] * scale, this.coffs[3] * scale, this.coffs[4] * scale, this.coffs[5] * scale, this.coffs[6] * scale, this.coffs[7] * scale, this.coffs[8] * scale, result);
|
|
2166
|
+
}
|
|
2167
|
+
/**
|
|
2168
|
+
* Create a Matrix3d whose columns are scaled copies of `this` Matrix3d.
|
|
2169
|
+
* @param scaleX scale factor for column 0
|
|
2170
|
+
* @param scaleY scale factor for column 1
|
|
2171
|
+
* @param scaleZ scale factor for column 2
|
|
2172
|
+
* @param result optional result
|
|
2081
2173
|
*/
|
|
2082
2174
|
scaleColumns(scaleX, scaleY, scaleZ, result) {
|
|
2083
2175
|
return Matrix3d.createRowValues(this.coffs[0] * scaleX, this.coffs[1] * scaleY, this.coffs[2] * scaleZ, this.coffs[3] * scaleX, this.coffs[4] * scaleY, this.coffs[5] * scaleZ, this.coffs[6] * scaleX, this.coffs[7] * scaleY, this.coffs[8] * scaleZ, result);
|
|
2084
2176
|
}
|
|
2085
|
-
/**
|
|
2086
|
-
*
|
|
2087
|
-
* @param
|
|
2088
|
-
* @param
|
|
2177
|
+
/**
|
|
2178
|
+
* Scale the columns of `this` Matrix3d in place.
|
|
2179
|
+
* @param scaleX scale factor for column 0
|
|
2180
|
+
* @param scaleY scale factor for column 1
|
|
2181
|
+
* @param scaleZ scale factor for column 2
|
|
2089
2182
|
*/
|
|
2090
2183
|
scaleColumnsInPlace(scaleX, scaleY, scaleZ) {
|
|
2091
2184
|
this.coffs[0] *= scaleX;
|
|
@@ -2098,7 +2191,7 @@ export class Matrix3d {
|
|
|
2098
2191
|
this.coffs[7] *= scaleY;
|
|
2099
2192
|
this.coffs[8] *= scaleZ;
|
|
2100
2193
|
if (this.inverseState === InverseMatrixState.inverseStored && this.inverseCoffs !== undefined) {
|
|
2101
|
-
// apply reciprocal scales to the ROWS of the inverse
|
|
2194
|
+
// apply reciprocal scales to the ROWS of the inverse
|
|
2102
2195
|
const divX = Geometry.conditionalDivideFraction(1.0, scaleX);
|
|
2103
2196
|
const divY = Geometry.conditionalDivideFraction(1.0, scaleY);
|
|
2104
2197
|
const divZ = Geometry.conditionalDivideFraction(1.0, scaleZ);
|
|
@@ -2117,18 +2210,55 @@ export class Matrix3d {
|
|
|
2117
2210
|
this.inverseState = InverseMatrixState.singular;
|
|
2118
2211
|
}
|
|
2119
2212
|
}
|
|
2120
|
-
/**
|
|
2121
|
-
*
|
|
2122
|
-
* @param
|
|
2123
|
-
* @param
|
|
2124
|
-
* @param
|
|
2213
|
+
/**
|
|
2214
|
+
* Create a Matrix3d whose rows are scaled copies of `this` Matrix3d.
|
|
2215
|
+
* @param scaleX scale factor for row 0
|
|
2216
|
+
* @param scaleY scale factor for row 1
|
|
2217
|
+
* @param scaleZ scale factor for row 2
|
|
2218
|
+
* @param result optional result
|
|
2125
2219
|
*/
|
|
2126
2220
|
scaleRows(scaleX, scaleY, scaleZ, result) {
|
|
2127
2221
|
return Matrix3d.createRowValues(this.coffs[0] * scaleX, this.coffs[1] * scaleX, this.coffs[2] * scaleX, this.coffs[3] * scaleY, this.coffs[4] * scaleY, this.coffs[5] * scaleY, this.coffs[6] * scaleZ, this.coffs[7] * scaleZ, this.coffs[8] * scaleZ, result);
|
|
2128
2222
|
}
|
|
2129
2223
|
/**
|
|
2130
|
-
*
|
|
2131
|
-
* @param
|
|
2224
|
+
* Scale the rows of `this` Matrix3d in place.
|
|
2225
|
+
* @param scaleX scale factor for row 0
|
|
2226
|
+
* @param scaleY scale factor for row 1
|
|
2227
|
+
* @param scaleZ scale factor for row 2
|
|
2228
|
+
*/
|
|
2229
|
+
scaleRowsInPlace(scaleX, scaleY, scaleZ) {
|
|
2230
|
+
this.coffs[0] *= scaleX;
|
|
2231
|
+
this.coffs[1] *= scaleX;
|
|
2232
|
+
this.coffs[2] *= scaleX;
|
|
2233
|
+
this.coffs[3] *= scaleY;
|
|
2234
|
+
this.coffs[4] *= scaleY;
|
|
2235
|
+
this.coffs[5] *= scaleY;
|
|
2236
|
+
this.coffs[6] *= scaleZ;
|
|
2237
|
+
this.coffs[7] *= scaleZ;
|
|
2238
|
+
this.coffs[8] *= scaleZ;
|
|
2239
|
+
if (this.inverseState === InverseMatrixState.inverseStored && this.inverseCoffs !== undefined) {
|
|
2240
|
+
// apply reciprocal scales to the COLUMNs of the inverse
|
|
2241
|
+
const divX = Geometry.conditionalDivideFraction(1.0, scaleX);
|
|
2242
|
+
const divY = Geometry.conditionalDivideFraction(1.0, scaleY);
|
|
2243
|
+
const divZ = Geometry.conditionalDivideFraction(1.0, scaleZ);
|
|
2244
|
+
if (divX !== undefined && divY !== undefined && divZ !== undefined) {
|
|
2245
|
+
this.inverseCoffs[0] *= divX;
|
|
2246
|
+
this.inverseCoffs[1] *= divY;
|
|
2247
|
+
this.inverseCoffs[2] *= divZ;
|
|
2248
|
+
this.inverseCoffs[3] *= divX;
|
|
2249
|
+
this.inverseCoffs[4] *= divY;
|
|
2250
|
+
this.inverseCoffs[5] *= divZ;
|
|
2251
|
+
this.inverseCoffs[6] *= divX;
|
|
2252
|
+
this.inverseCoffs[7] *= divY;
|
|
2253
|
+
this.inverseCoffs[8] *= divZ;
|
|
2254
|
+
}
|
|
2255
|
+
else
|
|
2256
|
+
this.inverseState = InverseMatrixState.singular;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Add scaled values from `other` Matrix3d to `this` Matrix3d.
|
|
2261
|
+
* @param other Matrix3d with values to be added.
|
|
2132
2262
|
* @param scale scale factor to apply to the added values.
|
|
2133
2263
|
*/
|
|
2134
2264
|
addScaledInPlace(other, scale) {
|
|
@@ -2137,18 +2267,19 @@ export class Matrix3d {
|
|
|
2137
2267
|
this.inverseState = InverseMatrixState.unknown;
|
|
2138
2268
|
}
|
|
2139
2269
|
/**
|
|
2140
|
-
*
|
|
2141
|
-
* * The scaled outer product is a
|
|
2270
|
+
* Add scaled values from an outer product of vectors U and V.
|
|
2271
|
+
* * The scaled outer product is a matrix with `rank 1` (all columns/rows are linearly dependent).
|
|
2142
2272
|
* * This is useful in constructing mirrors and directional scales.
|
|
2143
2273
|
* ```
|
|
2144
2274
|
* equation
|
|
2145
2275
|
* A += s \columnSubXYZ{U}\rowSubXYZ{V}
|
|
2146
2276
|
* \\ \matrixXY{A} += s \begin{bmatrix}
|
|
2147
|
-
* U_x * V_x &
|
|
2148
|
-
*
|
|
2149
|
-
*
|
|
2277
|
+
* U_x * V_x & U_x * V_y & U_x * V_z \\
|
|
2278
|
+
* U_y * V_x & U_y * V_y & U_y * V_z \\
|
|
2279
|
+
* U_z * V_x & U_z * V_y & U_z * V_z \end{bmatrix}
|
|
2150
2280
|
* ```
|
|
2151
|
-
* @param
|
|
2281
|
+
* @param vectorU first vector in the outer product.
|
|
2282
|
+
* @param vectorV second vector in the outer product.
|
|
2152
2283
|
* @param scale scale factor to apply to the added values.
|
|
2153
2284
|
*/
|
|
2154
2285
|
addScaledOuterProductInPlace(vectorU, vectorV, scale) {
|
|
@@ -2163,65 +2294,107 @@ export class Matrix3d {
|
|
|
2163
2294
|
this.coffs[8] += scale * vectorU.z * vectorV.z;
|
|
2164
2295
|
this.inverseState = InverseMatrixState.unknown;
|
|
2165
2296
|
}
|
|
2166
|
-
/**
|
|
2167
|
-
*
|
|
2168
|
-
*
|
|
2169
|
-
*
|
|
2297
|
+
/**
|
|
2298
|
+
* Create a rigid matrix (columns and rows are unit length and pairwise perpendicular) for the given eye coordinate.
|
|
2299
|
+
* * column 2 is parallel to (x,y,z).
|
|
2300
|
+
* * column 0 is perpendicular to column 2 and is in the xy plane.
|
|
2301
|
+
* * column 1 is perpendicular to both. It is the "up" vector on the view plane.
|
|
2302
|
+
* * Multiplying the returned matrix times a local (view) vector gives the world vector.
|
|
2303
|
+
* * Multiplying transpose of the returned matrix times a world vector gives the local (view) vector.
|
|
2304
|
+
* @param x eye x coordinate
|
|
2305
|
+
* @param y eye y coordinate
|
|
2306
|
+
* @param z eye z coordinate
|
|
2307
|
+
* @param result optional preallocated result
|
|
2170
2308
|
*/
|
|
2171
|
-
|
|
2172
|
-
|
|
2309
|
+
static createRigidViewAxesZTowardsEye(x, y, z, result) {
|
|
2310
|
+
result = Matrix3d.createIdentity(result);
|
|
2311
|
+
const rxy = Geometry.hypotenuseXY(x, y);
|
|
2312
|
+
// if coordinate is (0,0,z), i.e., Top or Bottom view
|
|
2313
|
+
if (Geometry.isSmallMetricDistance(rxy)) {
|
|
2314
|
+
if (z < 0.0)
|
|
2315
|
+
result.scaleColumnsInPlace(1.0, -1.0, -1.0);
|
|
2316
|
+
}
|
|
2317
|
+
else {
|
|
2318
|
+
/**
|
|
2319
|
+
* The matrix that the "else" statement creates is
|
|
2320
|
+
* [-s -s1*c c1*c]
|
|
2321
|
+
* [c -s1*s c1*s]
|
|
2322
|
+
* [0 c1 s1 ]
|
|
2323
|
+
* where
|
|
2324
|
+
* c = x / sqrt(x*x + y*y)
|
|
2325
|
+
* s = y / sqrt(x*x + y*y)
|
|
2326
|
+
* c1 = sqrt(x*x + y*y) / sqrt(x*x + y*y + z*z)
|
|
2327
|
+
* s1 = z / sqrt(x*x + y*y + z*z)
|
|
2328
|
+
*
|
|
2329
|
+
* This is an orthogonal matrix meaning it rotates the standard XYZ axis to ABC axis system
|
|
2330
|
+
* (if matrix is [A B C]). The matrix rotates (0,0,1), i.e., the default Top view or Z axis,
|
|
2331
|
+
* to the eye point (x/r,y/r,z/r) where r = sqrt(x*x + y*y + z*z). The matrix also rotates
|
|
2332
|
+
* (1,0,0) to a point on XY plane.
|
|
2333
|
+
*/
|
|
2334
|
+
const c = x / rxy;
|
|
2335
|
+
const s = y / rxy;
|
|
2336
|
+
// if coordinate is (x,y,0), e.g., Front or Back or Left or Right view (for those 4 views x or y is 0 not both)
|
|
2337
|
+
result.setRowValues(-s, 0, c, c, 0, s, 0, 1, 0);
|
|
2338
|
+
// if coordinate is (x,y,z) and z is not 0, i.e., other views such as Iso or RightIso
|
|
2339
|
+
if (z !== 0.0) {
|
|
2340
|
+
const r = Geometry.hypotenuseXYZ(x, y, z);
|
|
2341
|
+
const s1 = z / r;
|
|
2342
|
+
const c1 = rxy / r;
|
|
2343
|
+
result.applyGivensColumnOp(1, 2, c1, -s1);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
return result;
|
|
2173
2347
|
}
|
|
2174
|
-
/** Return the determinant of this matrix. */
|
|
2348
|
+
/** Return the determinant of `this` matrix. */
|
|
2175
2349
|
determinant() {
|
|
2176
2350
|
return this.coffs[0] * this.coffs[4] * this.coffs[8]
|
|
2177
|
-
- this.coffs[0] * this.coffs[
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
+ this.coffs[
|
|
2181
|
-
- this.coffs[
|
|
2351
|
+
- this.coffs[0] * this.coffs[5] * this.coffs[7]
|
|
2352
|
+
- this.coffs[1] * this.coffs[3] * this.coffs[8]
|
|
2353
|
+
+ this.coffs[1] * this.coffs[5] * this.coffs[6]
|
|
2354
|
+
+ this.coffs[2] * this.coffs[3] * this.coffs[7]
|
|
2355
|
+
- this.coffs[2] * this.coffs[4] * this.coffs[6];
|
|
2182
2356
|
}
|
|
2183
|
-
/**
|
|
2357
|
+
/**
|
|
2358
|
+
* Return an estimate of how independent the columns of `this` matrix are. Near zero is bad (i.e.,
|
|
2359
|
+
* columns are almost dependent and matrix is nearly singular). Near 1 is good (i.e., columns are
|
|
2360
|
+
* almost independent and matrix is invertible).
|
|
2184
2361
|
*/
|
|
2185
2362
|
conditionNumber() {
|
|
2186
2363
|
const determinant = this.determinant();
|
|
2187
|
-
const
|
|
2364
|
+
const columnMagnitudeSum = Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[3], this.coffs[6])
|
|
2188
2365
|
+ Geometry.hypotenuseXYZ(this.coffs[1], this.coffs[4], this.coffs[7])
|
|
2189
2366
|
+ Geometry.hypotenuseXYZ(this.coffs[2], this.coffs[5], this.coffs[8]);
|
|
2190
|
-
return Geometry.safeDivideFraction(determinant,
|
|
2367
|
+
return Geometry.safeDivideFraction(determinant, columnMagnitudeSum, 0.0);
|
|
2191
2368
|
}
|
|
2192
2369
|
/** Return the sum of squares of all entries */
|
|
2193
2370
|
sumSquares() {
|
|
2194
|
-
let i = 0;
|
|
2195
2371
|
let sum = 0;
|
|
2196
|
-
for (i = 0; i < 9; i++)
|
|
2372
|
+
for (let i = 0; i < 9; i++)
|
|
2197
2373
|
sum += this.coffs[i] * this.coffs[i];
|
|
2198
2374
|
return sum;
|
|
2199
2375
|
}
|
|
2200
2376
|
/** Return the sum of squares of diagonal entries */
|
|
2201
2377
|
sumDiagonalSquares() {
|
|
2202
|
-
let i = 0;
|
|
2203
2378
|
let sum = 0;
|
|
2204
|
-
for (i = 0; i < 9; i += 4)
|
|
2379
|
+
for (let i = 0; i < 9; i += 4)
|
|
2205
2380
|
sum += this.coffs[i] * this.coffs[i];
|
|
2206
2381
|
return sum;
|
|
2207
2382
|
}
|
|
2208
|
-
/** Return the sum of diagonal entries
|
|
2383
|
+
/** Return the matrix `trace` (sum of diagonal entries) */
|
|
2209
2384
|
sumDiagonal() {
|
|
2210
2385
|
return this.coffs[0] + this.coffs[4] + this.coffs[8];
|
|
2211
2386
|
}
|
|
2212
2387
|
/** Return the Maximum absolute value of any single entry */
|
|
2213
2388
|
maxAbs() {
|
|
2214
|
-
let i = 0;
|
|
2215
2389
|
let max = 0;
|
|
2216
|
-
for (i = 0; i < 9; i++)
|
|
2390
|
+
for (let i = 0; i < 9; i++)
|
|
2217
2391
|
max = Math.max(max, Math.abs(this.coffs[i]));
|
|
2218
2392
|
return max;
|
|
2219
2393
|
}
|
|
2220
2394
|
/** Return the maximum absolute difference between corresponding entries of `this` and `other` */
|
|
2221
2395
|
maxDiff(other) {
|
|
2222
|
-
let i = 0;
|
|
2223
2396
|
let max = 0;
|
|
2224
|
-
for (i = 0; i < 9; i++)
|
|
2397
|
+
for (let i = 0; i < 9; i++)
|
|
2225
2398
|
max = Math.max(max, Math.abs(this.coffs[i] - other.coffs[i]));
|
|
2226
2399
|
return max;
|
|
2227
2400
|
}
|
|
@@ -2236,90 +2409,111 @@ export class Matrix3d {
|
|
|
2236
2409
|
const sumOff = Math.abs(sumAll - sumDiagonal);
|
|
2237
2410
|
return Math.sqrt(sumOff) <= Geometry.smallAngleRadians * (1.0 + Math.sqrt(sumAll));
|
|
2238
2411
|
}
|
|
2412
|
+
/** Sum of squared differences between symmetric pairs (symmetric pairs have indices (1,3), (2,6), and (5,7).) */
|
|
2413
|
+
sumSkewSquares() {
|
|
2414
|
+
return Geometry.hypotenuseSquaredXYZ(this.coffs[1] - this.coffs[3], this.coffs[2] - this.coffs[6], this.coffs[5] - this.coffs[7]);
|
|
2415
|
+
}
|
|
2416
|
+
/** Test if the matrix is (very near to) symmetric */
|
|
2417
|
+
isSymmetric() {
|
|
2418
|
+
const offDiagonal = this.sumSkewSquares();
|
|
2419
|
+
return Math.sqrt(offDiagonal) <= Geometry.smallAngleRadians * (1.0 + Math.sqrt(this.sumSquares()));
|
|
2420
|
+
}
|
|
2239
2421
|
/** Test if the stored inverse is present and marked valid */
|
|
2240
2422
|
get hasCachedInverse() {
|
|
2241
2423
|
return this.inverseState === InverseMatrixState.inverseStored && this.inverseCoffs !== undefined;
|
|
2242
2424
|
}
|
|
2243
|
-
/** Test if the below diagonal entries are all nearly zero */
|
|
2425
|
+
/** Test if the below diagonal entries (3,6,7) are all nearly zero */
|
|
2244
2426
|
get isUpperTriangular() {
|
|
2245
2427
|
const sumAll = this.sumSquares();
|
|
2246
2428
|
const sumLow = Geometry.hypotenuseSquaredXYZ(this.coffs[3], this.coffs[6], this.coffs[7]);
|
|
2247
2429
|
return Math.sqrt(sumLow) <= Geometry.smallAngleRadians * (1.0 + Math.sqrt(sumAll));
|
|
2248
2430
|
}
|
|
2249
|
-
/**
|
|
2431
|
+
/** Test if the above diagonal entries (1,2,5) are all nearly zero */
|
|
2432
|
+
get isLowerTriangular() {
|
|
2433
|
+
const sumAll = this.sumSquares();
|
|
2434
|
+
const sumLow = Geometry.hypotenuseSquaredXYZ(this.coffs[1], this.coffs[2], this.coffs[5]);
|
|
2435
|
+
return Math.sqrt(sumLow) <= Geometry.smallAngleRadians * (1.0 + Math.sqrt(sumAll));
|
|
2436
|
+
}
|
|
2437
|
+
/**
|
|
2438
|
+
* If the matrix is diagonal and all diagonals are almost equal, return the first diagonal (entry 0
|
|
2439
|
+
* which is same as entry 4 and 8). Otherwise return `undefined`.
|
|
2250
2440
|
*/
|
|
2251
2441
|
sameDiagonalScale() {
|
|
2252
2442
|
const sumAll = this.sumSquares();
|
|
2253
2443
|
const sumDiagonal = this.sumDiagonalSquares();
|
|
2254
2444
|
const sumOff = Math.abs(sumAll - sumDiagonal);
|
|
2255
2445
|
if (Math.sqrt(sumOff) <= Geometry.smallAngleRadians * (1.0 + Math.sqrt(sumAll))
|
|
2256
|
-
&& Geometry.isSameCoordinate(this.coffs[0], this.coffs[4])
|
|
2446
|
+
&& Geometry.isSameCoordinate(this.coffs[0], this.coffs[4])
|
|
2447
|
+
&& Geometry.isSameCoordinate(this.coffs[0], this.coffs[8]))
|
|
2257
2448
|
return this.coffs[0];
|
|
2258
2449
|
return undefined;
|
|
2259
2450
|
}
|
|
2260
|
-
/**
|
|
2261
|
-
|
|
2262
|
-
|
|
2451
|
+
/**
|
|
2452
|
+
* Test if all rows and columns are unit length and are perpendicular to each other, i.e., the matrix is either
|
|
2453
|
+
* a `pure rotation` (determinant is +1) or is a `mirror` (determinant is -1).
|
|
2454
|
+
* * **Note:** such a matrix is called `orthogonal` and its inverse is its transpose.
|
|
2455
|
+
*/
|
|
2456
|
+
testPerpendicularUnitRowsAndColumns() {
|
|
2457
|
+
const product = this.multiplyMatrixMatrixTranspose(this);
|
|
2458
|
+
return product.isIdentity;
|
|
2263
2459
|
}
|
|
2264
|
-
/**
|
|
2265
|
-
*
|
|
2460
|
+
/**
|
|
2461
|
+
* Test if the matrix is a `rigid` matrix (or `pure rotation`, i.e., columns and rows are unit length and
|
|
2462
|
+
* pairwise perpendicular and determinant is +1).
|
|
2463
|
+
* @param allowMirror whether to widen the test to return true if the matrix is a `mirror` (determinant is -1).
|
|
2266
2464
|
*/
|
|
2267
2465
|
isRigid(allowMirror = false) {
|
|
2268
2466
|
return this.testPerpendicularUnitRowsAndColumns() && (allowMirror || this.determinant() > 0);
|
|
2269
2467
|
}
|
|
2270
|
-
/**
|
|
2271
|
-
*
|
|
2272
|
-
*
|
|
2273
|
-
*
|
|
2468
|
+
/**
|
|
2469
|
+
* Test if all rows and columns are perpendicular to each other and have equal length.
|
|
2470
|
+
* If so, the length (or its negative) is the `scale` factor from a set of `orthonormal axes` to
|
|
2471
|
+
* the set of axes created by columns of `this` matrix. Otherwise, returns `undefined`.
|
|
2472
|
+
* @returns returns `{ rigidAxes, scale }` where `rigidAxes` is a Matrix3d with its columns as the rigid axes
|
|
2473
|
+
* (with the scale factor removed) and `scale` is the scale factor.
|
|
2474
|
+
* * Note that determinant of a rigid matrix is +1.
|
|
2475
|
+
* * The context for this method is to determine if the matrix is the product a `rotation` matrix and a uniform
|
|
2476
|
+
* `scale` matrix (diagonal matrix with all diagonal entries the same nonzero number).
|
|
2274
2477
|
*/
|
|
2275
2478
|
factorRigidWithSignedScale() {
|
|
2276
2479
|
const product = this.multiplyMatrixMatrixTranspose(this);
|
|
2277
|
-
const
|
|
2278
|
-
if (
|
|
2480
|
+
const scaleSquare = product.sameDiagonalScale();
|
|
2481
|
+
if (scaleSquare === undefined || scaleSquare <= 0.0)
|
|
2279
2482
|
return undefined;
|
|
2280
|
-
const
|
|
2281
|
-
const
|
|
2282
|
-
const result = { rigidAxes: this.scaleColumns(
|
|
2483
|
+
const scale = this.determinant() > 0 ? Math.sqrt(scaleSquare) : -Math.sqrt(scaleSquare);
|
|
2484
|
+
const scaleInverse = 1.0 / scale;
|
|
2485
|
+
const result = { rigidAxes: this.scaleColumns(scaleInverse, scaleInverse, scaleInverse), scale };
|
|
2283
2486
|
return result;
|
|
2284
2487
|
}
|
|
2285
|
-
/** Test if
|
|
2488
|
+
/** Test if `this` matrix reorders and/or negates the columns of the `identity` matrix. */
|
|
2286
2489
|
get isSignedPermutation() {
|
|
2287
2490
|
let count = 0;
|
|
2288
2491
|
for (let row = 0; row < 3; row++)
|
|
2289
2492
|
for (let col = 0; col < 3; col++) {
|
|
2290
2493
|
const q = this.at(row, col);
|
|
2291
|
-
if (q === 0) {
|
|
2494
|
+
if (q === 0) {
|
|
2495
|
+
// do nothing
|
|
2292
2496
|
}
|
|
2293
2497
|
else if (q === 1 || q === -1) {
|
|
2294
|
-
// the rest of this row and column should be 0.
|
|
2295
|
-
// "at" will apply cyclic indexing.
|
|
2296
2498
|
count++;
|
|
2297
|
-
if
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
return false;
|
|
2301
|
-
if (this.at(row, col + 1) !== 0)
|
|
2302
|
-
return false;
|
|
2303
|
-
if (this.at(row, col + 2) !== 0)
|
|
2499
|
+
// if the rest of this row and column should be 0 ("at" will apply cyclic indexing)
|
|
2500
|
+
if ((this.at(row + 1, col) !== 0) || (this.at(row + 2, col) !== 0) ||
|
|
2501
|
+
(this.at(row, col + 1) !== 0) || (this.at(row, col + 2) !== 0))
|
|
2304
2502
|
return false;
|
|
2305
2503
|
}
|
|
2306
|
-
else { // entry is not
|
|
2504
|
+
else { // entry is not 0, 1, or -1
|
|
2307
2505
|
return false;
|
|
2308
2506
|
}
|
|
2309
2507
|
}
|
|
2310
2508
|
return count === 3;
|
|
2311
2509
|
}
|
|
2312
|
-
/**
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
/** Adjust the matrix in place so that:
|
|
2318
|
-
* * columns are perpendicular and have unit length
|
|
2319
|
-
* * transpose equals inverse
|
|
2320
|
-
* * mirroring is removed
|
|
2510
|
+
/**
|
|
2511
|
+
* Adjust the matrix in place to make is a `rigid` matrix so that:
|
|
2512
|
+
* * columns are perpendicular and have unit length.
|
|
2513
|
+
* * transpose equals inverse.
|
|
2514
|
+
* * mirroring is removed.
|
|
2321
2515
|
* @param axisOrder how to reorder the matrix columns
|
|
2322
|
-
* @return whether the
|
|
2516
|
+
* @return whether the adjusted matrix is `rigid` on return
|
|
2323
2517
|
*/
|
|
2324
2518
|
makeRigid(axisOrder = AxisOrder.XYZ) {
|
|
2325
2519
|
const maxAbs = this.maxAbs();
|
|
@@ -2340,22 +2534,13 @@ export class Matrix3d {
|
|
|
2340
2534
|
return result;
|
|
2341
2535
|
return undefined;
|
|
2342
2536
|
}
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
}
|
|
2351
|
-
else {
|
|
2352
|
-
coff = numerator * reciprocal;
|
|
2353
|
-
}
|
|
2354
|
-
return coff;
|
|
2355
|
-
}
|
|
2356
|
-
/** create a matrix from a quaternion.
|
|
2357
|
-
* **WARNING:** There is frequent confusion over whether a "from quaternion" matrix is organized by rows and columns.
|
|
2358
|
-
* **WARNING:** If you find that the matrix seems to rotate by the opposite angle expect it, transpose it.
|
|
2537
|
+
/**
|
|
2538
|
+
* Create a matrix from a quaternion.
|
|
2539
|
+
* **WARNING:** There is frequent confusion over whether a "from quaternion" matrix is organized by
|
|
2540
|
+
* rows or columns. If you find that the matrix seems to rotate by the opposite angle, transpose it.
|
|
2541
|
+
*
|
|
2542
|
+
* Some math details can be found at
|
|
2543
|
+
* http://marc-b-reynolds.github.io/quaternions/2017/08/08/QuatRotMatrix.html
|
|
2359
2544
|
*/
|
|
2360
2545
|
static createFromQuaternion(quat) {
|
|
2361
2546
|
const qqx = quat.x * quat.x;
|
|
@@ -2368,31 +2553,57 @@ export class Matrix3d {
|
|
|
2368
2553
|
}
|
|
2369
2554
|
else {
|
|
2370
2555
|
const a = 1.0 / mag2;
|
|
2371
|
-
const matrix = Matrix3d.createRowValues(
|
|
2556
|
+
const matrix = Matrix3d.createRowValues(
|
|
2557
|
+
// first row
|
|
2558
|
+
a * (qqw + qqx - qqy - qqz), 2.0 * a * (quat.w * quat.z + quat.x * quat.y), 2.0 * a * (quat.x * quat.z - quat.w * quat.y),
|
|
2559
|
+
// second row
|
|
2560
|
+
2.0 * a * (quat.x * quat.y - quat.w * quat.z), a * (qqw - qqx + qqy - qqz), 2.0 * a * (quat.w * quat.x + quat.y * quat.z),
|
|
2561
|
+
// third row
|
|
2562
|
+
2.0 * a * (quat.x * quat.z + quat.w * quat.y), 2.0 * a * (quat.y * quat.z - quat.w * quat.x), a * (qqw - qqx - qqy + qqz));
|
|
2372
2563
|
return matrix;
|
|
2373
2564
|
}
|
|
2374
2565
|
}
|
|
2375
|
-
/** convert
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2566
|
+
/** Calculate quaternion terms used to convert matrix to a quaternion */
|
|
2567
|
+
static computeQuatTerm(numerator, denomCoff, reciprocal, diagSum) {
|
|
2568
|
+
let coff;
|
|
2569
|
+
const diagTol = 0.500;
|
|
2570
|
+
if (diagSum > diagTol) {
|
|
2571
|
+
coff = 0.5 * Math.sqrt(diagSum);
|
|
2572
|
+
if (denomCoff * numerator < 0.0)
|
|
2573
|
+
coff = -coff;
|
|
2574
|
+
}
|
|
2575
|
+
else {
|
|
2576
|
+
coff = numerator * reciprocal;
|
|
2577
|
+
}
|
|
2578
|
+
return coff;
|
|
2579
|
+
}
|
|
2580
|
+
/**
|
|
2581
|
+
* Create `this` matrix to a quaternion.
|
|
2582
|
+
* **Note:** This calculation requires `this` matrix to have unit length rows and columns.
|
|
2583
|
+
* **WARNING:** There is frequent confusion over whether a "from quaternion" matrix is organized by
|
|
2584
|
+
* rows or columns. If you find that the matrix seems to rotate by the opposite angle, transpose it.
|
|
2585
|
+
*
|
|
2586
|
+
* Some math details can be found at
|
|
2587
|
+
* http://marc-b-reynolds.github.io/quaternions/2017/08/08/QuatRotMatrix.html
|
|
2379
2588
|
*/
|
|
2380
2589
|
toQuaternion() {
|
|
2381
2590
|
const result = Point4d.createZero();
|
|
2382
|
-
const props = [
|
|
2591
|
+
const props = [
|
|
2592
|
+
[this.coffs[0], this.coffs[3], this.coffs[6]],
|
|
2383
2593
|
[this.coffs[1], this.coffs[4], this.coffs[7]],
|
|
2384
|
-
[this.coffs[2], this.coffs[5], this.coffs[8]]
|
|
2594
|
+
[this.coffs[2], this.coffs[5], this.coffs[8]],
|
|
2595
|
+
];
|
|
2385
2596
|
const xx = props[0][0];
|
|
2386
2597
|
const yy = props[1][1];
|
|
2387
2598
|
const zz = props[2][2];
|
|
2388
2599
|
const dSum = [];
|
|
2389
|
-
let denom, maxIndex, i;
|
|
2390
2600
|
dSum[0] = 1.0 + xx - yy - zz;
|
|
2391
2601
|
dSum[1] = 1.0 - xx + yy - zz;
|
|
2392
2602
|
dSum[2] = 1.0 - xx - yy + zz;
|
|
2393
2603
|
dSum[3] = 1.0 + xx + yy + zz;
|
|
2394
|
-
|
|
2395
|
-
|
|
2604
|
+
let denom;
|
|
2605
|
+
let maxIndex = 0;
|
|
2606
|
+
for (let i = 1; i <= 3; i++) {
|
|
2396
2607
|
if (dSum[i] > dSum[maxIndex])
|
|
2397
2608
|
maxIndex = i;
|
|
2398
2609
|
}
|
|
@@ -2433,5 +2644,6 @@ Matrix3d.useCachedInverse = true; // cached inverse can be suppressed for testin
|
|
|
2433
2644
|
Matrix3d.numUseCache = 0;
|
|
2434
2645
|
/** Total number of times a cached inverse was computed. */
|
|
2435
2646
|
Matrix3d.numComputeCache = 0;
|
|
2647
|
+
/** temporary buffer to store a matrix as a Float64Array (array of 9 floats) */
|
|
2436
2648
|
Matrix3d._productBuffer = new Float64Array(9);
|
|
2437
2649
|
//# sourceMappingURL=Matrix3d.js.map
|