/***********************************************************************
 ** Calculus for Kinetic Modeling Mathlet: Reflecting Power Functions **
 ** Interactive MathVision, Copyright by John Pais 1997-1998.         **
 ** All Rights Reserved.             Created by John Pais, July 1998. ** 
 ***********************************************************************/
 
/** USE EXISTING JAVA CLASSES **/

import java.awt.*;
import java.awt.event.*;                               // Needed for version 1.1 of the AWT.
import java.applet.Applet;

/** EXTEND THE JAVA APPLET CLASS **/

public class ReflectingPowerFunctions extends Applet   // Declare some class instance variables:
 {backgroundChangeButton colorbutton;                  // backgroundChangeButton (a 2nd class),                         
  animatorControlButton controlbutton;                 // animatorControlButton (a 3rd class),
  animatorScrollableCanvas animatorCanvas;             // animatorScrollableCanvas (a 4th class).
  int animatorinitframeNumber = -1;		  
  Button pinkButton,purpleButton,plumButton,           // Declare all the Buttons.
	 skyblueButton,aquaButton,cyanButton,         
	 backoneButton,forwardoneButton,
	 stopButton,startButton,
	 fasterButton,slowerButton,
	 scrollLeftButton,scrollRightButton,
	 stopLeftRightButton,
	 scrollUpButton,scrollDownButton,
	 stopUpDownButton,
	 panelUpButton,panelDownButton;
	 
  boolean scrollLeft,scrollRight,
	  scrollUp,scrollDown,
	  panelUp,panelDown;
  		   		                  
  Font gridBagFont = new Font("TimesRoman",Font.BOLD,20);  // The gridBagFont for the 
  FontMetrics gridBagFontm = getFontMetrics(gridBagFont);  // top level text.
  
  Dimension gridBagPreImageDim;                // Define preImage variables needed to
  Image gridBagPreImage;                       // create the offscreen copy of the image
  Graphics gridBagPreImageGraphics;            // we want to paint on the screen.
   
  Image gifImage[];                            // Define an array of gif images and
  MediaTracker tracker;                        // a tracker to preload them.
  
  Color pink = new Color(234,173,234);         // Primary colors used in this applet  
  Color purple = new Color(135,31,120);        // and on corresponding web page.
  Color plum = new Color(127,0,127);
  Color skyblue = new Color(112,147,219);
  Color aqua = new Color(56,176,222);
  Color richblue = new Color(63,0,255);
  Color newmidnightblue = new Color(0,0,156);
  Color starblue = new Color(0,100,255);
  Color darkblue = new Color(0,0,80);
  
  Color hotpink = new Color(255,28,174);       // Several additional colors to try.
  Color neonpink = new Color(255,110,199); 
  Color turquoise = new Color(56,147,219);
  Color aquamarine = new Color(112,219,147);
  Color chocolate = new Color(92,51,23);
  Color blueviolet = new Color(159,95,159);
  Color darkgreen = new Color(74,118,110);
  Color darkorchid = new Color(153,50,205);
  Color firebrick = new Color(142,35,35);
  Color forestgreen = new Color(35,142,35);
  Color huntergreen = new Color(33,94,33);
  Color limegreen = new Color(50,205,50);
  Color mandarinorange = new Color(228,120,51);
  Color maroon = new Color(142,35,107);
  Color mediumblue = new Color(50,50,205);
  Color mediumorchid = new Color(147,112,219);
  Color mediumslateblue = new Color(127,0,255);
  Color mediumturquoise = new Color(112,219,219);
  Color mediumvioletred = new Color(219,112,147);
  Color midnightblue = new Color(47,47,79);
  Color navyblue = new Color(35,35,142);
  Color neonblue = new Color(77,77,255);
  Color brightgold = new Color(217,217,25);
  Color orchid = new Color(219,112,219);
  //Color richblue = new Color(89,89,171);
  Color seagreen = new Color(35,142,104);
  //Color skyblue = new Color(50,153,204);
  Color slateblue = new Color(0,127,255);
  Color springgreen = new Color(0,255,127);
  Color lightturquoise = new Color(173,234,234);
  Color violetred = new Color(204,50,153);
  Color verydarkbrown = new Color(92,64,51);
   
  public void init()  
   {setBackground(purple);
    String str;
    gifImage = new Image[170];
    //gifImage = new Image[168];
    //gifImage = new Image[165];
    //gifImage = new Image[111];
    tracker = new MediaTracker(this);         // Spawn the tracker background thread(s)
    int j;                                    // in order to preload the images.
    for (int i = 1; i <= 80; i++)             // This loop codes the first 80 images for Panel 1
     {j = (i - (i % 20))/20;                  // into an array of 108 images with extra padded  
      if (i % 20 == 0)                        // images at the important transition frames. 
       {gifImage[i+(j-1)*7+4] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	tracker.addImage(gifImage[i+(j-1)*7+4],0);
	gifImage[i+(j-1)*7+5] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	tracker.addImage(gifImage[i+(j-1)*7+4],0);
	gifImage[i+(j-1)*7+6] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	tracker.addImage(gifImage[i+(j-1)*7+4],0);
	}
       else if (i % 20 == 1)
	{gifImage[i+j*7-1] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	 tracker.addImage(gifImage[i+j*7-1],0);
	 gifImage[i+j*7] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	 tracker.addImage(gifImage[i+j*7],0);
	 gifImage[i+j*7+1] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	 tracker.addImage(gifImage[i+j*7+1],0);
	 }
	else if ( (1 < i % 20) && (i % 20 < 10))  
	 {gifImage[i+j*7+1] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	  tracker.addImage(gifImage[i+j*7+1],0);
	  }
	else if (i % 20 == 10)
	 {gifImage[i+j*7+1] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	  tracker.addImage(gifImage[i+j*7+1],0);
	  gifImage[i+j*7+2] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	  tracker.addImage(gifImage[i+j*7+2],0);
	  gifImage[i+j*7+3] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	  tracker.addImage(gifImage[i+j*7+3],0);
	  gifImage[i+j*7+4] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	  tracker.addImage(gifImage[i+j*7+4],0);
	  }   
	 else if (i % 20 < 20)
	  {gifImage[i+j*7+4] = getImage(getCodeBase(),"images/rpfpnl1"+i+".gif");
	   tracker.addImage(gifImage[i+j*7+4],0);
	   }
          }
	  
    gifImage[108] = getImage(getCodeBase(),"images/rpfpnl21.gif"); // Panel 2.
    tracker.addImage(gifImage[108],0);
    gifImage[109] = getImage(getCodeBase(),"images/rpfpnl31.gif"); // Panel 3.
    tracker.addImage(gifImage[109],0);
    gifImage[110] = getImage(getCodeBase(),"images/rpfpnl32.gif");  
    tracker.addImage(gifImage[110],0);
    
    for (int i = 1; i <= 40; i++)             // Panel 4.
     {j = (i - (i % 20))/20;                    
      if (i % 20 == 0)                        
       {gifImage[111+i+(j-1)*7+4] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	tracker.addImage(gifImage[111+i+(j-1)*7+4],0);
	gifImage[111+i+(j-1)*7+5] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	tracker.addImage(gifImage[111+i+(j-1)*7+4],0);
	gifImage[111+i+(j-1)*7+6] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	tracker.addImage(gifImage[111+i+(j-1)*7+4],0);
	}
       else if (i % 20 == 1)
	{gifImage[111+i+j*7-1] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	 tracker.addImage(gifImage[111+i+j*7-1],0);
	 gifImage[111+i+j*7] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	 tracker.addImage(gifImage[i+j*7],0);
	 gifImage[111+i+j*7+1] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	 tracker.addImage(gifImage[111+i+j*7+1],0);
	 }
	else if ( (1 < i % 20) && (i % 20 < 10))  
	 {gifImage[111+i+j*7+1] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	  tracker.addImage(gifImage[111+i+j*7+1],0);
	  }
	else if (i % 20 == 10)
	 {gifImage[111+i+j*7+1] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	  tracker.addImage(gifImage[111+i+j*7+1],0);
	  gifImage[111+i+j*7+2] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	  tracker.addImage(gifImage[111+i+j*7+2],0);
	  gifImage[111+i+j*7+3] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	  tracker.addImage(gifImage[111+i+j*7+3],0);
	  gifImage[111+i+j*7+4] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	  tracker.addImage(gifImage[111+i+j*7+4],0);
	  }   
	 else if (i % 20 < 20)
	  {gifImage[111+i+j*7+4] = getImage(getCodeBase(),"images/rpfpnl4"+i+".gif");
	   tracker.addImage(gifImage[111+i+j*7+4],0);
	   }
          }
    
    gifImage[165] = getImage(getCodeBase(),"images/rpfpnl51.gif"); // Panel 5.
    tracker.addImage(gifImage[165],0);
    gifImage[166] = getImage(getCodeBase(),"images/rpfpnl52.gif"); 
    tracker.addImage(gifImage[166],0);
    gifImage[167] = getImage(getCodeBase(),"images/rpfpnl53.gif");  
    tracker.addImage(gifImage[167],0); 
    
    gifImage[168] = getImage(getCodeBase(),"images/rpfpnl61.gif"); // Panel 6.
    tracker.addImage(gifImage[168],0);
    gifImage[169] = getImage(getCodeBase(),"images/rpfpnl62.gif"); 
    tracker.addImage(gifImage[169],0);
    
    GridBagLayout gridBag = new GridBagLayout();               // Define a GridBagLayout  
    GridBagConstraints constraints = new GridBagConstraints(); // and GridBagConstraints.
    setLayout(gridBag);                                        // Note that it is DIFFICULT 
							       // to understand the total 
   /***************************************************        // cumulative effect of 
    ** DEFAULT VALUES OF GridBagConstraints                    // several components and 
    ***************************************************        // their constraints.
    ** constraints.anchor = GridBagConstraints.CENTER;  [EAST,NORTH,NORTHEAST,NORTHWEST,etc.]
    ** constraints.fill = GridBagConstraints.NONE;      [BOTH,HORIZONTAL,VERTICAL]
    ** constraints.gridx = GridBagConstraints.RELATIVE; [0,1,2,3,4,etc.]
    ** constraints.gridy = GridBagConstraints.RELATIVE; [0,1,2,3,4,etc.]
    ** constraints.gridwidth = 1;                       [RELATIVE,REMAINDER,1,2,3,4,etc.]
    ** constraints.gridheight = 1;                      [RELATIVE,REMAINDER,1,2,3,4,etc.]
    ** constraints.ipadx = 0;                           [1,2,3,4,etc.]
    ** constraints.ipady = 0;                           [1,2,3,4,etc.]
    ** constraints.insets = new Insets(0,0,0,0);        [1,2,3,4,etc.]
    ** constraints.weightx = 0.0;                       [floats]
    ** constraints.weighty = 0.0;                       [floats]
    ***************************************************/
     
   /*******************************************************************************************************
    ** GridBagConstraints for backgroundChangeButtons and Animation animatorContolButtons (Begin First Row)
    *******************************************************************************************************/
    constraints.anchor = GridBagConstraints.CENTER;    
    constraints.fill = GridBagConstraints.BOTH;        // Fill grid cell in both directions.
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = GridBagConstraints.RELATIVE;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.ipadx = 0;
    constraints.ipady = 0;
    //constraints.insets = new Insets(2,2,2,2);          // Space 2 pixels between buttons.
    constraints.weightx = 1.0;                         // Use one grid cell per button in first row.
    constraints.weighty = 0.0;
    
    constraints.insets = new Insets(2,25,2,2);
    backoneButton = new Button("Back One");            // Create and position the labeled buttons in
    controlbutton = new animatorControlButton(this);   // order of desired layout, register the Button 
    backoneButton.addActionListener(controlbutton);    // ActionListeners, and add the Buttons to the 
    gridBag.setConstraints(backoneButton,constraints); // GridBagLayout.
    add(backoneButton);  
    
    constraints.insets = new Insets(2,2,2,2);
    forwardoneButton = new Button("Forward One"); 
    controlbutton = new animatorControlButton(this); 
    forwardoneButton.addActionListener(controlbutton);            
    gridBag.setConstraints(forwardoneButton,constraints); 
    add(forwardoneButton);  
    
    constraints.insets = new Insets(2,2,2,2);
    stopButton = new Button("Stop");     
    controlbutton = new animatorControlButton(this); 
    stopButton.addActionListener(controlbutton);   
    gridBag.setConstraints(stopButton,constraints); 
    add(stopButton); 
    
    constraints.insets = new Insets(2,2,2,2);
    startButton = new Button("Start");  
    controlbutton = new animatorControlButton(this); 
    startButton.addActionListener(controlbutton);         
    gridBag.setConstraints(startButton,constraints); 
    add(startButton);  
    
    constraints.insets = new Insets(2,2,2,2);
    fasterButton = new Button("Faster");  
    controlbutton = new animatorControlButton(this); 
    fasterButton.addActionListener(controlbutton);          
    gridBag.setConstraints(fasterButton,constraints); 
    add(fasterButton);  
    
    constraints.insets = new Insets(2,2,2,25);
    slowerButton = new Button("Slower");   
    controlbutton = new animatorControlButton(this); 
    slowerButton.addActionListener(controlbutton);         
    gridBag.setConstraints(slowerButton,constraints); 
    add(slowerButton);  
    
    constraints.insets = new Insets(2,2,2,2);				 	 				 
    pinkButton = new Button("Pink");                        
    colorbutton = new backgroundChangeButton(this,pink); 
    pinkButton.addActionListener(colorbutton);                 
    gridBag.setConstraints(pinkButton,constraints);         
    add(pinkButton);  
                                         
    constraints.insets = new Insets(2,2,2,2); 
    purpleButton = new Button("Purple");
    colorbutton = new backgroundChangeButton(this,purple);
    purpleButton.addActionListener(colorbutton);    
    gridBag.setConstraints(purpleButton,constraints);
    add(purpleButton); 
    
    constraints.insets = new Insets(2,2,2,2);      
    plumButton = new Button("Dark Purple");
    colorbutton = new backgroundChangeButton(this,plum);
    plumButton.addActionListener(colorbutton);    
    gridBag.setConstraints(plumButton,constraints);
    add(plumButton);  
    
    constraints.insets = new Insets(2,2,2,2);  
    cyanButton = new Button("Cyan");
    colorbutton = new backgroundChangeButton(this,Color.cyan);
    cyanButton.addActionListener(colorbutton);    
    gridBag.setConstraints(cyanButton,constraints);
    add(cyanButton);    
    
    constraints.insets = new Insets(2,2,2,2);
    aquaButton = new Button("Aqua");
    colorbutton = new backgroundChangeButton(this,aqua);
    aquaButton.addActionListener(colorbutton);      
    gridBag.setConstraints(aquaButton,constraints);  
    add(aquaButton);
    
   /******************************************************
    ** GridBagConstraints for Cyan button (End First Row)
    ******************************************************/
    constraints.anchor = GridBagConstraints.CENTER;    
    constraints.fill = GridBagConstraints.BOTH;           // Fill each grid cell in both directions.
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = GridBagConstraints.RELATIVE;
    constraints.gridwidth = GridBagConstraints.REMAINDER; // End the first row with this button.
    constraints.gridheight = 1;
    constraints.ipadx = 0;
    constraints.ipady = 0;
    //constraints.insets = new Insets(2,2,2,2);             // Space 2 pixels between buttons.
    constraints.weightx = 1.0;                            // Use one grid cell per button in current row.
    constraints.weighty = 0.0;
    
    constraints.insets = new Insets(2,2,2,25);
    skyblueButton = new Button("Sky Blue");        
    colorbutton = new backgroundChangeButton(this,skyblue); 
    skyblueButton.addActionListener(colorbutton);   
    gridBag.setConstraints(skyblueButton,constraints); 
    add(skyblueButton);  
     
   /*******************************************************************
    ** GridBagConstraints for ScrollPane (Begin Second and Third Rows)
    *******************************************************************/
    constraints.anchor = GridBagConstraints.CENTER;    
    constraints.fill = GridBagConstraints.BOTH;           // Fill each grid cell in both directions.
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = GridBagConstraints.RELATIVE;
    constraints.gridwidth = GridBagConstraints.REMAINDER; // End the second row with this ScrollPane.
    constraints.gridheight = 2;                           // Set height to span rows 2 and 3.
    constraints.ipadx = 0;
    constraints.ipady = 0;
    constraints.insets = new Insets(5,5,5,5);         // Space 5 pixels.
    constraints.weightx = 0.0;                
    constraints.weighty = 1.0;                     // Since this ScrollPane spans rows 2 and 3, this  
						   // weighty value gets assigned to row 3.
                
    /********************************************************************************************* 
     ** NOTE that the SCROLLBARS are not used in the ScrollPane, since vertical and horizontal  **
     ** change events flashed terribly when viewed using either Nescape Communicator 4.05 or MS **
     ** Explorer 4.0, in spite of the fact that everything looked lovely in the Applet viewer.  **
     *********************************************************************************************/
    
    ScrollPane scrollPane = new ScrollPane(ScrollPane.SCROLLBARS_NEVER);
    animatorCanvas = new animatorScrollableCanvas(this,gifImage,animatorinitframeNumber);
    animatorCanvas.setBackground(richblue);
    scrollPane.add(animatorCanvas);                   // Add animatorScrollableCanvas to ScrollPane 
    gridBag.setConstraints(scrollPane,constraints);   // and then add ScrollPane to GridBagLayout.
    add(scrollPane);                                  // Event Listeners for ScrollPane are
						      // implemented automatically in 1.1 AWT.
    
    /*				      
    animatorCanvas = new animatorScrollableCanvas(this,gifImage,animatorinitframeNumber);
    animatorCanvas.setBackground(richblue);
    gridBag.setConstraints(animatorCanvas,constraints);   
    add(animatorCanvas);                                 						      						      
    */		
    				      
   /*****************************************************************************
    ** GridBagConstraints for Scrolling animatorContolButtons (Begin Fourth Row)
    *****************************************************************************/
    constraints.anchor = GridBagConstraints.CENTER;    
    constraints.fill = GridBagConstraints.BOTH;        // Fill grid cell in both directions.
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = GridBagConstraints.RELATIVE;
    //constraints.gridwidth = 2;
    constraints.gridheight = 1;
    constraints.ipadx = 0;
    constraints.ipady = 0;
    //constraints.insets = new Insets(2,2,2,2);          // Space 2 pixels between buttons.
    constraints.weightx = 1.0;                         // Use one grid cell per button in first row.
    constraints.weighty = 0.0;
    
    constraints.gridwidth = 1;
    constraints.insets = new Insets(2,25,2,2);
    scrollLeftButton = new Button("Scroll Left");            
    controlbutton = new animatorControlButton(this);    
    scrollLeftButton.addActionListener(controlbutton);    
    gridBag.setConstraints(scrollLeftButton,constraints); 
    add(scrollLeftButton);
     
    constraints.gridwidth = 1;
    constraints.insets = new Insets(2,2,2,2);
    stopLeftRightButton = new Button("Stop Scrolling");            
    controlbutton = new animatorControlButton(this);    
    stopLeftRightButton.addActionListener(controlbutton);    
    gridBag.setConstraints(stopLeftRightButton,constraints); 
    add(stopLeftRightButton);  
    
    constraints.gridwidth = 2;
    constraints.insets = new Insets(2,2,2,2);
    scrollRightButton = new Button("Scroll Right");            
    controlbutton = new animatorControlButton(this);    
    scrollRightButton.addActionListener(controlbutton);    
    gridBag.setConstraints(scrollRightButton,constraints); 
    add(scrollRightButton);  
    
    constraints.gridwidth = 2;
    constraints.insets = new Insets(2,25,2,2);
    panelUpButton = new Button("Panel Up");            
    controlbutton = new animatorControlButton(this);    
    panelUpButton.addActionListener(controlbutton);    
    gridBag.setConstraints(panelUpButton,constraints); 
    add(panelUpButton);  
    
    constraints.gridwidth = 2;
    constraints.insets = new Insets(2,2,2,2);
    panelDownButton = new Button("Panel Down");            
    controlbutton = new animatorControlButton(this);    
    panelDownButton.addActionListener(controlbutton);    
    gridBag.setConstraints(panelDownButton,constraints); 
    add(panelDownButton); 
     
    constraints.gridwidth = 1;
    constraints.insets = new Insets(2,25,2,2);
    scrollUpButton = new Button("Scroll Up");            
    controlbutton = new animatorControlButton(this);    
    scrollUpButton.addActionListener(controlbutton);    
    gridBag.setConstraints(scrollUpButton,constraints); 
    add(scrollUpButton);  
    
    constraints.gridwidth = 2;
    constraints.insets = new Insets(2,2,2,2);
    stopUpDownButton = new Button("Stop Scrolling");            
    controlbutton = new animatorControlButton(this);    
    stopUpDownButton.addActionListener(controlbutton);    
    gridBag.setConstraints(stopUpDownButton,constraints); 
    add(stopUpDownButton);  
    
    
   /**************************************************************
    ** GridBagConstraints for Scroll Down button (End Fourth Row)
    **************************************************************/
    constraints.anchor = GridBagConstraints.CENTER;    
    constraints.fill = GridBagConstraints.BOTH;           // Fill each grid cell in both directions.
    constraints.gridx = GridBagConstraints.RELATIVE;
    constraints.gridy = GridBagConstraints.RELATIVE;
    constraints.gridwidth = GridBagConstraints.REMAINDER; // End the first row with this button.
    constraints.gridheight = 1;
    constraints.ipadx = 0;
    constraints.ipady = 0;
    //constraints.insets = new Insets(2,2,2,2);             // Space 2 pixels between buttons.
    constraints.weightx = 1.0;                            // Use one grid cell per button in current row.
    constraints.weighty = 0.0;
    
    constraints.insets = new Insets(2,2,2,25);
    scrollDownButton = new Button("Scroll Down");            
    controlbutton = new animatorControlButton(this);    
    scrollDownButton.addActionListener(controlbutton);    
    gridBag.setConstraints(scrollDownButton,constraints); 
    add(scrollDownButton);  				      
    }                                                         

  public Insets getInsets()                           // Set all (top, left, bottom, right)
   {return new Insets(40,2,40,2);}                    // outer boundary insets of GridBagLayout: 
						      // 40 pixels on top and 40 on bottom for 
						      // identification/user-instruction text,   
						      // and 2 pixels at left and at right.		
						      						      				      			      
  public void start()						      
   {animatorCanvas.start();}
  
  public void stop()						      
   {animatorCanvas.stop();}  
   
  public void backoneButtonMethod()						      
   {animatorCanvas.backoneButtonMethod();}
   
  public void forwardoneButtonMethod()						      
   {animatorCanvas.forwardoneButtonMethod();}
  
  public void startButtonMethod()						      
   {animatorCanvas.startButtonMethod();}
  
  public void stopButtonMethod()						      
   {animatorCanvas.stopButtonMethod();}  
     
  public void fasterButtonMethod()						      
   {animatorCanvas.fasterButtonMethod();}  
   
  public void slowerButtonMethod()						      
   {animatorCanvas.slowerButtonMethod();}  
   
  public void scrollingCanvasMethod()						      
   {animatorCanvas.scrollingCanvasMethod();}
   
  public void stopscrollingCanvasMethod()
   {animatorCanvas.stopscrollingCanvasMethod();}	
   
  public void panelUpDownMethod()
   {animatorCanvas.panelUpDownMethod();}	 
				      				      
     
  /** CREATE THE CURRENT GRIDBAG FRAME **/	 
  
  public void paint(Graphics g)                    // This technique of painting by calling
   {update(g);}                                    // update, prevents the default (and hidden)  
						   // behavior of update, and instead allows
  public void update(Graphics g)                   // the creation of an offscreen gridBagPreImage 
   {Dimension d = size();                          // before painting the screen.        
    if ((gridBagPreImageGraphics == null) ||                // When all images are loaded, compare
	(d.width != gridBagPreImageDim.width) ||            // the current applet viewing size to 
	(d.height != gridBagPreImageDim.height))            // our gridBagPreImage, and if different,
     {gridBagPreImageDim = d;                               // resize our next gridBagPreImage.
      gridBagPreImage = createImage(d.width,d.height);      
      gridBagPreImageGraphics = gridBagPreImage.getGraphics();
      }                                     
    gridBagPreImageGraphics.setColor(getBackground());      // Clear the previous gridBagPreImage
    gridBagPreImageGraphics.fillRect(0,0,d.width,d.height); // by filling with the current
							    // background.
    gridBagPreImageGraphics.setFont(gridBagFont);
    String toptext = "CKM Mathlet:  Reflecting Power Functions"; 
    String bottext = "pais@interactive-mathvision.com";
    
    int topxstart = (size().width - gridBagFontm.stringWidth(toptext))/2;
    int botxstart = (size().width - gridBagFontm.stringWidth(bottext))/2;
    
    int fontA = gridBagFontm.getAscent();                   // Contrary to the usual 
    int fontD = gridBagFontm.getDescent();                  // description, the Ascent 
    int fontL = gridBagFontm.getLeading();                  // of a font contains white 
						     // space at the top. 
    int fontAwhite = fontD - fontL;                  // The Ascent white space. 
    int fontAtext = fontA - fontAwhite;              // The Ascent text space. 
    int fonttext = fontAtext + fontD;                // The actual text space. 
  
    int topyspacing = (40 - fonttext)/2;             // Compute vertical spacing.
    int botyspacing = (44 - fonttext)/2; 
    
    int topystart = topyspacing -2 + fonttext;
    int botystart = (size().height -44) + botyspacing + fonttext;
                    
    gridBagPreImageGraphics.setColor(newmidnightblue);  
    gridBagPreImageGraphics.drawString(toptext,topxstart,topystart);
    gridBagPreImageGraphics.drawString(bottext,botxstart,botystart);         
						     // Offscreen preImage completed.
    g.drawImage(gridBagPreImage,0,0,this);	     // Paint preImage on the screen.                                
    }    																		      																																	      					  					       							      						      
   }   
    
