@itwin/core-geometry 4.0.0-dev.2 → 4.0.0-dev.22

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.
Files changed (134) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/lib/cjs/Geometry.d.ts +3 -3
  3. package/lib/cjs/Geometry.d.ts.map +1 -1
  4. package/lib/cjs/Geometry.js +27 -11
  5. package/lib/cjs/Geometry.js.map +1 -1
  6. package/lib/cjs/curve/CurveChainWithDistanceIndex.d.ts.map +1 -1
  7. package/lib/cjs/curve/CurveChainWithDistanceIndex.js +19 -12
  8. package/lib/cjs/curve/CurveChainWithDistanceIndex.js.map +1 -1
  9. package/lib/cjs/curve/CurveCurve.d.ts +11 -8
  10. package/lib/cjs/curve/CurveCurve.d.ts.map +1 -1
  11. package/lib/cjs/curve/CurveCurve.js +16 -12
  12. package/lib/cjs/curve/CurveCurve.js.map +1 -1
  13. package/lib/cjs/curve/CurveCurveIntersectXY.d.ts +5 -1
  14. package/lib/cjs/curve/CurveCurveIntersectXY.d.ts.map +1 -1
  15. package/lib/cjs/curve/CurveCurveIntersectXY.js +11 -10
  16. package/lib/cjs/curve/CurveCurveIntersectXY.js.map +1 -1
  17. package/lib/cjs/geometry3d/Angle.d.ts +19 -0
  18. package/lib/cjs/geometry3d/Angle.d.ts.map +1 -1
  19. package/lib/cjs/geometry3d/Angle.js +39 -0
  20. package/lib/cjs/geometry3d/Angle.js.map +1 -1
  21. package/lib/cjs/geometry3d/AngleSweep.d.ts +1 -0
  22. package/lib/cjs/geometry3d/AngleSweep.d.ts.map +1 -1
  23. package/lib/cjs/geometry3d/AngleSweep.js +1 -0
  24. package/lib/cjs/geometry3d/AngleSweep.js.map +1 -1
  25. package/lib/cjs/geometry3d/CoincidentGeometryOps.d.ts +1 -0
  26. package/lib/cjs/geometry3d/CoincidentGeometryOps.d.ts.map +1 -1
  27. package/lib/cjs/geometry3d/CoincidentGeometryOps.js +3 -0
  28. package/lib/cjs/geometry3d/CoincidentGeometryOps.js.map +1 -1
  29. package/lib/cjs/geometry3d/Matrix3d.d.ts +326 -208
  30. package/lib/cjs/geometry3d/Matrix3d.d.ts.map +1 -1
  31. package/lib/cjs/geometry3d/Matrix3d.js +683 -613
  32. package/lib/cjs/geometry3d/Matrix3d.js.map +1 -1
  33. package/lib/cjs/geometry3d/Point3dVector3d.d.ts +15 -13
  34. package/lib/cjs/geometry3d/Point3dVector3d.d.ts.map +1 -1
  35. package/lib/cjs/geometry3d/Point3dVector3d.js +18 -13
  36. package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
  37. package/lib/cjs/geometry3d/Segment1d.d.ts +1 -1
  38. package/lib/cjs/geometry3d/Segment1d.js +1 -1
  39. package/lib/cjs/geometry3d/Segment1d.js.map +1 -1
  40. package/lib/cjs/numerics/Polynomials.d.ts +12 -0
  41. package/lib/cjs/numerics/Polynomials.d.ts.map +1 -1
  42. package/lib/cjs/numerics/Polynomials.js +14 -0
  43. package/lib/cjs/numerics/Polynomials.js.map +1 -1
  44. package/lib/cjs/polyface/PolyfaceBuilder.d.ts +1 -0
  45. package/lib/cjs/polyface/PolyfaceBuilder.d.ts.map +1 -1
  46. package/lib/cjs/polyface/PolyfaceBuilder.js +46 -6
  47. package/lib/cjs/polyface/PolyfaceBuilder.js.map +1 -1
  48. package/lib/cjs/polyface/PolyfaceQuery.d.ts +54 -0
  49. package/lib/cjs/polyface/PolyfaceQuery.d.ts.map +1 -1
  50. package/lib/cjs/polyface/PolyfaceQuery.js +71 -1
  51. package/lib/cjs/polyface/PolyfaceQuery.js.map +1 -1
  52. package/lib/cjs/polyface/multiclip/OffsetMeshContext.d.ts +202 -0
  53. package/lib/cjs/polyface/multiclip/OffsetMeshContext.d.ts.map +1 -0
  54. package/lib/cjs/polyface/multiclip/OffsetMeshContext.js +1038 -0
  55. package/lib/cjs/polyface/multiclip/OffsetMeshContext.js.map +1 -0
  56. package/lib/cjs/serialization/GeometrySamples.d.ts +4 -1
  57. package/lib/cjs/serialization/GeometrySamples.d.ts.map +1 -1
  58. package/lib/cjs/serialization/GeometrySamples.js +14 -6
  59. package/lib/cjs/serialization/GeometrySamples.js.map +1 -1
  60. package/lib/cjs/topology/Graph.d.ts +113 -7
  61. package/lib/cjs/topology/Graph.d.ts.map +1 -1
  62. package/lib/cjs/topology/Graph.js +185 -7
  63. package/lib/cjs/topology/Graph.js.map +1 -1
  64. package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts +38 -0
  65. package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts.map +1 -0
  66. package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.js +82 -0
  67. package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.js.map +1 -0
  68. package/lib/esm/Geometry.d.ts +3 -3
  69. package/lib/esm/Geometry.d.ts.map +1 -1
  70. package/lib/esm/Geometry.js +27 -11
  71. package/lib/esm/Geometry.js.map +1 -1
  72. package/lib/esm/curve/CurveChainWithDistanceIndex.d.ts.map +1 -1
  73. package/lib/esm/curve/CurveChainWithDistanceIndex.js +20 -13
  74. package/lib/esm/curve/CurveChainWithDistanceIndex.js.map +1 -1
  75. package/lib/esm/curve/CurveCurve.d.ts +11 -8
  76. package/lib/esm/curve/CurveCurve.d.ts.map +1 -1
  77. package/lib/esm/curve/CurveCurve.js +16 -12
  78. package/lib/esm/curve/CurveCurve.js.map +1 -1
  79. package/lib/esm/curve/CurveCurveIntersectXY.d.ts +5 -1
  80. package/lib/esm/curve/CurveCurveIntersectXY.d.ts.map +1 -1
  81. package/lib/esm/curve/CurveCurveIntersectXY.js +11 -10
  82. package/lib/esm/curve/CurveCurveIntersectXY.js.map +1 -1
  83. package/lib/esm/geometry3d/Angle.d.ts +19 -0
  84. package/lib/esm/geometry3d/Angle.d.ts.map +1 -1
  85. package/lib/esm/geometry3d/Angle.js +39 -0
  86. package/lib/esm/geometry3d/Angle.js.map +1 -1
  87. package/lib/esm/geometry3d/AngleSweep.d.ts +1 -0
  88. package/lib/esm/geometry3d/AngleSweep.d.ts.map +1 -1
  89. package/lib/esm/geometry3d/AngleSweep.js +1 -0
  90. package/lib/esm/geometry3d/AngleSweep.js.map +1 -1
  91. package/lib/esm/geometry3d/CoincidentGeometryOps.d.ts +1 -0
  92. package/lib/esm/geometry3d/CoincidentGeometryOps.d.ts.map +1 -1
  93. package/lib/esm/geometry3d/CoincidentGeometryOps.js +3 -0
  94. package/lib/esm/geometry3d/CoincidentGeometryOps.js.map +1 -1
  95. package/lib/esm/geometry3d/Matrix3d.d.ts +326 -208
  96. package/lib/esm/geometry3d/Matrix3d.d.ts.map +1 -1
  97. package/lib/esm/geometry3d/Matrix3d.js +683 -613
  98. package/lib/esm/geometry3d/Matrix3d.js.map +1 -1
  99. package/lib/esm/geometry3d/Point3dVector3d.d.ts +15 -13
  100. package/lib/esm/geometry3d/Point3dVector3d.d.ts.map +1 -1
  101. package/lib/esm/geometry3d/Point3dVector3d.js +18 -13
  102. package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
  103. package/lib/esm/geometry3d/Segment1d.d.ts +1 -1
  104. package/lib/esm/geometry3d/Segment1d.js +1 -1
  105. package/lib/esm/geometry3d/Segment1d.js.map +1 -1
  106. package/lib/esm/numerics/Polynomials.d.ts +12 -0
  107. package/lib/esm/numerics/Polynomials.d.ts.map +1 -1
  108. package/lib/esm/numerics/Polynomials.js +14 -0
  109. package/lib/esm/numerics/Polynomials.js.map +1 -1
  110. package/lib/esm/polyface/PolyfaceBuilder.d.ts +1 -0
  111. package/lib/esm/polyface/PolyfaceBuilder.d.ts.map +1 -1
  112. package/lib/esm/polyface/PolyfaceBuilder.js +46 -6
  113. package/lib/esm/polyface/PolyfaceBuilder.js.map +1 -1
  114. package/lib/esm/polyface/PolyfaceQuery.d.ts +54 -0
  115. package/lib/esm/polyface/PolyfaceQuery.d.ts.map +1 -1
  116. package/lib/esm/polyface/PolyfaceQuery.js +69 -0
  117. package/lib/esm/polyface/PolyfaceQuery.js.map +1 -1
  118. package/lib/esm/polyface/multiclip/OffsetMeshContext.d.ts +202 -0
  119. package/lib/esm/polyface/multiclip/OffsetMeshContext.d.ts.map +1 -0
  120. package/lib/esm/polyface/multiclip/OffsetMeshContext.js +1032 -0
  121. package/lib/esm/polyface/multiclip/OffsetMeshContext.js.map +1 -0
  122. package/lib/esm/serialization/GeometrySamples.d.ts +4 -1
  123. package/lib/esm/serialization/GeometrySamples.d.ts.map +1 -1
  124. package/lib/esm/serialization/GeometrySamples.js +14 -6
  125. package/lib/esm/serialization/GeometrySamples.js.map +1 -1
  126. package/lib/esm/topology/Graph.d.ts +113 -7
  127. package/lib/esm/topology/Graph.d.ts.map +1 -1
  128. package/lib/esm/topology/Graph.js +185 -7
  129. package/lib/esm/topology/Graph.js.map +1 -1
  130. package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts +38 -0
  131. package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts.map +1 -0
  132. package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.js +78 -0
  133. package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.js.map +1 -0
  134. 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
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
@@ -421,9 +422,20 @@ export class Matrix3d {
421
422
  return result;
422
423
  }
423
424
  /**
424
- * Copy the transpose of the coffs to the inverseCoffs.
425
- * * Mark the matrix as inverseStored.
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,7 @@ export class Matrix3d {
651
665
  }
652
666
  return Matrix3d.createIdentity(result);
653
667
  }
654
- /** Return the matrix for rotation of `angle` around `axis` */
668
+ /** Return the matrix for rotation of `angle` around desired `axis` */
655
669
  static createRotationAroundVector(axis, angle, result) {
656
670
  // Rodriguez formula (matrix form), https://mathworld.wolfram.com/RodriguesRotationFormula.html
657
671
  const c = angle.cos();
@@ -665,7 +679,7 @@ export class Matrix3d {
665
679
  }
666
680
  return undefined;
667
681
  }
668
- /** Returns a rotation of specified angle around an axis
682
+ /** Returns a rotation of specified angle around one of the main axis (X,Y,Z).
669
683
  * @param axisIndex index of axis (AxisIndex.X, AxisIndex.Y, AxisIndex.Z) kept fixed by the rotation.
670
684
  * @param angle angle of rotation
671
685
  * @param result optional result matrix.
@@ -688,14 +702,33 @@ export class Matrix3d {
688
702
  return myResult;
689
703
  }
690
704
  /**
691
- * Replace current columns Ui and Uj with (c*Ui - s*Uj) and (c*Uj + s*Ui).
692
- * * There is no checking for i,j being 0,1,2.
693
- * * This is used in compute intensive inner loops
694
- * @param i first row index. **must be 0,1,2** (unchecked)
695
- * @param j second row index. **must be 0,1,2** (unchecked)
696
- * @param c fist coefficient
697
- * @param s second coefficient
698
- */
705
+ * Replace current rows Ui and Uj with (c*Ui + s*Uj) and (c*Uj - s*Ui).
706
+ * * There is no checking for i,j being 0,1,2.
707
+ * @param i first row index. **must be 0,1,2** (unchecked)
708
+ * @param j second row index. **must be 0,1,2** (unchecked)
709
+ * @param c fist coefficient
710
+ * @param s second coefficient
711
+ */
712
+ applyGivensRowOp(i, j, c, s) {
713
+ let ii = 3 * i;
714
+ let jj = 3 * j;
715
+ const limit = ii + 3;
716
+ for (; ii < limit; ii++, jj++) {
717
+ const a = this.coffs[ii];
718
+ const b = this.coffs[jj];
719
+ this.coffs[ii] = a * c + b * s;
720
+ this.coffs[jj] = -a * s + b * c;
721
+ }
722
+ }
723
+ /**
724
+ * Replace current columns Ui and Uj with (c*Ui + s*Uj) and (c*Uj - s*Ui).
725
+ * * There is no checking for i,j being 0,1,2.
726
+ * * This is used in compute intensive inner loops
727
+ * @param i first row index. **must be 0,1,2** (unchecked)
728
+ * @param j second row index. **must be 0,1,2** (unchecked)
729
+ * @param c fist coefficient
730
+ * @param s second coefficient
731
+ */
699
732
  applyGivensColumnOp(i, j, c, s) {
700
733
  const limit = i + 9;
701
734
  for (; i < limit; i += 3, j += 3) {
@@ -706,12 +739,12 @@ export class Matrix3d {
706
739
  }
707
740
  }
708
741
  /**
709
- * Create a matrix from column vectors.
710
- * ```
711
- * equation
712
- * \begin{bmatrix}U_x & V_x & W_x \\ U_y & V_y & W_y \\ U_z & V_z & W_z \end{bmatrix}
713
- * ```
714
- */
742
+ * Create a matrix from column vectors.
743
+ * ```
744
+ * equation
745
+ * \begin{bmatrix}U_x & V_x & W_x \\ U_y & V_y & W_y \\ U_z & V_z & W_z \end{bmatrix}
746
+ * ```
747
+ */
715
748
  static createColumns(vectorU, vectorV, vectorW, result) {
716
749
  return Matrix3d.createRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, vectorU.z, vectorV.z, vectorW.z, result);
717
750
  }
