/*
cube.java           by Gengbin Zheng
using Z-Buffer to perform hidden surface elimination
*/

import java.awt.*;
import java.applet.*;

public class cube extends Applet implements Runnable {
   Button moveButton, scaleButton, RotateButton, resetButton;
   Checkbox slowCheck;
   myCanvas canvas;
   TextField statusBar;
   Thread thread;

   public void init()
   {
      GridBagLayout layout = new GridBagLayout();
      setLayout(layout);

      moveButton = new Button("Move");
      scaleButton = new Button("Scale");
      RotateButton = new Button("Rotate");
      resetButton = new Button("Reset");
      slowCheck = new Checkbox("Too slow?", null, false);

      GridBagConstraints constraints = new GridBagConstraints();

      canvas = new myCanvas(slowCheck);
      constraints.fill = GridBagConstraints.BOTH;
      constraints.weightx = 1;
      constraints.gridheight = 5;
      constraints.gridwidth = GridBagConstraints.RELATIVE;
      constraints.weighty = 1;
      layout.setConstraints(canvas, constraints);
      add(canvas);

      constraints.fill = GridBagConstraints.BOTH;
      constraints.weightx = 0;
      constraints.gridheight = 1;
      constraints.gridwidth = GridBagConstraints.REMAINDER;
      layout.setConstraints(moveButton, constraints);
      add(moveButton);

      layout.setConstraints(scaleButton, constraints);
      add(scaleButton);

      layout.setConstraints(RotateButton, constraints);
      add(RotateButton);

      layout.setConstraints(resetButton, constraints);
      add(resetButton);

      layout.setConstraints(slowCheck, constraints);
      add(slowCheck);

      statusBar = new TextField("Move");
      statusBar.setEditable(false);
      constraints.fill = GridBagConstraints.HORIZONTAL;
      constraints.weightx = 1;
      constraints.gridheight = 1;
      constraints.gridwidth = GridBagConstraints.REMAINDER;
      constraints.weighty = 0;
      layout.setConstraints(statusBar, constraints);
      add(statusBar);

      resize(450,350);
   }

   public void start()
   {
      thread = new Thread(this);
      thread.start();
   }

   public void run()
   {
   }

   public void stop()
   {
      thread.stop();
   }

   void HandleButtons(Object label)
   {
      String helpMsg;

      if (label == "Move")
	helpMsg = "Move";
      else if (label == "Scale")
	helpMsg = "Scale";
      else if (label == "Rotate")
	helpMsg = "Rotate";
      else
	helpMsg = "";

      if (helpMsg != "") statusBar.setText(helpMsg);
      canvas.HandleButtons(label);
   }

   public boolean action(Event evt, Object arg)
   {
     if (evt.target instanceof Button)
     {
	HandleButtons(arg);
     }
     else if (evt.target instanceof Checkbox)
     {
	canvas.HandleCheckbox(evt);
     }
     return true;
   }
}

class myCanvas extends Canvas {
   Mycube mycube;
   Point oldPt, newPt;
   boolean working;
   // double buffering
   Image img = null;
   Graphics backg;
   final int MOVE=1, SCALE=2, ROTATE=3;
   int action;
   Checkbox slowCheck;

   public myCanvas(Checkbox box)
   {
     mycube = new Mycube();
     working = false;
     action = MOVE;
     slowCheck = box;
   }

   void setcursor(boolean working)
   {
     Cursor curs;
     if (working)
	curs = new Cursor(Cursor.HAND_CURSOR);
     else
        curs = new Cursor(Cursor.DEFAULT_CURSOR);
     setCursor(curs);
   }

