@itwin/core-geometry 4.0.0-dev.37 → 4.0.0-dev.39

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 (83) hide show
  1. package/lib/cjs/Geometry.d.ts +28 -12
  2. package/lib/cjs/Geometry.d.ts.map +1 -1
  3. package/lib/cjs/Geometry.js +33 -17
  4. package/lib/cjs/Geometry.js.map +1 -1
  5. package/lib/cjs/curve/Loop.d.ts.map +1 -1
  6. package/lib/cjs/curve/Loop.js +6 -0
  7. package/lib/cjs/curve/Loop.js.map +1 -1
  8. package/lib/cjs/curve/RegionOps.d.ts +8 -9
  9. package/lib/cjs/curve/RegionOps.d.ts.map +1 -1
  10. package/lib/cjs/curve/RegionOps.js +8 -9
  11. package/lib/cjs/curve/RegionOps.js.map +1 -1
  12. package/lib/cjs/curve/RegionOpsClassificationSweeps.d.ts.map +1 -1
  13. package/lib/cjs/curve/RegionOpsClassificationSweeps.js +2 -0
  14. package/lib/cjs/curve/RegionOpsClassificationSweeps.js.map +1 -1
  15. package/lib/cjs/geometry3d/Matrix3d.d.ts +137 -89
  16. package/lib/cjs/geometry3d/Matrix3d.d.ts.map +1 -1
  17. package/lib/cjs/geometry3d/Matrix3d.js +244 -155
  18. package/lib/cjs/geometry3d/Matrix3d.js.map +1 -1
  19. package/lib/cjs/geometry3d/Point3dVector3d.d.ts +15 -15
  20. package/lib/cjs/geometry3d/Point3dVector3d.d.ts.map +1 -1
  21. package/lib/cjs/geometry3d/Point3dVector3d.js +16 -19
  22. package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
  23. package/lib/cjs/geometry3d/PolygonOps.d.ts +7 -9
  24. package/lib/cjs/geometry3d/PolygonOps.d.ts.map +1 -1
  25. package/lib/cjs/geometry3d/PolygonOps.js +7 -9
  26. package/lib/cjs/geometry3d/PolygonOps.js.map +1 -1
  27. package/lib/cjs/polyface/PolyfaceBuilder.d.ts +3 -0
  28. package/lib/cjs/polyface/PolyfaceBuilder.d.ts.map +1 -1
  29. package/lib/cjs/polyface/PolyfaceBuilder.js +7 -0
  30. package/lib/cjs/polyface/PolyfaceBuilder.js.map +1 -1
  31. package/lib/cjs/serialization/GeometrySamples.d.ts +4 -5
  32. package/lib/cjs/serialization/GeometrySamples.d.ts.map +1 -1
  33. package/lib/cjs/serialization/GeometrySamples.js +12 -13
  34. package/lib/cjs/serialization/GeometrySamples.js.map +1 -1
  35. package/lib/cjs/solid/SweepContour.d.ts.map +1 -1
  36. package/lib/cjs/solid/SweepContour.js +7 -0
  37. package/lib/cjs/solid/SweepContour.js.map +1 -1
  38. package/lib/cjs/topology/HalfEdgeGraphSearch.d.ts +2 -1
  39. package/lib/cjs/topology/HalfEdgeGraphSearch.d.ts.map +1 -1
  40. package/lib/cjs/topology/HalfEdgeGraphSearch.js +1 -0
  41. package/lib/cjs/topology/HalfEdgeGraphSearch.js.map +1 -1
  42. package/lib/esm/Geometry.d.ts +28 -12
  43. package/lib/esm/Geometry.d.ts.map +1 -1
  44. package/lib/esm/Geometry.js +33 -17
  45. package/lib/esm/Geometry.js.map +1 -1
  46. package/lib/esm/curve/Loop.d.ts.map +1 -1
  47. package/lib/esm/curve/Loop.js +6 -0
  48. package/lib/esm/curve/Loop.js.map +1 -1
  49. package/lib/esm/curve/RegionOps.d.ts +8 -9
  50. package/lib/esm/curve/RegionOps.d.ts.map +1 -1
  51. package/lib/esm/curve/RegionOps.js +8 -9
  52. package/lib/esm/curve/RegionOps.js.map +1 -1
  53. package/lib/esm/curve/RegionOpsClassificationSweeps.d.ts.map +1 -1
  54. package/lib/esm/curve/RegionOpsClassificationSweeps.js +2 -0
  55. package/lib/esm/curve/RegionOpsClassificationSweeps.js.map +1 -1
  56. package/lib/esm/geometry3d/Matrix3d.d.ts +137 -89
  57. package/lib/esm/geometry3d/Matrix3d.d.ts.map +1 -1
  58. package/lib/esm/geometry3d/Matrix3d.js +244 -155
  59. package/lib/esm/geometry3d/Matrix3d.js.map +1 -1
  60. package/lib/esm/geometry3d/Point3dVector3d.d.ts +15 -15
  61. package/lib/esm/geometry3d/Point3dVector3d.d.ts.map +1 -1
  62. package/lib/esm/geometry3d/Point3dVector3d.js +16 -19
  63. package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
  64. package/lib/esm/geometry3d/PolygonOps.d.ts +7 -9
  65. package/lib/esm/geometry3d/PolygonOps.d.ts.map +1 -1
  66. package/lib/esm/geometry3d/PolygonOps.js +7 -9
  67. package/lib/esm/geometry3d/PolygonOps.js.map +1 -1
  68. package/lib/esm/polyface/PolyfaceBuilder.d.ts +3 -0
  69. package/lib/esm/polyface/PolyfaceBuilder.d.ts.map +1 -1
  70. package/lib/esm/polyface/PolyfaceBuilder.js +7 -0
  71. package/lib/esm/polyface/PolyfaceBuilder.js.map +1 -1
  72. package/lib/esm/serialization/GeometrySamples.d.ts +4 -5
  73. package/lib/esm/serialization/GeometrySamples.d.ts.map +1 -1
  74. package/lib/esm/serialization/GeometrySamples.js +12 -13
  75. package/lib/esm/serialization/GeometrySamples.js.map +1 -1
  76. package/lib/esm/solid/SweepContour.d.ts.map +1 -1
  77. package/lib/esm/solid/SweepContour.js +7 -0
  78. package/lib/esm/solid/SweepContour.js.map +1 -1
  79. package/lib/esm/topology/HalfEdgeGraphSearch.d.ts +2 -1
  80. package/lib/esm/topology/HalfEdgeGraphSearch.d.ts.map +1 -1
  81. package/lib/esm/topology/HalfEdgeGraphSearch.js +1 -0
  82. package/lib/esm/topology/HalfEdgeGraphSearch.js.map +1 -1
  83. package/package.json +3 -3