@@ -728,8 +761,9 @@ export class Matrix3d {
728
761
  * * ColumnX points in the rightVector direction
729
762
  * * ColumnY points in the upVector direction
730
763
  * * ColumnZ is a unit cross product of ColumnX and ColumnY.
731
- * * Optionally rotate the standard cube by 45 degrees ccw around Y to bring its left or right vertical edge to center.
732
- * * Optionally rotate the standard cube by 35.264 degrees ccw around X (isometric rotation).
764
+ * * Optionally rotate by 45 degrees around `upVector` to bring its left or right vertical edge to center.
765
+ * * Optionally rotate by arctan(1/sqrt(2)) ~ 35.264 degrees around `rightVector` to bring the top or bottom
766
+ * horizontal edge of the view to the center (for isometric views).
733
767
  * * This is expected to be used with various principal unit vectors that are perpendicular to each other.
734
768
  * * STANDARD TOP VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitY(), 0, 0)
735
769
  * * STANDARD FRONT VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitZ(), 0, 0)
@@ -737,16 +771,20 @@ export class Matrix3d {
737
771
  * * STANDARD RIGHT VIEW: createViewedAxes(Vector3d.unitY(), Vector3d.unitZ(), 0, 0)
738
772
  * * STANDARD LEFT VIEW: createViewedAxes(Vector3d.unitY(-1), Vector3d.unitZ(), 0, 0)
739
773
  * * STANDARD BOTTOM VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitY(-1), 0, 0)
774
+ * * STANDARD ISO VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitZ(), -1, 1)
775
+ * * STANDARD RIGHT ISO VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitZ(), 1, 1)
776
+ * * Front, right, back, left, top, and bottom standard views are views from faces of the cube
777
+ * and iso and right iso standard views are views from corners of the cube.
740
778
  * * Note: createViewedAxes is column-based so always returns local to world
741
779
  *
742
780
  * @param rightVector ColumnX of the returned matrix. Expected to be perpendicular to upVector.
743
781
  * @param upVector ColumnY of the returned matrix. Expected to be perpendicular to rightVector.
744
- * @param leftNoneRight Specifies the ccw rotation around Y axis. Normally one of "-1", "0", and "1", where
745
- * "-1" indicates rotation by 45 degrees to bring the left vertical edge to center, "0" means no rotation,
782
+ * @param leftNoneRight Specifies the ccw rotation around `upVector` axis. Normally one of "-1", "0", and "1",
783
+ * where "-1" indicates rotation by 45 degrees to bring the left vertical edge to center, "0" means no rotation,
746
784
  * and "1" indicates rotation by 45 degrees to bring the right vertical edge to center. Other numbers are
747
785
  * used as multiplier for this 45 degree rotation.
748
- * @param topNoneBottom Specifies the ccw rotation around X axis. Normally one of "-1", "0", and "1", where
749
- * "-1" indicates isometric rotation (35.264 degrees) to bring the bottom upward, "0" means no rotation,
786
+ * @param topNoneBottom Specifies the ccw rotation around `rightVector` axis. Normally one of "-1", "0", and "1",
787
+ * where "-1" indicates isometric rotation (35.264 degrees) to bring the bottom upward, "0" means no rotation,
750
788
  * and "1" indicates isometric rotation (35.264 degrees) to bring the top downward. Other numbers are
751
789
  * used as multiplier for the 35.264 degree rotation.
752
790
  * @returns matrix = [rightVector, upVector, rightVector cross upVector] with the applied rotations specified