   public boolean mouseDown(Event evt, int x, int y)
   {
     if (action == MOVE)
     {
      if (working == false)
      {
	 working = true;
         oldPt = new Point(x,y);
	 mycube.setShow(!slowCheck.getState());
	 setcursor(true);
      }
     }
     if (action == SCALE)
     {
      if (working == false)
      {
	 working = true;
         oldPt = new Point(x,y);
	 mycube.setShow(!slowCheck.getState());
	 setcursor(true);
      }
     }
     if (action == ROTATE)
     {
       if (working == false)
       {
	 working = true;
         oldPt = new Point(x,y);
	 mycube.setShow(!slowCheck.getState());
	 setcursor(true);
       }
     }

     return true;
   }

   public boolean mouseUp(Event evt, int x, int y)
   {
      if (working)
      {
	 working = false;
	 mycube.setShow(true);
	 if (slowCheck.getState() == true) repaint();
	 setcursor(false);
      }
      return true;
   }

   public boolean mouseDrag(Event evt, int x, int y)
   {
      if (action == MOVE)
      {
      if (working)
      {
	 newPt = new Point(x,y);
	 mycube.move(oldPt, newPt);
	 oldPt = newPt;
	 repaint();
      }
      }
      if (action == SCALE)
      {
	if (working)
	{
	 newPt = new Point(x,y);
	 mycube.scale(oldPt, newPt);
	 oldPt = newPt;
	 repaint();
	}
      }
      if (action == ROTATE)
      {
	if (working)
	{
	 newPt = new Point(x,y);
	 mycube.rotate(oldPt, newPt);
	 oldPt = newPt;
	 repaint();
        }
      }
      return true;
   }

   public void HandleButtons(Object label)
   {
      if (label == "Move")
      {
	action = MOVE;
      }
      else if (label == "Scale")
      {
	action = SCALE;
      }
      else if (label == "Rotate")
      {
	action = ROTATE;
      }
      else if (label == "Reset")
      {
	mycube.reset();
	repaint();
      }
   }

   public void HandleCheckbox(Event evt)
   {
      String label = slowCheck.getLabel();

      if (label == "Too slow?")
      {
//	 mycube.setShow(!slowCheck.getState());
      }
   }

   public void paint(Graphics g) 
   {
     update(g);
   }

   public void update(Graphics g) 
   {
     Dimension d=size();


     if (img == null)
     {
       img = createImage(d.width, d.height);
       backg = img.getGraphics();
     }

     backg.setColor(new Color(255,255,255));    //Set color for background
     backg.fillRect(0,0, d.width, d.height);  //Draw Backround

     mycube.draw(g, backg, d);

     g.drawImage(img, 0, 0, this);
   }
}


class Mycube {
   CVector  points[][];
   CVector  v[];
   int nPlanes;
   int nVertex;
   final int size = 60;
   final int SHIFT = 130;
   final int zshift = 300;
   final int BUFWID = 400;
   final int BUFHIG = 350;
   double a,b,c,d;
   double aa,bb,cc,dd;
//   int      maxx,maxy;
//   int      pcb,pct,pcl,pcr;
   int             xpc1,ypc1,xpc2,ypc2;
   P_Y_TONG py_tong[];
   Y_TONG   y_tong[];
   P_Y_TONG p_aet;
   EDGE_ELE edge_aet;
   double buf[];
   Color planeColor[];
   CVector center;
   boolean show;

   // tmp
   double cosv, sinv;

   class P_Y_TONG
   {
      double a,b,c,d;
      int deltay;
      int id;
      Color color;
      P_Y_TONG  next;
   }

   class Y_TONG
   {
      double xup;
      double deltax;
      int deltay;
      int id;
      Y_TONG next;
   }

   class EDGE_ELE
   {
     double xl, deltaxl;
     int deltayl;
     double xr, deltaxr;
     int deltayr;
     double zl, deltazx, deltazy;
     int id;
     EDGE_ELE next;
   }

