public static Matrix4D operator *( Matrix4D m, double s ) { Matrix4D result = new Matrix4D(); for( int i=0; i<4; i++ ) for( int j=0; j<4; j++ ) result[i, j] = m[i, j] * s; return result; }
public static Matrix4D operator *( Matrix4D m1, Matrix4D m2 ) { Matrix4D result = new Matrix4D(); for( int i=0; i<4; i++ ) for( int j=0; j<4; j++ ) for( int k=0; k<4; k++ ) result[i, j] += m1[i, k] * m2[k, j]; return result; }
/// <summary> /// Handles updating our rotation matrices based on mouse dragging. /// </summary> public void MouseDragged( double dx, double dy, bool xz_yz, bool xw_yw, bool xy_zw ) { Matrix4D spinDelta = new Matrix4D(); // Sensitivity. dx *= 0.012; dy *= 0.012; if( xz_yz ) { spinDelta[0,2] += dx; spinDelta[2,0] -= dx; spinDelta[1,2] += dy; spinDelta[2,1] -= dy; } if( xw_yw ) { spinDelta[0,3] -= dx; spinDelta[3,0] += dx; spinDelta[1,3] -= dy; spinDelta[3,1] += dy; } if( xy_zw ) { spinDelta[0,1] += dx; spinDelta[1,0] -= dx; spinDelta[3,2] -= dy; spinDelta[2,3] += dy; } ApplySpinDelta( spinDelta ); }
public RotationHandler4D( Matrix4D initialMatrix ) { Current4dView = initialMatrix; }
private void ApplySpinDelta( Matrix4D spinDelta ) { Matrix4D delta = Matrix4D.Identity() + spinDelta; delta = Matrix4D.GramSchmidt( delta ); // Orthonormalize ViewMat4d = delta * ViewMat4d; ViewMat4d = Matrix4D.GramSchmidt( ViewMat4d ); // Orthonormalize }
public static Matrix4D operator +( Matrix4D m1, Matrix4D m2 ) { Matrix4D result = new Matrix4D(); for( int i=0; i<4; i++ ) for( int j=0; j<4; j++ ) result[i, j] = m1[i, j] + m2[i, j]; return result; }
public Matrix4D Clone() { Matrix4D result = new Matrix4D(); for( int i=0; i<4; i++ ) for( int j=0; j<4; j++ ) result.Data[i][j] = this.Data[i][j]; return result; }
public static Matrix4D Identity() { Matrix4D result = new Matrix4D(); for( int i=0; i<4; i++ ) result[i,i] = 1; return result; }
public static Matrix4D Transpose( Matrix4D m ) { Matrix4D result = new Matrix4D(); for( int i=0; i<4; i++ ) for( int j=0; j<4; j++ ) result[i, j] = m[j, i]; return result; }
/// <summary> /// Gram-Schmidt orthonormalize /// </summary> public static Matrix4D GramSchmidt( Matrix4D input, Func<VectorND, VectorND, double> innerProduct, Func<VectorND, VectorND> normalize ) { Matrix4D result = input; for( int i=0; i<4; i++ ) { for( int j=i+1; j<4; j++ ) { VectorND iVec = result[i]; VectorND jVec = result[j]; iVec -= innerProduct( iVec, jVec ) * jVec; result[i] = iVec; } result[i] = normalize( result[i] ); } return result; }
/// <summary> /// Gram-Schmidt orthonormalize /// </summary> public static Matrix4D GramSchmidt( Matrix4D input ) { Matrix4D result = input; for( int i=0; i<4; i++ ) { for( int j=0; j<i; j++ ) { // result[j] is already unit length... // result[i] -= (result[i] dot result[j])*result[j] VectorND iVec = result[i]; VectorND jVec = result[j]; iVec -= ( iVec.Dot( jVec ) ) * jVec; result[i] = iVec; } result[i].Normalize(); } return result; }
public static Matrix4D ReverseRows( Matrix4D m ) { Matrix4D result = new Matrix4D(); for( int i=0; i<4; i++ ) result[i] = m[3-i]; return result; }
public static Matrix4D GramSchmidt( Matrix4D input, Matrix4D innerProductValues ) { Matrix4D result = input.Clone(); for( int i = 0; i < 4; i++ ) { for( int j = 0; j < i; j++ ) { VectorND iVec = result[i]; VectorND jVec = result[j]; double inner = innerProductValues[i].Dot( jVec ); iVec -= inner * jVec; result[i] = iVec; } // Normalize. We don't use VectorND normalize because we might have timelike vectors. double mag2 = innerProductValues[i].Dot( result[i] ); double abs = mag2 < 0 ? -Math.Sqrt( -mag2 ) : Math.Sqrt( mag2 ); result[i].Divide( abs ); } return result; }
/// <summary> /// This calculates the 4 vertices of a general (but finite) Goursat Tetrahedron. Result is in the ball model. /// /// The method comes from the dissertation "Hyperbolic polyhedra: volume and scissors congruence", /// by Yana Zilberberg Mohanty, section 2.4, steps 1-5. /// /// A,B,C are the three dihedral angles surrounding a vertex. /// A_,B_,C_ are the three oppoite dihedral angles. /// </summary> public static Vector3D[] GoursatTetrahedron( double A, double B, double C, double A_, double B_, double C_ ) { // Step 1: Construct Gram matrix with reversed rows/columns. // NOTE: The sign of the diagonal in the paper was incorrect. double pi = Math.PI; double[,] gramMatrixData = new double[,] { { -1, Math.Cos(pi/A_), Math.Cos(pi/B_), Math.Cos(pi/C) }, { Math.Cos(pi/A_), -1, Math.Cos(pi/C_), Math.Cos(pi/B) }, { Math.Cos(pi/B_), Math.Cos(pi/C_), -1, Math.Cos(pi/A) }, { Math.Cos(pi/C), Math.Cos(pi/B), Math.Cos(pi/A), -1 }, }; Matrix4D gramMatrix = new Matrix4D( gramMatrixData ); gramMatrix *= -1; Matrix4D identity = Matrix4D.Identity(); // Step 2: Gram-Schmidt. Matrix4D W = GramSchmidt( identity, gramMatrix ); // Step 3: Divide 4th row by i (already effectively done in our Gram-Schmidt routine below), and reverse order of rows. Matrix4D W_ = ReverseRows( W ); // Step 4 Matrix4D D = identity.Clone(); D[0, 0] = -1; Matrix4D U = Matrix4D.Transpose( D * W_ ); // Step 5 Matrix4D U_ = ReverseRows( U ); for( int i=0; i<4; i++ ) MinkowskiNormalize( U_[i] ); // Now move from the hyperboloid model to the ball. List<Vector3D> result = new List<Vector3D>(); for( int i=0; i<4; i++ ) result.Add( HyperboloidToBall( U_[i] ) ); return result.ToArray(); }