@@ -784,9 +822,11 @@ export class Matrix3d {
784
822
  * * Default is TOP view (`local X = world X`, `local Y = world Y`, `local Z = world Z`).
785
823
  * * To change view from the TOP to one of the other 7 standard views, we need to multiply "world data" to
786
824
  * the corresponding matrix1 provided by `createStandardWorldToView(index, false)` and then
787
- * `matrix1.multiply(world data)` will returns "local data".
825
+ * `matrix1.multiply(world data)` will return "local data".
788
826
  * * To change view back to the TOP, we need to multiply "local data" to the corresponding matrix2 provided
789
827
  * by `createStandardWorldToView(index, true)` and then `matrix2.multiply(local data)` will returns "world data".
828
+ * * Note: No matter how you rotate the world axis, local X is always pointing right, local Y is always pointing up,
829
+ * and local Z is always pointing toward you.
790
830
  *
791
831
  * @param index standard view index `StandardViewIndex.Top, Bottom, Left, Right, Front, Back, Iso, RightIso`
792
832
  * @param invert if false (default), the return matrix is world to local (view) and if true, the the return
@@ -795,343 +835,182 @@ export class Matrix3d {
795
835
  */
796
836
  static createStandardWorldToView(index, invert = false, result) {
797
837
  switch (index) {
798
- // start with TOP view, ccw rotation by 180 degrees around X
838
+ // Start with TOP view, ccw rotation by 180 degrees around X
799
839
  case StandardViewIndex.Bottom:
800
840
  result = Matrix3d.createRowValues(1, 0, 0, 0, -1, 0, 0, 0, -1);
801
841
  break;
802
- // start with TOP view, ccw rotation by -90 degrees around X and by 90 degrees around Z
842
+ // Start with TOP view, ccw rotation by -90 degrees around X and by 90 degrees around Z
803
843
  case StandardViewIndex.Left:
804
844
  result = Matrix3d.createRowValues(0, -1, 0, 0, 0, 1, -1, 0, 0);
805
845
  break;
806
- // start with TOP view, ccw rotation by -90 degrees around X and by -90 degrees around Z
846
+ // Start with TOP view, ccw rotation by -90 degrees around X and by -90 degrees around Z
807
847
  case StandardViewIndex.Right:
808
848
  result = Matrix3d.createRowValues(0, 1, 0, 0, 0, 1, 1, 0, 0);
809
849
  break;
810
- // start with TOP view, ccw rotation by -90 degrees around X
850
+ // Start with TOP view, ccw rotation by -90 degrees around X
811
851
  case StandardViewIndex.Front:
812
852
  result = Matrix3d.createRowValues(1, 0, 0, 0, 0, 1, 0, -1, 0);
813
853
  break;
814
- // start with TOP view, ccw rotation by -90 degrees around X and by 180 degrees around Z
854
+ // Start with TOP view, ccw rotation by -90 degrees around X and by 180 degrees around Z
815
855
  case StandardViewIndex.Back:
816
856
  result = Matrix3d.createRowValues(-1, 0, 0, 0, 0, 1, 0, 1, 0);
817
857
  break;
858
+ /**
859
+ * Isometric view
860
+ * Start with FRONT view, ccw rotation by -45 degrees around Y and by arctan(1/sqrt(2)) ~ 35.264 degrees around X
861
+ * cos(45) = 1/sqrt(2) = 0.70710678118 and sin(45) = 1/sqrt(2) = 0.70710678118
862
+ * cos(35.264) = 2/sqrt(6) = 0.81649658092 and sin(35.264) = 1/sqrt(3) = 0.57735026919
863
+ * More info: https://en.wikipedia.org/wiki/Isometric_projection
864
+ */
818
865
  case StandardViewIndex.Iso:
819
- // start with FRONT view, ccw rotation by -45 degrees around Y and by 35.264 degrees around X
820
866
  result = Matrix3d.createRowValues(0.707106781186548, -0.70710678118654757, 0.00000000000000000, 0.408248290463863, 0.40824829046386302, 0.81649658092772603, -0.577350269189626, -0.57735026918962573, 0.57735026918962573);
821
867
  break;
868
+ // Start with FRONT view, ccw rotation by 45 degrees around Y and by 35.264 degrees around X
822
869
  case StandardViewIndex.RightIso:
823
870
  result = Matrix3d.createRowValues(0.707106781186548, 0.70710678118654757, 0.00000000000000000, -0.408248290463863, 0.40824829046386302, 0.81649658092772603, 0.577350269189626, -0.57735026918962573, 0.57735026918962573);
824
871
  break;
825
- case StandardViewIndex.Top: // no rotation
872
+ // no rotation
873
+ case StandardViewIndex.Top:
826
874
  default:
827
875
  result = Matrix3d.createIdentity(result);
828
876
  }
829
877
  if (invert)
830
- result.transposeInPlace();
878
+ result.transposeInPlace(); // matrix is rigid so transpose and inverse are the same
831
879
  return result;
832
880
  }
833
- /*
834
- // this implementation has problems distinguishing failure (normalize) from small angle.
835
- public getAxisAndAngleOfRotation(): { axis: Vector3d, angle: Angle, error: boolean } {
836
-
837
- const result = { axis: Vector3d.unitZ(), angle: Angle.createRadians(0), error: true };
838
- if (this.isIdentity) {
839
- result.error = false;
840
- return result;
841
- }
842
- if (!this.isRigid())
843
- return result;
844
- const QMinusI = this.clone();
845
- QMinusI.coffs[0] -= 1.0;
846
- QMinusI.coffs[4] -= 1.0;
847
- QMinusI.coffs[8] -= 1.0;
848
- // Each column of (Q - I) is the motion of the corresponding axis vector
849
- // during the rotation.
850
- // Only one of the three axes can really be close to the rotation axis.
851
- const delta0 = QMinusI.columnX();
852
- const delta1 = QMinusI.columnY();
853
- const delta2 = QMinusI.columnZ();
854
- const cross01 = delta0.crossProduct(delta1);
855
- const cross12 = delta1.crossProduct(delta2);
856
- const cross20 = delta2.crossProduct(delta0);
857
-
858
- const aa01 = cross01.magnitudeSquared();
859
- const aa12 = cross12.magnitudeSquared();
860
- const aa20 = cross20.magnitudeSquared();
861
-
862
- const cross = cross01.clone(); // This will end up as the biggest cross product
863
- const v0 = delta0.clone(); // This will end up as one of the two largest delta vectors
864
- let aaMax = aa01;
865
- if (aa12 > aaMax) {
866
- cross.setFrom(cross12);
867
- aaMax = aa12;
868
- v0.setFrom(delta1);
869
- }
870
- if (aa20 > aaMax) {
871
- cross.setFrom(cross20);
872
- aaMax = aa20;
873
- v0.setFrom(delta2);
874
- }
875
-
876
- if (aaMax === 0.0) {
877
- // The vectors did not move. Just accept the zero rotation, with error flag set.
878
- return result;
879
- }
880
-
881
- v0.normalizeInPlace();
882
- // V0 is a unit vector perpendicular to the rotation axis.
883
- // Rotate it. Its image V1 is also a unit vector, and the angle from V0 to V1 is the quat angle.
884
- // CrossProduct is axis vector times sine of angle.
885
- // Dot Product is cosine of angle.
886
- // V2 is zero in 180 degree case, so we use the Cross from the search as the axis
887
- // as direction, being careful to keep sine positive.
888
- const v1 = this.multiplyVector(v0);
889
- const v2 = v0.crossProduct(v1);
890
- const sine = v2.magnitude();
891
- if (v2.dotProduct(cross) < 0.0)
892
- cross.scaleInPlace(-1.0);
893
- const cosine = v0.dotProduct(v1);
894
- result.angle.setRadians(Math.atan2(sine, cosine));
895
- result.axis.setFrom(cross);
896
- result.error = !result.axis.tryNormalizeInPlace();
897
- return result
898
- }
899
- */
881
+ /**
882
+ * Apply (in place) a jacobi update that zeros out this.at(i,j).
883
+ * @param i row index of zeroed member
884
+ * @param j column index of zeroed member
885
+ * @param k other row/column index (different from i and j)
886
+ * @param leftEigenVectors a matrix that its columns will be filled by eigenvectors of this Matrix3d
887
+ * (allocated by caller, computed and filled by this function)
888
+ */
889
+ applyFastSymmetricJacobiUpdate(i, j, k, leftEigenVectors) {
890
+ const indexII = 4 * i;
891
+ const indexJJ = 4 * j;
892
+ const indexIJ = 3 * i + j;
893
+ const indexIK = 3 * i + k;
894
+ const indexJK = 3 * j + k;
895
+ const dotUU = this.coffs[indexII];
896
+ const dotVV = this.coffs[indexJJ];
897
+ const dotUV = this.coffs[indexIJ];
898
+ const jacobi = Angle.trigValuesToHalfAngleTrigValues(dotUU - dotVV, 2.0 * dotUV);
899
+ if (Math.abs(dotUV) < 1.0e-15 * (dotUU + dotVV))
900
+ return 0.0;
901
+ const c = jacobi.c;
902
+ const s = jacobi.s;
903
+ const cc = c * c;
904
+ const ss = s * s;
905
+ const sc2 = 2.0 * c * s;
906
+ this.coffs[indexII] = cc * dotUU + sc2 * dotUV + ss * dotVV;
907
+ this.coffs[indexJJ] = ss * dotUU - sc2 * dotUV + cc * dotVV;
908
+ this.coffs[indexIJ] = 0.0;
909
+ const a = this.coffs[indexIK];
910
+ const b = this.coffs[indexJK];
911
+ this.coffs[indexIK] = a * c + b * s;
912
+ this.coffs[indexJK] = -s * a + c * b;
913
+ this.coffs[3 * j + i] = 0.0;
914
+ this.coffs[3 * k + i] = this.coffs[indexIK];
915
+ this.coffs[3 * k + j] = this.coffs[indexJK];
916
+ leftEigenVectors.applyGivensColumnOp(i, j, c, s);
917
+ return Math.abs(dotUV);
918
+ }
919
+ /**
920
+ * Factor this (symmetrized) as a product U * lambda * UT where U is orthogonal, lambda is diagonal.
921
+ * The upper triangle is mirrored to lower triangle to enforce symmetry.
922
+ * @param leftEigenvectors a matrix that its columns will be filled by eigenvectors of this Matrix3d
923
+ * (allocated by caller, computed and filled by this function)
924
+ * @param lambda a vector that its entries will be filled by eigenvalues of this Matrix3d
925
+ * (allocated by caller, computed and filled by this function)
926
+ */
927
+ fastSymmetricEigenvalues(leftEigenvectors, lambda) {
928
+ const matrix = this.clone();
929
+ leftEigenvectors.setIdentity();
930
+ const tolerance = 1.0e-12 * this.sumSquares();
931
+ for (let iteration = 0; iteration < 7; iteration++) {
932
+ const sum = matrix.applyFastSymmetricJacobiUpdate(0, 1, 2, leftEigenvectors)
933
+ + matrix.applyFastSymmetricJacobiUpdate(0, 2, 1, leftEigenvectors)
934
+ + matrix.applyFastSymmetricJacobiUpdate(1, 2, 0, leftEigenvectors);
935
+ // console.log("symmetric sum", sum);
936
+ // console.log ("sum", sum);
937
+ if (sum < tolerance) {
938
+ // console.log("symmetric iterations", iteration);
939
+ lambda.set(matrix.at(0, 0), matrix.at(1, 1), matrix.at(2, 2));
940
+ return true;
941
+ }
942
+ }
943
+ return false;
944
+ }
900
945
  /**
901
946
  * Compute the (unit vector) axis and angle of rotation.
947
+ * * math details can be found at docs/learning/geometry/Angle.md
902
948
  * @returns Returns axis and angle of rotation with result.ok === true when the conversion succeeded.
903
949
  */
904
950
  getAxisAndAngleOfRotation() {
905
951
  const trace = this.coffs[0] + this.coffs[4] + this.coffs[8];
906
- // trace = (xx + yy * zz) * (1-c) + 3 * c = 1 + 2c ==> c = (trace-1) / 2
907
- const skewXY = this.coffs[3] - this.coffs[1]; // == 2sz
908
- const skewYZ = this.coffs[7] - this.coffs[5]; // == 2sx
909
- const skewZX = this.coffs[2] - this.coffs[6]; // == 2sy
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;
952
+ const skewXY = this.coffs[3] - this.coffs[1]; // 2*z*sin
953
+ const skewYZ = this.coffs[7] - this.coffs[5]; // 2*y*sin
954
+ const skewZX = this.coffs[2] - this.coffs[6]; // 2*x*sin
955
+ // trace = (m00^2 + m11^2 + m22^2) * (1-cos) + 3cos = (1-cos) + 3cos = 1 + 2cos ==> cos = (trace-1) / 2
956
+ const c = (trace - 1.0) / 2.0; // cosine
957
+ const s = Geometry.hypotenuseXYZ(skewXY, skewYZ, skewZX) / 2.0; // sine
958
+ const e = c * c + s * s - 1.0; // s^2 + c^2 = 1
959
+ // if s^2 + c^2 != 1 then we have a bad matrix so return false
913
960
  if (Math.abs(e) > Geometry.smallAngleRadians) {
914
- // the sine and cosine are not a unit circle point. bad matrix . ..
915
961
  return { axis: Vector3d.create(0, 0, 1), angle: Angle.createRadians(0), ok: false };
916
962
  }
963
+ // sin is close to 0 then we got to special cases (angle 0 or 180) which needs to be handled differently
917
964
  if (Math.abs(s) < Geometry.smallAngleRadians) {
918
- // There is no significant skew.
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
965
+ if (c > 0) // sin = 0 and cos = 1 so angle = 0 (i.e., no rotation)
922
966
  return { axis: Vector3d.create(0, 0, 1), angle: Angle.createRadians(0), ok: true };
923
- // 180 degree flip around some axis ?
924
- // Look for the simple case of a principal rotation ...
925
- // look for a pair of (-1) entries on the diagonal ...
967
+ /**
968
+ * If sin = 0 and cos = -1 then angle = 180 (i.e., 180 degree rotation around some axis)
969
+ * then the rotation matrix becomes
970
+ * 2x^2-1 2xy 2xz
971
+ * 2xy 2y^2-1 2yz
972
+ * 2xz 2yz 2z^2-1
973
+ * Note that the matrix is symmetric.
974
+ * If rotation is around one the standard basis then non-diagonal entries become 0 and we
975
+ * have one 1 and two -1s on the diagonal.
976
+ * If rotation is around an axis other than standard basis, then the axis is the eigenvector
977
+ * of the rotation matrix with eigenvalue = 1.
978
+ */
926
979
  const axx = this.coffs[0];
927
980
  const ayy = this.coffs[4];
928
981
  const azz = this.coffs[8];
929
- const theta180 = Angle.createDegrees(180);
930
- // Look for principal axis flips as a special case . ..
982
+ // Look for a pair of "-1" entries on the diagonal (for rotation around the basis X,Y,Z axis)
931
983
  if (Geometry.isAlmostEqualNumber(-1.0, ayy) && Geometry.isAlmostEqualNumber(-1, azz)) {
932
- // rotate around
933
- return { axis: Vector3d.create(1, 0, 0), angle: theta180, ok: true };
984
+ return { axis: Vector3d.create(1, 0, 0), angle: Angle.createDegrees(180), ok: true };
934
985
  }
935
986
  else if (Geometry.isAlmostEqualNumber(-1.0, axx) && Geometry.isAlmostEqualNumber(-1, azz)) {
936
- return { axis: Vector3d.create(0, 1, 0), angle: theta180, ok: true };
987
+ return { axis: Vector3d.create(0, 1, 0), angle: Angle.createDegrees(180), ok: true };
937
988
  }
938
989
  else if (Geometry.isAlmostEqualNumber(-1.0, axx) && Geometry.isAlmostEqualNumber(-1, ayy)) {
939
- return { axis: Vector3d.create(0, 0, 1), angle: theta180, ok: true };
990
+ return { axis: Vector3d.create(0, 0, 1), angle: Angle.createDegrees(180), ok: true };
940
991
  }
941
- // 180 degree flip around some other axis ...
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.
992
+ // Look for eigenvector with eigenvalue = 1
946
993
  const eigenvectors = Matrix3d.createIdentity();
947
994
  const eigenvalues = Vector3d.create(0, 0, 0);
948
995
  if (this.fastSymmetricEigenvalues(eigenvectors, eigenvalues)) {
949
996
  for (let axisIndex = 0; axisIndex < 2; axisIndex++) {
950
997
  const lambda = eigenvalues.at(axisIndex);
951
998
  if (Geometry.isAlmostEqualNumber(1, lambda))
952
- return { axis: eigenvectors.getColumn(axisIndex), angle: theta180, ok: true };
999
+ return { axis: eigenvectors.getColumn(axisIndex), angle: Angle.createDegrees(180), ok: true };
953
1000
  }
954
- // Don't know if this can be reached ....
1001
+ // if no eigenvalue = 1 was found return false
955
1002
  return { axis: Vector3d.create(0, 0, 1), angle: Angle.createRadians(0), ok: false };
956
1003
  }
1004
+ // if no axis was found return false
957
1005
  return { axis: Vector3d.create(0, 0, 1), angle: Angle.createRadians(0), ok: false };
958
1006
  }
1007
+ // good matrix and non-zero sine
959
1008
  const a = 1.0 / (2.0 * s);
960
- const result = { axis: Vector3d.create(skewYZ * a, skewZX * a, skewXY * a), angle: Angle.createAtan2(s, c), ok: true };
961
- return result;
962
- }
963
- /**
964
- * Returns a matrix that rotates from vectorA to vectorB.
965
- */
966
- static createRotationVectorToVector(vectorA, vectorB, result) {
967
- return this.createPartialRotationVectorToVector(vectorA, 1.0, vectorB, result);
968
- }
969
- /**
970
- * Return a matrix that rotates a fraction of the angular sweep from vectorA to vectorB.
971
- * @param vectorA initial vector position
972
- * @param fraction fractional rotation. 1.0 is "all the way"
973
- * @param vectorB final vector position
974
- * @param result optional result matrix.
975
- */
976
- static createPartialRotationVectorToVector(vectorA, fraction, vectorB, result) {
977
- let upVector = vectorA.unitCrossProduct(vectorB);
978
- if (upVector) { // the usual case --
979
- return Matrix3d.createRotationAroundVector(upVector, Angle.createRadians(fraction * vectorA.planarAngleTo(vectorB, upVector).radians));
980
- }
981
- // fail if either vector is zero ...
982
- if (Geometry.isSmallMetricDistance(vectorA.magnitude())
983
- || Geometry.isSmallMetricDistance(vectorB.magnitude()))
984
- return undefined;
985
- // nonzero but aligned vectors ...
986
- if (vectorA.dotProduct(vectorB) > 0.0)
987
- return Matrix3d.createIdentity(result);
988
- // nonzero opposing vectors ..
989
- upVector = Matrix3d.createPerpendicularVectorFavorPlaneContainingZ(vectorA, upVector);
990
- return Matrix3d.createRotationAroundVector(upVector, Angle.createRadians(fraction * Math.PI));
991
- }
992
- /** Create a 90 degree rotation around a principal axis */
993
- static create90DegreeRotationAroundAxis(axisIndex) {
994
- axisIndex = Geometry.cyclic3dAxis(axisIndex);
995
- if (axisIndex === 0) {
996
- const retVal = Matrix3d.createRowValues(1, 0, 0, 0, 0, -1, 0, 1, 0);
997
- retVal.setupInverseTranspose();
998
- return retVal;
999
- }
1000
- else if (axisIndex === 1) {
1001
- const retVal = Matrix3d.createRowValues(0, 0, 1, 0, 1, 0, -1, 0, 0);
1002
- retVal.setupInverseTranspose();
1003
- return retVal;
1004
- }
1005
- else {
1006
- const retVal = Matrix3d.createRowValues(0, -1, 0, 1, 0, 0, 0, 0, 1);
1007
- retVal.setupInverseTranspose();
1008
- return retVal;
1009
- }
1010
- }
1011
- /** Return (a copy of) the X column */
1012
- columnX(result) { return Vector3d.create(this.coffs[0], this.coffs[3], this.coffs[6], result); }
1013
- /** Return (a copy of)the Y column */
1014
- columnY(result) { return Vector3d.create(this.coffs[1], this.coffs[4], this.coffs[7], result); }
1015
- /** Return (a copy of)the Z column */
1016
- columnZ(result) { return Vector3d.create(this.coffs[2], this.coffs[5], this.coffs[8], result); }
1017
- /** Return the X column magnitude squared */
1018
- columnXMagnitudeSquared() { return Geometry.hypotenuseSquaredXYZ(this.coffs[0], this.coffs[3], this.coffs[6]); }
1019
- /** Return the Y column magnitude squared */
1020
- columnYMagnitudeSquared() { return Geometry.hypotenuseSquaredXYZ(this.coffs[1], this.coffs[4], this.coffs[7]); }
1021
- /** Return the Z column magnitude squared */
1022
- columnZMagnitudeSquared() { return Geometry.hypotenuseSquaredXYZ(this.coffs[2], this.coffs[5], this.coffs[8]); }
1023
- /** Return the X column magnitude */
1024
- columnXMagnitude() { return Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[3], this.coffs[6]); }
1025
- /** Return the Y column magnitude */
1026
- columnYMagnitude() { return Geometry.hypotenuseXYZ(this.coffs[1], this.coffs[4], this.coffs[7]); }
1027
- /** Return the Z column magnitude */
1028
- columnZMagnitude() { return Geometry.hypotenuseXYZ(this.coffs[2], this.coffs[5], this.coffs[8]); }
1029
- /** Return magnitude of columnX cross columnY. */
1030
- columnXYCrossProductMagnitude() {
1031
- return Geometry.crossProductMagnitude(this.coffs[0], this.coffs[3], this.coffs[6], this.coffs[1], this.coffs[4], this.coffs[7]);
1032
- }
1033
- /** Return the X row magnitude d */
1034
- rowXMagnitude() { return Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[1], this.coffs[2]); }
1035
- /** Return the Y row magnitude */
1036
- rowYMagnitude() { return Geometry.hypotenuseXYZ(this.coffs[3], this.coffs[4], this.coffs[5]); }
1037
- /** Return the Z row magnitude */
1038
- rowZMagnitude() { return Geometry.hypotenuseXYZ(this.coffs[6], this.coffs[7], this.coffs[8]); }
1039
- /** Return the dot product of column X with column Y */
1040
- /** Return the dot product of column X with column Y */
1041
- columnXDotColumnY() {
1042
- return this.coffs[0] * this.coffs[1]
1043
- + this.coffs[3] * this.coffs[4]
1044
- + this.coffs[6] * this.coffs[7];
1045
- }
1046
- /**
1047
- * Dot product of an indexed column with a vector given as x,y,z
1048
- * @param columnIndex index of column. Must be 0,1,2
1049
- * @param x x component of vector
1050
- * @param y y component of vector
1051
- * @param z z component of vector
1052
- */
1053
- columnDotXYZ(columnIndex, x, y, z) {
1054
- return this.coffs[columnIndex] * x + this.coffs[columnIndex + 3] * y + this.coffs[columnIndex + 6] * z;
1055
- }
1056
- /** Return (a copy of) the X row */
1057
- rowX(result) { return Vector3d.create(this.coffs[0], this.coffs[1], this.coffs[2], result); }
1058
- /** Return (a copy of) the Y row */
1059
- rowY(result) { return Vector3d.create(this.coffs[3], this.coffs[4], this.coffs[5], result); }
1060
- /** Return (a copy of) the Z row */
1061
- rowZ(result) { return Vector3d.create(this.coffs[6], this.coffs[7], this.coffs[8], result); }
1062
- /** Return the dot product of the vector parameter with the X column. */
1063
- dotColumnX(vector) { return vector.x * this.coffs[0] + vector.y * this.coffs[3] + vector.z * this.coffs[6]; }
1064
- /** Return the dot product of the vector parameter with the Y column. */
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);
1084
- }
1085
- /*
1086
- * Replace current rows Ui Uj with (c*Ui - s*Uj) and (c*Uj + s*Ui).
1087
- * @param i first row index. must be 0,1,2 (unchecked)
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
- }
1102
- }
1103
- /**
1104
- * create a rigid coordinate frame column z parallel to (_x_,_y_,_z_) and column x in the xy plane.
1105
- * * column z points from origin to x,y,z
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
- }
1009
+ const result = {
1010
+ axis: Vector3d.create(skewYZ * a, skewZX * a, skewXY * a),
1011
+ angle: Angle.createAtan2(s, c),
1012
+ ok: true,
1013
+ };
1135
1014
  return result;
1136
1015
  }
1137
1016
  /** Rotate so columns i and j become perpendicular */
