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 Matrix scale(double s)
{ return this*scaling(s, 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); }
}
}