import java.applet.*;
import java.awt.*;
import java.lang.*;
import java.awt.event.*;

/**
 * Bezier
 *
 *
 * Created: Tue Dec  5 18:47:56 2000
 *
 * @author Copper
 * @version 1.0
 */

public class Bezier extends Applet implements AdjustmentListener {

  Button start;
  /* Kontrollpunkte in einem Rechtshaendigen 
     Koordinatensystem
   */
  Point3d cpoints[][] = {
    { new Point3d(0,0,-150), new Point3d(50,0,-150), new Point3d(100,0,-150), new Point3d(150,0,-150)},
    { new Point3d(0,0,-100), new Point3d(50,100,-100), new Point3d(100,100,-100), new Point3d(150,0,-100)},
    { new Point3d(0,0,-50), new Point3d(50,100,-50), new Point3d(100,100,-50), new Point3d(150,0,-50) },
    { new Point3d(0,0,0), new Point3d(50,0,0), new Point3d(100,0,0), new Point3d(150,0,0) }};
  Point3d bakpoint[][] = {
    { new Point3d(0,0,-150), new Point3d(50,0,-150), new Point3d(100,0,-150), new Point3d(150,0,-150)},
    { new Point3d(0,0,-100), new Point3d(50,100,-100), new Point3d(100,100,-100), new Point3d(150,0,-100)},
    { new Point3d(0,0,-50), new Point3d(50,100,-50), new Point3d(100,100,-50), new Point3d(150,0,-50) },
    { new Point3d(0,0,0), new Point3d(50,0,0), new Point3d(100,0,0), new Point3d(150,0,0) }};

  int N = 13; // Anzahl Bezierpunkte
  double XWink = 0;
  double ZWink = 0;

  // Feld für entgueltige Punkte
  double feld[][][] = new double[50][50][3];
  double feld2d[][][] = new double[50][50][2];
  Bernstein bezier[] = new Bernstein[50];
  Plane plist[] = new Plane[50*50*2];
  int pindex;
  
  //Lichtquelle
  double llpx = 0;
  double llpy = 0;
  double llpz = 500;
  double lpx,lpy,lpz;

  int i,j;
  Panel pan13,span;
  BezierPanel pan3d;
  ControlPanel konpan;
  MyMouse mm;
  Scrollbar scr,nscr;
  Image dbimage1,dbimage2;
  TextField tf1,tfx,tfy,tfz;
  Checkbox cb1,cb2,cb3,cb4;
  Button reset;
  // Darstellungsmodi
  final int WIRE = 0;
  final int FILL = 1;
  final int SHADE = 2;
  final int FAST = 3;
  int drawmode = WIRE;
  // Fastdraw ?
  boolean fast = false;

  // Init Applet
  public void init() {
  super.init();

  // Images fuer doublebuffer erstellen
  dbimage1 = createImage(300,270);
  dbimage2 = createImage(300,270);

  // eigener Mouselistener
  mm = new MyMouse(this);

  // Bernsteinpolynome vorberechnen
  calcbernstein();
  // Bezierpunkte berechnen
  calcbezier();
  // Bezierflaeche zeichnen
  drawbezier();
  // Kontrollpunkte zeichnen
  drawcontrol();

  buildGUI();

  }

  // Handle Actions
  public boolean action(Event e, Object o)
  {
    Object ob = e.target;
    if (ob.equals(cb1))
    {
      drawmode = WIRE;
      update();
      System.out.println("Wire");
    }
    if (ob.equals(cb2))
    {
      drawmode = FILL;
      update();
      System.out.println("Filled");
    }
    if (ob.equals(cb3))
    {
      drawmode = SHADE;
      update();
      System.out.println("Shade");
    }
    if (ob.equals(cb4))
    {
      if(((Checkbox)ob).getState())
	fast = true;
      else
	fast = false;

      update();
      System.out.println("Fast");
    }
    if (ob.equals(reset))
    {
      System.out.println("Reset");
      for(i=0;i<4;i++)
      {
	for(j=0;j<4;j++)
	{
	  cpoints[i][j].p[0] = bakpoint[i][j].p[0];
	  cpoints[i][j].p[1] = bakpoint[i][j].p[1];
	  cpoints[i][j].p[2] = bakpoint[i][j].p[2];
	}
      }
      calcbernstein();
      update();
   }

    return super.action(e,o);
  }