@@ -12,7 +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
+ // cSpell:words XXYZ YXYZ ZXYZ SaeedTorabi arctan newcommand
16
16
  /**
17
17
  * PackedMatrix3dOps contains static methods for matrix operations where the matrix is a Float64Array.
18
18
  * * The Float64Array contains the matrix entries in row-major order
@@ -308,7 +308,7 @@ export class Matrix3d {
308
308
  /**
309
309
  * Here we rotate this.columnX() around this.columnZ() by "angle" and expect to get other.columnX().
310
310
  * Then we rotate this.columnY() around this.columnZ() by the same "angle" and if we get other.columnY(),
311
- * that means this` and `other` have X and Y columns differing only by a rotation around that Z.
311
+ * that means `this` and `other` have X and Y columns differing only by a rotation around column Z.
312
312
  */
313
313
  let column = Vector3d.createRotateVectorAroundVector(columnX, columnZ, angle);
314
314
  if (other.isAlmostEqualColumnXYZ(0, column.x, column.y, column.z, tol)) {
@@ -757,14 +757,16 @@ export class Matrix3d {
757
757
  static createColumnsXYW(vectorU, u, vectorV, v, vectorW, w, result) {
758
758
  return Matrix3d.createRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, u, v, w, result);
759
759
  }
760
- /** Create a matrix from "as viewed" right and up vectors.
761
- * * ColumnX points in the rightVector direction
762
- * * ColumnY points in the upVector direction
760
+ /**
761
+ * Create a matrix from "as viewed" right and up vectors.
762
+ * * ColumnX points in the rightVector direction.
763
+ * * ColumnY points in the upVector direction.
763
764
  * * ColumnZ is a unit cross product of ColumnX and ColumnY.
764
765
  * * Optionally rotate by 45 degrees around `upVector` to bring its left or right vertical edge to center.
765
766
  * * Optionally rotate by arctan(1/sqrt(2)) ~ 35.264 degrees around `rightVector` to bring the top or bottom
766
767
  * horizontal edge of the view to the center (for isometric views).
767
- * * This is expected to be used with various principal unit vectors that are perpendicular to each other.
768
+ *
769
+ * This is expected to be used with various principal unit vectors that are perpendicular to each other.
768
770
  * * STANDARD TOP VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitY(), 0, 0)
769
771
  * * STANDARD FRONT VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitZ(), 0, 0)
770
772
  * * STANDARD BACK VIEW: createViewedAxes(Vector3d.unitX(-1), Vector3d.unitZ(), 0, 0)
@@ -1253,6 +1255,18 @@ export class Matrix3d {
1253
1255
  + this.coffs[3] * this.coffs[4]
1254
1256
  + this.coffs[6] * this.coffs[7];
1255
1257
  }
1258
+ /** Return the dot product of column X with column Z */
1259
+ columnXDotColumnZ() {
1260
+ return this.coffs[0] * this.coffs[2]
1261
+ + this.coffs[3] * this.coffs[5]
1262
+ + this.coffs[6] * this.coffs[8];
1263
+ }
1264
+ /** Return the dot product of column Y with column Z */
1265
+ columnYDotColumnZ() {
1266
+ return this.coffs[1] * this.coffs[2]
1267
+ + this.coffs[4] * this.coffs[5]
1268
+ + this.coffs[7] * this.coffs[8];
1269
+ }
1256
1270
  /**
1257
1271
  * Dot product of an indexed column with a vector given as x,y,z
1258
1272
  * @param columnIndex index of column. Must be 0,1,2.
@@ -1547,7 +1561,7 @@ export class Matrix3d {
1547
1561
  * \text{Treating U as a row to the left of untransposed matrix\: return row}&\rowSubXYZ{V}&=&\rowSubXYZ{U}\matrixXY{A}
1548
1562
  * \end{matrix}
1549
1563
  * ```
1550
- * @return the vector result (optional)
1564
+ * @param result the vector result (optional)
1551
1565
  */
1552
1566
  multiplyTransposeVector(vector, result) {
1553
1567
  result = result ? result : new Vector3d();
@@ -1561,7 +1575,7 @@ export class Matrix3d {
1561
1575
  }
1562
1576
  /**
1563
1577
  * 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)
1578
+ * @param result the vector result (optional)
1565
1579
  */
1566
1580
  multiplyXYZ(x, y, z, result) {
1567
1581
  result = result ? result : new Vector3d();
@@ -1586,7 +1600,7 @@ export class Matrix3d {
1586
1600
  }
1587
1601
  /**
1588
1602
  * 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)
1603
+ * @param result the vector result (optional)
1590
1604
  */
1591
1605
  multiplyXY(x, y, result) {
1592
1606
  result = result ? result : new Vector3d();
@@ -1597,7 +1611,7 @@ export class Matrix3d {
1597
1611
  }
1598
1612
  /**
1599
1613
  * Compute origin + the matrix * [x,y,0].
1600
- * @return the vector result (optional)
1614
+ * @param result the Point3d result (optional)
1601
1615
  */
1602
1616
  originPlusMatrixTimesXY(origin, x, y, result) {
1603
1617
  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);
@@ -1605,7 +1619,7 @@ export class Matrix3d {
1605
1619
  /**
1606
1620
  * Multiply the matrix * (x,y,z) in place, i.e. the vector (x,y,z) is a column vector on the right and
1607
1621
  * the multiplication updates the vector values.
1608
- * @param xyzData the vector data
1622
+ * @param xyzData the vector data.
1609
1623
  */
1610
1624
  multiplyVectorInPlace(xyzData) {
1611
1625
  const x = xyzData.x;
@@ -1619,7 +1633,7 @@ export class Matrix3d {
1619
1633
  * Multiply the transpose matrix times [x,y,z] in place, i.e. the vector [x,y,z] is a column vector on
1620
1634
  * the right and the multiplication updates the vector values.
1621
1635
  * * This is equivalent to `multiplyTransposeVector` but always returns the result directly in the input.
1622
- * @param xyzData the vector data
1636
+ * @param vectorU the vector data
1623
1637
  */
1624
1638
  multiplyTransposeVectorInPlace(vectorU) {
1625
1639
  const x = vectorU.x;
@@ -1640,7 +1654,7 @@ export class Matrix3d {
1640
1654
  * \phantom{8888}\text{and return V as a Vector3d} & & &
1641
1655
  * \end{matrix}
1642
1656
  * ````
1643
- * @return the vector result (optional)
1657
+ * @param result the vector result (optional)
1644
1658
  */
1645
1659
  multiplyTransposeXYZ(x, y, z, result) {
1646
1660
  result = result ? result : new Vector3d();
@@ -1651,8 +1665,8 @@ export class Matrix3d {
1651
1665
  }
1652
1666
  /**
1653
1667
  * 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)
1668
+ * * This is equivalent to multiplication `result = matrixInverse * vector`.
1669
+ * * Result is `undefined` if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
1656
1670
  */
1657
1671
  multiplyInverse(vector, result) {
1658
1672
  this.computeCachedInverse(true);
@@ -1667,7 +1681,7 @@ export class Matrix3d {
1667
1681
  /**
1668
1682
  * Solve `matrixTranspose * result = vector` for an unknown `result`.
1669
1683
  * * This is equivalent to multiplication `result = matrixInverseTranspose * vector`.
1670
- * * Result is undefined if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
1684
+ * * Result is `undefined` if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
1671
1685
  */
1672
1686
  multiplyInverseTranspose(vector, result) {
1673
1687
  this.computeCachedInverse(true);
@@ -1682,7 +1696,7 @@ export class Matrix3d {
1682
1696
  /**
1683
1697
  * Multiply `matrixInverse * [x,y,z]`.
1684
1698
  * * 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)
1699
+ * * Result is `undefined` if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
1686
1700
  * @return result as a Vector3d or undefined (if the matrix is singular).
1687
1701
  */
1688
1702
  multiplyInverseXYZAsVector3d(x, y, z, result) {
@@ -1695,7 +1709,7 @@ export class Matrix3d {
1695
1709
  /**
1696
1710
  * Multiply `matrixInverse * [x,y,z]` and return result as `Point4d` with given weight.
1697
1711
  * * 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)
1712
+ * * Result is `undefined` if the matrix is singular (e.g. has parallel columns or a zero magnitude column)
1699
1713
  * @return result as a Point4d with the same weight.
1700
1714
  */
1701
1715
  multiplyInverseXYZW(x, y, z, w, result) {
@@ -1708,7 +1722,7 @@ export class Matrix3d {
1708
1722
  /**
1709
1723
  * Multiply `matrixInverse * [x,y,z]` and return result as `Point3d`.
1710
1724
  * * 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).
1725
+ * @return result as a Point3d or `undefined` (if the matrix is singular).
1712
1726
  */
1713
1727
  multiplyInverseXYZAsPoint3d(x, y, z, result) {
1714
1728
  this.computeCachedInverse(true);
@@ -1719,7 +1733,7 @@ export class Matrix3d {
1719
1733
  }
1720
1734
  /**
1721
1735
  * 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
1736
+ * * If either input coffA or coffB is `undefined`, set state to `InverseMatrixState.unknown` but
1723
1737
  * leave the inverseCoffs untouched.
1724
1738
  * @param f the given matrix*matrix operation that is called by this function to compute the inverse.
1725
1739
  * `f` must be a matrix*matrix operation. Otherwise, the function does not generate the inverse properly.
@@ -1941,10 +1955,12 @@ export class Matrix3d {
1941
1955
  * * [A B C] ===> [A B AxB] ===> [A (AxB)xA AxB]
1942
1956
  *
1943
1957
  * This means that in the final matrix:
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).
1946
- * * column C is perpendicular to A and B of both the original and final.
1958
+ * * first column is same as original column A.
1959
+ * * second column is linear combination of original A and B (i.e., is in the plane of original A and B).
1960
+ * * third column is perpendicular to first and second columns of both the original and final.
1947
1961
  * * original column C is overwritten and does not participate in the result.
1962
+ *
1963
+ * The final matrix will have 3 orthogonal columns.
1948
1964
  */
1949
1965
  axisOrderCrossProductsInPlace(axisOrder) {
1950
1966
  switch (axisOrder) {
@@ -1982,35 +1998,35 @@ export class Matrix3d {
1982
1998
  }
1983
1999
  /**
1984
2000
  * 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
2001
+ * @param originalColumnMagnitudes optional vector to store original column magnitudes.
2002
+ * @returns return true if all columns have non-zero lengths. Otherwise, return false.
2003
+ * * If false is returned, the magnitudes are stored in the `originalColumnMagnitudes` vector but no columns
1988
2004
  * are altered.
1989
2005
  */
1990
- normalizeColumnsInPlace(originalRowMagnitudes) {
2006
+ normalizeColumnsInPlace(originalColumnMagnitudes) {
1991
2007
  const ax = this.columnXMagnitude();
1992
2008
  const ay = this.columnYMagnitude();
1993
2009
  const az = this.columnZMagnitude();
1994
- if (originalRowMagnitudes)
1995
- originalRowMagnitudes.set(ax, ay, az);
2010
+ if (originalColumnMagnitudes)
2011
+ originalColumnMagnitudes.set(ax, ay, az);
1996
2012
  if (Geometry.isSmallMetricDistance(ax) || Geometry.isSmallMetricDistance(ay) || Geometry.isSmallMetricDistance(az))
1997
2013
  return false;
1998
2014
  this.scaleColumns(1.0 / ax, 1.0 / ay, 1.0 / az, this);
1999
2015
  return true;
2000
2016
  }
2001
2017
  /**
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) {
2018
+ * Normalize each row in place.
2019
+ * @param originalRowMagnitudes optional vector to store original row magnitudes.
2020
+ * @returns return true if all rows have non-zero lengths. Otherwise, return false.
2021
+ * * If false is returned, the magnitudes are stored in the `originalRowMagnitudes` vector but no rows
2022
+ * are altered.
2023
+ */
2024
+ normalizeRowsInPlace(originalRowMagnitudes) {
2009
2025
  const ax = this.rowXMagnitude();
2010
2026
  const ay = this.rowYMagnitude();
2011
2027
  const az = this.rowZMagnitude();
2012
- if (originalColumnMagnitudes)
2013
- originalColumnMagnitudes.set(ax, ay, az);
2028
+ if (originalRowMagnitudes)
2029
+ originalRowMagnitudes.set(ax, ay, az);
2014
2030
  if (Geometry.isSmallMetricDistance(ax) || Geometry.isSmallMetricDistance(ay) || Geometry.isSmallMetricDistance(az))
2015
2031
  return false;
2016
2032
  this.scaleRows(1.0 / ax, 1.0 / ay, 1.0 / az, this);
@@ -2032,7 +2048,7 @@ export class Matrix3d {
2032
2048
  * Compute the inverse of `this` Matrix3d. The inverse is stored in `this.inverseCoffs` for later use.
2033
2049
  * @param useCacheIfAvailable if `true`, use the previously computed inverse if available. If `false`,
2034
2050
  * recompute the inverse.
2035
- * @returns Return `true` if the inverse is computed. Return `false` if matrix is singular.
2051
+ * @returns return `true` if the inverse is computed. Return `false` if matrix is singular.
2036
2052
  */
2037
2053
  computeCachedInverse(useCacheIfAvailable) {
2038
2054
  if (useCacheIfAvailable && Matrix3d.useCachedInverse && this.inverseState !== InverseMatrixState.unknown) {
@@ -2045,10 +2061,11 @@ export class Matrix3d {
2045
2061
  const inverseCoffs = this.inverseCoffs;
2046
2062
  /**
2047
2063
  * 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".
2064
+ * Math details can be found at docs/learning/matrix/Matrix.md
2065
+ * [ A ]
2066
+ * In summary, if M = [ B ] then inverse of M = (1/det)[BxC CxA AxB] where
2067
+ * [ C ]
2068
+ * det is the determinant of matrix M (which is equal to "A dot BxC").
2052
2069
  */
2053
2070
  Matrix3d.indexedRowCrossProduct(coffs, 3, 6, inverseCoffs, 0); // BxC
2054
2071
  Matrix3d.indexedRowCrossProduct(coffs, 6, 0, inverseCoffs, 1); // CxA
@@ -2090,19 +2107,30 @@ export class Matrix3d {
2090
2107
  this.coffs[Matrix3d.flatIndexOf(row, column)] = value;
2091
2108
  this.inverseState = InverseMatrixState.unknown;
2092
2109
  }
2093
- /** create a Matrix3d whose columns are scaled copies of this Matrix3d.
2094
- * @param scaleX scale factor for column x
2095
- * @param scaleY scale factor for column y
2096
- * @param scaleZ scale factor for column z
2097
- * @param result optional preallocated result.
2110
+ /**
2111
+ * Create a Matrix3d whose values are uniformly scaled from `this` Matrix3d.
2112
+ * @param scale scale factor to apply.
2113
+ * @param result optional result.
2114
+ * @returns return the scaled matrix.
2115
+ */
2116
+ scale(scale, result) {
2117
+ 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);
2118
+ }
2119
+ /**
2120
+ * Create a Matrix3d whose columns are scaled copies of `this` Matrix3d.
2121
+ * @param scaleX scale factor for column 0
2122
+ * @param scaleY scale factor for column 1
2123
+ * @param scaleZ scale factor for column 2
2124
+ * @param result optional result
2098
2125
  */
2099
2126
  scaleColumns(scaleX, scaleY, scaleZ, result) {
2100
2127
  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);
2101
2128
  }
2102
- /** Scale the columns of this Matrix3d.
2103
- * @param scaleX scale factor for column x
2104
- * @param scaleY scale factor for column y
2105
- * @param scaleZ scale factor for column z
2129
+ /**
2130
+ * Scale the columns of `this` Matrix3d in place.
2131
+ * @param scaleX scale factor for column 0
2132
+ * @param scaleY scale factor for column 1
2133
+ * @param scaleZ scale factor for column 2
2106
2134
  */
2107
2135
  scaleColumnsInPlace(scaleX, scaleY, scaleZ) {
2108
2136
  this.coffs[0] *= scaleX;
@@ -2134,18 +2162,55 @@ export class Matrix3d {
2134
2162
  this.inverseState = InverseMatrixState.singular;
2135
2163
  }
2136
2164
  }
2137
- /** create a Matrix3d whose rows are scaled copies of this Matrix3d.
2138
- * @param scaleX scale factor for row x
2139
- * @param scaleY scale factor for row y
2140
- * @param scaleZ scale factor for row z
2141
- * @param result optional preallocated result.
2165
+ /**
2166
+ * Create a Matrix3d whose rows are scaled copies of `this` Matrix3d.
2167
+ * @param scaleX scale factor for row 0
2168
+ * @param scaleY scale factor for row 1
2169
+ * @param scaleZ scale factor for row 2
2170
+ * @param result optional result
2142
2171
  */
2143
2172
  scaleRows(scaleX, scaleY, scaleZ, result) {
2144
2173
  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);
2145
2174
  }
2146
2175
  /**
2147
- * add scaled values from other Matrix3d to this Matrix3d
2148
- * @param other Matrix3d with values to be added
2176
+ * Scale the rows of `this` Matrix3d in place.
2177
+ * @param scaleX scale factor for row 0
2178
+ * @param scaleY scale factor for row 1
2179
+ * @param scaleZ scale factor for row 2
2180
+ */
2181
+ scaleRowsInPlace(scaleX, scaleY, scaleZ) {
2182
+ this.coffs[0] *= scaleX;
2183
+ this.coffs[1] *= scaleX;
2184
+ this.coffs[2] *= scaleX;
2185
+ this.coffs[3] *= scaleY;
2186
+ this.coffs[4] *= scaleY;
2187
+ this.coffs[5] *= scaleY;
2188
+ this.coffs[6] *= scaleZ;
2189
+ this.coffs[7] *= scaleZ;
2190
+ this.coffs[8] *= scaleZ;
2191
+ if (this.inverseState === InverseMatrixState.inverseStored && this.inverseCoffs !== undefined) {
2192
+ // apply reciprocal scales to the COLUMNs of the inverse
2193
+ const divX = Geometry.conditionalDivideFraction(1.0, scaleX);
2194
+ const divY = Geometry.conditionalDivideFraction(1.0, scaleY);
2195
+ const divZ = Geometry.conditionalDivideFraction(1.0, scaleZ);
2196
+ if (divX !== undefined && divY !== undefined && divZ !== undefined) {
2197
+ this.inverseCoffs[0] *= divX;
2198
+ this.inverseCoffs[1] *= divY;
2199
+ this.inverseCoffs[2] *= divZ;
2200
+ this.inverseCoffs[3] *= divX;
2201
+ this.inverseCoffs[4] *= divY;
2202
+ this.inverseCoffs[5] *= divZ;
2203
+ this.inverseCoffs[6] *= divX;
2204
+ this.inverseCoffs[7] *= divY;
2205
+ this.inverseCoffs[8] *= divZ;
2206
+ }
2207
+ else
2208
+ this.inverseState = InverseMatrixState.singular;
2209
+ }
2210
+ }
2211
+ /**
2212
+ * Add scaled values from `other` Matrix3d to `this` Matrix3d.
2213
+ * @param other Matrix3d with values to be added.
2149
2214
  * @param scale scale factor to apply to the added values.
2150
2215
  */
2151
2216
  addScaledInPlace(other, scale) {
@@ -2154,18 +2219,19 @@ export class Matrix3d {
2154
2219
  this.inverseState = InverseMatrixState.unknown;
2155
2220
  }
2156
2221
  /**
2157
- * add scaled values from an outer product.
2158
- * * The scaled outer product is a "rank 1" matrix.
2222
+ * Add scaled values from an outer product of vectors U and V.
2223
+ * * The scaled outer product is a matrix with `rank 1` (all columns/rows are linearly dependent).
2159
2224
  * * This is useful in constructing mirrors and directional scales.
2160
2225
  * ```
2161
2226
  * equation
2162
2227
  * A += s \columnSubXYZ{U}\rowSubXYZ{V}
2163
2228
  * \\ \matrixXY{A} += s \begin{bmatrix}
2164
- * U_x * V_x & U_y * V_x & U_z * V_x \\
2165
- * U_x * V_y & U_y * V_y & U_z * V_y \\
2166
- * U_x * V_z & U_y * V_z & U_z * V_z \end{bmatrix}
2229
+ * U_x * V_x & U_x * V_y & U_x * V_z \\
2230
+ * U_y * V_x & U_y * V_y & U_y * V_z \\
2231
+ * U_z * V_x & U_z * V_y & U_z * V_z \end{bmatrix}
2167
2232
  * ```
2168
- * @param other Matrix3d with values to be added
2233
+ * @param vectorU first vector in the outer product.
2234
+ * @param vectorV second vector in the outer product.
2169
2235
  * @param scale scale factor to apply to the added values.
2170
2236
  */
2171
2237
  addScaledOuterProductInPlace(vectorU, vectorV, scale) {
@@ -2180,14 +2246,6 @@ export class Matrix3d {
2180
2246
  this.coffs[8] += scale * vectorU.z * vectorV.z;
2181
2247
  this.inverseState = InverseMatrixState.unknown;
2182
2248
  }
2183
- /** create a Matrix3d whose values are uniformly scaled from this.
2184
- * @param scale scale factor to apply.
2185
- * @param result optional preallocated result.
2186
- * @returns Return the new or repopulated matrix
2187
- */
2188
- scale(scale, result) {
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);
2190
- }
2191
2249
  /**
2192
2250
  * Create a rigid matrix (columns and rows are unit length and pairwise perpendicular) for
2193
2251
  * the given eye coordinate.
@@ -2210,7 +2268,6 @@ export class Matrix3d {
2210
2268
  result.scaleColumnsInPlace(1.0, -1.0, -1.0);
2211
2269
  }
2212
2270
  else {
2213
- // if coordinate is (x,y,0), i.e., Front or Back or Left or Right view
2214
2271
  /**
2215
2272
  * The matrix that the "else" statement creates is
2216
2273
  * [-s -s1*c c1*c]
@@ -2240,57 +2297,56 @@ export class Matrix3d {
2240
2297
  }
2241
2298
  return result;
2242
2299
  }
2243
- /** Return the determinant of this matrix. */
2300
+ /** Return the determinant of `this` matrix. */
2244
2301
  determinant() {
2245
2302
  return this.coffs[0] * this.coffs[4] * this.coffs[8]
2246
- - this.coffs[0] * this.coffs[7] * this.coffs[5]
2247
- + this.coffs[3] * this.coffs[7] * this.coffs[2]
2248
- - this.coffs[3] * this.coffs[1] * this.coffs[8]
2249
- + this.coffs[6] * this.coffs[1] * this.coffs[5]
2250
- - this.coffs[6] * this.coffs[4] * this.coffs[2];
2303
+ - this.coffs[0] * this.coffs[5] * this.coffs[7]
2304
+ - this.coffs[1] * this.coffs[3] * this.coffs[8]
2305
+ + this.coffs[1] * this.coffs[5] * this.coffs[6]
2306
+ + this.coffs[2] * this.coffs[3] * this.coffs[7]
2307
+ - this.coffs[2] * this.coffs[4] * this.coffs[6];
2251
2308
  }
2252
- /** Return an estimate of how independent the columns are. Near zero is bad. Near 1 is good
2309
+ /**
2310
+ * Return an estimate of how independent the columns of `this` matrix are. Near zero is bad (i.e.,
2311
+ * columns are almost dependent and matrix is nearly singular). Near 1 is good (i.e., columns are
2312
+ * almost independent and matrix is invertible).
2253
2313
  */
2254
2314
  conditionNumber() {
2255
2315
  const determinant = this.determinant();
2256
- const columnMagnitudeProduct = Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[3], this.coffs[6])
2316
+ const columnMagnitudeSum = Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[3], this.coffs[6])
2257
2317
  + Geometry.hypotenuseXYZ(this.coffs[1], this.coffs[4], this.coffs[7])
2258
2318
  + Geometry.hypotenuseXYZ(this.coffs[2], this.coffs[5], this.coffs[8]);
2259
- return Geometry.safeDivideFraction(determinant, columnMagnitudeProduct, 0.0);
2319
+ return Geometry.safeDivideFraction(determinant, columnMagnitudeSum, 0.0);
2260
2320
  }
2261
2321
  /** Return the sum of squares of all entries */
2262
2322
  sumSquares() {
2263
- let i = 0;
2264
2323
  let sum = 0;
2265
- for (i = 0; i < 9; i++)
2324
+ for (let i = 0; i < 9; i++)
2266
2325
  sum += this.coffs[i] * this.coffs[i];
2267
2326
  return sum;
2268
2327
  }
2269
2328
  /** Return the sum of squares of diagonal entries */
2270
2329
  sumDiagonalSquares() {
2271
- let i = 0;
2272
2330
  let sum = 0;
2273
- for (i = 0; i < 9; i += 4)
2331
+ for (let i = 0; i < 9; i += 4)
2274
2332
  sum += this.coffs[i] * this.coffs[i];
2275
2333
  return sum;
2276
2334
  }
2277
- /** Return the sum of diagonal entries (also known as the trace) */
2335
+ /** Return the matrix `trace` (sum of diagonal entries) */
2278
2336
  sumDiagonal() {
2279
2337
  return this.coffs[0] + this.coffs[4] + this.coffs[8];
2280
2338
  }
2281
2339
  /** Return the Maximum absolute value of any single entry */
2282
2340
  maxAbs() {
2283
- let i = 0;
2284
2341
  let max = 0;
2285
- for (i = 0; i < 9; i++)
2342
+ for (let i = 0; i < 9; i++)
2286
2343
  max = Math.max(max, Math.abs(this.coffs[i]));
2287
2344
  return max;
2288
2345
  }
2289
2346
  /** Return the maximum absolute difference between corresponding entries of `this` and `other` */
2290
2347
  maxDiff(other) {
2291
- let i = 0;
2292
2348
  let max = 0;
2293
- for (i = 0; i < 9; i++)
2349
+ for (let i = 0; i < 9; i++)
2294
2350
  max = Math.max(max, Math.abs(this.coffs[i] - other.coffs[i]));
2295
2351
  return max;
2296
2352
  }
@@ -2309,86 +2365,102 @@ export class Matrix3d {
2309
2365
  get hasCachedInverse() {
2310
2366
  return this.inverseState === InverseMatrixState.inverseStored && this.inverseCoffs !== undefined;
2311
2367
  }
2312
- /** Test if the below diagonal entries are all nearly zero */
2368
+ /** Test if the below diagonal entries (3,6,7) are all nearly zero */
2313
2369
  get isUpperTriangular() {
2314
2370
  const sumAll = this.sumSquares();
2315
2371
  const sumLow = Geometry.hypotenuseSquaredXYZ(this.coffs[3], this.coffs[6], this.coffs[7]);
2316
2372
  return Math.sqrt(sumLow) <= Geometry.smallAngleRadians * (1.0 + Math.sqrt(sumAll));
2317
2373
  }
2318
- /** If the matrix is diagonal and all diagonals are within tolerance, return the first diagonal. Otherwise return undefined.
2374
+ /** Test if the above diagonal entries (1,2,5) are all nearly zero */
2375
+ get isLowerTriangular() {
2376
+ const sumAll = this.sumSquares();
2377
+ const sumLow = Geometry.hypotenuseSquaredXYZ(this.coffs[1], this.coffs[2], this.coffs[75]);
2378
+ return Math.sqrt(sumLow) <= Geometry.smallAngleRadians * (1.0 + Math.sqrt(sumAll));
2379
+ }
2380
+ /**
2381
+ * If the matrix is diagonal and all diagonals are almost equal, return the first diagonal (entry 0
2382
+ * which is same as entry 4 and 8). Otherwise return `undefined`.
2319
2383
  */
2320
2384
  sameDiagonalScale() {
2321
2385
  const sumAll = this.sumSquares();
2322
2386
  const sumDiagonal = this.sumDiagonalSquares();
2323
2387
  const sumOff = Math.abs(sumAll - sumDiagonal);
2324
2388
  if (Math.sqrt(sumOff) <= Geometry.smallAngleRadians * (1.0 + Math.sqrt(sumAll))
2325
- && Geometry.isSameCoordinate(this.coffs[0], this.coffs[4]) && Geometry.isSameCoordinate(this.coffs[0], this.coffs[8]))
2389
+ && Geometry.isSameCoordinate(this.coffs[0], this.coffs[4])
2390
+ && Geometry.isSameCoordinate(this.coffs[0], this.coffs[8]))
2326
2391
  return this.coffs[0];
2327
2392
  return undefined;
2328
2393
  }
2329
- /** Sum of squared differences between symmetric pairs */
2394
+ /** Sum of squared differences between symmetric pairs (entry 1 and 3 - 2 and 6 - 5 and 7) */
2330
2395
  sumSkewSquares() {
2331
2396
  return Geometry.hypotenuseSquaredXYZ(this.coffs[1] - this.coffs[3], this.coffs[2] - this.coffs[6], this.coffs[5] - this.coffs[7]);
2332
2397
  }
2333
- /** Test if the matrix is a pure rotation.
2334
- * @param allowMirror whether to widen the test to return true if the matrix is orthogonal (a pure rotation or a mirror)
2398
+ /**
2399
+ * Test if all rows and columns are unit length and are perpendicular to each other, i.e., the matrix is either
2400
+ * a `pure rotation` (determinant is +1) or is a `mirror` (determinant is -1).
2401
+ * * **Note:** such a matrix is called `orthogonal` and its inverse is its transpose.
2402
+ */
2403
+ testPerpendicularUnitRowsAndColumns() {
2404
+ const product = this.multiplyMatrixMatrixTranspose(this);
2405
+ return product.isIdentity;
2406
+ }
2407
+ /**
2408
+ * Test if the matrix is a `rigid` matrix (or `pure rotation`, i.e., columns and rows are unit length and
2409
+ * pairwise perpendicular and determinant is +1).
2410
+ * @param allowMirror whether to widen the test to return true if the matrix is a `mirror` (determinant is -1).
2335
2411
  */
2336
2412
  isRigid(allowMirror = false) {
2337
2413
  return this.testPerpendicularUnitRowsAndColumns() && (allowMirror || this.determinant() > 0);
2338
2414
  }
2339
- /** Test if all rows and columns are perpendicular to each other and have equal length.
2340
- * If so, the length (or its negative) is the scale factor from a set of rigid axes to these axes.
2341
- * * result.rigidAxes is the rigid axes (with the scale factor removed)
2342
- * * result.scale is the scale factor
2415
+ /**
2416
+ * Test if all rows and columns are perpendicular to each other and have equal length.
2417
+ * If so, the length (or its negative) is the `scale` factor from a set of `orthonormal axes` to
2418
+ * the set of axes created by columns of `this` matrix. Otherwise, returns `undefined`.
2419
+ * @returns returns `{ rigidAxes, scale }` where `rigidAxes` is a Matrix3d with its columns as the rigid axes
2420
+ * (with the scale factor removed) and `scale` is the scale factor.
2421
+ * * Note that determinant of a rigid matrix is +1.
2422
+ * * The context for this method is to determine if the matrix is the product a `rotation` matrix and a uniform
2423
+ * `scale` matrix (diagonal matrix with all diagonal entries the same nonzero number).
2343
2424
  */
2344
2425
  factorRigidWithSignedScale() {
2345
2426
  const product = this.multiplyMatrixMatrixTranspose(this);
2346
- const ss = product.sameDiagonalScale();
2347
- if (ss === undefined || ss <= 0.0)
2427
+ const scaleSquare = product.sameDiagonalScale();
2428
+ if (scaleSquare === undefined || scaleSquare <= 0.0)
2348
2429
  return undefined;
2349
- const s = this.determinant() > 0 ? Math.sqrt(ss) : -Math.sqrt(ss);
2350
- const divS = 1.0 / s;
2351
- const result = { rigidAxes: this.scaleColumns(divS, divS, divS), scale: s };
2430
+ const scale = this.determinant() > 0 ? Math.sqrt(scaleSquare) : -Math.sqrt(scaleSquare);
2431
+ const scaleInverse = 1.0 / scale;
2432
+ const result = { rigidAxes: this.scaleColumns(scaleInverse, scaleInverse, scaleInverse), scale };
2352
2433
  return result;
2353
2434
  }
2354
- /** Test if the matrix is shuffles and negates columns. */
2435
+ /** Test if `this` matrix reorders and/or negates the columns of the `identity` matrix. */
2355
2436
  get isSignedPermutation() {
2356
2437
  let count = 0;
2357
2438
  for (let row = 0; row < 3; row++)
2358
2439
  for (let col = 0; col < 3; col++) {
2359
2440
  const q = this.at(row, col);
2360
- if (q === 0) { // This comment makes the block non-empty
2441
+ if (q === 0) {
2442
+ // do nothing
2361
2443
  }
2362
2444
  else if (q === 1 || q === -1) {
2363
- // the rest of this row and column should be 0.
2364
- // "at" will apply cyclic indexing.
2365
2445
  count++;
2366
- if (this.at(row + 1, col) !== 0)
2367
- return false;
2368
- if (this.at(row + 2, col) !== 0)
2369
- return false;
2370
- if (this.at(row, col + 1) !== 0)
2371
- return false;
2372
- if (this.at(row, col + 2) !== 0)
2446
+ // if the rest of this row and column should be 0 ("at" will apply cyclic indexing)
2447
+ if ((this.at(row + 1, col) !== 0) || (this.at(row + 2, col) !== 0) ||
2448
+ (this.at(row, col + 1) !== 0) || (this.at(row, col + 2) !== 0))
2373
2449
  return false;
2374
2450
  }
2375
- else { // entry is not from 0,1,-1 . . .
2451
+ else { // entry is not 0, 1, or -1
2376
2452
  return false;
2377
2453
  }
2378
2454
  }
2379
2455
  return count === 3;
2380
2456
  }
2381
- /** Test if all rows and columns are length 1 and are perpendicular to each other. (I.e. the matrix is either a pure rotation with uniform scale factor of 1 or -1) */
2382
- testPerpendicularUnitRowsAndColumns() {
2383
- const product = this.multiplyMatrixMatrixTranspose(this);
2384
- return product.isIdentity;
2385
- }
2386
- /** Adjust the matrix in place so that:
2387
- * * columns are perpendicular and have unit length
2388
- * * transpose equals inverse
2389
- * * mirroring is removed
2457
+ /**
2458
+ * Adjust the matrix in place to make is a `rigid` matrix so that:
2459
+ * * columns are perpendicular and have unit length.
2460
+ * * transpose equals inverse.
2461
+ * * mirroring is removed.
2390
2462
  * @param axisOrder how to reorder the matrix columns
2391
- * @return whether the instance is rigid on return
2463
+ * @return whether the adjusted matrix is `rigid` on return
2392
2464
  */
2393
2465
  makeRigid(axisOrder = AxisOrder.XYZ) {
2394
2466
  const maxAbs = this.maxAbs();
@@ -2409,22 +2481,13 @@ export class Matrix3d {
2409
2481
  return result;
2410
2482
  return undefined;
2411
2483
  }
2412
- static computeQuatTerm(numerator, denomCoff, reciprocal, diagSum) {
2413
- let coff;
2414
- const diagTol = 0.500;
2415
- if (diagSum > diagTol) {
2416
- coff = Math.sqrt(diagSum) * 0.5;
2417
- if (denomCoff * numerator < 0.0)
2418
- coff = -coff;
2419
- }
2420
- else {
2421
- coff = numerator * reciprocal;
2422
- }
2423
- return coff;
2424
- }
2425
- /** create a matrix from a quaternion.
2426
- * **WARNING:** There is frequent confusion over whether a "from quaternion" matrix is organized by rows and columns.
2427
- * **WARNING:** If you find that the matrix seems to rotate by the opposite angle expect it, transpose it.
2484
+ /**
2485
+ * Create a matrix from a quaternion.
2486
+ * **WARNING:** There is frequent confusion over whether a "from quaternion" matrix is organized by
2487
+ * rows or columns. If you find that the matrix seems to rotate by the opposite angle, transpose it.
2488
+ *
2489
+ * Some math details can be found at
2490
+ * http://marc-b-reynolds.github.io/quaternions/2017/08/08/QuatRotMatrix.html
2428
2491
  */
2429
2492
  static createFromQuaternion(quat) {
2430
2493
  const qqx = quat.x * quat.x;
@@ -2437,31 +2500,57 @@ export class Matrix3d {
2437
2500
  }
2438
2501
  else {
2439
2502
  const a = 1.0 / mag2;
2440
- const matrix = Matrix3d.createRowValues(a * (qqw + qqx - qqy - qqz), 2.0 * a * (quat.w * quat.z + quat.x * quat.y), 2.0 * a * (quat.x * quat.z - quat.w * quat.y), 2.0 * a * (quat.x * quat.y - quat.w * quat.z), a * (qqw - qqx + qqy - qqz), 2.0 * a * (quat.w * quat.x + quat.y * quat.z), 2.0 * a * (quat.x * quat.z + quat.w * quat.y), 2.0 * a * (quat.y * quat.z - quat.w * quat.x), a * (qqw - qqx - qqy + qqz));
2503
+ const matrix = Matrix3d.createRowValues(
2504
+ // first row
2505
+ a * (qqw + qqx - qqy - qqz), 2.0 * a * (quat.w * quat.z + quat.x * quat.y), 2.0 * a * (quat.x * quat.z - quat.w * quat.y),
2506
+ // second row
2507
+ 2.0 * a * (quat.x * quat.y - quat.w * quat.z), a * (qqw - qqx + qqy - qqz), 2.0 * a * (quat.w * quat.x + quat.y * quat.z),
2508
+ // third row
2509
+ 2.0 * a * (quat.x * quat.z + quat.w * quat.y), 2.0 * a * (quat.y * quat.z - quat.w * quat.x), a * (qqw - qqx - qqy + qqz));
2441
2510
  return matrix;
2442
2511
  }
2443
2512
  }
2444
- /** convert the matrix to a quaternion.
2445
- * @note This calculation requires the matrix to have unit length rows and columns.
2446
- * **WARNING:** There is frequent confusion over whether a "from quaternion" matrix is organized by rows and columns.
2447
- * **WARNING:** If you find that the matrix seems to rotate by the opposite angle expect it, transpose it.
2513
+ /** Calculate quaternion terms used to convert matrix to a quaternion */
2514
+ static computeQuatTerm(numerator, denomCoff, reciprocal, diagSum) {
2515
+ let coff;
2516
+ const diagTol = 0.500;
2517
+ if (diagSum > diagTol) {
2518
+ coff = 0.5 * Math.sqrt(diagSum);
2519
+ if (denomCoff * numerator < 0.0)
2520
+ coff = -coff;
2521
+ }
2522
+ else {
2523
+ coff = numerator * reciprocal;
2524
+ }
2525
+ return coff;
2526
+ }
2527
+ /**
2528
+ * Create `this` matrix to a quaternion.
2529
+ * **Note:** This calculation requires `this` matrix to have unit length rows and columns.
2530
+ * **WARNING:** There is frequent confusion over whether a "from quaternion" matrix is organized by
2531
+ * rows or columns. If you find that the matrix seems to rotate by the opposite angle, transpose it.
2532
+ *
2533
+ * Some math details can be found at
2534
+ * http://marc-b-reynolds.github.io/quaternions/2017/08/08/QuatRotMatrix.html
2448
2535
  */
2449
2536
  toQuaternion() {
2450
2537
  const result = Point4d.createZero();
2451
- const props = [[this.coffs[0], this.coffs[3], this.coffs[6]],
2538
+ const props = [
2539
+ [this.coffs[0], this.coffs[3], this.coffs[6]],
2452
2540
  [this.coffs[1], this.coffs[4], this.coffs[7]],
2453
- [this.coffs[2], this.coffs[5], this.coffs[8]]];
2541
+ [this.coffs[2], this.coffs[5], this.coffs[8]],
2542
+ ];
2454
2543
  const xx = props[0][0];
2455
2544
  const yy = props[1][1];
2456
2545
  const zz = props[2][2];
2457
2546
  const dSum = [];
2458
- let denom, maxIndex, i;
2459
2547
  dSum[0] = 1.0 + xx - yy - zz;
2460
2548
  dSum[1] = 1.0 - xx + yy - zz;
2461
2549
  dSum[2] = 1.0 - xx - yy + zz;
2462
2550
  dSum[3] = 1.0 + xx + yy + zz;
2463
- maxIndex = 0;
2464
- for (i = 1; i < 4; i++) {
2551
+ let denom;
2552
+ let maxIndex = 0;
2553
+ for (let i = 1; i <= 3; i++) {
2465
2554
  if (dSum[i] > dSum[maxIndex])
2466
2555
  maxIndex = i;
2467
2556
  }