/*
@Copyright (c) 1997 by the author: Phillip Dukes, Brigham Young University,
Department of Physics and Astronomy.

All rights reserved.

Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the
above copyright notice and the following two paragraphs appear in all
copies of this software.

IN NO EVENT SHALL THE AUTHOR OR BRIGHAM YOUNG UNIVERSITY BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
THE ABOVE PARTIES HAVE BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE AUTHOR HAS NO 
OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
OR MODIFICATIONS.
*/

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

public class LightRefract extends Applet implements Runnable{
	Thread animate;
	DrawingRegionLR region;
	myCustomControlsLR controls;
	
	public void init(){
		setLayout(new GridLayout(1,2));
		region = new DrawingRegionLR();
		controls = new myCustomControlsLR(region,this);
		add(region);
		add(controls);
	}
	
	public void start(){
		if(animate == null){
			animate = new Thread(this);
			animate.start();
		}
	}
	
	public void stop(){
		if(animate != null){
			animate.stop();
			animate = null;
		}
	}
	
	public void run(){
		while(true){
			region.k++;
			if(region.k == 5){region.k = 0;}
			region.repaint();
			try{Thread.sleep(200);}
			catch(InterruptedException e){}
		}
	}
}

class myCustomControlsLR extends Panel{
	DrawingRegionLR region;
	LightRefract parent;
	Choice c = new Choice();
	
	TextField waveTF, indexTF, angleTF;
	TextField refwavLengTF, refAngleTF;
	
	public Insets insets(){
		return new Insets(0,0,100,0);
	}	
	