  // ein Scroller wurde bewegt
  public void adjustmentValueChanged(AdjustmentEvent e)
  {
    Adjustable ad = e.getAdjustable();
    String s = ((Component)e.getSource()).getName();

    if(s.equals("kscroll"))
    {
	if(mm.select==true)
	{
	    cpoints[mm.i][mm.j].p[1] = 0-(double)ad.getValue();
	    update();
	}
    }
    if(s.equals("NSCR"))
    {
	N = ad.getValue();
	tf1.setText(String.valueOf(N));
	calcbernstein();
	repaint();
    }
    if(s.equals("X"))
    {
      llpx = ad.getValue();
      tfx.setText(String.valueOf((int)llpx));
      repaint();
    }
    if(s.equals("Y"))
    {
      llpy = ad.getValue();
      tfy.setText(String.valueOf((int)llpy));
      repaint();
    }
    if(s.equals("Z"))
    {
      llpz = ad.getValue();
      tfz.setText(String.valueOf((int)llpz));
      repaint();
    }
  }

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

  public void update()
  {
    Graphics p = konpan.getGraphics();
    paint(p);
    pan3d.repaint();
    if(mm.rselect==false)
    konpan.repaint();
  }
  

  
  // Zeichne Bezieroberflaeche
  void drawbezier()
  {
    i=0;j=0;
    int x,y;
    Graphics p = dbimage2.getGraphics();

    p.setColor(Color.black);
    p.fillRect(0,0,300,270);
    p.setColor(Color.white);

    if((drawmode == WIRE)||(drawmode == FILL || drawmode == SHADE) && (fast == true && mm.rselect == true))
    {
      for(i=0;i<N;i++)
	{
	  for(j=0;j<N;j++)
	    {
	      x = 150+(int)(feld2d[i][j][0]);
	      y = 150-(int)(feld2d[i][j][1]);

	      if (j!=0)
	      {
		p.drawLine(x,y,150+(int)(feld2d[i][j-1][0]),150-(int)(feld2d[i][j-1][1]));
	      }
	      if (i!=0)
	      {
		p.drawLine(x,y,150+(int)(feld2d[i-1][j][0]),150-(int)(feld2d[i-1][j][1]));
	      }
	
	    }
	}
    }

    if(((drawmode == FILL||drawmode == SHADE) && fast == false)||((drawmode == FILL || drawmode == SHADE)&&(fast == true && mm.rselect == false)))
    {
      
      // unterteile Bezierflaeche in Dreiecke
      // und sortiere nach der Z Koordinate
      makeplanes();

      int xp[] = new int[3];
      int yp[] = new int[3];

      // Zeichne die einzelnen Flaechen
      for(int i=0;i<((N-1)*(N-1)*2);i++)
      {
	      xp[0] = 150+(int)(plist[i].x1);
	      yp[0] = 150-(int)(plist[i].y1);
	      xp[1] = 150+(int)(plist[i].x2);
	      yp[1] = 150-(int)(plist[i].y2);
	      xp[2] = 150+(int)(plist[i].x3);
	      yp[2] = 150-(int)(plist[i].y3);

	      if(drawmode == SHADE)
	      {
		if(plist[i].vs == 0)
		{
		  p.setColor(new Color(0,0,plist[i].c));
		}
		else
		{
		  p.setColor(new Color(0,plist[i].c),0));
		}
		p.fillPolygon(xp,yp,3);
	      }
	      else
	      {
		if(plist[i].vs == 0)
		{
		  p.setColor(new Color(0,0,240));
		}
    		else
		{
		  p.setColor(new Color(0,0,130));
		}
		p.fillPolygon(xp,yp,3);
		p.setColor(Color.white);
	        p.drawPolygon(xp,yp,3);
	      }
      }	       
    }
  }

  // Zeichne Kontrollpunktegitter
  void drawcontrol()
  {
    i=0;j=0;
    int x,y;
    Graphics p = dbimage1.getGraphics();

    p.setColor(Color.black);
    p.fillRect(0,0,300,270);
    p.setColor(Color.red);

    for(i=0;i<4;i++)
    {
      for(j=0;j<4;j++)
      {
	x = (int)cpoints[i][j].p[0]+50;
	y = 200+(int)cpoints[i][j].p[2];
	if ((mm.select)&&(mm.i == i)&&(mm.j == j))
	p.fillOval(x-5,y-5,11,11);
	else
	p.drawOval(x-5,y-5,11,11);
	if (j!=0)
	{
	  p.drawLine(x,y,(int)cpoints[i][j-1].p[0]+50,200+(int)cpoints[i][j-1].p[2]);
	}
	if (i!=0)
	{
	  p.drawLine(x,y,(int)cpoints[i-1][j].p[0]+50,200+(int)cpoints[i-1][j].p[2]);
	}
	
      }
    }
  }

  // Berechne Bezierkoeffzienten
  void calcbernstein()
  {
    int t;
    double tt,v;
    for (t=0;t<N;t++)
    {
      tt = (double)t/(N-1);
      v=1-tt;
      Bernstein b = new Bernstein();
      b.a = v*v*v;
      b.b = 3*tt*v*v;
      b.c = 3*tt*tt*v;
      b.d = tt*tt*tt;
      bezier[t]=b;      
    }
  }

  // Berechne Bezierpunkte
  void calcbezier()
  {
    int i,j,k;
    double t;

    Matrix bezierkoeff1 = new Matrix(1,4);
    Matrix xcpoint = new Matrix(4,4);
    Matrix ycpoint = new Matrix(4,4);
    Matrix zcpoint = new Matrix(4,4);
    Matrix bezierkoeff2 = new Matrix(4,1);
    Matrix rotpoint = new Matrix(4,1);


    // Point Matrizen füllen
    for(i=0;i<4;i++)
    {
      for(j=0;j<4;j++)
      {
	xcpoint.ma[i][j] = cpoints[j][i].p[0];
	ycpoint.ma[i][j] = cpoints[j][i].p[1];
	zcpoint.ma[i][j] = cpoints[j][i].p[2];
      }
    }

    rotpoint.ma[3][0] = 1;

    // hole eine Rotationsmatrix fuer die Winkel XWink und ZWink
    Matrix rotmatrix = getRotationsMatrix(XWink, ZWink);

    // jetzt die Kurvenpunkte berechnen
    for(i=0;i<N;i++)
    {
      bezierkoeff2.ma[0][0] = bezier[i].a;
      bezierkoeff2.ma[1][0] = bezier[i].b;
      bezierkoeff2.ma[2][0] = bezier[i].c;
      bezierkoeff2.ma[3][0] = bezier[i].d;

      for(j=0;j<N;j++)
      {
	  bezierkoeff1.ma[0][0] = bezier[j].a;
	  bezierkoeff1.ma[0][1] = bezier[j].b;
	  bezierkoeff1.ma[0][2] = bezier[j].c;
	  bezierkoeff1.ma[0][3] = bezier[j].d;

	  rotpoint.ma[0][0] = ((bezierkoeff1.multiply(xcpoint)).multiply(bezierkoeff2)).ma[0][0];
	  rotpoint.ma[1][0] = ((bezierkoeff1.multiply(ycpoint)).multiply(bezierkoeff2)).ma[0][0];
	  rotpoint.ma[2][0] = ((bezierkoeff1.multiply(zcpoint)).multiply(bezierkoeff2)).ma[0][0];

	  rotpoint = rotmatrix.multiply(rotpoint);

	  // Zentralprojektion
	  t = (0-rotpoint.ma[2][0])/500+1;
	  feld2d[i][j][0] = (rotpoint.ma[0][0]-75)/t;
	  feld2d[i][j][1] = rotpoint.ma[1][0]/t;

	  feld[i][j][0] = rotpoint.ma[0][0];
	  feld[i][j][1] = rotpoint.ma[1][0];
	  feld[i][j][2] = rotpoint.ma[2][0];
      }
    }
  }

  // Erzeuge Flaechenstrukturen
  public void makeplanes()
  {
    int i,j;
    double x1,x2,x3,x4,y1,y2,y3,y4,z1,z2,z3,z4,xx1,yy1,zz1,xx2,yy2,zz2;
    double vx,vy,vz,t,tx,ty,tz;
    Plane pl1,pl2;
    Point p1,p2,p3,p4;

    // Flaechenzahler
    pindex = 0;

    for(i=0;i<N-1;i++)
    {
      for(j=0;j<N-1;j++)
      {
	pl1 = new Plane();
	pl2 = new Plane();

	x1 = feld[i][j][0];
	y1 = feld[i][j][1];
	z1 = feld[i][j][2];

	x2 = feld[i][j+1][0];
	y2 = feld[i][j+1][1];
	z2 = feld[i][j+1][2];

	x3 = feld[i+1][j+1][0];
	y3 = feld[i+1][j+1][1];
	z3 = feld[i+1][j+1][2];

	x4 = feld[i+1][j][0];
	y4 = feld[i+1][j][1];
	z4 = feld[i+1][j][2];

      	pl1.mz = (z1+z2+z4)/3;   //mittle Z koordinate berechnen;
	pl2.mz = (z2+z3+z4)/3;

	// Sichtbarkeit erste flaeche checken
	xx1 = x2 - x1;
	yy1 = y2 - y1;
	zz1 = z2 - z1;

	xx2 = x4 - x1;
	yy2 = y4 - y1;
	zz2 = z4 - z1;

	// Vektorprodukt berechnen
	vx = yy1*zz2-zz1*yy2;
	vy = zz1*xx2-xx1*zz2;
	vz = xx1*yy2-yy1*xx2;

	// Vector normieren
	t = Math.sqrt(vx*vx+vy*vy+vz*vz);
	vx = vx / t;
	vy = vy / t;
	vz = vz / t;

	//Vector und Winkel zur Lichtquelle berechnen

	lpx = llpx - x1;
	lpy = llpy - y1;
	lpz = llpz - z1;

	//Vector Normieren
	t = Math.sqrt(llpx*llpx+llpy*llpy+llpz*llpz);
	lpx = llpx /t;
	lpz = llpz /t;
	lpy = llpy /t;

	//Skalarprodukt bilden
	//double s = (x1-75)*vx+(y1)*vy+(z1-500)*vz;

	// da wir immer in Richtung der Z Koordinate blicke
	// reicht auch ein Vorzeichenvergleich der Z Koordinate

	if (vz>0)
	{
	  pl1.vs = 1;
	  // Farbwert berechnen
	  t = Math.abs(vx*lpx+vy*lpy+vz*lpz);
	  pl1.c = (int)(50+205*t);
	}
	else
	{
	  pl1.vs = 0;
	  t = Math.abs((0-vx)*lpx+(0-vy)*lpy+(0-vz)*lpz);
	  pl1.c = (int)(50+205*t);
	  
	}

	pl1.x1 = feld2d[i][j][0];
	pl1.y1 = feld2d[i][j][1];
	pl1.x2 = feld2d[i][j+1][0];
	pl1.y2 = feld2d[i][j+1][1];
	pl1.x3 = feld2d[i+1][j][0];
	pl1.y3 = feld2d[i+1][j][1];

	plist[pindex] = pl1;
	pindex++;

	// Sichtbarkeit zweite flaeche checken

	xx1 = x3 - x2;
	yy1 = y3 - y2;
	zz1 = z3 - z2;

	xx2 = x4 - x2;
	yy2 = y4 - y2;
	zz2 = z4 - z2;

	// Vektorprodukt berechnen
	vx = yy1*zz2-zz1*yy2;
	vy = zz1*xx2-xx1*zz2;
	vz = xx1*yy2-yy1*xx2;

	// Vector Normieren
	t = Math.sqrt(vx*vx+vy*vy+vz*vz);
	vx = vx / t;
	vy = vy / t;
	vz = vz / t;

	//Vector und Winkel zur Lichtquelle berechnen

	lpx = llpx - x3;
	lpy = llpy - y3;
	lpz = llpz - z3;

	//Vector Normieren
	t = Math.sqrt(llpx*llpx+llpy*llpy+llpz*llpz);
	lpx = llpx /t;
	lpz = llpz /t;
	lpy = llpy /t;

	// Sichtbarkeit checken
	if (vz>0)
	{
	  pl2.vs = 1;
	  t = Math.abs(vx*lpx+vy*lpy+vz*lpz);
	  pl2.c = (int)(50+205*t);
	}
	else
	{
	  pl2.vs = 0;
	  t = Math.abs((0-vx)*lpx+(0-vy)*lpy+(0-vz)*lpz);
	  pl2.c = (int)(50+205*t);
	  
	}

	pl2.x1 = feld2d[i][j+1][0];
	pl2.y1 = feld2d[i][j+1][1];
	pl2.x2 = feld2d[i+1][j+1][0];
	pl2.y2 = feld2d[i+1][j+1][1];
	pl2.x3 = feld2d[i+1][j][0];
	pl2.y3 = feld2d[i+1][j][1];

	plist[pindex] = pl2;
	pindex++;

      }
    }
        

    // Flaechen sortieren
    Quicksort(0,pindex-1);

  }


  // Mit Quicksort sortieren
  public void Quicksort (int unten, int oben)
  {   
    Plane tmp;
    int i = unten;
    int j = oben;
    double x = plist[(unten+oben) / 2].mz;                  // Pivotelement, willkuerlich
  
    do {
        while (plist[i].mz < x) i++;                     // x fungiert als Bremse
        while (plist[j].mz > x) j--;                     // x fungiert als Bremse
        if ( i<=j )  {
            tmp  = plist[i];                          // Hilfsspeicher
            plist[i] = plist[j];                          // a[i] und 
            plist[j] = tmp;                           // a[j] werden getauscht
            i++;                  
            j--;  
        }                        
    } while (i <= j); 
                              // alle Elemente der linken Haelfte sind kleiner
                              // als alle Elemente der rechten Haelfte 

    if (unten < j)  Quicksort(unten, j);            // sortiere linke Haelfte
    if (i < oben )  Quicksort(i, oben );            // sortiere rechte Haelfte
  }


  // Checke ob ein Punkt getroffen wurde
  public Point checkPointat(int x, int y)
  {
     int i,j;

     for(i=0;i<4;i++)
     {
	 for(j=0;j<4;j++)
	 {
	     if(Math.abs((cpoints[i][j].p[0]+50-x))<=5)
		 if(Math.abs(200+cpoints[i][j].p[2]-y)<=5)
		     {
			 scr.setValue(0-(int)cpoints[i][j].p[1]);
			 return new Point(i,j);
		     }
	 }
     }

     return new Point(5,5);

  }


  /* Bastle eine Rotationsmatix fuer die Winkel X und Y
     
     R = T2*Rz*Rx*T1

   */  

  public Matrix getRotationsMatrix(double X, double Z)
  {
    Matrix rm = new Matrix(4,4);

    double cX = X*Math.PI/180; // In Radiant umwandeln
    double cZ = Z*Math.PI/180;

    double sinx = Math.sin(cX);
    double sinz = Math.sin(cZ);
    double cosx = Math.cos(cX);
    double cosz = Math.cos(cZ);

    rm.ma[0][0] = cosz; 
    rm.ma[0][1] = -cosx*sinz;
    rm.ma[0][2] = sinx*sinz;
    rm.ma[0][3] = -75*((cosz-sinx*sinz)-1);

    rm.ma[1][0] = sinz;
    rm.ma[1][1] = cosx*cosz;
    rm.ma[1][2] = -sinx*cosz;
    rm.ma[1][3] = -75*(sinz+sinx*cosz);

    rm.ma[2][0] = 0;
    rm.ma[2][1] = sinx;
    rm.ma[2][2] = cosx;
    rm.ma[2][3] = 75*(cosx-1);

    rm.ma[3][0] = 0;
    rm.ma[3][1] = 0;
    rm.ma[3][2] = 0;
    rm.ma[3][3] = 1;

    return rm;
  }

  public void buildGUI()
  {
    setLayout(new GridLayout(2,1,3,3));
    Panel pan1 = new Panel(new GridLayout(1,2,3,3));
    konpan = new ControlPanel(this);
    konpan.addMouseListener(mm);
    konpan.addMouseMotionListener(mm);
    konpan.setBackground(Color.black);
    konpan.setLayout(new BorderLayout());
    konpan.setName("Kontroll");
    konpan.setSize(new Dimension(300,270));

    pan13 = new Panel(new GridLayout(1,1));
    pan13.setBackground(Color.white);
    scr = new Scrollbar(Scrollbar.VERTICAL,0,10,-150,150);
    scr.setName("kscroll");
    scr.addAdjustmentListener(this);
    pan13.add(scr);
    konpan.add(pan13,BorderLayout.EAST);
    pan1.add(konpan);

    // 3d Darstellungs Panel
    pan3d = new BezierPanel(this);
    pan3d.setBackground(Color.black);
    pan3d.setName("3dpan");
    pan3d.addMouseListener(mm);
    pan3d.addMouseMotionListener(mm);
    pan3d.setSize(new Dimension(300,270));
    pan1.add(pan3d);
    add(pan1);

    //Steuerungspanel
    Panel scrpan = new Panel();
    scrpan.setSize(new Dimension(700,250));

    GridBagLayout gbl = new GridBagLayout();
    GridBagConstraints gbc = new GridBagConstraints();
    scrpan.setLayout(gbl);

    Label l = new Label("Drawmodus:");
    gbc.gridx = -1;
    gbc.gridy = 0;
    gbc.gridwidth = 5;
    gbc.weightx = 100;
    gbc.weighty = 0;
    gbl.setConstraints(l, gbc);
    scrpan.add(l);

    CheckboxGroup cbg = new CheckboxGroup();
    cb1 = new Checkbox("Wire",cbg,true);
    cb1.setName("Wire");
    gbc.gridx = 0;
    gbc.gridy = 1;
    gbc.gridwidth = 3;
    gbc.gridheight = 1;
    gbl.setConstraints(cb1, gbc);
    scrpan.add(cb1);

    cb2 = new Checkbox("Filled",cbg,false);
    cb2.setName("Filled");
    gbc.gridx = 3;
    gbl.setConstraints(cb2, gbc);
    scrpan.add(cb2);

    cb3 = new Checkbox("Shaded",cbg,false);
    cb3.setName("Shade");
    gbc.gridx = 6;
    gbl.setConstraints(cb3, gbc);
    scrpan.add(cb3);

    cb4 = new Checkbox("Fast Draw");
    cb4.setName("Fast");
    gbc.gridx = 9;
    gbl.setConstraints(cb4, gbc);
    scrpan.add(cb4);

    l = new Label("Bezierpunkte:");
    gbc.gridx = 0;
    gbc.gridy = 2;
    gbc.gridwidth = 10;
    gbc.gridheight = 1;
    gbc.weightx = 100;
    gbc.weighty = 0;
    gbc.fill = GridBagConstraints.BOTH;
    gbl.setConstraints(l, gbc);
    scrpan.add(l);

    nscr = new Scrollbar(Scrollbar.HORIZONTAL,13,1,4,45);
    nscr.setName("NSCR");
    nscr.addAdjustmentListener(this);
    gbc.gridx = 0;
    gbc.gridy = 3;
    gbc.gridwidth = 10;
    gbl.setConstraints(nscr, gbc);
    scrpan.add(nscr);

    tf1 = new TextField("13",3);
    tf1.setEditable(false);
    gbc.gridx = 10;
    gbc.gridwidth = 1;
    gbc.gridheight = 1;
    gbc.weightx = 0;
    gbc.fill = GridBagConstraints.BOTH;
    gbl.setConstraints(tf1, gbc);
    scrpan.add(tf1);

    l = new Label("Position der Lichtquelle:");
    gbc.gridx = 0;
    gbc.gridy = 4;
    gbc.gridwidth = 10;
    gbc.weightx = 100;
    gbl.setConstraints(l, gbc);
    scrpan.add(l);

    nscr = new Scrollbar(Scrollbar.HORIZONTAL,0,20,-1000,1000);
    nscr.setName("X");
    nscr.addAdjustmentListener(this);
    gbc.gridy = 5;
    gbc.fill = GridBagConstraints.BOTH;
    gbl.setConstraints(nscr, gbc);
    scrpan.add(nscr);

    tfx = new TextField("0",5);
    tfx.setEditable(false);
    gbc.gridx = 10;
    gbc.gridwidth = 1;
    gbc.weightx = 0;
    gbc.fill = GridBagConstraints.BOTH;
    gbl.setConstraints(tfx, gbc);
    scrpan.add(tfx);

    l = new Label("X");
    gbc.gridx = 11;
    gbc.gridwidth = 1;
    gbc.fill = GridBagConstraints.BOTH;
    gbl.setConstraints(l, gbc);
    scrpan.add(l);

    nscr = new Scrollbar(Scrollbar.HORIZONTAL,0,20,-1000,1000);
    nscr.setName("Y");
    nscr.addAdjustmentListener(this);
    gbc.gridx = 0;
    gbc.gridy = 6;
    gbc.gridwidth = 10;
    gbl.setConstraints(nscr, gbc);
    scrpan.add(nscr);

    tfy = new TextField("0",5);
    tfy.setEditable(false);
    gbc.gridx = 10;
    gbc.gridwidth = 1;
    gbc.weightx = 0;
    gbl.setConstraints(tfy, gbc);
    scrpan.add(tfy);

    l = new Label("Y");
    gbc.gridx = 11;
    gbc.gridwidth = 1;
    gbl.setConstraints(l, gbc);
    scrpan.add(l);

    nscr = new Scrollbar(Scrollbar.HORIZONTAL,500,20,-1000,1000);
    nscr.setName("Z");
    nscr.addAdjustmentListener(this);
    gbc.gridx = 0;
    gbc.gridy = 7;
    gbc.gridwidth = 10;
    gbl.setConstraints(nscr, gbc);
    scrpan.add(nscr);

    tfz = new TextField("0",5);
    tfz.setEditable(false);
    gbc.gridx = 10;
    gbc.gridwidth = 1;
    gbc.weightx = 0;
    gbc.fill = GridBagConstraints.BOTH;
    gbl.setConstraints(tfz, gbc);
    scrpan.add(tfz);

    l = new Label("Z");
    gbc.gridx = 11;
    gbc.gridwidth = 1;
    gbl.setConstraints(l, gbc);
    scrpan.add(l);

    reset = new Button("Reset Points");
    gbc.gridx = 0;
    gbc.gridy = 9;
 
   gbc.gridwidth = 2;
    gbc.weightx = 0;
    gbl.setConstraints(reset, gbc);
    scrpan.add(reset);
 
    add(scrpan);
  }
} // Bezier


