/******************************************************************************************
 * Surface Plotter version 1.10                                               Oct 14 1996 *
 * Surface Plotter version 1.20                                               Nov  8 1996 *
 *                                                                            Nov 12 1996 *
 *                                                                                        *
 * SurfaceCanvas.java: Canvas for Surface Plotter                                         *
 *                                                                                        *
 * Copyright (c) 1996 Yanto Suryono. All Rights Reserved.                                 *
 *                                                                                        *
 * Permission to use, copy, and distribute this software for NON-COMMERCIAL purposes      *
 * and without fee is hereby granted, provided that this copyright notice appears in all  *
 * copies and this software is not modified in any way                                    *
 *                                                                                        *
 * Please send bug reports and/or corrections/suggestions to                              *
 * Yanto Suryono <d0771@cranesv.egg.kushiro-ct.ac.jp>                                     *
 ******************************************************************************************/

//import java.awt.*;
//
import java.lang.*;
import javax.swing.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Event;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.*;

//import Constants;

class SurfaceCanvas extends JPanel 
    implements Runnable, MouseListener, MouseMotionListener, Constants {
    
    private ThreeDResponse applet;                  // the parent, Surface Plotter applet 
    private Image Buffer;                    // the backing buffer
    private Graphics BufferGC = null;        // the graphics context of backing buffer
    private boolean image_drawn;             // image drawn flag
    private Projector projector;             // the projector
    private Thread thread;                   // current thread
    private SurfaceVertex[][] vertex;        // vertices array
    private boolean data_available;          // data availability flag
    private boolean interrupted = false;     // interrupted flag
    private boolean critical;                // for speed up
    private int prevwidth, prevheight;       // hold canvas size  
    private float color;                     // color of surface
    private SurfaceVertex cop;               // center of projection
    
    // setting variables
    
    private int plot_mode;
    private int calc_divisions; 
    private boolean plotfunc1, plotfunc2, plotboth;
    private boolean isBoxed, isMesh, isScaleBox, 
	isDisplayXY, isDisplayZ, isDisplayGrids;
    private float xmin,xmax,ymin;
    private float ymax,zmin,zmax;
    
    // constants
    
    private final int WIREFRAME   = 0;
    private final int NORENDER    = 1;
    private final int SPECTRUM    = 2;
    private final int GRAYSCALE   = 3;
    private final int DUALSHADE   = 4;
    
    private final int TOP         = 0;
    private final int CENTER      = 1;
    
    // for splitting polygons
    
    private final int UPPER       =  1;
    private final int COINCIDE    =  0;
    private final int LOWER       = -1;
    
    
    /*--------------------------------------------------------------------------------                     
      Constructor for Surface Canvas
      --------------------------------------------------------------------------------*/  
    
    SurfaceCanvas(ThreeDResponse applet) {
	super();
	addMouseListener(this);
	addMouseMotionListener(this);
	this.applet = applet;
	image_drawn = false;
	data_available = false;
	thread = new Thread(this);
	prevwidth = prevheight = 0;
	projector = new Projector();
	projector.setDistance(70);
	projector.set2DScaling(15);
	projector.setRotationAngle(125);
	projector.setElevationAngle(10);
	SurfaceVertex.setProjector(projector);
	vertex = new SurfaceVertex[2][];
    }
    
    /*--------------------------------------------------------------------------------                     
      This will force SurfaceCanvas to re-plot surface when paint() method is called
      --------------------------------------------------------------------------------*/  
    
    public void destroyImage() {
	image_drawn = false;
    }
    
    /*--------------------------------------------------------------------------------                     
      These functions are called from surface applet when variables needed
      by SurfaceCanvas have changed
      --------------------------------------------------------------------------------*/  
    
    public void setRanges(float xmin, float xmax, float ymin, float ymax) {
	this.xmin = xmin;
	this.xmax = xmax;
	this.ymin = ymin;
	this.ymax = ymax;
    }
    
    public void setValuesArray(SurfaceVertex[][] vertex) {
	this.vertex = vertex;
    }
    
    public void setDataAvailability(boolean avail) {
	data_available = avail;
	is_data_available = avail;  // see Handlers for mouse input events section
    }
    
    /*--------------------------------------------------------------------------------                     
      Imports setting variables from surface applet.
      Called before doing any plotting operations
      --------------------------------------------------------------------------------*/  
    
    public void importVariables() {
	plot_mode = applet.getPlotMode();
	isBoxed = applet.isBoxed();
	isMesh = applet.isMesh();
	isScaleBox = applet.isScaleBox();
	isDisplayXY = applet.isDisplayXY();
	isDisplayZ = applet.isDisplayZ();
	isDisplayGrids = applet.isDisplayGrids();
	calc_divisions = applet.getCalcDivisions();
	plotfunc1 = applet.isPlotFunction1();
	plotfunc2 = applet.isPlotFunction2();
	plotboth = plotfunc1 && plotfunc2;
    }
    
    /*--------------------------------------------------------------------------------
      Handlers for mouse input events
      --------------------------------------------------------------------------------*/  
    
    private boolean is_data_available;   // holds the original data availability flag
    private boolean dragged;             // dragged flag
    private int     click_x, click_y;    // previous mouse cursor position
    
    public void mouseClicked(MouseEvent e) {
	
    	int x = e.getX();
    	int y = e.getY();
	
    	click_x = x;
    	click_y = y;
	
    }
    
    public void mouseReleased(MouseEvent e) {
	
	if (applet.isExpectDelay() && dragged) {
	    destroyImage(); 
	    data_available = is_data_available;
	    repaint(); 
	    dragged = false;
	}
	
    }
    
    public void mouseDragged(MouseEvent e) {
	int x = e.getX();
	int y = e.getY();
	float new_value = 0.0f;
	System.out.println("--->CANVAS Dragged! x="+x+"y="+y);
	if (!thread.isAlive() || !data_available) {
	    if (e.isControlDown()) {
		projector.set2D_xTranslation(
					     projector.get2D_xTranslation() + (x - click_x));
		projector.set2D_yTranslation(
					     projector.get2D_yTranslation() + (y - click_y));
	    }
	    else
		if (e.isShiftDown()) {
		    new_value = projector.get2DScaling() + 
			(y - click_y) * 0.5f;
		    if (new_value > 60.0f) new_value = 60.0f;
		    if (new_value < 2.0f) new_value = 2.0f;
		    projector.set2DScaling(new_value);
		}
		else {
		    new_value = projector.getRotationAngle() + (x - click_x);
		    while (new_value > 360) new_value -= 360;
		    while (new_value < 0) new_value += 360;
		    projector.setRotationAngle(new_value);
		    new_value = projector.getElevationAngle() + (y - click_y);
		    if (new_value > 90) new_value = 90;
		    else
			if (new_value < 0) new_value = 0;
		    projector.setElevationAngle(new_value);
		}
	    image_drawn = false;
	    if (!applet.isExpectDelay()) {
		repaint(); 
	    }
	    else {
		if (!dragged) {
		    is_data_available = data_available;
		    dragged = true;
		}
		data_available = false;
		repaint();
	    }    
	}
	click_x = x;
	click_y = y; 
    }
    
    public void mouseEntered(MouseEvent e) {
	;
    }
    public void mouseExited(MouseEvent e) {
	;
    }
    public void mousePressed(MouseEvent e) {}
    public void mouseMoved(MouseEvent e){}
    
    /*--------------------------------------------------------------------------------
      Bounding box, Ticks, Grids, Axes drawing routines
      --------------------------------------------------------------------------------*/  
    
    
    private int factor_x, factor_y;   // conversion factors 
    private int t_x, t_y, t_z;        // determines ticks density
    
    private void drawBoundingBox() {
	Point startingpoint, projection;
	
	startingpoint = projector.project(factor_x*10,factor_y*10,10);
	BufferGC.setColor(Color.black);
	projection =  projector.project(-factor_x*10,factor_y*10,10);
	BufferGC.drawLine(startingpoint.x, startingpoint.y, projection.x, projection.y);
	projection =  projector.project(factor_x*10,-factor_y*10,10);
	BufferGC.drawLine(startingpoint.x, startingpoint.y, projection.x, projection.y);
	projection =  projector.project(factor_x*10,factor_y*10,-10);
	BufferGC.drawLine(startingpoint.x, startingpoint.y, projection.x, projection.y);
    }
    
    private void drawBase(Graphics g, int x[], int y[]) {
	Point projection = projector.project(-10,-10,-10);                  
	x[0] = projection.x; y[0] = projection.y;
	projection = projector.project(-10, 10,-10);                  
	x[1] = projection.x; y[1] = projection.y;
	projection = projector.project( 10, 10,-10);                  
	x[2] = projection.x; y[2] = projection.y;
	projection = projector.project( 10,-10,-10);                  
	x[3] = projection.x; y[3] = projection.y;
	x[4] = x[0]; y[4] = y[0];
	
	if (plot_mode != WIREFRAME) {
	    if (plot_mode == NORENDER)
		g.setColor(Color.lightGray);
	    else
		g.setColor(new Color(192,220,192));      
	    g.fillPolygon(x,y,4);
	}
	g.setColor(Color.black);
	g.drawPolygon(x,y,5);
    }
    
    private void drawBoxGridsTicksLabels(Graphics g, boolean draw_axes) {
	Point    projection, tickpos;   
	boolean  x_left = false, y_left = false;
	int      x[],y[],i;
	double temp;
	
	x = new int[5]; y = new int[5];
	if (projector == null) return;
	if (draw_axes) {
	    drawBase(g,x,y);
	    projection = projector.project(0,0,-10);                  
	    x[0] = projection.x; y[0] = projection.y;
	    projection = projector.project(10.5f,0,-10);                  
	    g.drawLine(x[0],y[0],projection.x,projection.y);
	    if (projection.x < x[0]) 
		outString(g,(int)(1.05*(projection.x-x[0]))+x[0],
			  (int)(1.05*(projection.y-y[0]))+y[0],"x",JLabel.RIGHT,TOP);
	    else
		outString(g,(int)(1.05*(projection.x-x[0]))+x[0],
			  (int)(1.05*(projection.y-y[0]))+y[0],"x",JLabel.LEFT,TOP);
	    projection = projector.project(0,11.5f,-10);                  
	    g.drawLine(x[0],y[0],projection.x,projection.y);
	    if (projection.x < x[0]) 
		outString(g,(int)(1.05*(projection.x-x[0]))+x[0],
			  (int)(1.05*(projection.y-y[0]))+y[0],"y",JLabel.RIGHT,TOP);
	    else
		outString(g,(int)(1.05*(projection.x-x[0]))+x[0],
			  (int)(1.05*(projection.y-y[0]))+y[0],"y",JLabel.LEFT,TOP);
	    projection = projector.project(0,0,10.5f);                  
	    g.drawLine(x[0],y[0],projection.x,projection.y);
	    outString(g,(int)(1.05*(projection.x-x[0]))+x[0],                    
		      (int)(1.05*(projection.y-y[0]))+y[0],"z",JLabel.CENTER,CENTER);
	}
	else {
	    factor_x = factor_y = 1;
	    projection = projector.project(0,0,-10);                  
	    x[0] = projection.x; 
	    projection = projector.project(10.5f,0,-10);                  
	    y_left = projection.x > x[0];
	    i = projection.y;
	    projection = projector.project(-10.5f,0,-10);
	    if (projection.y > i) {
		factor_x = -1;
		y_left = projection.x > x[0];
	    }
	    projection = projector.project(0,10.5f,-10);                  
	    x_left = projection.x > x[0];
	    i = projection.y;
	    projection = projector.project(0,-10.5f,-10);
	    if (projection.y > i) {
		factor_y = -1;
		x_left = projection.x > x[0]; 
	    }
	    setAxesScale(); drawBase(g,x,y);
	    
	    if (isBoxed) {
		projection = projector.project(-factor_x*10,-factor_y*10,-10);
		x[0] = projection.x; y[0] = projection.y;
		projection = projector.project(-factor_x*10,-factor_y*10,10);                  
		x[1] = projection.x; y[1] = projection.y;
		projection = projector.project(factor_x*10,-factor_y*10,10);
		x[2] = projection.x; y[2] = projection.y;
		projection = projector.project(factor_x*10,-factor_y*10,-10);
		x[3] = projection.x; y[3] = projection.y;
		x[4] = x[0]; y[4] = y[0];
		
		if (plot_mode != WIREFRAME) {
		    if (plot_mode == NORENDER)
			g.setColor(Color.lightGray);
		    else
			g.setColor(new Color(192,220,192));          
		    g.fillPolygon(x,y,4);
		}
		g.setColor(Color.black);
		g.drawPolygon(x,y,5);
		
		projection = projector.project(-factor_x*10,factor_y*10,10);
		x[2] = projection.x; y[2] = projection.y;
		projection = projector.project(-factor_x*10,factor_y*10,-10);
		x[3] = projection.x; y[3] = projection.y;
		x[4] = x[0]; y[4] = y[0];
		
		if (plot_mode != WIREFRAME) {
		    if (plot_mode == NORENDER)
			g.setColor(Color.lightGray);
		    else
			g.setColor(new Color(192,220,192));
		    g.fillPolygon(x,y,4);
		}
		g.setColor(Color.black);
		g.drawPolygon(x,y,5);
	    }
	    else
		if (isDisplayZ) {
		    projection = projector.project(factor_x*10,-factor_y*10,-10);
		    x[0] = projection.x; y[0] = projection.y;  
		    projection = projector.project(factor_x*10,-factor_y*10,10);
		    g.drawLine(x[0],y[0],projection.x,projection.y);
		    
		    projection = projector.project(-factor_x*10,factor_y*10,-10);
		    x[0] = projection.x; y[0] = projection.y;  
		    projection = projector.project(-factor_x*10,factor_y*10,10);
		    g.drawLine(x[0],y[0],projection.x,projection.y);
		}
	    
	    for (i=-9; i <= 9; i++) {
		if (isDisplayXY || isDisplayGrids) {
		    if (!isDisplayGrids || (i % (t_y / 2) == 0) || isDisplayXY) {
			if (isDisplayGrids && (i % t_y == 0)) 
			    projection = projector.project(-factor_x * 10,i,-10);
			else {
			    if (i % t_y != 0) 
				projection = projector.project(factor_x * 9.8f,i,-10);
			    else
				projection = projector.project(factor_x * 9.5f,i,-10);
			}
			tickpos = projector.project(factor_x * 10,i,-10);
			g.drawLine(projection.x,projection.y,tickpos.x,tickpos.y);
			if ((i % t_y == 0) && isDisplayXY) {
			    tickpos = projector.project(factor_x * 10.5f,i,-10);
			    if (y_left) {
				//outString(g,tickpos.x,tickpos.y,
				//Float.toString((float)(i+10)/20.0*(ymax-ymin)+ymin),
				//JLabel.LEFT,TOP);
				
				temp = (double)((i+10)/20.0*(ymax-ymin)+ymin);
				temp = SetDecimal(temp,1);
				outString(g,tickpos.x,tickpos.y,
					  Double.toString(temp),
					  JLabel.LEFT,TOP);
			    }
			    
			    else {
				//outString(g,tickpos.x,tickpos.y,
				//Float.toString((float)(i+10)/20.0*(ymax-ymin)+ymin),
				//JLabel.RIGHT,TOP);
				
				temp = (double)((i+10)/20.0*(ymax-ymin)+ymin);
				temp = SetDecimal(temp,1);
			        outString(g,tickpos.x,tickpos.y,
					  Double.toString(temp),
					  JLabel.RIGHT,TOP);
			    }
			    
			}
		    }  
		    if (!isDisplayGrids || (i % (t_x / 2) == 0) || isDisplayXY) {
			if (isDisplayGrids && (i % t_x == 0))
			    projection = projector.project(i,-factor_y * 10,-10);
			else {
			    if (i % t_x != 0) 
				projection = projector.project(i,factor_y * 9.8f,-10);
			    else
				projection = projector.project(i,factor_y * 9.5f,-10);
			}
			tickpos = projector.project(i,factor_y * 10,-10);
			g.drawLine(projection.x,projection.y,tickpos.x,tickpos.y);
			if ((i % t_x == 0) && isDisplayXY) {
			    tickpos = projector.project(i,factor_y * 10.5f,-10);
			    if (x_left) {
				//outString(g,tickpos.x,tickpos.y,
				//Float.toString((float)(i+10)/20.0*(xmax-xmin)+xmin),
				//JLabel.LEFT,TOP);
				temp = (double)((i+10)/20.0*(xmax-xmin)+xmin);
				temp = SetDecimal(temp,1);
			        outString(g,tickpos.x,tickpos.y,
					  Double.toString(temp),
					  JLabel.LEFT,TOP);
			    }
			    
			    else {
				//outString(g,tickpos.x,tickpos.y,
				//Float.toString((float)(i+10)/20.0*(xmax-xmin)+xmin),
				//    JLabel.RIGHT,TOP);
				temp = (double)((i+10)/20.0*(xmax-xmin)+xmin);
				temp = SetDecimal(temp,1);
			        outString(g,tickpos.x,tickpos.y,
					  Double.toString(temp),
					  JLabel.RIGHT,TOP);
			    }
			}
		    }
		}  
		
		if (isDisplayXY) {
		    tickpos = projector.project(0,factor_y * 14,-10);
		    outString(g,tickpos.x,tickpos.y,"Real",JLabel.CENTER,TOP);
		    tickpos = projector.project(factor_x * 14,0,-10);
		    outString(g,tickpos.x,tickpos.y,"Imaginary",JLabel.CENTER,TOP);
		}
		
		// z grids and ticks
		
		if (isDisplayZ || (isDisplayGrids && isBoxed)) {
		    if (!isDisplayGrids || (i % (t_z / 2) == 0) || isDisplayZ) {
			if (isBoxed && isDisplayGrids && (i % t_z == 0)) {
			    projection = projector.project(-factor_x*10,-factor_y*10,i);
			    tickpos = projector.project(-factor_x*10,factor_y*10,i);
			}
			else {
			    if (i % t_z == 0) 
				projection = projector.project(-factor_x*10,factor_y*9.5f,i);
			    else
				projection = projector.project(-factor_x*10,factor_y*9.8f,i);  
			    tickpos = projector.project(-factor_x*10,factor_y*10,i);
			}                            
			g.drawLine(projection.x,projection.y,tickpos.x,tickpos.y);
			if (isDisplayZ) {
			    tickpos = projector.project(-factor_x*10,factor_y*10.5f,i);
			    if (i % t_z == 0) {
				if (x_left) {
				    outString(g,tickpos.x,tickpos.y,
					      Double.toString((i+10)/20.0*(zmax-zmin)+zmin),
					      JLabel.LEFT,CENTER);
				}
				else {
				    outString(g,tickpos.x,tickpos.y,
					      Double.toString((i+10)/20.0 *(zmax-zmin)+zmin),
					      JLabel.RIGHT,CENTER);
				}
			    }
			}
			if (isDisplayGrids && isBoxed && (i % t_z == 0)) {
			    projection = projector.project(-factor_x*10,-factor_y*10,i);
			    tickpos = projector.project(factor_x*10,-factor_y*10,i);
			} 
			else {
			    if (i % t_z == 0)
				projection = projector.project(factor_x*9.5f,-factor_y*10,i);
			    else
				projection = projector.project(factor_x*9.8f,-factor_y*10,i);
			    tickpos = projector.project(factor_x*10,-factor_y*10,i);
			}             
			g.drawLine(projection.x,projection.y,tickpos.x,tickpos.y);
			if (isDisplayZ) {
			    tickpos = projector.project(factor_x*10.5f,-factor_y*10,i);
			    if (i % t_z == 0) {
				if (y_left) {
				    outString(g,tickpos.x,tickpos.y,
					      Double.toString((i+10)/20.0 *(zmax-zmin)+zmin),
					      JLabel.LEFT,CENTER);
				}
				else {
				    outString(g,tickpos.x,tickpos.y,
					      Double.toString((i+10)/20.0 *(zmax-zmin)+zmin),
					      JLabel.RIGHT,CENTER);
				}
			    }
			}
			if (isDisplayGrids && isBoxed) {
			    if (i % t_y == 0) {
				projection = projector.project(-factor_x * 10,i,-10);
				tickpos = projector.project(-factor_x * 10,i,10);
				g.drawLine(projection.x,projection.y,tickpos.x,tickpos.y);
			    }
			    if (i % t_x  == 0) {
				projection = projector.project(i,-factor_y * 10,-10);
				tickpos = projector.project(i,-factor_y * 10,10);
				g.drawLine(projection.x,projection.y,tickpos.x,tickpos.y);
			    }
			}   
		    }
		}
	    }
	}
    }
    
    private void setAxesScale() {
	float scale_x, scale_y, scale_z ,divisor;
	int   longest;
	
	if (!isScaleBox) {
	    projector.setScaling(1); 
	    t_x = t_y = t_z = 4; 
	    return;
	}
	
	scale_x = xmax - xmin; scale_y = ymax - ymin; scale_z = zmax - zmin;
	
	if (scale_x < scale_y) {
	    if (scale_y < scale_z) {
		longest = 3; divisor = scale_z;
	    }
	    else {
		longest = 2; divisor = scale_y;
	    }
	}
	else {
	    if (scale_x < scale_z) {
		longest = 3; divisor = scale_z;
	    }
	    else {
		longest = 1; divisor = scale_x;
	    }
	}
	
	scale_x /= divisor; scale_y /= divisor; scale_z /= divisor;
	
	if ((scale_x < 0.3f) || (scale_y < 0.3f) && (scale_z < 0.3f)) {
	    switch(longest) {
	    case 1: if (scale_y < scale_z) {
		scale_y /= scale_z; scale_z = 1.0f;
	    }
	    else {
		scale_z /= scale_y; scale_y = 1.0f;
	    } break;
	    case 2: if (scale_x < scale_z) {
		scale_x /= scale_z; scale_z = 1.0f;
	    }
	    else {
		scale_z /= scale_x; scale_x = 1.0f;
	    } break;
	    case 3: if (scale_y < scale_x) {
		scale_y /= scale_x; scale_x = 1.0f;
	    }
	    else {
		scale_x /= scale_y; scale_y = 1.0f;
	    } break;
	    }
	}
	if (scale_x < 0.3f) scale_x = 1.0f; projector.setXScaling(scale_x);
	if (scale_y < 0.3f) scale_y = 1.0f; projector.setYScaling(scale_y);
	if (scale_z < 0.3f) scale_z = 1.0f; projector.setZScaling(scale_z);
	
	if (scale_x < 0.5f) t_x = 8; else t_x = 4;
	if (scale_y < 0.5f) t_y = 8; else t_y = 4;
	if (scale_z < 0.5f) t_z = 8; else t_z = 4;
    }
    
    
    private void outString(Graphics g, int x, int y, 
			   String s, int x_align, int y_align) {
	switch (y_align) {
	case TOP    : y += g.getFontMetrics(g.getFont()).getAscent(); break;
	case CENTER : y += g.getFontMetrics(g.getFont()).getAscent()/2; break;
	}
	switch (x_align) {
	case JLabel.LEFT  : g.drawString(s,x,y);
	    break;
	case JLabel.RIGHT : g.drawString(s,x-g.getFontMetrics(
							      g.getFont()).stringWidth(s),y); 
	break;
	case JLabel.CENTER: g.drawString(s,x-g.getFontMetrics(
							      g.getFont()).stringWidth(s)/2,y); 
	break;
	}     
    }
    
    public double SetDecimal(double doubleNumber, int decimalPlaces) {
	
        int wholeNumber = (int)doubleNumber;
        double pastDecimal = ((doubleNumber - wholeNumber) * 
			      Math.pow(10,decimalPlaces));
        int roundedDecimal = (int)Math.round(pastDecimal);
        int combine = (int)(wholeNumber * 
			    Math.pow(10,decimalPlaces)+roundedDecimal);
        double result = combine / Math.pow(10,decimalPlaces);
        return result;
    }
    /*--------------------------------------------------------------------------------
      Plotting routines
      --------------------------------------------------------------------------------*/  
    
    float color_factor;
    
    private int poly_x[] = new int[9];                       
    private int poly_y[] = new int[9];                   
    
    private void plot_plane(SurfaceVertex[] vertex, int verticescount) {
	int     count,loop,index;
	float   z,result;
	boolean low1,low2;
	boolean valid1,valid2;
	Point   projection;   
	Color   line_color = Color.black; 
	
	if (verticescount < 3) return; 
	count = 0; z = 0.0f;
	low1 = (vertex[0].z < zmin); valid1 = !low1 && (vertex[0].z <= zmax); index = 1;
	for (loop = 0; loop < verticescount; loop++) {
	    low2 = (vertex[index].z < zmin); valid2 = !low2 && (vertex[index].z <= zmax);
	    if ((valid1 || valid2) || (low1^low2)) {
		if (!valid1) {
		    if (low1) result = zmin; else result = zmax;
		    float ratio = (result-vertex[index].z)/(vertex[loop].z-vertex[index].z);
		    float new_x = ratio*(vertex[loop].x-vertex[index].x)+vertex[index].x; 
		    float new_y = ratio*(vertex[loop].y-vertex[index].y)+vertex[index].y;
		    if (low1)
			projection = projector.project(new_x,new_y,-10);
		    else
			projection = projector.project(new_x,new_y,10);
		    poly_x[count] = projection.x;
		    poly_y[count] = projection.y;  
		    count++; z += result;
		} 
		if (valid2) {
		    projection = vertex[index].projection();
		    poly_x[count] = projection.x;
		    poly_y[count] = projection.y;  
		    count++; z += vertex[index].z;
		}
		else {
		    if (low2) result = zmin; else result = zmax;
		    float ratio = (result-vertex[loop].z)/(vertex[index].z-vertex[loop].z);
		    float new_x = ratio*(vertex[index].x-vertex[loop].x)+vertex[loop].x; 
		    float new_y = ratio*(vertex[index].y-vertex[loop].y)+vertex[loop].y;
		    if (low2)
			projection = projector.project(new_x,new_y,-10);
		    else
			projection = projector.project(new_x,new_y,10);
		    poly_x[count] = projection.x;
		    poly_y[count] = projection.y;  
		    count++; z += result;
		} 
	    }
	    index = (index + 1) % verticescount; valid1 = valid2; low1 = low2;
	}
	if (count > 0) {
	    switch (plot_mode) {
	    case NORENDER:    BufferGC.setColor(Color.lightGray); 
		break;
	    case SPECTRUM:    z = 0.8f-(z/count-zmin)*color_factor;
		BufferGC.setColor(new Color(Color.HSBtoRGB(z,1.0f,1.0f))); 
		break;
	    case GRAYSCALE:   z = (z/count-zmin)*color_factor;
		BufferGC.setColor(Color.getHSBColor(0,0,z));
		if (z < 0.3f) line_color = new Color(0.6f,0.6f,0.6f);                    
		break; 
	    case DUALSHADE:   z = (z/count-zmin)*color_factor+0.4f;
		BufferGC.setColor(Color.getHSBColor(color,0.7f,z));
		break;
	    }
	    
	    BufferGC.fillPolygon(poly_x,poly_y,count);
	    BufferGC.setColor(line_color);
	    if (isMesh || (plot_mode == NORENDER)) {
		poly_x[count] = poly_x[0];
		poly_y[count] = poly_y[0];
		count++;
		BufferGC.drawPolygon(poly_x,poly_y,count);
	    }
	}
    }
    
    private SurfaceVertex upperpart[] = new SurfaceVertex[8];
    private SurfaceVertex lowerpart[] = new SurfaceVertex[8];
    
    private void split_plot_plane(SurfaceVertex[] values1, SurfaceVertex[] values2) { 
	int trackposition = COINCIDE;
	int uppercount = 0, lowercount = 0;
	boolean coincide = true;
	boolean upper_first = false;
	float factor, xi ,yi, zi;        
	int i = 0, j = 0;
	
	for (int counter=0; counter <= 4; counter++) {
	    if (values1[i].z < values2[i].z) {
		coincide = false;
		if (trackposition == COINCIDE) {
		    trackposition = UPPER;
		    upperpart[uppercount++] = values2[i];
		}
		else
		    if (trackposition != UPPER) {      
			
			// intersects
			
			factor = (values1[i].z-values2[i].z)/
			    (values1[i].z-values2[i].z+values2[j].z-values1[j].z);
			if (values1[i].x==values1[j].x) {   
			    
			    // intersects in y direction
			    
			    yi = factor * (values1[j].y-values1[i].y) + values1[i].y;
			    xi = values1[i].x;            
			}
			else { 
			    
			    // intersects in x direction
			    
			    xi = factor * (values1[j].x-values1[i].x) + values1[i].x;
			    yi = values1[i].y;            
			}
			zi = factor * (values2[j].z-values2[i].z) + values2[i].z;
			
			upperpart[uppercount++] = 
			    lowerpart[lowercount++] = new SurfaceVertex(xi,yi,zi);
			upperpart[uppercount++] = values2[i];                    
			
			trackposition = UPPER;          
		    }
		    else {            
			upperpart[uppercount++] = values2[i];
		    }
		
	    }
	    else
		if (values1[i].z > values2[i].z) {
		    coincide = false;
		    if (trackposition == COINCIDE) {
			trackposition = LOWER;
			lowerpart[lowercount++] = values2[i];
		    }
		    else
			if (trackposition != LOWER) {      
			    
			    // intersects
			    
			    factor = (values1[i].z-values2[i].z)/
				(values1[i].z-values2[i].z+values2[j].z-values1[j].z);
			    if (values1[i].x==values1[j].x) {   
				
				// intersects in y direction
				
				yi = factor * (values1[j].y-values1[i].y) + values1[i].y;
				xi = values1[i].x;            
			    }
			    else {            
				
				// intersects in x direction
				
				xi = factor * (values1[j].x-values1[i].x) + values1[i].x;
				yi = values1[i].y;            
			    }
			    zi = factor * (values2[j].z-values2[i].z) + values2[i].z;
			    
			    lowerpart[lowercount++] = 
				upperpart[uppercount++] = new SurfaceVertex(xi,yi,zi);
			    lowerpart[lowercount++] = values2[i];
			    
			    trackposition = LOWER;          
			}
			else {            
			    lowerpart[lowercount++] = values2[i];
			}        
		}
		else {
		    upperpart[uppercount++] = values2[i];
		    lowerpart[lowercount++] = values2[i];
		    trackposition = COINCIDE;
		}
	    
	    j = i;
	    i = (i + 1) % 4;
	}
	
	if (coincide) {            // the two planes completely coincide
	    plot_plane(values1,4);
	}
	else {
	    if (critical) 
		upper_first = false; 
	    else {
		
		/*
		  Priority Determination:
		  
		  Theory: if center of projection (c.o.p) is above plane 0-1-2-3, then the plane 
		  below plane 0-1-2-3 should be plotted first, otherwise the plane above 
		  plane 0-1-2-3 should be plotted first.
		  Task:   calculate the height of plane 0-1-2-3 at the projection of c.o.p
		  on plane xy (point c) and compare it with the height of c.o.p
		  
		  To complete the task, we first calculate the height of plane 0-1-2-3 at point
		  P,Q. Fortunately, this is just a 2-dimensional problem. The plane height at 
		  point P  can be calculate using line equation on plane 3-2-P. (plane 1-2-Q for 
		  point Q)
		  The next job is to calculate the plane height at point R. Again this is an easy
		  job. Because R is always in the middle of P and Q, the plane height at R can be 
		  calculated by using this formula:
		  
		  zR = (zP + zQ) / 2
		  
		  Note that point P, Q, R in 3-D space are ON the plane 0-1-2-3
		  The final job is to calculate the plane height at point c. This the same
		  with previous job:
		  
		  zR = (z2 + zc) / 2
		  
		  zc = 2 * zR - z2
		  
		  
		  --plane xy--
		  
		  |      |
		  -----------0------3--------------------
		  |      |
		  |      |                             c : center of projection
		  -----------1------2---------Q----------
		  |      |         |
		  |      |    R    |
		  |      |         |
		  |      P---------c
		  |      |
		  |      |
		  
		  
		  thus, the Java instructions for priority test might look like this: 
		  
		  float zP,zQ,zR;
		  
		  zP = (values1[2].z-values1[3].z)*(cop.x-values1[3].x)/
		  (values1[2].x-values1[3].x)+values1[3].z;
		  zQ = (values1[2].z-values1[1].z)*(cop.y-values1[1].y)/
		  (values1[2].y-values1[1].y)+values1[1].z;
		  zR = (zP+zQ)/2;
		  
		  // upper_first = 2 * zR - z2 > cop.z;
		  upper_first = zP + zQ - z2 > cop.z;
		  
		  
		  But, using new variables zP, zQ, and zR is not a good idea.
		  It is better to calculate zR in a single instruction to speed up
		  the calculation, even only by a litte. We are in hurry !   :)                        
		*/
                
		if (values1[1].x == values1[2].x) {
		    upper_first = (values1[2].z-values1[3].z) * (cop.x-values1[3].x) /
                        (values1[2].x-values1[3].x) + values1[3].z +
                        (values1[2].z-values1[1].z) * (cop.y-values1[1].y) /
                        (values1[2].y-values1[1].y) + values1[1].z - values1[2].z > cop.z;
		}
		else {
		    upper_first = (values1[2].z-values1[1].z) * (cop.x-values1[1].x) /
                        (values1[2].x-values1[1].x) + values1[1].z +
                        (values1[2].z-values1[3].z) * (cop.y-values1[3].y) /
                        (values1[2].y-values1[3].y) + values1[3].z - values1[2].z > cop.z;
		}
	    }     
	    
	    if (lowercount < 3) {
		if (upper_first) {
		    color = 0.7f;
		    plot_plane(upperpart,uppercount);
		    color = 0.2f;
		    plot_plane(values1,4);
		}
		else {
		    color = 0.2f;
		    plot_plane(values1,4);
		    color = 0.7f;
		    plot_plane(upperpart,uppercount);
		}
	    }
	    else
		if (uppercount < 3) {
		    if (upper_first) {
			color = 0.2f;
			plot_plane(values1,4);
			color = 0.7f;
			plot_plane(lowerpart,lowercount);
		    }
		    else {
			color = 0.7f;
			plot_plane(lowerpart,lowercount);
			color = 0.2f;
			plot_plane(values1,4);
		    }
		}
		else {
		    if (upper_first) {
			color = 0.7f;
			plot_plane(upperpart,uppercount);   
			color = 0.2f;
			plot_plane(values1,4);
			color = 0.7f;
			plot_plane(lowerpart,lowercount);
		    }
		    else {
			color = 0.7f;
			plot_plane(lowerpart,lowercount);
			color = 0.2f;
			plot_plane(values1,4);
			color = 0.7f;
			plot_plane(upperpart,uppercount);   
		    }
		} 
	}
    }
    
    private boolean plottable(SurfaceVertex[] values) {
	return (!values[0].isInvalid() &&
		!values[1].isInvalid() &&
		!values[2].isInvalid() &&            
		!values[3].isInvalid()); 
    }
    
    private SurfaceVertex values1[] = new SurfaceVertex[4];                  
    private SurfaceVertex values2[] = new SurfaceVertex[4];                  
    
    private void plot_area(int start_lx, int start_ly,
			   int end_lx, int end_ly,
			   int sx, int sy) {
	
	start_lx *= calc_divisions + 1;
	sx *= calc_divisions + 1;
	end_lx *= calc_divisions + 1;
	
	int lx = start_lx;
	int ly = start_ly;
	
	while (ly != end_ly) {
	    if (plotfunc1) {
		values1[1] = vertex[0][lx+ly];
		values1[2] = vertex[0][lx+ly+sy];
	    }
	    if (plotfunc2) {
		values2[1] = vertex[1][lx+ly];  
		values2[2] = vertex[1][lx+ly+sy];
	    }
	    
	    while (lx != end_lx) {
		thread.yield();
		if (plotfunc1) {
		    values1[0] = values1[1];
		    values1[1] = vertex[0][lx+sx+ly];
		    values1[3] = values1[2];
		    values1[2] = vertex[0][lx+sx+ly+sy];
		}
		if (plotfunc2) {
		    values2[0] = values2[1];
		    values2[1] = vertex[1][lx+sx+ly];
		    values2[3] = values2[2];
		    values2[2] = vertex[1][lx+sx+ly+sy];
		}
		if (!plotboth) {          
		    if (plotfunc1) {
			if (plot_mode == DUALSHADE) color = 0.2f;
			if (plottable(values1)) plot_plane(values1,4);
		    }
		    else {
			if (plot_mode == DUALSHADE) color = 0.7f;
			if (plottable(values2)) plot_plane(values2,4);
		    }
		}           
		else {
		    if (plottable(values1)) {
			if (plottable(values2))
			    split_plot_plane(values1,values2);
			else
			    plot_plane(values1,4);
		    }
		    else {
			if (plottable(values2)) plot_plane(values2,4);
		    }
		}
		lx += sx; 
	    }
	    ly += sy; lx = start_lx;
	}
    }
    
    private Point[] testpoint = new Point[5];
    
    private void plot_surface() {
	float    zi, zx;
	int      sx, sy;
	int      start_lx, end_lx;
	int      start_ly, end_ly;
	
	image_drawn = false; 
	try {
	    zi = Float.valueOf(applet.getZMin()).floatValue();
	    zx = Float.valueOf(applet.getZMax()).floatValue();
	    if (zi >= zx) throw new NumberFormatException();
	}
	catch(NumberFormatException e) {
	    applet.setMessage("Error in ranges"); 
	    return;
	}
	
	int plot_density = applet.getDispDivisions(); 
	int multiple_factor = calc_divisions / plot_density;
	applet.setDispDivisions(plot_density + "");
	
	thread.yield();
	zmin = zi; zmax = zx; 
	applet.setMessage("regenerating ...");
	color_factor = 0.8f / (zmax-zmin);    
	if (plot_mode == DUALSHADE) color_factor *= 0.6f;
	
	BufferGC.setColor(Color.lightGray);
	BufferGC.fillRect(1,1,getBounds().width-2,getBounds().height-2);
	BufferGC.draw3DRect(0,0,getBounds().width-1,getBounds().height-1,false);
	drawBoxGridsTicksLabels(BufferGC,false); 
	
	
	if (!plotfunc1 && !plotfunc2) {    
	    if (isBoxed) drawBoundingBox();
	    applet.setMessage("completed"); 
	    image_drawn = true;
	    return; 
	}
	
	SurfaceVertex.setZRange(zmin,zmax);
	
	
	
	// direction test
	
	float distance = projector.getDistance() * projector.getCosElevationAngle();
	
	// cop : center of projection
	
	cop = new SurfaceVertex(distance * projector.getSinRotationAngle(), 
				distance * projector.getCosRotationAngle(), 
				projector.getDistance() * projector.getSinElevationAngle());
	cop.transform();
	
	boolean inc_x = cop.x > 0;
	boolean inc_y = cop.y > 0;
	
	critical = false;
	
	if (inc_x) {
	    start_lx = 0; end_lx = calc_divisions; 
	    sx = multiple_factor;
	}
	else {
	    start_lx = calc_divisions; end_lx = 0; 
	    sx = -multiple_factor; 
	}     
	if (inc_y) {
	    start_ly = 0; end_ly = calc_divisions; 
	    sy = multiple_factor; 
	}
	else {
	    start_ly = calc_divisions; end_ly = 0; 
	    sy = -multiple_factor; 
	}
	
	if ((cop.x > 10) || (cop.x < -10)) {
	    if ((cop.y > 10) || (cop.y < -10)) {
		plot_area(start_lx,start_ly,end_lx,end_ly,sx,sy);
	    }
	    else {    // split in y direction
		int split_y = (int)((cop.y+10)*plot_density/20)*multiple_factor;               
		plot_area(start_lx,0,end_lx,split_y,sx,multiple_factor);
		plot_area(start_lx,calc_divisions,end_lx,split_y,sx,-multiple_factor);        
	    }
	}
	else {
	    if ((cop.y > 10) || (cop.y < -10)) {   // split in x direction
		int split_x = (int)((cop.x+10)*plot_density/20)*multiple_factor;               
		plot_area(0,start_ly,split_x,end_ly,multiple_factor,sy);
		plot_area(calc_divisions,start_ly,split_x,end_ly,-multiple_factor,sy);        
	    }
	    else {    // split in both x and y directions
		int split_x = (int)((cop.x+10)*plot_density/20)*multiple_factor;               
		int split_y = (int)((cop.y+10)*plot_density/20)*multiple_factor;               
		critical = true;
		plot_area(0,0,split_x,split_y,multiple_factor,multiple_factor);      
		plot_area(0,calc_divisions,split_x,split_y,multiple_factor,-multiple_factor);      
		plot_area(calc_divisions,0,split_x,split_y,-multiple_factor,multiple_factor);      
		plot_area(calc_divisions,calc_divisions,split_x,split_y,
			  -multiple_factor,-multiple_factor);      
	    }
	}
	
	if (isBoxed) drawBoundingBox();
	applet.setMessage("completed"); 
	image_drawn = true; 
    }
    
    private void plot_wireframe() {
	int      i,j,k;
	int      plot_density,multiple_factor; 
	int      counter;    
	float    zi,zx;    
	float    z;
	float    lx = 0, ly = 0, lastz = 0;
	Point    projection = new Point(0,0);
	Point    lastproj = new Point(0,0);
	boolean  error,lasterror,invalid;
	
	image_drawn = false; 
	try {
	    zi = Float.valueOf(applet.getZMin()).floatValue();
	    zx = Float.valueOf(applet.getZMax()).floatValue();
	    if (zi >= zx) throw new NumberFormatException();
	}
	catch(NumberFormatException e) {
	    applet.setMessage("Error in ranges"); 
	    return;
	}
	
	plot_density = applet.getDispDivisions();
	multiple_factor = calc_divisions / plot_density;
	applet.setDispDivisions(plot_density + "");
	
	zmin = zi; zmax = zx; 
	applet.setMessage("regenerating ...");
	BufferGC.setColor(Color.lightGray);
	BufferGC.fillRect(1,1,getBounds().width-2,getBounds().height-2);
	//    BufferGC.fillRect(1,1,bounds().width-2,bounds().height-2);
	BufferGC.draw3DRect(0,0,getBounds().width-1,getBounds().height-1,false);
	//    BufferGC.draw3DRect(0,0,bounds().width-1,bounds().height-1,false);
	
	thread.yield();
	drawBoxGridsTicksLabels(BufferGC,false);  
	BufferGC.setColor(Color.black);
	
	SurfaceVertex.setZRange(zmin,zmax);
	
	for (int func = 0; func < 2; func++) {
	    
	    if ((func == 0) && !plotfunc1) continue;
	    if ((func == 1) && !plotfunc2) continue;
	    
	    i = 0; j = 0; k = 0; counter = 0;
	    
	    // plot - x direction
	    
	    while (i <= calc_divisions) {
		lasterror = true;
		if (counter == 0) {
		    while (j <= calc_divisions) {
			thread.yield();
			z = vertex[func][k].z;
			invalid = Float.isNaN(z);
			if (!invalid) {
			    if (z < zmin) {
				error = true;
				float ratio = (zmin-lastz)/(z-lastz);
				projection = projector.project(ratio*(vertex[func][k].x-lx)+lx,
							       ratio*(vertex[func][k].y-ly)+ly,-10);         
			    }
			    else
				if (z > zmax) {
				    error = true;
				    float ratio = (zmax-lastz)/(z-lastz);
				    projection = projector.project(ratio*(vertex[func][k].x-lx)+lx,
								   ratio*(vertex[func][k].y-ly)+ly,10);
				}
				else {
				    error = false;
				    projection = vertex[func][k].projection();
				}
			    if (lasterror && (!error) && (j != 0)) {
				if (lastz > zmax) {
				    float ratio = (zmax-z)/(lastz-z);
				    lastproj = projector.project(
								 ratio*(lx-vertex[func][k].x)+vertex[func][k].x,
								 ratio*(ly-vertex[func][k].y)+vertex[func][k].y,10);
				}
				else
				    if (lastz < zmin) {
					float ratio = (zmin-z)/(lastz-z);
					lastproj = projector.project(
								     ratio*(lx-vertex[func][k].x)+vertex[func][k].x,
								     ratio*(ly-vertex[func][k].y)+vertex[func][k].y,-10);         
				    }
			    } else invalid = error && lasterror;
			} else error = true;
			if (!invalid && (j != 0)) {
			    BufferGC.drawLine(lastproj.x, lastproj.y, projection.x, projection.y);
			} 
			lastproj = projection;
			lasterror = error;
			lx = vertex[func][k].x; 
			ly = vertex[func][k].y; 
			lastz = z;
			j++; k++;       
		    }  
		}
		else k += calc_divisions+1;
		j = 0; i++;  
		counter = (counter + 1) % multiple_factor;
	    }
	    
	    // plot - y direction
	    
	    i = 0; j = 0; k = 0; counter = 0; 
	    
	    while (j <= calc_divisions) {
		lasterror = true;
		if (counter == 0) {
		    while (i <= calc_divisions) {
			thread.yield();
			z = vertex[func][k].z;
			invalid = Float.isNaN(z);
			if (!invalid) {
			    if (z < zmin) {
				error = true;
				float ratio = (zmin-lastz)/(z-lastz);
				projection = projector.project(ratio*(vertex[func][k].x-lx)+lx,
							       ratio*(vertex[func][k].y-ly)+ly,-10);         
			    }
			    else
				if (z > zmax) {
				    error = true;
				    float ratio = (zmax-lastz)/(z-lastz);
				    projection = projector.project(ratio*(vertex[func][k].x-lx)+lx,
								   ratio*(vertex[func][k].y-ly)+ly,10);
				}
				else {
				    error = false;
				    projection = vertex[func][k].projection();
				}
			    if (lasterror && (!error) && (i != 0)) {
				if (lastz > zmax) {
				    float ratio = (zmax-z)/(lastz-z);
				    lastproj = projector.project(
								 ratio*(lx-vertex[func][k].x)+vertex[func][k].x,
								 ratio*(ly-vertex[func][k].y)+vertex[func][k].y,10);
				}
				else
				    if (lastz < zmin) {
					float ratio = (zmin-z)/(lastz-z);
					lastproj = projector.project(
								     ratio*(lx-vertex[func][k].x)+vertex[func][k].x,
								     ratio*(ly-vertex[func][k].x)+vertex[func][k].y,-10);         
				    }
			    } else invalid = error && lasterror;
			} else error = true;
			if (!invalid && (i != 0)) {
			    BufferGC.drawLine(lastproj.x, lastproj.y, projection.x, projection.y);
			} 
			lastproj = projection;
			lasterror = error;
			lx = vertex[func][k].x; 
			ly = vertex[func][k].y; 
			lastz = z;
			i++; k += calc_divisions+1;       
		    }  
		}
		i = 0; k = ++j;
		counter = (counter + 1) % multiple_factor;
	    }
	}
	if (isBoxed) drawBoundingBox();
	applet.setMessage("completed"); 
	image_drawn = true; 
    }
    
    public void paintComponent(Graphics g) {
	super.paintComponent(g);
	if ((getBounds().width <= 0) || (getBounds().height <= 0)) return;
	importVariables();
	if (image_drawn && (Buffer != null)) {
	    g.drawImage(Buffer,0,0,this);
	}
	else
	    if (data_available && !interrupted) {
		if (thread.isAlive()) {
		    //thread.stop();
		    thread = null;
		    while (thread.isAlive()) {
			thread.yield();
		    } 
		}
		thread = new Thread(this);
		thread.start();
	    }
	    else {
		g.setColor(Color.lightGray);
		g.fillRect(1,1,getBounds().width-2,getBounds().height-2); 
		drawBoxGridsTicksLabels(g,true);
		g.setColor(Color.lightGray);
		g.draw3DRect(0,0,getBounds().width-1,getBounds().height-1,false);
	    }
	interrupted = false;
    }
    
    public void update(Graphics g) {
	paintComponent(g);                        // do not erase, just paint
    }  
    
    public void run() {
	SurfaceVertex.invalidate(); 
	if (plot_mode == WIREFRAME)
	    plot_wireframe(); 
	else
	    plot_surface();
	repaint();           
    }
    
    public synchronized void interrupt() {
	if (thread.isAlive()) {
	    System.out.println("2:surface.canvas1311");
	    //thread.stop();
	    thread = null;
	    try {
		thread.join();
	    }
	    catch (InterruptedException e) {};
	    applet.setMessage("Interrupted");
	    image_drawn = false;
	    interrupted = true;
	    repaint();
	}
    }
    
    public Dimension PreferredSize() {
	return new Dimension(550,400);  // initial canvas size
    }
    
    public synchronized void validate() {
	super.validate();
	
	if (thread.isAlive()) {
	    System.out.println("3:surface.canvas1332");
	    //thread.stop();
	    thread = null;
	    try {
		thread.join();
	    }
	    catch (InterruptedException e) {};
	}
	
	// backing buffer creation
	
	if ((getBounds().width > 0) && (getBounds().height > 0)) {
	    if ((getBounds().width != prevwidth) 
		|| (getBounds().height != prevheight)) {
		applet.setMessage("New image size: " + 
				  getBounds().width + "x" + getBounds().height);
		projector.setProjectionArea(new Rectangle(
							  0,0,getBounds().width, getBounds().height));  
		image_drawn = false;
		Buffer = createImage(getBounds().width, getBounds().height);
		if (BufferGC != null) BufferGC.dispose();
		BufferGC = Buffer.getGraphics();     
		prevwidth = getBounds().width;
		prevheight = getBounds().height;
	    } 
	    repaint();
	}
    }
}
