/*
 * Author: Paul Koshevoy
 * m4x4.java  Sat, Jan 17 1998, 15:40:00 1998
 */

public class m4x4
{
public double m[][];

public m4x4(double M[][])
    {
	m = M;
    }
    

// R - rotation matrix, d - translation vector
public m4x4(m3x3 R, v3x1 d)
    {
	m = new double[4][4];
	for(int row=0; row<3; row++)
	    for(int col=0; col<3; col++)
		m[row][col] = R.m[row][col];
	for(int i=0; i<3; i++)
	    m[3][i] = 0;

	m[0][3] = d.x;
	m[1][3] = d.y;
	m[2][3] = d.z;
	m[3][3] = 1;
    }

    
public v4x1 times(v4x1 vec)
    {
	return (new v4x1(m[0][0]*vec.v[0] + m[0][1]*vec.v[1] + m[0][2]*vec.v[2] +  m[0][3]*vec.v[3],
			 m[1][0]*vec.v[0] + m[1][1]*vec.v[1] + m[1][2]*vec.v[2] +  m[1][3]*vec.v[3],
			 m[2][0]*vec.v[0] + m[2][1]*vec.v[1] + m[2][2]*vec.v[2] +  m[2][3]*vec.v[3],
			 m[3][0]*vec.v[0] + m[3][1]*vec.v[1] + m[3][2]*vec.v[2] +  m[3][3]*vec.v[3]));
    }

    
public m4x4 times(m4x4 MATRIX)
    {
	double M[][] = new double[4][4];
	for(int row=0; row<4; row++)
	    for(int col=0; col<4; col++)
		{
		    double temp = 0;
		    for(int i=0; i<4; i++)
			temp+=m[row][i]*MATRIX.m[i][col];
		    M[row][col] = temp;
		}
	return (new m4x4(M));
    }

    
public static m4x4 screen(double nx, double ny, double xmin, double ymin, double xmax, double ymax)
    {
	m4x4 T1 = m4x4.translate((nx-1)/2, (ny-1)/2, 0);
	m4x4 Sc = m4x4.scale( -nx/(xmax-xmin), -ny/(ymax-ymin), 1);
	m4x4 T2 = m4x4.translate((-xmax-xmin)/2, (-ymax-ymin)/2, 0);
	return (T1.times(Sc.times(T2)));
    }


public static m4x4 persp(double n, double f)
    {
	double M[][] = new double[4][4];
	for(int row=0; row<4; row++)
	    for(int col=0; col<4; col++)
		M[row][col]=0;
	M[0][0] = 1;
	M[1][1] = 1;
	M[2][2] = (n+f)/n;
	M[2][3] = -f;
	M[3][2] = 1/n;
	return (new m4x4(M));	
    }
    

public static m4x4 view(v3x1 lf, v3x1 la, v3x1 Vup)
    {
	v3x1 w = v3x1.unit(la.minus(lf));
	v3x1 u = v3x1.unit(v3x1.cross(Vup, w));
	v3x1 v = v3x1.cross(w, u);
	double M[][] = new double[4][4];

	M[0][0] = u.x;
	M[0][1] = u.y;
	M[0][2] = u.z;
	M[0][3] = 0;

	M[1][0] = v.x;
	M[1][1] = v.y;
	M[1][2] = v.z;
	M[1][3] = 0;

	M[2][0] = w.x;
	M[2][1] = w.y;
	M[2][2] = w.z;
	M[2][3] = 0;

	M[3][0] = 0;
	M[3][1] = 0;
	M[3][2] = 0;
	M[3][3] = 1;

	m4x4 R = new m4x4(M);
	m4x4 T = m4x4.translate(-lf.x, -lf.y, -lf.z);
//	T.dump("translate");
	return R.times(T);
    }
    
public static m4x4 translate(double x, double y, double z)
    {
	double M[][] = new double[4][4];
	for(int row=0; row<4; row++)
	    for(int col=0; col<4; col++)
		if(row == col)
		    M[row][col]=1;
		else
		    M[row][col]=0;
	M[0][3] = x;
	M[1][3] = y;
	M[2][3] = z;
	return (new m4x4(M));
    }

    
public static m4x4 scale(double sx, double sy, double sz)
    {
	double M[][] = new double[4][4];
	for(int row=0; row<4; row++)
	    for(int col=0; col<4; col++)
		M[row][col]=0;
	M[0][0] = sx;
	M[1][1] = sy;
	M[2][2] = sz;
	M[3][3] = 1;
	return (new m4x4(M));
    }


public static m4x4 MOM(v3x1 lf, v3x1 la, v3x1 Vup, double n, double f, double xmax,
		       double xmin, double ymax, double ymin, double nx, double ny,
		       double sx, double sy, double sz)
    {
	m4x4 mscal = m4x4.scale(sx, sy, sz);
	m4x4 mview = m4x4.view(lf, la, Vup);
	m4x4 mpers = m4x4.persp(n, f);
	m4x4 mscrn = m4x4.screen(nx, ny, xmin, ymin, xmax, ymax);
	m4x4 mom = mscrn.times(mpers.times(mview.times(mscal)));
	return mom;
    }

    
public void dump(String s)
    {
	System.out.println(s);
	for(int i=0; i<4; i++)
	    System.out.println("|"+m[i][0]+" "+m[i][1]+" "+m[i][2]+" "+m[i][3]+"|");
	System.out.println();
    }
}

