// multipols.java
// JAVA 1.0.x

import java.awt.*;
import java.applet.Applet;
import java.util.*;
import java.io.*;

class multipols_viewport extends Canvas {
   Image img_buffer=null;
   int img_width=600,img_height=200;
   double xrel[], yrel[];
   double xpoint[], ypoint[];
   double srcph[];
   int max_points=420;
   int max_sources=40;
   int num_sources=4;
   int xbase=120;
   int ybase=160;
   double scale_factor=16000.0;
   double eps=10.0;
   double seed=0.68;
   double phase_speed=40.0;
   double wave_number=0.0;
   double multi_value[];
   boolean compact_flag=true;
   boolean special_flag=true;
   int phase_flag=0;
   boolean repaint_flag=true;
   int multi_level;
   int max_multi=33;
   Graphics bgra;

   public multipols_viewport(int max_sources) {
      setBackground(Color.white);

      this.max_sources = max_sources;

      xpoint = new double[max_sources];
      ypoint = new double[max_sources];
      xrel = new double[max_sources];
      yrel = new double[max_sources];
      srcph = new double[max_sources];

      multi_value = new double[max_multi];
      multi_level = 0;

      init_sources();
      show();
   }

   double next_val(double val) {
      //return (4.0 * val * (1.0 - val));
      val = (2.0 * Math.sqrt(val)) + 0.1;
      val = val*val;
      val = val - Math.floor(val);
      return val;
   }

   void init_sources() {
      double c=seed;
      double x,y;
      double xsum,ysum;
      int i;
      xsum=0.0;
      for (i=0; i < num_sources; i++) {
	 c = next_val(c);
	 xrel[i] = 2.0*(c-0.5);
	 //xrel[i] = (double)i/(double)num_sources;
	 xsum += xrel[i];
      }
      ysum=0.0;
      for (i=0; i < num_sources; i++) {
	 c = next_val(c);
	 yrel[i] = 2.0*(c-0.5);
	 //yrel[i] = 0.0;
	 ysum += yrel[i];
      }
      xsum = xsum/num_sources;
      ysum = ysum/num_sources;
      for (i=0; i < num_sources; i++) {
	 xrel[i] -= xsum;
	 yrel[i] -= ysum;
	 srcph[i] = 1.0;
	 if ((phase_flag == 1) && ((i % 2) == 0)) {
	    srcph[i] = -1.0;
	 }
	 if ((phase_flag == 2) && ((i % 4) == 0)) {
	    srcph[i] = -1.0;
	 }
         //System.out.println(i + " " + srcph[i]);
      }
      calc_coefficients();
   }

   // -- calc Coefficients  --

   void calc_coefficients() {
      int i,j;
      for (j=0; j < max_multi; j++) {
	 multi_value[j] = 0.0;
      }
      for (i=0; i < num_sources; i++) {
	 double h = srcph[i];
	 for (j=0; j < max_multi; j++) {
	    multi_value[j] += h;
	    h *= xrel[i] * eps * wave_number/((double)(j+1));
	 }
      }
   }

   public void set_phase(int ival) {
      phase_flag=ival;
      init_sources();
   }

   public void set_scale(int ival) {
      scale_factor=ival * 16000.0;
   }

   public void set_eps(double val) {
      eps = val;
      calc_coefficients();
   }

   public void set_seed(double val) {
      seed=val;
      init_sources();
   }

   public void set_level(int ival) {
      multi_level=ival;
   }

   public void set_xbase(double val) {
      xbase = (int) val;
   }

   public void set_num_sources(int ival) {
      num_sources=ival;
      if (num_sources < 2) num_sources=2;
      if (num_sources > max_sources) num_sources=max_sources;
      init_sources();
   }

   public void set_lambda(double val) {
      phase_speed = val;
      wave_number=2.0*Math.PI/phase_speed;
      calc_coefficients();
   }

   // --- Berechne approx. Wert im Abstand r ---

   double get_value(double r) {
      double sum;
      double hcos, hsin;
      hcos = Math.cos(wave_number * r);
      hsin = Math.sin(wave_number * r);
      sum = 0.0;

      double h = 1.0;
      int j;
      for (j = 0; j <= multi_level; j++) {
	 if ((j % 2) == 1) {
	    sum += multi_value[j] * hsin/r;
	    hsin = -hsin;
	 } else {
	    sum += multi_value[j] * hcos/r;
	    hcos = -hcos;
	 }
      }
      return(sum);
   }
   
   // --- Zeichne Balkendiagramm der Multipolstaerken ---
   
