
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

//----------------------------------------------------------

public class Spirale2d extends Applet implements ActionListener, Runnable
{
  int width = 900, totalWidth, height;         // Abmessungen
  Graphics g1, g2;
  Image offscreen;
  Color bgColor = Color.white;                  // Hintergrundfarbe Grafikbereich
  Color pColor = new Color(200,200,200);        // Farbe Hintergrund Formular
  Color sColor = Color.black;                   // Schriftfarbe
  Color xColor = Color.red;                     // Farbe X-Achse
  Color yColor = new Color(0,128,0);            // Farbe Y-Achse
  Color zColor = Color.blue;                    // Farbe Z-Achse
  Color a1Color = Color.black;                  // 4 Farben KurveA
  Color a2Color = Color.black;
  Color a3Color = Color.red;
  Color a4Color = Color.red;
  Color b1Color = Color.magenta;                // 4 Farben KurveB
  Color b2Color = Color.magenta;
  Color b3Color = Color.blue;
  Color b4Color = Color.blue;
  Font fSS;                                    // Zeichensatz
  Panel p;
  Checkbox cbA, cbB;                           // Schalter
  Button bPause;
  Label lR, lS;
  TextField tfR, tfS, tfTheta, tfbvk;
  Thread thr;
  double t;                                    // Zeit (s)
  boolean on;                                  // Flag für Bewegung
  final double omega = -Math.PI/10;            // Winkelgeschwindigkeit (1/s)
  final double PI=Math.PI, zPI=2*PI, dreiPI=3*PI;
  int u0, v0, ish, ishdiff;                                  // Ursprung
  double theta;                                // Winkel bezüglich x-y-Ebene
  double phi;                                  // Winkel bezüglich x-Achse
  double a1, a2, b1, b2, b3, c1, c2, c3;       // Koeffizienten für Projektion
  double vR, kreisteile, bvk, teile;               // Torusgrößen R,r, Windg., Kreisteile
  final int max = 1000;                        // Maximalwert für Koordinaten

  String KA,KB;
  String lang;                                 // Sprache (Parameter aus Applet-Tag)
  String[] text;
  String[] german = { "Maße und Winkelschritte", "Schrittanzahl", "Kipp-Winkel", "Stop / Weiter", "",
  "Daten speichern im HTM-Code","Download www.webstickers.de","© G.Mueller 2003, © W.Fendt 1998", "Bildverkleinerung" };
//um z-achse,um Schlauch

//----------------------------------------------------------

 // Komponente zum Panel hinzufügen:
  // comp .... Komponente
  // color ... Hintergrundfarbe
  // x ....... Spalte
  // y ....... Zeile
  // w ....... Breite
  // top usw.. Abstände zu den benachbarten Komponenten

  void addComponent (Component comp, Color color, int x, int y, int w,
    int top, int left, int bottom, int right)
  {
    GridBagConstraints c = new GridBagConstraints();
    c.gridx = x; c.gridy = y; c.gridwidth = w; c.gridheight = 1;
    c.fill = GridBagConstraints.HORIZONTAL;
    c.anchor = GridBagConstraints.CENTER;
    c.weightx = 1; c.weighty = 1;
    c.insets = new Insets(top,left,bottom,right);
    ((GridBagLayout)p.getLayout()).setConstraints(comp,c);
    comp.setFont(fSS); comp.setBackground(color);
    p.add(comp);
  }





//----------------------------------------------------------

  public void init()
  {
    int i, j;
    totalWidth = getSize().width;
    height = getSize().height;

    g1 = getGraphics();
    offscreen = createImage(width,height);
    g2 = offscreen.getGraphics();
    fSS = new Font("SansSerif",Font.BOLD,12);
    setLayout(new BorderLayout());
    u0 = width/2; v0 = height/2;
  }

//----------------------------------------------------------

 // Starten der Animation:

