@itwin/core-geometry 3.6.0-dev.8 → 4.0.0-dev.10

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 (171) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/LICENSE.md +1 -1
  3. package/lib/cjs/Geometry.d.ts +28 -17
  4. package/lib/cjs/Geometry.d.ts.map +1 -1
  5. package/lib/cjs/Geometry.js +52 -25
  6. package/lib/cjs/Geometry.js.map +1 -1
  7. package/lib/cjs/curve/CurveChainWithDistanceIndex.d.ts +10 -6
  8. package/lib/cjs/curve/CurveChainWithDistanceIndex.d.ts.map +1 -1
  9. package/lib/cjs/curve/CurveChainWithDistanceIndex.js +29 -31
  10. package/lib/cjs/curve/CurveChainWithDistanceIndex.js.map +1 -1
  11. package/lib/cjs/curve/CurveCurve.d.ts +11 -8
  12. package/lib/cjs/curve/CurveCurve.d.ts.map +1 -1
  13. package/lib/cjs/curve/CurveCurve.js +16 -12
  14. package/lib/cjs/curve/CurveCurve.js.map +1 -1
  15. package/lib/cjs/curve/CurveCurveIntersectXY.d.ts +5 -1
  16. package/lib/cjs/curve/CurveCurveIntersectXY.d.ts.map +1 -1
  17. package/lib/cjs/curve/CurveCurveIntersectXY.js +11 -10
  18. package/lib/cjs/curve/CurveCurveIntersectXY.js.map +1 -1
  19. package/lib/cjs/geometry3d/Angle.d.ts +19 -0
  20. package/lib/cjs/geometry3d/Angle.d.ts.map +1 -1
  21. package/lib/cjs/geometry3d/Angle.js +39 -0
  22. package/lib/cjs/geometry3d/Angle.js.map +1 -1
  23. package/lib/cjs/geometry3d/AngleSweep.d.ts +1 -0
  24. package/lib/cjs/geometry3d/AngleSweep.d.ts.map +1 -1
  25. package/lib/cjs/geometry3d/AngleSweep.js +1 -0
  26. package/lib/cjs/geometry3d/AngleSweep.js.map +1 -1
  27. package/lib/cjs/geometry3d/CoincidentGeometryOps.d.ts +1 -0
  28. package/lib/cjs/geometry3d/CoincidentGeometryOps.d.ts.map +1 -1
  29. package/lib/cjs/geometry3d/CoincidentGeometryOps.js +3 -0
  30. package/lib/cjs/geometry3d/CoincidentGeometryOps.js.map +1 -1
  31. package/lib/cjs/geometry3d/GrowableXYArray.d.ts +1 -1
  32. package/lib/cjs/geometry3d/GrowableXYArray.js +1 -1
  33. package/lib/cjs/geometry3d/GrowableXYArray.js.map +1 -1
  34. package/lib/cjs/geometry3d/Matrix3d.d.ts +369 -254
  35. package/lib/cjs/geometry3d/Matrix3d.d.ts.map +1 -1
  36. package/lib/cjs/geometry3d/Matrix3d.js +807 -660
  37. package/lib/cjs/geometry3d/Matrix3d.js.map +1 -1
  38. package/lib/cjs/geometry3d/OrderedRotationAngles.d.ts +71 -14
  39. package/lib/cjs/geometry3d/OrderedRotationAngles.d.ts.map +1 -1
  40. package/lib/cjs/geometry3d/OrderedRotationAngles.js +204 -114
  41. package/lib/cjs/geometry3d/OrderedRotationAngles.js.map +1 -1
  42. package/lib/cjs/geometry3d/Point2dVector2d.d.ts +1 -0
  43. package/lib/cjs/geometry3d/Point2dVector2d.d.ts.map +1 -1
  44. package/lib/cjs/geometry3d/Point2dVector2d.js +1 -0
  45. package/lib/cjs/geometry3d/Point2dVector2d.js.map +1 -1
  46. package/lib/cjs/geometry3d/Point3dVector3d.d.ts +16 -16
  47. package/lib/cjs/geometry3d/Point3dVector3d.d.ts.map +1 -1
  48. package/lib/cjs/geometry3d/Point3dVector3d.js +21 -18
  49. package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
  50. package/lib/cjs/geometry3d/Segment1d.d.ts +1 -1
  51. package/lib/cjs/geometry3d/Segment1d.js +1 -1
  52. package/lib/cjs/geometry3d/Segment1d.js.map +1 -1
  53. package/lib/cjs/geometry3d/YawPitchRollAngles.d.ts +49 -25
  54. package/lib/cjs/geometry3d/YawPitchRollAngles.d.ts.map +1 -1
  55. package/lib/cjs/geometry3d/YawPitchRollAngles.js +146 -50
  56. package/lib/cjs/geometry3d/YawPitchRollAngles.js.map +1 -1
  57. package/lib/cjs/numerics/Polynomials.d.ts +12 -0
  58. package/lib/cjs/numerics/Polynomials.d.ts.map +1 -1
  59. package/lib/cjs/numerics/Polynomials.js +14 -0
  60. package/lib/cjs/numerics/Polynomials.js.map +1 -1
  61. package/lib/cjs/polyface/PolyfaceBuilder.d.ts +5 -4
  62. package/lib/cjs/polyface/PolyfaceBuilder.d.ts.map +1 -1
  63. package/lib/cjs/polyface/PolyfaceBuilder.js +50 -10
  64. package/lib/cjs/polyface/PolyfaceBuilder.js.map +1 -1
  65. package/lib/cjs/polyface/PolyfaceQuery.d.ts +82 -0
  66. package/lib/cjs/polyface/PolyfaceQuery.d.ts.map +1 -1
  67. package/lib/cjs/polyface/PolyfaceQuery.js +150 -1
  68. package/lib/cjs/polyface/PolyfaceQuery.js.map +1 -1
  69. package/lib/cjs/polyface/multiclip/OffsetMeshContext.d.ts +202 -0
  70. package/lib/cjs/polyface/multiclip/OffsetMeshContext.d.ts.map +1 -0
  71. package/lib/cjs/polyface/multiclip/OffsetMeshContext.js +1038 -0
  72. package/lib/cjs/polyface/multiclip/OffsetMeshContext.js.map +1 -0
  73. package/lib/cjs/serialization/GeometrySamples.d.ts +4 -1
  74. package/lib/cjs/serialization/GeometrySamples.d.ts.map +1 -1
  75. package/lib/cjs/serialization/GeometrySamples.js +14 -6
  76. package/lib/cjs/serialization/GeometrySamples.js.map +1 -1
  77. package/lib/cjs/serialization/IModelJsonSchema.d.ts +1 -1
  78. package/lib/cjs/serialization/IModelJsonSchema.js.map +1 -1
  79. package/lib/cjs/topology/Graph.d.ts +113 -7
  80. package/lib/cjs/topology/Graph.d.ts.map +1 -1
  81. package/lib/cjs/topology/Graph.js +185 -7
  82. package/lib/cjs/topology/Graph.js.map +1 -1
  83. package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts +38 -0
  84. package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts.map +1 -0
  85. package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.js +82 -0
  86. package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.js.map +1 -0
  87. package/lib/esm/Geometry.d.ts +28 -17
  88. package/lib/esm/Geometry.d.ts.map +1 -1
  89. package/lib/esm/Geometry.js +52 -25
  90. package/lib/esm/Geometry.js.map +1 -1
  91. package/lib/esm/curve/CurveChainWithDistanceIndex.d.ts +10 -6
  92. package/lib/esm/curve/CurveChainWithDistanceIndex.d.ts.map +1 -1
  93. package/lib/esm/curve/CurveChainWithDistanceIndex.js +30 -32
  94. package/lib/esm/curve/CurveChainWithDistanceIndex.js.map +1 -1
  95. package/lib/esm/curve/CurveCurve.d.ts +11 -8
  96. package/lib/esm/curve/CurveCurve.d.ts.map +1 -1
  97. package/lib/esm/curve/CurveCurve.js +16 -12
  98. package/lib/esm/curve/CurveCurve.js.map +1 -1
  99. package/lib/esm/curve/CurveCurveIntersectXY.d.ts +5 -1
  100. package/lib/esm/curve/CurveCurveIntersectXY.d.ts.map +1 -1
  101. package/lib/esm/curve/CurveCurveIntersectXY.js +11 -10
  102. package/lib/esm/curve/CurveCurveIntersectXY.js.map +1 -1
  103. package/lib/esm/geometry3d/Angle.d.ts +19 -0
  104. package/lib/esm/geometry3d/Angle.d.ts.map +1 -1
  105. package/lib/esm/geometry3d/Angle.js +39 -0
  106. package/lib/esm/geometry3d/Angle.js.map +1 -1
  107. package/lib/esm/geometry3d/AngleSweep.d.ts +1 -0
  108. package/lib/esm/geometry3d/AngleSweep.d.ts.map +1 -1
  109. package/lib/esm/geometry3d/AngleSweep.js +1 -0
  110. package/lib/esm/geometry3d/AngleSweep.js.map +1 -1
  111. package/lib/esm/geometry3d/CoincidentGeometryOps.d.ts +1 -0
  112. package/lib/esm/geometry3d/CoincidentGeometryOps.d.ts.map +1 -1
  113. package/lib/esm/geometry3d/CoincidentGeometryOps.js +3 -0
  114. package/lib/esm/geometry3d/CoincidentGeometryOps.js.map +1 -1
  115. package/lib/esm/geometry3d/GrowableXYArray.d.ts +1 -1
  116. package/lib/esm/geometry3d/GrowableXYArray.js +1 -1
  117. package/lib/esm/geometry3d/GrowableXYArray.js.map +1 -1
  118. package/lib/esm/geometry3d/Matrix3d.d.ts +369 -254
  119. package/lib/esm/geometry3d/Matrix3d.d.ts.map +1 -1
  120. package/lib/esm/geometry3d/Matrix3d.js +807 -660
  121. package/lib/esm/geometry3d/Matrix3d.js.map +1 -1
  122. package/lib/esm/geometry3d/OrderedRotationAngles.d.ts +71 -14
  123. package/lib/esm/geometry3d/OrderedRotationAngles.d.ts.map +1 -1
  124. package/lib/esm/geometry3d/OrderedRotationAngles.js +205 -115
  125. package/lib/esm/geometry3d/OrderedRotationAngles.js.map +1 -1
  126. package/lib/esm/geometry3d/Point2dVector2d.d.ts +1 -0
  127. package/lib/esm/geometry3d/Point2dVector2d.d.ts.map +1 -1
  128. package/lib/esm/geometry3d/Point2dVector2d.js +1 -0
  129. package/lib/esm/geometry3d/Point2dVector2d.js.map +1 -1
  130. package/lib/esm/geometry3d/Point3dVector3d.d.ts +16 -16
  131. package/lib/esm/geometry3d/Point3dVector3d.d.ts.map +1 -1
  132. package/lib/esm/geometry3d/Point3dVector3d.js +21 -18
  133. package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
  134. package/lib/esm/geometry3d/Segment1d.d.ts +1 -1
  135. package/lib/esm/geometry3d/Segment1d.js +1 -1
  136. package/lib/esm/geometry3d/Segment1d.js.map +1 -1
  137. package/lib/esm/geometry3d/YawPitchRollAngles.d.ts +49 -25
  138. package/lib/esm/geometry3d/YawPitchRollAngles.d.ts.map +1 -1
  139. package/lib/esm/geometry3d/YawPitchRollAngles.js +146 -50
  140. package/lib/esm/geometry3d/YawPitchRollAngles.js.map +1 -1
  141. package/lib/esm/numerics/Polynomials.d.ts +12 -0
  142. package/lib/esm/numerics/Polynomials.d.ts.map +1 -1
  143. package/lib/esm/numerics/Polynomials.js +14 -0
  144. package/lib/esm/numerics/Polynomials.js.map +1 -1
  145. package/lib/esm/polyface/PolyfaceBuilder.d.ts +5 -4
  146. package/lib/esm/polyface/PolyfaceBuilder.d.ts.map +1 -1
  147. package/lib/esm/polyface/PolyfaceBuilder.js +50 -10
  148. package/lib/esm/polyface/PolyfaceBuilder.js.map +1 -1
  149. package/lib/esm/polyface/PolyfaceQuery.d.ts +82 -0
  150. package/lib/esm/polyface/PolyfaceQuery.d.ts.map +1 -1
  151. package/lib/esm/polyface/PolyfaceQuery.js +148 -0
  152. package/lib/esm/polyface/PolyfaceQuery.js.map +1 -1
  153. package/lib/esm/polyface/multiclip/OffsetMeshContext.d.ts +202 -0
  154. package/lib/esm/polyface/multiclip/OffsetMeshContext.d.ts.map +1 -0
  155. package/lib/esm/polyface/multiclip/OffsetMeshContext.js +1032 -0
  156. package/lib/esm/polyface/multiclip/OffsetMeshContext.js.map +1 -0
  157. package/lib/esm/serialization/GeometrySamples.d.ts +4 -1
  158. package/lib/esm/serialization/GeometrySamples.d.ts.map +1 -1
  159. package/lib/esm/serialization/GeometrySamples.js +14 -6
  160. package/lib/esm/serialization/GeometrySamples.js.map +1 -1
  161. package/lib/esm/serialization/IModelJsonSchema.d.ts +1 -1
  162. package/lib/esm/serialization/IModelJsonSchema.js.map +1 -1
  163. package/lib/esm/topology/Graph.d.ts +113 -7
  164. package/lib/esm/topology/Graph.d.ts.map +1 -1
  165. package/lib/esm/topology/Graph.js +185 -7
  166. package/lib/esm/topology/Graph.js.map +1 -1
  167. package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts +38 -0
  168. package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts.map +1 -0
  169. package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.js +78 -0
  170. package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.js.map +1 -0
  171. package/package.json +4 -4
@@ -15,6 +15,7 @@ const Point2dVector2d_1 = require("./Point2dVector2d");
15
15
  const Point3dVector3d_1 = require("./Point3dVector3d");
16
16
  const Transform_1 = require("./Transform");
17
17
  /* eslint-disable @itwin/prefer-get */
18
+ // cSpell:words XXYZ YXYZ ZXYZ
18
19
  /**
19
20
  * PackedMatrix3dOps contains static methods for matrix operations where the matrix is a Float64Array.
20
21
  * * The Float64Array contains the matrix entries in row-major order
@@ -50,7 +51,7 @@ class PackedMatrix3dOps {
50
51
  dest[8] = a22;
51
52
  }
52
53
  /**
53
- * * multiply 3x3 matrix `a*b`, store in c.
54
+ * Multiply 3x3 matrix `a*b`, store in `result`.
54
55
  * * All params assumed length 9, allocated by caller.
55
56
  * * c may alias either input.
56
57
  */
@@ -61,7 +62,7 @@ class PackedMatrix3dOps {
61
62
  return result;
62
63
  }
63
64
  /**
64
- * * multiply 3x3 matrix `a*bTranspose`, store in c.
65
+ * Multiply 3x3 matrix `a*bTranspose`, store in `result`.
65
66
  * * All params assumed length 9, allocated by caller.
66
67
  * * c may alias either input.
67
68
  */
@@ -72,7 +73,7 @@ class PackedMatrix3dOps {
72
73
  return result;
73
74
  }
74
75
  /**
75
- * * multiply 3x3 matrix `a*bTranspose`, store in c.
76
+ * Multiply 3x3 matrix `aTranspose*b`, store in `result`.
76
77
  * * All params assumed length 9, allocated by caller.
77
78
  * * c may alias either input.
78
79
  */
@@ -82,7 +83,7 @@ class PackedMatrix3dOps {
82
83
  PackedMatrix3dOps.loadMatrix(result, (a[0] * b[0] + a[3] * b[3] + a[6] * b[6]), (a[0] * b[1] + a[3] * b[4] + a[6] * b[7]), (a[0] * b[2] + a[3] * b[5] + a[6] * b[8]), (a[1] * b[0] + a[4] * b[3] + a[7] * b[6]), (a[1] * b[1] + a[4] * b[4] + a[7] * b[7]), (a[1] * b[2] + a[4] * b[5] + a[7] * b[8]), (a[2] * b[0] + a[5] * b[3] + a[8] * b[6]), (a[2] * b[1] + a[5] * b[4] + a[8] * b[7]), (a[2] * b[2] + a[5] * b[5] + a[8] * b[8]));
83
84
  return result;
84
85
  }