   Mycube()
   {
     int i;

     nPlanes = 6;
     nVertex = 4;

     v = new CVector[8];
     for (i=0; i<8; i++) v[i] = new CVector();
     init_pos(v);

     points = new CVector[6][4];
     int plane[] = new int[4];

     plane[0]=0 ; plane[1]=1; plane[2]=2; plane[3]=3;
     for (i=0; i<4 ; i++)
	points[0][i] = v[plane[i]];

     plane[0]=4 ; plane[1]=5; plane[2]=6; plane[3]=7;
     for (i=0; i<4 ; i++)
	points[1][i] = v[plane[i]];

     plane[0]=1 ; plane[1]=2; plane[2]=6; plane[3]=5;
     for (i=0; i<4 ; i++)
	points[2][i] = v[plane[i]];

     plane[0]=0 ; plane[1]=3; plane[2]=7; plane[3]=4;
     for (i=0; i<4 ; i++)
	points[3][i] = v[plane[i]];

     plane[0]=2 ; plane[1]=3; plane[2]=7; plane[3]=6;
     for (i=0; i<4 ; i++)
	points[4][i] = v[plane[i]];

     plane[0]=1 ; plane[1]=0; plane[2]=4; plane[3]=5;
     for (i=0; i<4 ; i++)
	points[5][i] = v[plane[i]];

     planeColor = new Color[nPlanes];
     for (i=0; i<nPlanes; i++)
     {
	switch (i)
	{
	case 0:
	case 1:
	     planeColor[i] = new Color(255,0,0); break;
        case 2:
	case 3:
	     planeColor[i] = new Color(0, 255, 0); break;
        case 4:
	case 5:
	     planeColor[i] = new Color(0, 0, 255); break;
        }
	// randomely
//	planeColor[i] = new Color((int)(Math.random()*255), (int)(Math.random()*255), (int)(Math.random()*255));
     }
     py_tong = new P_Y_TONG[BUFHIG+10];
     y_tong = new Y_TONG[BUFHIG+10];
     p_aet= new P_Y_TONG();
     edge_aet = new EDGE_ELE();
     buf = new double[BUFWID];

     center = new CVector();
     show = true;

     init3d();
   }

   void init_pos(CVector v[])
   {
     int i;
     v[0].set(0,0,0);
     v[1].set(0,size,0);
     v[2].set(size,size,0);
     v[3].set(size,0,0);
     v[4].set(0,0,size);
     v[5].set(0,size,size);
     v[6].set(size,size,size);
     v[7].set(size,0,size);

     CVector vshift = new CVector(SHIFT,SHIFT,SHIFT);

     for (i=0; i<8; i++) v[i].add(vshift);

     CVector vcenter = new CVector();
     vcenter.x = (v[0].x + v[6].x)/2;
     vcenter.y = (v[0].y + v[6].y)/2;
     vcenter.z = (v[0].z + v[6].z)/2;
     for (i=0; i<8; i++)
     {
	v[i].sub(vcenter);
	v[i].rotatex(45.0);
        v[i].rotatey(45.0);
        v[i].rotatez(45.0);
        v[i].add(vcenter);
     }
   }

   void init3d()
   {
     final double VXL=-200.0,VXR=200.0,WXR=200.0,WXL=-200.0;
     final double VYT=200.0,VYB=-200.0,WYT=200.0,WYB=-200.0;

     a=(VXR-VXL)/(WXR-WXL);
     b=VXL-a*WXL;
     c=(VYT-VYB)/(WYT-WYB);
     d=VYB-c*WYB;
   }

/*
   void set_abcd(int mx, int my)
   {
      maxx=mx;
      maxy=my;
      pcb=maxy-1;   pct=0;
      pcr=maxx-1;   pcl=0;
      a=(pcr-pcl)/(1+1);
      b=pcl-a*(-1);
      c=(pct-pcb)/(1+1);
      d=pcb-c*(-1);
   }
*/