	void buildConst(GridBagConstraints gbc, int gx, int gy, int gw, int gh, int wx, int wy){
		gbc.gridx = gx;
		gbc.gridy = gy;
		gbc.gridwidth = gw;
		gbc.gridheight = gh;
		gbc.weightx = wx;
		gbc.weighty = wy;
	}	
		
	
	public myCustomControlsLR(DrawingRegionLR region, LightRefract parent){
		this.parent = parent;
		this.region = region;
		GridBagLayout gridbag = new GridBagLayout();
		GridBagConstraints constraints = new GridBagConstraints();
		setLayout(gridbag);
		constraints.fill = GridBagConstraints.NONE;
		setBackground(Color.gray);
		
		Label titleL = new Label("Refraction of Light",Label.CENTER);
		titleL.setBackground(Color.black);
		titleL.setForeground(Color.green);
		titleL.setFont(new Font("times",Font.BOLD,14));
		buildConst(constraints,0,0,5,1,100,100);
		gridbag.setConstraints(titleL, constraints);
		add(titleL);
		
		Label angleL = new Label("Incident Angle",Label.RIGHT);
		buildConst(constraints,0,1,1,1,100,100);
		constraints.anchor = GridBagConstraints.EAST;
		gridbag.setConstraints(angleL, constraints);
		add(angleL);
		
		angleTF = new TextField("45",3);
		buildConst(constraints,1,1,1,1,100,100);
		gridbag.setConstraints(angleTF, constraints);
		add(angleTF);
		
		Label angleUT = new Label("degrees",Label.LEFT);
		buildConst(constraints,2,1,1,1,100,100);
		constraints.anchor = GridBagConstraints.WEST;
		gridbag.setConstraints(angleUT, constraints);
		add(angleUT);
		
		Button plus5deg = new Button(" +5 ");
		buildConst(constraints,3,1,1,1,100,100);
		constraints.anchor = GridBagConstraints.WEST;
		gridbag.setConstraints(plus5deg, constraints);
		add(plus5deg);
		
		Button minus5deg = new Button(" -5 ");
		buildConst(constraints,4,1,1,1,100,100);
		constraints.anchor = GridBagConstraints.WEST;
		gridbag.setConstraints(minus5deg, constraints);
		add(minus5deg);
		
		Label indexL = new Label("Index of Refraction",Label.RIGHT);
		buildConst(constraints,0,2,1,1,100,100);
		constraints.anchor = GridBagConstraints.EAST;
		gridbag.setConstraints(indexL, constraints);
		add(indexL);
		
		indexTF = new TextField("1.33",3);
		buildConst(constraints,1,2,1,1,100,100);
		gridbag.setConstraints(indexTF, constraints);
		add(indexTF);
		
		c.addItem("Vacuum (Air) n=1.00");
		c.addItem("Water        n=1.33");
		c.addItem("Glycerine    n=1.47");
		c.addItem("Glass, crown n=1.52");
		c.addItem("Glass, flint n=1.66");
		c.addItem("Zircon       n=1.92");
		c.addItem("Diamond      n=2.42");
		c.select(1);
		buildConst(constraints,2,2,3,1,100,100);
		constraints.anchor = GridBagConstraints.EAST;
		gridbag.setConstraints(c, constraints);
		add(c);
		
		Label waveL = new Label("Incident Wavelength",Label.RIGHT);
		buildConst(constraints,0,3,1,1,100,100);
		constraints.anchor = GridBagConstraints.EAST;
		gridbag.setConstraints(waveL, constraints);
		add(waveL);
		
		waveTF = new TextField("650",3);
		buildConst(constraints,1,3,1,1,100,100);
		gridbag.setConstraints(waveTF, constraints);
		add(waveTF);
		
		Label waveUT = new Label("nm        ",Label.LEFT);
		buildConst(constraints,2,3,1,1,100,100);
		constraints.anchor = GridBagConstraints.WEST;
		gridbag.setConstraints(waveUT, constraints);
		add(waveUT);
		
		Button plus15nm = new Button("+15");
		buildConst(constraints,3,3,1,1,100,100);
		constraints.anchor = GridBagConstraints.WEST;
		gridbag.setConstraints(plus15nm, constraints);
		add(plus15nm);
		
		Button minus15nm = new Button("-15");
		buildConst(constraints,4,3,1,1,100,100);
		constraints.anchor = GridBagConstraints.WEST;
		gridbag.setConstraints(minus15nm, constraints);
		add(minus15nm);
		
		Label refwavLengL = new Label("Refracted Wavelength",Label.RIGHT);
		buildConst(constraints,0,4,1,1,100,100);
		constraints.anchor = GridBagConstraints.EAST;
		gridbag.setConstraints(refwavLengL, constraints);
		add(refwavLengL);
		
		refwavLengTF = new TextField("268",3);
		buildConst(constraints,1,4,1,1,100,100);
		gridbag.setConstraints(refwavLengTF, constraints);
		refwavLengTF.setEditable(false);
		add(refwavLengTF);		
		
		Label refwaveUT = new Label("nm        ",Label.LEFT);
		buildConst(constraints,2,4,1,1,100,100);
		constraints.anchor = GridBagConstraints.WEST;
		gridbag.setConstraints(refwaveUT, constraints);
		add(refwaveUT);
		
		Label refAngleL = new Label("Refracted Angle",Label.RIGHT);
		buildConst(constraints,0,5,1,1,100,100);
		constraints.anchor = GridBagConstraints.EAST;
		gridbag.setConstraints(refAngleL, constraints);
		add(refAngleL);
		
		refAngleTF = new TextField("16.9",3);
		buildConst(constraints,1,5,1,1,100,100);
		gridbag.setConstraints(refAngleTF, constraints);
		refAngleTF.setEditable(false);
		add(refAngleTF);
		
		Label refAngleUT = new Label("degrees   ",Label.LEFT);
		buildConst(constraints,2,5,1,1,100,100);
		constraints.anchor = GridBagConstraints.WEST;
		gridbag.setConstraints(refAngleUT, constraints);
		add(refAngleUT);
		
	}
	
