
// Import packages

import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Image;
import java.awt.Font;
import java.awt.FontMetrics;

// applet class

/* This part of the declarations contains the class that defines the
   control point objects. These control points contain the x, y, and z
   values necessary to compute the graph using the functions for Bezier surfaces.
*/

class ControlPoint extends Object {
	public int x;
	public int y;
	public double z;
	public static final int PT_SIZE = 4;

	public ControlPoint(int a, int b) {
		x = a;
		y = b;
	}

	public void SetZ(double a) {
		z = a;
	}

	public boolean within(int a, int b) {
		if (a >= x - PT_SIZE &&
			b >= y - PT_SIZE &&
			a <= x + PT_SIZE &&
			b <= y + PT_SIZE)
			return true;
		else
			return false;
	}
}

public class BezSurf extends java.applet.Applet {

	int dh, dv,dhx2,dvx2,xinc,yinc;
	int moving_point;
	int selected_point;
	Image buf;
	Graphics gbuf;

	// vars for 3d -> 2d transform
	double xform[] = new double[11];
	int hv[] = new int[2];
	double alt = 30, az = 20, mag = 30;
	double mag0 = 30;

	int mx0 = 0, my0 = 0;
	Font f = new Font("TimesRoman", Font.PLAIN, 10);
	Font f1 = new Font("TimesRoman", Font.PLAIN, 12);

	// arrays for surface coords - 3d
	double surfx[] = new double[10 * 10];
	double surfy[] = new double[10 * 10];
	double surfz[] = new double[10 * 10];

	ControlPoint points[] = new ControlPoint[25];

	// 2d
	int surfh[] = new int[10 * 10];
	int surfv[] = new int[10 * 10];
	double xp[] = new double[25];
	double yp[] = new double[25];
	double zp[] = new double[25];

	// slider params
	double zval = 0, zoom = 30;

	// slider data type
	/*
	slider[0] = min
	slider[1] = max
	slider[2] = val
	slider[3] = h
	slider[4] = v
	slider[5] = hit
	slider[6] = dv0
	slider[7] = name index
	*/

	// slider vars
	String snames[] = new String[3];
	double slider1[] = new double[8];
	double slider2[] = new double[8];

	/*
	these functions are used to convert the control points pixel position
	to its real number value for use in the Bezier surface equation and vice-versa
	*/
	public int real2pixelx(double realx) {
		int pixel;
		pixel = (int)(realx*xinc + 5*xinc);
		return pixel;
	}

	public double pixel2realx(int pixel) {
		double realx;
		realx = (double)((pixel - 5*xinc)/xinc);
		return realx;
	}

	public int real2pixely(double realy) {
		int pixel;
		pixel = (int)(-(realy*yinc) + 5*yinc);
		return pixel;
	}

	public double pixel2realy(int pixel){
		double realy;
		realy = (double)((-pixel + 5*yinc)/yinc);
		return realy;
	}

