/******************************************************************
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);
}
}