	public boolean action(Event evt, Object arg){
	
		if(evt.target instanceof Choice){
			int which = c.getSelectedIndex();
			if (which == 0){
				double index = 1.00;
				region.index = index;	
				indexTF.setText(String.valueOf(index));
			}
			if (which == 1){
				double index = 1.33;
				region.index = index;
				indexTF.setText(String.valueOf(index));
			}
			if (which == 2){
				double index = 1.47;
				region.index = index;	
				indexTF.setText(String.valueOf(index));
			}
			if (which == 3){
				double index = 1.52;
				region.index = index;
				indexTF.setText(String.valueOf(index));
			}
			if (which == 4){
				double index = 1.66;
				region.index = index;	
				indexTF.setText(String.valueOf(index));
			}
			if (which == 5){
				double index = 1.92;
				region.index = index;
				indexTF.setText(String.valueOf(index));
			}
			if (which == 6){
				double index = 2.42;
				region.index = index;	
				indexTF.setText(String.valueOf(index));
			}
		}	
			
		if(evt.target instanceof Button){
			String label = (String)arg;
			if(label.equals(" +5 ")){
				double incidentangle = Double.valueOf(angleTF.getText()).doubleValue() + 5.0;
				if(incidentangle < 0.0){
					incidentangle = 0.0;
				}
				if(incidentangle > 85.0){
					incidentangle = 85.0;
				}
				region.incidentangle = incidentangle;
				angleTF.setText(String.valueOf(incidentangle));	
			}
			if(label.equals(" -5 ")){
				double incidentangle = Double.valueOf(angleTF.getText()).doubleValue() - 5.0;
				if(incidentangle < 0.0){
					incidentangle = 0.0;
				}
				if(incidentangle > 85.0){
					incidentangle = 85.0;
				}
				region.incidentangle = incidentangle;
				angleTF.setText(String.valueOf(incidentangle));			
			}
			if(label.equals("+.02")){
				double index = Double.valueOf(indexTF.getText()).doubleValue() + 0.02;
				if(index < 1.0){
					index = 1.0;
				}
				if(index > 2.42){
					index = 2.42;
				}
				region.index = index;	
				indexTF.setText(String.valueOf(index));
			}
			if(label.equals("-.02")){
				double index = Double.valueOf(indexTF.getText()).doubleValue() - 0.02;
				if(index < 1.0){
					index = 1.0;
				}
				if(index > 2.42){
					index = 2.42;
				}
				region.index = index;	
				indexTF.setText(String.valueOf(index));
			}
			if(label.equals("+15")){
				double wavelength = Double.valueOf(waveTF.getText()).doubleValue() + 15.0;
				if(wavelength < 475.0){
					wavelength = 475.0;
				}
				if(wavelength > 650.0){
					wavelength = 650.0;
				}
				double Hue = (650 - wavelength)*240/(650-475);
				region.light = Color.getHSBColor((float)Hue/360,1,1);
				region.wavelength = wavelength*20/650;
				waveTF.setText(String.valueOf(wavelength));
			}
			if(label.equals("-15")){
				double wavelength = Double.valueOf(waveTF.getText()).doubleValue() - 15.0;
				if(wavelength < 475.0){
					wavelength = 475.0;
				}
				if(wavelength > 650.0){
					wavelength = 650.0;
				}
				double Hue = (650 - wavelength)*240/(650-475);
				region.light = Color.getHSBColor((float)Hue/360,1,1);
				region.wavelength = wavelength*20/650;
				waveTF.setText(String.valueOf(wavelength));
			}
			
			double index = Double.valueOf(indexTF.getText()).doubleValue();
			double wavelength = Double.valueOf(waveTF.getText()).doubleValue();
			double incidentangle = Double.valueOf(angleTF.getText()).doubleValue();
			refwavLengTF.setText(String.valueOf(wavelength/index));
			refAngleTF.setText(String.valueOf((180/Math.PI) * Math.asin((1/index) *
						Math.sin(incidentangle*Math.PI/180))));
							
			return true;
		}
		if(evt.target instanceof TextField){
			
			double wavelength = Double.valueOf(waveTF.getText()).doubleValue();
			if(wavelength < 475.0){
				waveTF.setText(String.valueOf(475));
				wavelength = 475.0;
			}
			if(wavelength > 650.0){
				waveTF.setText(String.valueOf(650));
				wavelength = 650.0;
			}			
			double Hue = (650 - wavelength)*240/(650-475);
			region.light = Color.getHSBColor((float)Hue/360,1,1);
			region.wavelength = wavelength*20/650;
			
			double incidentangle = Double.valueOf(angleTF.getText()).doubleValue();
			if(incidentangle < 0.0){
				angleTF.setText(String.valueOf(0));
				incidentangle = 0.0;
			}
			if(incidentangle > 85.0){
				angleTF.setText(String.valueOf(85));
				incidentangle = 85.0;
			}
			region.incidentangle = incidentangle;
			
			double index = Double.valueOf(indexTF.getText()).doubleValue();
			if(index < 1.0){
				indexTF.setText(String.valueOf(1.0));
				index = 1.0;
			}
			if(index > 2.42){
				indexTF.setText(String.valueOf(2.42));
				index = 2.42;
			}
			region.index = index;
			
			refwavLengTF.setText(String.valueOf(wavelength/index));
			refAngleTF.setText(String.valueOf((180/Math.PI) * Math.asin((1/index) *
						Math.sin(incidentangle*Math.PI/180))));
			
			return true;
		}else{
			return false;
		}	
	}	
}

