// Snell.java // /************************************************************************* * * * Copyright (c) 1998 Greg W. Anderson * * * * This program is free software. You can copy, modify, or redistribute * * this software under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version, provided that both * * the above copyright notice appears in all copies and that this * * permission notice appear in any supporting documentation. * * * * This software is provided "as is" in the hope that it will be useful, * * but WITHOUT any expressed or implied warranty of merchantability or * * fitness for any particular purpose. * * See the GNU General Public License for more details. * * Your milage may vary. * *************************************************************************/ // Modification History // Date Initials Change // 2/01/98 GWA Initial release // Abstract: /* * * @author Greg Anderson * @version 0.1, 21 Feb 1998 */ import java.awt.*; import java.awt.event.*; import java.applet.*; import java.text.*; import Caption; import IncidentIndex; import RefractedIndex; public class SnellGraph extends java.applet.Applet { SnellControls controls; Banner banner; SnellCanvas snellCanvas; Label appletName = new Label ("Virtual Interactive Demonstration"); Label department = new Label ("Department of Physics and Astronomy"); Label university = new Label ("Northwestern University"); public SnellGraph(){ setLayout(new BorderLayout()); banner = new Banner (2,2); banner.add("Center",department); banner.add(appletName); banner.add("Center",university); banner.add("South", new Label ("Snell's Law")); add ("North",banner); snellCanvas = new SnellCanvas(); add("Center",snellCanvas); controls = new SnellControls(snellCanvas.snellStatus); add("South",controls); } public void init(){ } public void start(){ controls.enable(); // controls.setEnabled(true); snellCanvas.init(); snellCanvas.start(); } public void stop(){ controls.disable(); // controls.setEnabled(false); } /* private class SnellActionListener implements ActionListener{ public void actionPerformed(ActionEvent e){ //do nothing } }*/ public boolean handleEvent(Event e){ if (e.id == Event.WINDOW_DESTROY){ System.exit(0); } return false; } } class SnellCanvas extends Panel implements Runnable{ public SnellStatus snellStatus; private SnellUpCanvas snellUpCanvas; private SnellDownCanvas snellDownCanvas; private Thread demo; SnellCanvas(){ snellStatus = new SnellStatus(this); snellUpCanvas = new SnellUpCanvas(snellStatus); snellDownCanvas = new SnellDownCanvas(snellStatus); setLayout(new GridLayout(2,1,0,0)); add(snellUpCanvas); add(snellDownCanvas); } public void init(){ snellStatus.init(); snellUpCanvas.init(); snellDownCanvas.init(); } public void start(){ if(demo == null){ demo = new Thread(this); demo.start(); } } public void run(){ while(true){ advance(); repaint(); try{ Thread.sleep(60); } catch (InterruptedException e){ /* do nothing*/ } } } public void stop(){ if(demo != null){ demo.stop(); demo = null; } } public void reset(){ snellUpCanvas.reset(); snellDownCanvas.reset(); } public void advance(){ snellUpCanvas.advance(); snellDownCanvas.advance(); } public void repaint(){ snellUpCanvas.repaint(); snellDownCanvas.repaint(); } public void redraw(Double N1, Double N2, Double Theta){ snellStatus.n1 = N1.doubleValue(); snellStatus.n2 = N2.doubleValue(); snellStatus.theta1 = (2*Math.PI)*Theta.doubleValue()/360.; snellStatus.setTheta2(); snellStatus.setIntensities(); init(); repaint(); } } class SnellStatus{ public double n1 = 1.0; // Index of Refraction above public double n2 = 1.33; // Index of Refraction below public double theta1 = Math.PI/4; // Angle if Incidence public double theta2 = Math.PI/4; // Angle of Refraction public double R = 1; // Reflectance R public double T = 1; // Transmittance T public boolean displayIncident = true; public boolean displayReflected = true; public boolean displayRefracted = true; public boolean displayNormal = false; public boolean TotalInternal = false; public boolean waveFrontView = false; private SnellCanvas parent; private class IncidentNListener implements ActionListener{ public void actionPerformed(ActionEvent e){ TextField tn1 = (TextField) e.getSource(); Double tmp = Double.valueOf( tn1.getText().trim()); n1 = tmp.doubleValue(); reset(); } } public ActionListener getIncidentNListener(){ return (new IncidentNListener()); } private class RefractedNListener implements ActionListener{ public void actionPerformed(ActionEvent e){ TextField tn2 = (TextField) e.getSource(); Double tmp = Double.valueOf(tn2.getText().trim()); n2 = tmp.doubleValue(); reset(); } } public ActionListener getRefractedNListener(){ return (new RefractedNListener()); } private class IncidentAngleListener implements ActionListener{ public void actionPerformed(ActionEvent e){ TextField theta = (TextField) e.getSource(); Double tmp = Double.valueOf( theta.getText().trim()); theta1 = (2*Math.PI)*tmp.doubleValue()/360.; reset(); } } public ActionListener getIncidentAngleListener(){ return (new IncidentAngleListener()); } private class ViewBoxListener implements ItemListener{ //method required to implement ItemListener public void itemStateChanged(ItemEvent e){ Choice c = (Choice) e.getSource(); int view = c.getSelectedIndex(); if (view == 0){ waveFrontView = false; } else{ waveFrontView = true; } } } public ItemListener getViewBoxListener(){ return (new ViewBoxListener()); } private class DisplayNormalBoxListener implements ItemListener{ //method required to implement ItemListener public void itemStateChanged(ItemEvent e){ Checkbox c = (Checkbox) e.getSource(); displayNormal = c.getState(); } } public ItemListener getNormalBoxListener(){ return (new DisplayNormalBoxListener()); } private class DisplayIncidentBoxListener implements ItemListener{ //method required to implement ItemListener public void itemStateChanged(ItemEvent e){ Checkbox c = (Checkbox) e.getSource(); displayIncident = c.getState(); } } public ItemListener getIncidentBoxListener(){ return (new DisplayIncidentBoxListener()); } private class DisplayReflectedBoxListener implements ItemListener{ //method required to implement ItemListener public void itemStateChanged(ItemEvent e){ Checkbox c = (Checkbox) e.getSource(); displayReflected = c.getState(); } } public ItemListener getReflectedBoxListener(){ return (new DisplayReflectedBoxListener()); } private class DisplayRefractedBoxListener implements ItemListener{ //method required to implement ItemListener public void itemStateChanged(ItemEvent e){ try{ Checkbox c = (Checkbox) e.getSource(); displayRefracted = c.getState(); } catch(RuntimeException excep){ //do nothing } } } public ItemListener getRefractedBoxListener(){ return (new DisplayRefractedBoxListener()); } public SnellStatus(SnellCanvas canvas){ parent = canvas; } public void init(){ reset(); } public void reset(){ setTheta2(); setIntensities(); parent.reset(); } public void setTheta2(){ setTheta2(theta1); } private void setTheta2(double theta_1){ // Angle of incidence double arg; arg = n1*Math.sin(theta_1)/n2; if ( arg < -1. || arg > 1){ TotalInternal = true; theta2 = Math.PI/2; return; } TotalInternal = false; theta2 = Math.asin(arg); } public void setR(){ // Angle of incidence if (theta1 == 0){ R = Math.pow(n2-n1,2)/Math.pow(n1+n2,2); return; } else{ R = 1. -T;} if (R==0) displayReflected = false; } public void setT(){ // Angle of incidence if (TotalInternal){ T = 0; return; } if (theta1 == 0){ T = 4*n1*n2/Math.pow(n1+n2,2); return; } else{ T = (Tpar() + Tperp())/2.; if (T < 0)T=0; } if (T==0) displayRefracted = false; } void setIntensities(){ setT(); setR(); } double Rpar(){ return ( Math.pow(rPar(),2) ); } double Rperp(){ return ( Math.pow(rPerp(),2) ); } double Tpar(){ double T_par; if (theta1 == 0){ T_par = 4*n1*n2/Math.pow(n1+n2,2); } else{ T_par = (Math.sin(2*theta1)*Math.sin(2*theta2)) /Math.pow((Math.sin(theta1+theta2)*Math.cos(theta1-theta2)),2); } return (T_par); } double Tperp(){ double T_perp; if (theta1 == 0){ T_perp = 4*n1*n2/Math.pow(n1+n2,2); } else { T_perp = (Math.sin(2*theta1)*Math.sin(2*theta2)) /Math.pow( Math.sin(theta1+theta2), 2); } return (T_perp); } // Amplitude Coefficients for Dielectrics double rPerp(){ double r_perp; if (theta1 == 0){ r_perp = (n1-n2)/(n1+n2); } else{ r_perp = -Math.sin(theta1-theta2)/Math.sin(theta1+theta2); } return r_perp; } double rPar(){ double r_par; if (theta1 == 0){ r_par = (n2-n1)/(n1+n2); } else{ r_par = Math.tan(theta1-theta2)/Math.tan(theta1+theta2); } return r_par; } double tPerp(){ double t_perp; if (theta1 == 0) { t_perp = 2*n1/(n1+n2); } else { t_perp = (2*Math.sin(theta2)*Math.cos(theta1)) /Math.sin(theta1+theta2); } return t_perp; } double tPar(){ double t_par; if (theta1 == 0) { t_par = 2*n1/(n1+n2); } else{ t_par = (2*Math.sin(theta2)*Math.cos(theta1)) /(Math.sin(theta1+theta2)*Math.cos(theta1-theta2)); } return t_par; } } class WaveFront{ private Canvas canvas; private Color myColor; private int width; private double velocity; private double waveLength; private Point origin; private double phase; public WaveFront(Canvas theCanvas, Color theColor, int ww){ canvas = theCanvas; myColor = theColor; width = ww; waveLength = 20; phase = 0; } public void setOrigin(Point p){ origin = p; } public void reset(){ phase = 0; } public void setVelocity(double v){ velocity = v * 3; } public void setWaveLength(double lambda){ waveLength = lambda * 20; } public void paint(Graphics g, double theta){ double r_min = - origin.y / Math.sin(theta) - width * Math.abs(Math.cos(theta)); double r_max = (canvas.size().height - origin.y) / Math.sin(theta) + width * Math.abs(Math.cos(theta)); double r = ((int) (r_min / waveLength)) * waveLength + phase; do { Point pp = new Point((int) (origin.x + r * Math.cos(theta)), (int) (origin.y + r * Math.sin(theta))); drawLine(g, pp, theta); r += waveLength; } while(r <= r_max); } public void advance(){ phase += velocity; phase = phase - ((int) (phase/waveLength)) * waveLength; } private void drawLine(Graphics g, Point p, double theta) { double w_prime = width * Math.sin(theta); Point pStart = new Point((int) (p.x - w_prime * Math.sin(theta)), (int) (p.y + w_prime * Math.cos(theta))); Point pEnd = new Point((int) (p.x + w_prime * Math.sin(theta)), (int) (p.y - w_prime * Math.cos(theta))); g.drawLine(pStart.x, pStart.y, pEnd.x, pEnd.y); } } class SnellUpCanvas extends Canvas{ private SnellStatus status; private WaveFront incidentWaveFront; private WaveFront reflectedWaveFront; private IncidentIndex inci; private Image offImage; private Graphics og; private int width; private int height; public SnellUpCanvas(SnellStatus snellStatus){ status = snellStatus; incidentWaveFront = new WaveFront(this, Color.blue, 100); reflectedWaveFront = new WaveFront(this, Color.green, 100); inci = new IncidentIndex(); } public void init(){ width = size().width; height = size().height; incidentWaveFront.setOrigin(new Point(width/2, height)); reflectedWaveFront.setOrigin(new Point(width/2, height)); reset(); offImage = createImage(width, height); og = offImage.getGraphics(); } public void reset(){ incidentWaveFront.setVelocity(1./status.n1); reflectedWaveFront.setVelocity(- 1./status.n1); incidentWaveFront.setWaveLength(1./status.n1); reflectedWaveFront.setWaveLength(1./status.n1); incidentWaveFront.reset(); reflectedWaveFront.reset(); } public final void update(Graphics g){ g.drawImage(offImage, 0, 0, null); } public void advance(){ if((size().width != width) || (size().height != height)){ init(); } incidentWaveFront.advance(); reflectedWaveFront.advance(); prepareImage(); } public void prepareImage(){ mypaint(og); } public void mypaint(Graphics g) { setBackground(Color.black); int leftSpace = 0; int rightSpace = 0; int bottomMargin = 0; int plotWidth = size().width - leftSpace -rightSpace; // n = 0 - plotwitdh int halfWidth = plotWidth/2; int plotHeight = size().height - bottomMargin; String snellTitle = "Snell's Law"; Color darkdarkGray = new Color(32,32,32); g.setColor(darkdarkGray); g.fillRect(leftSpace, 0, plotWidth+leftSpace, plotHeight); NumberFormat nf = NumberFormat.getInstance(); nf.setMaximumFractionDigits(3); // labels g.setColor(Color.lightGray); inci.plot(g, -15, plotHeight - 40); g.drawString(nf.format(status.n1), 32, plotHeight-15); // The incident line if (status.displayIncident && Math.cos(getTheta1()) > 0){ g.setColor(Color.blue); if(status.waveFrontView) incidentWaveFront.paint(g, Math.PI / 2 - getTheta1()); else g.drawLine(leftSpace, plotHeight - (int)(halfWidth/Math.tan(getTheta1())) , halfWidth, plotHeight); } // The normal if (status.displayNormal){ g.setColor(Color.gray); g.drawLine(halfWidth,plotHeight, halfWidth,0); } else{ g.setColor(Color.white); g.drawString(snellTitle,leftSpace+halfWidth-30, 30); } // The reflected line if (status.displayReflected && Math.cos(getTheta1()) > 0){ g.setColor(Color.green); if(status.waveFrontView) reflectedWaveFront.paint(g, Math.PI / 2 + getTheta1()); else g.drawLine(halfWidth +(int)(plotHeight*Math.tan(getTheta1())) , 0, halfWidth, plotHeight); } } private double getTheta1(){ return status.theta1; } } class SnellDownCanvas extends Canvas{ private SnellStatus status; private WaveFront refractedWaveFront; private RefractedIndex ref; private Caption caption; private Image offImage; private Graphics og; private int width; private int height; public SnellDownCanvas(SnellStatus snellStatus){ status = snellStatus; refractedWaveFront = new WaveFront(this, Color.red, 100); ref = new RefractedIndex(); caption = new Caption(); } public void init(){ width = size().width; height = size().height; refractedWaveFront.setOrigin(new Point(width/2, 0)); reset(); offImage = createImage(width, height); og = offImage.getGraphics(); } public void reset(){ refractedWaveFront.setVelocity(1./status.n2); refractedWaveFront.setWaveLength(1./status.n2); refractedWaveFront.reset(); } public final void update(Graphics g){ g.drawImage(offImage, 0, 0, null); } public void advance(){ if((size().width != width) || (size().height != height)){ init(); } refractedWaveFront.advance(); prepareImage(); } public void prepareImage(){ mypaint(og); } public void mypaint(Graphics g) { setBackground(Color.black); int leftSpace = 0; int rightSpace = 0; int bottomMargin = 0; int plotWidth = size().width - leftSpace -rightSpace; // n = 0 - plotwitdh int halfWidth = plotWidth/2; int plotHeight = size().height - bottomMargin; g.setColor(getBackground()); g.fillRect(leftSpace, 0, plotWidth+leftSpace, plotHeight); NumberFormat nf = NumberFormat.getInstance(); nf.setMaximumFractionDigits(3); // labels g.setColor(Color.gray); ref.plot(g, -15, -12); g.drawString(nf.format(status.n2), 32, 15); caption.plot(g, 0, plotHeight - 40); g.drawString(nf.format(Math.sin(getTheta1())),60,plotHeight-25); g.drawString(nf.format(Math.sin(status.theta2)),60,plotHeight-10); g.drawString("R = "+ nf.format(status.R),5,plotHeight-55); g.drawString("T = "+ nf.format(status.T),5,plotHeight-40); // The normal if (status.displayNormal){ g.setColor(Color.gray); g.drawLine(halfWidth,plotHeight, halfWidth,0); // extra stuff g.drawString("R_perp = "+nf.format(status.Rperp()),5,plotHeight-120); g.drawString("T_perp = "+nf.format(status.Tperp()),5,plotHeight-105); g.drawString("R_par = "+nf.format(status.Rpar()),5,plotHeight-90); g.drawString("T_par = "+nf.format(status.Tpar()),5,plotHeight-75); // draw arcs and label angles } // The refracted line if (!status.TotalInternal){ if (status.displayRefracted && Math.cos(getTheta1()) > 0){ g.setColor(Color.red); if (status.waveFrontView) refractedWaveFront.paint(g,Math.PI / 2 - getTheta2()); else g.drawLine(halfWidth +(int)(plotHeight*Math.tan(status.theta2)) , plotHeight, halfWidth, 0); } } else{ // Total Internal Reflection g.setColor(Color.red); g.drawString("Total Internal Reflection", halfWidth-30, 50); } } public double getTheta2(){ return status.theta2; } private double getTheta1(){ return status.theta1; } } class SnellControls extends Panel{ Checkbox Incident; Checkbox Reflected; Checkbox Refracted; Checkbox Normal; Label btemp; TextField n1; TextField n2; TextField theta; Choice c = new Choice(); SnellStatus status; Double dtemp; public SnellControls(SnellStatus status){ setLayout(new GridLayout(3,4,5,5)); /* nr,nc, vg, hg */ this.status = status; Color darkMagenta = new Color(150,0,150); Color darkGreen = new Color(0,150,0); btemp = new Label(" n1 = "); btemp.setForeground(Color.black); add (btemp); dtemp = new Double(status.n1); n1 = new TextField(dtemp.toString(),4 ); n1.addActionListener(status.getIncidentNListener()); add(n1); btemp = new Label(" n2 = "); btemp.setForeground(Color.black); add (btemp); dtemp = new Double(status.n2); n2 = new TextField(dtemp.toString(),4 ); n2.addActionListener(status.getRefractedNListener()); add(n2); add (new Label("Incident Angle:",Label.LEFT)); btemp = new Label(" theta_1 = "); btemp.setForeground(Color.blue); add (btemp); dtemp = new Double(360*status.theta1/(2.*Math.PI)); theta = new TextField(dtemp.toString(),4); theta.addActionListener(status.getIncidentAngleListener()); add(theta); c.addItem("Laser View"); c.addItem("Wave Fronts View"); c.addItemListener(status.getViewBoxListener()); add(c); Incident = new Checkbox("Incident Ray"); Incident.setState(status.displayIncident); Incident.setForeground(Color.blue); Incident.addItemListener(status.getIncidentBoxListener()); add (Incident); Reflected = new Checkbox("Reflected Ray"); Reflected.setState(status.displayReflected); Reflected.setForeground(darkGreen); Reflected.addItemListener(status.getReflectedBoxListener()); add (Reflected); Refracted = new Checkbox("Refracted Ray"); Refracted.setState(status.displayRefracted); Refracted.setForeground(Color.red); Refracted.addItemListener(status.getRefractedBoxListener()); add (Refracted); Normal = new Checkbox("Show Normal"); Normal.setState(status.displayNormal); Normal.addItemListener(status.getNormalBoxListener()); add (Normal); } } class Banner extends Panel{ public Banner(int n, int m){ Color darkMagenta = new Color(150,0,150); setBackground(darkMagenta); setLayout(new GridLayout(n,m,40,-5)); /* nr,nc, vg, hg */ } }