85
- /** transpose 3x3 coefficients in place */
86
+ /** Transpose 3x3 matrix `a` in place */
86
87
  static transposeInPlace(a) {
87
88
  let q = a[1];
88
89
  a[1] = a[3];
@@ -94,10 +95,14 @@ class PackedMatrix3dOps {
94
95
  a[5] = a[7];
95
96
  a[7] = q;
96
97
  }
97
- /** transpose 3x3 coefficients in place */
98
+ /**
99
+ * Returns the transpose of 3x3 matrix `a`
100
+ * * If `dest` is passed as argument, then the function copies the transpose of 3x3 matrix `a` into `dest`
101
+ * * `a` is not changed unless also passed as the dest, i.e., copyTransposed(a,a) transposes `a` in place
102
+ */
98
103
  static copyTransposed(a, dest) {
99
104
  if (dest === a) {
100
- PackedMatrix3dOps.transposeInPlace(a);
105
+ PackedMatrix3dOps.transposeInPlace(dest);
101
106
  }
102
107
  else {
103
108
  if (!dest)
@@ -114,7 +119,7 @@ class PackedMatrix3dOps {
114
119
  }
115
120
  return dest;
116
121
  }
117
- /** transpose 3x3 coefficients in place */
122
+ /** Copy matrix `a` entries into `dest` */
118
123
  static copy(a, dest) {
119
124
  if (dest !== a) {
120
125
  dest[0] = a[0];
@@ -133,29 +138,31 @@ class PackedMatrix3dOps {
133
138
  exports.PackedMatrix3dOps = PackedMatrix3dOps;
134
139
  /** A Matrix3d is tagged indicating one of the following states:
135
140
  * * unknown: it is not know if the matrix is invertible.
136
- * * inverseStored: the matrix has its inverse stored
141
+ * * inverseStored: the matrix has its inverse stored.
137
142
  * * singular: the matrix is known to be singular.
138
143
  * @public
139
144
  */
140
145
  var InverseMatrixState;
141
146
  (function (InverseMatrixState) {
142
147
  /**
143
- * * The invertibility of the `coffs` array has not been determined.
144
- * * Any `inverseCoffs` contents are random.
148
+ * The invertibility of the `coffs` array has not been determined.
149
+ * Any `inverseCoffs` contents are random.
145
150
  */
146
151
  InverseMatrixState[InverseMatrixState["unknown"] = 0] = "unknown";
147
- /** An inverse was computed and stored as the `inverseCoffs` */
152
+ /**
153
+ * An inverse was computed and stored as the `inverseCoffs`
154
+ */
148
155
  InverseMatrixState[InverseMatrixState["inverseStored"] = 1] = "inverseStored";
149
156
  /**
150
- * * The `coffs` array is known to be singular.
151
- * * Any `inverseCoffs` contents are random.
157
+ * The `coffs` array is known to be singular.
158
+ * Any `inverseCoffs` contents are random.
152
159
  */
153
160
  InverseMatrixState[InverseMatrixState["singular"] = 2] = "singular";
154
161
  })(InverseMatrixState = exports.InverseMatrixState || (exports.InverseMatrixState = {}));
155
162
  /** A Matrix3d is a 3x3 matrix.
156
163
  * * A very common use is to hold a rigid body rotation (which has no scaling or skew), but the 3x3 contents can
157
164
  * also hold scaling and skewing.
158
- * * The matrix with 2-dimensional layout
165
+ * * The matrix with 2-dimensional layout (note: a 2d array can be shown by a matrix)
159
166
  * ```
160
167
  * equation
161
168
  * \matrixXY{A}
@@ -167,19 +174,21 @@ var InverseMatrixState;
167
174
  * ```
168
175
  * * If the matrix inverse is known it is stored in the inverseCoffs array.
169
176
  * * The inverse status (`unknown`, `inverseStored`, `singular`) status is indicated by the `inverseState` property.
170
- * * construction methods that are able to trivially construct the inverse store it immediately and note that in the inverseState.
171
- * * constructions (e.g. createRowValues) for which the inverse is not immediately known mark the
172
- * inverseState as unknown.
173
- * * Later queries for the inverse trigger full computation if needed at that time.
177
+ * * Construction methods that are able to trivially construct the inverse, store it immediately and note that in
178
+ * the inverseState.
179
+ * * Constructions (e.g. createRowValues) for which the inverse is not immediately known mark the inverseState as
180
+ * unknown.
181
+ * * Later queries for the inverse, trigger full computation if needed at that time.
174
182
  * * Most matrix queries are present with both "column" and "row" variants.
175
- * * Usage elsewhere in the library is typically "column" based. For example, in a Transform
176
- * that carries a coordinate frame the matrix columns are the unit vectors for the axes.
183
+ * * Usage elsewhere in the library is typically "column" based. For example, in a Transform that carries a
184
+ * coordinate frame, the matrix columns are the unit vectors for the axes.
177
185
  * @public
178
186
  */
179
187
  class Matrix3d {
180
188
  /**
181
- *
182
- * @param coffs optional coefficient array. This is captured.
189
+ * Constructor
190
+ * @param coffs optional coefficient array.
191
+ * * **WARNING:** coffs is captured (i.e., is now owned by the Matrix3d object and can be modified by it).
183
192
  */
184
193
  constructor(coffs) {
185
194
  this.coffs = coffs ? coffs : new Float64Array(9);
@@ -197,14 +206,16 @@ class Matrix3d {
197
206
  /** Freeze this Matrix3d. */
198
207
  freeze() {
199
208
  this.computeCachedInverse(true);
200
- /* hm.. can't freeze the Float64Arrays . . .
209
+ /*
210
+ hm.. can't freeze the Float64Arrays..
201
211
  Object.freeze(this.coffs);
202
212
  if (this.inverseCoffs)
203
213
  Object.freeze(this.inverseCoffs);
204
214
  */
205
215
  return Object.freeze(this);
206
216
  }
207
- /** Return a json object containing the 9 numeric entries as a single array in row major order,
217
+ /**
218
+ * Return a json object containing the 9 numeric entries as a single array in row major order,
208
219
  * `[ [1, 2, 3],[ 4, 5, 6], [7, 8, 9] ]`
209
220
  */
210
221
  toJSON() {
@@ -212,46 +223,81 @@ class Matrix3d {
212
223
  [this.coffs[3], this.coffs[4], this.coffs[5]],
213
224
  [this.coffs[6], this.coffs[7], this.coffs[8]]];
214
225
  }
215
- /** copy data from various input forms to this matrix.
226
+ /**
227
+ * Copy data from various input forms to this matrix.
216
228
  * The source can be:
217
229
  * * Another `Matrix3d`
218
230
  * * An array of 3 arrays, each of which has the 3 numbers for a row of the matrix.
219
- * * An array of 9 numbers in row major order.
231
+ * * An array of 4 or 9 numbers in row major order.
232
+ * * **WARNING:** if json is an array of numbers but size is not 4 or 9, the matrix is set to zeros.
220
233
  */
221
234
  setFromJSON(json) {
222
235
  this.inverseCoffs = undefined;
236
+ // if no json is passed
223
237
  if (!json) {
224
238
  this.setRowValues(0, 0, 0, 0, 0, 0, 0, 0, 0);
225
239
  return;
226
240
  }
241
+ // if json is Matrix3d
227
242
  if (!Array.isArray(json)) {
228
243
  if (json instanceof Matrix3d)
229
244
  this.setFrom(json);
230
245
  return;
231
246
  }
247
+ // if json is Matrix3dProps and is an array of arrays
232
248
  if (Geometry_1.Geometry.isArrayOfNumberArray(json, 3, 3)) {
233
249
  const data = json;
234
250
  this.setRowValues(data[0][0], data[0][1], data[0][2], data[1][0], data[1][1], data[1][2], data[2][0], data[2][1], data[2][2]);
235
251
  return;
236
252
  }
253
+ // if json is Matrix3dProps and is an array of numbers
237
254
  if (json.length === 9) {
238
255
  const data = json;
239
256
  this.setRowValues(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8]);
257
+ return;
240
258
  }
241
259
  else if (json.length === 4) {
242
260
  const data = json;
243
261
  this.setRowValues(data[0], data[1], 0, data[2], data[3], 0, 0, 0, 1);
262
+ return;
244
263
  }
264
+ // if json is Matrix3dProps but is not the right size
265
+ this.setRowValues(0, 0, 0, 0, 0, 0, 0, 0, 0);
266
+ return;
267
+ }
268
+ /** Return a new Matrix3d constructed from contents of the json value. See `setFromJSON` for layout rules */
269
+ static fromJSON(json) {
270
+ const result = Matrix3d.createIdentity();
271
+ result.setFromJSON(json);
272
+ return result;
245
273
  }
246
- /** Return a new Matrix3d constructed from contents of the json value. Se `setFromJSON` for layout rules */
247
- static fromJSON(json) { const result = Matrix3d.createIdentity(); result.setFromJSON(json); return result; }
248
- /** Test if this Matrix3d and other are within tolerance in all numeric entries.
274
+ /**
275
+ * Test if `this` and `other` are within tolerance in all numeric entries.
249
276
  * @param tol optional tolerance for comparisons by Geometry.isDistanceWithinTol
250
277
  */
251
278
  isAlmostEqual(other, tol) {
252
279
  return Geometry_1.Geometry.isDistanceWithinTol(this.maxDiff(other), tol);
253
280
  }
254
- /** Test if `this` and `other` have almost equal Z column and have X and Y columns differing only by a rotation around that Z.
281
+ /**
282
+ * Test if `this` and `other` are within tolerance in the column entries specified by `columnIndex`.
283
+ * @param tol optional tolerance for comparisons by Geometry.isDistanceWithinTol
284
+ */
285
+ isAlmostEqualColumn(columnIndex, other, tol) {
286
+ const max = Geometry_1.Geometry.maxAbsXYZ(this.coffs[columnIndex] - other.coffs[columnIndex], this.coffs[columnIndex + 3] - other.coffs[columnIndex + 3], this.coffs[columnIndex + 6] - other.coffs[columnIndex + 6]);
287
+ return Geometry_1.Geometry.isDistanceWithinTol(max, tol);
288
+ }
289
+ /**
290
+ * Test if column (specified by `columnIndex`) entries of `this` and [ax,ay,az] are within tolerance.
291
+ * @param tol optional tolerance for comparisons by Geometry.isDistanceWithinTol
292
+ */
293
+ isAlmostEqualColumnXYZ(columnIndex, ax, ay, az, tol) {
294
+ const max = Geometry_1.Geometry.maxAbsXYZ(this.coffs[columnIndex] - ax, this.coffs[columnIndex + 3] - ay, this.coffs[columnIndex + 6] - az);
295
+ return Geometry_1.Geometry.isDistanceWithinTol(max, tol);
296
+ }
297
+ /**
298
+ * Test if `this` and `other` have almost equal Z column and have X and Y columns differing only by a
299
+ * rotation of the same angle around that Z.
300
+ * * **WARNING:** X and Y columns have to be perpendicular to Z column in both `this` and `other`.
255
301
  * @param tol optional tolerance for comparisons by Geometry.isDistanceWithinTol
256
302
  */
257
303
  isAlmostEqualAllowZRotation(other, tol) {
@@ -259,10 +305,15 @@ class Matrix3d {
259
305
  return true;
260
306
  if (this.isAlmostEqualColumn(Geometry_1.AxisIndex.Z, other, tol)) {
261
307
  const radians = Angle_1.Angle.radiansBetweenVectorsXYZ(this.coffs[0], this.coffs[3], this.coffs[6], other.coffs[0], other.coffs[3], other.coffs[6]);
262
- const angle = Angle_1.Angle.createRadians(radians);
308
+ const angle = Angle_1.Angle.createRadians(radians); // angle between X columns in `this` and `other`
263
309
  const columnX = this.columnX();
264
310
  const columnY = this.columnY();
265
311
  const columnZ = this.columnZ();
312
+ /**
313
+ * Here we rotate this.columnX() around this.columnZ() by "angle" and expect to get other.columnX().
314
+ * Then we rotate this.columnY() around this.columnZ() by the same "angle" and if we get other.columnY(),
315
+ * that means this` and `other` have X and Y columns differing only by a rotation around that Z.
316
+ */
266
317
  let column = Point3dVector3d_1.Vector3d.createRotateVectorAroundVector(columnX, columnZ, angle);
267
318
  if (other.isAlmostEqualColumnXYZ(0, column.x, column.y, column.z, tol)) {
268
319
  column = Point3dVector3d_1.Vector3d.createRotateVectorAroundVector(columnY, columnZ, angle);
@@ -271,16 +322,10 @@ class Matrix3d {
271
322
  }
272
323
  return false;
273
324
  }
274
- isAlmostEqualColumn(columnIndex, other, tol) {
275
- const a = Geometry_1.Geometry.maxAbsXYZ(this.coffs[columnIndex] - other.coffs[columnIndex], this.coffs[columnIndex + 3] - other.coffs[columnIndex + 3], this.coffs[columnIndex + 6] - other.coffs[columnIndex + 6]);
276
- return Geometry_1.Geometry.isDistanceWithinTol(a, tol);
277
- }
278
- isAlmostEqualColumnXYZ(columnIndex, ax, ay, az, tol) {
279
- const a = Geometry_1.Geometry.maxAbsXYZ(this.coffs[columnIndex] - ax, this.coffs[columnIndex + 3] - ay, this.coffs[columnIndex + 6] - az);
280
- return Geometry_1.Geometry.isDistanceWithinTol(a, tol);
281
- }
282
325
  /** Test for exact (bitwise) equality with other. */
283
- isExactEqual(other) { return this.maxDiff(other) === 0.0; }
326
+ isExactEqual(other) {
327
+ return this.maxDiff(other) === 0.0;
328
+ }
284
329
  /** test if all entries in the z row and column are exact 001, i.e. the matrix only acts in 2d */
285
330
  get isXY() {
286
331
  return this.coffs[2] === 0.0
@@ -289,10 +334,16 @@ class Matrix3d {
289
334
  && this.coffs[7] === 0.0
290
335
  && this.coffs[8] === 1.0;
291
336
  }
292
- // !! does not clear supplied result !!
293
- static _create(result) { return result ? result : new Matrix3d(); }
294
- /** Returns a Matrix3d populated by numeric values given in row-major order.
295
- * set all entries in the matrix from call parameters appearing in row - major order, i.e.
337
+ /**
338
+ * If result is not provided, then the method returns a new (zeroed) matrix; otherwise the result is
339
+ * not zeroed first and is just returned as-is.
340
+ */
341
+ static _create(result) {
342
+ return result ? result : new Matrix3d();
343
+ }
344
+ /**
345
+ * Returns a Matrix3d populated by numeric values given in row-major order.
346
+ * Sets all entries in the matrix from call parameters appearing in row-major order, i.e.
296
347
  * ```
297
348
  * equation
298
349
  * \begin{bmatrix}a_{xx}\ a_{xy}\ a_{xz}\\ a_{yx}\ a_{yy}\ a_{yz}\\ a_{zx}\ a_{zy}\ a_{zz}\end{bmatrix}
@@ -323,7 +374,7 @@ class Matrix3d {
323
374
  }
324
375
  /**
325
376
  * Create a Matrix3d with caller-supplied coefficients and optional inverse coefficients.
326
- * * The inputs are captured into the new Matrix3d.
377
+ * * The inputs are captured into (i.e., owned by) the new Matrix3d.
327
378
  * * The caller is responsible for validity of the inverse coefficients.
328
379
  * @param coffs (required) array of 9 coefficients.
329
380
  * @param inverseCoffs (optional) array of 9 coefficients.
@@ -341,12 +392,15 @@ class Matrix3d {
341
392
  return result;
342
393
  }
343
394
  /**
344
- * create a matrix by distributing vectors to columns in one of 6 orders.
395
+ * Create a matrix by distributing vectors to columns in one of 6 orders.
345
396
  * @param axisOrder identifies where the columns are placed.
346
- * @param columnA vector to place in the first column named by the axis order.
347
- * @param columnB vector to place in the second column named by the axis order.
348
- * @param columnC vector to place in the third column named by the axis order.
349
- * @param result
397
+ * @param columnA vector to place in the column specified by first letter in the AxisOrder name.
398
+ * @param columnB vector to place in the column specified by second letter in the AxisOrder name.
399
+ * @param columnC vector to place in the column specified by third letter in the AxisOrder name.
400
+ * @param result optional result matrix3d
401
+ * * Example: If you pass AxisOrder.YZX, then result will be [columnC, columnA, columnB] because
402
+ * first letter Y means columnA should go to the second column, second letter Z means columnB should
403
+ * go to the third column, and third letter X means columnC should go to the first column.
350
404
  */
351
405
  static createColumnsInAxisOrder(axisOrder, columnA, columnB, columnC, result) {
352
406
  if (!result)
@@ -366,13 +420,26 @@ class Matrix3d {
366
420
  else if (axisOrder === Geometry_1.AxisOrder.ZYX) {
367
421
  result.setColumns(columnC, columnB, columnA);
368
422
  }
369
- else { // fallthrough should only happen for AxisOrder.XYZ
423
+ else { // AxisOrder.XYZ
370
424
  result.setColumns(columnA, columnB, columnC);
371
425
  }
372
426
  return result;
373
427
  }
374
428
  /**
375
- * set all entries in the matrix from call parameters appearing in row-major order.
429
+ * Copy the transpose of the coffs to the inverseCoffs.
430
+ * * Mark the matrix as inverseStored.
431
+ */
432
+ setupInverseTranspose() {
433
+ const coffs = this.coffs;
434
+ this.inverseState = InverseMatrixState.inverseStored;
435
+ this.inverseCoffs = Float64Array.from([
436
+ coffs[0], coffs[3], coffs[6],
437
+ coffs[1], coffs[4], coffs[7],
438
+ coffs[2], coffs[5], coffs[8],
439
+ ]);
440
+ }
441
+ /**
442
+ * Set all entries in the matrix from call parameters appearing in row-major order.
376
443
  * @param axx Row x, column x (0,0) entry
377
444
  * @param axy Row x, column y (0,1) entry
378
445
  * @param axz Row x, column z (0,2) entry
@@ -396,15 +463,22 @@ class Matrix3d {
396
463
  this.inverseState = InverseMatrixState.unknown;
397
464
  }
398
465
  /** Set the matrix to an identity. */
399
- setIdentity() { this.setRowValues(1, 0, 0, 0, 1, 0, 0, 0, 1); this.setupInverseTranspose(); }
466
+ setIdentity() {
467
+ this.setRowValues(1, 0, 0, 0, 1, 0, 0, 0, 1);
468
+ this.setupInverseTranspose();
469
+ }
400
470
  /** Set the matrix to all zeros. */
401
- setZero() { this.setRowValues(0, 0, 0, 0, 0, 0, 0, 0, 0); this.inverseState = InverseMatrixState.singular; }
402
- /** copy contents from another matrix. */
471
+ setZero() {
472
+ this.setRowValues(0, 0, 0, 0, 0, 0, 0, 0, 0);
473
+ this.inverseState = InverseMatrixState.singular;
474
+ }
475
+ /** Copy contents from another matrix. */
403
476
  setFrom(other) {
404
477
  if (other === undefined) {
405
478
  this.setIdentity();
479
+ return;
406
480
  }
407
- else if (other !== this) {
481
+ if (other !== this) {
408
482
  for (let i = 0; i < 9; i++)
409
483
  this.coffs[i] = other.coffs[i];
410
484
  if (other.inverseState === InverseMatrixState.inverseStored && other.inverseCoffs !== undefined) {
@@ -416,28 +490,29 @@ class Matrix3d {
416
490
  else if (other.inverseState !== InverseMatrixState.inverseStored) {
417
491
  this.inverseState = other.inverseState;
418
492
  }
419
- else { // This is reached of other says stored but does not have coffs. This should not happen.
493
+ else { // This is reached when other says stored but does not have coffs. This should not happen.
420
494
  this.inverseState = InverseMatrixState.unknown;
421
495
  }
422
496
  }
423
497
  }
424
- /** return a clone of this matrix.
425
- * * coefficients are copied.
426
- * * inverse coefficients are NOT copied.
427
- * * inverse status is set to unknown
498
+ /**
499
+ * Return a clone of this matrix.
500
+ * * Coefficients are copied.
501
+ * * Inverse coefficients and inverse status are copied if stored by `this`.
428
502
  */
429
503
  clone(result) {
430
504
  result = result ? result : new Matrix3d();
431
505
  result.setFrom(this);
432
506
  return result;
433
507
  }
434
- /** create a matrix with all zeros.
508
+ /**
509
+ * Create a matrix with all zeros.
435
510
  * * Note that for geometry transformations "all zeros" is not a useful default state.
436
- * * Hence almost always use `createIdentity` for graphics transformations.
437
- * * "all zeros" is appropriate for summing moment data.
511
+ * * Hence, almost always use `createIdentity` for graphics transformations.
512
+ * * "All zeros" is appropriate for summing moment data.
438
513
  * ```
439
514
  * equation
440
- * \begin{bmatrix}0 0 0 \\ 0 0 0 \\ 0 0 0\end{bmatrix}
515
+ * \begin{bmatrix}0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 0\end{bmatrix}
441
516
  * ```
442
517
  */
443
518
  static createZero() {
@@ -445,13 +520,14 @@ class Matrix3d {
445
520
  retVal.inverseState = InverseMatrixState.singular;
446
521
  return retVal;
447
522
  }
448
- /** create an identity matrix.
449
- * * all diagonal entries (xx,yy,zz) are one
450
- * * all others are zero.
451
- * * This (rather than all zeros) is the useful state for most graphics transformations.
523
+ /**
524
+ * Create an identity matrix.
525
+ * * All diagonal entries (xx,yy,zz) are one
526
+ * * All others are zero.
527
+ * * This (rather than "all zeros") is the useful state for most graphics transformations.
452
528
  * ```
453
529
  * equation
454
- * \begin{bmatrix}1 0 0 \\ 0 1 0 \\ 0 0 1\end{bmatrix}
530
+ * \begin{bmatrix}1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1\end{bmatrix}
455
531
  * ```
456
532
  *
457
533
  */
@@ -461,55 +537,7 @@ class Matrix3d {
461
537
  return result;
462
538
  }
463
539
  /**
464
- * Create a matrix with uniform scale factors.
465
- * For scale factor _s_,
466
- * ```
467
- * equation
468
- * \begin{bmatrix}s & 0 & 0 \\ 0 & s & 0\\ 0 & 0 & s\end{bmatrix}
469
- * ```
470
- */
471
- static createUniformScale(scaleFactor) {
472
- return Matrix3d.createScale(scaleFactor, scaleFactor, scaleFactor);
473
- }
474
- /**
475
- * Construct a rigid matrix using createPerpendicularVectorFavorXYPlane to generate a vector perpendicular to vectorA.
476
- * *
477
- */
478
- static createRigidHeadsUp(vectorA, axisOrder = Geometry_1.AxisOrder.ZXY, result) {
479
- const vectorB = Matrix3d.createPerpendicularVectorFavorXYPlane(vectorA);
480
- const matrix = Matrix3d.createRigidFromColumns(vectorA, vectorB, axisOrder, result);
481
- if (matrix) {
482
- matrix.setupInverseTranspose();
483
- return matrix;
484
- }
485
- return Matrix3d.createIdentity(result);
486
- }
487
- /**
488
- * return a vector that is perpendicular to the input direction.
489
- * * Among the infinite number of perpendiculars possible, this method
490
- * favors having one in the xy plane.
491
- * * Hence, when vectorA is NOT close to the Z axis, the returned vector is Z cross vectorA.
492
- * * But vectorA is close to the Z axis, the returned vector is unitY cross vectorA.
493
- */
494
- static createPerpendicularVectorFavorXYPlane(vector, result) {
495
- const a = vector.magnitude();
496
- const b = a / 64.0; // A constant from the dawn of time in the CAD industry.
497
- if (Math.abs(vector.x) < b && Math.abs(vector.y) < b) {
498
- return Point3dVector3d_1.Vector3d.createCrossProduct(vector.x, vector.y, vector.z, 0, -1, 0, result);
499
- }
500
- return Point3dVector3d_1.Vector3d.createCrossProduct(0, 0, 1, vector.x, vector.y, vector.z, result);
501
- }
502
- /**
503
- * return a vector that is perpendicular to the input direction.
504
- * * Among the infinite number of perpendiculars possible, this method
505
- * favors having one near the Z.
506
- * That is achieved by crossing "this" vector with the result of createPerpendicularVectorFavorXYPlane.
507
- */
508
- static createPerpendicularVectorFavorPlaneContainingZ(vector, result) {
509
- result = Matrix3d.createPerpendicularVectorFavorXYPlane(vector, result);
510
- return vector.crossProduct(result, result);
511
- }
512
- /** Create a matrix with distinct x,y,z diagonal (scale) entries.
540
+ * Create a matrix with distinct x,y,z diagonal (scale) entries.
513
541
  * ```
514
542
  * equation
515
543
  * \begin{bmatrix}s_x & 0 & 0 \\ 0 & s_y & 0\\ 0 & 0 & s_z\end{bmatrix}
@@ -534,8 +562,104 @@ class Matrix3d {
534
562
  }
535
563
  return result;
536
564
  }
537
- /** return a rotation of specified angle around an axis */
565
+ /**
566
+ * Create a matrix with uniform scale factors for scale factor "s"
567
+ * ```
568
+ * equation
569
+ * \begin{bmatrix}s & 0 & 0 \\ 0 & s & 0\\ 0 & 0 & s\end{bmatrix}
570
+ * ```
571
+ */
572
+ static createUniformScale(scaleFactor) {
573
+ return Matrix3d.createScale(scaleFactor, scaleFactor, scaleFactor);
574
+ }
575
+ /**
576
+ * Return a vector that is perpendicular to the input `vectorA`.
577
+ * * Among the infinite number of perpendiculars possible, this method favors having one in the xy plane.
578
+ * * Hence, when `vectorA` is close to the Z axis, the returned vector is `vectorA cross -unitY`
579
+ * but when `vectorA` is NOT close to the Z axis, the returned vector is `unitZ cross vectorA`.
580
+ */
581
+ static createPerpendicularVectorFavorXYPlane(vectorA, result) {
582
+ const a = vectorA.magnitude();
583
+ const scale = 64.0; // A constant from the dawn of time in the CAD industry
584
+ const b = a / scale;
585
+ // if vectorA is close to the Z axis
586
+ if (Math.abs(vectorA.x) < b && Math.abs(vectorA.y) < b) {
587
+ return Point3dVector3d_1.Vector3d.createCrossProduct(vectorA.x, vectorA.y, vectorA.z, 0, -1, 0, result);
588
+ }
589
+ // if vectorA is NOT close to the Z axis
590
+ return Point3dVector3d_1.Vector3d.createCrossProduct(0, 0, 1, vectorA.x, vectorA.y, vectorA.z, result);
591
+ }
592
+ /**
593
+ * Return a vector that is perpendicular to the input `vectorA`.
594
+ * * Among the infinite number of perpendiculars possible, this method favors having one near the plane
595
+ * containing Z.
596
+ * That is achieved by cross product of `this` vector with the result of createPerpendicularVectorFavorXYPlane.
597
+ */
598
+ static createPerpendicularVectorFavorPlaneContainingZ(vectorA, result) {
599
+ /**
600
+ * vectorA, result (below), and "vectorA cross result" form a coordinate system where "result" is located on
601
+ * the XY-plane. Once you've got a coordinate system with an axis in the XY-plane, your other two axes form
602
+ * a plane that includes the z-axis.
603
+ */
604
+ result = Matrix3d.createPerpendicularVectorFavorXYPlane(vectorA, result);
605
+ return vectorA.crossProduct(result, result);
606
+ }
607
+ /**
608
+ * Create a matrix from column vectors, shuffled into place per axisOrder
609
+ * For example, if axisOrder = XYZ then it returns [vectorU, vectorV, vectorW]
610
+ * Another example, if axisOrder = YZX then it returns [vectorW, vectorU, vectorV] because
611
+ * Y is at index 0 so vectorU goes to the column Y (column 2), Z is at index 1 so vectorV goes
612
+ * to the column Z (column 3), and X is at index 2 so vectorW goes to the column X (column 1)
613
+ */
614
+ static createShuffledColumns(vectorU, vectorV, vectorW, axisOrder, result) {
615
+ const target = Matrix3d._create(result);
616
+ target.setColumn(Geometry_1.Geometry.axisOrderToAxis(axisOrder, 0), vectorU);
617
+ target.setColumn(Geometry_1.Geometry.axisOrderToAxis(axisOrder, 1), vectorV);
618
+ target.setColumn(Geometry_1.Geometry.axisOrderToAxis(axisOrder, 2), vectorW);
619
+ return target;
620
+ }
621
+ /**
622
+ * Create a new orthogonal matrix (perpendicular columns, unit length, transpose is inverse).
623
+ * * `vectorA1 = Normalized vectorA` is placed in the column specified by **first** letter in
624
+ * the AxisOrder name.
625
+ * * Normalized `vectorC1 = vectorA1 cross vectorB` is placed in the column specified by **third**
626
+ * letter in the AxisOrder name.
627
+ * * Normalized `vectorC1 cross vectorA` is placed in the column specified by **second**
628
+ * letter in the AxisOrder name.
629
+ * * This function internally uses createShuffledColumns.
630
+ */
631
+ static createRigidFromColumns(vectorA, vectorB, axisOrder, result) {
632
+ const vectorA1 = vectorA.normalize();
633
+ if (vectorA1) {
634
+ const vectorC1 = vectorA1.unitCrossProduct(vectorB);
635
+ if (vectorC1) {
636
+ const vectorB1 = vectorC1.unitCrossProduct(vectorA);
637
+ if (vectorB1) {
638
+ const retVal = Matrix3d.createShuffledColumns(vectorA1, vectorB1, vectorC1, axisOrder, result);
639
+ retVal.setupInverseTranspose();
640
+ return retVal;
641
+ }
642
+ }
643
+ }
644
+ return undefined;
645
+ }
646
+ /**
647
+ * Construct a rigid matrix (orthogonal matrix with +1 determinant) using vectorA and its 2 perpendicular.
648
+ * * If axisOrder is not passed then `AxisOrder = AxisOrder.ZXY` is used as default.
649
+ * * This function internally uses createPerpendicularVectorFavorXYPlane and createRigidFromColumns.
650
+ */
651
+ static createRigidHeadsUp(vectorA, axisOrder = Geometry_1.AxisOrder.ZXY, result) {
652
+ const vectorB = Matrix3d.createPerpendicularVectorFavorXYPlane(vectorA);
653
+ const matrix = Matrix3d.createRigidFromColumns(vectorA, vectorB, axisOrder, result);
654
+ if (matrix) {
655
+ matrix.setupInverseTranspose();
656
+ return matrix;
657
+ }
658
+ return Matrix3d.createIdentity(result);
659
+ }
660
+ /** Return the matrix for rotation of `angle` around desired `axis` */
538
661
  static createRotationAroundVector(axis, angle, result) {
662
+ // Rodriguez formula (matrix form), https://mathworld.wolfram.com/RodriguesRotationFormula.html
539
663
  const c = angle.cos();
540
664
  const s = angle.sin();
541
665
  const v = 1.0 - c;
@@ -547,10 +671,11 @@ class Matrix3d {
547
671
  }
548
672
  return undefined;
549
673
  }
550
- /** Returns a rotation of specified angle around an axis
674
+ /** Returns a rotation of specified angle around one of the main axis (X,Y,Z).
551
675
  * @param axisIndex index of axis (AxisIndex.X, AxisIndex.Y, AxisIndex.Z) kept fixed by the rotation.
552
676
  * @param angle angle of rotation
553
677
  * @param result optional result matrix.
678
+ * * Math details of 3d rotation matrices derivation can be found at docs/learning/geometry/Angle.md
554
679
  */
555
680
  static createRotationAroundAxisIndex(axisIndex, angle, result) {
556
681
  const c = angle.cos();
@@ -568,27 +693,101 @@ class Matrix3d {
568
693
  myResult.setupInverseTranspose();
569
694
  return myResult;
570
695
  }
696
+ /**
697
+ * Replace current rows Ui and Uj with (c*Ui + s*Uj) and (c*Uj - s*Ui).
698
+ * * There is no checking for i,j being 0,1,2.
699
+ * @param i first row index. **must be 0,1,2** (unchecked)
700
+ * @param j second row index. **must be 0,1,2** (unchecked)
701
+ * @param c fist coefficient
702
+ * @param s second coefficient
703
+ */
704
+ applyGivensRowOp(i, j, c, s) {
705
+ let ii = 3 * i;
706
+ let jj = 3 * j;
707
+ const limit = ii + 3;
708
+ for (; ii < limit; ii++, jj++) {
709
+ const a = this.coffs[ii];
710
+ const b = this.coffs[jj];
711
+ this.coffs[ii] = a * c + b * s;
712
+ this.coffs[jj] = -a * s + b * c;
713
+ }
714
+ }
715
+ /**
716
+ * Replace current columns Ui and Uj with (c*Ui + s*Uj) and (c*Uj - s*Ui).
717
+ * * There is no checking for i,j being 0,1,2.
718
+ * * This is used in compute intensive inner loops
719
+ * @param i first row index. **must be 0,1,2** (unchecked)
720
+ * @param j second row index. **must be 0,1,2** (unchecked)
721
+ * @param c fist coefficient
722
+ * @param s second coefficient
723
+ */
724
+ applyGivensColumnOp(i, j, c, s) {
725
+ const limit = i + 9;
726
+ for (; i < limit; i += 3, j += 3) {
727
+ const a = this.coffs[i];
728
+ const b = this.coffs[j];
729
+ this.coffs[i] = a * c + b * s;
730
+ this.coffs[j] = -a * s + b * c;
731
+ }
732
+ }
733
+ /**
734
+ * Create a matrix from column vectors.
735
+ * ```
736
+ * equation
737
+ * \begin{bmatrix}U_x & V_x & W_x \\ U_y & V_y & W_y \\ U_z & V_z & W_z \end{bmatrix}
738
+ * ```
739
+ */
740
+ static createColumns(vectorU, vectorV, vectorW, result) {
741
+ return Matrix3d.createRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, vectorU.z, vectorV.z, vectorW.z, result);
742
+ }
743
+ /** Create a matrix with each column's _x,y_ parts given `XAndY` and separate numeric z values.
744
+ * ```
745
+ * equation
746
+ * \begin{bmatrix}U_x & V_x & W_x \\ U_y & V_y & W_y \\ u & v & w \end{bmatrix}
747
+ * ```
748
+ */
749
+ static createColumnsXYW(vectorU, u, vectorV, v, vectorW, w, result) {
750
+ return Matrix3d.createRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, u, v, w, result);
751
+ }
571
752
  /** Create a matrix from "as viewed" right and up vectors.
572
753
  * * ColumnX points in the rightVector direction
573
- * * ColumnY points in in the upVectorDirection
574
- * * ColumnZ is a unit cross product.
575
- * Optionally rotate the standard cube by 45 to bring its left or right vertical edge to center
576
- * * leftNoneRight = [-1,0,1] respectively for left edge, no rotation, or right edge
577
- * * bottomNoneTop = [-1,0,1] respectively for isometric rotation to view the bottom, no isometric rotation, and isometric rotation to view the top
578
- * This is expected to be used with various principal unit vectors that are perpendicular to each other.
579
- * * STANDARD TOP VIEW: (Vector3d.UnitX (), Vector3d.UnitY (), 0, 0)
580
- * * STANDARD FRONT VIEW: (Vector3d.UnitX (), Vector3d.UnitZ (), 0, 0)
581
- * * STANDARD BACK VIEW: (Vector3d.UnitX (-1), Vector3d.UnitZ (), 0, 0)
582
- * * STANDARD RIGHT VIEW: (Vector3d.UnitY (1), Vector3d.UnitZ (), 0, 0)
583
- * * STANDARD LEFT VIEW: (Vector3d.UnitY (-1), Vector3d.UnitZ (), 0, 0)
584
- * * STANDARD BOTTOM VIEW: (Vector3d.UnitX (1), Vector3d.UnitY (-1), 0, 0)
585
- * @param leftNoneRight Normally one of {-1,0,1}, where (-1) indicates the left vertical is rotated to center and (1) for right. Other numbers are used as multiplier for this 45 degree rotation
586
- * @returns undefined if columnX, columnY are coplanar.
754
+ * * ColumnY points in the upVector direction
755
+ * * ColumnZ is a unit cross product of ColumnX and ColumnY.
756
+ * * Optionally rotate by 45 degrees around `upVector` to bring its left or right vertical edge to center.
757
+ * * Optionally rotate by arctan(1/sqrt(2)) ~ 35.264 degrees around `rightVector` to bring the top or bottom
758
+ * horizontal edge of the view to the center (for isometric views).
759
+ * * This is expected to be used with various principal unit vectors that are perpendicular to each other.
760
+ * * STANDARD TOP VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitY(), 0, 0)
761
+ * * STANDARD FRONT VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitZ(), 0, 0)
762
+ * * STANDARD BACK VIEW: createViewedAxes(Vector3d.unitX(-1), Vector3d.unitZ(), 0, 0)
763
+ * * STANDARD RIGHT VIEW: createViewedAxes(Vector3d.unitY(), Vector3d.unitZ(), 0, 0)
764
+ * * STANDARD LEFT VIEW: createViewedAxes(Vector3d.unitY(-1), Vector3d.unitZ(), 0, 0)
765
+ * * STANDARD BOTTOM VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitY(-1), 0, 0)
766
+ * * STANDARD ISO VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitZ(), -1, 1)
767
+ * * STANDARD RIGHT ISO VIEW: createViewedAxes(Vector3d.unitX(), Vector3d.unitZ(), 1, 1)
768
+ * * Front, right, back, left, top, and bottom standard views are views from faces of the cube
769
+ * and iso and right iso standard views are views from corners of the cube.
770
+ * * Note: createViewedAxes is column-based so always returns local to world
771
+ *
772
+ * @param rightVector ColumnX of the returned matrix. Expected to be perpendicular to upVector.
773
+ * @param upVector ColumnY of the returned matrix. Expected to be perpendicular to rightVector.
774
+ * @param leftNoneRight Specifies the ccw rotation around `upVector` axis. Normally one of "-1", "0", and "1",
775
+ * where "-1" indicates rotation by 45 degrees to bring the left vertical edge to center, "0" means no rotation,
776
+ * and "1" indicates rotation by 45 degrees to bring the right vertical edge to center. Other numbers are
777
+ * used as multiplier for this 45 degree rotation.
778
+ * @param topNoneBottom Specifies the ccw rotation around `rightVector` axis. Normally one of "-1", "0", and "1",
779
+ * where "-1" indicates isometric rotation (35.264 degrees) to bring the bottom upward, "0" means no rotation,
780
+ * and "1" indicates isometric rotation (35.264 degrees) to bring the top downward. Other numbers are
781
+ * used as multiplier for the 35.264 degree rotation.
782
+ * @returns matrix = [rightVector, upVector, rightVector cross upVector] with the applied rotations specified
783
+ * by leftNoneRight and topNoneBottom. Returns undefined if rightVector and upVector are parallel.
587
784
  */
588
785
  static createViewedAxes(rightVector, upVector, leftNoneRight = 0, topNoneBottom = 0) {
589
786
  const columnZ = rightVector.crossProduct(upVector);
590
787
  if (columnZ.normalizeInPlace()) {
591
- const geometry = Matrix3d.createColumns(rightVector, upVector, columnZ);
788
+ // matrix = [rightVector, upVector, rightVector cross upVector]
789
+ const matrix = Matrix3d.createColumns(rightVector, upVector, columnZ);
790
+ // "45 degrees * leftNoneRight" rotation around Y
592
791
  if (leftNoneRight !== 0.0) {
593
792
  let c = Math.sqrt(0.5);
594
793
  let s = leftNoneRight < 0.0 ? -c : c;
@@ -597,377 +796,213 @@ class Matrix3d {
597
796
  c = Math.cos(radians);
598
797
  s = Math.sin(radians);
599
798
  }
600
- geometry.applyGivensColumnOp(2, 0, c, s); // rotate around Y
799
+ matrix.applyGivensColumnOp(2, 0, c, s); // rotate around Y (equivalent to matrix*rotationY)
601
800
  }
801
+ // "35.264 degrees * topNoneBottom" rotation around X
602
802
  if (topNoneBottom !== 0.0) {
603
803
  const theta = topNoneBottom * Math.atan(Math.sqrt(0.5));
604
804
  const c = Math.cos(theta);
605
805
  const s = Math.sin(theta);
606
- geometry.applyGivensColumnOp(1, 2, c, -s); // rotate around X
806
+ matrix.applyGivensColumnOp(1, 2, c, -s); // rotate around X (equivalent to matrix*rotationX)
607
807
  }
608
- return geometry;
808
+ return matrix;
609
809
  }
610
810
  return undefined;
611
811
  }
612
812
  /**
613
813
  * Create a rotation matrix for one of the 8 standard views.
614
- * * With `invert === false` the return is such that `matrix.multiply(worldVector)` returns the vector as seen in the xy (projected) coordinates of the view.
615
- * * With invert === true the matrix is transposed so that `matrix.multiply(viewVector` maps the "in view" vector to a world vector.
814
+ * * Default is TOP view (`local X = world X`, `local Y = world Y`, `local Z = world Z`).
815
+ * * To change view from the TOP to one of the other 7 standard views, we need to multiply "world data" to
816
+ * the corresponding matrix1 provided by `createStandardWorldToView(index, false)` and then
817
+ * `matrix1.multiply(world data)` will return "local data".
818
+ * * To change view back to the TOP, we need to multiply "local data" to the corresponding matrix2 provided
819
+ * by `createStandardWorldToView(index, true)` and then `matrix2.multiply(local data)` will returns "world data".
820
+ * * Note: No matter how you rotate the world axis, local X is always pointing right, local Y is always pointing up,
821
+ * and local Z is always pointing toward you.
616
822
  *
617
- * @param index standard view index `StandardViewIndex.Top, Bottom, LEft, Right, Front, Back, Iso, LeftIso`
618
- * @param invert if false (default), the returned Matrix3d "projects" world vectors into XY view vectors. If true, it is inverted to map view vectors to world.
823
+ * @param index standard view index `StandardViewIndex.Top, Bottom, Left, Right, Front, Back, Iso, RightIso`
824
+ * @param invert if false (default), the return matrix is world to local (view) and if true, the the return
825
+ * matrix is local (view) to world.
619
826
  * @param result optional result.
620
827
  */
621
828
  static createStandardWorldToView(index, invert = false, result) {
622
829
  switch (index) {
830
+ // Start with TOP view, ccw rotation by 180 degrees around X
623
831
  case Geometry_1.StandardViewIndex.Bottom:
624
832
  result = Matrix3d.createRowValues(1, 0, 0, 0, -1, 0, 0, 0, -1);
625
833
  break;
834
+ // Start with TOP view, ccw rotation by -90 degrees around X and by 90 degrees around Z
626
835
  case Geometry_1.StandardViewIndex.Left:
627
836
  result = Matrix3d.createRowValues(0, -1, 0, 0, 0, 1, -1, 0, 0);
628
837
  break;
838
+ // Start with TOP view, ccw rotation by -90 degrees around X and by -90 degrees around Z
629
839
  case Geometry_1.StandardViewIndex.Right:
630
840
  result = Matrix3d.createRowValues(0, 1, 0, 0, 0, 1, 1, 0, 0);
631
841
  break;
632
- case Geometry_1.StandardViewIndex.Front: // 0-based 4
842
+ // Start with TOP view, ccw rotation by -90 degrees around X
843
+ case Geometry_1.StandardViewIndex.Front:
633
844
  result = Matrix3d.createRowValues(1, 0, 0, 0, 0, 1, 0, -1, 0);
634
845
  break;
635
- case Geometry_1.StandardViewIndex.Back: // 0-based 5
846
+ // Start with TOP view, ccw rotation by -90 degrees around X and by 180 degrees around Z
847
+ case Geometry_1.StandardViewIndex.Back:
636
848
  result = Matrix3d.createRowValues(-1, 0, 0, 0, 0, 1, 0, 1, 0);
637
849
  break;
850
+ /**
851
+ * Isometric view
852
+ * Start with FRONT view, ccw rotation by -45 degrees around Y and by arctan(1/sqrt(2)) ~ 35.264 degrees around X
853
+ * cos(45) = 1/sqrt(2) = 0.70710678118 and sin(45) = 1/sqrt(2) = 0.70710678118
854
+ * cos(35.264) = 2/sqrt(6) = 0.81649658092 and sin(35.264) = 1/sqrt(3) = 0.57735026919
855
+ * More info: https://en.wikipedia.org/wiki/Isometric_projection
856
+ */
638
857
  case Geometry_1.StandardViewIndex.Iso:
639
858
  result = Matrix3d.createRowValues(0.707106781186548, -0.70710678118654757, 0.00000000000000000, 0.408248290463863, 0.40824829046386302, 0.81649658092772603, -0.577350269189626, -0.57735026918962573, 0.57735026918962573);
640
859
  break;
860
+ // Start with FRONT view, ccw rotation by 45 degrees around Y and by 35.264 degrees around X
641
861
  case Geometry_1.StandardViewIndex.RightIso:
642
862
  result = Matrix3d.createRowValues(0.707106781186548, 0.70710678118654757, 0.00000000000000000, -0.408248290463863, 0.40824829046386302, 0.81649658092772603, 0.577350269189626, -0.57735026918962573, 0.57735026918962573);
643
863
  break;
864
+ // no rotation
644
865
  case Geometry_1.StandardViewIndex.Top:
645
866
  default:
646
867
  result = Matrix3d.createIdentity(result);
647
868
  }
648
869
  if (invert)
649
- result.transposeInPlace();
870
+ result.transposeInPlace(); // matrix is rigid so transpose and inverse are the same
650
871
  return result;
651
872
  }
652
- /*
653
- // this implementation has problems distinguishing failure (normalize) from small angle.
654
- public getAxisAndAngleOfRotation(): { axis: Vector3d, angle: Angle, error: boolean } {
655
-
656
- const result = { axis: Vector3d.unitZ(), angle: Angle.createRadians(0), error: true };
657
- if (this.isIdentity) {
658
- result.error = false;
659
- return result;
660
- }
661
- if (!this.isRigid())
662
- return result;
663
- const QMinusI = this.clone();
664
- QMinusI.coffs[0] -= 1.0;
665
- QMinusI.coffs[4] -= 1.0;
666
- QMinusI.coffs[8] -= 1.0;
667
- // Each column of (Q - I) is the motion of the corresponding axis vector
668
- // during the rotation.
669
- // Only one of the three axes can really be close to the rotation axis.
670
- const delta0 = QMinusI.columnX();
671
- const delta1 = QMinusI.columnY();
672
- const delta2 = QMinusI.columnZ();
673
- const cross01 = delta0.crossProduct(delta1);
674
- const cross12 = delta1.crossProduct(delta2);
675
- const cross20 = delta2.crossProduct(delta0);
676
-
677
- const aa01 = cross01.magnitudeSquared();
678
- const aa12 = cross12.magnitudeSquared();
679
- const aa20 = cross20.magnitudeSquared();
680
-
681
- const cross = cross01.clone(); // This will end up as the biggest cross product
682
- const v0 = delta0.clone(); // This will end up as one of the two largest delta vectors
683
- let aaMax = aa01;
684
- if (aa12 > aaMax) {
685
- cross.setFrom(cross12);
686
- aaMax = aa12;
687
- v0.setFrom(delta1);
688
- }
689
- if (aa20 > aaMax) {
690
- cross.setFrom(cross20);
691
- aaMax = aa20;
692
- v0.setFrom(delta2);
693
- }
694
-
695
- if (aaMax === 0.0) {
696
- // The vectors did not move. Just accept the zero rotation, with error flag set.
697
- return result;
698
- }
699
-
700
- v0.normalizeInPlace();
701
- // V0 is a unit vector perpendicular to the rotation axis.
702
- // Rotate it. Its image V1 is also a unit vector, and the angle from V0 to V1 is the quat angle.
703
- // CrossProduct is axis vector times sine of angle.
704
- // Dot Product is cosine of angle.
705
- // V2 is zero in 180 degree case, so we use the Cross from the search as the axis
706
- // as direction, being careful to keep sine positive.
707
- const v1 = this.multiplyVector(v0);
708
- const v2 = v0.crossProduct(v1);
709
- const sine = v2.magnitude();
710
- if (v2.dotProduct(cross) < 0.0)
711
- cross.scaleInPlace(-1.0);
712
- const cosine = v0.dotProduct(v1);
713
- result.angle.setRadians(Math.atan2(sine, cosine));
714
- result.axis.setFrom(cross);
715
- result.error = !result.axis.tryNormalizeInPlace();
716
- return result
717
- }
718
- */
873
+ /**
874
+ * Apply (in place) a jacobi update that zeros out this.at(i,j).
875
+ * @param i row index of zeroed member
876
+ * @param j column index of zeroed member
877
+ * @param k other row/column index (different from i and j)
878
+ * @param leftEigenVectors a matrix that its columns will be filled by eigenvectors of this Matrix3d
879
+ * (allocated by caller, computed and filled by this function)
880
+ */
881
+ applyFastSymmetricJacobiUpdate(i, j, k, leftEigenVectors) {
882
+ const indexII = 4 * i;
883
+ const indexJJ = 4 * j;
884
+ const indexIJ = 3 * i + j;
885
+ const indexIK = 3 * i + k;
886
+ const indexJK = 3 * j + k;
887
+ const dotUU = this.coffs[indexII];
888
+ const dotVV = this.coffs[indexJJ];
889
+ const dotUV = this.coffs[indexIJ];
890
+ const jacobi = Angle_1.Angle.trigValuesToHalfAngleTrigValues(dotUU - dotVV, 2.0 * dotUV);
891
+ if (Math.abs(dotUV) < 1.0e-15 * (dotUU + dotVV))
892
+ return 0.0;
893
+ const c = jacobi.c;
894
+ const s = jacobi.s;
895
+ const cc = c * c;
896
+ const ss = s * s;
897
+ const sc2 = 2.0 * c * s;
898
+ this.coffs[indexII] = cc * dotUU + sc2 * dotUV + ss * dotVV;
899
+ this.coffs[indexJJ] = ss * dotUU - sc2 * dotUV + cc * dotVV;
900
+ this.coffs[indexIJ] = 0.0;
901
+ const a = this.coffs[indexIK];
902
+ const b = this.coffs[indexJK];
903
+ this.coffs[indexIK] = a * c + b * s;
904
+ this.coffs[indexJK] = -s * a + c * b;
905
+ this.coffs[3 * j + i] = 0.0;
906
+ this.coffs[3 * k + i] = this.coffs[indexIK];
907
+ this.coffs[3 * k + j] = this.coffs[indexJK];
908
+ leftEigenVectors.applyGivensColumnOp(i, j, c, s);
909
+ return Math.abs(dotUV);
910
+ }
911
+ /**
912
+ * Factor this (symmetrized) as a product U * lambda * UT where U is orthogonal, lambda is diagonal.
913
+ * The upper triangle is mirrored to lower triangle to enforce symmetry.
914
+ * @param leftEigenvectors a matrix that its columns will be filled by eigenvectors of this Matrix3d
915
+ * (allocated by caller, computed and filled by this function)
916
+ * @param lambda a vector that its entries will be filled by eigenvalues of this Matrix3d
917
+ * (allocated by caller, computed and filled by this function)
918
+ */
919
+ fastSymmetricEigenvalues(leftEigenvectors, lambda) {
920
+ const matrix = this.clone();
921
+ leftEigenvectors.setIdentity();
922
+ const tolerance = 1.0e-12 * this.sumSquares();
923
+ for (let iteration = 0; iteration < 7; iteration++) {
924
+ const sum = matrix.applyFastSymmetricJacobiUpdate(0, 1, 2, leftEigenvectors)
925
+ + matrix.applyFastSymmetricJacobiUpdate(0, 2, 1, leftEigenvectors)
926
+ + matrix.applyFastSymmetricJacobiUpdate(1, 2, 0, leftEigenvectors);
927
+ // console.log("symmetric sum", sum);
928
+ // console.log ("sum", sum);
929
+ if (sum < tolerance) {
930
+ // console.log("symmetric iterations", iteration);
931
+ lambda.set(matrix.at(0, 0), matrix.at(1, 1), matrix.at(2, 2));
932
+ return true;
933
+ }
934
+ }
935
+ return false;
936
+ }
719
937
  /**
720
938
  * Compute the (unit vector) axis and angle of rotation.
721
- * @returns Returns with result.ok === true when the conversion succeeded.
939
+ * * math details can be found at docs/learning/geometry/Angle.md
940
+ * @returns Returns axis and angle of rotation with result.ok === true when the conversion succeeded.
722
941
  */
723
942
  getAxisAndAngleOfRotation() {
724
943
  const trace = this.coffs[0] + this.coffs[4] + this.coffs[8];
725
- // trace = (xx + yy * zz) * (1-c) + 3 * c = 1 + 2c ==> c = (trace-1) / 2
726
- const skewXY = this.coffs[3] - this.coffs[1]; // == 2sz
727
- const skewYZ = this.coffs[7] - this.coffs[5]; // == 2sx
728
- const skewZX = this.coffs[2] - this.coffs[6]; // == 2sy
729
- const c = (trace - 1.0) / 2.0;
730
- const s = Geometry_1.Geometry.hypotenuseXYZ(skewXY, skewYZ, skewZX) / 2.0;
731
- const e = c * c + s * s - 1.0;
944
+ const skewXY = this.coffs[3] - this.coffs[1]; // 2*z*sin
945
+ const skewYZ = this.coffs[7] - this.coffs[5]; // 2*y*sin
946
+ const skewZX = this.coffs[2] - this.coffs[6]; // 2*x*sin
947
+ // trace = (m00^2 + m11^2 + m22^2) * (1-cos) + 3cos = (1-cos) + 3cos = 1 + 2cos ==> cos = (trace-1) / 2
948
+ const c = (trace - 1.0) / 2.0; // cosine
949
+ const s = Geometry_1.Geometry.hypotenuseXYZ(skewXY, skewYZ, skewZX) / 2.0; // sine
950
+ const e = c * c + s * s - 1.0; // s^2 + c^2 = 1
951
+ // if s^2 + c^2 != 1 then we have a bad matrix so return false
732
952
  if (Math.abs(e) > Geometry_1.Geometry.smallAngleRadians) {
733
- // the sine and cosine are not a unit circle point. bad matrix . ..
734
953
  return { axis: Point3dVector3d_1.Vector3d.create(0, 0, 1), angle: Angle_1.Angle.createRadians(0), ok: false };
735
954
  }
955
+ // sin is close to 0 then we got to special cases (angle 0 or 180) which needs to be handled differently
736
956
  if (Math.abs(s) < Geometry_1.Geometry.smallAngleRadians) {
737
- // There is no significant skew.
738
- // The matrix is symmetric
739
- // So it has simple eigenvalues -- either (1,1,1) or (1,-1,-1).
740
- if (c > 0) // no rotation
957
+ if (c > 0) // sin = 0 and cos = 1 so angle = 0 (i.e., no rotation)
741
958
  return { axis: Point3dVector3d_1.Vector3d.create(0, 0, 1), angle: Angle_1.Angle.createRadians(0), ok: true };
742
- // 180 degree flip around some axis ?
743
- // Look for the simple case of a principal rotation ...
744
- // look for a pair of (-1) entries on the diagonal ...
959
+ /**
960
+ * If sin = 0 and cos = -1 then angle = 180 (i.e., 180 degree rotation around some axis)
961
+ * then the rotation matrix becomes
962
+ * 2x^2-1 2xy 2xz
963
+ * 2xy 2y^2-1 2yz
964
+ * 2xz 2yz 2z^2-1
965
+ * Note that the matrix is symmetric.
966
+ * If rotation is around one the standard basis then non-diagonal entries become 0 and we
967
+ * have one 1 and two -1s on the diagonal.
968
+ * If rotation is around an axis other than standard basis, then the axis is the eigenvector
969
+ * of the rotation matrix with eigenvalue = 1.
970
+ */
745
971
  const axx = this.coffs[0];
746
972
  const ayy = this.coffs[4];
747
973
  const azz = this.coffs[8];
748
- const theta180 = Angle_1.Angle.createDegrees(180);
749
- // Look for principal axis flips as a special case . ..
974
+ // Look for a pair of "-1" entries on the diagonal (for rotation around the basis X,Y,Z axis)
750
975
  if (Geometry_1.Geometry.isAlmostEqualNumber(-1.0, ayy) && Geometry_1.Geometry.isAlmostEqualNumber(-1, azz)) {
751
- // rotate around
752
- return { axis: Point3dVector3d_1.Vector3d.create(1, 0, 0), angle: theta180, ok: true };
976
+ return { axis: Point3dVector3d_1.Vector3d.create(1, 0, 0), angle: Angle_1.Angle.createDegrees(180), ok: true };
753
977
  }
754
978
  else if (Geometry_1.Geometry.isAlmostEqualNumber(-1.0, axx) && Geometry_1.Geometry.isAlmostEqualNumber(-1, azz)) {
755
- return { axis: Point3dVector3d_1.Vector3d.create(0, 1, 0), angle: theta180, ok: true };
979
+ return { axis: Point3dVector3d_1.Vector3d.create(0, 1, 0), angle: Angle_1.Angle.createDegrees(180), ok: true };
756
980
  }
757
981
  else if (Geometry_1.Geometry.isAlmostEqualNumber(-1.0, axx) && Geometry_1.Geometry.isAlmostEqualNumber(-1, ayy)) {
758
- return { axis: Point3dVector3d_1.Vector3d.create(0, 0, 1), angle: theta180, ok: true };
982
+ return { axis: Point3dVector3d_1.Vector3d.create(0, 0, 1), angle: Angle_1.Angle.createDegrees(180), ok: true };
759
983
  }
760
- // 180 degree flip around some other axis ...
761
- // eigenvalues will have 1.0 once, -1.0 twice.
762
- // These cases look for each place (x,y,z) that the 1.0 might appear.
763
- // But fastSymmetricEigenvalues reliably always seems to put the 1.0 as the x eigenvalue.
764
- // so only the getColumn(0) return seems reachable in unit tests.
984
+ // Look for eigenvector with eigenvalue = 1
765
985
  const eigenvectors = Matrix3d.createIdentity();
766
986
  const eigenvalues = Point3dVector3d_1.Vector3d.create(0, 0, 0);
767
987
  if (this.fastSymmetricEigenvalues(eigenvectors, eigenvalues)) {
768
988
  for (let axisIndex = 0; axisIndex < 2; axisIndex++) {
769
989
  const lambda = eigenvalues.at(axisIndex);
770
990
  if (Geometry_1.Geometry.isAlmostEqualNumber(1, lambda))
771
- return { axis: eigenvectors.getColumn(axisIndex), angle: theta180, ok: true };
991
+ return { axis: eigenvectors.getColumn(axisIndex), angle: Angle_1.Angle.createDegrees(180), ok: true };
772
992
  }
773
- // Don't know if this can be reached ....
993
+ // if no eigenvalue = 1 was found return false
774
994
  return { axis: Point3dVector3d_1.Vector3d.create(0, 0, 1), angle: Angle_1.Angle.createRadians(0), ok: false };
775
995
  }
996
+ // if no axis was found return false
776
997
  return { axis: Point3dVector3d_1.Vector3d.create(0, 0, 1), angle: Angle_1.Angle.createRadians(0), ok: false };
777
998
  }
999
+ // good matrix and non-zero sine
778
1000
  const a = 1.0 / (2.0 * s);
779
- const result = { axis: Point3dVector3d_1.Vector3d.create(skewYZ * a, skewZX * a, skewXY * a), angle: Angle_1.Angle.createAtan2(s, c), ok: true };
780
- return result;
781
- }
782
- /**
783
- * Returns a matrix that rotates from vectorA to vectorB.
784
- */
785
- static createRotationVectorToVector(vectorA, vectorB, result) {
786
- return this.createPartialRotationVectorToVector(vectorA, 1.0, vectorB, result);
787
- }
788
- /**
789
- * Return a matrix that rotates a fraction of the angular sweep from vectorA to vectorB.
790
- * @param vectorA initial vector position
791
- * @param fraction fractional rotation. 1.0 is "all the way"
792
- * @param vectorB final vector position
793
- * @param result optional result matrix.
794
- */
795
- static createPartialRotationVectorToVector(vectorA, fraction, vectorB, result) {
796
- let upVector = vectorA.unitCrossProduct(vectorB);
797
- if (upVector) { // the usual case --
798
- return Matrix3d.createRotationAroundVector(upVector, Angle_1.Angle.createRadians(fraction * vectorA.planarAngleTo(vectorB, upVector).radians));
799
- }
800
- // fail if either vector is zero ...
801
- if (Geometry_1.Geometry.isSmallMetricDistance(vectorA.magnitude())
802
- || Geometry_1.Geometry.isSmallMetricDistance(vectorB.magnitude()))
803
- return undefined;
804
- // nonzero but aligned vectors ...
805
- if (vectorA.dotProduct(vectorB) > 0.0)
806
- return Matrix3d.createIdentity(result);
807
- // nonzero opposing vectors ..
808
- upVector = Matrix3d.createPerpendicularVectorFavorPlaneContainingZ(vectorA, upVector);
809
- return Matrix3d.createRotationAroundVector(upVector, Angle_1.Angle.createRadians(fraction * Math.PI));
810
- }
811
- /** Create a 90 degree rotation around a principal axis */
812
- static create90DegreeRotationAroundAxis(axisIndex) {
813
- axisIndex = Geometry_1.Geometry.cyclic3dAxis(axisIndex);
814
- if (axisIndex === 0) {
815
- const retVal = Matrix3d.createRowValues(1, 0, 0, 0, 0, -1, 0, 1, 0);
816
- retVal.setupInverseTranspose();
817
- return retVal;
818
- }
819
- else if (axisIndex === 1) {
820
- const retVal = Matrix3d.createRowValues(0, 0, 1, 0, 1, 0, -1, 0, 0);
821
- retVal.setupInverseTranspose();
822
- return retVal;
823
- }
824
- else {
825
- const retVal = Matrix3d.createRowValues(0, -1, 0, 1, 0, 0, 0, 0, 1);
826
- retVal.setupInverseTranspose();
827
- return retVal;
828
- }
829
- }
830
- /** Return (a copy of) the X column */
831
- columnX(result) { return Point3dVector3d_1.Vector3d.create(this.coffs[0], this.coffs[3], this.coffs[6], result); }
832
- /** Return (a copy of)the Y column */
833
- columnY(result) { return Point3dVector3d_1.Vector3d.create(this.coffs[1], this.coffs[4], this.coffs[7], result); }
834
- /** Return (a copy of)the Z column */
835
- columnZ(result) { return Point3dVector3d_1.Vector3d.create(this.coffs[2], this.coffs[5], this.coffs[8], result); }
836
- /** Return the X column magnitude squared */
837
- columnXMagnitudeSquared() { return Geometry_1.Geometry.hypotenuseSquaredXYZ(this.coffs[0], this.coffs[3], this.coffs[6]); }
838
- /** Return the Y column magnitude squared */
839
- columnYMagnitudeSquared() { return Geometry_1.Geometry.hypotenuseSquaredXYZ(this.coffs[1], this.coffs[4], this.coffs[7]); }
840
- /** Return the Z column magnitude squared */
841
- columnZMagnitudeSquared() { return Geometry_1.Geometry.hypotenuseSquaredXYZ(this.coffs[2], this.coffs[5], this.coffs[8]); }
842
- /** Return the X column magnitude */
843
- columnXMagnitude() { return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[3], this.coffs[6]); }
844
- /** Return the Y column magnitude */
845
- columnYMagnitude() { return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[1], this.coffs[4], this.coffs[7]); }
846
- /** Return the Z column magnitude */
847
- columnZMagnitude() { return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[2], this.coffs[5], this.coffs[8]); }
848
- /** Return magnitude of columnX cross columnY. */
849
- columnXYCrossProductMagnitude() {
850
- return Geometry_1.Geometry.crossProductMagnitude(this.coffs[0], this.coffs[3], this.coffs[6], this.coffs[1], this.coffs[4], this.coffs[7]);
851
- }
852
- /** Return the X row magnitude d */
853
- rowXMagnitude() { return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[1], this.coffs[2]); }
854
- /** Return the Y row magnitude */
855
- rowYMagnitude() { return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[3], this.coffs[4], this.coffs[5]); }
856
- /** Return the Z row magnitude */
857
- rowZMagnitude() { return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[6], this.coffs[7], this.coffs[8]); }
858
- /** Return the dot product of column X with column Y */
859
- /** Return the dot product of column X with column Y */
860
- columnXDotColumnY() {
861
- return this.coffs[0] * this.coffs[1]
862
- + this.coffs[3] * this.coffs[4]
863
- + this.coffs[6] * this.coffs[7];
864
- }
865
- /**
866
- * Dot product of an indexed column with a vector given as x,y,z
867
- * @param columnIndex index of column. Must be 0,1,2
868
- * @param x x component of vector
869
- * @param y y component of vector
870
- * @param z z component of vector
871
- */
872
- columnDotXYZ(columnIndex, x, y, z) {
873
- return this.coffs[columnIndex] * x + this.coffs[columnIndex + 3] * y + this.coffs[columnIndex + 6] * z;
874
- }
875
- /** Return (a copy of) the X row */
876
- rowX(result) { return Point3dVector3d_1.Vector3d.create(this.coffs[0], this.coffs[1], this.coffs[2], result); }
877
- /** Return (a copy of) the Y row */
878
- rowY(result) { return Point3dVector3d_1.Vector3d.create(this.coffs[3], this.coffs[4], this.coffs[5], result); }
879
- /** Return (a copy of) the Z row */
880
- rowZ(result) { return Point3dVector3d_1.Vector3d.create(this.coffs[6], this.coffs[7], this.coffs[8], result); }
881
- /** Return the dot product of the vector parameter with the X column. */
882
- dotColumnX(vector) { return vector.x * this.coffs[0] + vector.y * this.coffs[3] + vector.z * this.coffs[6]; }
883
- /** Return the dot product of the vector parameter with the Y column. */
884
- dotColumnY(vector) { return vector.x * this.coffs[1] + vector.y * this.coffs[4] + vector.z * this.coffs[7]; }
885
- /** Return the dot product of the vector parameter with the Z column. */
886
- dotColumnZ(vector) { return vector.x * this.coffs[2] + vector.y * this.coffs[5] + vector.z * this.coffs[8]; }
887
- /** Return the dot product of the vector parameter with the X row. */
888
- dotRowX(vector) { return vector.x * this.coffs[0] + vector.y * this.coffs[1] + vector.z * this.coffs[2]; }
889
- /** Return the dot product of the vector parameter with the Y row. */
890
- dotRowY(vector) { return vector.x * this.coffs[3] + vector.y * this.coffs[4] + vector.z * this.coffs[5]; }
891
- /** Return the dot product of the vector parameter with the Z row. */
892
- dotRowZ(vector) { return vector.x * this.coffs[6] + vector.y * this.coffs[7] + vector.z * this.coffs[8]; }
893
- // cSpell:words XXYZ YXYZ ZXYZ XYZAs Eigen
894
- /** Return the dot product of the x,y,z with the X row. */
895
- dotRowXXYZ(x, y, z) { return x * this.coffs[0] + y * this.coffs[1] + z * this.coffs[2]; }
896
- /** Return the dot product of the x,y,z with the Y row. */
897
- dotRowYXYZ(x, y, z) { return x * this.coffs[3] + y * this.coffs[4] + z * this.coffs[5]; }
898
- /** Return the dot product of the x,y,z with the Z row. */
899
- dotRowZXYZ(x, y, z) { return x * this.coffs[6] + y * this.coffs[7] + z * this.coffs[8]; }
900
- /** Return the (vector) cross product of the Z column with the vector parameter. */
901
- columnZCrossVector(vector, result) {
902
- return Geometry_1.Geometry.crossProductXYZXYZ(this.coffs[2], this.coffs[5], this.coffs[8], vector.x, vector.y, vector.z, result);
903
- }
904
- /**
905
- * Replace current rows Ui Uj with (c*Ui - s*Uj) and (c*Uj + s*Ui).
906
- * @param i first row index. must be 0,1,2 (unchecked)
907
- * @param j second row index. must be 0,1,2 (unchecked)
908
- * @param c fist coefficient
909
- * @param s second coefficient
910
- */
911
- applyGivensRowOp(i, j, c, s) {
912
- let ii = 3 * i;
913
- let jj = 3 * j;
914
- const limit = ii + 3;
915
- for (; ii < limit; ii++, jj++) {
916
- const a = this.coffs[ii];
917
- const b = this.coffs[jj];
918
- this.coffs[ii] = a * c + b * s;
919
- this.coffs[jj] = -a * s + b * c;
920
- }
921
- }
922
- /**
923
- * Replace current columns Ui Uj with (c*Ui - s*Uj) and (c*Uj + s*Ui).
924
- * * This is used in compute intensive inner loops -- there is no checking for i,j being 0,1,2.
925
- * @param i first row index. must be 0,1,2 (unchecked)
926
- * @param j second row index. must be 0,1,2 (unchecked)
927
- * @param c fist coefficient
928
- * @param s second coefficient
929
- */
930
- applyGivensColumnOp(i, j, c, s) {
931
- const limit = i + 9;
932
- for (; i < limit; i += 3, j += 3) {
933
- const a = this.coffs[i];
934
- const b = this.coffs[j];
935
- this.coffs[i] = a * c + b * s;
936
- this.coffs[j] = -a * s + b * c;
937
- }
938
- }
939
- /**
940
- * create a rigid coordinate frame column z parallel to (_x_,_y_,_z_) and column x in the xy plane.
941
- * * column z points from origin to x,y,z
942
- * * column x is perpendicular and in the xy plane
943
- * * column y is perpendicular to both. It is the "up" vector on the view plane.
944
- * * Multiplying a world vector times the transpose of this matrix transforms into the view xy
945
- * * Multiplying the matrix times the an in-view vector transforms the vector to world.
946
- * @param x eye x coordinate
947
- * @param y eye y coordinate
948
- * @param z eye z coordinate
949
- * @param result
950
- */
951
- static createRigidViewAxesZTowardsEye(x, y, z, result) {
952
- result = Matrix3d.createIdentity(result);
953
- const rxy = Geometry_1.Geometry.hypotenuseXY(x, y);
954
- if (Geometry_1.Geometry.isSmallMetricDistance(rxy)) {
955
- // special case for top or bottom view.
956
- if (z < 0.0)
957
- result.scaleColumnsInPlace(1.0, -1, -1.0);
958
- }
959
- else {
960
- // const d = Geometry.hypotenuseSquaredXYZ(x, y, z);
961
- const c = x / rxy;
962
- const s = y / rxy;
963
- result.setRowValues(-s, 0, c, c, 0, s, 0, 1, 0);
964
- if (z !== 0.0) {
965
- const r = Geometry_1.Geometry.hypotenuseXYZ(x, y, z);
966
- const s1 = z / r;
967
- const c1 = rxy / r;
968
- result.applyGivensColumnOp(1, 2, c1, -s1);
969
- }
970
- }
1001
+ const result = {
1002
+ axis: Point3dVector3d_1.Vector3d.create(skewYZ * a, skewZX * a, skewXY * a),
1003
+ angle: Angle_1.Angle.createAtan2(s, c),
1004
+ ok: true,
1005
+ };
971
1006
  return result;
972
1007
  }
973
1008
  /** Rotate so columns i and j become perpendicular */
@@ -999,8 +1034,7 @@ class Matrix3d {
999
1034
  factorPerpendicularColumns(matrixC, matrixU) {
1000
1035
  matrixC.setFrom(this);
1001
1036
  matrixU.setIdentity();
1002
- const ss = this.sumSquares();
1003
- const tolerance = 1.0e-12 * ss;
1037
+ const tolerance = 1.0e-12 * this.sumSquares();
1004
1038
  for (let iteration = 0; iteration < 7; iteration++) {
1005
1039
  const sum = matrixC.applyJacobiColumnRotation(0, 1, matrixU)
1006
1040
  + matrixC.applyJacobiColumnRotation(0, 2, matrixU)
@@ -1058,90 +1092,26 @@ class Matrix3d {
1058
1092
  const uDotU = lambda.at(i, i);
1059
1093
  const vDotV = lambda.at(j, j);
1060
1094
  const uDotV = lambda.at(i, j);
1061
- if (Math.abs(uDotV) < 1.0e-15 * (uDotU + vDotV))
1062
- return 0.0;
1063
- // const c2 = uDotU - vDotV;
1064
- // const s2 = 2.0 * uDotV;
1065
- const jacobi = Angle_1.Angle.trigValuesToHalfAngleTrigValues(uDotU - vDotV, 2.0 * uDotV);
1066
- // const h = Math.hypot(c2, s2);
1067
- // console.log(" c2 s2", c2 / h, s2 / h);
1068
- // console.log(" C S ", Math.cos(2 * jacobi.radians), Math.sin(2 * jacobi.radians));
1069
- // console.log("i j uDotV", i, j, uDotV);
1070
- if (Math.abs(jacobi.s) < 2.0e-15)
1071
- return 0.0;
1072
- // Factored form is this *lambda * thisTranspose
1073
- // Let Q be the rotation matrix. Q*QT is inserted, viz
1074
- // this*Q * QT * lambda * Q*thisTranspose
1075
- this.applyGivensColumnOp(i, j, jacobi.c, jacobi.s);
1076
- lambda.applyGivensRowOp(i, j, jacobi.c, jacobi.s);
1077
- lambda.applyGivensColumnOp(i, j, jacobi.c, jacobi.s);
1078
- // const BTB = this.multiplyMatrixTransposeMatrix(this);
1079
- // console.log("BTB", BTB.at(0, 0), BTB.at(1, 1), BTB.at(2, 2), " off", BTB.at(0, 1), BTB.at(0, 2), BTB.at(1, 2), " at(i,j)", BTB.at(i, j));
1080
- return Math.abs(uDotV);
1081
- }
1082
- /**
1083
- * Factor this (symmetrized) as a product U * lambda * UT where U is orthogonal, lambda is diagonal.
1084
- * The upper triangle is mirrored to lower triangle to enforce symmetry.
1085
- * @param matrixC (allocate by caller, computed here)
1086
- * @param factor (allocate by caller, computed here)
1087
- */
1088
- symmetricEigenvalues(leftEigenvectors, lambda) {
1089
- const matrix = this.clone();
1090
- leftEigenvectors.setIdentity();
1091
- matrix.coffs[3] = matrix.coffs[1];
1092
- matrix.coffs[6] = matrix.coffs[2];
1093
- matrix.coffs[7] = matrix.coffs[5];
1094
- const ss = this.sumSquares();
1095
- const tolerance = 1.0e-12 * ss;
1096
- for (let iteration = 0; iteration < 7; iteration++) {
1097
- const sum = leftEigenvectors.applySymmetricJacobi(0, 1, matrix)
1098
- + leftEigenvectors.applySymmetricJacobi(0, 2, matrix)
1099
- + leftEigenvectors.applySymmetricJacobi(1, 2, matrix);
1100
- // console.log("symmetric sum", sum);
1101
- // console.log (" sum", sum);
1102
- if (sum < tolerance) {
1103
- // console.log("symmetric iterations", iteration);
1104
- lambda.set(matrix.at(0, 0), matrix.at(1, 1), matrix.at(2, 2));
1105
- return true;
1106
- }
1107
- }
1108
- return false;
1109
- }
1110
- /** Apply (in place a jacobi update that zeros out this.at(i,j).
1111
- *
1112
- */
1113
- applyFastSymmetricJacobiUpdate(i, // row index of zeroed member
1114
- j, // column index of zeroed member
1115
- k, // other row/column index (different from i and j)
1116
- leftEigenVectors) {
1117
- const indexII = 4 * i;
1118
- const indexJJ = 4 * j;
1119
- const indexIJ = 3 * i + j;
1120
- const indexIK = 3 * i + k;
1121
- const indexJK = 3 * j + k;
1122
- const dotUU = this.coffs[indexII];
1123
- const dotVV = this.coffs[indexJJ];
1124
- const dotUV = this.coffs[indexIJ];
1125
- const jacobi = Angle_1.Angle.trigValuesToHalfAngleTrigValues(dotUU - dotVV, 2.0 * dotUV);
1126
- if (Math.abs(dotUV) < 1.0e-15 * (dotUU + dotVV))
1095
+ if (Math.abs(uDotV) < 1.0e-15 * (uDotU + vDotV))
1127
1096
  return 0.0;
1128
- const c = jacobi.c;
1129
- const s = jacobi.s;
1130
- const cc = c * c;
1131
- const ss = s * s;
1132
- const sc2 = 2.0 * c * s;
1133
- this.coffs[indexII] = cc * dotUU + sc2 * dotUV + ss * dotVV;
1134
- this.coffs[indexJJ] = ss * dotUU - sc2 * dotUV + cc * dotVV;
1135
- this.coffs[indexIJ] = 0.0;
1136
- const a = this.coffs[indexIK];
1137
- const b = this.coffs[indexJK];
1138
- this.coffs[indexIK] = a * c + b * s;
1139
- this.coffs[indexJK] = -s * a + c * b;
1140
- this.coffs[3 * j + i] = 0.0;
1141
- this.coffs[3 * k + i] = this.coffs[indexIK];
1142
- this.coffs[3 * k + j] = this.coffs[indexJK];
1143
- leftEigenVectors.applyGivensColumnOp(i, j, c, s);
1144
- return Math.abs(dotUV);
1097
+ // const c2 = uDotU - vDotV;
1098
+ // const s2 = 2.0 * uDotV;
1099
+ const jacobi = Angle_1.Angle.trigValuesToHalfAngleTrigValues(uDotU - vDotV, 2.0 * uDotV);
1100
+ // const h = Math.hypot(c2, s2);
1101
+ // console.log(" c2 s2", c2 / h, s2 / h);
1102
+ // console.log(" C S ", Math.cos(2 * jacobi.radians), Math.sin(2 * jacobi.radians));
1103
+ // console.log("i j uDotV", i, j, uDotV);
1104
+ if (Math.abs(jacobi.s) < 2.0e-15)
1105
+ return 0.0;
1106
+ // Factored form is this *lambda * thisTranspose
1107
+ // Let Q be the rotation matrix. Q*QT is inserted, viz
1108
+ // this*Q * QT * lambda * Q*thisTranspose
1109
+ this.applyGivensColumnOp(i, j, jacobi.c, jacobi.s);
1110
+ lambda.applyGivensRowOp(i, j, jacobi.c, jacobi.s);
1111
+ lambda.applyGivensColumnOp(i, j, jacobi.c, jacobi.s);
1112
+ // const BTB = this.multiplyMatrixTransposeMatrix(this);
1113
+ // console.log("BTB", BTB.at(0, 0), BTB.at(1, 1), BTB.at(2, 2), " off", BTB.at(0, 1), BTB.at(0, 2), BTB.at(1, 2), " at(i,j)", BTB.at(i, j));
1114
+ return Math.abs(uDotV);
1145
1115
  }
1146
1116
  /**
1147
1117
  * Factor this (symmetrized) as a product U * lambda * UT where U is orthogonal, lambda is diagonal.
@@ -1149,15 +1119,17 @@ class Matrix3d {
1149
1119
  * @param matrixC (allocate by caller, computed here)
1150
1120
  * @param factor (allocate by caller, computed here)
1151
1121
  */
1152
- fastSymmetricEigenvalues(leftEigenvectors, lambda) {
1122
+ symmetricEigenvalues(leftEigenvectors, lambda) {
1153
1123
  const matrix = this.clone();
1154
1124
  leftEigenvectors.setIdentity();
1155
- const ss = this.sumSquares();
1156
- const tolerance = 1.0e-12 * ss;
1125
+ matrix.coffs[3] = matrix.coffs[1];
1126
+ matrix.coffs[6] = matrix.coffs[2];
1127
+ matrix.coffs[7] = matrix.coffs[5];
1128
+ const tolerance = 1.0e-12 * this.sumSquares();
1157
1129
  for (let iteration = 0; iteration < 7; iteration++) {
1158
- const sum = matrix.applyFastSymmetricJacobiUpdate(0, 1, 2, leftEigenvectors)
1159
- + matrix.applyFastSymmetricJacobiUpdate(0, 2, 1, leftEigenvectors)
1160
- + matrix.applyFastSymmetricJacobiUpdate(1, 2, 0, leftEigenvectors);
1130
+ const sum = leftEigenvectors.applySymmetricJacobi(0, 1, matrix)
1131
+ + leftEigenvectors.applySymmetricJacobi(0, 2, matrix)
1132
+ + leftEigenvectors.applySymmetricJacobi(1, 2, matrix);
1161
1133
  // console.log("symmetric sum", sum);
1162
1134
  // console.log (" sum", sum);
1163
1135
  if (sum < tolerance) {
@@ -1169,32 +1141,180 @@ class Matrix3d {
1169
1141
  return false;
1170
1142
  }
1171
1143
  /**
1172
- * Create a matrix from column vectors.
1173
- * ```
1174
- * equation
1175
- * \begin{bmatrix}U_x & V_x & W_x \\ U_y & V_y & W_y \\ U_z & V_z & W_z \end{bmatrix}
1176
- * ```
1144
+ * Return a matrix that rotates a fraction of the angular sweep from vectorA to vectorB.
1145
+ * @param vectorA initial vector position
1146
+ * @param fraction fractional rotation (1 means rotate all the way)
1147
+ * @param vectorB final vector position
1148
+ * @param result optional result matrix.
1177
1149
  */
1178
- static createColumns(vectorU, vectorV, vectorW, result) {
1179
- return Matrix3d.createRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, vectorU.z, vectorV.z, vectorW.z, result);
1150
+ static createPartialRotationVectorToVector(vectorA, fraction, vectorB, result) {
1151
+ let upVector = vectorA.unitCrossProduct(vectorB);
1152
+ // the usual case (both vectors and also their cross product is non-zero)
1153
+ if (upVector) {
1154
+ return Matrix3d.createRotationAroundVector(upVector, Angle_1.Angle.createRadians(fraction * vectorA.planarAngleTo(vectorB, upVector).radians));
1155
+ }
1156
+ // if either vector is zero
1157
+ if (Geometry_1.Geometry.isSmallMetricDistance(vectorA.magnitude())
1158
+ || Geometry_1.Geometry.isSmallMetricDistance(vectorB.magnitude()))
1159
+ return undefined;
1160
+ // aligned vectors (cross product = 0, dot product > 0)
1161
+ if (vectorA.dotProduct(vectorB) > 0.0)
1162
+ return Matrix3d.createIdentity(result);
1163
+ // opposing vectors (cross product = 0, dot product < 0)
1164
+ upVector = Matrix3d.createPerpendicularVectorFavorPlaneContainingZ(vectorA, upVector);
1165
+ return Matrix3d.createRotationAroundVector(upVector, Angle_1.Angle.createRadians(fraction * Math.PI));
1180
1166
  }
1181
- /** Create a matrix with each column's _x,y_ parts given `XAndY` and separate numeric z values.
1182
- * ```
1183
- * equation
1184
- * \begin{bmatrix}U_x & V_x & W_x \\ U_y & V_y & W_y \\ u & v & w \end{bmatrix}
1185
- * ```
1167
+ /** Returns a matrix that rotates from vectorA to vectorB. */
1168
+ static createRotationVectorToVector(vectorA, vectorB, result) {
1169
+ return this.createPartialRotationVectorToVector(vectorA, 1.0, vectorB, result);
1170
+ }
1171
+ /** Create a 90 degree rotation around a principal axis */
1172
+ static create90DegreeRotationAroundAxis(axisIndex) {
1173
+ axisIndex = Geometry_1.Geometry.cyclic3dAxis(axisIndex);
1174
+ if (axisIndex === 0) {
1175
+ const retVal = Matrix3d.createRowValues(1, 0, 0, 0, 0, -1, 0, 1, 0);
1176
+ retVal.setupInverseTranspose();
1177
+ return retVal;
1178
+ }
1179
+ else if (axisIndex === 1) {
1180
+ const retVal = Matrix3d.createRowValues(0, 0, 1, 0, 1, 0, -1, 0, 0);
1181
+ retVal.setupInverseTranspose();
1182
+ return retVal;
1183
+ }
1184
+ else {
1185
+ const retVal = Matrix3d.createRowValues(0, -1, 0, 1, 0, 0, 0, 0, 1);
1186
+ retVal.setupInverseTranspose();
1187
+ return retVal;
1188
+ }
1189
+ }
1190
+ /** Return (a copy of) the X column */
1191
+ columnX(result) {
1192
+ return Point3dVector3d_1.Vector3d.create(this.coffs[0], this.coffs[3], this.coffs[6], result);
1193
+ }
1194
+ /** Return (a copy of) the Y column */
1195
+ columnY(result) {
1196
+ return Point3dVector3d_1.Vector3d.create(this.coffs[1], this.coffs[4], this.coffs[7], result);
1197
+ }
1198
+ /** Return (a copy of) the Z column */
1199
+ columnZ(result) {
1200
+ return Point3dVector3d_1.Vector3d.create(this.coffs[2], this.coffs[5], this.coffs[8], result);
1201
+ }
1202
+ /** Return the X column magnitude squared */
1203
+ columnXMagnitudeSquared() {
1204
+ return Geometry_1.Geometry.hypotenuseSquaredXYZ(this.coffs[0], this.coffs[3], this.coffs[6]);
1205
+ }
1206
+ /** Return the Y column magnitude squared */
1207
+ columnYMagnitudeSquared() {
1208
+ return Geometry_1.Geometry.hypotenuseSquaredXYZ(this.coffs[1], this.coffs[4], this.coffs[7]);
1209
+ }
1210
+ /** Return the Z column magnitude squared */
1211
+ columnZMagnitudeSquared() {
1212
+ return Geometry_1.Geometry.hypotenuseSquaredXYZ(this.coffs[2], this.coffs[5], this.coffs[8]);
1213
+ }
1214
+ /** Return the X column magnitude */
1215
+ columnXMagnitude() {
1216
+ return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[3], this.coffs[6]);
1217
+ }
1218
+ /** Return the Y column magnitude */
1219
+ columnYMagnitude() {
1220
+ return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[1], this.coffs[4], this.coffs[7]);
1221
+ }
1222
+ /** Return the Z column magnitude */
1223
+ columnZMagnitude() {
1224
+ return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[2], this.coffs[5], this.coffs[8]);
1225
+ }
1226
+ /** Return magnitude of columnX cross columnY. */
1227
+ columnXYCrossProductMagnitude() {
1228
+ return Geometry_1.Geometry.crossProductMagnitude(this.coffs[0], this.coffs[3], this.coffs[6], this.coffs[1], this.coffs[4], this.coffs[7]);
1229
+ }
1230
+ /** Return the X row magnitude */
1231
+ rowXMagnitude() {
1232
+ return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[0], this.coffs[1], this.coffs[2]);
1233
+ }
1234
+ /** Return the Y row magnitude */
1235
+ rowYMagnitude() {
1236
+ return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[3], this.coffs[4], this.coffs[5]);
1237
+ }
1238
+ /** Return the Z row magnitude */
1239
+ rowZMagnitude() {
1240
+ return Geometry_1.Geometry.hypotenuseXYZ(this.coffs[6], this.coffs[7], this.coffs[8]);
1241
+ }
1242
+ /** Return the dot product of column X with column Y */
1243
+ columnXDotColumnY() {
1244
+ return this.coffs[0] * this.coffs[1]
1245
+ + this.coffs[3] * this.coffs[4]
1246
+ + this.coffs[6] * this.coffs[7];
1247
+ }
1248
+ /**
1249
+ * Dot product of an indexed column with a vector given as x,y,z
1250
+ * @param columnIndex index of column. Must be 0,1,2.
1251
+ * @param x x component of vector
1252
+ * @param y y component of vector
1253
+ * @param z z component of vector
1186
1254
  */
1187
- static createColumnsXYW(vectorU, u, vectorV, v, vectorW, w, result) {
1188
- return Matrix3d.createRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, u, v, w, result);
1255
+ columnDotXYZ(columnIndex, x, y, z) {
1256
+ return this.coffs[columnIndex] * x + this.coffs[columnIndex + 3] * y + this.coffs[columnIndex + 6] * z;
1257
+ }
1258
+ /** Return (a copy of) the X row */
1259
+ rowX(result) {
1260
+ return Point3dVector3d_1.Vector3d.create(this.coffs[0], this.coffs[1], this.coffs[2], result);
1261
+ }
1262
+ /** Return (a copy of) the Y row */
1263
+ rowY(result) {
1264
+ return Point3dVector3d_1.Vector3d.create(this.coffs[3], this.coffs[4], this.coffs[5], result);
1265
+ }
1266
+ /** Return (a copy of) the Z row */
1267
+ rowZ(result) {
1268
+ return Point3dVector3d_1.Vector3d.create(this.coffs[6], this.coffs[7], this.coffs[8], result);
1269
+ }
1270
+ /** Return the dot product of the vector parameter with the X column. */
1271
+ dotColumnX(vector) {
1272
+ return vector.x * this.coffs[0] + vector.y * this.coffs[3] + vector.z * this.coffs[6];
1273
+ }
1274
+ /** Return the dot product of the vector parameter with the Y column. */
1275
+ dotColumnY(vector) {
1276
+ return vector.x * this.coffs[1] + vector.y * this.coffs[4] + vector.z * this.coffs[7];
1277
+ }
1278
+ /** Return the dot product of the vector parameter with the Z column. */
1279
+ dotColumnZ(vector) {
1280
+ return vector.x * this.coffs[2] + vector.y * this.coffs[5] + vector.z * this.coffs[8];
1281
+ }
1282
+ /** Return the dot product of the vector parameter with the X row. */
1283
+ dotRowX(vector) {
1284
+ return vector.x * this.coffs[0] + vector.y * this.coffs[1] + vector.z * this.coffs[2];
1285
+ }
1286
+ /** Return the dot product of the vector parameter with the Y row. */
1287
+ dotRowY(vector) {
1288
+ return vector.x * this.coffs[3] + vector.y * this.coffs[4] + vector.z * this.coffs[5];
1289
+ }
1290
+ /** Return the dot product of the vector parameter with the Z row. */
1291
+ dotRowZ(vector) {
1292
+ return vector.x * this.coffs[6] + vector.y * this.coffs[7] + vector.z * this.coffs[8];
1293
+ }
1294
+ /** Return the dot product of the x,y,z with the X row. */
1295
+ dotRowXXYZ(x, y, z) {
1296
+ return x * this.coffs[0] + y * this.coffs[1] + z * this.coffs[2];
1297
+ }
1298
+ /** Return the dot product of the x,y,z with the Y row. */
1299
+ dotRowYXYZ(x, y, z) {
1300
+ return x * this.coffs[3] + y * this.coffs[4] + z * this.coffs[5];
1301
+ }
1302
+ /** Return the dot product of the x,y,z with the Z row. */
1303
+ dotRowZXYZ(x, y, z) {
1304
+ return x * this.coffs[6] + y * this.coffs[7] + z * this.coffs[8];
1189
1305
  }
1190
- /** Install data from xyz parts of Point4d (w part of Point4d ignored) */
1306
+ /** Return the cross product of the Z column with the vector parameter. */
1307
+ columnZCrossVector(vector, result) {
1308
+ return Geometry_1.Geometry.crossProductXYZXYZ(this.coffs[2], this.coffs[5], this.coffs[8], vector.x, vector.y, vector.z, result);
1309
+ }
1310
+ /** Set data from xyz parts of Point4d (w part of Point4d ignored) */
1191
1311
  setColumnsPoint4dXYZ(vectorU, vectorV, vectorW) {
1192
1312
  this.inverseState = InverseMatrixState.unknown;
1193
1313
  this.setRowValues(vectorU.x, vectorV.x, vectorW.x, vectorU.y, vectorV.y, vectorW.y, vectorU.z, vectorV.z, vectorW.z);
1194
1314
  }
1195
1315
  /**
1196
- * set entries in one column of the matrix.
1197
- * @param columnIndex column index. this is interpreted cyclically.
1316
+ * Set entries in one column of the matrix.
1317
+ * @param columnIndex column index (this is interpreted cyclically. See Geometry.cyclic3dAxis for more info).
1198
1318
  * @param value x,yz, values for column. If undefined, zeros are installed.
1199
1319
  */
1200
1320
  setColumn(columnIndex, value) {
@@ -1211,16 +1331,22 @@ class Matrix3d {
1211
1331
  this.coffs[index + 6] = 0.0;
1212
1332
  }
1213
1333
  }
1214
- /** Set all columns of the matrix. Any undefined vector is zeros. */
1334
+ /**
1335
+ * Set all columns of the matrix. Any undefined vector is zeros.
1336
+ * @param vectorX values for column 0
1337
+ * @param vectorY values for column 1
1338
+ * @param vectorZ optional values for column 2 (it's optional in case column 2 is 000, which is a
1339
+ * projection onto the xy-plane)
1340
+ */
1215
1341
  setColumns(vectorX, vectorY, vectorZ) {
1216
1342
  this.setColumn(0, vectorX);
1217
1343
  this.setColumn(1, vectorY);
1218
1344
  this.setColumn(2, vectorZ);
1219
1345
  }
1220
1346
  /**
1221
- * set entries in one row of the matrix.
1222
- * @param rowIndex row index. this is interpreted cyclically.
1223
- * @param value x,yz, values for row. If undefined, zeros are installed.
1347
+ * Set entries in one row of the matrix.
1348
+ * @param rowIndex row index. This is interpreted cyclically (using Geometry.cyclic3dAxis).
1349
+ * @param value x,y,z values for row.
1224
1350
  */
1225
1351
  setRow(rowIndex, value) {
1226
1352
  const index = 3 * Geometry_1.Geometry.cyclic3dAxis(rowIndex);
@@ -1229,29 +1355,26 @@ class Matrix3d {
1229
1355
  this.coffs[index + 2] = value.z;
1230
1356
  this.inverseState = InverseMatrixState.unknown;
1231
1357
  }
1232
- /** Return a (copy of) a column of the matrix.
1233
- * @param i column index. This is corrected to 012 by Geometry.cyclic3dAxis.
1358
+ /**
1359
+ * Return (a copy of) a column of the matrix.
1360
+ * @param i column index. This is interpreted cyclically (using Geometry.cyclic3dAxis).
1361
+ * @param result optional preallocated result.
1234
1362
  */
1235
1363
  getColumn(columnIndex, result) {
1236
1364
  const index = Geometry_1.Geometry.cyclic3dAxis(columnIndex);
1237
1365
  return Point3dVector3d_1.Vector3d.create(this.coffs[index], this.coffs[index + 3], this.coffs[index + 6], result);
1238
1366
  }
1239
- /** Return a (copy of) a row of the matrix.
1240
- * @param i row index. This is corrected to 012 by Geometry.cyclic3dAxis.
1367
+ /**
1368
+ * Return a (copy of) a row of the matrix.
1369
+ * @param i row index. This is interpreted cyclically (using Geometry.cyclic3dAxis).
1370
+ * @param result optional preallocated result.
1241
1371
  */
1242
1372
  getRow(columnIndex, result) {
1243
1373
  const index = 3 * Geometry_1.Geometry.cyclic3dAxis(columnIndex);
1244
1374
  return Point3dVector3d_1.Vector3d.create(this.coffs[index], this.coffs[index + 1], this.coffs[index + 2], result);
1245
1375
  }
1246
- /** Create a matrix from column vectors, shuffled into place per AxisTriple */
1247
- static createShuffledColumns(vectorU, vectorV, vectorW, axisOrder, result) {
1248
- const target = Matrix3d._create(result);
1249
- target.setColumn(Geometry_1.Geometry.axisOrderToAxis(axisOrder, 0), vectorU);
1250
- target.setColumn(Geometry_1.Geometry.axisOrderToAxis(axisOrder, 1), vectorV);
1251
- target.setColumn(Geometry_1.Geometry.axisOrderToAxis(axisOrder, 2), vectorW);
1252
- return target;
1253
- }
1254
- /** Create a matrix from row vectors.
1376
+ /**
1377
+ * Create a matrix from row vectors.
1255
1378
  * ```
1256
1379
  * equation
1257
1380
  * \begin{bmatrix}U_x & U_y & U_z \\ V_x & V_y & V_z \\ W_x & W_y & W_z \end{bmatrix}
@@ -1260,13 +1383,18 @@ class Matrix3d {
1260
1383
  static createRows(vectorU, vectorV, vectorW, result) {
1261
1384
  return Matrix3d.createRowValues(vectorU.x, vectorU.y, vectorU.z, vectorV.x, vectorV.y, vectorV.z, vectorW.x, vectorW.y, vectorW.z, result);
1262
1385
  }
1263
- /** Create a matrix that scales along a specified direction.
1264
- * * The scale factor can be negative.
1265
- * * A scale of -1.0 (negative one) is a mirror across the plane perpendicular to the vector.
1386
+ /**
1387
+ * Create a matrix that scales along a specified `direction`. This means if you multiply the returned matrix
1388
+ * by a `vector`, you get `directional scale` of that `vector`. Suppose `plane` is the plane perpendicular
1389
+ * to the `direction`. When scale = 0, `directional scale` is projection of the `vector` to the `plane`.
1390
+ * When scale = 1, `directional scale` is the `vector` itself. When scale = -1, `directional scale` is
1391
+ * mirror of the `vector` across the `plane`. In general, When scale != 0, the result is computed by first
1392
+ * projecting the `vector` to the `plane`, then translating that projection along the `direction` (if scale > 0)
1393
+ * or in opposite direction (if scale < 0).
1266
1394
  * ```
1267
1395
  * equation
1268
- * \text{The matrix is } I - (s-1) U U^T
1269
- * \\ \text{with }U\text{ being the unit vector in the direction of the input vector.}
1396
+ * \text{The matrix is } I + (s-1) D D^T
1397
+ * \\ \text{with }D\text{ being the normalized direction vector and }s\text{ being the scale.}
1270
1398
  * ```
1271
1399
  */
1272
1400
  static createDirectionalScale(direction, scale, result) {
@@ -1275,19 +1403,13 @@ class Matrix3d {
1275
1403
  const x = unit.x;
1276
1404
  const y = unit.y;
1277
1405
  const z = unit.z;
1278
- const a = (scale - 1);
1406
+ const a = scale - 1;
1279
1407
  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);
1280
1408
  }
1281
1409
  return Matrix3d.createUniformScale(scale);
1282
1410
  }
1283
- /* Create a matrix with the indicated column in the (normalized) direction, and the other two columns perpendicular. All columns are normalized.
1284
- * * The direction vector is normalized and appears in column axisIndex
1285
- * * 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)
1286
- * * If the direction vector is close to Z, the "next" column ((axisIndex + 1) mode 3) will be in the direction of (direction cross Y)
1287
- */
1288
- // static create1Vector(direction: Vector3d, axisIndex: number): Matrix3d;
1289
- // static createFromXYVectors(vectorX: Vector3d, vectorY: Vector3d, axisIndex: number): Matrix3d;
1290
- /** Multiply the matrix * vector, treating the vector is a column vector on the right.
1411
+ /**
1412
+ * Multiply `matrix * vector`, treating the vector is a column vector on the right.
1291
1413
  * ```
1292
1414
  * equation
1293
1415
  * \matrixXY{A}\columnSubXYZ{U}
@@ -1298,36 +1420,38 @@ class Matrix3d {
1298
1420
  const x = vectorU.x;
1299
1421
  const y = vectorU.y;
1300
1422
  const z = vectorU.z;
1301
- return Point3dVector3d_1.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);
1423
+ return Point3dVector3d_1.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);
1302
1424
  }
1303
- /** Multiply matrix * vector for each array member, i.e. the vector is a column vector on the right.
1304
- * @return the vector result
1425
+ /**
1426
+ * Multiply `matrix * vector` in place for vector in the array, i.e. treating the vector is a column
1427
+ * vector on the right.
1428
+ * * Each `vector` is updated to be `matrix * vector`
1305
1429
  */
1306
1430
  multiplyVectorArrayInPlace(data) {
1307
1431
  for (const v of data)
1308
- 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));
1432
+ 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);
1309
1433
  }
1310
- /** compute `origin - matrix * vector` */
1434
+ /** Compute `origin - matrix * vector` */
1311
1435
  static xyzMinusMatrixTimesXYZ(origin, matrix, vector, result) {
1312
1436
  const x = vector.x;
1313
1437
  const y = vector.y;
1314
1438
  const z = vector.z;
1315
1439
  return Point3dVector3d_1.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);
1316
1440
  }
1317
- /** compute `origin + matrix * vector` using only the xy parts of the inputs. */
1441
+ /** Compute `origin + matrix * vector` using only the xy parts of the inputs. */
1318
1442
  static xyPlusMatrixTimesXY(origin, matrix, vector, result) {
1319
1443
  const x = vector.x;
1320
1444
  const y = vector.y;
1321
1445
  return Point2dVector2d_1.Point2d.create(origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y, origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y, result);
1322
1446
  }
1323
- /** compute `origin + matrix * vector` using all xyz parts of the inputs. */
1447
+ /** Compute `origin + matrix * vector` using all xyz parts of the inputs. */
1324
1448
  static xyzPlusMatrixTimesXYZ(origin, matrix, vector, result) {
1325
1449
  const x = vector.x;
1326
1450
  const y = vector.y;
1327
1451
  const z = vector.z;
1328
1452
  return Point3dVector3d_1.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);
1329
1453
  }
1330
- /** compute `origin + matrix * vector` using all xyz parts of the inputs. */
1454
+ /** Updates vector to be `origin + matrix * vector` using all xyz parts of the inputs. */
1331
1455
  static xyzPlusMatrixTimesXYZInPlace(origin, matrix, vector) {
1332
1456
  const x = vector.x;
1333
1457
  const y = vector.y;
@@ -1336,61 +1460,72 @@ class Matrix3d {
1336
1460
  vector.y = origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z;
1337
1461
  vector.z = origin.z + matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z;
1338
1462
  }
1339
- /** compute `origin + matrix * vector` where the final vector is given as direct x,y,z coordinates */
1463
+ /** Compute `origin + matrix * vector` where the final vector is given as direct x,y,z coordinates */
1340
1464
  static xyzPlusMatrixTimesCoordinates(origin, matrix, x, y, z, result) {
1341
1465
  return Point3dVector3d_1.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);
1342
1466
  }
1343
1467
  /**
1344
1468
  * Treat the 3x3 matrix and origin as upper 3x4 part of a 4x4 matrix, with 0001 as the final row.
1345
- * Multiply times point with coordinates `[x,y,z,w]`
1469
+ * Multiply the 4x4 matrix by `[x,y,z,w]`
1470
+ * ```
1471
+ * equation
1472
+ * \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}
1473
+ * ```
1346
1474
  * @param origin translation part (xyz in column 3)
1347
1475
  * @param matrix matrix part (leading 3x3)
1348
1476
  * @param x x part of multiplied point
1349
1477
  * @param y y part of multiplied point
1350
1478
  * @param z z part of multiplied point
1351
1479
  * @param w w part of multiplied point
1352
- * @param result optional result.
1480
+ * @param result optional preallocated result.
1353
1481
  */
1354
1482
  static xyzPlusMatrixTimesWeightedCoordinates(origin, matrix, x, y, z, w, result) {
1355
- return Point4d_1.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);
1483
+ return Point4d_1.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);
1356
1484
  }
1357
1485
  /**
1358
1486
  * Treat the 3x3 matrix and origin as upper 3x4 part of a 4x4 matrix, with 0001 as the final row.
1359
- * Multiply times point with coordinates `[x,y,z,w]`
1487
+ * Multiply the 4x4 matrix by `[x,y,z,w]`
1488
+ * ```
1489
+ * equation
1490
+ * \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}
1491
+ * ```
1360
1492
  * @param origin translation part (xyz in column 3)
1361
1493
  * @param matrix matrix part (leading 3x3)
1362
1494
  * @param x x part of multiplied point
1363
1495
  * @param y y part of multiplied point
1364
1496
  * @param z z part of multiplied point
1365
1497
  * @param w w part of multiplied point
1366
- * @param result optional result.
1498
+ * @param result optional preallocated result.
1367
1499
  */
1368
1500
  static xyzPlusMatrixTimesWeightedCoordinatesToFloat64Array(origin, matrix, x, y, z, w, result) {
1369
1501
  if (!result)
1370
1502
  result = new Float64Array(4);
1371
- result[0] = w * origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z;
1372
- result[1] = w * origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z;
1373
- result[2] = w * origin.z + matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z;
1503
+ result[0] = matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z + origin.x * w;
1504
+ result[1] = matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z + origin.y * w;
1505
+ result[2] = matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z + origin.z * w;
1374
1506
  result[3] = w;
1375
1507
  return result;
1376
1508
  }
1377
1509
  /**
1378
- * Treat the 3x3 matrix and origin as upper 3x4 part of a 4x4 matrix, with 0001 as the final row.
1379
- * Multiply times point with coordinates `[x,y,z,w]`
1510
+ * Treat the 3x3 matrix and origin as a 3x4 matrix.
1511
+ * * Multiply the 3x4 matrix by `[x,y,z,1]`
1512
+ * ```
1513
+ * equation
1514
+ * \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}
1515
+ * ```
1380
1516
  * @param origin translation part (xyz in column 3)
1381
1517
  * @param matrix matrix part (leading 3x3)
1382
1518
  * @param x x part of multiplied point
1383
1519
  * @param y y part of multiplied point
1384
1520
  * @param z z part of multiplied point
1385
- * @param w w part of multiplied point
1386
- * @param result optional result.
1521
+ * @param result optional preallocated result.
1387
1522
  */
1388
1523
  static xyzPlusMatrixTimesCoordinatesToFloat64Array(origin, matrix, x, y, z, result) {
1389
1524
  if (!result)
1390
1525
  result = new Float64Array(3);
1391
- result[0] = origin.x + matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z;
1392
- result[1] = origin.y + matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z;
1393
- result[2] = origin.z + matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z;
1526
+ result[0] = matrix.coffs[0] * x + matrix.coffs[1] * y + matrix.coffs[2] * z + origin.x;
1527
+ result[1] = matrix.coffs[3] * x + matrix.coffs[4] * y + matrix.coffs[5] * z + origin.y;
1528
+ result[2] = matrix.coffs[6] * x + matrix.coffs[7] * y + matrix.coffs[8] * z + origin.z;
1394
1529
  return result;
1395
1530
  }
1396
1531
  /**
@@ -1410,9 +1545,9 @@ class Matrix3d {
1410
1545
  const x = vector.x;
1411
1546
  const y = vector.y;
1412
1547
  const z = vector.z;
1413
- result.x = (this.coffs[0] * x + this.coffs[3] * y + this.coffs[6] * z);
1414
- result.y = (this.coffs[1] * x + this.coffs[4] * y + this.coffs[7] * z);
1415
- result.z = (this.coffs[2] * x + this.coffs[5] * y + this.coffs[8] * z);
1548
+ result.x = this.coffs[0] * x + this.coffs[3] * y + this.coffs[6] * z;
1549
+ result.y = this.coffs[1] * x + this.coffs[4] * y + this.coffs[7] * z;
1550
+ result.z = this.coffs[2] * x + this.coffs[5] * y + this.coffs[8] * z;
1416
1551
  return result;
1417
1552
  }
1418
1553
  /** Multiply the matrix * (x,y,z), i.e. the vector (x,y,z) is a column vector on the right.
@@ -1682,7 +1817,11 @@ class Matrix3d {
1682
1817
  this.multiplyMatrixMatrix(other.matrix, result.matrix);
1683
1818
  return result;
1684
1819
  }
1685
- /** return a transposed matrix. `this` is not changed unless also passed as the result. */
1820
+ /**
1821
+ * Return the transpose of `this` matrix.
1822
+ * If `result` is passed as argument, then the function copies the transpose of `this` into `result`
1823
+ * `this` is not changed unless also passed as the result, i.e., this.transpose(this) transposes `this` in place
1824
+ */
1686
1825
  transpose(result) {
1687
1826
  if (!result)
1688
1827
  result = new Matrix3d();
@@ -1697,8 +1836,8 @@ class Matrix3d {
1697
1836
  }
1698
1837
  return result;
1699
1838
  }
1700
- /** transpose this matrix in place.
1701
- *
1839
+ /**
1840
+ * Transpose this matrix in place.
1702
1841
  */
1703
1842
  transposeInPlace() {
1704
1843
  PackedMatrix3dOps.transposeInPlace(this.coffs);
@@ -1729,18 +1868,6 @@ class Matrix3d {
1729
1868
  result.inverseState = this.inverseState;
1730
1869
  return result;
1731
1870
  }
1732
- /** copy the transpose of the coffs to the inverseCoffs.
1733
- * * mark the matrix as inverseStored.
1734
- */
1735
- setupInverseTranspose() {
1736
- const coffs = this.coffs;
1737
- this.inverseState = InverseMatrixState.inverseStored;
1738
- this.inverseCoffs = Float64Array.from([
1739
- coffs[0], coffs[3], coffs[6],
1740
- coffs[1], coffs[4], coffs[7],
1741
- coffs[2], coffs[5], coffs[8]
1742
- ]);
1743
- }
1744
1871
  /* Alternate implementation of computedCachedInverse - more direct addressing of arrays.
1745
1872
  This is indeed 10% faster than using static work areas. */
1746
1873
  // take the cross product of two rows of source.
@@ -1848,8 +1975,10 @@ class Matrix3d {
1848
1975
  markSingular() {
1849
1976
  this.inverseState = InverseMatrixState.singular;
1850
1977
  }
1851
- /** Create the inverseCoffs member (filled with zeros)
1852
- * This is for use by matrix * matrix multiplications which need to be sure the member is there to be filled with method-specific content.
1978
+ /**
1979
+ * Create the inverseCoffs member (filled with zeros)
1980
+ * This is for use by matrix * matrix multiplications which need to be sure the member is there to be
1981
+ * filled with method-specific content.
1853
1982
  */
1854
1983
  createInverseCoffsWithZeros() {
1855
1984
  if (!this.inverseCoffs) {
@@ -1946,7 +2075,7 @@ class Matrix3d {
1946
2075
  * @param scaleX scale factor for column x
1947
2076
  * @param scaleY scale factor for column y
1948
2077
  * @param scaleZ scale factor for column z
1949
- * @param result optional result.
2078
+ * @param result optional preallocated result.
1950
2079
  */
1951
2080
  scaleColumns(scaleX, scaleY, scaleZ, result) {
1952
2081
  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);
@@ -1967,7 +2096,7 @@ class Matrix3d {
1967
2096
  this.coffs[7] *= scaleY;
1968
2097
  this.coffs[8] *= scaleZ;
1969
2098
  if (this.inverseState === InverseMatrixState.inverseStored && this.inverseCoffs !== undefined) {
1970
- // apply reciprocal scales to the ROWS of the inverse . . .
2099
+ // apply reverse scales to the ROWS of the inverse
1971
2100
  const divX = Geometry_1.Geometry.conditionalDivideFraction(1.0, scaleX);
1972
2101
  const divY = Geometry_1.Geometry.conditionalDivideFraction(1.0, scaleY);
1973
2102
  const divZ = Geometry_1.Geometry.conditionalDivideFraction(1.0, scaleZ);
@@ -1990,7 +2119,7 @@ class Matrix3d {
1990
2119
  * @param scaleX scale factor for row x
1991
2120
  * @param scaleY scale factor for row y
1992
2121
  * @param scaleZ scale factor for row z
1993
- * @param result optional result.
2122
+ * @param result optional preallocated result.
1994
2123
  */
1995
2124
  scaleRows(scaleX, scaleY, scaleZ, result) {
1996
2125
  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);
@@ -1998,7 +2127,7 @@ class Matrix3d {
1998
2127
  /**
1999
2128
  * add scaled values from other Matrix3d to this Matrix3d
2000
2129
  * @param other Matrix3d with values to be added
2001
- * @param scale scale factor to apply to th eadded values.
2130
+ * @param scale scale factor to apply to the added values.
2002
2131
  */
2003
2132
  addScaledInPlace(other, scale) {
2004
2133
  for (let i = 0; i < 9; i++)
@@ -2034,12 +2163,49 @@ class Matrix3d {
2034
2163
  }
2035
2164
  /** create a Matrix3d whose values are uniformly scaled from this.
2036
2165
  * @param scale scale factor to apply.
2037
- * @param result optional result.
2166
+ * @param result optional preallocated result.
2038
2167
  * @returns Return the new or repopulated matrix
2039
2168
  */
2040
2169
  scale(scale, result) {
2041
2170
  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);
2042
2171
  }
2172
+ /**
2173
+ * Create a rigid matrix (columns and rows are unit length and pairwise perpendicular) for
2174
+ * the given eye coordinate.
2175
+ * * column z is parallel to x,y,z
2176
+ * * column x is perpendicular to column z and is in the xy plane
2177
+ * * column y is perpendicular to both. It is the "up" vector on the view plane.
2178
+ * * Multiplying the returned matrix times a local (view) vector gives the world vector.
2179
+ * * Multiplying transpose of the returned matrix times a world vector gives the local
2180
+ * (view) vector.
2181
+ * @param x eye x coordinate
2182
+ * @param y eye y coordinate
2183
+ * @param z eye z coordinate
2184
+ * @param result optional preallocated result
2185
+ */
2186
+ static createRigidViewAxesZTowardsEye(x, y, z, result) {
2187
+ result = Matrix3d.createIdentity(result);
2188
+ const rxy = Geometry_1.Geometry.hypotenuseXY(x, y);
2189
+ // if coordinate is (0,0,z), i.e., Top or Bottom view
2190
+ if (Geometry_1.Geometry.isSmallMetricDistance(rxy)) {
2191
+ if (z < 0.0)
2192
+ result.scaleColumnsInPlace(1.0, -1.0, -1.0);
2193
+ }
2194
+ else {
2195
+ const c = x / rxy;
2196
+ const s = y / rxy;
2197
+ // if coordinate is (x,y,0), i.e., Front or Back or Left or Right view
2198
+ result.setRowValues(-s, 0, c, c, 0, s, 0, 1, 0);
2199
+ // if coordinate is (x,y,z), i.e., other views such as Iso or RightIso
2200
+ if (z !== 0.0) {
2201
+ const r = Geometry_1.Geometry.hypotenuseXYZ(x, y, z);
2202
+ const s1 = z / r;
2203
+ const c1 = rxy / r;
2204
+ result.applyGivensColumnOp(1, 2, c1, -s1);
2205
+ }
2206
+ }
2207
+ return result;
2208
+ }
2043
2209
  /** Return the determinant of this matrix. */
2044
2210
  determinant() {
2045
2211
  return this.coffs[0] * this.coffs[4] * this.coffs[8]
@@ -2061,18 +2227,18 @@ class Matrix3d {
2061
2227
  /** Return the sum of squares of all entries */
2062
2228
  sumSquares() {
2063
2229
  let i = 0;
2064
- let a = 0;
2230
+ let sum = 0;
2065
2231
  for (i = 0; i < 9; i++)
2066
- a += this.coffs[i] * this.coffs[i];
2067
- return a;
2232
+ sum += this.coffs[i] * this.coffs[i];
2233
+ return sum;
2068
2234
  }
2069
2235
  /** Return the sum of squares of diagonal entries */
2070
2236
  sumDiagonalSquares() {
2071
2237
  let i = 0;
2072
- let a = 0;
2238
+ let sum = 0;
2073
2239
  for (i = 0; i < 9; i += 4)
2074
- a += this.coffs[i] * this.coffs[i];
2075
- return a;
2240
+ sum += this.coffs[i] * this.coffs[i];
2241
+ return sum;
2076
2242
  }
2077
2243
  /** Return the sum of diagonal entries (also known as the trace) */
2078
2244
  sumDiagonal() {
@@ -2081,18 +2247,18 @@ class Matrix3d {
2081
2247
  /** Return the Maximum absolute value of any single entry */
2082
2248
  maxAbs() {
2083
2249
  let i = 0;
2084
- let a = 0;
2250
+ let max = 0;
2085
2251
  for (i = 0; i < 9; i++)
2086
- a = Math.max(a, Math.abs(this.coffs[i]));
2087
- return a;
2252
+ max = Math.max(max, Math.abs(this.coffs[i]));
2253
+ return max;
2088
2254
  }
2089
- /** Return the maximum absolute difference between corresponding entries */
2255
+ /** Return the maximum absolute difference between corresponding entries of `this` and `other` */
2090
2256
  maxDiff(other) {
2091
2257
  let i = 0;
2092
- let a = 0;
2258
+ let max = 0;
2093
2259
  for (i = 0; i < 9; i++)
2094
- a = Math.max(a, Math.abs(this.coffs[i] - other.coffs[i]));
2095
- return a;
2260
+ max = Math.max(max, Math.abs(this.coffs[i] - other.coffs[i]));
2261
+ return max;
2096
2262
  }
2097
2263
  /** Test if the matrix is (very near to) an identity */
2098
2264
  get isIdentity() {
@@ -2183,25 +2349,6 @@ class Matrix3d {
2183
2349
  const product = this.multiplyMatrixMatrixTranspose(this);
2184
2350
  return product.isIdentity;
2185
2351
  }
2186
- /** create a new orthogonal matrix (perpendicular columns, unit length, transpose is inverse).
2187
- * vectorA is placed in the first column of the axis order.
2188
- * vectorB is projected perpendicular to vectorA within their plane and placed in the second column.
2189
- */
2190
- static createRigidFromColumns(vectorA, vectorB, axisOrder, result) {
2191
- const vectorA1 = vectorA.normalize();
2192
- if (vectorA1) {
2193
- const vectorC1 = vectorA1.unitCrossProduct(vectorB);
2194
- if (vectorC1) {
2195
- const vectorB1 = vectorC1.unitCrossProduct(vectorA);
2196
- if (vectorB1) {
2197
- const retVal = Matrix3d.createShuffledColumns(vectorA1, vectorB1, vectorC1, axisOrder, result);
2198
- retVal.setupInverseTranspose();
2199
- return retVal;
2200
- }
2201
- }
2202
- }
2203
- return undefined;
2204
- }
2205
2352
  /** Adjust the matrix in place so that:
2206
2353
  * * columns are perpendicular and have unit length
2207
2354
  * * transpose equals inverse
@@ -2242,8 +2389,8 @@ class Matrix3d {
2242
2389
  return coff;
2243
2390
  }
2244
2391
  /** create a matrix from a quaternion.
2245
- * WARNING: There is frequent confusion over whether a "from quaternion" matrix is organized by rows and columns.
2246
- * WARNING: If you find that the matrix seems to rotate by the opposite angle expect it, transpose it.
2392
+ * **WARNING:** There is frequent confusion over whether a "from quaternion" matrix is organized by rows and columns.
2393
+ * **WARNING:** If you find that the matrix seems to rotate by the opposite angle expect it, transpose it.
2247
2394
  */
2248
2395
  static createFromQuaternion(quat) {
2249
2396
  const qqx = quat.x * quat.x;
@@ -2262,8 +2409,8 @@ class Matrix3d {
2262
2409
  }
2263
2410
  /** convert the matrix to a quaternion.
2264
2411
  * @note This calculation requires the matrix to have unit length rows and columns.
2265
- * WARNING: There is frequent confusion over whether a "from quaternion" matrix is organized by rows and columns.
2266
- * WARNING: If you find that the matrix seems to rotate by the opposite angle expect it, transpose it.
2412
+ * **WARNING:** There is frequent confusion over whether a "from quaternion" matrix is organized by rows and columns.
2413
+ * **WARNING:** If you find that the matrix seems to rotate by the opposite angle expect it, transpose it.
2267
2414
  */
2268
2415
  toQuaternion() {
2269
2416
  const result = Point4d_1.Point4d.createZero();
@@ -2318,9 +2465,9 @@ class Matrix3d {
2318
2465
  exports.Matrix3d = Matrix3d;
2319
2466
  /** Control flag for whether this class uses cached inverse of matrices. */
2320
2467
  Matrix3d.useCachedInverse = true; // cached inverse can be suppressed for testing.
2321
- /** total number of times a cached inverse was used to avoid recompute */
2468
+ /** Total number of times a cached inverse was used to avoid recompute */
2322
2469
  Matrix3d.numUseCache = 0;
2323
- /** total number of times a cached inverse was computed. */
2470
+ /** Total number of times a cached inverse was computed. */
2324
2471
  Matrix3d.numComputeCache = 0;
2325
2472
  Matrix3d._productBuffer = new Float64Array(9);
2326
2473
  //# sourceMappingURL=Matrix3d.js.map