class DrawingRegionLR extends Canvas{
	Image offscreen;
	Graphics offgraphics;
	Dimension offscreensize;
	Dimension d = size();
		
	double index = 1.33;
	double incidentangle = 45.0;
	double refractangle;				   
	double theta;
	double wavelength = 20.0;
	double thewavelength;
	double dx=0;
	double dy=0;
	int baseline;
	int xo = 100;
	int yo = 0;
	int xp;
	int x1, y1, x2, y2;
	int incidentlength = 65;
	double length;
	int k = 0;	
	Color mygray = new Color(200,200,200);
	Color light = new Color(255,0,0);
	
	public void update(Graphics g){
		paint(g);
	}	
		
	public void paint(Graphics g){
		setBackground(Color.white);
		d = size();
		
		theta = refractangle;
		thewavelength = wavelength;
		refractangle = (180/Math.PI) * Math.asin((1/index) *
						Math.sin(incidentangle*Math.PI/180));
		baseline = (int)(size().height/2);
		
    	if ((d.width <= 0) || (d.height <= 0))
    	  return;

    	if ((offscreen == null) ||
    	    (d.width != offscreensize.width) ||
    	    (d.height != offscreensize.height)) {
    	  offscreen = createImage(d.width, d.height);
    	  offscreensize = d;
    	  offgraphics = offscreen.getGraphics();
    	}
		
		offgraphics.setColor(Color.white);
		offgraphics.fillRect(0,0,size().width,baseline);
		offgraphics.setColor(mygray);
		offgraphics.fillRect(0, baseline, size().width, size().height-baseline);
		
		for (int i = 15; i >= -35; i--){
			if ((dy - (i)*thewavelength*Math.cos(theta*Math.PI/180)) <= 0){
				thewavelength = wavelength;
				theta = incidentangle;
				length = incidentlength;				
			}else{
				thewavelength = wavelength/index;
				theta = refractangle;
				length = incidentlength * Math.cos(refractangle*Math.PI/180) /
				         Math.cos(incidentangle*Math.PI/180);
			}
			
			dx = k*0.2*thewavelength*Math.sin(theta*Math.PI/180);
			dy = k*0.2*thewavelength*Math.cos(theta*Math.PI/180);
		
			x1 = (int)(xo + dx - (i)*thewavelength*Math.sin(theta*Math.PI/180));
			y1 = (int)(baseline + dy - (i)*thewavelength*Math.cos(theta*Math.PI/180));
			x2 = (int)(xo + dx + length*Math.cos(theta*Math.PI/180) -
				       (i)*thewavelength*Math.sin(theta*Math.PI/180));
			y2 = (int)(baseline + dy - length*Math.sin(theta*Math.PI/180) -
					   (i)*thewavelength*Math.cos(theta*Math.PI/180));
					   
			if ((y1 >= baseline && y2 < baseline)){
				dx = k*0.2*wavelength*Math.sin(incidentangle*Math.PI/180);
				dy = k*0.2*wavelength*Math.cos(incidentangle*Math.PI/180);
				offgraphics.setColor(light);
				xp = (int)(x1 + (y1 - baseline)/Math.tan(refractangle*Math.PI/180));
				offgraphics.drawLine(x1, y1, xp, baseline);
				x2 = (int)(xo + dx + incidentlength*Math.cos(incidentangle*Math.PI/180)
						   - (i)*wavelength*Math.sin(incidentangle*Math.PI/180));
				y2 = (int)(baseline + dy -
					       incidentlength*Math.sin(incidentangle*Math.PI/180) -
						   (i)*wavelength*Math.cos(incidentangle*Math.PI/180));			
				offgraphics.drawLine(xp, baseline, x2, y2);
			}else if (y1 <= baseline && y2 <= baseline){
				offgraphics.setColor(light);
				offgraphics.drawLine(x1, y1, x2, y2);
			}else if (y1 >= baseline && y2 >= baseline){
				offgraphics.setColor(light);
				offgraphics.drawLine(x1, y1, x2, y2);
			}	
		}
		g.drawImage(offscreen,0,0,this);
	}
	
	public void destroy(){
		offgraphics.dispose();
	}
}	