  public void start ()
  {

    String s, kt="360";
    double wink=30.0;
    text = german;
    lang = getParameter("language");
    if (lang == null) lang = "German";

    p = new Panel();
    p.setLayout(new GridBagLayout());
    p.setBackground(pColor);
    addComponent(new Label(text[0]),pColor,0,0,3,30,10,0,10);

    lR = new Label("R ="); lR.setForeground(Color.black);
    addComponent(lR,pColor,0,1,1,0,60,0,0);

    tfR = new TextField(8); tfR.setText("4");
    addComponent(tfR,Color.white,1,1,1,0,0,0,20);

    lS = new Label(text[1]); lS.setForeground(Color.black);
    addComponent(lS,pColor,0,8,1,0,10,0,0);

    tfS = new TextField(4); tfS.setText("360");
    addComponent(tfS,Color.white,1,8,1,10,0,0,20);

    addComponent(new Label(text[2]),pColor,0,9,1,0,10,0,0);

    tfTheta = new TextField(4); tfTheta.setText("30");
    addComponent(tfTheta,Color.white,1,9,1,10,0,0,20);

    addComponent(new Label(text[8]),pColor,0,10,1,0,10,0,0);

    tfbvk = new TextField(4); tfbvk.setText("1");
    addComponent(tfbvk,Color.white,1,10,1,10,0,0,20);


    s = getParameter("anzeigenKurveA");
    if (s == null) cbA = new Checkbox("KurveA", true);
	if ("false".equals(s))
	{ cbA = new Checkbox("KurveA", false);}
	else
	{ cbA = new Checkbox("KurveA", true); }

	s = getParameter("anzeigenKurveB");
	if (s == null) cbB = new Checkbox("KurveB", true);
	if ("false".equals(s))
	{ cbB = new Checkbox("KurveB",false); }
	else
	{ cbB = new Checkbox("KurveB", true); }

	addComponent(cbA,pColor,0,11,1,0,40,0,0);       // Hinzufügen zur Schaltfläche
	addComponent(cbB,pColor,1,11,1,0,0,0,0);

    bPause = new Button(text[3]);
    addComponent(bPause,Color.white,0,12,4,10,10,0,10);
    addComponent(new Label(text[4]),pColor,0,13,4,0,10,0,0);
    addComponent(new Label(text[5]),pColor,0,14,4,0,10,0,0);
    addComponent(new Label(text[6]),pColor,0,15,4,0,10,0,0);
    addComponent(new Label(text[7]),pColor,0,16,4,0,10,0,0);
    add("East",p);

    tfR.addActionListener(this);
    tfS.addActionListener(this);
    tfTheta.addActionListener(this);
    tfbvk.addActionListener(this);
    bPause.addActionListener(this);


    s = getParameter("Radius_gross");
    if (s != null) { tfR.setText(s); vR = stringToDouble(s); }

	s = getParameter("Schrittzahl");
    if (s != null) { tfS.setText(s); kreisteile = Integer.parseInt(s); }

	s = getParameter("Kippwinkel");
	if (s != null) { tfTheta.setText(s); wink = stringToDouble(s); }

    s = getParameter("Bildverkleinerung");
	if (s != null) { tfbvk.setText(s); bvk = stringToDouble(s); }

    s = getParameter("Teile");
	if (s != null) { teile = stringToDouble(s); }

	s = getParameter("ColorBackground");
	if (s != null) bgColor = new Color(Integer.parseInt(s,16));

	s = getParameter("ColorPanel");
	if (s != null) pColor = new Color(Integer.parseInt(s,16));

	s = getParameter("ColorSchrift");
	if (s != null) sColor = new Color(Integer.parseInt(s,16));

	s = getParameter("ColorX_Achse");
	if (s != null) xColor = new Color(Integer.parseInt(s,16));

	s = getParameter("ColorY_Achse");
	if (s != null) yColor = new Color(Integer.parseInt(s,16));

	s = getParameter("ColorZ_Achse");
	if (s != null) zColor = new Color(Integer.parseInt(s,16));

    if ((int)kreisteile >= 360) { teile = 3.5; kt="360"; };
    if ((int)kreisteile >= 500) { teile = 2.83; kt="500"; } ;
    if ((int)kreisteile >= 600) { teile = 2.64; kt="600"; } ;
    if ((int)kreisteile >= 720) { teile = 2.5; kt="720"; } ;
    if ((int)kreisteile >= 1000) { teile = 2.35; kt="1000"; } ;
    if ((int)kreisteile >= 1080) { teile = 2.33; kt="1080"; } ;
    if ((int)kreisteile >= 1440) { teile = 2.25; kt="1440"; } ;
    if ((int)kreisteile >= 1550) { teile = 2.235; kt="1550"; } ;
    if ((int)kreisteile >= 2000) { teile = 2.19; kt="2000"; };
    if ((int)kreisteile >= 3000) { teile = 2.145; kt="3000"; };
    if ((int)kreisteile >= 4000) { teile = 2.12; kt="4000"; };
    if ((int)kreisteile >= 5000) { teile = 2.11; kt="5000"; };

    tfS.setText(kt);
    kreisteile = stringToDouble(tfS.getText());

    ish = (int)(kreisteile);  // = is/7
    ishdiff=(int)(kreisteile*7.0-ish); // = is-ish

    theta = PI/180*(180 + wink);
    on = true;
    thr = new Thread(this);
    thr.start();
  }

//----------------------------------------------------------