	/*
	init() sets up the applet the first time through and is called whenever the reset button
	is hit in order to put the applet back at the starting point.
	*/
	public void init() {
		dhx2 = this.size().width;
		dv = this.size().height;
		dh = dhx2/2;
		xinc = dh/12;
		yinc = dv/12;

		zoom = 30;
		mag = zoom;
		zval = 0;
		points[0] = new ControlPoint(real2pixelx(-2.0),real2pixely(2.0));
		points[1] = new ControlPoint(real2pixelx(-1.0),real2pixely(2.0));
		points[2] = new ControlPoint(real2pixelx( 0.0),real2pixely(2.0));
		points[3] = new ControlPoint(real2pixelx( 1.0),real2pixely(2.0));
		points[4] = new ControlPoint(real2pixelx( 2.0),real2pixely(2.0));

		points[5] = new ControlPoint(real2pixelx(-2.0),real2pixely(1.0));
		points[6] = new ControlPoint(real2pixelx(-1.0),real2pixely(1.0));
		points[7] = new ControlPoint(real2pixelx( 0.0),real2pixely(1.0));
		points[8] = new ControlPoint(real2pixelx( 1.0),real2pixely(1.0));
		points[9] = new ControlPoint(real2pixelx( 2.0),real2pixely(1.0));

		points[10] = new ControlPoint(real2pixelx(-2.0),real2pixely(0.0));
		points[11] = new ControlPoint(real2pixelx(-1.0),real2pixely(0.0));
		points[12] = new ControlPoint(real2pixelx( 0.0),real2pixely(0.0));
		points[13] = new ControlPoint(real2pixelx( 1.0),real2pixely(0.0));
		points[14] = new ControlPoint(real2pixelx( 2.0),real2pixely(0.0));

		points[15] = new ControlPoint(real2pixelx(-2.0),real2pixely(-1.0));
		points[16] = new ControlPoint(real2pixelx(-1.0),real2pixely(-1.0));
		points[17] = new ControlPoint(real2pixelx( 0.0),real2pixely(-1.0));
		points[18] = new ControlPoint(real2pixelx( 1.0),real2pixely(-1.0));
		points[19] = new ControlPoint(real2pixelx( 2.0),real2pixely(-1.0));

		points[20] = new ControlPoint(real2pixelx(-2.0),real2pixely(-2.0));
		points[21] = new ControlPoint(real2pixelx(-1.0),real2pixely(-2.0));
		points[22] = new ControlPoint(real2pixelx( 0.0),real2pixely(-2.0));
		points[23] = new ControlPoint(real2pixelx( 1.0),real2pixely(-2.0));
		points[24] = new ControlPoint(real2pixelx( 2.0),real2pixely(-2.0));

		// create offscreen drawing
		buf = createImage(dhx2, dv);
		gbuf = buf.getGraphics();

		// set up initial 3d coordinates, surface, and 2d representation
		reset_coords(0.0, 0.0, 0.0, alt, az, mag, xform);
		calc_surf();
		project_surf();

		// initialize sliders (not currently a separate class)
		snames[0] = "Zoom";
		snames[1] = "Z value";

		slider1[0] = 0;
		slider1[1] = 60;
		slider1[2] = zoom;
		slider1[3] = dh - 20;
		slider1[4] = 19;
		slider1[5] = 0;
		slider1[6] = 0;
		slider1[7] = 0;

		slider2[0] = -5;
		slider2[1] = 5;
		slider2[2] = zval;
		slider2[3] = 2*dh - 20;
		slider2[4] = 19;
		slider2[5] = 0;
		slider2[6] = 0;
		slider2[7] = 1;

	}

	private int findPoint(int a, int b) {
		// Scan the list of control points to find out which (if any) point
		// contains the coordinates: a,b.
		// If a point is found, return the point's index, otherwise return -1
		int max = 24;

		for(int i = 0; i <= max; i++) {
			ControlPoint pnt = points[i];
			if (pnt.within(a,b)) {
				return i;
			}
		}
		return -1;
	}

