/*
fractype.java
copyright 1990,2001 Bill Prewitt

Inspired by the "COKE" illustration on page 100 of "Fractals Everywhere",
1st edition,by Michael Barnsley, 1988, Academic Press
Written in MS Quick C 2.5 in 1990, then ported to Java Applet in 2001
*/

import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.io.*;
import java.lang.String;
import java.util.Random;

/*
<applet code="fractype02" width=800 height=600 >
</applet>
*/


public class fractype02 extends Applet
  implements ActionListener {
//  TextField scrwidth;
//  TextField scrheight;
  TextField myword;
  CheckboxGroup iterationSet;
  Button updateButton;

  public void init() {

  this.setBackground(Color.white);

//    Label scrwidthp = new Label("Width (pixels): ",Label.RIGHT);
//    scrwidth= new TextField("800");
//    add(scrwidthp);
//    add(scrwidth);

//    Label scrheightp = new Label("Height (pixels): ",Label.RIGHT);
//    scrheight= new TextField("600");
//    add(scrheightp);
//    add(scrheight);

    Label mywordp = new Label("Word: ",Label.LEFT);
    myword= new TextField(10);
    myword.setText("beer");
    add(mywordp);
    add(myword);

    Label iterationp = new Label("Iterations: ",Label.LEFT);
    iterationSet = new CheckboxGroup();
    add(iterationp);
    add(new Checkbox("20000",iterationSet,true));
    add(new Checkbox("80000",iterationSet,false));
    add(new Checkbox("500000",iterationSet,false));

    updateButton = new Button("Draw");
    add(updateButton);

    // register to receive action events
    updateButton.addActionListener(this);   // press the button
    myword.addActionListener(this);   // Enter the word
//    scrwidth.addActionListener(this);
//    scrheight.addActionListener(this);
  }


  // User pressed Enter or clicked button or whatever was registered above
  public void actionPerformed(ActionEvent ae) {
    repaint();
  }


  public void paint( Graphics g) {

    g.drawString(" ************* F R A C T Y P E ******************* ",10,40);
    g.drawString(" A fractal font rendering program ",10,60);
    g.drawString("   written by Bill Prewitt 13 Nov 1990, rewritten as Java applet 11 Dec 2001  ",10,80);

     rifs(g, myword);      // Random Iterated Function Set

    g.drawString(" .... done ",10,570);

  }


//public void rifs (Graphics g,  TextField scrwidth,  TextField scrheight,  TextField myword)
public void rifs (Graphics g,  TextField myword)
  {
     double x=0. , y=0. , newx=0., newy=0. ;
     int i , j, k, l, m, n, ix, iy ;
     int nstrokes ;
     double p0,  p1,  p2,  p3,  p4,  p5,  p6,  p7,  p8,  p9  ;
     double p10, p11, p12, p13, p14, p15, p16, p17, p18, p19 ;
     double p20, p21, p22, p23, p24, p25, p26, p27, p28, p29 ;
     double p30, p31, p32, p33, p34, p35, p36, p37, p38, p39 ;
     // BUG Here - this is only good for 40 strokes - then it blows up.
     double a[],b[],c[],d[],e[],f[],p[];
     a = new double[40];
     b = new double[40];
     c = new double[40];
     d = new double[40];
     e = new double[40];
     f = new double[40];
     p = new double[40];

   double a11 = -.0015625  ;
   double a12 = .0015625   ;
   double a13 = 0.         ;
   double a21 = -.00104166 ;
   double a22 = -.00104166 ;
   double a23 = .00208333  ;
   double a31 = 1.         ;
   double a32 = 0.         ;
   double a33 = 0.         ;

   double dotx[][], doty[][] ;
   dotx = new double[40][3] ;
   doty = new double[40][3] ;
   char charnam[] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','.','-','!','?'} ;
   int charstrokes[] = {3,6,3,4,4,3,4,3,1,3,3,2,4,3,4,4,5,5,5,2,3,2,4,2,3,3,1,1,2,5,0,0,0} ;
	  // (sum is 97)  a b c d e f g h i j k l m n o p q r s t u v w x y z . - ! ?

   int charofs[] ;
   charofs = new int[32] ;
   double charkern[];
   charkern = new double[32];
   double maxkern, avgkern ;
   int nchar, letrno, istroke ;
   char letr ;
   double ymult, xmult, xofs ;

   double fontstroke[][] = {
      // this array is x0,y0,x1,y1,x2,y2 (three points define stroke area)
      // lay it out on graph paper in a 100x100 grid
      // A3
      {20.,0.,40.,100.,10.,50.},
      {80.,0.,45.,100.,40.,55.},
      {25.,15.,60.,15.,40.,35.},
      // B6
      {30.,0.,30.,100.,0.,50.},
      {20.,80.,70.,80.,40.,100.},
      {20.,40.,70.,40.,40.,60.},
      {20.,0.,70.,0.,40.,20.},
      {60.,95.,60.,50.,75.,70.},
      {60.,45.,60.,05.,75.,20.},
      // C3
      {30.,0.,30.,100.,0.,50.},
      {20.,80.,70.,80.,40.,100.},
      {20.,0.,70.,0.,40.,20.},
      // D4
      {30.,0.,30.,100.,0.,50.},
      {20.,85.,70.,70.,40.,90.},
      {60.,80.,60.,10.,80.,50.},
      {20.,0.,70.,10.,40.,20.},
      // E4
      {0.,20.,0.,80.,20.,50.},
      {0.,80.,70.,80.,35.,100.},
      {20.,40.,60.,40.,40.,60.},
      {0.,0.,80.,0.,40.,20.},
      // F3
      {30.,0.,30.,100.,0.,50.},
      {25.,80.,70.,80.,40.,100.},
      {25.,40.,50.,40.,35.,60.},
      // G4
      {30.,0.,30.,100.,0.,50.},
      {20.,80.,70.,80.,40.,100.},
      {50.,40.,50.,10.,70.,30.},
      {20.,0.,70.,0.,40.,20.},
      // H3
      {30.,0.,30.,100.,0.,50.},
      {50.,0.,50.,100.,80.,50.},
      {25.,30.,55.,30.,40.,50.},
      // I1
      {30.,0.,30.,100.,10.,50.},
      // J3
      {70.,10.,70.,100.,40.,50.},
      {10.,0.,65.,0.,30.,20.},
      {0.,40.,0.,10.,20.,25.},
      // K3
      {30.,0.,30.,100.,0.,50.},
      {25.,50.,70.,80.,35.,80.},
      {25.,50.,50.,00.,60.,35.},
      // L2
      {0.,100.,0.,10.,20.,55.},
      {0.,0.,65.,0.,35.,20.},
      // M4
      {0.,0.,30.,0.,15.,100.},
      {60.,0.,90.,0.,75.,100.},
      {20.,75.,35.,40.,40.,70.},
      {45.,40.,70.,80.,50.,70.},
      // N3
      {20.,0.,20.,100.,0.,60.},
      {20.,80.,50.,0.,50.,60.},
      {50.,100.,50.,0.,70.,60.},
      // O4
      {20.,0.,20.,100.,0.,50.},
      {50.,100.,50.,0.,70.,50.},
      {10.,80.,60.,80.,35.,100.},
      {10.,0.,60.,0.,35.,20.},
      // P4
      {30.,0.,30.,100.,0.,50.},
      {10.,80.,60.,80.,35.,100.},
      {50.,90.,50.,50.,70.,70.},
      {15.,40.,60.,40.,40.,60.},
      // Q5
      {20.,40.,55.,80.,20.,70.},
      {30.,85.,60.,50.,70.,80.},
      {70.,60.,30.,20.,65.,30.},
      {40.,20.,15.,50.,15.,25.},
      {50.,15.,60.,0.,70.,15.},
      // R5
      {30.,0.,30.,100.,0.,50.},
      {05.,80.,60.,80.,40.,100.},
      {50.,90.,50.,50.,70.,70.},
      {15.,40.,50.,40.,40.,60.},
      {45.,40.,65.,0.,70.,30.},
      // S5
      {10.,80.,60.,80.,40.,100.},
      {30.,50.,30.,90.,0.,70.},
      {15.,40.,50.,40.,40.,60.},
      {40.,50.,40.,10.,70.,30.},
      {00.,00.,60.,00.,30.,20.},
      // T2
      {25.,0.,45.,0.,35.,80.},
      {0.,80.,70.,80.,35.,100.},
      // U3
      {20.,10.,20.,100.,0.,60.},
      {50.,100.,50.,10.,70.,60.},
      {10.,0.,60.,0.,35.,20.},
      // V2
      {0.,85.,25.,0.,30.,60.},
      {40.,0.,70.,80.,40.,60.},
      // W4
      {0.,90.,15.,0.,30.,50.},
      {80.,0.,90.,95.,60.,50.},
      {30.,0.,50.,60.,30.,35.},
      {40.,50.,60.,0.,60.,30.},
      // X2
      {60.,0.,0.,95.,50.,60.},
      {25.,0.,80.,100.,30.,55.},
      // Y3
      {30.,0.,50.,0.,40.,60.},
      {0.,90.,30.,60.,30.,80.},
      {50.,60.,80.,90.,50.,80.},
      // Z3
      {0.,80.,80.,80.,40.,100.},
      {20.,0.,75.,80.,35.,60.},
      {0.,0.,80.,0.,40.,20.},
      // .
      {0.,0.,20.,0.,20.,20.},
      // -
      {10.,40.,60.,40.,35.,60.},
      // !
      {5.,0.,25.,0.,15.,20.},
      {0.,30.,30.,30.,15.,100.},
      // ?
      {20.,0.,40.,0.,30.,20.},
      {25.,20.,70.,50.,30.,50.},
      {50.,50.,70.,50.,60.,90.},
      {10.,80.,60.,80.,35.,100.},
      {0.,70.,20.,70.,10.,90}   } ;

     char buf[] ;
     String inWord;
     String upperWord;
     Random rand = new Random();
     char bitmask, bytout;

     long count = Long.parseLong(iterationSet.getSelectedCheckbox().getLabel());

//     String sscrwidth = scrwidth.getText();
//     String sscrheight = scrheight.getText();
//     Integer nscrwidth=Integer.valueOf(sscrwidth);
//     Integer nscrheight=Integer.valueOf(sscrheight);

     inWord=myword.getText();
     upperWord=inWord.toUpperCase();
     char mychar = '?';
     char  isay[] = {'?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?'};

     nchar=upperWord.length();

     for (i = 0; i < nchar; i++){
       mychar=upperWord.charAt(i);
       isay[i]=mychar;
     }

//     g.drawString(" The rifs WIDTH is: " + nscrwidth,10,240);
//     g.drawString(" The rifs HEIGHT is: " + nscrheight,10,260);
//     g.drawString(" The rifs WORD is: " + upperWord,10,280);
//     g.drawString(" nchar is: "+nchar ,10,300);

   // derive some arrays from the stroke maps
   charofs[0] = (int) 0 ;
   for(i = 1; i < 32; i++){
      charofs[i] = charofs[i-1] + charstrokes[i-1] ;
   }


   // charkern array is max X in each letter
   for(i = 0; i < 32; i++){
      j=charofs[i];
      charkern[i] = 0. ;
      for (k = 0; k < charstrokes[i]; k++){
	 // find max X in each of 3 points in each stroke of each letter
   	 if(fontstroke[j+k][0] > charkern[i] )
  charkern[i] = fontstroke[j+k][0] ;
  	 if(fontstroke[j+k][2] > charkern[i] )
  charkern[i] = fontstroke[j+k][2] ;
  	 if(fontstroke[j+k][4] > charkern[i] )
  charkern[i] = fontstroke[j+k][4] ;
      }
   }


   // calculate maxkern = total character cell width requested
   maxkern = 0. ;
   avgkern = 0. ;
   for( i = 0; i < nchar; i++){
      letr = upperWord.charAt(i) ;
//      g.drawString(" letr: "+ letr,500,320+20*i);
      for( letrno = 0; letrno < 33; letrno++){
	 if(letr == charnam[letrno]) break ;
      }
      // we broke out with either a match or 32,
      //    but 32 points to white space
      // BUG here - blows up if no match
//    g.drawString(" maxkern,avgkern,nchar: "+ maxkern+avgkern+nchar,300,340+20*i);
      maxkern = maxkern + charkern[letrno] ;
      avgkern = maxkern / nchar ;
   }


   // initialize & calculate cell multipliers to fit screen
   // each letter was mapped into 100x100 cell: map into nscrheight x nscrwidth screen
   ymult = 4.8 ;
   xofs = 0. ;
   xmult = 640./((avgkern+5.) * nchar) ;
   // look up strokes for each character, scale & put in dot arrays
   nstrokes = 0 ;

     for( i = 0; i < nchar; i++){
      letr = isay[i] ;
      for( letrno = 0; letrno < 33; letrno++){
	 if(letr == charnam[letrno]) break ;
      }
      // we broke out with either a match or 32,
      //    but 32 points to white space
      // BUG here - blows up if no match
//      g.drawString(" letrno: "+ letrno,500,320+20*i);

      for( j = 0; j < charstrokes[letrno]; j++){
	 istroke = charofs[letrno] + j ;
	 dotx[nstrokes][0] = fontstroke[istroke][0]  * xmult + xofs ;
	 dotx[nstrokes][1] = fontstroke[istroke][2]  * xmult + xofs ;
	 dotx[nstrokes][2] = fontstroke[istroke][4]  * xmult + xofs ;

	 doty[nstrokes][0] = fontstroke[istroke][1]  * ymult ;
	 doty[nstrokes][1] = fontstroke[istroke][3]  * ymult ;
	 doty[nstrokes][2] = fontstroke[istroke][5]  * ymult ;
	 nstrokes++ ;
      }
      xofs = xofs + xmult * charkern[letrno] + 10. ;
//      g.drawString(" nstrokes,xofs: "+nstrokes+"|"+xofs,400,320+20*i);
   }

   for ( i=0 ; i<40 ; i++){
      p[i] = 0. ;
   }

   for ( i = 0; i < nstrokes; i++){
	  // scale: multiply by inverted full screen
      a[i] = dotx[i][0] * a11 + dotx[i][1] * a12 + dotx[i][2] * a13 ;
      b[i] = dotx[i][0] * a21 + dotx[i][1] * a22 + dotx[i][2] * a23 ;
      c[i] = doty[i][0] * a11 + doty[i][1] * a12 + doty[i][2] * a13 ;
      d[i] = doty[i][0] * a21 + doty[i][1] * a22 + doty[i][2] * a23 ;
      e[i] = dotx[i][0] ;
      f[i] = doty[i][0] ;
      p[i] = 1. / nstrokes ;  // should be weighted by area of stroke
   }

   x= 0. ;
   y= 0. ;
   p0= p[0]*32768 ;
   p1= p0+p[1]*32768 ;
   p2= p1+p[2]*32768 ;
   p3= p2+p[3]*32768 ;
   p4= p3+p[4]*32768 ;
   p5= p4+p[5]*32768 ;
   p6= p5+p[6]*32768 ;
   p7= p6+p[7]*32768 ;
   p8= p7+p[8]*32768 ;
   p9= p8+p[9]*32768 ;
   p10= p9+p[10]*32768 ;
   p11= p10+p[11]*32768 ;
   p12= p11+p[12]*32768 ;
   p13= p12+p[13]*32768 ;
   p14= p13+p[14]*32768 ;
   p15= p14+p[15]*32768 ;
   p16= p15+p[16]*32768 ;
   p17= p16+p[17]*32768 ;
   p18= p17+p[18]*32768 ;
   p19= p18+p[19]*32768 ;
   p20= p19+p[20]*32768 ;
   p21= p20+p[21]*32768 ;
   p22= p21+p[22]*32768 ;
   p23= p22+p[23]*32768 ;
   p24= p23+p[24]*32768 ;
   p25= p24+p[25]*32768 ;
   p26= p25+p[26]*32768 ;
   p27= p26+p[27]*32768 ;
   p28= p27+p[28]*32768 ;
   p29= p28+p[29]*32768 ;
   p30= p29+p[30]*32768 ;
   p31= p30+p[31]*32768 ;
   p32= p31+p[32]*32768 ;
   p33= p32+p[33]*32768 ;
   p34= p33+p[34]*32768 ;
   p35= p34+p[35]*32768 ;
   p36= p35+p[36]*32768 ;
   p37= p36+p[37]*32768 ;
   p38= p37+p[38]*32768 ;
   p39= p38+p[39]*32768 ;



// *****************************************************************
   // main rendering loop:
// *****************************************************************

   for ( i=0; i <count; i++ ) {
      j= (int) (32768. * rand.nextDouble()); // j is random between 0 and 32767
      if(j < 0) j= -j ;
      k= 0 ;             // select one of the strokes
      if(j > p0 )k= 1 ;
      if(j > p1 )k= 2 ;
      if(j > p2 )k= 3 ;
      if(j > p3 )k= 4 ;
      if(j > p4 )k= 5 ;
      if(j > p5 )k= 6 ;
      if(j > p6 )k= 7 ;
      if(j > p7 )k= 8 ;
      if(j > p8 )k= 9 ;
      if(j > p9 )k= 10 ;
      if(j > p10)k= 11 ;
      if(j > p11)k= 12 ;
      if(j > p12)k= 13 ;
      if(j > p13)k= 14 ;
      if(j > p14)k= 15 ;
      if(j > p15)k= 16 ;
      if(j > p16)k= 17 ;
      if(j > p17)k= 18 ;
      if(j > p18)k= 19 ;
      if(j > p19)k= 20 ;
      if(j > p20)k= 21 ;
      if(j > p21)k= 22 ;
      if(j > p22)k= 23 ;
      if(j > p23)k= 24 ;
      if(j > p24)k= 25 ;
      if(j > p25)k= 26 ;
      if(j > p26)k= 27 ;
      if(j > p27)k= 28 ;
      if(j > p28)k= 29 ;
      if(j > p29)k= 30 ;
      if(j > p30)k= 31 ;
      if(j > p31)k= 32 ;
      if(j > p32)k= 33 ;
      if(j > p33)k= 34 ;
      if(j > p34)k= 35 ;
      if(j > p35)k= 36 ;
      if(j > p36)k= 37 ;
      if(j > p37)k= 38 ;
      if(j > p38)k= 39 ;

// do the affine transform for this stroke
      newx= a[k]*x + b[k]*y + e[k] ;
      newy= c[k]*x + d[k]*y + f[k] ;
      x= newx ;
      y= newy ;
      ix= (int) x + (int) 100;
      iy= (int) (580. - y) ;
      g.drawLine(ix,iy,ix,iy);

//      if(kbhit()) break ;
     }
//     g.drawString(" rifs .... after dot plot ",10,320);
  }
}



