Blob Blame Raw
using System;

namespace EllipseTruncate {
	public struct Matrix {
		public double m00, m01, m02,
		              m10, m11, m12,
		              m20, m21, m22;
		
		public Matrix(Point p0, Point p1, Point p2 = new Point()) {
			m00 = p0.x; m01 = p0.y; m02 = 0.0;
			m10 = p1.x; m11 = p1.y; m12 = 0.0;
			m20 = p2.x; m21 = p2.y; m22 = 1.0;
		}

		public Matrix(Cairo.Matrix m) {
			m00 = m.Xx; m01 = m.Yx; m02 = 0.0;
			m10 = m.Xy; m11 = m.Yy; m12 = 0.0;
			m20 = m.X0; m21 = m.Y0; m22 = 1.0;
		}

		public Point row0()
			{ return new Point(m00, m01); }
		public Point row1()
			{ return new Point(m10, m11); }
		public Point row2()
			{ return new Point(m20, m21); }

		public Point row(int index) {
			switch(index) {
			case 0: return row0();
			case 1: return row1();
			case 2: return row2();
			}
			return new Point();
		}

		public static Matrix zero()
			{ return new Matrix(); }

		public static Matrix identity() {
			Matrix m = new Matrix();
			m.m00 = m.m11 = m.m22 = 1.0;
			return m;
		}

		public static Matrix rotation(double angle) {
			double s = Math.Sin(angle);
			double c = Math.Cos(angle);
			Matrix m = identity();
			m.m00 = c; m.m01 = s;
			m.m10 =-s; m.m11 = c;
			return m;
		}

		public static Matrix translation(double x, double y) {
			Matrix m = identity();
			m.m20 = x; m.m21 = y;
			return m;
		}

		public static Matrix scaling(double x, double y) {
			Matrix m = identity();
			m.m00 = x; m.m11 = y;
			return m;
		}
		
		public static Matrix translation(Point t)
			{ return translation(t.x, t.y); }
		public static Matrix scaling(Point s)
			{ return scaling(s.x, s.y); }
		public static Matrix scaling(double s)
			{ return scaling(s, s); }
		
		public Matrix rotate(double angle)
			{ return this*rotation(angle); }
		public Matrix translate(double x, double y)
			{ return this*translation(x, y); }
		public Matrix translate(Point t)
			{ return this*translation(t); }
		public Matrix scale(double x, double y)
			{ return this*scaling(x, y); }
		public Matrix scale(Point s)
			{ return this*scaling(s); }
			
		public Point transform(Point p) {
			return new Point( m00*p.x + m10*p.y + m20,
			                  m01*p.x + m11*p.y + m21 );
		}

		public Point turn(Point p) {
			return new Point( m00*p.x + m10*p.y,
			                  m01*p.x + m11*p.y );
		}

		public static Point operator* (Matrix m, Point p)
			{ return m.transform(p); }
		
		public static Matrix operator* (Matrix a, Matrix b) {
			Matrix m = new Matrix();
			
			m.m00 = a.m00*b.m00 + a.m10*b.m01 + a.m20*b.m02;
			m.m01 = a.m01*b.m00 + a.m11*b.m01 + a.m21*b.m02;
			m.m02 = a.m02*b.m00 + a.m12*b.m01 + a.m22*b.m02;
		
			m.m10 = a.m00*b.m10 + a.m10*b.m11 + a.m20*b.m12;
			m.m11 = a.m01*b.m10 + a.m11*b.m11 + a.m21*b.m12;
			m.m12 = a.m02*b.m10 + a.m12*b.m11 + a.m22*b.m12;

			m.m20 = a.m00*b.m20 + a.m10*b.m21 + a.m20*b.m22;
			m.m21 = a.m01*b.m20 + a.m11*b.m21 + a.m21*b.m22;
			m.m22 = a.m02*b.m20 + a.m12*b.m21 + a.m22*b.m22;
		
			return m;
		}
		
		public bool isEqual(Matrix other) {
			return Math.Abs(m00 - other.m00) <= Geometry.precision
			    && Math.Abs(m01 - other.m01) <= Geometry.precision
			    && Math.Abs(m02 - other.m02) <= Geometry.precision
			    && Math.Abs(m10 - other.m10) <= Geometry.precision
			    && Math.Abs(m11 - other.m11) <= Geometry.precision
			    && Math.Abs(m12 - other.m12) <= Geometry.precision
			    && Math.Abs(m20 - other.m20) <= Geometry.precision
			    && Math.Abs(m21 - other.m21) <= Geometry.precision
			    && Math.Abs(m22 - other.m22) <= Geometry.precision;
		}
		
		static double det2x2(double m00, double m01, double m10, double m11)
			{ return m00*m11 - m10*m01; }
		
		public double det() {
			return m00*m11*m22 - m00*m12*m21
			     + m01*m12*m20 - m01*m10*m22
			     + m02*m10*m21 - m02*m11*m20;
		}
		
		public Matrix invert() {
			double d = det();
			Matrix m = new Matrix();
			if (Math.Abs(d) > Geometry.precision) {
				d = 1.0/d;
				double e = -d;
				m.m00 = det2x2(m11, m12, m21, m22)*d;
				m.m10 = det2x2(m10, m12, m20, m22)*e;
				m.m20 = det2x2(m10, m11, m20, m21)*d;

				m.m01 = det2x2(m01, m02, m21, m22)*e;
				m.m11 = det2x2(m00, m02, m20, m22)*d;
				m.m21 = det2x2(m00, m01, m20, m21)*e;

				m.m02 = det2x2(m01, m02, m11, m12)*d;
				m.m12 = det2x2(m00, m02, m10, m12)*e;
				m.m22 = det2x2(m00, m01, m10, m11)*d;
			}
			return m;
		}

		public Cairo.Matrix toCairo()
			{ return new Cairo.Matrix(m00, m01, m10, m11, m20, m21); }
	}
}