// Modification history // May 31,1997 fixed bug when object located at focus point // May 22,1997 add paraxial ray option for mirror // Feb 21,1997 major modification, almost rewritten the whole code // try without frame => very slow for netscape // Nov. 3, 1996 impletemt double buffering //----------------------------------------------------- // written by Fu-Kwun Hwang // I hope that you enjoy this applet // Suggestions? E-mail to hwang@phy03.phy.ntnu.edu.tw //----------------------------------------------------- import java.awt.*; public class thinLens extends java.applet.Applet{ String buttonText="start"; String windowTitle="Thin Lens demonstration by Fu-Kwun Hwang"; int windowWidth = 600; int windowHeight = 300; LensWindow m; int windowCount=0; public void init() { String str; // get parameters if((str=getParameter("buttonText"))!=null) buttonText=str; if((str=getParameter("windowTitle"))!=null) windowTitle=str; if((str=getParameter("windowWidth"))!=null) windowWidth=Integer.parseInt(str); if((str=getParameter("windowHeight"))!=null) windowHeight=Integer.parseInt(str); if((str=getParameter("autoStart"))!=null){ m = new LensWindow(windowTitle, true); go(); } add(new Button("Start")); } private void go(){ m.resize(windowWidth,windowHeight); m.show(); m.start(); } public boolean action(Event e,Object arg) { if (e.target instanceof Button && ((String)arg).equals(buttonText)) { m = new LensWindow(String.valueOf(++windowCount)+":"+ windowTitle, true); } go(); return true; } //allow the applet to also run as an application. public static void main(String args[]) { new thinLens().begin(); } private void begin() { m = new LensWindow(windowTitle, false); go(); } } class LensWindow extends Frame { boolean is_applet; boolean is_lens=true; LensWindow(String title, boolean isapp){ super(title); is_applet=isapp; setBackground(Color.lightGray); init(); } public void start(){ reset(); repaint(); lMirror.hide(); cThin.hide(); } public boolean handleEvent(Event e) { if (e.id == Event.WINDOW_DESTROY) { hide(); removeAll(); dispose(); if(!is_applet) System.exit(0); } return super.handleEvent(e); } TextField textP,textQ,textF,textM,textR; Label lMirror; Checkbox cThin; Button type; Dimension area; int xc,yc; double scale=10.; void init(){ Panel p=new Panel(); p.add(textR=new TextField(" X , Y",6)); p.add(new Label("p=")); p.add(textP=new TextField("10.",2)); p.add(new Label("q=")); p.add(textQ=new TextField("10.",2)); p.add(new Label("f=")); p.add(type=new Button("+")); p.add(textF=new TextField("5.",2)); p.add(new Label("M=")); p.add(textM=new TextField("1.",2)); Choice c; p.add(c=new Choice()); c.addItem("Lens"); c.addItem("Mirror"); p.add(new Button("Reset")); p.add(lMirror=new Label("Paraxial")); p.add(cThin=new Checkbox()); cThin.setState(true); thinMirror=cThin.getState(); add("North",p); //show(); } boolean thinMirror=true; public boolean action(Event ev, Object arg) { if (ev.target instanceof Button) { String label = (String)arg; if(label.equals("Reset"))reset(); else{ if(label.equals("+")) type.setLabel("-"); else if(label.equals("-")) type.setLabel("+"); lf*=-1.; repaint(); } }else if( ev.target instanceof TextField){ String label = (String)arg; double value; value=Double.valueOf(label).doubleValue(); if(ev.target==textP) textInput(1,value); else if(ev.target==textQ) textInput(2,value); else if(ev.target==textF) textInput(3,value); else if(ev.target==textM) textInput(4,value); repaint(); }else if(ev.target instanceof Choice){ String label = (String)arg; if(label.equals("Lens")){ is_lens=true; lMirror.hide(); cThin.hide(); }else{ is_lens=false; lMirror.show(); cThin.show(); show(); } lshow(); repaint(); }else if(ev.target instanceof Checkbox){ thinMirror=cThin.getState(); lshow(); repaint(); } return true; } public void reset() { area=size(); xc=area.width/2; yc=area.height/2-10; double ff=10.*scale; type.setLabel("+"); textF.setText(Double.toString(ff)); linit(xc,yc,(int)(0.7*yc),ff); xc-=(int)(2.*lf); // initial object position yc=yc*3/4; repaint(); } boolean objectmove=false,rightClick=false; public boolean mouseDown(Event e, int x, int y){ if(!lmouseDown(x,y)){ if(Math.abs(x-xc)<5 ){ xc=x; yc=y; repaint(); objectmove=true; } } if(e.modifiers==Event.META_MASK)//"Right Click, "; rightClick=true; return true; } public boolean mouseDrag(Event e, int x, int y){ if(working)return true; if(lmouseDrag(x,y)){ if(rightClick)xc=lxc-ox;// also move objects repaint(); // change lens }else if(objectmove){ // move object if(!is_lens && x>lxc || lh0;j++,i=lxc-(int)(j*scale)) g.drawLine(i,lyc-2,i,lyc+2); for(i=lxc,j=0;ixc){ ox=lxc-xc; side=1; }else{ ox=xc-lxc; side=-1; } oy=lyc-yc; if(ox==lf){// located at focus point normal=false; textQ.setText("Inf"); }else { normal=true; ix=1./(1./lf-1./ox); writeText(textQ,ix); } magnify=-ix/ox; // write P,Q,F writeText(textP,Math.abs(lxc-xc)); writeText(textF,Math.abs(lf)); textM.setText(String.valueOf((int)(magnify*100.)/100.)); int x1,y1,x2,y2; // ray paths x1=lxc-side*ox; y1=lyc-oy; if(normal)iy=magnify*oy; else{// May 31,1997 double mag2=-100.*magnify/Math.abs(magnify); iy=-mag2*oy; ix=mag2*ox; } y2=lyc-(int)iy; //draw object drawit(g,x1,lyc,oy); g.setColor(Color.blue); if(lh>Math.abs(iy))inrange=true; else inrange=false; if(is_lens){// lens x2=lxc+side*(int)ix; g.drawLine(x1,y1,lxc,y1); //1-1 平行入射光 if(magnify<0){ g.drawLine(lxc,y1,x2,y2); //1-2 g.drawLine(x1,y1,x2,y2); //2 穿過透鏡中心 if(inrange){ g.drawLine(x1,y1,lxc,y2); //3-1 穿過焦點入射光 g.drawLine(lxc,y2,x2,y2); //3-2 平行主軸 } g.setColor(Color.blue); }else{ if(lf>0){ // converging lens g.drawLine(lxc,y1,lxc+2*side*(int)lf,lyc+oy); //1-2 g.drawLine(x1,y1,lxc-side*(int)ix,lyc+(int)iy); //2-1 if(inrange) { g.drawLine(x1,y1,lxc,lyc-(int)iy); //3-1 g.drawLine(lxc,y2,lxc+2*side*(int)lf,y2); //3-2 } g.setColor(Color.green); g.drawLine(x1,y1,x2,y2); //2-2 g.drawLine(lxc,y1,x2,y2); //1-3 if(inrange) g.drawLine(lxc,y2,x2,y2); //3-3 }else{ // diverging lens g.drawLine(lxc,y1,lxc-2*side*(int)lf,lyc-3*oy); //1-2 g.drawLine(x1,y1,lxc+side*ox,lyc+(int)oy); //2 if(inrange){ g.drawLine(x1,y1,lxc,y2); //3-1 g.drawLine(lxc,y2,lxc-2*side*(int)lf,y2); //3-2 } g.setColor(Color.green); g.drawLine(lxc,y1,lxc+side*(int)lf,lyc); //1-3 if(inrange) g.drawLine(lxc,y2,lxc+side*(int)ix,y2); //3-3 } } }else{// mirror double angle=Math.asin(oy/lr); double dw=0.,dw2; int xx,xx2,yy; x2=lxc-(int)ix; if(!thinMirror)dw=lr*(1.-Math.cos(angle)); xx=lxc-(int)dw; angle*=2.; if(lf>0){// concave mirror 凹面鏡 boolean within; g.drawLine(x1,y1,lxc,lyc); //2-1 物 通過 鏡面中心 yy=lyc-(int)(lr*oy/(lr-ox)); double mm=iy/(-ix+lr),xt=0.; if(!thinMirror) xt=lr*(1./Math.sqrt(1.+mm*mm)-1.); if( (within=(Math.abs(mm*(xt+lr))0)side=1;else side=-1; if(oy*iy>0){//虛像 if(!thinMirror)g.drawLine(x1,y1,lxc-(int)lr,lyc);//4-2 垂直入射 else g.drawLine(lxc-(int)(2.*lf),lyc,lxc,yy=lyc-(int)(iy*2.*lf/(2.*lf-ix))); g.drawLine(lxc,lyc,lxc-2*ox,lyc+2*oy);//2-2 鏡心反射 g.setColor(Color.green); g.drawLine(lxc,lyc,x2,y2);//2-3 g.drawLine(lxc+(int)xt,yy,x2,y2);//2-1 g.setColor(Color.blue); g.drawLine(x1,y1,xx,y1);//1-1 平行入射光 if(!thinMirror)g.drawLine(xx,y1,lxc-(int)lr,lyc-oy+(int)(Math.tan(angle)*(lr-dw)));//1-2 g.setColor(Color.green); if(!thinMirror){ g.drawLine(xx,y1,lxc-(int)ix,lyc-oy-(int)(Math.tan(angle)*(-ix+dw)));//1-3 g.setColor(Color.yellow); g.drawLine(lxc-(int)lf,lyc,x2,y2); }else{ g.drawLine(lxc,y1,x2,y2); g.drawLine(lxc,yy,x2,y2); g.setColor(Color.blue); g.drawLine(lxc,y1,lxc-(int)lf,lyc); } }else{//實像 g.drawLine(lxc,lyc,x2,y2);//2-2 鏡面中心--像 if(within&&!thinMirror)g.drawLine(x1,y1,x2,y2);//4-2 g.drawLine(x1,y1,xx,y1);//1-1 平行入射光 if(!thinMirror){ g.drawLine(xx,y1,lxc-(int)ix,lyc-oy+side*(int)(Math.abs(Math.tan(angle))*(ix-dw)));//1-2 g.setColor(Color.yellow); } g.drawLine(xx,y1,lxc,yy=lyc+(int)(lf*iy/(ix-lf))); g.drawLine(x2,y2,lxc,yy); if(inrange){ g.drawLine(x1,y1,lxc,yy=lyc+(int)(lf*oy/(ox-lf)));//3-1 g.drawLine(x2,y2,lxc,yy);//3-2 } } if(!thinMirror){ g.setColor(Color.white); g.drawLine(lxc-(int)lr,lyc,xx,y1); } }else{// convex mirror dw2=lr*(1.-Math.cos(Math.asin(oy/(lr+ox)))); yy=lyc+(int)((2*lf+dw2/2.)*oy/(ox-2.*lf)); g.drawLine(x1,y1,xx=lxc+(int)dw2,yy);//2垂直射向鏡面 g.drawLine(x1,y1,lxc,lyc);//3 通過鏡面中心 g.drawLine(lxc-2*ox,lyc+2*oy,lxc,lyc);//3-1 g.setColor(Color.green); if(!thinMirror)g.drawLine(lxc+(int)dw2,yy,lxc+(int)lr,lyc);//2-1 else g.drawLine(xx,yy,lxc-(int)(2*lf),lyc); g.drawLine(lxc,lyc,x2,y2);//3-2 g.setColor(Color.blue); g.drawLine(x1,y1,xx=lxc+(int)(dw),y1);//1-1 平行入射光 if(!thinMirror)g.drawLine(lxc+(int)(dw+lf),y1+(int)(lf*Math.tan(angle)),xx,y1); else g.drawLine(lxc,(int)y1,lxc+(int)(2*lf),y1-2*oy); if(!thinMirror){ g.setColor(Color.white); g.drawLine(lxc+(int)(2*dw-lr),2*y1-lyc,lxc-(int)(2*lf),lyc);//通過焦點 } g.setColor(Color.green); if(!thinMirror){ g.drawLine(xx,y1,lxc+(int)(dw-lf),y1-(int)(lf*Math.tan(angle))); g.setColor(Color.yellow); } g.drawLine(lxc,y1,lxc-(int)lf,lyc); } } if(oy*iy>0)g.setColor(Color.green); else g.setColor(Color.blue); drawit(g,x2,lyc,(int)iy); // draw image } private void drawit(Graphics g,int x,int y,int height){ // for object and image int x1,y1,width=2,sign=1; x1=x-width/2; if(height<0){ sign=-1; y1=y; }else y1=y-height; g.fillRect(x1,y1,width,sign*height); y1=y-height; width=2*width; sign*=2; // draw hats g.drawLine(x,y1,x+width,y1+sign*width); g.drawLine(x,y1,x-width,y1+sign*width); } int lxc,lyc,lh; // (xc,yc):center position, h:height of lens double lr,lf; // focusLength*2; int lwidth; // half/quater width of lens public void linit(int x, int y,int hi,double fi){ lxc=x; lyc=y; lr=Math.abs(2.*fi); lf=fi; if(hi<10)lh=10; // minimum height of lens else if(hi>lr) lh=(int)lr-10; else lh=hi; if(!is_lens && lf<0)type.setLabel("-"); else type.setLabel("+"); } boolean moving=false; boolean sizing=false; boolean lmouseDown(int x,int y){ if(Math.abs(x-lxc+ww)lxc && (x-lxc)0) g.fillOval(lxc-lwidth,lyc-lh,2*lwidth,2*lh); else{ double x0,y0; double c=Math.PI-angle,dc; int ncnt=100;//(int)(lr*c/15.)+1; int ncnt2=2*ncnt; x0=lxc+lwidth/2+lr; y0=lyc; if(ncnt>cnt){ X=new int[ncnt2]; Y=new int[ncnt2]; cnt=ncnt; } //g.drawString(String.valueOf(ncnt),100,100); ncnt2-=1; dc=2.*angle/(ncnt-1); if(is_lens){ for(int i=0;i0){// concave mirror ww=lwidth; x0=lxc-lr+1; for(int i=0;i