@@ -1163,8 +1042,7 @@ export class Matrix3d {
1163
1042
  factorPerpendicularColumns(matrixC, matrixU) {
1164
1043
  matrixC.setFrom(this);
1165
1044
  matrixU.setIdentity();
1166
- const ss = this.sumSquares();
1167
- const tolerance = 1.0e-12 * ss;
1045
+ const tolerance = 1.0e-12 * this.sumSquares();
1168
1046
  for (let iteration = 0; iteration < 7; iteration++) {
1169
1047
  const sum = matrixC.applyJacobiColumnRotation(0, 1, matrixU)
1170
1048
  + matrixC.applyJacobiColumnRotation(0, 2, matrixU)
@@ -1255,8 +1133,7 @@ export class Matrix3d {
1255
1133
  matrix.coffs[3] = matrix.coffs[1];
1256
1134
  matrix.coffs[6] = matrix.coffs[2];
1257
1135
  matrix.coffs[7] = matrix.coffs[5];
1258
- const ss = this.sumSquares();
1259
- const tolerance = 1.0e-12 * ss;
1136
+ const tolerance = 1.0e-12 * this.sumSquares();
1260
1137
  for (let iteration = 0; iteration < 7; iteration++) {
1261
1138
  const sum = leftEigenvectors.applySymmetricJacobi(0, 1, matrix)
1262
1139
  + leftEigenvectors.applySymmetricJacobi(0, 2, matrix)
@@ -1271,68 +1148,174 @@ export class Matrix3d {
1271
1148
  }
1272
1149
  return false;
1273
1150
  }
1274
- /** Apply (in place a jacobi update that zeros out this.at(i,j).
1275
- *
1151
+ /**
1152
+ * Return a matrix that rotates a fraction of the angular sweep from vectorA to vectorB.
1153
+ * @param vectorA initial vector position
1154
+ * @param fraction fractional rotation (1 means rotate all the way)
1155
+ * @param vectorB final vector position
1156
+ * @param result optional result matrix.
1276
1157
  */
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);
1158
+ static createPartialRotationVectorToVector(vectorA, fraction, vectorB, result) {
1159
+ let upVector = vectorA.unitCrossProduct(vectorB);
1160
+ // the usual case (both vectors and also their cross product is non-zero)
1161
+ if (upVector) {
1162
+ return Matrix3d.createRotationAroundVector(upVector, Angle.createRadians(fraction * vectorA.planarAngleTo(vectorB, upVector).radians));
1163
+ }
1164
+ // if either vector is zero
1165
+ if (Geometry.isSmallMetricDistance(vectorA.magnitude())
1166
+ || Geometry.isSmallMetricDistance(vectorB.magnitude()))
1167
+ return undefined;
1168
+ // aligned vectors (cross product = 0, dot product > 0)
1169
+ if (vectorA.dotProduct(vectorB) > 0.0)
1170
+ return Matrix3d.createIdentity(result);
1171
+ // opposing vectors (cross product = 0, dot product < 0)
1172
+ upVector = Matrix3d.createPerpendicularVectorFavorPlaneContainingZ(vectorA, upVector);
1173
+ return Matrix3d.createRotationAroundVector(upVector, Angle.createRadians(fraction * Math.PI));
1174
+ }
1175
+ /** Returns a matrix that rotates from vectorA to vectorB. */
1176
+ static createRotationVectorToVector(vectorA, vectorB, result) {
1177
+ return this.createPartialRotationVectorToVector(vectorA, 1.0, vectorB, result);
1178
+ }
1179
+ /** Create a 90 degree rotation around a principal axis */
1180
+ static create90DegreeRotationAroundAxis(axisIndex) {
1181
+ axisIndex = Geometry.cyclic3dAxis(axisIndex);
1182
+ if (axisIndex === 0) {
1183
+ const retVal = Matrix3d.createRowValues(1, 0, 0, 0, 0, -1, 0, 1, 0);
1184
+ retVal.setupInverseTranspose();
1185
+ return retVal;
1186
+ }
1187
+ else if (axisIndex === 1) {
1188
+ const retVal = Matrix3d.createRowValues(0, 0, 1, 0, 1, 0, -1, 0, 0);
1189
+ retVal.setupInverseTranspose();
1190
+ return retVal;
1191
+ }
1192
+ else {
1193
+ const retVal = Matrix3d.createRowValues(0, -1, 0, 1, 0, 0, 0, 0, 1);
1194
+ retVal.setupInverseTranspose();
1195
+ return retVal;
1196
+ }
1197
+ }
1198
+ /** Return (a copy of) the X column */
1199
+ columnX(result) {
1200
+ return Vector3d.create(this.coffs[0], this.coffs[3], this.coffs[6], result);
1201
+ }
1202
+ /** Return (a copy of) the Y column */
1203
+ columnY(result) {
1204
+ return Vector3d.create(this.coffs[1], this.coffs[4], this.coffs[7], result);
1205
+ }
1206
+ /** Return (a copy of) the Z column */
1207
+ columnZ(result) {
1208
+ return Vector3d.create(this.coffs[2], this.coffs[5], this.coffs[8], result);
1209
+ }
1210
+ /** Return the X column magnitude squared */
1211
+ columnXMagnitudeSquared() {
1212
+ return Geometry.hypotenuseSquaredXYZ(this.coffs[0], this.coffs[3], this.coffs[6]);
1213
+ }
1214
+ /** Return the Y column magnitude squared */
1215
+ columnYMagnitudeSquared() {
1216
+ return Geometry.hypotenuseSquaredXYZ(this.coffs[1], this.coffs[4], this.coffs[7]);
1217
+ }
1218
+ /** Return the Z column magnitude squared */
1219
+ columnZMagnitudeSquared() {
1220
+ return Geometry.hypotenuseSquaredXYZ(this.coffs[2], this.coffs[5], this.coffs[8]);
1221
+ }
1222
+ /** Return the X column magnitude */
1223
+ columnXMagnitude() {
1224
+ return Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[3], this.coffs[6]);
1225
+ }
1226
+ /** Return the Y column magnitude */
1227
+ columnYMagnitude() {
1228
+ return Geometry.hypotenuseXYZ(this.coffs[1], this.coffs[4], this.coffs[7]);
1229
+ }
1230
+ /** Return the Z column magnitude */
1231
+ columnZMagnitude() {
1232
+ return Geometry.hypotenuseXYZ(this.coffs[2], this.coffs[5], this.coffs[8]);
1233
+ }
1234
+ /** Return magnitude of columnX cross columnY. */
1235
+ columnXYCrossProductMagnitude() {
1236
+ return Geometry.crossProductMagnitude(this.coffs[0], this.coffs[3], this.coffs[6], this.coffs[1], this.coffs[4], this.coffs[7]);
1237
+ }
1238
+ /** Return the X row magnitude */
1239
+ rowXMagnitude() {
1240
+ return Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[1], this.coffs[2]);
1241
+ }
1242
+ /** Return the Y row magnitude */
1243
+ rowYMagnitude() {
1244
+ return Geometry.hypotenuseXYZ(this.coffs[3], this.coffs[4], this.coffs[5]);
1245
+ }
1246
+ /** Return the Z row magnitude */
1247
+ rowZMagnitude() {
1248
+ return Geometry.hypotenuseXYZ(this.coffs[6], this.coffs[7], this.coffs[8]);
1249
+ }
1250
+ /** Return the dot product of column X with column Y */
1251
+ columnXDotColumnY() {
1252
+ return this.coffs[0] * this.coffs[1]
1253
+ + this.coffs[3] * this.coffs[4]
1254
+ + this.coffs[6] * this.coffs[7];
1309
1255
  }
1310
1256
  /**
1311
- * Factor this (symmetrized) as a product U * lambda * UT where U is orthogonal, lambda is diagonal.
1312
- * The upper triangle is mirrored to lower triangle to enforce symmetry.
1313
- * @param matrixC (allocate by caller, computed here)
1314
- * @param factor (allocate by caller, computed here)
1257
+ * Dot product of an indexed column with a vector given as x,y,z
1258
+ * @param columnIndex index of column. Must be 0,1,2.
1259
+ * @param x x component of vector
1260
+ * @param y y component of vector
1261
+ * @param z z component of vector
1315
1262
  */
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;
1263
+ columnDotXYZ(columnIndex, x, y, z) {
1264
+ return this.coffs[columnIndex] * x + this.coffs[columnIndex + 3] * y + this.coffs[columnIndex + 6] * z;
1265
+ }
1266
+ /** Return (a copy of) the X row */
1267
+ rowX(result) {
1268
+ return Vector3d.create(this.coffs[0], this.coffs[1], this.coffs[2], result);
1269
+ }
1270
+ /** Return (a copy of) the Y row */
1271
+ rowY(result) {
1272
+ return Vector3d.create(this.coffs[3], this.coffs[4], this.coffs[5], result);
1273
+ }
1274
+ /** Return (a copy of) the Z row */
1275
+ rowZ(result) {
1276
+ return Vector3d.create(this.coffs[6], this.coffs[7], this.coffs[8], result);
1277
+ }
1278
+ /** Return the dot product of the vector parameter with the X column. */
1279
+ dotColumnX(vector) {
1280
+ return vector.x * this.coffs[0] + vector.y * this.coffs[3] + vector.z * this.coffs[6];
1281
+ }
1282
+ /** Return the dot product of the vector parameter with the Y column. */
1283
+ dotColumnY(vector) {
1284
+ return vector.x * this.coffs[1] + vector.y * this.coffs[4] + vector.z * this.coffs[7];
1285
+ }
1286
+ /** Return the dot product of the vector parameter with the Z column. */
1287
+ dotColumnZ(vector) {
1288
+ return vector.x * this.coffs[2] + vector.y * this.coffs[5] + vector.z * this.coffs[8];
1289
+ }
1290
+ /** Return the dot product of the vector parameter with the X row. */
1291
+ dotRowX(vector) {
1292
+ return vector.x * this.coffs[0] + vector.y * this.coffs[1] + vector.z * this.coffs[2];
1293
+ }
1294
+ /** Return the dot product of the vector parameter with the Y row. */
1295
+ dotRowY(vector) {
1296
+ return vector.x * this.coffs[3] + vector.y * this.coffs[4] + vector.z * this.coffs[5];
1297
+ }
1298
+ /** Return the dot product of the vector parameter with the Z row. */
1299
+ dotRowZ(vector) {
1300
+ return vector.x * this.coffs[6] + vector.y * this.coffs[7] + vector.z * this.coffs[8];
1301
+ }
1302
+ /** Return the dot product of the x,y,z with the X row. */
1303
+ dotRowXXYZ(x, y, z) {
1304
+ return x * this.coffs[0] + y * this.coffs[1] + z * this.coffs[2];
1305
+ }
1306
+ /** Return the dot product of the x,y,z with the Y row. */
1307
+ dotRowYXYZ(x, y, z) {
1308
+ return x * this.coffs[3] + y * this.coffs[4] + z * this.coffs[5];
1309
+ }
1310
+ /** Return the dot product of the x,y,z with the Z row. */
1311
+ dotRowZXYZ(x, y, z) {
1312
+ return x * this.coffs[6] + y * this.coffs[7] + z * this.coffs[8];
1334
1313
  }
1335
- /** Install data from xyz parts of Point4d (w part of Point4d ignored) */
1314
+ /** Return the cross product of the Z column with the vector parameter. */
1315
+ columnZCrossVector(vector, result) {
1316
+ return Geometry.crossProductXYZXYZ(this.coffs[2], this.coffs[5], this.coffs[8], vector.x, vector.y, vector.z, result);
1317
+ }
1318
+ /** Set data from xyz parts of Point4d (w part of Point4d ignored) */
1336
1319
  setColumnsPoint4dXYZ(vectorU, vectorV, vectorW) {
1337
1320
  this.inverseState = InverseMatrixState.unknown;
1338
1321
  this.setRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, vectorU.z, vectorV.z, vectorW.z);
@@ -1356,16 +1339,22 @@ export class Matrix3d {
1356
1339
  this.coffs[index + 6] = 0.0;
1357
1340
  }
1358
1341
  }
1359
- /** Set all columns of the matrix. Any undefined vector is zeros. */
1342
+ /**
1343
+ * Set all columns of the matrix. Any undefined vector is zeros.
1344
+ * @param vectorX values for column 0
1345
+ * @param vectorY values for column 1
1346
+ * @param vectorZ optional values for column 2 (it's optional in case column 2 is 000, which is a
1347
+ * projection onto the xy-plane)
1348
+ */
1360
1349
  setColumns(vectorX, vectorY, vectorZ) {
1361
1350
  this.setColumn(0, vectorX);
1362
1351
  this.setColumn(1, vectorY);
1363
1352
  this.setColumn(2, vectorZ);
1364
1353
  }
