
public class Quadratic extends Shape {
   double A,B,C,D,E,F,G,H,I,J, _A,_B,_C,_D,_E,_F,_G,_H,_I,_J;

   int nClipPlanes = 0;
   double cp[][] = new double[10][4], _cp[][] = new double[10][4];

   void addClipPlane(double a, double b, double c, double d) {
      cp[nClipPlanes][0] = _cp[nClipPlanes][0] = a;
      cp[nClipPlanes][1] = _cp[nClipPlanes][1] = b;
      cp[nClipPlanes][2] = _cp[nClipPlanes][2] = c;
      cp[nClipPlanes][3] = _cp[nClipPlanes][3] = d;
      nClipPlanes++;
   }

   Quadratic(double c[]) {
      A=c[0];B=c[1];C=c[2];D=c[3];E=c[4];F=c[5];G=c[6];H=c[7];I=c[8];J=c[9];
      _A=A;_B=B;_C=C;_D=D;_E=E;_F=F;_G=G;_H=H;_I=I;_J=J;
   }
   void transform() {
      double m[][] = { { cos, 0, sin, x},
	               {   0, 1,   0, y},
	               {-sin, 0, cos, z},
	               {   0, 0,   0, 1} };
      transform(m);
   }
   double m[][] = new double[4][4];
   void rotate(double theta) {
      Matrix.identity(m);
      Matrix.rotateY(m, theta);
      transform(m);
   }