   void draw_bars() {
      int i;
      double max_val = 0.01;
      for (i=0; i < max_multi; i++) {
	 double h = Math.abs(multi_value[i]);
	 if (h > max_val) max_val=h;
      }
      double bscale = 50.0/max_val;
      int p, p0, ix;
      bgra.setColor(Color.black);
      p0 = img_height - 20;
      for (i=0; i < max_multi; i++) {
	 ix = 20 + i * 4;
      	 p = p0 - (int) (bscale * Math.abs(multi_value[i]));
	 bgra.drawLine(ix,p0,ix,p);
	 bgra.drawLine(ix+1,p0,ix+1,p);
	 if (i == multi_level) bgra.setColor(Color.red);
      }
   }

   // --- Hauptzeichenroutine ---
   
   synchronized void draw_wave() {
      bgra.setColor(Color.blue);
      bgra.fillRect(xbase-4,ybase-4,9,9);

      // --- Quellverteilung ---
      
      int i;
      int ix,iy;
      for (i=0; i < num_sources; i++) {
	 xpoint[i] = xbase + eps * xrel[i];
	 ypoint[i] = ybase + eps * yrel[i];
	 ix = (int)(xpoint[i]);
	 iy = (int)(ypoint[i]);
	 if (srcph[i] < 0.0) {
	    bgra.setColor(Color.yellow);
	 } else {
	    bgra.setColor(Color.red);
	 }
	 bgra.fillRect(ix-2,iy-2,5,5);
      }

      // --- exakte Loesung ---
      
      int j,p,ix0;
      double dx,dy,s;
      double sum;
      double pscale=scale_factor/num_sources;
      bgra.setColor(Color.orange);

      ix0 = img_width - 40 - max_points;
      for (i=0; i < max_points; i++) {
	 ix = ix0  + i;
	 sum=0.0;
	 for (j=0; j < num_sources; j++) {
	    dx = ix - xpoint[j];
	    dy = ybase - ypoint[j];
	    s = Math.sqrt(dx*dx+dy*dy);
	    if (s > 0.0) {
	       sum += srcph[j] * Math.cos(wave_number * s)/s;
	    }
	 }
	 p = ybase + (int) (pscale * sum);
	 bgra.drawLine(ix,ybase,ix,p);
      }

      // --- Approximation ---

      int pmem;
      double s0;
      bgra.setColor(Color.black);

      pmem = ybase;
      for (i=0; i < max_points; i++) {
	 ix = ix0 + i;
	 s = ix - xbase;
	 p = ybase + (int) (pscale * get_value(s));
	 if (i > 0) bgra.drawLine(ix-1,pmem,ix,p);
	 pmem=p;
      }

   }

   public void update (Graphics g) {
      //System.out.println("update");
      repaint_flag=true;
      paint(g);
   }

   public void paint(Graphics g) {
      //System.out.println("paint");
      if (img_buffer == null) {
         img_width=size().width;
         img_height=size().height;
	 img_buffer = createImage(img_width, img_height);
         System.out.println("Image: " + img_width + "x" + img_height);
	 bgra = img_buffer.getGraphics();
      }
      //bgra.setColor(Color.orange);
      if (repaint_flag) {
	 bgra.setColor(new Color(100,240,255));
	 bgra.fillRect(0,0,img_width,img_height);
	 bgra.setColor(Color.white);
	 bgra.fillRect(10,img_height-80,20+4*max_multi,70);
	 draw_wave();
	 draw_bars();
	 repaint_flag=false;
      }
      g.drawImage(img_buffer,0,0,this);
   }
}

// -- Regler zum Einstellen der Parameter ---

class multipols_slider {
   int num_steps=400;
   double max_val, min_val;
   double fak;
   double val;
   Scrollbar sb;
   Label lb1;
   Label lb2;
   public multipols_slider (Panel home,
			   String text,
			   double range,
			   double init_val,
			   double min_val,
			   int num_steps) {

      max_val = min_val + range;
      this.min_val = min_val;
      this.num_steps = num_steps;
      val = init_val;
      fak=range/num_steps;
      int n = (int)((init_val-min_val)/range * num_steps);
      if (n < 0) n = 0;
      if (n > num_steps) n = num_steps;
      int bubb=(int)(0.2*num_steps);
      sb= new Scrollbar(Scrollbar.HORIZONTAL,n,bubb,0,num_steps+bubb);
      lb1= new Label(text + ": ");
      lb1.setAlignment(Label.RIGHT);
      lb2= new Label("");
      home.add(lb1);
      home.add(lb2);
      home.add(sb);
      set_val(n);
   }

   public void set_val(int n) {
      if (n > num_steps) n = num_steps;
      val = (n * fak) + min_val;
      if (val > max_val) val = max_val;
      if (val < min_val) val = min_val;
      String s;
      
      if (val == Math.rint(val)) {
	 s = new String((int) val + "           0");
      } else {
	 s = new String(val + "            0");
      }

      lb2.setText(s.substring(0,7));
   }
}

