/// <summary> /// Build a transformation taking the independent vectors v1 and v2 to w1 and w2, respectively. /// </summary> /// <param name="v1">A first vector; must be nonzero. </param> /// <param name="v2">A second vector; must be linearly independent of the first</param> /// <param name="w1">Where the first vector will be sent</param> /// <param name="w2">Where the second vector will be sent</param> /// <returns>A linear transformation taking v1 to w1 and v2 to w2 </returns> public static LinearTransform2 VectorsToVectors(Vector v1, Vector v2, Vector w1, Vector w2) { LinearTransform2 V = new LinearTransform2(v1, v2); LinearTransform2 W = new LinearTransform2(w1, w2); return(W * V.InverseTransform()); }
/// <summary> /// Given a point p1 and linearly independent vectors v1 and v2, build the unique transformation taking p1 to q1, v1 to w1, and v2 to w2. /// <param name="p1">The point to be moved</param> /// <param name="v1">The first vector</param> /// <param name="v2">The second vector</param> /// <param name="q1">The point to which p1 will be moved</param> /// <param name="w1">The vector to which v1 will be moved</param> /// <param name="w2">The vector to which v2 will be moved</param> /// <returns>The affine transform. </returns> /// </summary> public static AffineTransform2 PointAndVectorsToPointAndVectors( Point p1, Vector v1, Vector v2, Point q1, Vector w1, Vector w2) { AffineTransform2 Trans1 = AffineTransform2.Translate(p1, new Point(0, 0)); LinearTransform2 T = LinearTransform2.VectorsToVectors(v1, v2, w1, w2); AffineTransform2 Trans2 = AffineTransform2.Translate(new Point(0, 0), q1); AffineTransform2 S = new AffineTransform2(); S.mat = T.Matrix(); return(Trans2 * S * Trans1); /* * double[,] linmat = T.Matrix(); * for (int i = 0; i < 2; i++) * { * for (int j = 0; j < 2; j++) * { * W.mat[i, j] = linmat[i, j]; * } * } * return W; */ }
/// <summary> /// Compose the linear transformation T1 with the linear transformation T2 to get T1 o T2. /// </summary> /// <param name="T1">The first transformation</param> /// <param name="T2">The second transformation</param> /// <remarks> Note that when this composite is applied to a vector v, we first apply T2 to v, and then apply T1 to the result. </remarks> /// <returns>The composite transformation. </returns> public static LinearTransform2 operator *(LinearTransform2 T1, LinearTransform2 T2) { LinearTransform2 S = new LinearTransform2(); S.mat = MatrixProduct(T1.mat, T2.mat); return(S); }
/// <summary> /// Compute the inverse of this transformation, if it exists. /// </summary> /// <returns></returns> public LinearTransform2 InverseTransform() { double[,] m = MatrixInverse(mat); LinearTransform2 T = new LinearTransform2(); T.mat = m; return(T); }
/// <summary> /// Build a rotation about the origin that moves the positive X-axis towards the postive Y-axis by the specified angle. /// </summary> /// <param name="angle">The angle to rotate, in radians</param> /// <returns>A rotation transformation</returns> public static LinearTransform2 RotateXY(double angle) { LinearTransform2 T = new LinearTransform2(); T.mat[0, 0] = Math.Cos(angle); T.mat[1, 1] = T.mat[0, 0]; T.mat[1, 0] = Math.Sin(angle); T.mat[0, 1] = -T.mat[1, 0]; return(T); }
/// <summary> /// Build a transformation taking the standard frame (0,0), (1, 0), (0, 1), and (1, 1) to the points /// p0, p1, p2, and p3. /// </summary> /// <param name="p0">Where (0, 0) is sent</param> /// <param name="p1">Where (1, 0) is sent</param> /// <param name="p2">Where (0, 1) is sent</param> /// <param name="p3">Where (1, 1) is sent</param> /// <returns>The projective transformation effecting the specified mappings</returns> public static ProjectiveTransform2 StandardFrameToPoints(Point p0, Point p1, Point p2, Point p3) { // ProjectiveTransform2 T = new ProjectiveTransform2(); // idea: // Send e1, e2, e3 to p0, p1, p2 by a map K. // Let L be Kinverse. // Then L sends p0, p1, p2 to e1, e2 and e3 . See where p4 goes; call this q. // build projective map P sending e1, e2, e3, and u= (e1+e2+e3) to e1, e2, e3, and q. // then let L = Kinverse; K * P sends e1 to p1; e2 to p2; e3 to p3; and u to q to e4. ProjectiveTransform2 K = new ProjectiveTransform2(); for (int i = 0; i < 3; i++) { K.mat[2, i] = 1.0d; } K.mat[0, 0] = p0.X; K.mat[1, 0] = p0.Y; K.mat[0, 1] = p1.X; K.mat[1, 1] = p1.Y; K.mat[0, 2] = p2.X; K.mat[1, 2] = p2.Y; ProjectiveTransform2 L = new ProjectiveTransform2(); L.mat = LinearTransform2.MatrixInverse(K.mat); double[] v = new double[3]; v[0] = p3.X; v[1] = p3.Y; v[2] = 1.0d; double[] q = new double[3]; for (int i = 0; i < 3; i++) { double tally = 0.0d; for (int j = 0; j < 3; j++) { tally += L.mat[i, j] * v[j]; } q[i] = tally; } double[,] p = new double[3, 3]; for (int i = 0; i < 3; i++) { p[i, i] = q[i]; } ProjectiveTransform2 S = new ProjectiveTransform2(); S.mat = ProjectiveTransform2.MatrixProduct(K.mat, p); return(S); }
private static void testLT() { // Results: inverse is broken for LinearTransform; otherwise OK. Debug.Print(new LinearTransform2() + "\nshould be identity\n"); Vector v1 = new Vector(2, 3); Vector v2 = new Vector(-1, 4); LinearTransform2 T1 = new LinearTransform2(v1, v2); Debug.Print(T1 + "\nshould be 2, -1; 3 4.\n"); Debug.Print(T1.Det() + "\n should be 11; " + T1.Trace() + "\nshould be 6\n"); Vector w1 = new Vector(0, 1); Vector w2 = new Vector(1, 1); LinearTransform2 Tvw = LinearTransform2.VectorsToVectors(v1, v2, w1, w2); LinearTransform2 T30 = LinearTransform2.RotateXY(30 * Math.PI / 180.0); LinearTransform2 Ti = T1.InverseTransform(); Debug.Print(T30 + "\nshould be Rot30; lower left = 0.5\n"); Debug.Print(Ti * v1 + "\nshould be e1\n"); Debug.Print(Ti * v2 + "\nshould be e2\n"); Debug.Print(T1 * T1 + "\nshould be [1 -6; 18 13]\n"); Debug.Print(T1 * Ti + "\n should be identity\n"); }
/// <summary> /// Compute the inverse of a 3 x 3 matrix. /// <param name="mat">A 3 x 3 matrix </param> /// <returns> The inverse of matrix mat1</returns> /// </summary> new private static double[,] MatrixInverse(double[,] mat) { double[] translation = new double[3]; translation[0] = mat[0, 2]; translation[1] = mat[1, 2]; translation[2] = 1.0d; double[,] mm = new double[3, 3]; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { mm[i, j] = mat[i, j]; } } mm[2, 2] = 1.0d; double[,] minv = LinearTransform2.MatrixInverse(mm); double[] reverseTranslation = new double[3]; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { reverseTranslation[i] -= minv[i, j] * translation[j]; } } reverseTranslation[2] = 1.0d; double[,] res = new double[3, 3]; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { res[i, j] = minv[i, j]; } res[i, 2] = reverseTranslation[i]; } res[2, 2] = 1.0d; return(res); }
private static void testPT() { Debug.Print(new ProjectiveTransform2() + "\n should be identity\n"); Vector v1 = new Vector(2, 3); Vector v2 = new Vector(-1, 4); Point p1 = new Point(1, 5); Point p2 = new Point(1, 1); Point p3 = new Point(4, 4); Point p4 = new Point(2, 5); Point q1 = new Point(1, 1); Point q2 = new Point(0, 0); Point q3 = new Point(1, 2); Point q4 = new Point(-1, 0); Point pt = p1 + 0.5 * (p2 - p1); Point qt = q1 + 0.5 * (q2 - q1); ProjectiveTransform2 T1 = ProjectiveTransform2.Translate(p1, q1); Debug.Print("T1:" + T1 * p1 + "\n should be " + q1 + "\n"); ProjectiveTransform2 T3 = ProjectiveTransform2.RotateXY(30 * Math.PI / 180); LinearTransform2 T4 = LinearTransform2.RotateXY(30 * Math.PI / 180); Debug.Print("T3,4:" + T3 + "\n should equal " + T4 + "\n"); ProjectiveTransform2 T5 = ProjectiveTransform2.AxisScale(2, -3); Debug.Print("T5:" + T5 + "\n should be [2 0 ; 0 -3]\n"); ProjectiveTransform2 T6 = ProjectiveTransform2.RotateAboutPoint(p1, 30 * Math.PI / 180); Debug.Print("T6:" + T6 * p1 + "\n should be " + p1 + "\n"); ProjectiveTransform2 T2 = ProjectiveTransform2.PointsToPoints(p1, p2, p3, p4, q1, q2, q3, q4); Debug.Print("T2:" + T2 * (p1 + 0.5 * (p2 - p1)) + "\n should be " + (q1 + 0.5 * (q2 - q1)) + "\n"); Debug.Print("T2:" + T2 * p1 + "\n should be " + q1 + "\n"); Debug.Print("T2:" + T2 * p2 + "\n should be " + q2 + "\n"); Debug.Print("T2:" + T2 * p3 + "\n should be " + q3 + "\n"); Debug.Print("T2:" + T2 * p4 + "\n should be " + q4 + "\n"); Debug.Print("InverseTransform:" + T2.InverseTransform() * q4 + "\n should be " + p4 + "\n"); Debug.Print("Composition:" + T2.InverseTransform() * T2 + "\n should be identity\n"); }
private static void testAT() { // Results: inverse is broken for LinearTransform; otherwise OK. Debug.Print("T0:" + new AffineTransform2() + "\nshould be identity\n"); Vector v1 = new Vector(2, 3); Vector v2 = new Vector(-1, 4); Vector w1 = new Vector(0, 1); Vector w2 = new Vector(1, 1); Point p1 = new Point(1, 5); Point p2 = new Point(1, 1); Point p3 = new Point(4, 4); Point p4 = new Point(2, 5); Point q1 = new Point(1, 1); Point q2 = new Point(0, 0); Point q3 = new Point(1, 2); Point q4 = new Point(-1, 0); Point pt = p1 + 0.5 * (p2 - p1); Point qt = q1 + 0.5 * (q2 - q1); AffineTransform2 T1 = AffineTransform2.Translate(p1, q1); Debug.Print("T1:" + T1 * p1 + "\n should be " + q1 + "\n"); AffineTransform2 T2 = AffineTransform2.PointAndVectorsToPointAndVectors(p1, v1, v2, q1, w1, w2); Debug.Print("T2:" + T2 * (v1 + v2) + "\n should be " + (w1 + w2) + "\n"); /// Broken Debug.Print("T2:" + T2 * p1 + "\n should be " + q1 + "\n"); AffineTransform2 T3 = AffineTransform2.RotateXY(30 * Math.PI / 180); LinearTransform2 T4 = LinearTransform2.RotateXY(30 * Math.PI / 180); Debug.Print("T3,4:" + T3 + "\n should equal " + T4 + "\n"); AffineTransform2 T5 = AffineTransform2.AxisScale(2, -3); Debug.Print("T5:" + T5 + "\n should be [2 0 ; 0 -3]\n"); AffineTransform2 T6 = AffineTransform2.RotateAboutPoint(p1, 30 * Math.PI / 180); Debug.Print("T6:" + T6 * p1 + "\n should be " + p1 + "\n"); Debug.Print("T6:" + T6 * new Vector(1, 0) + "\n should be [.866, .5]\n"); AffineTransform2 TPV = AffineTransform2.PointAndVectorsToPointAndVectors(p1, v1, v2, q1, w1, w2); // Broken Debug.Print("TPV:" + TPV * p1 + "\n should be " + q1 + "\n"); Debug.Print("TPV:" + TPV * (p1 + 0.3 * v2) + "\n should be " + (q1 + 0.3 * w2) + "\n"); AffineTransform2 T7 = AffineTransform2.PointsToPoints(p1, p2, p3, q1, q2, q3); Debug.Print("T7:" + T7 * pt + "\n should be " + qt + "\n"); Debug.Print("T7:" + T7 * p1 + "\n should be " + q1 + "\n"); Debug.Print("T7:" + T7 * p2 + "\n should be " + q2 + "\n"); Debug.Print("T7:" + T7 * p3 + "\n should be " + q3 + "\n"); Debug.Print("Inverse:" + T7 * (T7.InverseTransform()) + "\n should be identity\n"); //BROKEN AffineTransform2 T7i = T7.InverseTransform(); Debug.Print("T7i:" + T7i * q1 + "\n should be " + p1 + "\n"); Debug.Print("T7i:" + T7i * q2 + "\n should be " + p2 + "\n"); Debug.Print("T7i:" + T7i * q3 + "\n should be " + p3 + "\n"); AffineTransform2 T8 = AffineTransform2.PointsAndVectorToPointsAndVector(p1, p2, p3 - p1, q1, q2, q3 - q1); Debug.Print("T8:" + T7 * (T8.InverseTransform()) + "\n should be identity\n"); //BROKEN Debug.Print("T8:" + T8 * p1 + "\n should be " + q1 + "\n"); Debug.Print("T8:" + T8 * p2 + "\n should be " + q2 + "\n"); Debug.Print("T8:" + T8 * p3 + "\n should be " + q3 + "\n"); }