class Matrix
{
  double ma[][];

  public Matrix(int i,int j)
  {
    ma = new double[i][j];
  }

  public Matrix multiply(Matrix b)
  {
    int iMax,jMax,kMax,i,j;
    iMax = this.getY();
    jMax = b.getX();

    kMax = this.getX();

    if (kMax == b.getY())
    {
      Matrix e = new Matrix(iMax,jMax);

      for (i = 0;i<iMax;i++)
      {
	for (j = 0;j<jMax;j++)
	{
	  double res = 0;
	  for(int k = 0;k < kMax; k++)
	  {
	  res += ma[i][k] * b.ma[k][j];
	  }
	  
	  e.ma[i][j] = res; 
	}		
      }

      return e;
    }
    else
    {
      return null;
    }

  }

  int getY()
  {
    return ma.length;
  }
  int getX()
  {
    return ma[0].length;
  }


}


// MouseListener fuer das Kontrollpunkte- und Bezierfeld
class MyMouse extends MouseAdapter implements MouseMotionListener
{
    Bezier be;
    Point p;
    boolean select,rselect;
    int i,j,xx,yy;
    double ox,oy;

    MyMouse(Bezier a)
    {
	this.be = a;
	select = false;
	rselect = false;
    }

    public void mousePressed(MouseEvent e)
    {
      String n = e.getComponent().getName();

      xx = e.getX();
      yy = e.getY();

      if (n.equals("Kontroll"))
      {
	
	p = be.checkPointat(e.getX(),e.getY());
	if (p.x != 5)
	{
	  select = true;
	  i = p.x;       //Koordinaten des selectierten Punktes merken
	  j = p.y;
	  ox = be.cpoints[i][j].p[0];
	  oy = be.cpoints[i][j].p[2];
	  be.drawcontrol();
	  be.update();
	}
      }
      if (n.equals("3dpan"))
      {
	rselect = true;
      }
    }