   void get_abcd(CVector v1, CVector v2, CVector v3)
   {
      CVector t1,t2;
      t1 = new CVector(v1);
      t1.sub(v3);
      t2 = new CVector(v1);
      t2.sub(v2);
      aa = t1.y*t2.z  - t1.z*t2.y;
      bb=-t1.x*t2.z + t1.z*t2.x;
      cc=t1.x*t2.y - t2.x*t1.y;
      dd=-(aa*v1.x+bb*v1.y+cc*v1.z);
   }

   Color GetColor(int id)
   {
      P_Y_TONG ptr;
      for (ptr=p_aet ; ptr != null; ptr=ptr.next)
	  if (ptr.id == id ) return ptr.color;
      return new Color(0,0,0);
   }

   P_Y_TONG  GetP_Y_Tong(int id)
   {
      P_Y_TONG ptr;
      for (ptr=p_aet ; ptr!=null; ptr=ptr.next)
	  if (ptr.id == id ) return ptr;
      return null;   
   }

   EDGE_ELE  GetEdgePair(int col,P_Y_TONG py_ptr1)
   {
      Y_TONG y_ptr,yl_ptr,yr_ptr;
      int flag;
      EDGE_ELE  e_ptr;

      flag=0;
      yl_ptr=yr_ptr=null;
      for (y_ptr=y_tong[col] ; y_ptr != null ; y_ptr=y_ptr.next) 
      {
	 if (y_ptr.id != py_ptr1.id) continue;
	 // parallel line
	 if (y_ptr.deltay == 0) continue;
	 if (flag == 0) {
	    yl_ptr=y_ptr;
	    flag++;
         }
	 else 
	    yr_ptr=y_ptr;
      }
      // no proper edge !
      if (yl_ptr==null) return null;
      if (yl_ptr.xup > yr_ptr.xup || (yl_ptr.xup==yr_ptr.xup && yl_ptr.deltax > yr_ptr.deltax))
      {
	 y_ptr=yl_ptr;
	 yl_ptr=yr_ptr;
	 yr_ptr=y_ptr;
      }
      e_ptr= new EDGE_ELE();
      e_ptr.id =py_ptr1.id;
      e_ptr.xl=yl_ptr.xup;
      e_ptr.deltaxl=yl_ptr.deltax;
      e_ptr.deltayl=yl_ptr.deltay;
      e_ptr.xr=yr_ptr.xup;
      e_ptr.deltaxr=yr_ptr.deltax;
      e_ptr.deltayr=yr_ptr.deltay;
      e_ptr.zl= py_ptr1.c !=0 ?-(py_ptr1.d+ py_ptr1.a*yl_ptr.xup + py_ptr1.b*col)/py_ptr1.c:0;
      e_ptr.deltazx = py_ptr1.c !=0? -py_ptr1.a/py_ptr1.c:0;
      e_ptr.deltazy = py_ptr1.c !=0 ?  py_ptr1.b/py_ptr1.c:0;
      return e_ptr;
   }

