/*
Copyright 1997
Omar Hamoui
Talha Rizvi
*/

// 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;
}

}

