+ .times(1/determinant);
+ }
+
+ public String toString() {
+ return
+ "\n [ " + a + "\t" + b + "\t" + c + "\t" + d + "\t" + "]" +
+ "\n [ " + e + "\t" + f + "\t" + g + "\t" + h + "\t" + "]" +
+ "\n [ " + i + "\t" + j + "\t" + k + "\t" + l + "\t" + "]" +
+ "\n [ " + m + "\t" + n + "\t" + o + "\t" + p + "\t" + "]\n";
+ }
+
+ public boolean equals(Object oo) {
+ if (oo==null) return false;
+ if (!(oo instanceof Matrix)) return false;
+ Matrix z = (Matrix)oo;
+ return
+ (a==z.a) &&
+ (b==z.b) &&
+ (c==z.c) &&
+ (d==z.d) &&
+ (e==z.e) &&
+ (f==z.f) &&
+ (g==z.g) &&
+ (h==z.h) &&
+ (i==z.i) &&
+ (j==z.j) &&
+ (k==z.k) &&
+ (l==z.l) &&
+ (m==z.m) &&
+ (n==z.n) &&
+ (o==z.o) &&
+ (p==z.p);
+ }
+
+ public boolean equalsModuloEpsilon(Matrix z, float epsilon) {
+ return
+ Math.abs(a-z.a)<epsilon &&
+ Math.abs(b-z.b)<epsilon &&
+ Math.abs(c-z.c)<epsilon &&
+ Math.abs(d-z.d)<epsilon &&
+ Math.abs(e-z.e)<epsilon &&
+ Math.abs(f-z.f)<epsilon &&
+ Math.abs(g-z.g)<epsilon &&
+ Math.abs(h-z.h)<epsilon &&
+ Math.abs(i-z.i)<epsilon &&
+ Math.abs(j-z.j)<epsilon &&
+ Math.abs(k-z.k)<epsilon &&
+ Math.abs(l-z.l)<epsilon &&
+ Math.abs(m-z.m)<epsilon &&
+ Math.abs(n-z.n)<epsilon &&
+ Math.abs(o-z.o)<epsilon &&
+ Math.abs(p-z.p)<epsilon;
+ }
+
+ public int hashCode() {
+ return
+ Float.floatToIntBits(a) ^
+ Float.floatToIntBits(b) ^
+ Float.floatToIntBits(c) ^
+ Float.floatToIntBits(d) ^
+ Float.floatToIntBits(e) ^
+ Float.floatToIntBits(f) ^
+ Float.floatToIntBits(g) ^
+ Float.floatToIntBits(h) ^
+ Float.floatToIntBits(i) ^
+ Float.floatToIntBits(j) ^
+ Float.floatToIntBits(k) ^
+ Float.floatToIntBits(l) ^
+ Float.floatToIntBits(m) ^
+ Float.floatToIntBits(n) ^
+ Float.floatToIntBits(o) ^
+ Float.floatToIntBits(p);
+ }
+
+ /** retrieves the current objectspace-to-windowspace projection matrix from the GL context */
+ public static Matrix getProjectionMatrix(GL gl) {
+ int view[] = new int[4];
+ double mvmatrix[] = new double[16];
+ double projmatrix[] = new double[16];
+ gl.glGetIntegerv(GL.GL_VIEWPORT, view, 0);
+ gl.glGetDoublev(GL.GL_MODELVIEW_MATRIX, mvmatrix, 0);
+ gl.glGetDoublev(GL.GL_PROJECTION_MATRIX, projmatrix, 0);
+ Matrix m = new Matrix(mvmatrix[0],
+ mvmatrix[4],
+ mvmatrix[8],
+ mvmatrix[12],
+ mvmatrix[1],
+ mvmatrix[5],
+ mvmatrix[9],
+ mvmatrix[13],
+ mvmatrix[2],
+ mvmatrix[6],
+ mvmatrix[10],
+ mvmatrix[14],
+ mvmatrix[3],
+ mvmatrix[7],
+ mvmatrix[11],
+ mvmatrix[15]);
+ Matrix p = new Matrix(projmatrix[0],
+ projmatrix[4],
+ projmatrix[8],
+ projmatrix[12],
+ projmatrix[1],
+ projmatrix[5],
+ projmatrix[9],
+ projmatrix[13],
+ projmatrix[2],
+ projmatrix[6],
+ projmatrix[10],
+ projmatrix[14],
+ projmatrix[3],
+ projmatrix[7],
+ projmatrix[11],
+ projmatrix[15]);
+ Matrix z =
+ new Matrix(0.5*view[2], 0, 0, view[0]+view[2]*0.5,
+ 0, 0.5*view[3], 0, view[1]+view[3]*0.5,
+ 0, 0, 0.5, 0.5,
+ 0, 0, 0, 1);
+ return z.times(p).times(m);
+ }
+
+ /** returns the constraint-conjunction "(forall v)Mv=v" */
+ public AffineConstraint getAffineConstraint(float epsilon) {
+ AffineConstraint c1 = getAffineConstraint(a-1, b, c, d , epsilon);
+ AffineConstraint c2 = getAffineConstraint(e, f-1, g, h , epsilon);
+ AffineConstraint c3 = getAffineConstraint(i, j, k-1, l , epsilon);
+ // FIXME: bottom row constraint?
+ return c1.intersect(c2.intersect(c3, epsilon), epsilon);
+ }
+
+ /** the AffineConstraint representing ax+by+cz+d=0 */
+ private static AffineConstraint getAffineConstraint(float a, float b, float c, float d, float epsilon) {
+ a = Math.abs(a) <= epsilon ? 0 : a;
+ b = Math.abs(b) <= epsilon ? 0 : b;
+ c = Math.abs(c) <= epsilon ? 0 : c;
+ d = Math.abs(d) <= epsilon ? 0 : d;
+ if (a!=0 || b!=0 || c!=0) return new Plane(a, b, c, d);
+ if (d==0) return AffineConstraint.ALL;
+ return AffineConstraint.NONE;