/***********************************************
 ** END OF 1ST CLASS: RefectingPowerFunctions **
 ***********************************************
 **  BEGIN 2ND CLASS: backgroundChangeButton  **
 ***********************************************/
	
 class backgroundChangeButton implements ActionListener  
   {Color newBackgroundColor;            		   
    ReflectingPowerFunctions callingApplet;			 
							          				   		                 
    backgroundChangeButton(ReflectingPowerFunctions methodArg,  
	        	    Color newColor)                
     {callingApplet = methodArg;	                   
      newBackgroundColor = newColor;			    
      }          					   
 
  public void actionPerformed(ActionEvent evt)             // Uses version 1.1 of the AWT.
    {if (evt.getSource() instanceof Button) 
      {if((evt.getSource() == callingApplet.pinkButton) ||
	  (evt.getSource() == callingApplet.purpleButton) ||
	  (evt.getSource() == callingApplet.plumButton) ||
	  (evt.getSource() == callingApplet.skyblueButton) ||
	  (evt.getSource() == callingApplet.aquaButton) ||
	  (evt.getSource() == callingApplet.cyanButton))
	{callingApplet.setBackground(newBackgroundColor);
	 callingApplet.repaint();
	 }
       }  
     }
   } 
        
/*********************************************
 ** END 2ND CLASS: backgroundChangeButton   **
 *********************************************
 ** BEGIN 3RD CLASS: animatorControlButton  **
 *********************************************/	
												   					   						   
  class animatorControlButton implements ActionListener                          				   
   {ReflectingPowerFunctions callingApplet;			    
    animatorControlButton(ReflectingPowerFunctions methodArg)           				 	    
     {callingApplet = methodArg;}                             					      
 
   public void actionPerformed(ActionEvent evt)             
    {if (evt.getSource() instanceof Button) 
      {if (evt.getSource() == callingApplet.backoneButton) 
	callingApplet.backoneButtonMethod();
       else if (evt.getSource() == callingApplet.forwardoneButton)
	callingApplet.forwardoneButtonMethod();
       else if (evt.getSource() == callingApplet.stopButton) 
	callingApplet.stopButtonMethod();
       else if (evt.getSource() == callingApplet.startButton)	
	callingApplet.startButtonMethod();	
       else if (evt.getSource() == callingApplet.fasterButton) 
	callingApplet.fasterButtonMethod();	
       else if (evt.getSource() == callingApplet.slowerButton) 
	callingApplet.slowerButtonMethod(); 
       else
	{callingApplet.scrollLeft = false; 
	 callingApplet.scrollRight = false; 
       	 callingApplet.scrollUp = false; 
	 callingApplet.scrollDown = false; 
	 callingApplet.panelUp = false; 
	 callingApplet.panelDown = false;  
	 if (evt.getSource() == callingApplet.scrollLeftButton) 
	  {callingApplet.scrollLeft = true; 
	   callingApplet.scrollingCanvasMethod();
	   } 
	 else if (evt.getSource() == callingApplet.scrollRightButton) 
	  {callingApplet.scrollRight = true; 
	   callingApplet.scrollingCanvasMethod();
	   } 
	 else if (evt.getSource() == callingApplet.scrollUpButton) 
	  {callingApplet.scrollUp = true; 
	   callingApplet.scrollingCanvasMethod();
	   } 
	 else if (evt.getSource() == callingApplet.scrollDownButton) 
	  {callingApplet.scrollDown = true; 
	   callingApplet.scrollingCanvasMethod();
	   } 
	 else if (evt.getSource() == callingApplet.stopLeftRightButton) 
	  callingApplet.stopscrollingCanvasMethod();  
	 else if (evt.getSource() == callingApplet.stopUpDownButton) 
	  callingApplet.stopscrollingCanvasMethod(); 
	 else if (evt.getSource() == callingApplet.panelUpButton) 
	  {callingApplet.panelUp = true; 
	   callingApplet.panelUpDownMethod();
	   } 
	 else if (evt.getSource() == callingApplet.panelDownButton) 
	  {callingApplet.panelDown = true; 
	   callingApplet.panelUpDownMethod();
	   }  
	 }  
       }  
     }
   }  
    
