/**
 * Matrix zur 3D-Transformation von Punkten. Es wird eine 4x4 Matrix benutzt, um
 * mit homogenen Koordinaten arbeiten zu kuennen. Damit lassen sich nicht nur Drehung
 * und Skalierung, sondern auch Verschiebung ueber die Multiplikation einer Matrix mit einem
 * Ortsvektor beschreiben.
 *
 * @version 1.0 vom 12.4.2011
 * @author T. Schaller
 */

public class Matrix {

    // Anfang Attribute
    private double[][] m = new double[4][4];
    // Ende Attribute

    /** Erzeugt eine neutrale Matrix, d.h. in der Diagonalen stehen Einsen, sonst Nullen.
     */
    public Matrix () {
        for (int i = 0; i< 4; i++) {
            set(i,i,1);
        }
    }

    // Anfang Methoden
    /** Multipliziert die 4x4 Matrix (eigentlich nur die obere 4x3 Matrix) mit
     * einem 3D-Vektor. Die 4. Dimension der homogenen Koordinaten wird hier nicht benuetigt.
     * @param v 3D-Vektor
     * @return 3D-Vektor
     */
    Vektor multipliziere(Vektor v) {
        return new Vektor(m[0][0]*v.getX()+m[0][1]*v.getY()+m[0][2]*v.getZ()+m[0][3],
            m[1][0]*v.getX()+m[1][1]*v.getY()+m[1][2]*v.getZ()+m[1][3],
            m[2][0]*v.getX()+m[2][1]*v.getY()+m[2][2]*v.getZ()+m[2][3]);
    }

    /** Multipliziert die 4x4 Matrix (eigentlich nur die obere 4x3 Matrix) mit
     * dem 3D-Ortsvektor eines Punktes. Die 4. Dimension der homogenen Koordinaten wird hier nicht benuetigt.
     * @param p 3D-Punkt
     * @return 3D-Punkt
     */
    Punkt multipliziere(Punkt p) {
        return new Punkt(m[0][0]*p.getX()+m[0][1]*p.getY()+m[0][2]*p.getZ()+m[0][3],
            m[1][0]*p.getX()+m[1][1]*p.getY()+m[1][2]*p.getZ()+m[1][3],
            m[2][0]*p.getX()+m[2][1]*p.getY()+m[2][2]*p.getZ()+m[2][3]);
    }

    /** Multipliziert die 4x4 Matrix mit einer anderen 4x4 Matrix.
     * @param m Matrix mit der multipliziert werden soll.
     * @return 4x4 Matrix
     */

    Matrix mulitpliziere(Matrix m) {
        double f;
        Matrix m2 = new Matrix();
        for (int i=0;i<4;i++) {
            for (int j=0;j<4;j++) {
                f =0;
                for (int k=0; k<4;k++) {
                    f += this.get(k,j) * m.get(i,k);
                }
                m2.set(i,j,f);
            }
        }
        return m2;
    }

    /** Setzt einen Koeffizienten der 4x4 Matrix.
     * @param x Spalte, in der ersetzt werden soll
     * @param y Zeile, in der ersetzt werden soll
     * @param f neuer Koeffizientenwert
     */
    public void set(int x, int y, double f) {
        m[x][y] = f;
    }

    /** Liefert einen Koeffizienten der 4x4 Matrix.
     * @param x Spalte des Koeffizienten
     * @param y Zeile des Koeffizienten
     * @return Wert des Koeffizienten
     */
    public double get(int x, int y) {
        return m[x][y];
    }

    /** Erzeugt eine Matrix fuer eine Verschiebung
     * @param x x-Komponente der Verschiebung
     * @param y y-Komponente der Verschiebung
     * @param z z-Komponente der Verschiebung
     * @return Translationsmatrix
     */
    public static Matrix Translation(double x, double y, double z) {
        Matrix m = new Matrix();
        m.set(0,3,x);
        m.set(1,3,y);
        m.set(2,3,z);
        return m;
    }

    /** Erzeugt eine Matrix fuer eine Rotation um die X-Achse
     * @param alpha Bogenmaue fuer den Winkel der Rotation
     * @return Rotationsmatrix
     */
    public static Matrix RotationX(double alpha) {
        Matrix m = new Matrix();
        m.set(1,1,Math.cos(alpha));
        m.set(1,2,-Math.sin(alpha));
        m.set(2,1,Math.sin(alpha));
        m.set(2,2,Math.cos(alpha));
        return m;
    }

    /** Erzeugt eine Matrix fuer eine Rotation um die Y-Achse
     * @param alpha Bogenmaue fuer den Winkel der Rotation
     * @return Rotationsmatrix
     */
    public static Matrix RotationY(double alpha) {
        Matrix m = new Matrix();
        m.set(0,0,Math.cos(alpha));
        m.set(0,2,Math.sin(alpha));
        m.set(2,0,-Math.sin(alpha));
        m.set(2,2,Math.cos(alpha));
        return m;
    }

    /** Erzeugt eine Matrix fuer eine Rotation um die Z-Achse
     * @param alpha Bogenmaue fuer den Winkel der Rotation
     * @return Rotationsmatrix
     */
    public static Matrix RotationZ(double alpha) {
        Matrix m = new Matrix();
        m.set(0,0,Math.cos(alpha));
        m.set(0,1,-Math.sin(alpha));
        m.set(1,0,Math.sin(alpha));
        m.set(1,1,Math.cos(alpha));
        return m;
    }

    /** Erzeugt eine Matrix fuer eine Skalierung um einen Faktor
     * @param scale Skalierungsfaktor
     * @return Skalierungsmatrix
     */
    public static Matrix Skalierung(double scale) {
        Matrix m = new Matrix();
        m.set(0,0,scale);
        m.set(1,1,scale);
        m.set(2,2,scale);
        return m;
    }

    // Ende Methoden
}
