// PictureApplet3.java: Create a Braid picture with three
//                      independent degrees of freedom
import java.applet.Applet;
import java.awt.*;import java.awt.event.*;
import java.util.Random;

public class PictureApplet3 extends Applet
     implements ActionListener {

   // applet parts
   Label prompt;
   private TextField mInput, dInput, sInput;
   private Button mpButton, dpButton, spButton, mmButton, dmButton, smButton;
   private Button fill;
   private double xc = 325, yc = 400; // center of circle
   private double r = 300; // radius of circle
   private int m = 7; // number of points around the circle
   private double alf = 2.0*Math.PI/m; // basic angle around circle
   private boolean isFilled = true;
   // points tracing one portion (out of m)
   private double x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6;
   // extra points used for the calculations
   private double x7, y7, x8, y8, x9, y9, x10, y10, x11, y11,
      x12, y12, x13, y13, x14, y14, x15, y15, x16, y16, x17, y17;
   private double xInter, yInter; // values returned by inter
   private double space = 0.03; // fraction of r that is space
   private double delta = 1.0 - (1.0 + space)*Math.cos(alf); //width of braid
   private double t = (1.0 - delta)*r; // used for calculations
   private double tp = (1.0 + space)*r; // used for calculations
   private double tq = (1.0 - delta - space)*r; // used for calculations
   private int[] xHandle = new int[6]; // used to fill polygon
   private int[] yHandle = new int[6]; // used to fill polygon

   public void init() {
      setBackground(Color.white);
      prompt = new Label("Points, Delta, Space:");
      mInput = new TextField(2);
      dInput = new TextField(7);
      sInput = new TextField(7);
      mpButton = new Button("+P");
      dpButton = new Button("+D");
      spButton = new Button("+S");
      mmButton = new Button("-P");
      dmButton = new Button("-D");
      smButton = new Button("-S");
      fill     = new Button("Fill/not");
      mInput.setEditable(true);
      dInput.setEditable(true);
      sInput.setEditable(true);
      mInput.addActionListener(this);
      dInput.addActionListener(this);
      sInput.addActionListener(this);
      mpButton.addActionListener(this);
      dpButton.addActionListener(this);
      spButton.addActionListener(this);
      mmButton.addActionListener(this);
      dmButton.addActionListener(this);
      smButton.addActionListener(this);
      fill.addActionListener(this);
      add(prompt);
      add(mInput); add(mpButton); add(mmButton);
      add(dInput); add(dpButton); add(dmButton);
      add(sInput); add(spButton); add(smButton);
      add(fill);
      mInput.setText(m + "");
      dInput.setText(shorten(delta + ""));
      sInput.setText(shorten(space + ""));
   }            

   // inter: calculate intersection of two lines
   //  point is returned in globals xInter and yInter
   public void inter(double x1, double y1, double x2, double y2,   
                double u1, double v1, double u2, double v2) {
      double m; // slope of first line
      double n; // slope of second line
      if (Math.abs(x1 - x2) < 0.00000001) { // nearly vertical
         n = (v2 - v1)/(u2 - u1);
         xInter = x1;
         yInter = n*x1 + v1 - n*u1;
      }
      else if (Math.abs(u1 - u2) < 0.00000001) { // 2nd line nearly vertical
         m = (y2 - y1)/(x2 - x1);
         xInter = u1;
         yInter = m*u1 + y1 - m*x1;
      }
      else {
         m = (y2 - y1)/(x2 - x1);
         n = (v2 - v1)/(u2 - u1);
         if (Math.abs(m - n) < 0.00000001) {
            System.out.println("Lines nearly parallel");
            System.exit(1);
         }
         xInter = (v1 - n*u1 - y1 + m*x1)/(m - n);
         yInter = (m*v1 - m*n*u1 - n*y1 + m*n*x1)/(m - n);
      }
   } 

   public void actionPerformed(ActionEvent e) {
      if (e.getSource() == mInput) { // set new number of points
         m = Integer.parseInt(e.getActionCommand());
         if (m < 5) {
            m = 5;
            mInput.setText(m + "");
         }
         alf = 2.0*Math.PI/m;
         delta = 1.0 - (1.0 + space)*Math.cos(alf);
         dInput.setText(shorten(delta + ""));
      }
      else if (e.getSource() == dInput) { // set new delta value
         delta = (new Double(e.getActionCommand())).doubleValue();
      }
      else if (e.getSource() == sInput) { // set new space value
         space = (new Double(e.getActionCommand())).doubleValue();
         // delta = 1.0 - (1.0 + space)*Math.cos(alf);
         // dInput.setText(shorten(delta + ""));
      }
      else if (e.getSource() == mpButton) { // increment m
         m++;
         alf = 2.0*Math.PI/m;
         delta = 1.0 - (1.0 + space)*Math.cos(alf);
         dInput.setText(shorten(delta + ""));
         mInput.setText(m + "");
      }
      else if (e.getSource() == mmButton) { // decrement m
         m--;
         if (m < 5) m = 5;
         alf = 2.0*Math.PI/m;
         delta = 1.0 - (1.0 + space)*Math.cos(alf);
         dInput.setText(shorten(delta + ""));
         mInput.setText(m + "");
      }
      else if (e.getSource() == dpButton) { // increase delta
         delta *= (1.0 + space);
         dInput.setText(shorten(delta + ""));
      }
      else if (e.getSource() == dmButton) { // decrease delta
         delta *= (1.0 - space);
         dInput.setText(shorten(delta + ""));
      }
      else if (e.getSource() == spButton) { // increase space
         space *= (1.1);
         sInput.setText(shorten(space + ""));
      }
      else if (e.getSource() == smButton) { // decrease space
         space *= (0.9);
         sInput.setText(shorten(space + ""));
      }
      else if (e.getSource() == fill) { // fill / no fill
         if (isFilled) isFilled = false;
         else isFilled = true;
      }
      repaint();
   }

   private String shorten(String s) {
      return s.substring(0, Math.min(7, s.length()));
   }

   public void paint(Graphics g) {
      g.drawString("Points: Number of vertices taken around" +
         " the circle", 20, 50);
      g.drawString("Delta: Width of the braid as a fraction of" +
         " the radius (defaults to maximum value)", 20, 66);
      g.drawString("Space: Space between braid parts" +
         " (as a fraction of the radius)", 20, 80);
      if (delta > 1.0 - (1.0 + space)*Math.cos(alf) || delta < 0.0) {
         g.setColor(Color.red);
         g.drawString("WARNING: Delta has meaningless value", 20, 96);
      }
      t = (1.0 - delta)*r;
      tp = (1.0 + space)*r;
      tq = (1.0 - delta - space)*r;
      for (int j = 0; j < m; j++) {
         x10 = tq*Math.cos(alf + j*alf);
         y10 = tq*Math.sin(alf + j*alf);
         x11 = tq*Math.cos(3*alf + j*alf);
         y11 = tq*Math.sin(3*alf + j*alf);
         x12 = r*Math.cos(2*alf + j*alf);
         y12 = r*Math.sin(2*alf + j*alf);
         x13 = t*Math.cos(2*alf + j*alf);
         y13 = t*Math.sin(2*alf + j*alf);
         x14 = t*Math.cos(-2*alf + j*alf);
         y14 = t*Math.sin(-2*alf + j*alf);
         x15 = r*Math.cos(-2*alf + j*alf);
         y15 = r*Math.sin(-2*alf + j*alf);
         x16 = tp*Math.cos(-alf + j*alf);
         y16 = tp*Math.sin(-alf + j*alf);
         x17 = tp*Math.cos(alf + j*alf);
         y17 = tp*Math.sin(alf + j*alf);

         x1 = r*Math.cos(0 + j*alf);
         y1 = r*Math.sin(0 + j*alf);
         // (x2,y2) is inter of 10-11 and 1-12
         inter(x10, y10, x11, y11, x1, y1, x12, y12);
         x2 = xInter;
         y2 = yInter;
         x4 = t*Math.cos(0 + j*alf);
         y4 = t*Math.sin(0 + j*alf);
         // (x3,y3) is inter of 10-11 and 4-13
         inter(x10, y10, x11, y11, x4, y4, x13, y13);
         x3 = xInter;
         y3 = yInter;
         // (x5,y5) is inter of 4-14 and 16-17
         inter(x4, y4, x14, y14, x16, y16, x17, y17);
         x5 = xInter;
         y5 = yInter;
         // (x6,y6) is inter of 1-15 and 16-17
         inter(x1, y1, x15, y15, x16, y16, x17, y17);
         x6 = xInter;
         y6 = yInter;
         g.setColor(Color.red);
         if (isFilled) {
            xHandle[0] = (int)(x1+xc); yHandle[0] = (int)(y1+yc);
            xHandle[1] = (int)(x2+xc); yHandle[1] = (int)(y2+yc);
            xHandle[2] = (int)(x3+xc); yHandle[2] = (int)(y3+yc);
            xHandle[3] = (int)(x4+xc); yHandle[3] = (int)(y4+yc);
            xHandle[4] = (int)(x5+xc); yHandle[4] = (int)(y5+yc);
            xHandle[5] = (int)(x6+xc); yHandle[5] = (int)(y6+yc);
            g.fillPolygon(xHandle, yHandle, xHandle.length);
         }
         else {
            g.drawLine((int)(x1+xc),(int)(y1+yc),(int)(x2+xc),(int)(y2+yc));
            g.drawLine((int)(x2+xc),(int)(y2+yc),(int)(x3+xc),(int)(y3+yc));
            g.drawLine((int)(x3+xc),(int)(y3+yc),(int)(x4+xc),(int)(y4+yc));
            g.drawLine((int)(x4+xc),(int)(y4+yc),(int)(x5+xc),(int)(y5+yc));
            g.drawLine((int)(x5+xc),(int)(y5+yc),(int)(x6+xc),(int)(y6+yc));
            g.drawLine((int)(x6+xc),(int)(y6+yc),(int)(x1+xc),(int)(y1+yc));
         } // end if
      } // end for
   } // end paint
}  