/***********************************************
 **   END 3RD CLASS: backgroundChangeButton   **
 ***********************************************
 ** BEGIN 4TH CLASS: animatorScrollableCanvas **
 ***********************************************/

class animatorScrollableCanvas extends Canvas implements Runnable
 {ReflectingPowerFunctions callingApplet;
  Image[] image;
  int frameNumber;
  int saveframeNumber;
  
  Dimension preferredSize = new Dimension(815, 919);
  //Dimension preferredSize = new Dimension(815, 619);
  //Dimension preferredSize = new Dimension(800, 600);
  Dimension minimumSize = new Dimension(10, 10);
   	
  int delayBetweenFrames = 200;                           // milisecond delay between frames.
  int savedelayBetweenFrames;
  
  int displayframeNumber;
  int displayFrameH = 0;
  int displayFrameV = 0;
 
  boolean scrolling = false;
 
  Thread animatorThread;	                                              
  boolean frozen = false;   
  
  Font waitfont=new Font("TimesRoman",Font.BOLD,14);
  FontMetrics waitfontm = getFontMetrics(waitfont);
  String waitstr1, waitstr2 = "";  
  
  Font panelfont = new Font("TimesRoman",Font.PLAIN,12);
  FontMetrics panelfontm = getFontMetrics(panelfont);
 
  Dimension preImageDim;                                  // Define preImage variables needed to
  Image preImage;                                         // create the offscreen copy of the image
  Graphics preImageGraphics;                              // we want to paint on the canvas.							 								 												
																							 				 			   		                 
  animatorScrollableCanvas(ReflectingPowerFunctions methodArg,   
			   Image[] img,
			   int frameNum)
   {callingApplet = methodArg;
    image = img;
    frameNumber = frameNum;
    }                                  
						 
  public Dimension getMinimumSize() 
   {return minimumSize;}
   
  public Dimension getPreferredSize() 
   {return preferredSize;}
   
   
    /** CLICK TO START OR STOP THE ANIMATOR SCROLLABLE CANVAS **/

    public boolean mouseDown(Event e, int x, int y)
     {if (!scrolling)
       {if (frozen)
         {frozen = false;
          start();
	  }
        else
         {frozen = true;
          animatorThread = null;                      // Instead of calling stop() here, since
	  }                                           // stop() now nullifies our preImage context,
	} 	                                      // nullify only the animation thread in case
      else if (scrolling)
       stopscrollingCanvasMethod();
      return true;
      }           

    public void backoneButtonMethod()
     {if (!scrolling)	
       {if (!frozen)
	 {frozen = true;
	  animatorThread = null;
	  }	
	frameNumber--;				            // These 8 lines of code remove the 
	if (frameNumber % 27 == 1) frameNumber -= 2;        // frame padding. Here we remove 2  
	else if (frameNumber % 27 == 0) frameNumber -= 1;   // extra padded frames.
	
	else if (frameNumber % 27 == 13) frameNumber -= 3;  // Here we remove 3 extra padded frames.
	else if (frameNumber % 27 == 12) frameNumber -= 2;  //
	else if (frameNumber % 27 == 11) frameNumber -= 1;  //
	
	else if (frameNumber % 27 == 25) frameNumber -= 2;  // Here we remove 2 extra padded frames.
	else if (frameNumber % 27 == 24) frameNumber -= 1;  //
	
	if (frameNumber == -1) frameNumber = 107;           //
	// frameNumber--;                                   // These 2 lines of code may be used
	// if (frameNumber == -1) frameNumber = 107;        // if you don't want to remove padding.
	repaint();
	}
      }
   
    public void forwardoneButtonMethod()	
     {if (!scrolling)					      
       {if (!frozen)
	 {frozen = true;
	  animatorThread = null;
	  }
	frameNumber = (frameNumber + 1) % 108;              // This line *alone* displays padded frames. 
							    // Including these 7 lines below removes the
	if (frameNumber % 27 == 1) frameNumber += 2;        // frame padding. Here we remove 2 extra 
	else if (frameNumber % 27 == 2) frameNumber += 1;   // padded frames.
	
	else if (frameNumber % 27 == 12) frameNumber += 3;  // Here we remove 3 extra padded frames.
	else if (frameNumber % 27 == 13) frameNumber += 2;  //
	else if (frameNumber % 27 == 14) frameNumber += 1;  //
	
	else if (frameNumber % 27 == 25) frameNumber += 2;  // Here we remove 2 extra padded frames.
	else if (frameNumber % 27 == 26) frameNumber += 1;  //
	
	repaint();
	}
      }
     
    public void startButtonMethod()	
     {if (!scrolling)					      
       {if (frozen) 
	 {frozen = false;
          start();
	  }
	}
      }
  
    public void stopButtonMethod()	
     {if (!scrolling)					      
       {if (!frozen) 
	 {frozen = true;
	  animatorThread = null;
	  }
	}	
      }
      
    public void fasterButtonMethod()	
     {if (!scrolling)
       {if (!frozen)
	 {frozen = true;
	  animatorThread = null;
	  }					      
	delayBetweenFrames = Math.max(25,delayBetweenFrames-25);
	frozen = false;
        start(); 
        }
      }
      
    public void slowerButtonMethod()	
     {if (!scrolling)
       {if (!frozen)
	 {frozen = true;
	  animatorThread = null;
	  }					      
	delayBetweenFrames = Math.min(600,delayBetweenFrames+25);
	frozen = false;
	start(); 
	}	
      }			    
      		
    public void scrollingCanvasMethod()		        	
     {if (!scrolling)
       {scrolling = true;
	if (!frozen)
	 {frozen = true;
	  animatorThread = null;
	  }		
	saveframeNumber = frameNumber;  
	savedelayBetweenFrames = delayBetweenFrames; 		      
	delayBetweenFrames = 10;
	frozen = false;
	start(); 
	}  
      }		
      
    public void stopscrollingCanvasMethod()	
     {if (scrolling)
       {scrolling = false;
	if (!frozen) 
	 {frozen = true;
	  animatorThread = null;
	  }
	frameNumber = saveframeNumber;  
	delayBetweenFrames = savedelayBetweenFrames; 	  
	}	
      }		
      
    public void panelUpDownMethod()
     {scrollingCanvasMethod();}		
     					      				      		      
     	
    /** GENERATE AND/OR START THE ANIMATOR SCROLLABLE CANVAS ANIMATION THREAD **/

    public void start()
       {if (frozen) { }                           // The animation is supposed to stay frozen.
        else                                      // Otherwise, generate and/or start the
         {if (animatorThread == null)             // animation thread. Apparently, if run
           {animatorThread = new Thread(this);}   // breaks after catching an exception,
            animatorThread.start();               // the animatorThread though not null
          }                                       // will still need to be restarted.
        }
	
   /** STOP THE ANIMATOR SCROLLABLE CANVAS ANIMATION THREAD **/

    public void stop()
      {animatorThread = null;             // The user either clicked to stop or left the page,
       preImageGraphics = null;           // so wipe out the current thread and preImage context.
       preImage = null;
       }						                   

    /** CREATE AND DISPLAY FRAMES OF THE ANIMATOR SCROLLABLE CANVAS ANIMATION THREAD **/

    public void run()
       {try                                                      // Start loading the images and
	 {callingApplet.tracker.waitForAll();}                   // wait until done before starting   
        catch (InterruptedException e) {}                        // the animation.
	
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY); // Set the priority low.
        long startTime = System.currentTimeMillis();             // Remember start time.
	
        while (Thread.currentThread() == animatorThread)         // The animation loop.
           {frameNumber++;                                       // Advance the frame number.
	    repaint();                         
            try                                                  // Sleep for the delay period.
             {startTime += delayBetweenFrames;
              Thread.sleep(Math.max(0,
                                    startTime-System.currentTimeMillis()));
              }
            catch (InterruptedException e) {break;}              // In case the try caused
            }                                                    // something strange, exit
        }                                                        // the while loop.

    /** CREATE THE CURRENT FRAME OF THE ANIMATOR SCROLLABLE CANVAS ANIMATION THREAD **/
  
    public void paint(Graphics g)                           // This technique of painting by calling
     {update(g);}                                           // update, prevents the default (and hidden)  
							    // behavior of update, and instead allows
    public void update(Graphics g)                          // the creation of an offscreen preImage 
     {Dimension d = size();                                 // before painting the canvas.
      if (!callingApplet.tracker.checkAll())                // If the images aren't all loaded, then
       {g.clearRect(0, 0, d.width, d.height);               // clear the canvas and notify the user
	g.setColor(Color.yellow);                           // to wait.
	g.setFont(waitfont);  
	waitstr1 = "Loading images, please wait...  Depending upon your computer ";
	waitstr2 = "and/or your connection speed, this may take several minutes...";
	g.drawString(waitstr1,25,50);
	g.drawString(waitstr2,25,50 + waitfontm.getHeight());                                                           
	}
      else	   	               
       {if ((preImageGraphics == null) ||                   // When all images are loaded, 
	    (d.width != preImageDim.width) ||               // compare the current applet
	    (d.height != preImageDim.height))               // viewing size to our preImage,
	     {preImageDim = d;                              // and if different, resize our 
	      preImage = createImage(d.width,d.height);     // next preImage.
	      preImageGraphics = preImage.getGraphics();
	      }                                     
	preImageGraphics.setColor(getBackground());         // Clear the previous preImage
	preImageGraphics.fillRect(0,0,d.width,d.height);    // by filling with the current
							    // background.    		
	if (!scrolling) displayframeNumber = frameNumber;
	else if (scrolling)
	 {displayframeNumber = saveframeNumber;
	  if ((callingApplet.scrollLeft == true) && (displayFrameH < 0)) displayFrameH++;
	  else if ((callingApplet.scrollRight == true) && (displayFrameH > -30)) displayFrameH--;
	  else if ((callingApplet.scrollUp == true)&& (displayFrameV < 0)) displayFrameV++;
	  else if ((callingApplet.scrollDown == true) && (displayFrameV > -600)) displayFrameV--;
	  else if ((callingApplet.panelUp == true) &&                  // Not the most concise logic
	           (-600 <= displayFrameV) && (displayFrameV < -300))  // for the panelUp and panelDown
	   {callingApplet.panelUp = false;                             // events. Had a lot of trouble
	    //displayFrameH = 0;                                       // obtaining the desired behavior
	    displayFrameV = -300;                                      // and so this code is somewhat
	    }                                                          // kludged. The statments that
	  else if ((callingApplet.panelUp == true) &&                  // reset panelUp/panelDown back
		   (-300 <= displayFrameV) && (displayFrameV <= 0))    // to false are necessary in order
	   {callingApplet.panelUp = false;                             // to prevent undesired interaction
	    //displayFrameH = 0;                                       // between the various cases, e.g.
	    displayFrameV = 0;                                         // when panelUp is true and displayFrameV
	    }                                                          // has just been changed to -300, the 
	  else if ((callingApplet.panelDown == true) &&                // next time through the run method loop
		   (-300 < displayFrameV) && (displayFrameV <= 0))     // displayFrameV is reassigned to 0.
	   {callingApplet.panelDown = false;                           // Similar comments hold when panelDown
	    //displayFrameH = 0;                                       // is true. To prevent this we can set 
	    displayFrameV = -300;                                      // PanelUp/PanelDown to false, so that
	    }                                                          // the screen is continually refreshed
	  else if ((callingApplet.panelDown == true) &&                // to the desired Panel as the run 
		   (-600 <= displayFrameV) && (displayFrameV <= -300)) // method iterates frameNumber, and  
	   {callingApplet.panelDown = false;                           // while displayframeNumber stays fixed
	    //displayFrameH = 0;                                       // at saveframeNumber.
	    displayFrameV = -600; 
	    g.setColor(getBackground()); 
	    g.fillRect(0,300,815,319);                                 // Clear screen bottom. 
	    }	     
	  else stopscrollingCanvasMethod();
	  } 
	  		 				 						 
	int imageWidth = image[displayframeNumber % 108].getWidth(this);                       
	int imageHeight = image[displayframeNumber % 108].getHeight(this);                         
	
	// Panel 1
	preImageGraphics.drawImage(image[displayframeNumber % 108], 0, 12, getBackground(), this);
	// Panel 2
	preImageGraphics.drawImage(image[108],imageWidth+8,12,getBackground(), this);
	// Panel 3
	preImageGraphics.drawImage(image[109],0,imageHeight+24,getBackground(), this);
	preImageGraphics.drawImage(image[110],90,imageHeight+100,getBackground(), this);
	// Panel 4
	preImageGraphics.drawImage(
	 image[111+(displayframeNumber % 54)],imageWidth+8,imageHeight+24,getBackground(), this);
	// Panel 5
	preImageGraphics.drawImage(image[165],0,2*imageHeight+36,getBackground(), this);
	preImageGraphics.drawImage(image[166],30,2*imageHeight+111,getBackground(), this);
	preImageGraphics.drawImage(image[167],24,2*imageHeight+185,getBackground(), this);
	// Panel 6
	preImageGraphics.drawImage(image[168],imageWidth+8,2*imageHeight+36,getBackground(), this);
	preImageGraphics.drawImage(image[169],imageWidth+30,2*imageHeight+99,getBackground(), this);
	
	preImageGraphics.setFont(panelfont);
	preImageGraphics.setColor(Color.white);
	preImageGraphics.drawString("Panel 1",1,10);
	preImageGraphics.drawString("Panel 2",imageWidth+9,10);
	preImageGraphics.drawString("Panel 3",1,imageHeight+22);
	preImageGraphics.drawString("Panel 4",imageWidth+9,imageHeight+22);
	preImageGraphics.drawString("Panel 5",1,2*imageHeight+34);
	preImageGraphics.drawString("Panel 6",imageWidth+9,2*imageHeight+34);
	
	g.drawImage(preImage,displayFrameH,displayFrameV,this);
	} 
     } 
   }

/*********************************************
 ** END 4TH CLASS: animatorScrollableCanvas **
 *********************************************/