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