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