
// UtilTimerTest.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/* Test přesnosti časovače z balíčku java.util použitý se třídou TimerTask 
   tak, že se pokoušíme překreslovat panel typu JPanel danou rychlostí FPS
   (snímků za sekund).
   současné měření skutečně dosahované hodnoty FPS.

   Pojmem snímek rozumíme jedno provedení metody run() ve
   třídě MyTimerTask.

   Použití:
     java UtilTimerTest <požadovaná hodntoa FPS>

   Naměřené průměrné hodnoty FPS při těchto požadovaných:
              20      50     80/83     100	
            ------------------------------
   Win 98:   20      47      81       99 
   Win 2000:  20      50      83       99
   Win XP:    20      50      83       95

   Při spuštění programu je hodnota FPS nízká, ale asi tak za 10 až 20
   sekund postupně vzrůstá. "Garbage collecotor" ji ještě o nějakých pár
   sekund sníží.

   Voláme-li v metodě reportStats() metodu System.currentTimeMillis()
   namísto J3DTimer.getValue(), vypadají výsledky v podstatě stejně. To je
   dáno tím, že MAX_STATS_INTERVAL je větší než rozlišení časovače.  
*/

import java.awt.*;
import javax.swing.*;
import java.util.Timer;
import java.util.TimerTask;
import java.text.DecimalFormat;

import com.sun.j3d.utils.timer.J3DTimer;     // pro měření přesnosti


public class UtilTimerTest 
{
  private static int DEFAULT_FPS = 80;

  public static void main(String args[]) 
  {
    int fps = DEFAULT_FPS;
    if (args.length != 0)
      fps = Integer.parseInt(args[0]);

    int period = (int) 1000.0/fps;     // v ms
    System.out.println("fps: " + fps + "; period: " + period + " ms");

    PaintPanel pp = new PaintPanel(period);

    // vytvoř okno typu JFrame uchovávající panel typu JPAnel s testovací časovačem
    JFrame app = new JFrame("Utilities Timer Test");
    app.getContentPane().add(pp, BorderLayout.CENTER);
    app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    MyTimerTask task = new MyTimerTask(pp);
    Timer t = new Timer();
    t.scheduleAtFixedRate(task, 0, period);  
           // prováděj v daných intervalech (pevně danou rychlostí)

     app.pack();
     app.setResizable(false);  
     app.setVisible(true);
  } // konec metody: main()

} // konec třídy: UtilTimerTest


//-------------------------------------------------

class MyTimerTask extends TimerTask 
{
  private PaintPanel pp;


  public MyTimerTask(PaintPanel pp) 
  { this.pp = pp;  } 


  public void run()
  // aktualizuje, vykreslí
  { sillyTask();
    pp.repaint();
  } // konec metody: run()


  private void sillyTask()
  { long tot = 0;
    for (long i=0; i < 200000L; i++)
      tot += i;
    // System.out.println("tot: " + tot);
  } // konec metody: sillyTask()

} // konec třídy: myTimerTask


//------------------------------------------------------

class PaintPanel extends JPanel 
{
  private static final int PWIDTH = 200;
  private static final int PHEIGHT = 75;

  private static long MAX_STATS_INTERVAL = 1000L;
    // zhruba každou sekundu zaznamená statistiky

  private static int NUM_FPS = 10;
     // počet ukládaných hodnot FPS pro výpočet průměru


  // pro statistické údaje
  private long statsInterval = 0L;    // v ms
  private long prevStatsTime;   
  private long totalElapsedTime = 0L;

  private long frameCount = 0;
  private double fpsStore[];
  private long statsCount = 0;
  private double averageFPS = 0.0;


  private DecimalFormat df = new DecimalFormat("0.##");  // 2 dm
  private DecimalFormat timedf = new DecimalFormat("0.####");  // 4 dm

  private int period;       // interval v ms mezi vykresleními
              // tzn. čas požadovaný pro jednu iteraci


  public PaintPanel(int p)
  {
    period = p;

    setBackground(Color.white);
    setPreferredSize( new Dimension(PWIDTH, PHEIGHT) );

    fpsStore = new double[NUM_FPS];
    for (int i=0; i < NUM_FPS; i++)
      fpsStore[i] = 0.0;

    prevStatsTime = J3DTimer.getValue();
  } // konec konstruktoru: PaintPanel()


   public void paintComponent(Graphics g)
   {
     super.paintComponent(g);

     // vyčisti pozadí
     g.setColor(Color.white);
     g.fillRect (0, 0, PWIDTH, PHEIGHT);

    // ohlaš průměrnou hodnotu FPS
	 g.setColor(Color.black);
	 g.drawString("FPS průměrně: " + df.format(averageFPS), 10, 25);

    reportStats();
   } // konec metody: paintComponent()


  private void reportStats()
  /* Statistické údaje:
       - součet dob trvání všech dosud provedených iterací,
         skutečná doba trvání tohoto periodu,
         diff (odchylka) těchto dvou čísel

       - celkový počet volání metody paintComponent();
   
       - Počet snímků za sekundu v tomto mezidobí a průměrná
         hodnota FPS za posledních NUM_FPS periodů.

     Tyto informace ukládáme každých MAX_STATS_INTERVAL/1000 sekund.
  */
  { 
    frameCount++;
    statsInterval += period;

    // zaznamenej statistiky každých MAX_STATS_INTERVAL milisekund
    if (statsInterval >= MAX_STATS_INTERVAL) {     
      long timeNow = J3DTimer.getValue();

      // čas od posledního výpočtu statistických údajů
      long realElapsedTime = timeNow - prevStatsTime;   
      totalElapsedTime += realElapsedTime;

      long sInterval = (long)statsInterval*1000000L;  // ms --> ns
      double timingError = 
          ((double)(realElapsedTime - sInterval)) / sInterval * 100.0;

      double actualFPS = 0;     // výpočet nejnovější hodnoty FPS
      if (totalElapsedTime > 0)
        actualFPS = (((double)frameCount / totalElapsedTime) * 1000000000L);

      // ulož nejnovější FPS
      fpsStore[ (int)statsCount%NUM_FPS ] = actualFPS;
      statsCount = statsCount+1;

      double totalFPS = 0.0;     // suma předchozích FPS
      for (int i=0; i < NUM_FPS; i++)
        totalFPS += fpsStore[i];

      if (statsCount < NUM_FPS)  // výpočet průměrného FPS
        averageFPS = totalFPS/statsCount;
      else
        averageFPS = totalFPS/NUM_FPS;

      System.out.println(timedf.format( (double) statsInterval/1000) + " " + 
                        timedf.format((double) realElapsedTime/1000000000L) + "s " + 
						df.format(timingError) + "% " + 
                        frameCount + "v " +
                        df.format(actualFPS) + " " +
                        df.format(averageFPS) + " afps" );

      prevStatsTime = timeNow;
      statsInterval = 0L;   // vynuluj
    }
  }  // konec metody: reportStats()


} // konec třídy: PaintPanel