   void z_buffer(Graphics g, int width, int height)
   {
     int i,j;
     int ymax, ymin, yy, maxy, miny, maxx, minx;
     P_Y_TONG py_ptr, py_head, py_ptr1;
     Y_TONG   y_ptr,y_head,yl_ptr;
     EDGE_ELE e_ptr,e_tmp;
     CVector v1, v2, v3;

     for (i=0; i<BUFHIG; i++)
     {
	py_tong[i] = null;
	y_tong[i] = null;
     }
     p_aet = null;
     edge_aet = null;

     // create Polygon Y Tong  & Y Tong
     maxy=-65530; miny=65530;
     maxx=-65530; minx=65530;
     for (i=0; i<nPlanes; i++)
     {
	py_ptr = new P_Y_TONG();
	py_ptr.id = i;
	py_ptr.color = planeColor[i];
	v1 = points[i][0];
	v2 = points[i][1];
	v3 = points[i][2];
	get_abcd(v1,v2,v3);
	py_ptr.a = aa;
	py_ptr.b = bb;
	py_ptr.c = cc;
	py_ptr.d = dd;
	ymax=-65530; ymin=65530;
	for (j=1; j<= nVertex; j++)
	{
	  y_ptr = new Y_TONG();
	  y_ptr.id=i;
	  v1 = points[i][j-1];
	  v2 = points[i][j%nVertex];
	  xpc1=(int)v1.x;                ypc1=(int)v1.y;
	  xpc2=(int)v2.x;                ypc2=(int)v2.y;
	  double del = 1.0*(xpc2-xpc1)/(ypc2-ypc1);
	  if (ypc1 > BUFHIG) {
	     ypc1 = BUFHIG-1;
	     xpc1 = (int)(xpc2 - del*(ypc2-ypc1));
          }
	  if (ypc2 > BUFHIG) {
	     ypc2 = BUFHIG-1;
	     xpc2 = (int)(v1.x + del*(ypc2-v1.y));
          }
	  y_ptr.deltay=Math.abs(ypc2 - ypc1);
	  y_ptr.deltax=ypc2==ypc1?0:-del;
	  y_ptr.xup=ypc2 > ypc1 ? xpc2 : xpc1;
	  if (ypc1>ymax) ymax=ypc1;
	  if (ypc1<ymin) ymin=ypc1;
	  if (ypc2>ymax) ymax=ypc2;
	  if (ypc2<ymin) ymin=ypc2;
	  // insert to Y Tong
	  yy=Math.max(ypc1,ypc2);
	  if (yy>=0 && yy<BUFHIG)
	  {
	     y_head=y_tong[yy];
	     y_ptr.next=y_head;
	     y_tong[yy]=y_ptr;
	  }
	}
	py_ptr.deltay=ymax-ymin;
	if (ymax >= 0 && ymax<BUFHIG)
	{
	  py_head=py_tong[ymax];
	  py_ptr.next=py_head;
	  py_tong[ymax]=py_ptr;
	}
	if (ymax > maxy) maxy = ymax;
	if (ymin < miny) miny = ymin;
	/*
	if (xpc1 > maxx) maxx = xpc1;
	if (xpc1 < minx) minx = xpc1;
	if (xpc2 > maxx) maxx = xpc2;
	if (xpc2 < minx) minx = xpc2;
	*/
     }

     for (i=BUFHIG-1; i>=0 ; i--)
//     for (i=maxy; i>=miny ; i--)
     {
	for (j=0; j<BUFWID;j++)  buf[j]=-65530.0;
	// new P_Y tong insert
	if (py_tong[i] != null)
	{
	   for (py_ptr=py_tong[i] ; py_ptr.next != null ; py_ptr=py_ptr.next);
	   py_ptr.next=p_aet;
	   p_aet=py_tong[i];
	   // new line pairs
	   for (py_ptr1=p_aet; py_ptr1 != py_ptr.next ; py_ptr1=py_ptr1.next)
	   {
	      e_ptr=GetEdgePair(i,py_ptr1);
	      if (e_ptr == null) continue;
	      e_ptr.next=edge_aet;
	      edge_aet=e_ptr;
	   }
	}
	for (e_ptr=edge_aet; e_ptr != null; e_ptr=e_ptr.next)
	{
	   if (e_ptr.deltayl != 0 && e_ptr.deltayr != 0) continue;
	   if (e_ptr.deltayl == 0 && e_ptr.deltayr == 0)
	   {
	      py_ptr=GetP_Y_Tong(e_ptr.id);
	      if (py_ptr != null) 
	      {
		e_tmp=GetEdgePair(i,py_ptr);
		if (e_tmp != null) 
		{
		   e_tmp.next=edge_aet;
		   edge_aet=e_tmp;
                }
	      }
	      continue;
	   }
	   // search new line
	   yl_ptr=null;
	   for (y_ptr=y_tong[i] ; y_ptr != null;y_ptr=y_ptr.next) 
	   {
	       if (y_ptr.id != e_ptr.id ) continue;
	       if (y_ptr.deltay == 0) continue;
	       break;
	   }
	   if (y_ptr != null) 
	   {
	       double tmpx,tmpdx;
	       int   tmpdy;
	       tmpx=e_ptr.deltayl != 0? e_ptr.xl: e_ptr.xr;
	       tmpdx=e_ptr.deltayl != 0? e_ptr.deltaxl: e_ptr.deltaxr;
	       tmpdy=e_ptr.deltayl != 0? e_ptr.deltayl: e_ptr.deltayr;
	       if (tmpx < y_ptr.xup || (tmpx==y_ptr.xup && tmpdx<y_ptr.deltax)) 
	       {
		  e_ptr.xl=tmpx;
		  e_ptr.deltaxl=tmpdx;
		  e_ptr.deltayl=tmpdy;
		  e_ptr.xr = y_ptr.xup;
		  e_ptr.deltaxr=y_ptr.deltax;
		  e_ptr.deltayr=y_ptr.deltay;
	       }
	       else {
		  e_ptr.xr=tmpx;
		  e_ptr.deltaxr=tmpdx;
		  e_ptr.deltayr=tmpdy;
		  e_ptr.xl = y_ptr.xup;
		  e_ptr.deltaxl=y_ptr.deltax;
		  e_ptr.deltayl=y_ptr.deltay;
	       }
	   }
	}
	//
	for (e_ptr=edge_aet ; e_ptr != null; e_ptr=e_ptr.next)
	{
	   int k;
	   double zx;
	   Color acolor;

	   if (e_ptr.deltayl ==0 && e_ptr.deltayr==0 ) continue;
	   if (e_ptr.xl > e_ptr.xr) continue;
	   zx=e_ptr.zl;
	   // tranform
	   xpc1=(int)(a*e_ptr.xl+b+0.5);
	   ypc1=(int)(c*i+d+0.5);
	   xpc2=(int)(a*e_ptr.xr+b+0.5);
	   int s0, s1;
	   s0 = s1= -1;
	   acolor=GetColor(e_ptr.id);
	   g.setColor(acolor);
	   for (k=xpc1 ; k<=xpc2 ; k++) 
	   {
	     zx+=e_ptr.deltazx;
	     if (k>=0 && k< BUFWID && zx > buf[k]) {
		 buf[k]=zx;
		 if (s0 == -1) s0 = s1 = k;
		 else {
		   if (k == s1+1) s1++;
		   else {
	            g.drawLine(s0, ypc1, s1, ypc1);
		    s0 = s1 = -1;
                   }
		 }
	     }
	   }
	   if (s0 != -1)
	      g.drawLine(s0, ypc1, s1, ypc1);
	}
	// modified Y_AET table
	for (e_ptr=edge_aet ; e_ptr != null; e_ptr=e_ptr.next)
	{
	   if (e_ptr.deltayl ==0 && e_ptr.deltayr==0 ) continue;
	   e_ptr.deltayl--;
	   e_ptr.deltayr--;
	   e_ptr.xl+=e_ptr.deltaxl; 
	   e_ptr.xr+=e_ptr.deltaxr;
	   e_ptr.zl+=e_ptr.deltazx*e_ptr.deltaxl + e_ptr.deltazy;
	}
	// modified P_Y_tong
	for (py_ptr=p_aet ; py_ptr != null; py_ptr=py_ptr.next)
	      if (py_ptr.deltay != 0) py_ptr.deltay--;
     }
   }

