<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">// ModeBox.java (c) 2001 by Paul Falstad, www.falstad.com.
// Rendering algorithm in this applet is based on the description of
// the algorithm used in Atom in a Box by Dean Dauger (www.dauger.com).
// We raytrace through a 3-d dataset, sampling a number of points and
// integrating over them using Simpson's rule.

import java.io.InputStream;
import java.awt.*;
import java.awt.image.ImageProducer;
import java.applet.Applet;
import java.applet.AudioClip;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.File;
import java.net.URL;
import java.util.Random;
import java.util.Arrays;
import java.awt.image.MemoryImageSource;
import java.lang.Math;
import java.awt.event.*;

class ModeBoxCanvas extends Canvas {
    ModeBoxFrame pg;
    ModeBoxCanvas(ModeBoxFrame p) {
	pg = p;
    }
    public Dimension getPreferredSize() {
	return new Dimension(300,400);
    }
    public void update(Graphics g) {
	pg.updateModeBox(g);
    }
    public void paint(Graphics g) {
	pg.updateModeBox(g);
    }
};

class ModeBoxLayout implements LayoutManager {
    public ModeBoxLayout() {}
    public void addLayoutComponent(String name, Component c) {}
    public void removeLayoutComponent(Component c) {}
    public Dimension preferredLayoutSize(Container target) {
	return new Dimension(500, 500);
    }
    public Dimension minimumLayoutSize(Container target) {
	return new Dimension(100,100);
    }
    public void layoutContainer(Container target) {
	int barwidth = 0;
	int i;
	for (i = 1; i &lt; target.getComponentCount(); i++) {
	    Component m = target.getComponent(i);
	    if (m.isVisible()) {
		Dimension d = m.getPreferredSize();
		if (d.width &gt; barwidth)
		    barwidth = d.width;
	    }
	}
	Insets insets = target.insets();
	int targetw = target.size().width - insets.left - insets.right;
	int cw = targetw-barwidth;
	int targeth = target.size().height - (insets.top+insets.bottom);
	target.getComponent(0).move(insets.left, insets.top);
	target.getComponent(0).resize(cw, targeth);
	cw += insets.left;
	int h = insets.top;
	for (i = 1; i &lt; target.getComponentCount(); i++) {
	    Component m = target.getComponent(i);
	    if (m.isVisible()) {
		Dimension d = m.getPreferredSize();
		if (m instanceof Scrollbar)
		    d.width = barwidth;
		if (m instanceof Label) {
		    h += d.height/5;
		    d.width = barwidth;
		}
		m.move(cw, h);
		m.resize(d.width, d.height);
		h += d.height;
	    }
	}
    }
};

public class ModeBox extends Applet {
    ModeBoxFrame oc;
    void destroyFrame() {
	if (oc != null) 
	    oc.dispose();
	oc = null;
    }
    public void init() {
	oc = new ModeBoxFrame(this);
	oc.init();
    }
    public void destroy() {
	if (oc != null)
	    oc.dispose();
	oc = null;
    }
};