1365
1354
  /**
1366
- * set entries in one row of the matrix.
1367
- * @param rowIndex row index. this is interpreted cyclically.
1368
- * @param value x,yz, values for row. If undefined, zeros are installed.
1355
+ * Set entries in one row of the matrix.
1356
+ * @param rowIndex row index. This is interpreted cyclically (using Geometry.cyclic3dAxis).
1357
+ * @param value x,y,z values for row.
1369
1358
  */
1370
1359
  setRow(rowIndex, value) {
1371
1360
  const index = 3 * Geometry.cyclic3dAxis(rowIndex);
@@ -1374,21 +1363,26 @@ export class Matrix3d {
1374
1363
  this.coffs[index + 2] = value.z;
1375
1364
  this.inverseState = InverseMatrixState.unknown;
1376
1365
  }
1377
- /** Return a (copy of) a column of the matrix.
1378
- * @param i column index. This is corrected to 012 by Geometry.cyclic3dAxis.
1366
+ /**
1367
+ * Return (a copy of) a column of the matrix.
1368
+ * @param i column index. This is interpreted cyclically (using Geometry.cyclic3dAxis).
1369
+ * @param result optional preallocated result.
1379
1370
  */
1380
1371
  getColumn(columnIndex, result) {
1381
1372
  const index = Geometry.cyclic3dAxis(columnIndex);
1382
1373
  return Vector3d.create(this.coffs[index], this.coffs[index + 3], this.coffs[index + 6], result);
1383
1374
  }
1384
- /** Return a (copy of) a row of the matrix.
1385
- * @param i row index. This is corrected to 012 by Geometry.cyclic3dAxis.
1375
+ /**
1376
+ * Return a (copy of) a row of the matrix.
1377
+ * @param i row index. This is interpreted cyclically (using Geometry.cyclic3dAxis).
1378
+ * @param result optional preallocated result.
1386
1379
  */
1387
1380
  getRow(columnIndex, result) {
1388
1381
  const index = 3 * Geometry.cyclic3dAxis(columnIndex);
1389
1382
  return Vector3d.create(this.coffs[index], this.coffs[index + 1], this.coffs[index + 2], result);
1390
1383
  }
1391
- /** Create a matrix from row vectors.
1384
+ /**
1385
+ * Create a matrix from row vectors.
1392
1386
  * ```
1393
1387
  * equation
1394
1388
  * \begin{bmatrix}U_x & U_y & U_z \\ V_x & V_y & V_z \\ W_x & W_y & W_z \end{bmatrix}
@@ -1397,14 +1391,20 @@ export class Matrix3d {
1397
1391
  static createRows(vectorU, vectorV, vectorW, result) {
1398
1392
  return Matrix3d.createRowValues(vectorU.x, vectorU.y, vectorU.z, vectorV.x, vectorV.y, vectorV.z, vectorW.x, vectorW.y, vectorW.z, result);
1399
1393
  }
1400
- /** Create a matrix that scales along a specified direction.
1401
- * * The scale factor can be negative.
1402
- * * A scale of -1.0 (negative one) is a mirror across the plane perpendicular to the vector.
1394
+ /**
1395
+ * Create a matrix that scales along a specified `direction`. This means if you multiply the returned matrix
1396
+ * by a `vector`, you get `directional scale` of that `vector`. Suppose `plane` is the plane perpendicular
1397
+ * to the `direction`. When scale = 0, `directional scale` is projection of the `vector` to the `plane`.
1398
+ * When scale = 1, `directional scale` is the `vector` itself. When scale = -1, `directional scale` is
1399
+ * mirror of the `vector` across the `plane`. In general, When scale != 0, the result is computed by first
1400
+ * projecting the `vector` to the `plane`, then translating that projection along the `direction` (if scale > 0)
1401
+ * or in opposite direction (if scale < 0).
1403
1402
  * ```
1404
1403
  * equation
1405
- * \text{The matrix is } I - (s-1) U U^T
1406
- * \\ \text{with }U\text{ being the unit vector in the direction of the input vector.}
1404
+ * \text{The matrix is } I + (s-1) D D^T
1405
+ * \\ \text{with }D\text{ being the normalized direction vector and }s\text{ being the scale.}
1407
1406
  * ```
1407
+ * * Visualization can be found at itwinjs.org/sandbox/SaeedTorabi/DirectionalScale
1408
1408
  */
1409
1409
  static createDirectionalScale(direction, scale, result) {
1410
1410
  const unit = direction.normalize();
@@ -1412,19 +1412,13 @@ export class Matrix3d {
1412
1412
  const x = unit.x;
1413
1413
  const y = unit.y;
1414
1414
  const z = unit.z;
1415
- const a = (scale - 1);
1415
+ const a = scale - 1;
1416
1416
  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
1417
  }
1418
1418
  return Matrix3d.createUniformScale(scale);
1419
1419
  }
1420
- /* Create a matrix with the indicated column in the (normalized) direction, and the other two columns perpendicular. All columns are normalized.
1421
- * * The direction vector is normalized and appears in column axisIndex
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.
1420
+ /**
1421
+ * Multiply `matrix * vector`, treating the vector is a column vector on the right.
1428
1422
  * ```
1429
1423
  * equation
1430
1424
  * \matrixXY{A}\columnSubXYZ{U}
@@ -1435,36 +1429,38 @@ export class Matrix3d {
1435
1429
  const x = vectorU.x;
1436
1430
  const y = vectorU.y;
1437
1431
  const z = vectorU.z;
1438
- 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);
1432
+ 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
1433
  }
1440
- /** Multiply matrix * vector for each array member, i.e. the vector is a column vector on the right.
1441
- * @return the vector result
1434
+ /**
1435
+ * Multiply `matrix * vector` in place for vector in the array, i.e. treating the vector is a column
1436
+ * vector on the right.
1437
+ * * Each `vector` is updated to be `matrix * vector`
1442
1438
  */
1443
1439
  multiplyVectorArrayInPlace(data) {
1444
1440
  for (const v of data)
1445
- 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));
1441
+ 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
1442
  }
1447
- /** compute `origin - matrix * vector` */
1443
+ /** Compute `origin - matrix * vector` */
1448
1444
  static xyzMinusMatrixTimesXYZ(origin, matrix, vector, result) {
1449
1445
  const x = vector.x;
1450
1446
  const y = vector.y;
1451
1447
  const z = vector.z;
1452
1448
  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
1449
  }
1454
- /** compute `origin + matrix * vector` using only the xy parts of the inputs. */
1450
+ /** Compute `origin + matrix * vector` using only the xy parts of the inputs. */
1455
1451
  static xyPlusMatrixTimesXY(origin, matrix, vector, result) {
1456
1452
  const x = vector.x;
1457
1453
  const y = vector.y;
1458
1454
  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
1455
  }
1460
- /** compute `origin + matrix * vector` using all xyz parts of the inputs. */
1456
+ /** Compute `origin + matrix * vector` using all xyz parts of the inputs. */
1461
1457
  static xyzPlusMatrixTimesXYZ(origin, matrix, vector, result) {
1462
1458
  const x = vector.x;
1463
1459
  const y = vector.y;
1464
1460
  const z = vector.z;
1465
1461
  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
1462
  }
