/****************************************************************** Java Applet fmr1.java April 1997 - Michael J. Hurben This applet provides a simulation of a simple FMR experiment. The user can choose between field swept and frequency swept models. The applet has two parts. On the left side, the H field, microwave pump field, and magnetization vectors are drawn in motion. On the right side, a plot of the absorbed microwave power versus field or frequency is drawn in 'real' time. All quantities are in arbitrary units, and values have been chosen in order to provide the most visually pleasing display. Later versions of this applet will be more physically rigorous. For now, the applet aims to provide a simple qualitative understanding of a typical FMR experiment. The applet uses double buffering in an attempt to eliminate (or reduce) the flicker in the animation. ******************************************************************/ import java.applet.Applet; import java.awt.*; public class fmr1 extends Applet implements Runnable { Dimension offDimension, d; // Variables used to create an Image offImage; // offscreen image via the update() Graphics offGraphics; // method, to reduce flicker int vDx = 5; // int vDy = 5; // Defines the drawing area int vDheight = 190; // for the vectors (left hand side) int vDwidth = 150; // int gDx = 2*vDx+vDwidth+50; // int gDy = vDy; // Defines drawing area for int gDheight = vDheight-20; // the graph (right hand side) int gDwidth = 200; // int bx = vDx+vDwidth/2; // Coordinates for vector bases int by = vDheight-30; // (centered) int tipWidth =10; // Size of the vector int tipLength = 16; // arrow head int Hfield = 0; // Initial value for Hfield int Mbasex=bx; // Set all initial variables int Mbasey=by; // for the magnetization, int Mtipx=bx; // or M, vector int Mtipy=by-vDwidth/2-6; int Mlength=vDwidth/2-6; int[] MtipPx={Mtipx, Mtipx+tipWidth/2, Mtipx-tipWidth/2}; int[] MtipPy={Mtipy, Mtipy+tipLength, Mtipy+tipLength}; int Hbasex=bx; // Set all initial variables int Hbasey=by; // for the static field, int Htipx=bx; // or H, vector int Htipy=by-vDwidth/2-6; int Hlength=vDwidth/2-6; int[] HtipPx={Htipx, Htipx+tipWidth/2, Htipx-tipWidth/2}; int[] HtipPy={Htipy-Hfield, Htipy-Hfield+tipLength, Htipy-Hfield+tipLength}; int hbasex=bx; // Set all inital variables int hbasey=6*gDy; // for the microwave pump, int htipx=bx+60; // or h, vector int htipy=6*gDy; int hlength=60; int[] htipPx={htipx, htipx-tipLength, htipx-tipLength}; int htipPy[]={htipy, htipy+tipWidth/2, htipy-tipWidth/2}; int tip1x = Mtipx; // int tip1y = Mtipy; // Temp variables used to draw arrow int tip2x = Mtipx; // on magnetization vector int tip2y = Mtipy; // int tmax = 40000; // Maximum time int time = 0; // Starting time int Hfield2 = Hfield; // Field H at time+1 : initial value int Hmax = 30; // Range for sweeping int Hmin = -30; // the H field strength int x1, x2, y1, y2; // Used in drawLine() to create plot double center = (2*3.14/4000); // Center frequency, arb units double c = center; // Resonance frequency double c2 = c; // Res freq at time+1 double freq = center; // Current frequency at given time double freq2 = center; // Frequency at time+1 double pow = 1; // Power double amp = 1; // Amplitude = sqrt(power) double pow1=1, pow2=1; // Power at time and time+1 double damp = 1; // Initial damping constant boolean freqSweep = true; // For selecting freq or field sweep boolean oldScreen = true; // For redrawing plot Thread t; Button b1, b2, b3, b4, b5, b6; Checkbox cb1, cb2; public void init() // Initialize : { // Set up the user interface setLayout(new BorderLayout(10,10)); // and start the thread. Panel p1 = new Panel(); // p1.setLayout(new GridLayout(8,1)); CheckboxGroup cbg = new CheckboxGroup(); // Radio buttons cb1 = new Checkbox("Sweep Frequency", cbg, true); cb2 = new Checkbox("Sweep Field", cbg, false); b1 = new Button("Start New Plot"); b2 = new Button("More Damping"); b3 = new Button("Less Damping"); b4 = new Button("Pause"); b5 = new Button("Resume"); b6 = new Button("Finish"); p1.add(cb1); p1.add(cb2); p1.add(b1); p1.add(b2); p1.add(b3); p1.add(b4); p1.add(b5); p1.add(b6); add("East", p1); // Put buttons on the far right side t = new Thread(this); t.start(); } public boolean action(Event e, Object o) { if (e.target.equals(cb1)) // Respond to button pushes { // or checkbox selections t.suspend(); freqSweep = true; time=0; oldScreen = false; t.resume(); } else if (e.target.equals(cb2)) { t.suspend(); freqSweep = false; time=0; oldScreen = false; t.resume(); } else if (o.equals("More Damping")) { damp = damp*1.1; time = 0; oldScreen = false; } else if (o.equals("Less Damping")) { damp = damp*0.9; time = 0; oldScreen = false; } else if (o.equals("Pause")) { t.suspend(); } else if (o.equals("Resume")) { t.resume(); } else if (o.equals("Finish")) { t.stop(); } else if (o.equals("Start New Plot")) { t.suspend(); time=0; t.resume(); oldScreen=false; } return true; } public void run() { while(true) { if(freqSweep) { freq = center/2+center*time/tmax; freq2 = freq+center/tmax; Hfield = 0; Hfield2 = 0; c = center; c2 = c; } else { freq = center; freq2 = freq; Hfield = (int) (Hmin+(Hmax-Hmin)*time/tmax); Hfield2 = (int) (Hmin+(Hmax-Hmin)*(time+1)/tmax); c = center/2+center*time/tmax; c2 = c+center/tmax; } HtipPy[0] = Mbasey-Mlength-Hfield; HtipPy[1] = Mbasey-Mlength-Hfield+tipLength; HtipPy[2] = Mbasey-Mlength-Hfield+tipLength; double nfreq = (c-freq)*(c-freq)*4e7; double nfreq2 = (c2-freq2)*(c2-freq2)*4e7; pow = damp*damp/(nfreq+damp*damp); pow1 = pow; pow2 = damp*damp/(nfreq2+damp*damp); x1=(int) (gDx+gDwidth*time/tmax); x2=(int) (gDx+gDwidth*(time+1)/tmax); y1=(int) (gDy+gDheight-(gDheight-5)*pow1); y2=(int) (gDy+gDheight-(gDheight-5)*pow2); amp = Math.sqrt(pow); int a = (int) (Mlength*amp*0.707* (Math.cos(time*freq))); int b = (int) (Mlength*0.5*amp*0.707* (Math.sin(time*freq))); int pump = (int) (hlength*Math.cos(time*freq)); htipx = hbasex+pump; Mtipx = Mbasex+a; Mtipy = (int) (Mbasey-Mlength*Math.sqrt(1-amp*amp/2)-b); // Now calculate the vertices for the triangular vector tip double denom = Math.sqrt((Mtipy-Mbasey)*(Mtipy-Mbasey) +(Mtipx-Mbasex)*(Mtipx-Mbasex)); tip1x=Mtipx+(int)(((Mbasex-Mtipx)*tipLength+(tipWidth/2) *(Mtipy-Mbasey))/denom); tip2x=Mtipx+(int)(((Mbasex-Mtipx)*tipLength-(tipWidth/2) *(Mtipy-Mbasey))/denom); tip1y=Mtipy+(int)(((Mbasey-Mtipy)*tipLength-(tipWidth/2) *(Mtipx-Mbasex))/denom); tip2y=Mtipy+(int)(((Mbasey-Mtipy)*tipLength+(tipWidth/2) *(Mtipx-Mbasex))/denom); MtipPx[0] = Mtipx; MtipPx[1] = tip1x; MtipPx[2] = tip2x; MtipPy[0] = Mtipy; MtipPy[1] = tip1y; MtipPy[2] = tip2y; htipPx[0] = htipx; if(htipx >= Mbasex) { htipPx[1] = htipx-tipLength; htipPx[2] = htipx-tipLength; } else { htipPx[1] = htipx+tipLength; htipPx[2] = htipx+tipLength; } htipPy[0] = htipy; htipPy[1] = htipy+tipWidth/2; htipPy[2] = htipy-tipWidth/2; if (time < tmax) { repaint(); time=time+1; } else { t.suspend(); } } } // public void paint(Graphics g) // The guts of the painting { // is done in the update() d=size(); // method update(g); // } public void update(Graphics g) { // if((offGraphics ==null) // Setup an off-screen image ||(d.width !=offDimension.width) // via the update() method. || (d.height != offDimension.height)) // { offDimension=d; offImage=createImage(d.width, d.height); offGraphics=offImage.getGraphics(); } // If start new plot button is pushed, // entire applet is redrawn. Otherwise, // only the vector drawing is redone. if(!oldScreen) { offGraphics.setColor(getBackground()); offGraphics.fillRect(0,0, d.width, d.height); oldScreen=true; } else { offGraphics.setColor(Color.black); offGraphics.fillRect(vDx,vDy, vDwidth, vDheight); } // Draw the Pump (Microwave) Field offGraphics.setColor(Color.cyan); offGraphics.drawLine(hbasex, hbasey, htipx, htipy); offGraphics.fillPolygon(htipPx, htipPy, 3); // Draw the Magnetic Field Vector offGraphics.setColor(Color.blue); offGraphics.drawLine(Hbasex, Hbasey, Hbasex, (Hbasey-Hlength-Hfield)); offGraphics.fillPolygon(HtipPx, HtipPy, 3); // Draw the Magnetization vector offGraphics.setColor(Color.red); offGraphics.drawLine(Mbasex, Mbasey, Mtipx, Mtipy); offGraphics.fillPolygon(MtipPx, MtipPy, 3); // Make sure the M vector actually goes in front & behind! if(Mtipx > Mbasex-10-tipWidth/2 && Mtipx < Mbasex+10+tipWidth/2 && Mtipy < (int) (Mbasey-Mlength*Math.sqrt(1- amp*amp/2))) { offGraphics.setColor(Color.blue); offGraphics.drawLine(Hbasex, Hbasey, Hbasex, (Hbasey-Hlength-Hfield)); offGraphics.fillPolygon(HtipPx, HtipPy, 3); } // Draw the plot offGraphics.setColor(Color.black); offGraphics.drawRect(gDx, gDy, gDwidth, gDheight); if(freqSweep) { offGraphics.drawString("Frequency", gDx+Mbasex-10, gDy+gDheight+18); } else { offGraphics.drawString("Field", gDx+Mbasex+5, gDy+gDheight+18); } offGraphics.drawString("Power", gDx-40, gDy+(gDheight)/2); offGraphics.drawLine(x1,y1,x2,y2); g.drawImage(offImage, 0, 0, this); } }