   double dot(CVector v1,CVector v2)
   {
     return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z;
   }

   void move(Point pt1, Point pt2)
   {
     center.x = (points[0][0].x + points[1][2].x)/2;
     center.y = (points[0][0].y + points[1][2].y)/2;
     center.z = (points[0][0].z + points[1][2].z)/2;

     CVector v1=new CVector(pt1.x,pt1.y,zshift); 
     CVector v2=new CVector(pt2.x,pt2.y,zshift);
     v1.sub(center);   v2.sub(center);
     CVector dist = new CVector();
     dist.x = v2.x - v1.x;
     dist.y = v2.y - v1.y;
     dist.z = v2.z - v1.z;

     for (int i=0; i<4; i++)
     {
        v1=points[0][i];
	v1.sub(center);
	v1.add(dist);
        v1.add(center);

	v2=points[1][i];
	v2.sub(center);
	v2.add(dist);
        v2.add(center);
     }
   }

   void scale(Point pt1, Point pt2)
   {
     double scale;

     center.x = (points[0][0].x + points[1][2].x)/2;
     center.y = (points[0][0].y + points[1][2].y)/2;
     center.z = (points[0][0].z + points[1][2].z)/2;
     
     CVector v1=new CVector(pt1.x,pt1.y,zshift); 
     CVector v2=new CVector(pt2.x,pt2.y,zshift);
     v1.sub(center);   v2.sub(center);
     scale = v2.mag()/v1.mag();

     for (int i=0; i<4; i++)
     {
        v1=points[0][i];
	v1.sub(center);
	v1.mul(scale);
        v1.add(center);

	v2=points[1][i];
	v2.sub(center);
	v2.mul(scale);
        v2.add(center);
     }
   }