 // Stoppen der Animation:

  public void stop ()
  {
    removeAll();
    thr = null;
  }

//----------------------------------------------------------

  public void destroy()
  {
    bPause.removeActionListener(this);
    tfR.removeActionListener(this);
    tfS.removeActionListener(this);
    tfTheta.removeActionListener(this);
    tfbvk.removeActionListener(this);
    g1 = null;
    g2 = null;
    offscreen = null;
    System.gc();
  }


//----------------------------------------------------------

 // Laufen des Programms (Endlosschleife)

  public void run ()
  {
    long t0, t1;
    t0 = System.currentTimeMillis();

    Thread thisThread = Thread.currentThread();

	while (thr == thisThread)
    {
      paint(g2);
      g1.drawImage(offscreen,0,0,this);

      try {Thread.sleep(50);}
      catch (InterruptedException ie) {}

      t1 = System.currentTimeMillis();
      if (on) t += (t1-t0)/1000.0;
      t0 = t1;
    }

  }

//----------------------------------------------------------

  // Bildschirmkoordinaten (orthogonale Parallelprojektion):

  int screenU (double x, double y)
  {
    x=x/bvk; y=y/bvk;
    return (int)Math.round(u0+20*(a1*x+a2*y));
  }

//----------------------------------------------------------

  int screenV (double x, double y, double z)
  {
	x=x/bvk; y=y/bvk; z=z/bvk;
    return (int)Math.round(v0+20*(b1*x+b2*y+b3*z));
  }

//----------------------------------------------------------

// Koeffizienten für Parallelprojektion:

  void calcCoeff ()
  {
    double sin = Math.sin(theta), cos = Math.cos(theta);
    a1 = Math.sin(phi); a2 = -Math.cos(phi);     // Nach "rechts"
    b1 = sin*a2; b2 = -sin*a1; b3 = -cos;        // Nach "oben"
    c1 = -cos*a2; c2 = cos*a1; c3 = -sin;        // Projektionsrichtung
  }

//----------------------------------------------------------

// Umwandlung eines Strings in eine Fließkommazahl:

  double stringToDouble (String s)
  {
    StringBuffer sbuf = new StringBuffer(s);
    double val;

    for (int i=0; i < sbuf.length(); i++) if (sbuf.charAt(i) == ',')

    sbuf.setCharAt(i,'.');
    try {val = Double.valueOf(sbuf.toString()).doubleValue();}
    catch (NumberFormatException e) {val = 0;}
    return val;
  }

//----------------------------------------------------------