   void transform(double m[][]) {
      Matrix.invert(m, matrix);

      // --------------------------------------------------------------------
      // TRANSFORM EQUATION OF SECOND ORDER POLYNOMIAL AS FOLLOWS:
      //
      // APPLY POLYNOMIAL Axx + Byy + Czz + Dyz + Ezx + Fxy + Gx + Hy + Iz + J
      //
      // TO TRANSFORMED POINT [ ax+dy+gz+j , bx+ey+hz+k , cx+fy+iz+l ] TO GET:
      //
      //    A(ax+dy+gz+j)^2 + B(bx+ey+hz+k)^2 + ... + I(cx+fy+iz+l) + J
      //
      // AND GET NEW VALUES FOR A...J BY REGROUPING COEFFICIENTS.
      // --------------------------------------------------------------------

      double a = matrix[0][0], b = matrix[1][0], c = matrix[2][0];
      double d = matrix[0][1], e = matrix[1][1], f = matrix[2][1];
      double g = matrix[0][2], h = matrix[1][2], i = matrix[2][2];
      double j = matrix[0][3], k = matrix[1][3], l = matrix[2][3];

      A = _A*a*a + _B*b*b + _C*c*c + _D*b*c + _E*c*a + _F*a*b;
      B = _A*d*d + _B*e*e + _C*f*f + _D*e*f + _E*f*d + _F*d*e;
      C = _A*g*g + _B*h*h + _C*i*i + _D*h*i + _E*i*g + _F*g*h;
      D = 2*(_A*d*g + _B*e*h + _C*f*i) + _D*(e*i+h*f) + _E*(f*g+i*d) + _F*(d*h+g*e);
      E = 2*(_A*g*a + _B*h*b + _C*i*c) + _D*(h*c+b*i) + _E*(i*a+c*g) + _F*(g*b+a*h);
      F = 2*(_A*a*d + _B*b*e + _C*c*f) + _D*(b*f+e*c) + _E*(c*d+f*a) + _F*(a*e+d*b);
      G = 2*(_A*a*j + _B*b*k + _C*c*l) + _G*a + _H*b + _I*c;
      H = 2*(_A*d*j + _B*e*k + _C*f*l) + _G*d + _H*e + _I*f;
      I = 2*(_A*g*j + _B*h*k + _C*i*l) + _G*g + _H*h + _I*i;
      J = _A*j*j + _B*k*k + _C*l*l + _D*k*l + _E*l*j + _F*j*k + _G*j + _H*k + _I*l + _J;

// TRANSFORM EQUATIONS OF CLIPPING PLANES:

      for (int I = 0 ; I < nClipPlanes ; I++) {
         cp[I][0] = _cp[I][0] * a + _cp[I][1] * b + _cp[I][2] * c;
         cp[I][1] = _cp[I][0] * d + _cp[I][1] * e + _cp[I][2] * f;
         cp[I][2] = _cp[I][0] * g + _cp[I][1] * h + _cp[I][2] * i;
         cp[I][3] = _cp[I][0] * j + _cp[I][1] * k + _cp[I][2] * l + _cp[I][3];
      }
   }
   int traceRay(double V[], double t[]) {
      int nroots = quadratic(t,                                    // SOLVE Att + Bt + C:
         A*V[0]  + B*V[1]  + C*V[2]  + D*V[3]  + E*V[4]  + F*V[5]  ,      // A

         A*V[6]  + B*V[7]  + C*V[8]  + D*V[9]  + E*V[10] + F*V[11] +
	                               G*V[12] + H*V[13] + I*V[14] ,      // B

         A*V[15] + B*V[16] + C*V[17] + D*V[18] + E*V[19] + F*V[20] +
	                               G*V[21] + H*V[22] + I*V[23] + J);  // C

      // --------------------------------------------------------------------
      // APPLY CLIP PLANE [a,b,c,d] TO POINT (v + t w) TO GET:
      //
      //    a(vx + t wx) + b(vy + t wy) + c(vz + t wz) + d >= 0
      //
      // WHICH IS THE SAME AS:
      //
      //    t * (a wx + b wy + c wz) + (a vx + b vy + c vz + d) >= 0
      // --------------------------------------------------------------------

      if (nroots > 0 && nClipPlanes > 0) {
         for (int i = 0 ; i < nClipPlanes ; i++) {
	     double A = 2*(cp[i][0]*V[12] + cp[i][1]*V[13] + cp[i][2]*V[14]);
	     double B = cp[i][0]*V[21]+cp[i][1]*V[22]+cp[i][2]*V[23]+cp[i][3];

	     if (A > 0 && t[0] * A + B < 0)   // IF NEAR CLIP PLANE IS AFTER 1ST ROOT
		t[0] = -B / A;                //    THEN REPLACE 1ST ROOT
             if (A < 0 && t[1] * A + B < 0)   // IF FAR CLIP PLANE IS BEFORE 2ND ROOT
		t[1] = -B / A;                //    THEN REPLACE 2ND ROOT
         }
         if (t[0] > t[1]) // SEE WHETHER CLIPPING HAS RESULTED IN NO ROOTS
	     nroots = 0;
      }
      return nroots;
   }
   int quadratic(double t[], double A, double B, double C) {
      double d = B * B - A * C;
      if (d < 0)
	 return 0;
      d = Math.sqrt(d);
      t[0] = (-B - d) / A;
      t[1] = (-B + d) / A;
      return 2;
   }
   void computeNormal(double p[], double n[]) {
      n[0] = 2*A*p[0] + E*p[2] + F*p[1] + G;
      n[1] = 2*B*p[1] + D*p[2] + F*p[0] + H;
      n[2] = 2*C*p[2] + D*p[1] + E*p[0] + I;
      normalize(n);
   }
   static void compute_V(double v[], double w[], double V[]) {
      double vx = v[0], vy = v[1], vz = v[2], wx = w[0], wy = w[1], wz = w[2];

      V[0] = wx*wx; V[1] = wy*wy; V[2] = wz*wz;
      V[3] = wy*wz; V[4] = wz*wx; V[5] = wx*wy;
      V[6] = vx*wx; V[7] = vy*wy; V[8] = vz*wz;
      V[ 9] = .5*(vy*wz + vz*wy);
      V[10] = .5*(vz*wx + vx*wz);
      V[11] = .5*(vx*wy + vy*wx);
      V[12] = .5*wx; V[13] = .5*wy; V[14] = .5*wz;
      V[15] = vx*vx; V[16] = vy*vy; V[17] = vz*vz;
      V[18] = vy*vz; V[19] = vz*vx; V[20] = vx*vy;
      V[21] =    vx; V[22] =    vy; V[23] =    vz;
   }
}