   void cal_cossin(int dim,CVector v1,CVector v2)
   {
     double t1,t2,t;

     cosv=dot(v1.unit(),v2.unit());
     t=1.0-cosv*cosv;
     if (t<1e-5) sinv=0.0;
     else sinv=Math.sqrt(t);
     switch (dim)
     {
     case 1:
	 t1=Math.atan2(v1.z,v1.y);  t2=Math.atan2(v2.z,v2.y);
	 break;
     case 2:
	 t1=Math.atan2(v1.x,v1.z);    t2=Math.atan2(v2.x,v2.z);
	 break;
     case 3:
	 t1=Math.atan2(v1.y,v1.x);    t2=Math.atan2(v2.y,v2.x);
	 break;
     default:
	 t1 = t2 = 0;
     }
     if (t1 * t2 < 0 && Math.max(t1,t2) > 3.14159265/2.0 )
     {
	 if (t1<0) t1+=2.0*3.1415;
	 else t2+=2.0*3.1415;
     }
     if ( t1 > t2)   sinv=-sinv;
   }

   void rotate(Point pt1, Point pt2)
   {
     double cosx,sinx,cosy,siny,cosz,sinz;

     center.x = (points[0][0].x + points[1][2].x)/2;
     center.y = (points[0][0].y + points[1][2].y)/2;
     center.z = (points[0][0].z + points[1][2].z)/2;
     
     CVector v1=new CVector(0,pt1.y,zshift); 
     CVector v2=new CVector(0,pt2.y,zshift);
     v1.sub(center);   v2.sub(center);
     cal_cossin(1,v1,v2);
     cosx = cosv; sinx = sinv;

     v1.x = pt1.x; v1.y = 0; v1.z = zshift;
     v2.x = pt2.x; v2.y = 0; v2.z = zshift;
     v1.sub(center);   v2.sub(center);
     cal_cossin(2,v1,v2);
     cosy = cosv; siny = sinv;

     v1.x = pt1.x; v1.y = pt1.y; v1.z = 0;
     v2.x = pt2.x; v2.y = pt2.y; v2.z = 0;
     v1.sub(center);   v2.sub(center);
     cal_cossin(3,v1,v2);
     cosz = cosv; sinz = sinv;

     for (int i=0; i<4; i++)
     {
	v1 = points[0][i];
	v1.sub(center);
	v1.rotatex(cosx,sinx);
	v1.rotatey(cosy,siny);
	v1.rotatez(cosz,sinz);
	v1.add(center);

	v2 = points[1][i];
	v2.sub(center);
	v2.rotatex(cosx,sinx);
	v2.rotatey(cosy,siny);
	v2.rotatez(cosz,sinz);
	v2.add(center);
     }
   }

