@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.
- package/CHANGELOG.md +28 -1
- package/LICENSE.md +1 -1
- package/lib/cjs/Geometry.d.ts +28 -17
- package/lib/cjs/Geometry.d.ts.map +1 -1
- package/lib/cjs/Geometry.js +52 -25
- package/lib/cjs/Geometry.js.map +1 -1
- package/lib/cjs/curve/CurveChainWithDistanceIndex.d.ts +10 -6
- package/lib/cjs/curve/CurveChainWithDistanceIndex.d.ts.map +1 -1
- package/lib/cjs/curve/CurveChainWithDistanceIndex.js +29 -31
- package/lib/cjs/curve/CurveChainWithDistanceIndex.js.map +1 -1
- package/lib/cjs/curve/CurveCurve.d.ts +11 -8
- package/lib/cjs/curve/CurveCurve.d.ts.map +1 -1
- package/lib/cjs/curve/CurveCurve.js +16 -12
- package/lib/cjs/curve/CurveCurve.js.map +1 -1
- package/lib/cjs/curve/CurveCurveIntersectXY.d.ts +5 -1
- package/lib/cjs/curve/CurveCurveIntersectXY.d.ts.map +1 -1
- package/lib/cjs/curve/CurveCurveIntersectXY.js +11 -10
- package/lib/cjs/curve/CurveCurveIntersectXY.js.map +1 -1
- package/lib/cjs/geometry3d/Angle.d.ts +19 -0
- package/lib/cjs/geometry3d/Angle.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Angle.js +39 -0
- package/lib/cjs/geometry3d/Angle.js.map +1 -1
- package/lib/cjs/geometry3d/AngleSweep.d.ts +1 -0
- package/lib/cjs/geometry3d/AngleSweep.d.ts.map +1 -1
- package/lib/cjs/geometry3d/AngleSweep.js +1 -0
- package/lib/cjs/geometry3d/AngleSweep.js.map +1 -1
- package/lib/cjs/geometry3d/CoincidentGeometryOps.d.ts +1 -0
- package/lib/cjs/geometry3d/CoincidentGeometryOps.d.ts.map +1 -1
- package/lib/cjs/geometry3d/CoincidentGeometryOps.js +3 -0
- package/lib/cjs/geometry3d/CoincidentGeometryOps.js.map +1 -1
- package/lib/cjs/geometry3d/GrowableXYArray.d.ts +1 -1
- package/lib/cjs/geometry3d/GrowableXYArray.js +1 -1
- package/lib/cjs/geometry3d/GrowableXYArray.js.map +1 -1
- package/lib/cjs/geometry3d/Matrix3d.d.ts +369 -254
- package/lib/cjs/geometry3d/Matrix3d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Matrix3d.js +807 -660
- package/lib/cjs/geometry3d/Matrix3d.js.map +1 -1
- package/lib/cjs/geometry3d/OrderedRotationAngles.d.ts +71 -14
- package/lib/cjs/geometry3d/OrderedRotationAngles.d.ts.map +1 -1
- package/lib/cjs/geometry3d/OrderedRotationAngles.js +204 -114
- package/lib/cjs/geometry3d/OrderedRotationAngles.js.map +1 -1
- package/lib/cjs/geometry3d/Point2dVector2d.d.ts +1 -0
- package/lib/cjs/geometry3d/Point2dVector2d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Point2dVector2d.js +1 -0
- package/lib/cjs/geometry3d/Point2dVector2d.js.map +1 -1
- package/lib/cjs/geometry3d/Point3dVector3d.d.ts +16 -16
- package/lib/cjs/geometry3d/Point3dVector3d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Point3dVector3d.js +21 -18
- package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
- package/lib/cjs/geometry3d/Segment1d.d.ts +1 -1
- package/lib/cjs/geometry3d/Segment1d.js +1 -1
- package/lib/cjs/geometry3d/Segment1d.js.map +1 -1
- package/lib/cjs/geometry3d/YawPitchRollAngles.d.ts +49 -25
- package/lib/cjs/geometry3d/YawPitchRollAngles.d.ts.map +1 -1
- package/lib/cjs/geometry3d/YawPitchRollAngles.js +146 -50
- package/lib/cjs/geometry3d/YawPitchRollAngles.js.map +1 -1
- package/lib/cjs/numerics/Polynomials.d.ts +12 -0
- package/lib/cjs/numerics/Polynomials.d.ts.map +1 -1
- package/lib/cjs/numerics/Polynomials.js +14 -0
- package/lib/cjs/numerics/Polynomials.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceBuilder.d.ts +5 -4
- package/lib/cjs/polyface/PolyfaceBuilder.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceBuilder.js +50 -10
- package/lib/cjs/polyface/PolyfaceBuilder.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceQuery.d.ts +82 -0
- package/lib/cjs/polyface/PolyfaceQuery.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceQuery.js +150 -1
- package/lib/cjs/polyface/PolyfaceQuery.js.map +1 -1
- package/lib/cjs/polyface/multiclip/OffsetMeshContext.d.ts +202 -0
- package/lib/cjs/polyface/multiclip/OffsetMeshContext.d.ts.map +1 -0
- package/lib/cjs/polyface/multiclip/OffsetMeshContext.js +1038 -0
- package/lib/cjs/polyface/multiclip/OffsetMeshContext.js.map +1 -0
- package/lib/cjs/serialization/GeometrySamples.d.ts +4 -1
- package/lib/cjs/serialization/GeometrySamples.d.ts.map +1 -1
- package/lib/cjs/serialization/GeometrySamples.js +14 -6
- package/lib/cjs/serialization/GeometrySamples.js.map +1 -1
- package/lib/cjs/serialization/IModelJsonSchema.d.ts +1 -1
- package/lib/cjs/serialization/IModelJsonSchema.js.map +1 -1
- package/lib/cjs/topology/Graph.d.ts +113 -7
- package/lib/cjs/topology/Graph.d.ts.map +1 -1
- package/lib/cjs/topology/Graph.js +185 -7
- package/lib/cjs/topology/Graph.js.map +1 -1
- package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts +38 -0
- package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts.map +1 -0
- package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.js +82 -0
- package/lib/cjs/topology/HalfEdgeGraphFromIndexedLoopsContext.js.map +1 -0
- package/lib/esm/Geometry.d.ts +28 -17
- package/lib/esm/Geometry.d.ts.map +1 -1
- package/lib/esm/Geometry.js +52 -25
- package/lib/esm/Geometry.js.map +1 -1
- package/lib/esm/curve/CurveChainWithDistanceIndex.d.ts +10 -6
- package/lib/esm/curve/CurveChainWithDistanceIndex.d.ts.map +1 -1
- package/lib/esm/curve/CurveChainWithDistanceIndex.js +30 -32
- package/lib/esm/curve/CurveChainWithDistanceIndex.js.map +1 -1
- package/lib/esm/curve/CurveCurve.d.ts +11 -8
- package/lib/esm/curve/CurveCurve.d.ts.map +1 -1
- package/lib/esm/curve/CurveCurve.js +16 -12
- package/lib/esm/curve/CurveCurve.js.map +1 -1
- package/lib/esm/curve/CurveCurveIntersectXY.d.ts +5 -1
- package/lib/esm/curve/CurveCurveIntersectXY.d.ts.map +1 -1
- package/lib/esm/curve/CurveCurveIntersectXY.js +11 -10
- package/lib/esm/curve/CurveCurveIntersectXY.js.map +1 -1
- package/lib/esm/geometry3d/Angle.d.ts +19 -0
- package/lib/esm/geometry3d/Angle.d.ts.map +1 -1
- package/lib/esm/geometry3d/Angle.js +39 -0
- package/lib/esm/geometry3d/Angle.js.map +1 -1
- package/lib/esm/geometry3d/AngleSweep.d.ts +1 -0
- package/lib/esm/geometry3d/AngleSweep.d.ts.map +1 -1
- package/lib/esm/geometry3d/AngleSweep.js +1 -0
- package/lib/esm/geometry3d/AngleSweep.js.map +1 -1
- package/lib/esm/geometry3d/CoincidentGeometryOps.d.ts +1 -0
- package/lib/esm/geometry3d/CoincidentGeometryOps.d.ts.map +1 -1
- package/lib/esm/geometry3d/CoincidentGeometryOps.js +3 -0
- package/lib/esm/geometry3d/CoincidentGeometryOps.js.map +1 -1
- package/lib/esm/geometry3d/GrowableXYArray.d.ts +1 -1
- package/lib/esm/geometry3d/GrowableXYArray.js +1 -1
- package/lib/esm/geometry3d/GrowableXYArray.js.map +1 -1
- package/lib/esm/geometry3d/Matrix3d.d.ts +369 -254
- package/lib/esm/geometry3d/Matrix3d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Matrix3d.js +807 -660
- package/lib/esm/geometry3d/Matrix3d.js.map +1 -1
- package/lib/esm/geometry3d/OrderedRotationAngles.d.ts +71 -14
- package/lib/esm/geometry3d/OrderedRotationAngles.d.ts.map +1 -1
- package/lib/esm/geometry3d/OrderedRotationAngles.js +205 -115
- package/lib/esm/geometry3d/OrderedRotationAngles.js.map +1 -1
- package/lib/esm/geometry3d/Point2dVector2d.d.ts +1 -0
- package/lib/esm/geometry3d/Point2dVector2d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Point2dVector2d.js +1 -0
- package/lib/esm/geometry3d/Point2dVector2d.js.map +1 -1
- package/lib/esm/geometry3d/Point3dVector3d.d.ts +16 -16
- package/lib/esm/geometry3d/Point3dVector3d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Point3dVector3d.js +21 -18
- package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
- package/lib/esm/geometry3d/Segment1d.d.ts +1 -1
- package/lib/esm/geometry3d/Segment1d.js +1 -1
- package/lib/esm/geometry3d/Segment1d.js.map +1 -1
- package/lib/esm/geometry3d/YawPitchRollAngles.d.ts +49 -25
- package/lib/esm/geometry3d/YawPitchRollAngles.d.ts.map +1 -1
- package/lib/esm/geometry3d/YawPitchRollAngles.js +146 -50
- package/lib/esm/geometry3d/YawPitchRollAngles.js.map +1 -1
- package/lib/esm/numerics/Polynomials.d.ts +12 -0
- package/lib/esm/numerics/Polynomials.d.ts.map +1 -1
- package/lib/esm/numerics/Polynomials.js +14 -0
- package/lib/esm/numerics/Polynomials.js.map +1 -1
- package/lib/esm/polyface/PolyfaceBuilder.d.ts +5 -4
- package/lib/esm/polyface/PolyfaceBuilder.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceBuilder.js +50 -10
- package/lib/esm/polyface/PolyfaceBuilder.js.map +1 -1
- package/lib/esm/polyface/PolyfaceQuery.d.ts +82 -0
- package/lib/esm/polyface/PolyfaceQuery.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceQuery.js +148 -0
- package/lib/esm/polyface/PolyfaceQuery.js.map +1 -1
- package/lib/esm/polyface/multiclip/OffsetMeshContext.d.ts +202 -0
- package/lib/esm/polyface/multiclip/OffsetMeshContext.d.ts.map +1 -0
- package/lib/esm/polyface/multiclip/OffsetMeshContext.js +1032 -0
- package/lib/esm/polyface/multiclip/OffsetMeshContext.js.map +1 -0
- package/lib/esm/serialization/GeometrySamples.d.ts +4 -1
- package/lib/esm/serialization/GeometrySamples.d.ts.map +1 -1
- package/lib/esm/serialization/GeometrySamples.js +14 -6
- package/lib/esm/serialization/GeometrySamples.js.map +1 -1
- package/lib/esm/serialization/IModelJsonSchema.d.ts +1 -1
- package/lib/esm/serialization/IModelJsonSchema.js.map +1 -1
- package/lib/esm/topology/Graph.d.ts +113 -7
- package/lib/esm/topology/Graph.d.ts.map +1 -1
- package/lib/esm/topology/Graph.js +185 -7
- package/lib/esm/topology/Graph.js.map +1 -1
- package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts +38 -0
- package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.d.ts.map +1 -0
- package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.js +78 -0
- package/lib/esm/topology/HalfEdgeGraphFromIndexedLoopsContext.js.map +1 -0
- package/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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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(
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
144
|
-
*
|
|
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
|
-
/**
|
|
152
|
+
/**
|
|
153
|
+
* An inverse was computed and stored as the `inverseCoffs`
|
|
154
|
+
*/
|
|
148
155
|
InverseMatrixState[InverseMatrixState["inverseStored"] = 1] = "inverseStored";
|
|
149
156
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
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
|
-
* *
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
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
|
-
*
|
|
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.
|
|
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
|
-
/*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
247
|
-
|
|
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
|
-
/**
|
|
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) {
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
*
|
|
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
|
|
347
|
-
* @param columnB vector to place in the
|
|
348
|
-
* @param columnC vector to place in the
|
|
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 { //
|
|
423
|
+
else { // AxisOrder.XYZ
|
|
370
424
|
result.setColumns(columnA, columnB, columnC);
|
|
371
425
|
}
|
|
372
426
|
return result;
|
|
373
427
|
}
|
|
374
428
|
/**
|
|
375
|
-
|
|
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() {
|
|
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() {
|
|
402
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
/**
|
|
425
|
-
*
|
|
426
|
-
* *
|
|
427
|
-
* * inverse status
|
|
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
|
-
/**
|
|
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
|
-
* * "
|
|
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
|
-
/**
|
|
449
|
-
*
|
|
450
|
-
* *
|
|
451
|
-
* *
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
574
|
-
* * ColumnZ is a unit cross product.
|
|
575
|
-
* Optionally rotate
|
|
576
|
-
* *
|
|
577
|
-
*
|
|
578
|
-
* This is expected to be used with various principal unit vectors that are perpendicular to each other.
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
806
|
+
matrix.applyGivensColumnOp(1, 2, c, -s); // rotate around X (equivalent to matrix*rotationX)
|
|
607
807
|
}
|
|
608
|
-
return
|
|
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
|
-
* *
|
|
615
|
-
* *
|
|
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,
|
|
618
|
-
* @param invert if false (default), the
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
726
|
-
const
|
|
727
|
-
const
|
|
728
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
743
|
-
|
|
744
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
982
|
+
return { axis: Point3dVector3d_1.Vector3d.create(0, 0, 1), angle: Angle_1.Angle.createDegrees(180), ok: true };
|
|
759
983
|
}
|
|
760
|
-
//
|
|
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:
|
|
991
|
+
return { axis: eigenvectors.getColumn(axisIndex), angle: Angle_1.Angle.createDegrees(180), ok: true };
|
|
772
992
|
}
|
|
773
|
-
//
|
|
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 = {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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
|
|
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
|
|
1129
|
-
const
|
|
1130
|
-
const
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
this
|
|
1140
|
-
this.
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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
|
-
|
|
1122
|
+
symmetricEigenvalues(leftEigenvectors, lambda) {
|
|
1153
1123
|
const matrix = this.clone();
|
|
1154
1124
|
leftEigenvectors.setIdentity();
|
|
1155
|
-
|
|
1156
|
-
|
|
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 =
|
|
1159
|
-
+
|
|
1160
|
-
+
|
|
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
|
-
*
|
|
1173
|
-
*
|
|
1174
|
-
*
|
|
1175
|
-
*
|
|
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
|
|
1179
|
-
|
|
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
|
-
/**
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
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
|
-
|
|
1188
|
-
return
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
1197
|
-
* @param columnIndex column index
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
1222
|
-
* @param rowIndex row index.
|
|
1223
|
-
* @param value x,
|
|
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
|
-
/**
|
|
1233
|
-
*
|
|
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
|
-
/**
|
|
1240
|
-
*
|
|
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
|
-
/**
|
|
1247
|
-
|
|
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
|
-
/**
|
|
1264
|
-
*
|
|
1265
|
-
*
|
|
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
|
|
1269
|
-
* \\ \text{with }
|
|
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 =
|
|
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
|
-
|
|
1284
|
-
* *
|
|
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(
|
|
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
|
-
/**
|
|
1304
|
-
*
|
|
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(
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
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(
|
|
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
|
|
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] =
|
|
1372
|
-
result[1] =
|
|
1373
|
-
result[2] =
|
|
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
|
|
1379
|
-
* Multiply
|
|
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
|
|
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] =
|
|
1392
|
-
result[1] =
|
|
1393
|
-
result[2] =
|
|
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 =
|
|
1414
|
-
result.y =
|
|
1415
|
-
result.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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
1852
|
-
*
|
|
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
|
|
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
|
|
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
|
|
2230
|
+
let sum = 0;
|
|
2065
2231
|
for (i = 0; i < 9; i++)
|
|
2066
|
-
|
|
2067
|
-
return
|
|
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
|
|
2238
|
+
let sum = 0;
|
|
2073
2239
|
for (i = 0; i < 9; i += 4)
|
|
2074
|
-
|
|
2075
|
-
return
|
|
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
|
|
2250
|
+
let max = 0;
|
|
2085
2251
|
for (i = 0; i < 9; i++)
|
|
2086
|
-
|
|
2087
|
-
return
|
|
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
|
|
2258
|
+
let max = 0;
|
|
2093
2259
|
for (i = 0; i < 9; i++)
|
|
2094
|
-
|
|
2095
|
-
return
|
|
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
|
|
2246
|
-
* WARNING
|
|
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
|
|
2266
|
-
* WARNING
|
|
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
|
-
/**
|
|
2468
|
+
/** Total number of times a cached inverse was used to avoid recompute */
|
|
2322
2469
|
Matrix3d.numUseCache = 0;
|
|
2323
|
-
/**
|
|
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
|