    public void mouseReleased(MouseEvent e)
    {
      rselect = false;
      if (be.fast == true)
	be.update();
	
    }

    public void mouseDragged(MouseEvent e)
    {
      int x,y;
      double tx,ty;
        
	x = e.getX();
	y = e.getY();
        String n = e.getComponent().getName();

	if (n.equals("Kontroll"))
	{
	  if (select == true)
	  {
	    tx = (x - xx);
	    ty = (y - yy);

	    be.cpoints[i][j].p[0] = ox+tx;
	    be.cpoints[i][j].p[2] = oy+ty;
	    be.repaint();

	  }
	}
	if (n.equals("3dpan"))
	{
	    tx = (x - xx);
	    ty = (y - yy);
	  
	    be.XWink = (be.XWink+ty) % 360;
	    be.ZWink = (be.ZWink+tx) % 360;
	    xx = x;
	    yy = y;
	    be.pan3d.repaint();
	}
    }

    public void mouseMoved(MouseEvent e)
    {
	
    }

}


class Bernstein
{
  double a,b,c,d;

  void Bernstein()
  {
    a=b=c=d=0;
  }
}

class Point3d
{
  double[] p;

  Point3d(double a, double b, double c)
  {
    p = new double[3];
    p[0] = a;
    p[1] = b;
    p[2] = c;
  }
}

class Plane
{
  //Koordinaten der Flaeche
  double x1,y1,x2,y2,x3,y3;
  // Mittlerer Z Wert
  double mz,tx,ty;

  //Welche Seite sichtbar ? 
  int vs;
  // Frabe
  int c;

  Plane()
  {
    vs = 0;
  }
}

class BezierPanel extends Panel
{
  Bezier b;
  BezierPanel(Bezier b)
  {
    super();
    this.b = b;
  }

  public void update(Graphics g)
  {
    b.calcbezier();
    b.drawbezier();
    paint(g);
  }
  
  public void paint(Graphics g)
  {
    g.drawImage(b.dbimage2,0,0,this);
  }
}

class ControlPanel extends Panel
{
  Bezier b;
  ControlPanel(Bezier b)
  {
    super();
    this.b = b;
  }

  public void update(Graphics g)
  {
    b.drawcontrol();
    paint(g);
  }
  
  public void paint(Graphics g)
  {
    g.drawImage(b.dbimage1,0,0,this);
  }
}





