	/*
	calc 3d surface values from control point values. We found it
	faster to hard code the Bezier blending function for 25 points here rather
	than to use an iterative method.  It is ugly however it is more efficient.
	*/
	public void calc_surf() {
		int count = 0;

	for (int q =0; q<=24;q++) {
		xp[q] = pixel2realx(points[q].x); yp[q] = pixel2realy(points[q].y); zp[q] = points[q].z;
	}

	for (double u = 0; u <= 1; u += 0.1111111111)
		for (double v = 0; v <= 1; v += 0.1111111111) {
			count++;
			surfx[count-1] =
			xp[0]*Math.pow(1 - u, 4)*Math.pow(1 - v, 4) +
			xp[1]*4*Math.pow(1 - u, 3)*u*Math.pow(1 - v, 4)+
			xp[2]*6*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(1 - v, 4) +
			xp[3]*4*Math.pow(u, 3)*(1-u)*Math.pow(1 - v, 4)+
			xp[4]*Math.pow(u, 4)*Math.pow(1 - v, 4)+
			xp[5]*4*Math.pow(1 - u, 4)*Math.pow(1 - v, 3)*v+
			xp[6]*16*Math.pow(1 - u, 3)*u*Math.pow(1 - v, 3)*v+
			xp[7]*24*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(1 - v, 3)*v+
			xp[8]*16*Math.pow(u, 3)*(1-u)*Math.pow(1 - v, 3)*v+
			xp[9]*4*Math.pow(u, 4)*Math.pow(1 - v, 3)*v +
			xp[10]*6*Math.pow(1-u, 4)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			xp[11]*24*Math.pow(1 - u, 3)*u*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			xp[12]*36*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			xp[13]*24*Math.pow(u, 3)*(1-u)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			xp[14]*6*Math.pow(u, 4)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			xp[15]*4*Math.pow(1-u, 4)*(1-v)*Math.pow(v, 3)+
			xp[16]*16*Math.pow(1 - u, 3)*u*(1-v)*Math.pow(v, 3)+
			xp[17]*24*Math.pow(1 - u, 2)*Math.pow(u, 2)*(1-v)*Math.pow(v, 3)+
			xp[18]*16*Math.pow(u, 3)*(1-u)*(1-v)*Math.pow(v, 3)+
			xp[19]*4*Math.pow(u, 4)*(1-v)*Math.pow(v, 3)+
			xp[20]*Math.pow(1-u, 4)*Math.pow(v, 4)+
			xp[21]*4*Math.pow(1-u, 3)*u*Math.pow(v, 4)+
			xp[22]*6*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(v, 4)+
			xp[23]*4*Math.pow(u, 3)*(1-u)*Math.pow(v, 4)+
			xp[24]*Math.pow(u, 4)*Math.pow(v, 4);

			surfy[count-1] =
			yp[0]*Math.pow(1 - u, 4)*Math.pow(1 - v, 4) +
			yp[1]*4*Math.pow(1 - u, 3)*u*Math.pow(1 - v, 4)+
			yp[2]*6*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(1 - v, 4) +
			yp[3]*4*Math.pow(u, 3)*(1-u)*Math.pow(1 - v, 4)+
			yp[4]*Math.pow(u, 4)*Math.pow(1 - v, 4)+
			yp[5]*4*Math.pow(1 - u, 4)*Math.pow(1 - v, 3)*v+
			yp[6]*16*Math.pow(1 - u, 3)*u*Math.pow(1 - v, 3)*v+
			yp[7]*24*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(1 - v, 3)*v+
			yp[8]*16*Math.pow(u, 3)*(1-u)*Math.pow(1 - v, 3)*v+
			yp[9]*4*Math.pow(u, 4)*Math.pow(1 - v, 3)*v +
			yp[10]*6*Math.pow(1-u, 4)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			yp[11]*24*Math.pow(1 - u, 3)*u*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			yp[12]*36*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			yp[13]*24*Math.pow(u, 3)*(1-u)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			yp[14]*6*Math.pow(u, 4)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			yp[15]*4*Math.pow(1-u, 4)*(1-v)*Math.pow(v, 3)+
			yp[16]*16*Math.pow(1 - u, 3)*u*(1-v)*Math.pow(v, 3)+
			yp[17]*24*Math.pow(1 - u, 2)*Math.pow(u, 2)*(1-v)*Math.pow(v, 3)+
			yp[18]*16*Math.pow(u, 3)*(1-u)*(1-v)*Math.pow(v, 3)+
			yp[19]*4*Math.pow(u, 4)*(1-v)*Math.pow(v, 3)+
			yp[20]*Math.pow(1-u, 4)*Math.pow(v, 4)+
			yp[21]*4*Math.pow(1-u, 3)*u*Math.pow(v, 4)+
			yp[22]*6*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(v, 4)+
			yp[23]*4*Math.pow(u, 3)*(1-u)*Math.pow(v, 4)+
			yp[24]*Math.pow(u, 4)*Math.pow(v, 4);

			surfz[count-1] =
			zp[0]*Math.pow(1 - u, 4)*Math.pow(1 - v, 4) +
			zp[1]*4*Math.pow(1 - u, 3)*u*Math.pow(1 - v, 4)+
			zp[2]*6*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(1 - v, 4) +
			zp[3]*4*Math.pow(u, 3)*(1-u)*Math.pow(1 - v, 4)+
			zp[4]*Math.pow(u, 4)*Math.pow(1 - v, 4)+
			zp[5]*4*Math.pow(1 - u, 4)*Math.pow(1 - v, 3)*v+
			zp[6]*16*Math.pow(1 - u, 3)*u*Math.pow(1 - v, 3)*v+
			zp[7]*24*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(1 - v, 3)*v+
			zp[8]*16*Math.pow(u, 3)*(1-u)*Math.pow(1 - v, 3)*v+
			zp[9]*4*Math.pow(u, 4)*Math.pow(1 - v, 3)*v +
			zp[10]*6*Math.pow(1-u, 4)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			zp[11]*24*Math.pow(1 - u, 3)*u*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			zp[12]*36*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			zp[13]*24*Math.pow(u, 3)*(1-u)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			zp[14]*6*Math.pow(u, 4)*Math.pow(1 - v, 2)*Math.pow(v, 2)+
			zp[15]*4*Math.pow(1-u, 4)*(1-v)*Math.pow(v, 3)+
			zp[16]*16*Math.pow(1 - u, 3)*u*(1-v)*Math.pow(v, 3)+
			zp[17]*24*Math.pow(1 - u, 2)*Math.pow(u, 2)*(1-v)*Math.pow(v, 3)+
			zp[18]*16*Math.pow(u, 3)*(1-u)*(1-v)*Math.pow(v, 3)+
			zp[19]*4*Math.pow(u, 4)*(1-v)*Math.pow(v, 3)+
			zp[20]*Math.pow(1-u, 4)*Math.pow(v, 4)+
			zp[21]*4*Math.pow(1-u, 3)*u*Math.pow(v, 4)+
			zp[22]*6*Math.pow(1 - u, 2)*Math.pow(u, 2)*Math.pow(v, 4)+
			zp[23]*4*Math.pow(u, 3)*(1-u)*Math.pow(v, 4)+
			zp[24]*Math.pow(u, 4)*Math.pow(v, 4);
		}
	}