  // Pfeil mit einfacher Dicke:
  // (u0,v0), (u1,v1) ... Endpunkte (Bildschirmkoordinaten)

  void drawArrow (int u0, int v0, int u1, int v1)
  {
    double w, dw = 0.2, r = 10, du = u1-u0, dv = v0-v1;
    if (du == 0 && dv == 0) return;
    g2.drawLine(u0,v0,u1,v1);
    w = Math.atan2(dv,du);
    u0 = (int)Math.round(u1-r*Math.cos(w+dw));
    v0 = (int)Math.round(v1+r*Math.sin(w+dw));
    g2.drawLine(u0,v0,u1,v1);
    u0 = (int)Math.round(u1-r*Math.cos(w-dw));
    v0 = (int)Math.round(v1+r*Math.sin(w-dw));
    g2.drawLine(u0,v0,u1,v1);
  }

//----------------------------------------------------------

  // Linie mit dreifacher Dicke:
  // (u0,v0), (u1,v1) ... Endpunkte (Bildschirmkoordinaten)

  void thickLine (int u0, int v0, int u1, int v1)
  {
    g2.drawLine(u0,v0,u1,v1);

    if (Math.abs(u1-u0) > Math.abs(v1-v0))
    {
      g2.drawLine(u0,v0-1,u1,v1-1);
      g2.drawLine(u0,v0+1,u1,v1+1);
    }
    else
    {
	  g2.drawLine(u0-1,v0,u1-1,v1);
	  g2.drawLine(u0+1,v0,u1+1,v1);
	}
  }

//----------------------------------------------------------

  // Pfeil mit dreifacher Dicke:
  // (u0,v0), (u1,v1) ... Endpunkte (Bildschirmkoordinaten)

  void thickArrow (int u0, int v0, int u1, int v1)
  {
    double w, dw = 0.2, r = 8, du = u1-u0, dv = v0-v1;

    if (du == 0 && dv == 0) return;

    thickLine(u0,v0,u1,v1);
    w = Math.atan2(dv,du);
    u0 = (int)Math.round(u1-r*Math.cos(w+dw));
    v0 = (int)Math.round(v1+r*Math.sin(w+dw));
    thickLine(u0,v0,u1,v1);
    u0 = (int)Math.round(u1-r*Math.cos(w-dw));
    v0 = (int)Math.round(v1+r*Math.sin(w-dw));
    thickLine(u0,v0,u1,v1);
  }

//----------------------------------------------------------

  // Achse mit Unterteilung:
  // (x1,y1,z1) ... Spitze

  void drawAxis (int u0, int v0, int u1, int v1)
  {
    int i, u, v, duu, dvv;
    double dU, dV, du, dv, length;
    dU = (u1-u0)/12.0; dV = (v1-v0)/12.0;
    du = -dV; dv = dU; length = Math.sqrt(du*du+dv*dv);
    duu = (int)Math.round(3*du/length);
    dvv = (int)Math.round(3*dv/length);
    drawArrow(u0,v0,u1,v1);

    for (i=1; i <= 11; i++)
    {
      u = (int)Math.round(u0+i*dU);
      v = (int)Math.round(v0+i*dV);
      //g2.drawLine(u-duu,v-dvv,u+duu,v+dvv);
    }
  }

//----------------------------------------------------------

  // Koordinatensystem:

 void drawCoordSystem ()
  {
    int u0, v0, u1, v1, a = 10;
    u0 = screenU(-a,0); v0 = screenV(-a,0,0);
    u1 = screenU(a,0); v1 = screenV(a,0,0);
    g2.setColor(xColor); drawAxis(u0,v0,u1,v1);
    g2.drawString("x",u1-4,v1+12);
    u0 = screenU(0,-a); v0 = screenV(0,-a,0);
    u1 = screenU(0,a); v1 = screenV(0,a,0);
    g2.setColor(yColor); drawAxis(u0,v0,u1,v1);
    g2.drawString("y",u1-4,v1+12);
    u0 = screenU(0,0); v0 = screenV(0,0,-a);
    u1 = u0; v1 = screenV(0,0,a);
    g2.setColor(zColor); drawAxis(u0,v0,u1,v1);
    g2.drawString("z",u1-12,v1+4);
  }

//----------------------------------------------------------


 // Torus:

  void drawTorus ()
  {
    int i,j,u001,v001,u01,v01,is;
    double winkelphi,uuu=0,vvv=0,www=0,u000,v000,w000,paramv,Lq,L=0,Lges;
    double swphi, radiusgross=vR, R_mal9=radiusgross*9;
    String Stext;
    double z6=1000000, dR=0, dz=0, writeparamv=0;



if (cbA.getState())
{

    uuu=radiusgross; vvv=0; www=0;
    paramv = radiusgross;
    swphi = zPI/kreisteile;

	winkelphi=0;
	g2.setColor(Color.red);




for (i=1; i <= 8; i++)
{
	winkelphi=0;


  for (j=1; j <= kreisteile; j++)
  {

         winkelphi +=swphi;
         if (winkelphi > zPI) winkelphi -= zPI;

         u000=uuu; v000=vvv; w000=www;

         uuu = paramv*Math.cos(winkelphi);
		 vvv = paramv*Math.sin(winkelphi);
		 www = 0;

	     u001 = screenU(u000,v000); v001 = screenV(u000,v000,w000);
		 u01 = screenU(uuu,vvv); v01 = screenV(uuu,vvv,www);

	     g2.drawLine(u001,v001,u01,v01);

     }

     paramv = paramv/2;
     uuu=paramv; vvv=0; www=0;

    }  // Ende for




   };// Ende if (cbA.getState())


if (cbB.getState())
{
    uuu=radiusgross; vvv=0; www=0;

    paramv = radiusgross;
    swphi = zPI/kreisteile;

	winkelphi=0;
	Lges=0;
    g2.setColor(Color.black);


is=0;


while (Lges < R_mal9)
{
         is++;

         dR = swphi*paramv/9;

         if (is < ish) dz -= dR/teile;
         if (is > ish) dz += dR/teile;

         winkelphi +=swphi;
         if (winkelphi > zPI) winkelphi -= zPI;

         u000=uuu; v000=vvv; w000=www;

         uuu = paramv*Math.cos(winkelphi);
		 vvv = paramv*Math.sin(winkelphi);
		 www = dz;

		 paramv -= dR;

	     u001 = screenU(u000,v000); v001 = screenV(u000,v000,w000);
		 u01 = screenU(uuu,vvv); v01 = screenV(uuu,vvv,www);

	     g2.drawLine(u001,v001,u01,v01);

	     Lq=(uuu-u000)*(uuu-u000) + (vvv-v000)*(vvv-v000) + (www-w000)*(www-w000);
	     L=Math.sqrt(Lq);
	     Lges +=L;

 } // Ende while1

         ish = (int)(is/7.15);
         ishdiff=(int)(is-ish);

 	     writeparamv=Math.round((paramv+dR/2)*z6)/z6;
 	     Stext = "Abbruch1 (L=9*R) ist bei R_aktuell = " + Double.toString(writeparamv) + " ";
	     g2.drawString(Stext,5,45);

 is=0;

 while (Lges < 2*R_mal9)
 {
         is++;

         dR = swphi*paramv/9;

         if (is < ishdiff) dz += dR/4;
         if (is > ishdiff) dz -= dR/4;

         winkelphi +=swphi;
         if (winkelphi > zPI) winkelphi -= zPI;

         u000=uuu; v000=vvv; w000=www;

         uuu = paramv*Math.cos(winkelphi);
 		 vvv = paramv*Math.sin(winkelphi);
 		 www = dz;

 		 paramv += dR;

 	     u001 = screenU(u000,v000); v001 = screenV(u000,v000,w000);
 		 u01 = screenU(uuu,vvv); v01 = screenV(uuu,vvv,www);

 	     g2.drawLine(u001,v001,u01,v01);

 	     Lq=(uuu-u000)*(uuu-u000) + (vvv-v000)*(vvv-v000) + (www-w000)*(www-w000);
 	     L=Math.sqrt(Lq);
 	     Lges +=L;

  } // Ende while2



         g2.setColor(sColor);
         Lq=Math.round((Lges-L/2)*z6)/z6;
         paramv=Math.round((paramv+dR/2)*z6)/z6;

	     Stext = "LängeB = " + Double.toString(Lq) + " = 2*(R*9)";
	     g2.drawString(Stext,5,30);
	     Stext = "Abbruch2 (L=18R) ist bei R_aktuell = " + Double.toString(paramv) + " ";
	     g2.drawString(Stext,5,60);



    }; // Ende if (cbB.getState())

			g2.setColor(sColor);
			Stext = "schrittweise addiert:";
		    g2.drawString(Stext,5,15);


  }


//----------------------------------------------------------

