/*
 * Compton.java
 * Simple Animated Simulation of Compton Scattering
 * Code: Jan Humble
 *
 *
 */

import java.applet.*;
import java.awt.*;


public class Compton extends Applet implements Runnable{

    public static double PLANCK = 4.14;  // 10 ^ -18 eVs
    public static double COMPTONWAVELENGTH = 0.02426;  // Å 
    public static double LIGHTSPEED = 2.998;  // 10 ^ 8 m/s


    int dY, dX;           // Applet size parameters
    
    Thread animThread;    // Thread animation process
    Image offscreen;      // Background graphics buffer
    Font font1 = new Font("Helvetica", Font.BOLD, 14);
    Font font2 = new Font("Helvetica", Font.PLAIN, 10);
    Font font3 = new Font("Helvetica", Font.BOLD, 12);
    
    // private Button emit_button;

    int time = 0;
    double deltaT = 50;

    double theta, phi; 

    
    // Compton particles

    Photon photon;
    Electron electron;

    

    public void init(){
	// Get the drawing area of the applet 
	dY=size().height; dX=size().width;

	// Button
	/*emit_button = new Button("Emit");
	  emit_button.setBackground(Color.white);
	  
	  this.add(emit_button);
	  emit_button.locate(200, 200);
	*/
      
	double photonenergy = 
	    (Double.valueOf(getParameter("photonenergy"))).doubleValue();
	photon = new Photon(photonenergy, 30, dY/2);
	electron = new Electron(0.0, dX/2, dY/2);

	theta = phi = 0.0;



    }
    
  
    
    public void start() { 
	if (animThread == null){ 
	    animThread = new Thread(this); 
	    animThread.start(); 
	} 
    } 

   
    public void stop() { 
	animThread = null; 
    } 
    

    public void run() { 
	
	
	while (animThread != null) {
	    
	    while (time < 100) { 
		
		// Use sleep to pause between movements 
		try { 
		    
		    Thread.sleep((int)(50)); 
		    
		} catch (InterruptedException e) {}
	    
		
		// Collision detection
		
		if (photon.currentX == electron.currentX &&
		    photon.currentY == electron.currentY)
		    {
			collision(photon, electron);
		    }
		
		photon.move(deltaT);
		electron.move(deltaT);
		
		repaint(); 
		time++;
	    } 
	
	    // Reset the process
  
	    time = 0;
	    electron.reset();
	    photon.reset();
	    theta = phi = 0.0;
	} 
    }
    
   


    public void update (Graphics g){
	
	// Update using double buffering

	  Dimension d = this.size();

	    if (offscreen == null)
		offscreen = this.createImage(d.width, d.height);
      		
	    // Draw to background buffer
	    g = offscreen.getGraphics();
	    paint(g);
	    
	    // draw from background buffer to screen buffer
	    g = this.getGraphics();
	    g.drawImage(offscreen, 0, 0, this);
	    
      
    }
      
   
    /* collision:                                         */
    /* Calculates new parameters after Compton Collision. */
    /* Can be overloaded to simulate collisions with      */
    /* different kinds of particles.                      */                 

    
    public void collision(Photon photon, Electron electron)
    {

	double prefreq = photon.frequency;
	double prewave = photon.wavelength;
	double postfreq, postwave;

	// Calculate a random theta angle
	
	
	theta = Math.random() * Math.PI;  // 0 < theta < 180
	
	//  theta = Math.PI;

	postwave = Math.abs
	    (prewave + COMPTONWAVELENGTH * (1 - Math.cos(theta)));
	postfreq = LIGHTSPEED / postwave;
	
	// Calculate the corresponding Compton scat. angle
	
	phi = Math.atan 
	    ( postfreq * Math.sin(theta) / 
	      (prefreq  - postfreq * Math.cos(theta)));
	
    
			
	// set new direction and velocities for the particles
	
	electron.setPath(phi, photon.vel);
	photon.setPath(-theta, photon.vel);
	
	// set new particle values
	
	electron.energy = Compton.PLANCK * (prefreq - postfreq);
	
	photon.wavelength = postwave;
	photon.frequency = postfreq;
	photon.energy = postfreq * Compton.PLANCK;
	photon.tailLength = (int) (photon.tailLength * postwave / prewave);
	
    }

    public String num2string (double num)
    {
	String s =Double.toString(num);
	
	return s.substring(0, Math.min(6, s.length()));
    }