	// handle mouse click
	// a function to test if they click inside the control point square.
	public boolean InsideSquare(int x,int y){
		if ((x>dh+xinc)&&(x<dh+11*xinc)&&(y>yinc)&&(y<11*yinc))
		return true;
		else
		return false;
	}

	// a function to test if they click the reset button.
	public boolean InReset(int x, int y) {
		if ((x>dh+5*xinc)&&(y>11*yinc+10)&&(x<dh+7*xinc)&&(y<11*yinc+10+yinc/2))
		return true;
		else
		return false;
	}

	/*
	here we must do a number of things. If they hit the reset
	button, we will reset the board.  If they hit inside the control point square,
	we will see if they selected a point by calling findPoint.  Otherwise, we will see
	if they are trying to rotate the graph or drag a slider and we will react accordingly.
	*/
	public boolean mouseDown(Event evt, int x, int y) {
		if (InReset(x,y))
			init();
		else
			if (x>dh)
			if (InsideSquare(x,y)) {
			moving_point = findPoint(x-dh-xinc, y-yinc);
			selected_point = moving_point;
			slider2[2] = points[selected_point].z;
		}
		else
			slider_hit(slider2, x, y);
		else {	// save current mouse pos and magnification
			mx0 = x;
			my0 = y;
			mag0 = mag;

			// check for click in sliders
			slider_hit(slider1, x, y);
		}

		return true;
	}

	/*
	 handle dragging (rotate or zoom or sliders or drag control points)
	note that surface computation is synchronized with drawing.  Also, if they drag a
	point, then we will re - compute the surface after they are finished draging the point.
	*/
	public synchronized boolean mouseDrag(Event evt, int x, int y) {

	// track mouse in slider and update surface
	if (x>dh)
		if (InsideSquare(x,y)&&(moving_point >=0)) {
			ControlPoint pnt = points[moving_point];
			pnt.x = x-dh-xinc;
			pnt.y = y-yinc;
		}

		// this slider handles the z value of the points
		else if (track_slider(slider2, x, y)) {
			zval = slider2[2];
			points[selected_point].z=zval;
		}
		else ;

		// this slider handles the zooming.
		else if (track_slider(slider1, x, y)) {
			zoom = slider1[2];
			mag =zoom;
			reset_coords(0.0, 0.0, 0.0, alt, az, mag, xform);
			project_surf();
		}
		else {	//do rotation
			// change altitude & azimuth

			alt += y - my0;
			az += x - mx0;

			my0 = y;
			mx0 = x;

			// update coords, surface, and redraw

			reset_coords(0.0, 0.0, 0.0, alt, az, mag, xform);
			project_surf();
		}

		repaint();
		return true;
	}

	/*
	Once the mouse comes up off of a control point or the slider to set the z value,
	we recalculate and redisplay the surface.  We don't do this while they are dragging
	the point because it would be much too slow.
	*/
	public boolean mouseUp(Event evt, int x,int y) {
		if ((moving_point >=0)||(selected_point >= 0)) {
			moving_point = -1;
			calc_surf();
			project_surf();
			repaint();
		}
		return true;
	}