1467
- /** compute `origin + matrix * vector` using all xyz parts of the inputs. */
1463
+ /** Updates vector to be `origin + matrix * vector` using all xyz parts of the inputs. */
1468
1464
  static xyzPlusMatrixTimesXYZInPlace(origin, matrix, vector) {
1469
1465
  const x = vector.x;
1470
1466
  const y = vector.y;
@@ -1473,65 +1469,76 @@ export class Matrix3d {
1473
1469
  vector.y = origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z;
1474
1470
  vector.z = origin.z + matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z;
1475
1471
  }
1476
- /** compute `origin + matrix * vector` where the final vector is given as direct x,y,z coordinates */
1472
+ /** Compute `origin + matrix * vector` where the final vector is given as direct x,y,z coordinates */
1477
1473
  static xyzPlusMatrixTimesCoordinates(origin, matrix, x, y, z, result) {
1478
1474
  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
1475
  }
1480
1476
  /**
1481
1477
  * Treat the 3x3 matrix and origin as upper 3x4 part of a 4x4 matrix, with 0001 as the final row.
1482
- * Multiply times point with coordinates `[x,y,z,w]`
1478
+ * Multiply the 4x4 matrix by `[x,y,z,w]`
1479
+ * ```
1480
+ * equation
1481
+ * \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}
1482
+ * ```
1483
1483
  * @param origin translation part (xyz in column 3)
1484
1484
  * @param matrix matrix part (leading 3x3)
1485
1485
  * @param x x part of multiplied point
1486
1486
  * @param y y part of multiplied point
1487
1487
  * @param z z part of multiplied point
1488
1488
  * @param w w part of multiplied point
1489
- * @param result optional result.
1489
+ * @param result optional preallocated result.
1490
1490
  */
1491
1491
  static xyzPlusMatrixTimesWeightedCoordinates(origin, matrix, x, y, z, w, result) {
1492
- return Point4d.create(w * origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z, w * origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z, w * origin.z + matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z, w, result);
1492
+ 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
1493
  }
1494
1494
  /**
1495
1495
  * Treat the 3x3 matrix and origin as upper 3x4 part of a 4x4 matrix, with 0001 as the final row.
1496
- * Multiply times point with coordinates `[x,y,z,w]`
1496
+ * Multiply the 4x4 matrix by `[x,y,z,w]`
1497
+ * ```
1498
+ * equation
1499
+ * \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}
1500
+ * ```
1497
1501
  * @param origin translation part (xyz in column 3)
1498
1502
  * @param matrix matrix part (leading 3x3)
1499
1503
  * @param x x part of multiplied point
1500
1504
  * @param y y part of multiplied point
1501
1505
  * @param z z part of multiplied point
1502
1506
  * @param w w part of multiplied point
1503
- * @param result optional result.
1507
+ * @param result optional preallocated result.
1504
1508
  */
1505
1509
  static xyzPlusMatrixTimesWeightedCoordinatesToFloat64Array(origin, matrix, x, y, z, w, result) {
1506
1510
  if (!result)
1507
1511
  result = new Float64Array(4);
1508
- result[0] = w * origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z;
1509
- result[1] = w * origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z;
1510
- result[2] = w * origin.z + matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z;
1512
+ result[0] = matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z + origin.x * w;
1513
+ result[1] = matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z + origin.y * w;
1514
+ result[2] = matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z + origin.z * w;
1511
1515
  result[3] = w;
1512
1516
  return result;
1513
1517
  }
1514
1518
  /**
1515
- * Treat the 3x3 matrix and origin as upper 3x4 part of a 4x4 matrix, with 0001 as the final row.
1516
- * Multiply times point with coordinates `[x,y,z,w]`
1519
+ * Treat the 3x3 matrix and origin as a 3x4 matrix.
1520
+ * * Multiply the 3x4 matrix by `[x,y,z,1]`
1521
+ * ```
1522
+ * equation
1523
+ * \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}
1524
+ * ```
1517
1525
  * @param origin translation part (xyz in column 3)
1518
1526
  * @param matrix matrix part (leading 3x3)
1519
1527
  * @param x x part of multiplied point
1520
1528
  * @param y y part of multiplied point
1521
1529
  * @param z z part of multiplied point
1522
- * @param w w part of multiplied point
1523
- * @param result optional result.
1530
+ * @param result optional preallocated result.
1524
1531
  */
1525
1532
  static xyzPlusMatrixTimesCoordinatesToFloat64Array(origin, matrix, x, y, z, result) {
1526
1533
  if (!result)
1527
1534
  result = new Float64Array(3);
1528
- result[0] = origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z;
1529
- result[1] = origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z;
1530
- result[2] = origin.z + matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z;
1535
+ result[0] = matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z + origin.x;
1536
+ result[1] = matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z + origin.y;
1537
+ result[2] = matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z + origin.z;
1531
1538
  return result;
1532
1539
  }
1533
1540
  /**
1534
- * Multiply transpose of this matrix times a vector.
1541
+ * Multiply the transpose matrix times a vector.
1535
1542
  * * This produces the same x,y,z as treating the vector as a row on the left of the (un-transposed) matrix.
1536
1543
  * ```
1537
1544
  * equation
@@ -1540,99 +1547,112 @@ export class Matrix3d {
1540
1547
  * \text{Treating U as a row to the left of untransposed matrix\: return row}&\rowSubXYZ{V}&=&\rowSubXYZ{U}\matrixXY{A}
1541
1548
  * \end{matrix}
1542
1549
  * ```
1543
- * @return the vector result
1550
+ * @return the vector result (optional)
1544
1551
  */
1545
1552
  multiplyTransposeVector(vector, result) {
1546
1553
  result = result ? result : new Vector3d();
1547
1554
  const x = vector.x;
1548
1555
  const y = vector.y;
1549
1556
  const z = vector.z;
1550
- result.x = (this.coffs[0] * x + this.coffs[3] * y + this.coffs[6] * z);
1551
- result.y = (this.coffs[1] * x + this.coffs[4] * y + this.coffs[7] * z);
1552
- result.z = (this.coffs[2] * x + this.coffs[5] * y + this.coffs[8] * z);
1557
+ result.x = this.coffs[0] * x + this.coffs[3] * y + this.coffs[6] * z;
1558
+ result.y = this.coffs[1] * x + this.coffs[4] * y + this.coffs[7] * z;
1559
+ result.z = this.coffs[2] * x + this.coffs[5] * y + this.coffs[8] * z;
1553
1560
  return result;
1554
1561
  }
1555
- /** Multiply the matrix * (x,y,z), i.e. the vector (x,y,z) is a column vector on the right.
1556
- * @return the vector result
1562
+ /**
1563
+ * Multiply the matrix * [x,y,z], i.e. the vector [x,y,z] is a column vector on the right.
1564
+ * @return the vector result (optional)
1557
1565
  */
1558
1566
  multiplyXYZ(x, y, z, result) {
1559
1567
  result = result ? result : new Vector3d();
1560
- result.x = (this.coffs[0] * x + this.coffs[1] * y + this.coffs[2] * z);
1561
- result.y = (this.coffs[3] * x + this.coffs[4] * y + this.coffs[5] * z);
1562
- result.z = (this.coffs[6] * x + this.coffs[7] * y + this.coffs[8] * z);
1568
+ result.x = this.coffs[0] * x + this.coffs[1] * y + this.coffs[2] * z;
1569
+ result.y = this.coffs[3] * x + this.coffs[4] * y + this.coffs[5] * z;
1570
+ result.z = this.coffs[6] * x + this.coffs[7] * y + this.coffs[8] * z;
1563
1571
  return result;
1564
1572
  }
1565
- /** Multiply the matrix * xyz, place result in (required) return value.
1566
- * @param xyz right side
1567
- * @param result result.
1573
+ /**
1574
+ * Multiply the matrix * xyz, place result in (required) return value.
1575
+ * @param xyz right side
1576
+ * @param result the result.
1568
1577
  */
1569
1578
  multiplyXYZtoXYZ(xyz, result) {
1570
1579
  const x = xyz.x;
1571
1580
  const y = xyz.y;
1572
1581
  const z = xyz.z;
1573
- result.x = (this.coffs[0] * x + this.coffs[1] * y + this.coffs[2] * z);
1574
- result.y = (this.coffs[3] * x + this.coffs[4] * y + this.coffs[5] * z);
1575
- result.z = (this.coffs[6] * x + this.coffs[7] * y + this.coffs[8] * z);
1582
+ result.x = this.coffs[0] * x + this.coffs[1] * y + this.coffs[2] * z;
1583
+ result.y = this.coffs[3] * x + this.coffs[4] * y + this.coffs[5] * z;
1584
+ result.z = this.coffs[6] * x + this.coffs[7] * y + this.coffs[8] * z;
1576
1585
  return result;
1577
1586
  }
1578
- /** Multiply the matrix * (x,y,0), i.e. the vector (x,y,z) is a column vector on the right.
1579
- * @return the vector result
1587
+ /**
1588
+ * Multiply the matrix * [x,y,0], i.e. the vector [x,y,0] is a column vector on the right.
1589
+ * @return the vector result (optional)
1580
1590
  */
1581
1591
  multiplyXY(x, y, result) {
1582
1592
  result = result ? result : new Vector3d();
1583
- result.x = (this.coffs[0] * x + this.coffs[1] * y);
1584
- result.y = (this.coffs[3] * x + this.coffs[4] * y);
1585
- result.z = (this.coffs[6] * x + this.coffs[7] * y);
1593
+ result.x = this.coffs[0] * x + this.coffs[1] * y;
1594
+ result.y = this.coffs[3] * x + this.coffs[4] * y;
1595
+ result.z = this.coffs[6] * x + this.coffs[7] * y;
1586
1596
  return result;
1587
1597
  }
1588
- /** compute `origin + this*[x,y,0]` */
1598
+ /**
1599
+ * Compute origin + the matrix * [x,y,0].
1600
+ * @return the vector result (optional)
1601
+ */
1589
1602
  originPlusMatrixTimesXY(origin, x, y, result) {
1590
1603
  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
1604
  }
1592
- /** Multiply matrix * (x, y, z) using any 3d object given containing those members */
1605
+ /**
1606
+ * Multiply the matrix * (x,y,z) in place, i.e. the vector (x,y,z) is a column vector on the right and
1607
+ * the multiplication updates the vector values.
1608
+ * @param xyzData the vector data
1609
+ */
1593
1610
  multiplyVectorInPlace(xyzData) {
1594
1611
  const x = xyzData.x;
1595
1612
  const y = xyzData.y;
1596
1613
  const z = xyzData.z;
1597
- const coffs = this.coffs;
1598
- xyzData.x = (coffs[0] * x + coffs[1] * y + coffs[2] * z);
1599
- xyzData.y = (coffs[3] * x + coffs[4] * y + coffs[5] * z);
1600
- xyzData.z = (coffs[6] * x + coffs[7] * y + coffs[8] * z);
1614
+ xyzData.x = this.coffs[0] * x + this.coffs[1] * y + this.coffs[2] * z;
1615
+ xyzData.y = this.coffs[3] * x + this.coffs[4] * y + this.coffs[5] * z;
1616
+ xyzData.z = this.coffs[6] * x + this.coffs[7] * y + this.coffs[8] * z;
1601
1617
  }
1602
- /** Multiply the transpose matrix times column using any 3d object with x,y,z members.
1603
- * This is equivalent to `multiplyTransposeVector` but always returns the result directly in the input.
1618
+ /**
1619
+ * Multiply the transpose matrix times [x,y,z] in place, i.e. the vector [x,y,z] is a column vector on
1620
+ * the right and the multiplication updates the vector values.
1621
+ * * This is equivalent to `multiplyTransposeVector` but always returns the result directly in the input.
1622
+ * @param xyzData the vector data
1604
1623
  */
1605
1624
  multiplyTransposeVectorInPlace(vectorU) {
1606
1625
  const x = vectorU.x;
1607
1626
  const y = vectorU.y;
1608
1627
  const z = vectorU.z;
1609
- const coffs = this.coffs;
1610
- vectorU.x = (coffs[0] * x + coffs[3] * y + coffs[6] * z);
1611
- vectorU.y = (coffs[1] * x + coffs[4] * y + coffs[7] * z);
1612
- vectorU.z = (coffs[2] * x + coffs[5] * y + coffs[8] * z);
1628
+ vectorU.x = this.coffs[0] * x + this.coffs[3] * y + this.coffs[6] * z;
1629
+ vectorU.y = this.coffs[1] * x + this.coffs[4] * y + this.coffs[7] * z;
1630
+ vectorU.z = this.coffs[2] * x + this.coffs[5] * y + this.coffs[8] * z;
1613
1631
  }
1614
- /** Multiply the transpose matrix times column using individual numeric inputs.
1615
- * * This is equivalent to multiplying with the vector as a row to the left of the plain matrix.
1632
+ /**
1633
+ * Multiply the transpose matrix times column using individual numeric inputs.
1634
+ * * This produces the same x,y,z as treating the vector as a row on the left of the (un-transposed) matrix.
1616
1635
  * ```
1617
1636
  * equation
1618
1637
  * \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 \\
1638
+ * \text{treating the input as a column vector } \columnXYZ{x}{y}{z}\text{ compute }&\columnSubXYZ{V} &= &A^T \columnXYZ{x}{y}{z} \\
1639
+ * \text{or as a row vector } \rowXYZ{x}{y}{z} \text{ compute }&\rowSubXYZ{V} &= &\rowXYZ{x}{y}{z} A \\
1621
1640
  * \phantom{8888}\text{and return V as a Vector3d} & & &
1622
1641
  * \end{matrix}
1623
1642
  * ````
1624
- * @return the vector result
1643
+ * @return the vector result (optional)
1625
1644
  */
1626
1645
  multiplyTransposeXYZ(x, y, z, result) {
1627
1646
  result = result ? result : new Vector3d();
1628
- result.x = (this.coffs[0] * x + this.coffs[3] * y + this.coffs[6] * z);
1629
- result.y = (this.coffs[1] * x + this.coffs[4] * y + this.coffs[7] * z);
1630
- result.z = (this.coffs[2] * x + this.coffs[5] * y + this.coffs[8] * z);
1647
+ result.x = this.coffs[0] * x + this.coffs[3] * y + this.coffs[6] * z;
1648
+ result.y = this.coffs[1] * x + this.coffs[4] * y + this.coffs[7] * z;
1649
+ result.z = this.coffs[2] * x + this.coffs[5] * y + this.coffs[8] * z;
1631
1650
  return result;
1632
1651
  }
1633
- /** Solve `matrix * result = vector`.
1634
- * * This is equivalent to multiplication `result = matrixInverse * vector`.
1635
- * * Result is undefined if the matrix is singular (e.g. has parallel or zero length columns)
1652
+ /**
1653
+ * Solve `matrix * result = vector` for an unknown `result`.
1654
+ * * This is equivalent to multiplication `result = inverse matrix * vector`.
1655
+ * * Result is undefined if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
1636
1656
  */
1637
1657
  multiplyInverse(vector, result) {
1638
1658
  this.computeCachedInverse(true);
@@ -1640,13 +1660,14 @@ export class Matrix3d {
1640
1660
  const x = vector.x;
1641
1661
  const y = vector.y;
1642
1662
  const z = vector.z;
1643
- 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);
1663
+ 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
1664
  }
1645
1665
  return undefined;
1646
1666
  }
1647
- /** Solve `matrixTranspose * result = vector`.
1667
+ /**
1668
+ * Solve `matrixTranspose * result = vector` for an unknown `result`.
1648
1669
  * * This is equivalent to multiplication `result = matrixInverseTranspose * vector`.
1649
- * * Result is undefined if the matrix is singular (e.g. has parallel or zero length columns)
1670
+ * * Result is undefined if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
1650
1671
  */
1651
1672
  multiplyInverseTranspose(vector, result) {
1652
1673
  this.computeCachedInverse(true);
@@ -1654,87 +1675,91 @@ export class Matrix3d {
1654
1675
  const x = vector.x;
1655
1676
  const y = vector.y;
1656
1677
  const z = vector.z;
1657
- 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);
1678
+ 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
1679
  }
1659
1680
  return undefined;
1660
1681
  }
1661
1682
  /**
1662
- * multiply `matrixInverse * [x,y,z]`.
1663
- * * This is equivalent to solving `matrix * result = [x,y,z]`
1664
- * * return as a Vector3d, or undefined if the matrix is singular.
1683
+ * Multiply `matrixInverse * [x,y,z]`.
1684
+ * * This is equivalent to solving `matrix * result = [x,y,z]` for an unknown `result`.
1685
+ * * Result is undefined if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
1686
+ * @return result as a Vector3d or undefined (if the matrix is singular).
1665
1687
  */
1666
1688
  multiplyInverseXYZAsVector3d(x, y, z, result) {
1667
1689
  this.computeCachedInverse(true);
1668
1690
  if (this.inverseCoffs) {
1669
- 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);
1691
+ 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
1692
  }
1671
1693
  return undefined;
1672
1694
  }
1673
1695
  /**
1674
- * multiply `matrixInverse * [x,y,z]` and return packaged as `Point4d` with given weight.
1675
- * * Equivalent to solving matrix * result = [x,y,z]
1676
- * * return as a Point4d with the same weight.
1677
- * * Called by Transform with x,y,z adjusted by subtraction ((xw) - w * origin.x, etc) where xw is the pre-weighted space point.
1696
+ * Multiply `matrixInverse * [x,y,z]` and return result as `Point4d` with given weight.
1697
+ * * Equivalent to solving `matrix * result = [x,y,z]` for an unknown `result`.
1698
+ * * Result is undefined if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
1699
+ * @return result as a Point4d with the same weight.
1678
1700
  */
1679
1701
  multiplyInverseXYZW(x, y, z, w, result) {
1680
1702
  this.computeCachedInverse(true);
1681
1703
  if (this.inverseCoffs) {
1682
- 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);
1704
+ 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
1705
  }
1684
1706
  return undefined;
1685
1707
  }
1686
1708
  /**
1687
- * multiply `matrixInverse * [x,y,z]` and return packaged as `Point3d`.
1688
- * * multiply matrixInverse * [x,y,z]
1689
- * * Equivalent to solving matrix * result = [x,y,z]
1690
- * * return as a Point3d.
1709
+ * Multiply `matrixInverse * [x,y,z]` and return result as `Point3d`.
1710
+ * * Equivalent to solving `matrix * result = [x,y,z]` for an unknown `result`.
1711
+ * @return result as a Point3d or undefined (if the matrix is singular).
1691
1712
  */
1692
1713
  multiplyInverseXYZAsPoint3d(x, y, z, result) {
1693
1714
  this.computeCachedInverse(true);
1694
1715
  if (this.inverseCoffs) {
1695
- 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);
1716
+ 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
1717
  }
1697
1718
  return undefined;
1698
1719
  }
1699
1720
  /**
1700
- * * invoke a given matrix-matrix operation (product function) to compute this.inverseCOffs
1701
- * * set this.inverseCoffs
1702
- * * if either input cffA or coffB is undefined, set state to `InverseMatrixState.unknown` (but leave the inverseCoffs untouched)
1721
+ * Invoke a given matrix*matrix operation to compute the inverse matrix and set this.inverseCoffs
1722
+ * * If either input coffA or coffB is undefined, set state to `InverseMatrixState.unknown` but
1723
+ * leave the inverseCoffs untouched.
1724
+ * @param f the given matrix*matrix operation that is called by this function to compute the inverse.
1725
+ * `f` must be a matrix*matrix operation. Otherwise, the function does not generate the inverse properly.
1703
1726
  */
1704
1727
  finishInverseCoffs(f, coffA, coffB) {
1705
1728
  if (coffA && coffB) {
1706
1729
  this.createInverseCoffsWithZeros();
1707
1730
  this.inverseState = InverseMatrixState.inverseStored;
1708
- f(coffA, coffB, this.inverseCoffs);
1731
+ f(coffA, coffB, this.inverseCoffs); // call function f (which is provided by user) to compute the inverse.
1709
1732
  }
1710
1733
  else {
1711
1734
  this.inverseState = InverseMatrixState.unknown;
1712
1735
  }
1713
1736
  }