// --- Hauptteil ---

public class multipols extends Applet {
   multipols_viewport vp=null;
   Thread engine=null;
   multipols_slider s[];
   Choice ch1,ch3,ch5;
   //Checkbox cbx;
   String ch3_1= new String("100% in Phase");
   String ch3_2= new String("50% gegenphasig");
   String ch3_3= new String("25% gegenphasig");
   int delay_time=100;
   int max_item=6;
   int max_slider=5;

   public void init () {
      setLayout(new BorderLayout());
      Panel leiste = new Panel();

      //Panel help1 = new Panel();
      Label ll1=new Label("Anzahl der Monopole:");
      ll1.setAlignment(Label.RIGHT);
      Label ll5=new Label("Skalierung:");
      ll5.setAlignment(Label.RIGHT);

      ch1 = new Choice();
      ch1.addItem("2");
      ch1.addItem("3");
      ch1.addItem("4");
      ch1.addItem("6");
      ch1.addItem("8");
      ch1.addItem("12");
      ch1.addItem("16");
      ch1.addItem("21");
      ch1.addItem("24");
      ch1.addItem("32");
      ch1.select("4");
      //help1.add(ch1);

      ch3 = new Choice();
      ch3.addItem(ch3_1);
      ch3.addItem(ch3_2);
      ch3.addItem(ch3_3);
      ch3.select(0);
      
      ch5 = new Choice();
      ch5.addItem("1x");
      ch5.addItem("2x");
      ch5.addItem("4x");

      leiste.add(ll1);
      leiste.add(ch1);
      leiste.add(ch3);
      leiste.add(ll5);
      leiste.add(ch5);

      Panel grid = new Panel();
      grid.setLayout(new GridLayout(5,3,4,4));

      s = new multipols_slider[max_slider];
      s[0] = new multipols_slider(grid, "Ausdehnung [Pixel]", 80.0, 20.0, 0.0, 400);
      s[4] = new multipols_slider(grid, "Position [Pixel]", 480.0, 120.0, -300.0, 320);
      s[1] = new multipols_slider(grid, "Wellenlaenge [Pixel]", 600.0, 80.0, 20.0, 400);
      s[2] = new multipols_slider(grid, "Streuung", 1.0, 0.68, 0.0, 400);
      s[3] = new multipols_slider(grid, "Level", 30.0, 0, 0, 30);

      add ("North",leiste);
      add ("South",grid);

      vp = new multipols_viewport(24);
      add ("Center", vp);

      if (vp != null) {
	 vp.set_eps(s[0].val);
	 vp.set_lambda(s[1].val);
	 vp.set_seed(s[2].val);
      }
   }

   public boolean action(Event evt, Object arg) {
      int i;
      if (vp != null) {
	 if (evt.target == ch1) {
	    int n = 4;
	    try  {
	       n = Integer.parseInt("" + arg);
	    }
	    catch (NumberFormatException e) {}
	    vp.set_num_sources(n);
	    vp.repaint();
	 }
	 if (evt.target == ch3) {
	    //System.out.println(arg);
	    if (vp != null) {
	       int n = 0;
	       if (ch3_2.equals(arg)) n = 1;
	       if (ch3_3.equals(arg)) n = 2;
	       vp.set_phase(n);
	       vp.repaint();
	    }
	 }
	 if (evt.target == ch5) {
	    //System.out.println(arg);
	    if (vp != null) {
	       int n;
	       for (n = 1; n <= 4; n++) {
		  String s = n + "x";
		  if (s.equals(arg)) {
		     vp.set_scale(n);
		     break;
		  }
	       }
	       vp.repaint();
	    }
	 }
      }
      return true;
   }

   public boolean handleEvent(Event evt) {
      //System.out.println(evt.target);
      int i;
      if ((vp != null) && (evt.arg != null)) {
	 for (i=0; i < max_slider; i++) {
	    if (evt.target == s[i].sb) {
	       int n = ((Integer)(evt.arg)).intValue();
	       //System.out.println("Ja: " + i + " " + n);
	       s[i].set_val(n);
	       switch (i) {
		case 0: vp.set_eps(s[i].val); break;
		case 1: vp.set_lambda(s[i].val); break;
		case 2: vp.set_seed(s[i].val); break;
		case 3: vp.set_level((int)s[i].val); break;
		case 4: vp.set_xbase(s[i].val); break;
		default: break;
	       }
	       vp.repaint();
	       return true;
	    }
	 }
      }
      return super.handleEvent(evt);
   }
}