	// transform 3d surface to 2d
	public void project_surf() {
		for (int i = 0; i < 10; i ++)
			for (int j = 0; j < 10; j ++){
			int k = i + j * 10;

			xform_3to2d(surfx[k], surfy[k], surfz[k], xform, hv);
			surfh[k] = hv[0];
			surfv[k] = hv[1];
		}
	}

	// draw the second frame containing the control points.
	public void draw_frame2(Graphics g) {
		g.setFont(f1);
		g.setColor(Color.white);
		g.fillRect(dh,0,dh,dv);
		g.setColor(Color.black);
		g.drawRect(dh+xinc,yinc,10*xinc,10*yinc);
		g.drawRect(dh+5*xinc,11*yinc+10,2*xinc,yinc/2);
		g.drawLine(dh+dh/2,yinc,dh+dh/2,dv-yinc-5);
		g.drawLine(dh+xinc,dv/2,dhx2-xinc-5,dv/2);
		g.drawString("Reset",dh+5*xinc+17,11*yinc+22);
		g.drawString("drag points on the x,y plane - use slider to set z",dh+2*xinc+3,25);
		g.setFont(f);
	}

	//This draws the first frame which contains the surface.
	public void draw_frame(Graphics g) {
		g.setColor(Color.black);
		g.fillRect(0, 0, dh, dv);

		g.setFont(f);

		// transform 3d axis positions to 2d
		xform_3to2d(0.0, 0.0, 0.0, xform, hv);
		int h0 = hv[0];
		int v0 = hv[1];
		xform_3to2d(1.0, 0.0, 0.0, xform, hv);
		int h1 = hv[0];
		int v1 = hv[1];
		xform_3to2d(0.0, 1.0, 0.0, xform, hv);
		int h2 = hv[0];
		int v2 = hv[1];
		xform_3to2d(0.0, 0.0, 1.0, xform, hv);
		int h3 = hv[0];
		int v3 = hv[1];

		// draw axes
		g.setColor(Color.yellow);
		g.drawLine(h0, v0, h1, v1);
		g.drawLine(h0, v0, h2, v2);
		g.drawLine(h0, v0, h3, v3);

		// draw axis labels
		xform_3to2d(1.1, 0.0, 0.0, xform, hv);
		g.drawString("X", hv[0], hv[1]);
		xform_3to2d(0.0, 1.1, 0.0, xform, hv);
		g.drawString("Y", hv[0], hv[1]);
		xform_3to2d(0.0, 0.0, 1.1, xform, hv);
		g.drawString("Z", hv[0], hv[1]);

		g.drawString("Drag to rotate", 2, dv - 5);

		FontMetrics fm = getFontMetrics(f);
	}

	//draw control points
	public void draw_points(Graphics g) {
		g.setColor(Color.red);
		for (int i=0; i <= 24; i++) {
			if (i==selected_point)
			   g.setColor(Color.blue);
			else
			   g.setColor(Color.red);
			ControlPoint p = points[i];
			g.drawRect(p.x-p.PT_SIZE+dh+xinc, p.y-p.PT_SIZE+yinc, p.PT_SIZE*2, p.PT_SIZE*2);
			g.drawString(String.valueOf(i),p.x+p.PT_SIZE+dh+xinc,p.y-p.PT_SIZE+yinc);
		}
	}

	// draw surface
	public void draw_surf(Graphics g) {
		g.setColor(Color.green);

		// draw one set of lines

		for (int i = 0; i < 10; i ++)
			for (int j = 1; j < 10; j ++) {
				int k1 = i + (j - 1) * 10;
				int k2 = i + j * 10;

				g.drawLine(surfh[k1], surfv[k1], surfh[k2], surfv[k2]);
			}

		// draw other set of lines

		for (int j = 0; j < 10; j ++)
			for (int i = 1; i < 10; i ++) {
				int k1 = i - 1 + j * 10;
				int k2 = i + j * 10;

				g.drawLine(surfh[k1], surfv[k1], surfh[k2], surfv[k2]);
			}
	}

	// do drawing
	// note that drawing is synchronized with surface computation
	public synchronized void paint(Graphics g) {
		draw_frame(gbuf);
		draw_surf(gbuf);
		draw_frame2(gbuf);
		draw_points(gbuf);

		draw_slider(slider1, gbuf);
		draw_slider(slider2, gbuf);

		// copy to screen

		g.drawImage(buf, 0, 0, this);
		//draw_points(g);
	}