class ModeBoxFrame extends Frame
  implements ComponentListener, ActionListener, AdjustmentListener,
             MouseMotionListener, MouseListener, ItemListener {
    
    Thread engine = null;

    Dimension winSize;
    Image dbimage;
    
    Random random;
    int gridSizeX = 200;
    int gridSizeY = 200;
    
    public String getAppletInfo() {
	return "ModeBox by Paul Falstad";
    }

    Button clearButton;
    Checkbox memoryImageSourceCheck;
    Checkbox stoppedCheck;
    Checkbox spectrumCheck;
    Choice modeChooser;
    Scrollbar speedBar;
    Scrollbar resolutionBar;
    Scrollbar brightnessBar;
    Scrollbar widthBar;
    Scrollbar heightBar;
    double dragZoomStart;
    double zoom = 6.5;
    double rotmatrix[];
    double selectedMinOmega;
    double selectedMaxOmega;
    Rectangle view3d;
    Rectangle viewSpectrum;
    Rectangle viewFreq[];
    double colorMult;
    static final double pi = 3.14159265358979323846;
    static final double pi2 = pi*2;
    int xpoints[];
    int ypoints[];
    int spectrum[];
    static final int spectrumSpacing = 50;
    double func[][][];
    double data[][][];
    double boxwidth = 2;
    double boxheight = 2;
    boolean dragging = false;
    MemoryImageSource imageSource;
    int pixels[];
    int maxTerms = 16;
    static int maxModes = 10;
    static int maxDispCoefs = 8;
    static int viewDistance = 12;
    Mode modes[];
    int modeCount = 0;
    int pause;
    ModeBox applet;
    int selection = -1;
    static final int SEL_NONE = 0;
    static final int SEL_3D = 1;
    static final int SEL_MAG = 2;
    static final int SEL_SPECTRUM = 3;
    static final int MODE_ANGLE = 0;
    static final int MODE_ZOOM = 1;
    int selectedCoefX = -1;
    int selectedCoefY;
    int selectedCoefZ;
    static final int sampleCount = 15;
    int sampleMult[];
    double magDragStart;
    int dragX, dragY, oldDragX, oldDragY, dragStartX, dragStartY;
    double t = 0;
    public static final double epsilon = .00001;
    public static final double epsilon2 = .003;
    
    int getrand(int x) {
	int q = random.nextInt();
	if (q &lt; 0) q = -q;
	return q % x;
    }
    ModeBoxCanvas cv;

    ModeBoxFrame(ModeBox a) {
	super("Box Modes Applet");
	applet = a;
    }

    public void init() {
	String os = System.getProperty("os.name");
	String jv = System.getProperty("java.version");
	boolean altRender = false;
	int res = 32;
	if (os.indexOf("Windows") == 0 &amp;&amp;
	    jv.indexOf("1.1") == 0) {
	    // change settings to speed things up where possible
	    altRender = true;
	    res = 48;
	}

	setLayout(new ModeBoxLayout());
	cv = new ModeBoxCanvas(this);
	cv.addComponentListener(this);
	cv.addMouseMotionListener(this);
	cv.addMouseListener(this);
	add(cv);

	add(clearButton = new Button("Clear"));
	clearButton.addActionListener(this);
	
	stoppedCheck = new Checkbox("Stopped");
	stoppedCheck.addItemListener(this);
	add(stoppedCheck);

	spectrumCheck = new Checkbox("Show Spectrum");
	spectrumCheck.addItemListener(this);
	add(spectrumCheck);

	memoryImageSourceCheck = new Checkbox("Alternate Rendering",
					      altRender);
	memoryImageSourceCheck.addItemListener(this);
	add(memoryImageSourceCheck);

	modeChooser = new Choice();
	modeChooser.add("Mouse = Adjust Angle");
	modeChooser.add("Mouse = Adjust Zoom");
	modeChooser.addItemListener(this);
	add(modeChooser);
	
	add(new Label("Simulation Speed", Label.CENTER));
	add(speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 40, 1, 1, 200));
	speedBar.addAdjustmentListener(this);

	add(new Label("Brightness", Label.CENTER));
	add(brightnessBar = new Scrollbar(Scrollbar.HORIZONTAL, 28,
					  1, 1, 200));
	brightnessBar.addAdjustmentListener(this);

	add(new Label("Image Resolution", Label.CENTER));
	add(resolutionBar =
	    new Scrollbar(Scrollbar.HORIZONTAL, res, 2, 20, 200));
	resolutionBar.addAdjustmentListener(this);

	add(new Label("Width", Label.CENTER));
	add(widthBar = new Scrollbar(Scrollbar.HORIZONTAL, 10, 1, 5, 31));
	widthBar.addAdjustmentListener(this);

	add(new Label("Height", Label.CENTER));
	add(heightBar = new Scrollbar(Scrollbar.HORIZONTAL, 10, 1, 5, 31));
	heightBar.addAdjustmentListener(this);

	add(new Label("http://www.falstad.com", Label.CENTER));

	try {
	    String param = applet.getParameter("PAUSE");
	    if (param != null)
		pause = Integer.parseInt(param);
	} catch (Exception e) { }
	
	modes = new Mode[maxModes];
	addMode(1, 0, 0).magcoef = 1;

	rotmatrix = new double[9];
	rotmatrix[0] = rotmatrix[4] = rotmatrix[8] = 1;
	xpoints = new int[2];
	ypoints = new int[2];

	// generate table of sample multipliers for efficient Simpson's rule
	int i;
	sampleMult = new int[sampleCount];
	for (i = 1; i &lt; sampleCount; i += 2) {
	    sampleMult[i  ] = 4;
	    sampleMult[i+1] = 2;
	}
	sampleMult[0] = sampleMult[sampleCount-1] = 1;

	random = new Random();
	reinit();
	cv.setBackground(Color.black);
	cv.setForeground(Color.white);
	resize(500,500);
	handleResize();
	show();
    }

    void handleResize() {
	reinit();
	// XXX
    }

    void reinit() {
	setMaxTerms();
        Dimension d = winSize = cv.getSize();
	if (winSize.width == 0)
	    return;
	calcSpectrum();
	dbimage = createImage(d.width, d.height);
	setupDisplay();
	pixels = new int[view3d.width*view3d.height];
	int i;
	for (i = 0; i != view3d.width*view3d.height; i++)
	    pixels[i] = 0xFF000000;
	imageSource = new MemoryImageSource(view3d.width, view3d.height,
					    pixels, 0, view3d.width);
    }

    int getTermWidth() {
	return 8;
    }

    // multiply rotation matrix by rotations through angle1 and angle2
    void rotate(double angle1, double angle2) {
	double r1cos = java.lang.Math.cos(angle1);
	double r1sin = java.lang.Math.sin(angle1);
	double r2cos = java.lang.Math.cos(angle2);
	double r2sin = java.lang.Math.sin(angle2);
	double rotm2[] = new double[9];

	// angle1 is angle about y axis, angle2 is angle about x axis
	rotm2[0] = r1cos;
	rotm2[1] = -r1sin*r2sin;
	rotm2[2] = r2cos*r1sin;

	rotm2[3] = 0;
	rotm2[4] = r2cos;
	rotm2[5] = r2sin;

	rotm2[6] = -r1sin;
	rotm2[7] = -r1cos*r2sin;
	rotm2[8] = r1cos*r2cos;

	double rotm1[] = rotmatrix;
	rotmatrix = new double[9];

	int i, j, k;
	for (j = 0; j != 3; j++)
	    for (i = 0; i != 3; i++) {
		double v = 0;
		for (k = 0; k != 3; k++)
		    v += rotm1[k+j*3]*rotm2[i+k*3];
		rotmatrix[i+j*3] = v;
	    }
    }

    double max(double a, double b) { return a &gt; b ? a : b; }
    double min(double a, double b) { return a &lt; b ? a : b; }

    void setMaxTerms() {
	gridSizeX = gridSizeY = (resolutionBar.getValue() &amp; ~1);
	maxTerms = gridSizeX;
	if (maxTerms &gt; 100)
	    maxTerms = 100;
	data = new double[maxTerms][maxTerms][maxTerms];
	func = new double[gridSizeX][gridSizeY][2];
    }

    // set view rectangles for the various subwindows
    void setupDisplay() {
	int perColumn = 2;
	int perRow = 4;
	int freqHeight = getTermWidth() * (maxDispCoefs+1) * perColumn;
	int spectrumHeight = (spectrumCheck.getState()) ?
	    getTermWidth()*6 : 0;
	view3d = new Rectangle(0, 0, winSize.width,
			       winSize.height-freqHeight-spectrumHeight);
	if (spectrumCheck.getState())
	    viewSpectrum = new Rectangle(0, view3d.height,
			 winSize.width, spectrumHeight);
	else
	    viewSpectrum = null;
	viewFreq = new Rectangle[maxDispCoefs];
	int i;
	int winw = getTermWidth() * maxDispCoefs;
	int winh = winw;
	int pad = getTermWidth();
	int x = (winSize.width-(winw*4+pad*3))/2;
	for (i = 0; i != maxDispCoefs; i++)
	    viewFreq[i] = new Rectangle(x+(i%perRow)*(winw+pad),
					view3d.height+spectrumHeight+
					  (i/perRow)*(winh+pad),
					winw, winh);
    }

    // compute func[][][] array (2-d view) by raytracing through a
    // 3-d dataset (data[][][])
    void computeFunction() {
	int i, j;
	genData(false);
	double q = 3.14159265/maxTerms;
	double cost = java.lang.Math.cos(t);
	double izoom = 1/zoom;
	double rotm[] = rotmatrix;
	double boxhalfwidth = boxwidth/2;
	double boxhalfheight = boxheight/2;
	double aratio = view3d.width/(double) view3d.height;
	for (i = 0; i != gridSizeX; i++)
	    for (j = 0; j != gridSizeY; j++) {
		// calculate camera position and direction
		double camx0 = 0;
		double camz0 = viewDistance;
		double camvx0 = (2*i/(double) gridSizeX - 1)*izoom;
		double camvy0 = (2*j/(double) gridSizeY - 1)*izoom;
		// preserve aspect ratio no matter what window dimensions
		if (aratio &lt; 1)
		    camvy0 /= aratio;
		else
		    camvx0 *= aratio;
		// rotate camera with rotation matrix
		double camvz0 = -1;
		double camx  = rotm[0]*camx0+rotm[2]*camz0;
		double camy  = rotm[5]*camz0;
		double camz  = rotm[6]*camx0+rotm[8]*camz0;
		double camvx = rotm[0]*camvx0+rotm[1]*camvy0+rotm[2]*camvz0;
		double camvy = rotm[3]*camvx0+rotm[4]*camvy0+rotm[5]*camvz0;
		double camvz = rotm[6]*camvx0+rotm[7]*camvy0+rotm[8]*camvz0;
		double camnorm =
		    java.lang.Math.sqrt(camvx*camvx+camvy*camvy+camvz*camvz);
		int n;
		double simpr = 0;
		double simpg = 0;
		// number of simpson's rule samples = 14
		int nmax = 14;
		// calculate intersections with planes containing box edges
		double tx1 = (-boxhalfwidth-camx)/camvx;
		double tx2 = ( boxhalfwidth-camx)/camvx;
		double ty1 = (-boxhalfheight-camy)/camvy;
		double ty2 = ( boxhalfheight-camy)/camvy;
		double tz1 = (-1-camz)/camvz;
		double tz2 = ( 1-camz)/camvz;
		// calculate portion of line that intersects box
		double mint =
		    max(min(tx1, tx2),
			max(min(ty1, ty2), min(tz1, tz2)))+.001;
		double maxt =
		    min(max(tx1, tx2),
			min(max(ty1, ty2), max(tz1, tz2)))-.001;
		if (maxt &lt; mint) {
		    // doesn't hit box
		    func[i][j][0] = func[i][j][1] = 0;
		    continue;
		}
		// sample evenly along intersecting portion
		double tstep = (maxt-mint)/(sampleCount-1);
		double pathlen = (maxt-mint)*camnorm;
		double xmult = maxTerms/boxwidth;
		double ymult = maxTerms/boxheight;
		double zmult = maxTerms/2.;
		for (n = 0; n &lt; sampleCount; n++) {
		    double t = mint+n*tstep;
		    double xx = camx+camvx*t;
		    double yy = camy+camvy*t;
		    double zz = camz+camvz*t;
		    // find grid element that contains sampled point
		    int xxi = (int) ((xx+boxhalfwidth )*xmult);
		    int yyi = (int) ((yy+boxhalfheight)*ymult);
		    int zzi = (int) ((zz+1)*zmult);
		    double f = data[xxi][yyi][zzi];
		    if (f &lt; 0) {
			f = java.lang.Math.abs(f);
			simpr += sampleMult[n] * f;
		    } else
			simpg += sampleMult[n] * f;
		}
		simpr *= pathlen/n;
		simpg *= pathlen/n;
		func[i][j][0] = simpr;
		func[i][j][1] = simpg;
	    }
    }

    int sign(double x) {
	return x &lt; 0 ? -1 : 1;
    }

    public void paint(Graphics g) {
	cv.repaint();
    }

    public void updateModeBox(Graphics realg) {
	Graphics g = null;
	if (winSize == null || winSize.width == 0)
	    return;
	boolean mis = memoryImageSourceCheck.getState();
	g = dbimage.getGraphics();
	g.setColor(cv.getBackground());
	g.fillRect(0, 0, winSize.width, winSize.height);
	g.setColor(cv.getForeground());

	boolean allQuiet = true;
	if (!stoppedCheck.getState()) {
	    int val = speedBar.getValue();
	    double tadd = val*(.1/16);
	    t += tadd;
	    if (modeCount &gt; 0)
		allQuiet = false;
	}
	
	int i, j, k;
	// evolve modes forward in time
	for (i = 0; i != modeCount; i++) {
	    Mode m = modes[i];
	    m.phasecoef = (m.omega*t+m.phasecoefadj) % (2*pi);
	    m.phasecoefcos = java.lang.Math.cos(m.phasecoef);
	    m.phasemult = m.phasecoefcos * m.magcoef;
	}
	if (modeCount != 0)
	    computeFunction();

	colorMult = brightnessBar.getValue() * 3;
	int winw = view3d.width;
	int winh = view3d.height;
	if (modeCount &gt; 0) {
	    for (i = 0; i != gridSizeX; i++)
		for (j = 0; j != gridSizeY; j++) {
		    int x = i*winw/gridSizeX;
		    int y = j*winh/gridSizeY;
		    int x2 = (i+1)*winw/gridSizeX;
		    int y2 = (j+1)*winh/gridSizeY;
		    int colval = 0xFF000000 +
			(getColorValue(i, j, 0) &lt;&lt; 16) |
			(getColorValue(i, j, 1) &lt;&lt; 8);
		    if (mis) {
			int l;
			for (k = x; k &lt; x2; k++)
			    for (l = y; l &lt; y2; l++)
				pixels[k+l*view3d.width] = colval;
		    } else {
			g.setColor(new Color(colval));
			g.fillRect(x, y, x2-x, y2-y);
		    }
		}
	    if (mis) {
		Image dbimage2 = cv.createImage(imageSource);
		g.drawImage(dbimage2, 0, 0, this);
	    }
	}
	g.setColor(Color.white);
	// find which sides of box are visible and draw the lines
	// bounding them.  Here we step through all 8 vertices on the box
	// and draw a line connecting each vertex to each adjacent vertex
	// if one of the faces adjacent to that line is visible.
	for (i = 0; i != 8; i++) {
	    // here we assume the box is cube (with vertices at (+-1,+-1,+-1))
	    // because the dimensions don't affect visibility.
	    int sign1 = ((i &amp; 1) == 0) ? -1 : 1;
	    int sign2 = ((i &amp; 2) == 0) ? -1 : 1;
	    int sign3 = ((i &amp; 4) == 0) ? -1 : 1;
	    if (sign1 == -1 &amp;&amp;
		  (visibleFace(0, sign2, 0) || visibleFace(0, 0, sign3))) {
		// draw a line from (-1, sign2, sign3) to (1, sign2, sign3)
		// if one of the adjacent faces is visible
		map3d(-1, sign2, sign3, xpoints, ypoints, 0);
		map3d( 1, sign2, sign3, xpoints, ypoints, 1);
		g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
	    }
	    if (sign2 == -1 &amp;&amp;
		  (visibleFace(sign1, 0, 0) || visibleFace(0, 0, sign3))) {
		// draw a line from (sign1, -1, sign3) to (sign1, 1, sign3)
		// etc.
		map3d(sign1, -1, sign3, xpoints, ypoints, 0);
		map3d(sign1,  1, sign3, xpoints, ypoints, 1);
		g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
	    }
	    if (sign3 == -1 &amp;&amp; 
		  (visibleFace(sign1, 0, 0) || visibleFace(0, sign2, 0))) {
		map3d(sign1, sign2, -1, xpoints, ypoints, 0);
		map3d(sign1, sign2,  1, xpoints, ypoints, 1);
		g.drawLine(xpoints[0], ypoints[0], xpoints[1], ypoints[1]);
	    }
	}
	g.setColor(Color.black);
	g.fillRect(0, view3d.height,
		   winSize.width, winSize.height-view3d.height);
	for (i = 0; i != maxDispCoefs; i++)
	    drawFrequencies(g, i);

	if (viewSpectrum != null) {
	    // draw spectrum
	    // modes can be identified either individually or by a
	    // frequency range.  We highlight whichever ones are selected.
	    double selw = (selectedCoefX == -1) ? 0 :
		getOmega(selectedCoefX, selectedCoefY, selectedCoefZ);
	    int selx = (int) (selw * spectrumSpacing);
	    int selmin = (int) (selectedMinOmega * spectrumSpacing);
	    int selmax = (int) (selectedMaxOmega * spectrumSpacing);
	    int ym = viewSpectrum.height - 10;
	    int y = viewSpectrum.y + viewSpectrum.height - 5;
	    for (i = 1; i != winSize.width; i++) {
		if (spectrum[i] == 0)
		    continue;
		int h = (int) (ym * (.2+java.lang.Math.log(spectrum[i])/4));
		if (h &gt; ym)
		    h = ym;
		g.setColor((i == selx || (i &gt;= selmin &amp;&amp; i &lt; selmax))
			   ? Color.yellow : Color.gray);
		g.drawLine(i, y, i, y-h);
	    }
	}

	if (selectedCoefX != -1) {
	    String s = "Selected mode = (" + selectedCoefX + "," +
		selectedCoefY + "," + selectedCoefZ + ")";
	    FontMetrics fm = g.getFontMetrics();
	    g.setColor(Color.yellow);
	    int y = view3d.y + view3d.height - fm.getDescent() - 2;
	    g.drawString(s, (winSize.width-fm.stringWidth(s))/2, y);
	}
	realg.drawImage(dbimage, 0, 0, this);
	if (!allQuiet)
	    cv.repaint(pause);
    }

    // see if the face containing (nx, ny, nz) is visible.
    boolean visibleFace(int nx, int ny, int nz) {
	double viewx = viewDistance*rotmatrix[2];
	double viewy = viewDistance*rotmatrix[5];
	double viewz = viewDistance*rotmatrix[8];
	return (nx-viewx)*nx+(ny-viewy)*ny+(nz-viewz)*nz &lt; 0;
    }

    // map 3-d point (x,y,z) to screen, storing coordinates
    // in xpoints[pt],ypoints[pt]
    void map3d(double x, double y, double z,
	       int xpoints[], int ypoints[], int pt) {
	x *= boxwidth/2;
	y *= boxheight/2;
	double rotm[] = rotmatrix;
	double realx =               x*rotm[0] + y*rotm[3] + z*rotm[6];
	double realy =               x*rotm[1] + y*rotm[4] + z*rotm[7];
	double realz = viewDistance-(x*rotm[2] + y*rotm[5] + z*rotm[8]);
	double scalex = view3d.width*zoom/2;
	double scaley = view3d.height*zoom/2;
	double aratio = view3d.width/(double) view3d.height;
	// preserve aspect ratio regardless of window dimensions
	if (aratio &lt; 1)
	    scaley *= aratio;
	else
	    scalex /= aratio;
	xpoints[pt] = view3d.width /2 + (int) (scalex*realx/realz);
	ypoints[pt] = view3d.height/2 + (int) (scaley*realy/realz);
    }

    void drawFrequencies(Graphics g, int z) {
	// draw one frequency grid for a particular z
	Rectangle view = viewFreq[z];
	int termWidth = getTermWidth();
	g.setColor(Color.white);
	int starti = 0;
	int i, j, x, y;
	for (i = starti; i &lt;= maxDispCoefs; i++) {
	    x = i*termWidth;
	    g.drawLine(view.x+starti*termWidth,   x+view.y,
		       view.x+termWidth*maxDispCoefs, x+view.y);
	    g.drawLine(view.x+x, view.y+starti*termWidth,
		       view.x+x, view.y+termWidth*maxDispCoefs);
	}
	int rcol = 0x00010000;
	int gcol = 0x00000100;
	for (i = 0; i != modeCount; i++) {
	    Mode m = modes[i];
	    if (m.z != z)
		continue;
	    x = view.x+m.x*termWidth;
	    y = view.y+m.y*termWidth;
	    int val = logcoef(m.magcoef);
	    if (val &lt; -255)
		val = -255;
	    if (val &gt; 255)
		val = 255;
	    if (val &lt; 0)
		g.setColor(new Color(0xFF000000 + rcol * -val));
	    else
		g.setColor(new Color(0xFF000000 + gcol * val));
	    g.fillRect(x+1, y+1, termWidth-1, termWidth-1);
	    int phx = (int) (m.phasecoefadj * termWidth * (1/(pi*2)));
	    if (phx &gt; 0) {
		// show phase line
		g.setColor(Color.blue);
		g.drawLine(x+phx, y+1,
			   x+phx, y+termWidth);
	    }
	}
	if (selectedCoefX != -1) {
	    // draw yellow square around degenerate modes
	    double selOmega =
		getOmega(selectedCoefX, selectedCoefY, selectedCoefZ);
	    g.setColor(Color.yellow);
	    for (i = starti; i != maxDispCoefs; i++)
		for (j = starti; j != maxDispCoefs; j++) {
		    x = view.x+i*termWidth;
		    y = view.y+j*termWidth;
		    if (getOmega(i, j, z) == selOmega)
			g.drawRect(x, y, termWidth, termWidth);
		}
	}
	if (selectedMinOmega &gt; 0 &amp;&amp; selectedMaxOmega &gt; 0) {
	    // draw yellow square around modes in selected range
	    g.setColor(Color.yellow);
	    for (i = starti; i != maxDispCoefs; i++)
		for (j = starti; j != maxDispCoefs; j++) {
		    x = view.x+i*termWidth;
		    y = view.y+j*termWidth;
		    double w = getOmega(i, j, z);
		    if (w &gt;= selectedMinOmega &amp;&amp; w &lt; selectedMaxOmega)
			g.drawRect(x, y, termWidth, termWidth);
		}
	}
    }

    double logep2 = 0;
    int logcoef(double x) {
	double ep2 = epsilon2;
	int sign = (x &lt; 0) ? -1 : 1;
	x *= sign;
	if (x &lt; ep2)
	    return 0;
	if (logep2 == 0)
	    logep2 = -java.lang.Math.log(2*ep2);
	return (int) (255 * sign * (java.lang.Math.log(x+ep2)+logep2)/logep2);
    }

    int getColorValue(int i, int j, int k) {
	int val = (int) (func[i][j][k] * colorMult);
	if (val &gt; 255)
	    val = 255;
	return val;
    }

    public void componentHidden(ComponentEvent e){}
    public void componentMoved(ComponentEvent e){}
    public void componentShown(ComponentEvent e) {
	cv.repaint();
    }

    public void componentResized(ComponentEvent e) {
	handleResize();
	cv.repaint(pause);
    }
    public void actionPerformed(ActionEvent e) {
	if (e.getSource() == clearButton) {
	    while (modeCount &gt; 0)
		deleteMode(0);
	    cv.repaint();
	}
    }

    public void adjustmentValueChanged(AdjustmentEvent e) {
	if (e.getSource() == widthBar || e.getSource() == heightBar)
	    setWidthHeight();
	if (e.getSource() == resolutionBar)
	    setMaxTerms();
	cv.repaint(pause);
    }

    // set the width and/or height of box and adjust omegas to match
    void setWidthHeight() {
	boxwidth  = widthBar.getValue()  / 5.;
	boxheight = heightBar.getValue() / 5.;
	int i;
	for (i = 0; i != modeCount; i++) {
	    Mode m = modes[i];
	    m.omega = getOmega(m.x, m.y, m.z);
	}
	calcSpectrum();
    }

    void calcSpectrum() {
	int i, j, k;
	if (winSize == null)
	    return;
	spectrum = new int[winSize.width];
	for (i = 0; i != maxDispCoefs; i++)
	    for (j = 0; j != maxDispCoefs; j++)
		for (k = 0; k != maxDispCoefs; k++) {
		    double w = getOmega(i, j, k);
		    int x = (int) (w*spectrumSpacing);
		    if (x &gt;= winSize.width)
			continue;
		    spectrum[x]++;
		}
    }

    public void mouseDragged(MouseEvent e) {
	dragging = true;
	oldDragX = dragX;
	oldDragY = dragY;
	dragX = e.getX(); dragY = e.getY();
	edit(e);
    }
    public void mouseMoved(MouseEvent e) {
	if ((e.getModifiers() &amp; MouseEvent.BUTTON1_MASK) != 0) {
	    if (selection != -1) {
		dragging = true;
	    }
	    return;
	}
	int x = e.getX();
	int y = e.getY();
	oldDragX = dragX;
	oldDragY = dragY;
	dragX = x; dragY = y;
	int oldCoefX = selectedCoefX;
	int oldCoefY = selectedCoefY;
	int oldCoefZ = selectedCoefZ;
	selectedCoefX = -1;
	selectedCoefY = -1;
	selectedCoefZ = -1;
	selection = 0;
	selectedMinOmega = selectedMaxOmega = 0;
	int i;
	if (view3d.inside(x,y))
	    selection = SEL_3D;
	if (viewSpectrum != null &amp;&amp; viewSpectrum.inside(x,y)) {
	    selection = SEL_SPECTRUM;
	    selectedMinOmega = (x-2)/(double) spectrumSpacing;
	    selectedMaxOmega = (x+2)/(double) spectrumSpacing;
	}
	for (i = 0; i != maxDispCoefs; i++)
	    if (viewFreq[i].inside(x, y)) {
		int termWidth = getTermWidth();
		Rectangle vf = viewFreq[i];
		selectedCoefX = (x-vf.x)/termWidth;
		selectedCoefY = (y-vf.y)/termWidth;
		selectedCoefZ = i;
		if (selectedCoefX &gt;= maxDispCoefs)
		    selectedCoefX = -1;
		if (selectedCoefY &gt;= maxDispCoefs)
		    selectedCoefX = -1;
		if (selectedCoefX != -1)
		    selection = SEL_MAG;
	    }
	if (selectedCoefX != oldCoefX || selectedCoefY != oldCoefY ||
	    selectedCoefZ != oldCoefZ)
	    cv.repaint(pause);
    }
    public void mouseClicked(MouseEvent e) {
	if (selection == SEL_MAG)
	    editMagClick();
    }
    public void mouseEntered(MouseEvent e) {
    }
    public void mouseExited(MouseEvent e) {
	if (!dragging &amp;&amp; selection != -1) {
	    selectedCoefX = selectedCoefY = selectedCoefZ = -1;
	    cv.repaint(pause);
	}
    }
    public void mousePressed(MouseEvent e) {
	if ((e.getModifiers() &amp; MouseEvent.BUTTON1_MASK) == 0)
	    return;
	oldDragX = dragStartX = e.getX();
	oldDragY = dragStartY = e.getY();
	dragZoomStart = zoom;
	if (selectedCoefX != -1) {
	    Mode m = findSelectedMode();
	    magDragStart = m.magcoef;
	}
	dragging = true;
	edit(e);
    }
    public void mouseReleased(MouseEvent e) {
	if (dragging)
	    cv.repaint();
	dragging = false;
    }
    public void itemStateChanged(ItemEvent e) {
	if (e.getItemSelectable() == spectrumCheck)
	    setupDisplay();
	cv.repaint(pause);
    }
    public boolean handleEvent(Event ev) {
        if (ev.id == Event.WINDOW_DESTROY) {
            applet.destroyFrame();
            return true;
        }
        return super.handleEvent(ev);
    }

    void edit(MouseEvent e) {
	if (selection == SEL_NONE)
	    return;
	int x = e.getX();
	int y = e.getY();
	switch (selection) {
	case SEL_MAG:      editMag(x, y);   break;
	case SEL_3D:       edit3d(x, y);    break;
	}
    }

    void edit3d(int x, int y) {
	if (modeChooser.getSelectedIndex() == MODE_ANGLE) {
	    int xo = oldDragX-x;
	    int yo = oldDragY-y;
	    rotate(xo/40., yo/40.);
	    cv.repaint(pause);
	} else if (modeChooser.getSelectedIndex() == MODE_ZOOM) {
	    int xo = x-dragStartX;
	    zoom = dragZoomStart + xo/20.;
	    if (zoom &lt; .1)
		zoom = .1;
	    cv.repaint(pause);
	}
    }

    void editMag(int x, int y) {
	if (selectedCoefX == -1)
	    return;
	double coef = (dragStartY-y)/20.+magDragStart;
	if (coef &lt; -1)
	    coef = -1;
	if (coef &gt; 1)
	    coef = 1;
	double pcoef = (x-dragStartX)/10.;
	if (pcoef &lt; 0)
	    pcoef = 0;
	if (pcoef &gt; 2*pi)
	    pcoef = 2*pi;
	Mode m = findSelectedMode();
	if (m.magcoef == coef &amp;&amp; m.phasecoefadj == pcoef)
	    return;
	m.magcoef = coef;
	m.phasecoefadj = pcoef;
	cv.repaint(pause);
    }

    void editMagClick() {
	if (selectedCoefX == -1)
	    return;
	Mode m = findSelectedMode();
	if (magDragStart &lt; .5)
	    m.magcoef = 1;
	else
	    m.magcoef = 0;
	m.phasecoefadj = 0;
	if (m.magcoef == 0)
	    deleteMode(m);
	cv.repaint(pause);
    }


    // generate 3-d dataset
    void genData(boolean fixed) {
	double q = 3.14159265/maxTerms;
	int x, y, z, mi;
	for (mi = 0; mi != modeCount; mi++) {
	    Mode m = modes[mi];
	    if (m.tableSize != maxTerms) {
		m.xtable = new double[maxTerms];
		m.ytable = new double[maxTerms];
		m.ztable = new double[maxTerms];
		m.tableSize = maxTerms;
	    }
	    // precompute cosine tables for speed
	    for (x = 0; x != maxTerms; x++) {
		m.xtable[x] = java.lang.Math.cos(x*m.x*q) * m.phasemult;
		m.ytable[x] = java.lang.Math.cos(x*m.y*q);
		m.ztable[x] = java.lang.Math.cos(x*m.z*q);
	    }
	    if (mi == 0) 
		for (x = 0; x != maxTerms; x++)
		    for (y = 0; y != maxTerms; y++)
			for (z = 0; z != maxTerms; z++)
			    data[x][y][z] =
				m.xtable[x]*m.ytable[y]*m.ztable[z];
	    else 
		for (x = 0; x != maxTerms; x++)
		    for (y = 0; y != maxTerms; y++)
			for (z = 0; z != maxTerms; z++)
			    data[x][y][z] +=
				m.xtable[x]*m.ytable[y]*m.ztable[z];
	}
    }

    void deleteMode(int i) {
	for (; i &lt; modeCount-1; i++) {
	    modes[i] = modes[i+1];
	}
	modeCount--;
    }

    void deleteMode(Mode m) {
	int i;
	for (i = 0; i != modeCount; i++)
	    if (modes[i] == m) {
		deleteMode(i);
		return;
	    }
    }

    Mode addMode(int x, int y, int z) {
	if (modeCount == maxModes) {
	    // one of the existing modes must be deleted; pick weakest one
	    int i;
	    double minmag = 1;
	    int minmagi = 0;
	    for (i = 0; i != modeCount; i++) {
		Mode m = modes[i];
		if (m.magcoef &lt; minmag) {
		    minmag = m.magcoef;
		    minmagi = i;
		}
	    }
	    deleteMode(minmagi);
	}
	Mode m = new Mode();
	m.x = x;
	m.y = y;
	m.z = z;
	m.magcoef = 0;
	m.phasecoef = 0;
	m.phasecoefcos = 1;
	m.phasecoefadj = 0;
	m.omega = getOmega(x, y, z);
	modes[modeCount++] = m;
	return m;
    }

    // get angular frequency of a particular mode
    double getOmega(int x, int y, int z) {
	return java.lang.Math.sqrt(x*x/(boxwidth*boxwidth)+
				   y*y/(boxheight*boxheight)+z*z/4.);
    }

    Mode findSelectedMode() {
	int i;
	for (i = 0; i != modeCount; i++) {
	    Mode m = modes[i];
	    if (selectedCoefX == m.x &amp;&amp; selectedCoefY == m.y &amp;&amp;
		selectedCoefZ == m.z)
		return m;
	}
	return addMode(selectedCoefX, selectedCoefY, selectedCoefZ);
    }

    class Mode {
	public int x, y, z;
	public double magcoef, phasecoef, phasecoefcos, phasemult,
	    phasecoefadj, omega;
	int tableSize;
	public double xtable[], ytable[], ztable[];
    };
}
</pre></body></html>