1714
- /*
1715
- Notes on inverses of products
1716
- * 1) M = A * B MInverse = BInverse * AInverse
1717
- * 2) M = A * BInverse MInverse = B * AInverse
1718
- * 3) M = AInverse * B MInverse = BInverse * A
1719
- * 4) M = ATranspose * B MInverse = BInverse * AInverseTranspose
1720
- * 5) M = A * BTranspose MInverse = BInverseTranspose * AInverse
1721
- */
1722
- /** Multiply the instance matrix A by the input matrix B.
1723
- * @return the matrix product A * B
1737
+ // Notes on inverse of matrix products:
1738
+ // 1) M = A * B ===> MInverse = BInverse * AInverse
1739
+ // 2) M = A * BInverse ===> MInverse = B * AInverse
1740
+ // 3) M = AInverse * B ===> MInverse = BInverse * A
1741
+ // 4) M = A * BTranspose ===> MInverse = BInverseTranspose * AInverse
1742
+ // 5) M = ATranspose * B ===> MInverse = BInverse * AInverseTranspose
1743
+ /**
1744
+ * Multiply `this` matrix times `other` matrix
1745
+ * @return the matrix result: this*other
1724
1746
  */
1725
1747
  multiplyMatrixMatrix(other, result) {
1726
1748
  result = result ? result : new Matrix3d();
1727
1749
  PackedMatrix3dOps.multiplyMatrixMatrix(this.coffs, other.coffs, result.coffs);
1728
- if (this.inverseState === InverseMatrixState.inverseStored && other.inverseState === InverseMatrixState.inverseStored)
1750
+ if (this.inverseState === InverseMatrixState.inverseStored
1751
+ && other.inverseState === InverseMatrixState.inverseStored)
1729
1752
  result.finishInverseCoffs(PackedMatrix3dOps.multiplyMatrixMatrix, other.inverseCoffs, this.inverseCoffs);
1730
- else if (this.inverseState === InverseMatrixState.singular || other.inverseState === InverseMatrixState.singular)
1753
+ else if (this.inverseState === InverseMatrixState.singular
1754
+ || other.inverseState === InverseMatrixState.singular)
1731
1755
  result.inverseState = InverseMatrixState.singular;
1732
1756
  else
1733
1757
  result.inverseState = InverseMatrixState.unknown;
1734
1758
  return result;
1735
1759
  }
1736
- /** Multiply this matrix times inverse of other
1737
- * @return the matrix result
1760
+ /**
1761
+ * Multiply `this` matrix times `inverse of other` matrix
1762
+ * @return the matrix result: this*otherInverse
1738
1763
  */