	// overload for no flicker
	public void update(Graphics g) {
		paint(g);
	}

	// coord transform data structure
	/*
	xform[0] = x0
	xform[1] = y0
	xform[2] = z0
	xform[3] = mag
	xform[4] = h0
	xform[5] = v0
	xform[6] = cxh
	xform[7] = cyh
	xform[8] = cxv
	xform[9] = cyv
	xform[10] = czv
	*/

	// update transform based on current alt, az, mag
	public void reset_coords(double x0, double y0, double z0,
		double alt, double az, double mag, double xform[]) {
		xform[0] = x0;
		xform[1] = y0;
		xform[2] = z0;
		xform[3] = mag;

		xform[4] = dh / 2.0;
		xform[5] = dv / 2.0;

		double cos_alt = Math.cos(alt * Math.PI / 180.0);
		double sin_alt = Math.sin(alt * Math.PI / 180.0);
		double cos_az = Math.cos(az * Math.PI / 180.0);
		double sin_az = Math.sin(az * Math.PI / 180.0);

		xform[6] = sin_az;
		xform[7] = cos_az;

		xform[8] = -sin_alt * cos_az;
		xform[9] = sin_alt * sin_az;
		xform[10] = cos_alt;
	}

	// transform 3d to 2d by matrix multiplication
	public void xform_3to2d(double x, double y, double z, double xform[], int hv[]) {
		x = (x - xform[0]) * xform[3];
		y = (y - xform[1]) * xform[3];
		z = (z - xform[2]) * xform[3];

		hv[0] = (int) (xform[4] + xform[6] * x + xform[7] * y);
		hv[1] = (int) (xform[5] - xform[8] * x - xform[9] * y - xform[10] * z);
	}

	// draw a slider
	public void draw_slider(double slider[], Graphics g) {
		if (slider[7] == 0)
		g.setColor(Color.yellow);
		else
		g.setColor(Color.blue);

		int h1 = (int) slider[3];
		int v1 = (int) slider[4];
		int v2 = v1 + 60;

		g.drawLine(h1, v1, h1, v2);

		// get current thumb pos and draw
		int v3;
		if (slider[0] == slider[1]) v3 = (v1 + v2) / 2;
		else v3 = (int) (v2 - (v2 - v1) * (slider[2] - slider[0]) / (slider[1] - slider[0]));

		g.fillRect(h1 - 4, v3 - 4, 10, 10);

		g.setFont(f);
		FontMetrics fm = getFontMetrics(f);

		String s = String.valueOf(((int) (slider[2] * 100)) / 100.0);
		int w = fm.stringWidth(s);

		g.drawString(s, h1 - w / 2, v2 + 15);

		String s2 = snames[(int) slider[7]];
		int w2 = fm.stringWidth(s2);

		g.drawString(s2, h1 - w2 / 2, v1 - 7);
	}

	// check to see if mouse click is in slider
	public void slider_hit(double slider[], int x, int y) {
		slider[5] = 0;
		slider[6] = 0;

		int h1 = (int) slider[3];
		int v1 = (int) slider[4];
		int v2 = v1 + 60;

		int v3;
		if (slider[0] == slider[1]) v3 = (v1 + v2) / 2;
		else v3 = (int) (v2 - (v2 - v1) * (slider[2] - slider[0]) / (slider[1] - slider[0]));

		if (x < h1 - 4) return;
		if (x > h1 + 4) return;
		if (y < v3 - 4) return;
		if (y > v3 + 4) return;

		slider[5] = 1;
		slider[6] = y - v3;
	}

	// track mouse dragging in slider
	public boolean track_slider(double slider[], int x, int y) {
		// see if slider is "hit"
		if (slider[5] != 1) return false;

		int h1 = (int) slider[3];
		int v1 = (int) slider[4];
		int v2 = v1 + 60;

		y -= slider[6];
		if (y < v1) y = v1;
		if (y > v2) y = v2;

		// update slider value

		double val;
		if (slider[0] == slider[1]) val = slider[0];
		else val = slider[0] + ((v2 - y) * (slider[1] - slider[0])) / (v2 - v1);

		if (val < slider[0]) val = slider[0];
		if (val > slider[1]) val = slider[1];

		slider[2] = val;

		return true;
	}
}