   void reset()
   {
     init_pos(v);
   }

   void draw(Graphics g, Graphics backg, Dimension d)
   {
     if (!show)
     {
     CVector v1,v2;
     backg.setColor(new Color(0,0,0));    //Set color for background
     for (int i=0; i< nPlanes; i++)
     {
       for (int j=1; j <= nVertex; j++)
       {
	  v1 = points[i][j-1];
	  v2 = points[i][j%nVertex];
	  backg.drawLine((int)v1.x, (int)v1.y, (int)v2.x, (int)v2.y);
       }
     }
     }
     else
       z_buffer(backg, d.width, d.height);
   }

   void setShow(boolean val)
   {
     show = val;
   }
}

class CVector {
   double x,y,z;

   CVector() 
   {
     x = 0.0; y = 0.0; z = 0.0;
   }

   CVector(double xx, double yy, double zz)
   {
     x = xx; y = yy; z = zz;
   }

   CVector(CVector a)
   {
     x = a.x; y = a.y; z = a.z;
   }

   void copy(CVector c)
   {
     x = c.x; y = c.y; z = c.z;
   }

   void set(double xx, double yy, double zz)
   {
     x = xx; y = yy; z = zz;
   }

   double mag()
   {
     double m;
     m=Math.sqrt(x*x+y*y+z*z);
     return m;
   }

   void add (CVector v2)
   {
     x = x + v2.x;
     y = y + v2.y;
     z = z + v2.z;
   }

   void sub (CVector v2)
   {
     x = x - v2.x;
     y = y - v2.y;
     z = z - v2.z;
   }

   void mul(double d)
   {
     x *= d;
     y *= d;
     z *= d;
   }

   void div(double d)
   {
     x /= d;
     y /= d;
     z /= d;
   }

   CVector unit()
   {
     double m = mag();
     CVector n = new CVector();

     n.x = x / m;
     n.y = y / m;
     n.z = z / m;
     return n;
   }

   void rotatex (double a)
   {
     double ty,tz;
     double b;
     b=a*3.14159265/180.0;
     ty=Math.cos(b)*y-Math.sin(b)*z;
     tz=Math.sin(b)*y+Math.cos(b)*z;
     y=ty;
     z=tz;
   }

   void rotatex(double cosb,double sinb)
   {
     double ty,tz;
     ty=cosb*y-sinb*z;
     tz=sinb*y+cosb*z;
     y=ty;
     z=tz;
   }

   void  rotatey(double a)
   {
     double tx,tz;
     double b;
     b=a*3.14159265/180.0;
     tx=Math.cos(b)*x+Math.sin(b)*z;
     tz=-Math.sin(b)*x+Math.cos(b)*z;
     x=tx;
     z=tz;
   }

   void  rotatey(double cosb,double sinb)
   {
     double tx,tz;
     tx=cosb*x+sinb*z;
     tz=-sinb*x+cosb*z;
     x=tx;
     z=tz;
   }

   void  rotatez (double a)
   {
     double tx,ty;
     double b;
     b=a*3.14159265/180.0;
     tx=Math.cos(b)*x-Math.sin(b)*y;
     ty=Math.sin(b)*x+Math.cos(b)*y;
     x=tx;
     y=ty;
   }

   void  rotatez (double cosb,double sinb)
   {
     double tx,ty;
     tx=cosb*x-sinb*y;
     ty=sinb*x+cosb*y;
     x=tx;
     y=ty;
   }

}

class Point 
{
    int x,y;

    Point(Point p)
    {
       x = p.x;
       y = p.y;
    }
    Point(int _x, int _y)
    {
       x = _x;
       y = _y;
    }
    Point()
    {
       x = 0;
       y = 0;
    }
    void copy(Point p)
    {
       x = p.x;
       y = p.y;
    }
}