1739
1764
  multiplyMatrixMatrixInverse(other, result) {
1740
1765
  if (!other.computeCachedInverse(true))
@@ -1748,8 +1773,9 @@ export class Matrix3d {
1748
1773
  PackedMatrix3dOps.copy(Matrix3d._productBuffer, result.coffs);
1749
1774
  return result;
1750
1775
  }
1751
- /** Multiply this matrix times inverse of other
1752
- * @return the matrix result
1776
+ /**
1777
+ * Multiply `inverse of this` matrix times `other` matrix
1778
+ * @return the matrix result: thisInverse*other
1753
1779
  */
1754
1780
  multiplyMatrixInverseMatrix(other, result) {
1755
1781
  if (!this.computeCachedInverse(true))
@@ -1763,30 +1789,32 @@ export class Matrix3d {
1763
1789
  PackedMatrix3dOps.copy(Matrix3d._productBuffer, result.coffs);
1764
1790
  return result;
1765
1791
  }
1766
- /** Multiply `this` matrix times the transpose of `matrixB`.
1792
+ /**
1793
+ * Multiply `this` matrix times the transpose of `other` matrix
1767
1794
  * ```
1768
1795
  * equation
1769
- * \text{for instance matrix }A\text{ and other matrix }B\text{ return matrix }C{\text where }\\\matrixXY{C}=\matrixXY{A}\matrixTransposeSubXY{B}
1796
+ * \text{for instance matrix }A\text{ and matrix }B\text{ return matrix }C{\text where }\\\matrixXY{C}=\matrixXY{A}\matrixTransposeSubXY{B}
1770
1797
  * ```
1771
- * @return the matrix result
1798
+ * @return the matrix result: this*otherTranspose
1772
1799
  */
1773
- multiplyMatrixMatrixTranspose(matrixB, result) {
1800
+ multiplyMatrixMatrixTranspose(other, result) {
1774
1801
  result = result ? result : new Matrix3d();
1775
- PackedMatrix3dOps.multiplyMatrixMatrixTranspose(this.coffs, matrixB.coffs, result.coffs);
1776
- if (this.inverseState === InverseMatrixState.inverseStored && matrixB.inverseState === InverseMatrixState.inverseStored)
1777
- result.finishInverseCoffs(PackedMatrix3dOps.multiplyMatrixTransposeMatrix, matrixB.inverseCoffs, this.inverseCoffs);
1778
- else if (this.inverseState === InverseMatrixState.singular || matrixB.inverseState === InverseMatrixState.singular)
1802
+ PackedMatrix3dOps.multiplyMatrixMatrixTranspose(this.coffs, other.coffs, result.coffs);
1803
+ if (this.inverseState === InverseMatrixState.inverseStored && other.inverseState === InverseMatrixState.inverseStored)
1804
+ result.finishInverseCoffs(PackedMatrix3dOps.multiplyMatrixTransposeMatrix, other.inverseCoffs, this.inverseCoffs);
1805
+ else if (this.inverseState === InverseMatrixState.singular || other.inverseState === InverseMatrixState.singular)
1779
1806
  result.inverseState = InverseMatrixState.singular;
1780
1807
  else
1781
1808
  result.inverseState = InverseMatrixState.unknown;
1782
1809
  return result;
1783
1810
  }
1784
- /** Matrix multiplication `thisTranspose * other`.
1811
+ /**
1812
+ * Multiply the transpose of `this` matrix times `other` matrix
1785
1813
  * ```
1786
1814
  * equation
1787
1815
  * \matrixXY{result}=\matrixXY{\text{this}}\matrixTransposeSubXY{\text{other}}
1788
1816
  * ```
1789
- * @return the matrix result
1817
+ * @return the matrix result: thisTranspose*other
1790
1818
  */
1791
1819
  multiplyMatrixTransposeMatrix(other, result) {
1792
1820
  result = result ? result : new Matrix3d();
@@ -1799,30 +1827,31 @@ export class Matrix3d {
1799
1827
  result.inverseState = InverseMatrixState.unknown;
1800
1828
  return result;
1801
1829
  }
1802
- /** multiply this Matrix3d (considered as a transform with 0 translation) times other Transform.
1830
+ /**
1831
+ * Multiply `this` Matrix3d (considered as a Transform with 0 translation) times `other` Transform.
1803
1832
  * ```
1804
1833
  * equation
1805
1834
  * \begin{matrix}
1806
- * \text{This matrix }\bold{A}\text{ promoted to block transform} & \blockTransform{A}{0} \\
1807
- * \text{other transform with matrix part }\bold{B}\text{ and translation }\bold{b} & \blockTransform{B}{b}\\
1835
+ * \text{This matrix }\bold{A}\text{ promoted to block transform} & \blockTransform{A}{0} \\
1836
+ * \text{other transform with matrix part }\bold{B}\text{ and translation }\bold{b} & \blockTransform{B}{b}\\
1808
1837
  * \text{product}& \blockTransform{A}{0}\blockTransform{B}{b}=\blockTransform{AB}{Ab}
1809
1838
  * \end{matrix}
1810
1839
  * ```
1811
- * @param other right hand Matrix3d for multiplication.
1812
- * @param result optional preallocated result to reuse.
1840
+ * @param other Right hand Matrix3d for multiplication.
1841
+ * @param result the Transform result (optional)
1813
1842
  */
1814
1843
  multiplyMatrixTransform(other, result) {
1815
1844
  if (!result)
1816
1845
  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 ..
1846
+ // be sure to do the point multiplication first before aliasing changes the matrix
1818
1847
  this.multiplyXYZtoXYZ(other.origin, result.origin);
1819
1848
  this.multiplyMatrixMatrix(other.matrix, result.matrix);
1820
1849
  return result;
1821
1850
  }
1822
1851
  /**
1823
1852
  * 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
1853
+ * * If `result` is passed as argument, then the function copies the transpose of `this` into `result`.
1854
+ * * `this` is not changed unless also passed as the result, i.e., `this.transpose(this)` transposes `this` in place.
1826
1855
  */
1827
1856
  transpose(result) {
1828
1857
  if (!result)
@@ -1844,18 +1873,22 @@ export class Matrix3d {
1844
1873
  transposeInPlace() {
1845
1874
  PackedMatrix3dOps.transposeInPlace(this.coffs);
1846
1875
  if (this.inverseCoffs)
1847
- PackedMatrix3dOps.transposeInPlace(this.inverseCoffs);
1876
+ PackedMatrix3dOps.transposeInPlace(this.inverseCoffs); // inverse of transpose is equal to transpose of inverse
1848
1877
  }
1849
- /** return the inverse matrix.
1850
- * The return is undefined if the matrix is singular (has columns that are coplanar or colinear)
1851
- * * Note that each Matrix3d object caches its own inverse, and has methods to multiply the inverse times matrices and vectors.
1852
- * * Hence explicitly constructing this new inverse object is rarely necessary.
1878
+ /**
1879
+ * Return the inverse matrix.
1880
+ * The return is undefined if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
1881
+ * * If `result == this`, then content of inverse of `this` matrix is copied into `this`. Otherwise, inverse
1882
+ * of `this` is stored in `result`.
1883
+ * * **Note:** Each Matrix3d object caches its own inverse (`this.inverseCoffs`) and has methods to multiply
1884
+ * the inverse times matrices and vectors (e.g., `multiplyMatrixInverseMatrix`, `multiplyMatrixMatrixInverse`,
1885
+ * `multiplyInverse`). Hence explicitly constructing this new inverse object is rarely necessary.
1853
1886
  */
1854
1887
  inverse(result) {
1855
1888
  if (!this.computeCachedInverse(true))
1856
1889
  return undefined;
1857
1890
  if (result === this) {
1858
- // swap the contents (preserve pointers .. caller better know what they are doing)
1891
+ // swap the contents of this.coffs and this.inverseCoffs
1859
1892
  PackedMatrix3dOps.copy(this.coffs, Matrix3d._productBuffer);
1860
1893
  PackedMatrix3dOps.copy(this.inverseCoffs, this.coffs);
1861
1894
  PackedMatrix3dOps.copy(Matrix3d._productBuffer, this.inverseCoffs);
@@ -1870,33 +1903,48 @@ export class Matrix3d {
1870
1903
  result.inverseState = this.inverseState;
1871
1904
  return result;
1872
1905
  }
1873
- /* Alternate implementation of computedCachedInverse - more direct addressing of arrays.
1874
- This is indeed 10% faster than using static work areas. */
1875
- // take the cross product of two rows of source.
1876
- // store as a column of dest.
1906
+ /**
1907
+ * Take the dot product of a row (specified by `rowStartA`) of `coffA` and `columnStartB` of `coffB`.
1908
+ * * **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.
1909
+ */
1910
+ static rowColumnDot(coffA, rowStartA, coffB, columnStartB) {
1911
+ return coffA[rowStartA] * coffB[columnStartB] +
1912
+ coffA[rowStartA + 1] * coffB[columnStartB + 3] +
1913
+ coffA[rowStartA + 2] * coffB[columnStartB + 6];
1914
+ }
1915
+ /**
1916
+ * Take the cross product of 2 rows (specified by `rowStart0` and `rowStart1`) of `source` and store the result
1917
+ * in `columnStart` of `dest`.
1918
+ * * **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.
1919
+ */
1877
1920
  static indexedRowCrossProduct(source, rowStart0, rowStart1, dest, columnStart) {
1878
1921
  dest[columnStart] = source[rowStart0 + 1] * source[rowStart1 + 2] - source[rowStart0 + 2] * source[rowStart1 + 1];
1879
1922
  dest[columnStart + 3] = source[rowStart0 + 2] * source[rowStart1] - source[rowStart0] * source[rowStart1 + 2];
1880
1923
  dest[columnStart + 6] = source[rowStart0] * source[rowStart1 + 1] - source[rowStart0 + 1] * source[rowStart1];
1881
1924
  }
1882
- // take the cross product of two columns of source.
1883
- // store as third column in same Matrix3d.
1884
- // This is private because the columnStart values are unchecked raw indices into the coffs
1925
+ /**
1926
+ * Take the cross product of 2 columns (i.e., `colStart0` and `colStart1`) of `this` matrix and store the
1927
+ * result in `colStart2` of the same matrix.
1928
+ * * **Note:** We don't validate column numbers. Pass 0/1/2 for column 0/1/2.
1929
+ */
1885
1930
  indexedColumnCrossProductInPlace(colStart0, colStart1, colStart2) {
1886
1931
  const coffs = this.coffs;
1887
1932
  coffs[colStart2] = coffs[colStart0 + 3] * coffs[colStart1 + 6] - coffs[colStart0 + 6] * coffs[colStart1 + 3];
1888
1933
  coffs[colStart2 + 3] = coffs[colStart0 + 6] * coffs[colStart1] - coffs[colStart0] * coffs[colStart1 + 6];
1889
1934
  coffs[colStart2 + 6] = coffs[colStart0] * coffs[colStart1 + 3] - coffs[colStart0 + 3] * coffs[colStart1];
1890
1935
  }
1891
- /** Form cross products among columns in axisOrder.
1892
- * For axis order ABC,
1893
- * * form cross product of column A and B, store in C
1936
+ /**
1937
+ * Form cross products among columns in axisOrder.
1938
+ * For axis order ABC:
1939
+ * * form cross product of column A and B, store in C.
1894
1940
  * * form cross product of column C and A, store in B.
1941
+ * * [A B C] ===> [A B AxB] ===> [A (AxB)xA AxB]
1942
+ *
1895
1943
  * This means that in the final matrix:
1896
- * * column A is strictly parallel to original column A
1897
- * * column B is linear combination of only original A and B
1944
+ * * column A is same as original column A.
1945
+ * * column B is linear combination of original A and B (i.e., is in the plane of original A and B).
1898
1946
  * * column C is perpendicular to A and B of both the original and final.
1899
- * * original column C does not participate in the result.
1947
+ * * original column C is overwritten and does not participate in the result.
1900
1948
  */
1901
1949
  axisOrderCrossProductsInPlace(axisOrder) {
1902
1950
  switch (axisOrder) {
@@ -1932,41 +1980,44 @@ export class Matrix3d {
1932
1980
  }
1933
1981
  }
1934
1982
  }
1935
- /** Normalize each column in place.
1936
- * * For false return the magnitudes are stored in the originalMagnitudes vector but no columns are altered.
1937
- * @returns Return true if all columns had nonzero lengths.
1938
- * @param originalMagnitudes optional vector to receive original column magnitudes.
1983
+ /**
1984
+ * Normalize each column in place.
1985
+ * @param originalRowMagnitudes optional vector to store original column magnitudes.
1986
+ * @returns Return true if all columns have non-zero lengths. Otherwise, return false.
1987
+ * * If false is returned, the magnitudes are stored in the `originalRowMagnitudes` vector but no columns
1988
+ * are altered.
1939
1989
  */
1940
- normalizeColumnsInPlace(originalMagnitudes) {
1990
+ normalizeColumnsInPlace(originalRowMagnitudes) {
1941
1991
  const ax = this.columnXMagnitude();
1942
1992
  const ay = this.columnYMagnitude();
1943
1993
  const az = this.columnZMagnitude();
1944
- if (originalMagnitudes)
1945
- originalMagnitudes.set(ax, ay, az);
1994
+ if (originalRowMagnitudes)
1995
+ originalRowMagnitudes.set(ax, ay, az);
1946
1996
  if (Geometry.isSmallMetricDistance(ax) || Geometry.isSmallMetricDistance(ay) || Geometry.isSmallMetricDistance(az))
1947
1997
  return false;
1948
1998
  this.scaleColumns(1.0 / ax, 1.0 / ay, 1.0 / az, this);
1949
1999
  return true;
1950
2000
  }
1951
- /** Normalize each row in place */
1952
- normalizeRowsInPlace(originalMagnitudes) {
2001
+ /**
2002
+ * Normalize each row in place.
2003
+ * @param originalColumnMagnitudes optional vector to store original row magnitudes.
2004
+ * @returns Return true if all rows have non-zero lengths. Otherwise, return false.
2005
+ * * If false is returned, the magnitudes are stored in the `originalColumnMagnitudes` vector but no rows
2006
+ * are altered.
2007
+ */
2008
+ normalizeRowsInPlace(originalColumnMagnitudes) {
1953
2009
  const ax = this.rowXMagnitude();
1954
2010
  const ay = this.rowYMagnitude();
1955
2011
  const az = this.rowZMagnitude();
1956
- if (originalMagnitudes)
1957
- originalMagnitudes.set(ax, ay, az);
2012
+ if (originalColumnMagnitudes)
2013
+ originalColumnMagnitudes.set(ax, ay, az);
1958
2014
  if (Geometry.isSmallMetricDistance(ax) || Geometry.isSmallMetricDistance(ay) || Geometry.isSmallMetricDistance(az))
1959
2015
  return false;
1960
2016
  this.scaleRows(1.0 / ax, 1.0 / ay, 1.0 / az, this);
1961
2017
  return true;
1962
2018
  }
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
2019
  /**
1969
- * Returns true if the matrix is singular (i.e. collapses data to a plane, line, or point)
2020
+ * Returns true if the matrix is singular.
1970
2021
  */
1971
2022
  isSingular() {
1972
2023
  return !this.computeCachedInverse(true);
@@ -1978,18 +2029,10 @@ export class Matrix3d {
1978
2029
  this.inverseState = InverseMatrixState.singular;
1979
2030
  }
1980
2031
  /**
1981
- * Create the inverseCoffs member (filled with zeros)
1982
- * This is for use by matrix * matrix multiplications which need to be sure the member is there to be
1983
- * filled with method-specific content.
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.)
2032
+ * Compute the inverse of `this` Matrix3d. The inverse is stored in `this.inverseCoffs` for later use.
2033
+ * @param useCacheIfAvailable if `true`, use the previously computed inverse if available. If `false`,
2034
+ * recompute the inverse.
2035
+ * @returns Return `true` if the inverse is computed. Return `false` if matrix is singular.
1993
2036
  */
1994
2037
  computeCachedInverse(useCacheIfAvailable) {
1995
2038
  if (useCacheIfAvailable && Matrix3d.useCachedInverse && this.inverseState !== InverseMatrixState.unknown) {
@@ -2000,71 +2043,45 @@ export class Matrix3d {
2000
2043
  this.createInverseCoffsWithZeros();
2001
2044
  const coffs = this.coffs;
2002
2045
  const inverseCoffs = this.inverseCoffs;
2003
- Matrix3d.indexedRowCrossProduct(coffs, 3, 6, inverseCoffs, 0);
2004
- Matrix3d.indexedRowCrossProduct(coffs, 6, 0, inverseCoffs, 1);
2005
- Matrix3d.indexedRowCrossProduct(coffs, 0, 3, inverseCoffs, 2);
2046
+ /**
2047
+ * We calculate the inverse using cross products.
2048
+ * Math details can be found at
2049
+ * https://www.chilimath.com/lessons/advanced-algebra/determinant-3x3-matrix/
2050
+ * In summary, if M = [A B C] then inverse of M = (1/det)[BxC CxA AxB] where det is the
2051
+ * determinant of matrix M and can be calculated by "A dot BxC".
2052
+ */
2053
+ Matrix3d.indexedRowCrossProduct(coffs, 3, 6, inverseCoffs, 0); // BxC
2054
+ Matrix3d.indexedRowCrossProduct(coffs, 6, 0, inverseCoffs, 1); // CxA
2055
+ Matrix3d.indexedRowCrossProduct(coffs, 0, 3, inverseCoffs, 2); // AxB
2006
2056
  Matrix3d.numComputeCache++;
2007
- const d = Matrix3d.rowColumnDot(coffs, 0, inverseCoffs, 0);
2008
- if (d === 0.0) { // better test?
2057
+ const det = Matrix3d.rowColumnDot(coffs, 0, inverseCoffs, 0); // A dot BxC
2058
+ if (det === 0.0) {
2009
2059
  this.inverseState = InverseMatrixState.singular;
2010
2060
  this.inverseCoffs = undefined;
2011
2061
  return false;
2012
2062
  }
2013
- const f = 1.0 / d;
2063
+ const f = 1.0 / det;
2014
2064
  for (let i = 0; i < 9; i++)
2015
2065
  inverseCoffs[i] *= f;
2016
2066
  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
2067
  return true;
2023
2068
  }
2024
- /* "Classic" inverse implementation with temporary vectors.
2025
- private static rowX: Vector3d = Vector3d.create();
2026
- private static rowY: Vector3d = Vector3d.create();
2027
- private static rowZ: Vector3d = Vector3d.create();
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 */
2069
+ /**
2070
+ * Convert a (row,column) index pair to the single index within flattened array of 9 numbers in row-major-order
2071
+ * * **Note:** Out of range row/column is interpreted cyclically.
2072
+ */
2059
2073
  static flatIndexOf(row, column) {
2060
2074
  return 3 * Geometry.cyclic3dAxis(row) + Geometry.cyclic3dAxis(column);
2061
2075
  }
2062
- /** Get a column by index (0,1,2), packaged as a Point4d with given weight. Out of range index is interpreted cyclically. */
2076
+ /**
2077
+ * Get elements of column `index` packaged as a Point4d with given `weight`.
2078
+ * * **Note:** Out of range index is interpreted cyclically.
2079
+ */
2063
2080
  indexedColumnWithWeight(index, weight, result) {
2064
2081
  index = Geometry.cyclic3dAxis(index);
2065
2082
  return Point4d.create(this.coffs[index], this.coffs[index + 3], this.coffs[index + 6], weight, result);
2066
2083
  }
2067
- /** return the entry at specific row and column */
2084
+ /** Return the entry at specific row and column */
2068
2085
  at(row, column) {
2069
2086
  return this.coffs[Matrix3d.flatIndexOf(row, column)];
2070
2087
  }
@@ -2077,7 +2094,7 @@ export class Matrix3d {
2077
2094
  * @param scaleX scale factor for column x
2078
2095
  * @param scaleY scale factor for column y
2079
2096
  * @param scaleZ scale factor for column z
2080
- * @param result optional result.
2097
+ * @param result optional preallocated result.
2081
2098
  */
2082
2099
  scaleColumns(scaleX, scaleY, scaleZ, result) {
2083
2100
  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);
@@ -2098,7 +2115,7 @@ export class Matrix3d {
2098
2115
  this.coffs[7] *= scaleY;
2099
2116
  this.coffs[8] *= scaleZ;
2100
2117
  if (this.inverseState === InverseMatrixState.inverseStored && this.inverseCoffs !== undefined) {
2101
- // apply reciprocal scales to the ROWS of the inverse . . .
2118
+ // apply reciprocal scales to the ROWS of the inverse
2102
2119
  const divX = Geometry.conditionalDivideFraction(1.0, scaleX);
2103
2120
  const divY = Geometry.conditionalDivideFraction(1.0, scaleY);
2104
2121
  const divZ = Geometry.conditionalDivideFraction(1.0, scaleZ);
@@ -2121,7 +2138,7 @@ export class Matrix3d {
2121
2138
  * @param scaleX scale factor for row x
2122
2139
  * @param scaleY scale factor for row y
2123
2140
  * @param scaleZ scale factor for row z
2124
- * @param result optional result.
2141
+ * @param result optional preallocated result.
2125
2142
  */
2126
2143
  scaleRows(scaleX, scaleY, scaleZ, result) {
2127
2144
  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);
@@ -2165,12 +2182,64 @@ export class Matrix3d {
2165
2182
  }
2166
2183
  /** create a Matrix3d whose values are uniformly scaled from this.
2167
2184
  * @param scale scale factor to apply.
2168
- * @param result optional result.
2185
+ * @param result optional preallocated result.
2169
2186
  * @returns Return the new or repopulated matrix
2170
2187
  */
2171
2188
  scale(scale, result) {
2172
2189
  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);
2173
2190
  }
2191
+ /**
2192
+ * Create a rigid matrix (columns and rows are unit length and pairwise perpendicular) for
2193
+ * the given eye coordinate.
2194
+ * * column 2 is parallel to (x,y,z).
2195
+ * * column 0 is perpendicular to column 2 and is in the xy plane.
2196
+ * * column 1 is perpendicular to both. It is the "up" vector on the view plane.
2197
+ * * Multiplying the returned matrix times a local (view) vector gives the world vector.
2198
+ * * Multiplying transpose of the returned matrix times a world vector gives the local (view) vector.
2199
+ * @param x eye x coordinate
2200
+ * @param y eye y coordinate
2201
+ * @param z eye z coordinate
2202
+ * @param result optional preallocated result
2203
+ */
2204
+ static createRigidViewAxesZTowardsEye(x, y, z, result) {
2205
+ result = Matrix3d.createIdentity(result);
2206
+ const rxy = Geometry.hypotenuseXY(x, y);
2207
+ // if coordinate is (0,0,z), i.e., Top or Bottom view
2208
+ if (Geometry.isSmallMetricDistance(rxy)) {
2209
+ if (z < 0.0)
2210
+ result.scaleColumnsInPlace(1.0, -1.0, -1.0);
2211
+ }
2212
+ else {
2213
+ // if coordinate is (x,y,0), i.e., Front or Back or Left or Right view
2214
+ /**
2215
+ * The matrix that the "else" statement creates is
2216
+ * [-s -s1*c c1*c]
2217
+ * [c -s1*s c1*s]
2218
+ * [0 c1 s1 ]
2219
+ * where
2220
+ * c = x / sqrt(x*x + y*y)
2221
+ * s = y / sqrt(x*x + y*y)
2222
+ * c1 = sqrt(x*x + y*y) / sqrt(x*x + y*y + z*z)
2223
+ * s1 = z / sqrt(x*x + y*y + z*z)
2224
+ *
2225
+ * This is an orthogonal matrix meaning it rotates the standard XYZ axis to ABC axis system
2226
+ * (if matrix is [A B C]). The matrix rotates (0,0,1), i.e., the default Top view or Z axis,
2227
+ * to the eye point (x/r,y/r,z/r). The matrix also rotates (1,0,0) to a point on XY plane.
2228
+ */
2229
+ const c = x / rxy;
2230
+ const s = y / rxy;
2231
+ // 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)
2232
+ result.setRowValues(-s, 0, c, c, 0, s, 0, 1, 0);
2233
+ // if coordinate is (x,y,z) and z is not 0, i.e., other views such as Iso or RightIso
2234
+ if (z !== 0.0) {
2235
+ const r = Geometry.hypotenuseXYZ(x, y, z);
2236
+ const s1 = z / r;
2237
+ const c1 = rxy / r;
2238
+ result.applyGivensColumnOp(1, 2, c1, -s1);
2239
+ }
2240
+ }
2241
+ return result;
2242
+ }
2174
2243
  /** Return the determinant of this matrix. */
2175
2244
  determinant() {
2176
2245
  return this.coffs[0] * this.coffs[4] * this.coffs[8]
@@ -2433,5 +2502,6 @@ Matrix3d.useCachedInverse = true; // cached inverse can be suppressed for testin
2433
2502
  Matrix3d.numUseCache = 0;
2434
2503
  /** Total number of times a cached inverse was computed. */
2435
2504
  Matrix3d.numComputeCache = 0;
2505
+ /** temporary buffer to store a matrix as a Float64Array (array of 9 floats) */
2436
2506
  Matrix3d._productBuffer = new Float64Array(9);
2437
2507
  //# sourceMappingURL=Matrix3d.js.map