 // Grafik:

  public void paint (Graphics g)
  {
    g.setFont(fSS);
    phi = omega*t;
    calcCoeff();
    g.setColor(bgColor);
    g.fillRect(0,0,width,height);
    drawCoordSystem();
    drawTorus();

  }

 //----------------------------------------------------------

 // Eingabe aus Textfeld:
  // tf .... Textfeld
  // min ... Minimum
  // max ... Maximum

  double inputTF (TextField tf, double min, double max)
  {
    double value = stringToDouble(tf.getText());

   /*
    if (value < min) value = min;
    if (value > max) value = max;
    */

    tf.setText("" + value);
    return value;
  }

//----------------------------------------------------------

  void newValue ()
  {
    String kt="360";

    vR = inputTF(tfR,-max,+max);
    kreisteile = inputTF(tfS,-max,+max);


    teile=3.5;
    if ((int)kreisteile >= 360) { teile = 3.5; kt="360"; };
    if ((int)kreisteile >= 500) { teile = 2.83; kt="500"; };
    if ((int)kreisteile >= 600) { teile = 2.64; kt="600"; } ;
    if ((int)kreisteile >= 720) { teile = 2.5; kt="720"; };
    if ((int)kreisteile >= 1000) { teile = 2.35; kt="1000"; };
    if ((int)kreisteile >= 1080) { teile = 2.33; kt="1080"; };
    if ((int)kreisteile >= 1440) { teile = 2.25; kt="1440"; };
    if ((int)kreisteile >= 1550) { teile = 2.235; kt="1550"; };
    if ((int)kreisteile >= 2000) { teile = 2.19; kt="2000"; };
    if ((int)kreisteile >= 3000) { teile = 2.145; kt="3000"; };
    if ((int)kreisteile >= 4000) { teile = 2.12; kt="4000"; };
    if ((int)kreisteile >= 5000) { teile = 2.11; kt="5000"; };

    tfS.setText(kt);
    kreisteile = stringToDouble(tfS.getText());


    double wink=inputTF(tfTheta,-360,360);
    theta = PI/180*(180+wink);
    if (bvk > 0) { bvk = inputTF(tfbvk,-max,+max); }
    else
    { bvk=1.0; tfbvk.setText("1.0"); }

    calcCoeff();
  }

//----------------------------------------------------------

  public void actionPerformed (ActionEvent e)
  {
      newValue();
      on = !on;

  }

//----------------------------------------------------------

}

//----------------------------------------------------------
//----------------------------------------------------------
