| We first develop a program with only the physics aspect 
              of the experiment simulation. The following demo shows the dropped 
              mass and the time taken for the drop:    We will use object design to make the program as modular 
              as possible both to clarify the coding and to emulate the actual 
              modularity of the experiment. You can look at an experiment as composed 
              of the following components:  
              the physics phenomena under study, the detector apparatus that measures some aspect of the phenomena, the controls for running the experiment, a data collection system and the analysis tools.  The following classes emulate the experiment with 
              classes for each of these components except for a detector and an 
              analysis tool, which we will add in a later versions of the simulation.  
             
              DropTestApplet 
                - the top level applet/app class that controls the program and 
                provides the display (on the applet panel) for the user interface. 
                It holds instances of the following classes:
 
 
                  DropModel 
                    - generates the physics data for the simulation. 
 
DropPanel 
                    - a subclass of PlotPanel 
                    (discussed in Chapter 
                    6: Tech) that displays the experiment simulation, i.e. 
                    the falling mass.
 
HistPanel 
                    - displays a histogram of drop times (discussed in Chapter 
                    6: Tech)
 
java.util.Timer 
                    - a new instance created to run the animation of each drop. 
                    It uses an instance of inner class PaintHistTask 
                    (a subclass of java.util.TimerTask) 
                    whose run() 
                    method is invoked for each frame of the animation. The run() 
                    invokes the methods from the above classes to generate the 
                    physics data, display the next increment in the ball drop, 
                    and record the data when the ball reaches the bottom. Below we show the code for the code. Note that the 
              drop time is just the sum of the animation frame times. The next 
              version will improve on this.  
              
                 
                  | DropTestApplet |   
                  |  import 
                      javax.swing.*;import java.awt.*;
 import java.awt.event.*;
 
 /**
 * This program will run as an applet inside
 * an application frame.<
 *
 * The applet uses DropPanel to simulate the 
                      dropping of a
 * mass in a constant gravitational field. A 
                      HistPanel displays
 * a histogram of the measurements of the acceleration. 
                      PlotFormat
 * is used by HistPanel to format the scale values.
 *
 * The java.util.Timer and java.util.TimerTask 
                      are used
 * to update the animation of the drop.
 *
 * Includes "Drop" button to initiate the dropping.
 *
 * The number of values taken from
 * entry in a JTextField. "Clear"  button 
                      clears the histogram.
 * In standalone mode, the Exit button closes 
                      the program.
 *
 **/
 public class DropTestApplet extends JApplet
 implements ActionListener
 {
 // The DropPanel displays the animation of the
 // falling mass.
 DropPanel fDropPanel;
 
 // The DropModel generates the physics data.
 DropModel fDropModel;
 
 // Use the HistPanel JPanel subclass here
 HistPanel fHistPanel;
 
 Histogram fHistogram;
 int fNumDataPoints = 100;
 
 boolean fMakingHist = false;
 boolean fUpdateDisplay = false;
 
 // Use the java.util Timer and TimerTask combo
 // for timing events.
 java.util.Timer fTimer;
 
 // A text field for getting number of drops 
                      per
 // run and the time factor
 JTextField fMaxNumDropsField;
 JTextField fTFactorField;
 
 // Flag for whether the applet is in a browser
 // or running via the main () below.
 boolean fInBrowser = true;
 
 //Buttons
 JButton fGoButton;   // start drop
 JButton fClearButton;// resets histogram
 JButton fExitButton;
 
 // The bottom coordinate of the drop tower
 double fYBottom = 0;
 
 // Starting coordinates of drop
 double fXBallStart =  25.0; // cm
 double fYBallStart = 200.0; // cm
 
 // Initial velocity.
 double fVyBallStart = 0.0;
 double fVxBallStart = 0.0;
 
 // Coordinate of ball.
 double fXBall;
 double fYBall;
 
 // Time in millisecs when the last drop step 
                      made
 double fTDropTotal;
 
 // Speed up or slow down factor for animation:
 double fTFactor = 1.0;
 double fTFrame = 0.020; // in secs
 
 // Allow for multiple drops per "run"
 int fMaxNumDrops = 1;
 int fNumDrops = 0;
 
 /**
 * Create a User Interface with a 
                      textarea with sroll bars
 * and a Go button to initiate processing 
                      and a Clear button
 * to clear the textarea.
 **/
 public void init () {
 
 JPanel panel = new JPanel (new BorderLayout 
                      ());
 
 // Use a textfield to get the number 
                      of drops per run.
 fMaxNumDropsField =
 new JTextField (Integer.toString 
                      (fMaxNumDrops), 10);
 
 // Use a textfield to get the number 
                      of drops per run.
 fTFactorField =
 new JTextField (Double.toString 
                      (fTFactor), 10);
 
 // If return hit after entering 
                      text, the
 // actionPerformed will be invoked.
 fMaxNumDropsField.addActionListener 
                      (this);
 fTFactorField.addActionListener 
                      (this);
 
 fGoButton = new JButton ("Drop");
 fGoButton.addActionListener (this);
 
 // Here the clear button will reset 
                      the histogram
 fClearButton = new JButton ("Reset");
 fClearButton.addActionListener (this);
 
 fExitButton = new JButton ("Exit");
 fExitButton.addActionListener (this);
 
 JPanel controlPanel = new JPanel 
                      (new GridLayout (1,2));
 JPanel panel1 = new JPanel (new 
                      GridLayout (1,2));
 JPanel panel2 = new JPanel (new 
                      GridLayout (1,3));
 
 panel1.add (fMaxNumDropsField);
 panel1.add (fTFactorField);
 
 panel2.add (fGoButton);
 panel2.add (fClearButton);
 panel2.add (fExitButton);
 
 controlPanel.add (panel1);
 controlPanel.add (panel2);
 
 // Create an instance of the DropPanel
 fDropPanel = new DropPanel (fYBallStart, 
                      0.0);
 
 // Create a histogram to plot the 
                      variance in the
 // drop times.
 fHistogram = new Histogram ("Delta 
                      Time","Data", 20,0.0,1.0);
 fHistPanel = new HistPanel (fHistogram);
 
 JPanel holder_panel = new JPanel 
                      (new GridLayout (1,2));
 holder_panel.add (fDropPanel);
 holder_panel.add (fHistPanel);
 
 panel.add (holder_panel,"Center");
 panel.add (controlPanel,"South");
 
 // Add text area with scrolling 
                      to the content pane.
 add (panel);
 
 // Create the drop physics model
 fDropModel = new DropModel ();
 fDropModel.setParams (fYBallStart, 
                      fVyBallStart);
 
 } // init
 
 /** Stop the timer if the browser page unloaded. 
                      */
 public void stop () {
 runDone ();
 } // stop
 
 /** Respond to the buttons.  */
 public void actionPerformed (ActionEvent e) 
                      {
 Object source = e.getSource ();
 if (source == fGoButton) {
 if (fGoButton.getText 
                      ().equals ("Drop")) {
 try {
 fMaxNumDrops 
                      =
 Integer.parseInt 
                      (fMaxNumDropsField.getText ());
 fTFactor 
                      =
 Double.parseDouble 
                      (fTFactorField.getText ());
 }
 catch (NumberFormatException 
                      ex) {
 // 
                      Could open an error dialog here but just
 // 
                      display a message on the browser status line.
 showStatus 
                      ("Bad input value");
 return;
 }
 dropReset 
                      ();
 fGoButton.setText 
                      ("Stop");
 fClearButton.setEnabled 
                      (false);
 
 } else {
 dropDone 
                      ();
 }
 }
 else if (source == fClearButton) 
                      {
 runReset();
 } else if (!fInBrowser)
 System.exit 
                      (0);
 
 } // actionPerformed
 
 /**
 *  Before each set of 
                      drops, need to create a new timer,
 *  and set up its schedule. 
                      The PaintHistTask innner class
 *  object will do the 
                      setup for each frame of a drop animation.
 **/
 void dropReset () {
 
 // Before starting the drop, create 
                      the timer task
 // that will cause the histogram 
                      display to update
 // during the filling.
 // Create a timer. TimerTask created 
                      in MakeHist ()
 fTimer = new java.util.Timer ();
 
 fDropPanel.reset (fXBallStart, fYBallStart);
 fDropModel.setParams (fYBallStart, 
                      fVyBallStart);
 
 // Start the timer after 20ms and 
                      then repeat calls
 // to run in PaintHistTask object 
                      by the rate set by
 // the fTFrame value.
 fTimer.schedule (new PaintHistTask 
                      (), 20, (int)(fTFrame*1000));
 
 // Reset time sum
 fTDropTotal = 0.0;
 fNumDrops   = 0;
 fYBall      = 
                      fYBallStart;
 fXBall      = 
                      fXBallStart;
 
 } // dropReset
 
 /**
 * Use the inner class technique 
                      to define the
 * TimerTask subclass for stepping 
                      through the
 * drop calculation and the frame 
                      refresh.
 * Use the real time in the drop 
                      calculation instead
 * of the given frame rate times 
                      in case there were
 * delays from thread interruptions, 
                      the processing
 * in the parts of the program take 
                      extra time, etc.
 **/
 class PaintHistTask extends java.util.TimerTask 
                      {
 public void run () {
 // Drop the ball
 fYBall = fDropModel.step 
                      (fTFactor * fTFrame);
 
 // Update the position 
                      of the ball in the
 // animation and redraw 
                      the frame.
 fDropPanel.updatePosition 
                      (fXBall, fYBall);
 
 // Save the time.
 fTDropTotal += fTFactor 
                      * fTFrame;
 
 // Check if ball has 
                      crossed the finish line.
 if (fYBall <= fYBottom) 
                      dropDone ();
 
 } // run
 
 } // PaintHistTask
 
 /**
 *  Invoked when all the 
                      drops are done. Reset
 *  all the parameters 
                      to set up for another drop.
 **/
 public void dropDone () {
 
 // Add time for this drop. Remove 
                      the time factor.
 fHistogram.add (fTDropTotal);
 
 ++fNumDrops;
 
 // Check if all drops completed.
 if (fNumDrops == fMaxNumDrops){
 // If so then finish 
                      up the data recording
 // for this run and 
                      return.
 runDone ();
 return;
 }
 
 // Reset time sum
 fTDropTotal = 0.0;
 fYBall = fYBallStart;
 fXBall = fXBallStart;
 
 fDropPanel.reset (fXBallStart, fYBallStart);
 fDropModel.setParams (fYBallStart, 
                      fVyBallStart);
 
 } // dropDone
 
 /** Invoked when Clear button selected. **/
 public void runReset () {
 
 // Clear the histogram
 fHistogram.clear ();
 
 // Reset time sum
 fTDropTotal = 0.0;
 fYBall = fYBallStart;
 fXBall = fXBallStart;
 
 fDropPanel.reset (fXBallStart, fYBallStart);
 fDropModel.setParams (fYBallStart, 
                      fVyBallStart);
 
 repaint();
 
 } // runReset
 
 /**
 *  Invoked when all the 
                      drops are done. Kill the timer.
 *  A new timer must be 
                      created for each run.
 *  Display the histogram 
                      with the additional data.
 **/
 public void runDone (){
 
 // Stop the animation.
 fTimer.cancel ();
 
 // Update histogram
 fHistPanel.repaint ();
 
 // Reset the buttons.
 fGoButton.setText ("Drop");
 fClearButton.setEnabled (true);
 
 } // runDone
 
 /**  Offer the option of running the 
                      applet in an app frame. */
 public static void main (String[] args) {
 //
 int frame_width=450;
 int frame_height=300;
 
 //
 DropTestApplet applet = new DropTestApplet 
                      ();
 applet.fInBrowser = false;
 applet.init ();
 
 // Following anonymous class used 
                      to close window & exit program
 JFrame f = new JFrame ("Drop Test");
 f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
 
 // Add applet to the frame
 f.getContentPane ().add ( applet);
 f.setSize (new Dimension (frame_width,frame_height));
 f.setVisible (true);
 } // main
 
 } // DropTestApplet
 
 |   
                  | DropModel |   
                  | /** *  This class models the dropping of 
                    a mass in
 *  a constant gravitational field.
 **/
 public class DropModel
 {
 double fYStart  = 100.0;
 double fVyStart = 0.0;
 double t, y, vy;
 
 // Constants
 double g  = -980.0;// centimeter per 
                    sec^2
 
 /** Initialize the drop parameters. **/
 public void init() {
 y = fYStart;
 vy= fVyStart;
 } // init
 
 /**
 *  Set the initial vertical 
                    position and velocity
 *  and the number of integration 
                    steps for a given
 *  delta time value.
 **/
 public void setParams(double yStart, double vyStart){
 
 fYStart  = yStart;
 fVyStart = vyStart;
 y = fYStart;
 vy= vyStart;
 
 } // setParams
 
 /**
 *  Calculate the change 
                    in position and velocity
 *  for each increment in 
                    time. For this case of a
 *  simple constant velocity 
                    and no drag, the dt step
 *  is not divided into finer 
                    step sizes for the
 *  integration.
 **/
 public double step(double dt){
 y = y + vy * dt;
 vy = vy + g * dt;
 return y;
 } // step
 
 } // DropModel
 |   
                  | DropPanel |   
                  | import java.awt.*;
import javax.swing.*;
/**
  *  This subclass of PlotPanel provides a
  *  display for a dropped object.
 **/
public class DropPanel extends PlotPanel
{
  Color fBgColor   = Color.WHITE;
  Color fBallColor = Color.RED;
  double fXBall, fYBall;
  int fXBallOld = 0,fYBallOld = -1;
  double fXStart,fYStart;
  // Limits for the vertical scale
  double fYDataMax; // Height in cm
  double fYDataMin;
  // Limits for the horizontal scale
  double fXDataMax = 50.0;
  double fXDataMin =  0.0;
  // Numbers to use for plotting axes scale values
  double [] fXScaleValue = new double[2];;
  double [] fYScaleValue;
  int fNumYScaleValues = 5;
  int fNumXScaleValues = 2;
  /**
    *  Create the panel and set up the scaling for the
    *  box in which to plot the dropped object.
    *
    *  @param fYDataMax - vertical scale max limit
    *  @param fYDataMin - vertical scale min limit
   **/
  public DropPanel (double y_data_max, double y_data_min) {
    fYDataMax = y_data_max;
    fYDataMin = y_data_min;
    // Put starting point at 90% of max
    fYStart = y_data_max * 0.9;
    fYBall = fYStart;
    fXBall = fXDataMax/2.0;// Put ball in middle
    // The horizontal axes scaling will be fixed
    // Use arbitrary default of 0 to 50 for scale.
    fXScaleValue[0] = 0.0;
    fXScaleValue[fNumXScaleValues-1] = fXDataMax;
    // The vertical  (y) values can be changed
    // so do the scaling in this method.
    getScaling ();
    setBackground (fBgColor);
  } // ctor
  /**
    * Get the values for the scaling numbers on
    * the vertical axis in case the limits have
    * changed.
   **/
  void getScaling () {
    getPositions ();
    fYScaleValue = new double[fNumYScaleValues];
    fYScaleValue[0] = fYDataMin;
    fYScaleValue[fNumYScaleValues-1] = fYDataMax;
    // Then calculate the difference between the values
    double range = fYScaleValue[fNumYScaleValues-1]
                        - fYScaleValue[0];
    double del = range/(fNumYScaleValues-1);
    // Now set the intermediate scale values.
    for (int i=1; i < (fNumYScaleValues-1); i++) {
        fYScaleValue[i] = i*del + fYScaleValue[0];
    }
  } // getScaling
  /**
    *  For each time increment, update the position
    *  of the falling object.
   **/
  public void updatePosition (double x, double y) {
    fXBall = x;
    fYBall = y;
    if (y >= fYDataMin) {
       repaint ();
    }
  } // updatePosition
  /**
    *  For a new drop, reset the starting position
    *  and redraw the plot area.
   **/
  public void reset (double x_start, double y_start){
    fXBall = x_start;
    fYBall = y_start;
    fYBallOld = -1;
    fYDataMax = y_start * 1.1;
    getScaling ();
    repaint ();
  } // reset
  /** Draw the ball falling. */
  void paintContents (Graphics g) {
    // Draw the numbers along the axes
    drawAxesNumbers (g, fXScaleValue, fYScaleValue);
    // Need conversion factor from data scale to pixel scale
    double y_convert = fFrameHeight/ (fYDataMax - fYDataMin);
    double x_convert = fFrameWidth/fXDataMax;
    // For dropped object use size relative to the frame.
    int symDim =  (int)(fFrameWidth *.04);
    // Clear old ball position
    if (fYBallOld > 0 ){
      g.setColor (fBgColor);
      g.fillOval (fXBallOld-symDim,fYBallOld-symDim, 2*symDim, 2*symDim);
    }
    // Set the foreground color back to the ball color
    g.setColor (fBallColor);
    int x =  (int)(fXBall*x_convert) + fFrameX;
    int y = fFrameHeight - (int)(fYBall*y_convert) + fFrameY;
    g.fillOval (x-symDim,y-symDim, 2*symDim, 2*symDim);
    // Save current ball coords for erasure in
    // next frame.
    fXBallOld = x; fYBallOld = y;
  } // paintContents
  /**
    * Return the title at the top of the plot.
    * Overrides method in PlotPanel.
   **/
  String getTitle ()
  {  return "Drop Tower Demo";}
  /**
    * Return the label on the horizontal axis.
    * Overrides method in PlotPanel.
   **/
  String getXLabel ()
  {  return "";}
} // DropPanel |    Most recent update: Nov. 5, 2005 |