    public void frameInfo(Graphics g)
    {
	// Draw label
	
	g.setColor(Color.black);
	g.setFont(font1);
	g.drawString("Compton Effect", 8, 20);

	g.setFont(font2);
	g.drawString("time = " + Integer.toString(time), 20, 40);

	// draw axis with origin on the electron

	g.setColor(Color.lightGray);
	g.drawLine(0, (int) electron.startY, dX, (int) electron.startY);
	g.drawLine((int) electron.startX, 0, (int) electron.startX, dY);

	// Draw photon info

	g.setColor(Color.blue);
	g.setFont(font3);
	g.drawString("Photon", 20, dY - 60);
	   
	g.setFont(font2);
	g.drawString("Energy = " + num2string(photon.energy) + " keV", 
		     20, dY-48);
	g.drawString("freq = " + num2string(photon.frequency) + " EHz", 
		     20, dY-36);
	g.drawString("wavelength = " + 
		     num2string(photon.wavelength) + " Å", 20, dY-24);
	g.drawString("theta = " + num2string(theta * 180/Math.PI), 
		     20, dY-12);


	// Draw electron info

	g.setColor(Color.red);
	g.setFont(font3);
	g.drawString("Electron", dX - 140, dY - 60);
	
	g.setFont(font2);
	g.drawString("Kin. energy = " + 
		     num2string(electron.energy) + " keV", 
		     dX - 140, dY-42);
	g.drawString("phi = " + num2string(electron.angle * 180/Math.PI), 
		     dX - 140, dY-30);


       	
    }
    

    public void paint(Graphics g) {

	// Clear screen
	g.setColor(Color.white);
	g.fillRect(0, 0, dX, dY);


	// Draw particles
	photon.draw(g);
	electron.draw(g);
	frameInfo(g);
	
    }
}





/* ------------------------------------------------ */
/* Base Particle class                              */
/*                                                  */
/* ------------------------------------------------ */

abstract class Particle {
    
    Color color;
    
    double startX, startY;
    double currentX, currentY;
    double startVel, vel, velX, velY;

    double startAngle, angle;

    double energy, startenergy;

    Particle(double energy, int startX, int startY, 
	     double startAngle, double startVel, 
	     Color color)
    {
	this.startX = startX;
	this.startY = startY;
	this.currentX = startX;	
	this.currentY = startY;
	
	this.startAngle = startAngle;
	this.startVel = startVel;
	
	this.color = color;	
	
	this.energy = this.startenergy = energy;

	setPath(startAngle, startVel);
	
    }   

    public void reset() {
	
	currentX = startX;
	currentY = startY;
	
	energy = startenergy;
	setPath(startAngle, startVel);
	
	
    }
    
    
    public void setPath(double newAngle, double vel)
    {
	this.angle = newAngle;
	this.vel = vel;
	velX = vel*Math.cos(angle);
	velY = vel*Math.sin(angle);
    }
    
    
    public void move(double deltaT) {
	
	currentX = currentX + velX*deltaT;
	currentY = currentY - velY*deltaT;
    }


    abstract void draw(Graphics g);
    
}




/* ------------------------------------------------ */
/* Electron class: Inherits Particle                */
/*                                                  */
/* ------------------------------------------------ */

class Electron extends Particle{
   
    final double restenergy = 511; // keV

    int drawDiameter;   // define diameter to draw on screen

   
    // Constructor

    Electron(double energy, int startX, int startY) {
	
	super(energy, startX, startY, 0, 0, Color.red);
	
	this.drawDiameter = 15;
      
    }
    
    public void draw(Graphics g) {
	
	g.setColor(color);
	g.fillOval((int) currentX - drawDiameter/2, 
		   (int) currentY - drawDiameter/2, 
		   drawDiameter, drawDiameter);
	
    }
}




/* ------------------------------------------------ */
/* Photon class: Inherits Particle                  */
/*                                                  */
/* ------------------------------------------------ */


class Photon extends Particle {

    double frequency, wavelength;

     final int TAIL = 60;
    int drawAmplitud, tailLength;  // photon graphical parameters
  

    // Constructor

    Photon (double energy, int startX, int startY) {
	
	super(energy, startX, startY, 0.0, 0.1, Color.blue);
	
       
	this.frequency = energy / Compton.PLANCK;
	this.wavelength = Compton.LIGHTSPEED / frequency;
	
	
	this.drawAmplitud = 5;
	this.tailLength = TAIL;
	

    }
    

    public void reset() {
	super.reset();
	
	this.frequency = energy / Compton.PLANCK;
	this.wavelength = Compton.LIGHTSPEED / frequency;
	
	this.tailLength = TAIL;
    }
    
    
    public void draw(Graphics g)
    {
		
	g.setColor(color);
	
	double x1, x2, y1, y2, x1r, x2r, y1r, y2r;
	
	for (int x = - tailLength; x < 0; x++)
	    {	
		// Calculate animated sinus tail
 
       
		y1 = Math.sin((currentX + x)*8*Math.PI/tailLength) * drawAmplitud;
		y2 = Math.sin((currentX + x+1)*8*Math.PI/tailLength) * drawAmplitud;

		// Rotate tail according to photon path angle

		x1r = currentX + 
		    Math.cos(angle)*x - Math.sin(-angle)*y1; 
		y1r = Math.sin(-angle)*x + Math.cos(angle)*y1 + currentY;
		
		x2r = currentX +
		    Math.cos(angle)*(x+1) - Math.sin(-angle)*y2; 
		y2r = Math.sin(-angle)*(x+1) + Math.cos(angle)*y2 + currentY;
		
		g.drawLine((int) x1r, (int) y1r, (int) x2r, (int) y2r);
	
		// g.fillOval(currentX - 5, currentY - 5, 10, 10);
	
	    }
	
    }
}

    
