//
//  Created by Steve Butler on 21 July 2008
//
//  Need to have window of size 840x733 for program
//

import java.text.DecimalFormat;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class stretch extends Applet implements MouseListener, MouseMotionListener {
	static int BUFFER=20, T_WIDTH=800, T_HEIGHT=693;  // constants used for size of triangle

	String[] centerTypes = { "bisectors", "medians", "Lemoine point", "Gergonne point",  "Spieker center", 
		"Isogonal of Spieker", "bisectors (x3)", "medians (x3)", "Lemoine point (x3)", "Gergonne point (x3)", 
		 "Spieker center (x3)", "Isogonal of Spieker (x3)"};
	int method=3;  //used to keep track of type of center used in the calculations

	static Point locationA = new Point(T_WIDTH/2-BUFFER,T_HEIGHT/2+BUFFER);
	static Point locationB = new Point(T_WIDTH/2+BUFFER,T_HEIGHT/2+BUFFER);

	boolean twoPoints=true;
	boolean drawLines=true;
	boolean inTriangle=true;
	boolean isDragging=false;
	boolean notDraggingInside=false;
	boolean DraggingPointA=true;
	
	Image backbuffer;
	Graphics backg;

	public void init() {
		backbuffer = createImage( 2*BUFFER+T_WIDTH, 2*BUFFER+T_HEIGHT );
		backg = backbuffer.getGraphics();
		newCoat();
		
		addMouseListener( this );
		addMouseMotionListener( this );
	}

	public void mouseEntered( MouseEvent e ) { }
	public void mouseExited( MouseEvent e ) { }
	public void mouseMoved( MouseEvent e ) { }
	public void mouseClicked( MouseEvent e ) { // GUI controls
		Point test = new Point (e.getX(),e.getY());
		if(testInTriangle(test)==false){
			if(test.x>BUFFER && test.x<BUFFER+15 && test.y>BUFFER && test.y<BUFFER+15){
				method++; if(method==12) { method=0; }
			}
			if(test.x>BUFFER && test.x<BUFFER+15 && test.y>BUFFER+15 && test.y<BUFFER+30){
				method--; if(method==-1) { method=11; }
			}
			if(test.x>50 && test.x<65 && test.y>70 && test.y<85){
				if(twoPoints==true){ twoPoints=false; } else{ twoPoints=true; }
			}
			if(test.x>50 && test.x<65 && test.y>100 && test.y<115){
				if(drawLines==true){ drawLines=false; } else{ drawLines=true; }
			}
			newCoat();
		}
		e.consume();
	}
	public void mousePressed( MouseEvent e ) {
		Point test = new Point (e.getX(),e.getY());

		if(testInTriangle(test)==true){
			inTriangle=true; isDragging=true;
			if(AisClosest(test)==true || twoPoints==false){
				DraggingPointA=true;
				locationA=test;
			}
			else {
				DraggingPointA=false;
				locationB=test;
			}
		}
		else{ inTriangle=false; }
		newCoat();
		e.consume();
	}
	public void mouseReleased( MouseEvent e ) {
		isDragging = false;
		e.consume();
	}
	public void mouseDragged( MouseEvent e ) {
		Point test = new Point(e.getX(),e.getY());
		if(testInTriangle(test)==true && isDragging==true){
			if(DraggingPointA==true){
				locationA=test;
			}
			else{
				locationB=test;
			}
			newCoat();
			e.consume();
		}
		else if(testInTriangle(test)==false && isDragging==true){
			if(DraggingPointA==true){
				locationA=nearestInTriangle(test);
			}
			else{
				locationB=nearestInTriangle(test);
			}
			newCoat();
			e.consume();
		}
	}
	
//
//  The following two functions are to eliminate flicker
//
	public void update( Graphics g ) {
		g.drawImage( backbuffer, 0, 0, this );
	}

	public void paint( Graphics g ) {
		update( g );
	}
	
	public void newCoat(){
		DecimalFormat ratiodf = new DecimalFormat("##0.000");
		DecimalFormat df = new DecimalFormat("##0.0");
		backg.setColor(Color.black);
		backg.fillRect(0,0,2*BUFFER+T_WIDTH,2*BUFFER+T_HEIGHT);
// draw the large white triangle
		int[] XArray={BUFFER,(T_WIDTH/2)+BUFFER,T_WIDTH+BUFFER};
		int[] YArray={BUFFER+T_HEIGHT,BUFFER,BUFFER+T_HEIGHT};
		backg.setColor( Color.white );
		backg.fillPolygon(XArray,YArray,3);

//draw GUI
		backg.setColor( Color.white );
		backg.drawRect( BUFFER,BUFFER,15,15);
		int[] UpTriangleX1 = {BUFFER+3,BUFFER+11,BUFFER+7};
		int[] UpTriangleY1 = {BUFFER+12,BUFFER+12,BUFFER+3};
		backg.fillPolygon( UpTriangleX1, UpTriangleY1, 3 );
		backg.drawRect( BUFFER,BUFFER+15,15,15);
		int[] DownTriangleX1 = {BUFFER+3,BUFFER+11,BUFFER+7};
		int[] DownTriangleY1 = {BUFFER+15+3,BUFFER+15+3,BUFFER+15+12};
		backg.fillPolygon( DownTriangleX1, DownTriangleY1, 3 );
		backg.drawRect(BUFFER+15,BUFFER,225,30);
		backg.drawString(centerTypes[method], BUFFER+30, BUFFER+20);

		backg.drawRect(50,70,15,15);
		backg.drawString("Two points", 80,83);
		if(twoPoints==true){ 
			//first check the box
			backg.drawLine(50,70,65,85); 
			backg.drawLine(50,85,65,70); 
			//now add box for lines
			backg.drawRect(50,100,15,15);
			backg.drawString("Lines",80,113);
			if(drawLines==true){
				backg.drawLine(50,100,65,115);
				backg.drawLine(50,115,65,100);
			}
			}

//draw red cross indicating current location of location A
		backg.setColor( Color.red );
		backg.drawLine( 620-5, 30+5,  620+5, 30-5);
		backg.drawLine( 620+5, 30+5,  620-5, 30-5);
		backg.drawLine( 620-5, 30+4,  620+4, 30-5);
		backg.drawLine( 620+5, 30+4,  620-4, 30-5);
		backg.drawLine( 620-4, 30+5,  620+5, 30-4);
		backg.drawLine( 620+4, 30+5,  620-5, 30-4);
		double anglesA[]=findTriangle(locationA);
		backg.setColor( Color.yellow );
		backg.drawString(df.format(anglesA[0]*180.0/Math.PI)+" -- "
						+df.format(anglesA[1]*180.0/Math.PI)+" -- "
						+df.format(anglesA[2]*180.0/Math.PI), 635,35);
		if(twoPoints==true){
//draw blue cross indicating current location of location B
			backg.setColor( Color.blue );
			backg.drawLine( 620-5, 60+5,  620+5, 60-5);
			backg.drawLine( 620+5, 60+5,  620-5, 60-5);
			backg.drawLine( 620-5, 60+4,  620+4, 60-5);
			backg.drawLine( 620+5, 60+4,  620-4, 60-5);
			backg.drawLine( 620-4, 60+5,  620+5, 60-4);
			backg.drawLine( 620+4, 60+5,  620-5, 60-4);
			double anglesB[]=findTriangle(locationB);
			backg.setColor( Color.yellow );
			backg.drawString(df.format(anglesB[0]*180.0/Math.PI)+" -- "
							+df.format(anglesB[1]*180.0/Math.PI)+" -- "
							+df.format(anglesB[2]*180.0/Math.PI), 635,65);
		}

//mark the points used by method A
		backg.setColor( Color.red );
		double Aa,Ab,Ac,Ba,Bb,Bc;  // angles to be used to compute daughters
		double triangleA[]=findTriangle(locationA);
		double triangleB[]=findTriangle(locationB);
		
		if(method==0 || method==6) {  // bisector
			Aa=bisector(triangleA[0],triangleA[1],triangleA[2]);
			Ab=bisector(triangleA[1],triangleA[2],triangleA[0]);
			Ac=bisector(triangleA[2],triangleA[0],triangleA[1]);
			Ba=bisector(triangleB[0],triangleB[1],triangleB[2]);
			Bb=bisector(triangleB[1],triangleB[2],triangleB[0]);
			Bc=bisector(triangleB[2],triangleB[0],triangleB[1]);
		}
		else if(method==1 || method==7){  // medians
			Aa=median(triangleA[0],triangleA[1],triangleA[2]);
			Ab=median(triangleA[1],triangleA[2],triangleA[0]);
			Ac=median(triangleA[2],triangleA[0],triangleA[1]);
			Ba=median(triangleB[0],triangleB[1],triangleB[2]);
			Bb=median(triangleB[1],triangleB[2],triangleB[0]);
			Bc=median(triangleB[2],triangleB[0],triangleB[1]);
		}
		else if(method==2 || method==8){  // Lemoine
			Aa=Lemoine(triangleA[0],triangleA[1],triangleA[2]);
			Ab=Lemoine(triangleA[1],triangleA[2],triangleA[0]);
			Ac=Lemoine(triangleA[2],triangleA[0],triangleA[1]);
			Ba=Lemoine(triangleB[0],triangleB[1],triangleB[2]);
			Bb=Lemoine(triangleB[1],triangleB[2],triangleB[0]);
			Bc=Lemoine(triangleB[2],triangleB[0],triangleB[1]);
		}
		else if(method==3 || method==9){  // Gergonne
			Aa=Gergonne(triangleA[0],triangleA[1],triangleA[2]);
			Ab=Gergonne(triangleA[1],triangleA[2],triangleA[0]);
			Ac=Gergonne(triangleA[2],triangleA[0],triangleA[1]);
			Ba=Gergonne(triangleB[0],triangleB[1],triangleB[2]);
			Bb=Gergonne(triangleB[1],triangleB[2],triangleB[0]);
			Bc=Gergonne(triangleB[2],triangleB[0],triangleB[1]);
		}
		else if(method==4 || method==10){  // Spieker
			Aa=Spieker(triangleA[0],triangleA[1],triangleA[2]);
			Ab=Spieker(triangleA[1],triangleA[2],triangleA[0]);
			Ac=Spieker(triangleA[2],triangleA[0],triangleA[1]);
			Ba=Spieker(triangleB[0],triangleB[1],triangleB[2]);
			Bb=Spieker(triangleB[1],triangleB[2],triangleB[0]);
			Bc=Spieker(triangleB[2],triangleB[0],triangleB[1]);
		}
		else{ // (method==5 || method==11)  // isogonal of Spieker 
			Aa=isoSpieker(triangleA[0],triangleA[1],triangleA[2]);
			Ab=isoSpieker(triangleA[1],triangleA[2],triangleA[0]);
			Ac=isoSpieker(triangleA[2],triangleA[0],triangleA[1]);
			Ba=isoSpieker(triangleB[0],triangleB[1],triangleB[2]);
			Bb=isoSpieker(triangleB[1],triangleB[2],triangleB[0]);
			Bc=isoSpieker(triangleB[2],triangleB[0],triangleB[1]);
		}
		if(method<=5){  //6 daughters
			Point AA,AB,AC,AD,AE,AF,BA,BB,BC,BD,BE,BF;  // daughter points
			AA=findPoint(Aa,Ac+triangleA[0]-Aa,Math.PI-Ac-triangleA[0]);
			AB=findPoint(triangleA[1]-Ab,Ac+triangleA[0],Math.PI+Ab-Ac-triangleA[0]-triangleA[1]);
			AC=findPoint(Aa+triangleA[1]-Ab,Ab,Math.PI-Aa-triangleA[1]);
			AD=findPoint(Math.PI+Ac-Aa-triangleA[1]-triangleA[2],Aa+triangleA[1],triangleA[2]-Ac);
			AE=findPoint(Math.PI-Ab-triangleA[2],Ac,Ab+triangleA[2]-Ac);
			AF=findPoint(Ab+triangleA[2],Math.PI+Aa-Ab-triangleA[2]-triangleA[0],triangleA[0]-Aa);

			BA=findPoint(Ba,Bc+triangleB[0]-Ba,Math.PI-Bc-triangleB[0]);
			BB=findPoint(triangleB[1]-Bb,Bc+triangleB[0],Math.PI+Bb-Bc-triangleB[0]-triangleB[1]);
			BC=findPoint(Ba+triangleB[1]-Bb,Bb,Math.PI-Ba-triangleB[1]);
			BD=findPoint(Math.PI+Bc-Ba-triangleB[1]-triangleB[2],Ba+triangleB[1],triangleB[2]-Bc);
			BE=findPoint(Math.PI-Bb-triangleB[2],Bc,Bb+triangleB[2]-Bc);
			BF=findPoint(Bb+triangleB[2],Math.PI+Ba-Bb-triangleB[2]-triangleB[0],triangleB[0]-Ba);

			if(twoPoints==true){
				if(drawLines==true){
					backg.setColor(Color.orange);
					backg.drawLine(locationA.x,locationA.y,locationB.x,locationB.y);
					backg.drawLine(AA.x,AA.y,BA.x,BA.y);
					backg.drawLine(AB.x,AB.y,BB.x,BB.y);
					backg.drawLine(AC.x,AC.y,BC.x,BC.y);
					backg.drawLine(AD.x,AD.y,BD.x,BD.y);
					backg.drawLine(AE.x,AE.y,BE.x,BE.y);
					backg.drawLine(AF.x,AF.y,BF.x,BF.y);

					backg.setColor( Color.orange );
					double dP,dA,dB,dC,dD,dE,dF;
					dP=Distance(locationA,locationB);
					if(dP>0){
						dA=Distance(AA,BA)/dP;
						dB=Distance(AB,BB)/dP;
						dC=Distance(AC,BC)/dP;
						dD=Distance(AD,BD)/dP;
						dE=Distance(AE,BE)/dP;
						dF=Distance(AF,BF)/dP;
						backg.drawString("Ratio of distances",620,100);
						if(dA<=100){ backg.drawString(ratiodf.format(dA),660,120); } else { backg.drawString(">100",660,120); }
						if(dB<=100){ backg.drawString(ratiodf.format(dB),660,140); } else { backg.drawString(">100",660,140); }
						if(dC<=100){ backg.drawString(ratiodf.format(dC),660,160); } else { backg.drawString(">100",660,160); }
						if(dD<=100){ backg.drawString(ratiodf.format(dD),660,180); } else { backg.drawString(">100",660,180); }
						if(dE<=100){ backg.drawString(ratiodf.format(dE),660,200); } else { backg.drawString(">100",660,200); }
						if(dF<=100){ backg.drawString(ratiodf.format(dF),660,220); } else { backg.drawString(">100",660,220); }
					}
				}
			

				
//draw daughters of A
				backg.setColor(Color.red);
				backg.fillOval(AA.x-3,AA.y-3,7,7);
				backg.fillOval(AB.x-3,AB.y-3,7,7);
				backg.fillOval(AC.x-3,AC.y-3,7,7);
				backg.fillOval(AD.x-3,AD.y-3,7,7);
				backg.fillOval(AE.x-3,AE.y-3,7,7);
				backg.fillOval(AF.x-3,AF.y-3,7,7);
//draw daughters of B
				backg.setColor(Color.blue);
				backg.fillOval(BA.x-3,BA.y-3,7,7);
				backg.fillOval(BB.x-3,BB.y-3,7,7);
				backg.fillOval(BC.x-3,BC.y-3,7,7);
				backg.fillOval(BD.x-3,BD.y-3,7,7);
				backg.fillOval(BE.x-3,BE.y-3,7,7);
				backg.fillOval(BF.x-3,BF.y-3,7,7);
//draw red cross indicating current location of location A
				backg.setColor( Color.red );
				backg.drawLine( locationA.x-5, locationA.y+5,  locationA.x+5, locationA.y-5);
				backg.drawLine( locationA.x+5, locationA.y+5,  locationA.x-5, locationA.y-5);
				backg.drawLine( locationA.x-5, locationA.y+4,  locationA.x+4, locationA.y-5);
				backg.drawLine( locationA.x+5, locationA.y+4,  locationA.x-4, locationA.y-5);
				backg.drawLine( locationA.x-4, locationA.y+5,  locationA.x+5, locationA.y-4);
				backg.drawLine( locationA.x+4, locationA.y+5,  locationA.x-5, locationA.y-4);
//draw blue cross indicating current location of location B
				backg.setColor( Color.blue );
				backg.drawLine( locationB.x-5, locationB.y+5,  locationB.x+5, locationB.y-5);
				backg.drawLine( locationB.x+5, locationB.y+5,  locationB.x-5, locationB.y-5);
				backg.drawLine( locationB.x-5, locationB.y+4,  locationB.x+4, locationB.y-5);
				backg.drawLine( locationB.x+5, locationB.y+4,  locationB.x-4, locationB.y-5);
				backg.drawLine( locationB.x-4, locationB.y+5,  locationB.x+5, locationB.y-4);
				backg.drawLine( locationB.x+4, locationB.y+5,  locationB.x-5, locationB.y-4);
			}
			else{
//draw daughters of A
				backg.setColor(Color.red);
				backg.fillOval(AA.x-3,AA.y-3,7,7);
				backg.fillOval(AB.x-3,AB.y-3,7,7);
				backg.fillOval(AC.x-3,AC.y-3,7,7);
				backg.fillOval(AD.x-3,AD.y-3,7,7);
				backg.fillOval(AE.x-3,AE.y-3,7,7);
				backg.fillOval(AF.x-3,AF.y-3,7,7);
//draw red cross indicating current location of location A
				backg.setColor( Color.red );
				backg.drawLine( locationA.x-5, locationA.y+5,  locationA.x+5, locationA.y-5);
				backg.drawLine( locationA.x+5, locationA.y+5,  locationA.x-5, locationA.y-5);
				backg.drawLine( locationA.x-5, locationA.y+4,  locationA.x+4, locationA.y-5);
				backg.drawLine( locationA.x+5, locationA.y+4,  locationA.x-4, locationA.y-5);
				backg.drawLine( locationA.x-4, locationA.y+5,  locationA.x+5, locationA.y-4);
				backg.drawLine( locationA.x+4, locationA.y+5,  locationA.x-5, locationA.y-4);
			}
		}
		else{  //3 daughters
			Point AA,AB,AC,BA,BB,BC;  // daughter points
			AA=findPoint(Aa,triangleA[1]-Ab,Math.PI+Ab-Aa-triangleA[1]);
			AB=findPoint(triangleA[2]-Ac,Math.PI+Ac-Ab-triangleA[2],Ab);
			AC=findPoint(Math.PI+Aa-Ac-triangleA[0],Ac,triangleA[0]-Aa);
			BA=findPoint(Ba,triangleB[1]-Bb,Math.PI+Bb-Ba-triangleB[1]);
			BB=findPoint(triangleB[2]-Bc,Math.PI+Bc-Bb-triangleB[2],Bb);
			BC=findPoint(Math.PI+Ba-Bc-triangleB[0],Bc,triangleB[0]-Ba);
			if(twoPoints==true){
				if(drawLines==true){
					backg.setColor(Color.orange);
					backg.drawLine(locationA.x,locationA.y,locationB.x,locationB.y);
					backg.drawLine(AA.x,AA.y,BA.x,BA.y);
					backg.drawLine(AB.x,AB.y,BB.x,BB.y);
					backg.drawLine(AC.x,AC.y,BC.x,BC.y);
					backg.setColor( Color.orange );

					double dP,dA,dB,dC;
					dP=Distance(locationA,locationB);
					if(dP>0){
						dA=Distance(AA,BA)/dP;
						dB=Distance(AB,BB)/dP;
						dC=Distance(AC,BC)/dP;
						backg.drawString("Ratio of distances",620,100);
						if(dA<=100){ backg.drawString(ratiodf.format(dA),660,120); } else { backg.drawString(">100",660,120); }
						if(dB<=100){ backg.drawString(ratiodf.format(dB),660,140); } else { backg.drawString(">100",660,140); }
						if(dC<=100){ backg.drawString(ratiodf.format(dC),660,160); } else { backg.drawString(">100",660,160); }
					}
					
				}
//draw daughters of A
				backg.setColor(Color.red);
				backg.fillOval(AA.x-3,AA.y-3,7,7);
				backg.fillOval(AB.x-3,AB.y-3,7,7);
				backg.fillOval(AC.x-3,AC.y-3,7,7);
//draw daughters of B
				backg.setColor(Color.blue);
				backg.fillOval(BA.x-3,BA.y-3,7,7);
				backg.fillOval(BB.x-3,BB.y-3,7,7);
				backg.fillOval(BC.x-3,BC.y-3,7,7);
//draw red cross indicating current location of location A
				backg.setColor( Color.red );
				backg.drawLine( locationA.x-5, locationA.y+5,  locationA.x+5, locationA.y-5);
				backg.drawLine( locationA.x+5, locationA.y+5,  locationA.x-5, locationA.y-5);
				backg.drawLine( locationA.x-5, locationA.y+4,  locationA.x+4, locationA.y-5);
				backg.drawLine( locationA.x+5, locationA.y+4,  locationA.x-4, locationA.y-5);
				backg.drawLine( locationA.x-4, locationA.y+5,  locationA.x+5, locationA.y-4);
				backg.drawLine( locationA.x+4, locationA.y+5,  locationA.x-5, locationA.y-4);
//draw blue cross indicating current location of location B
				backg.setColor( Color.blue );
				backg.drawLine( locationB.x-5, locationB.y+5,  locationB.x+5, locationB.y-5);
				backg.drawLine( locationB.x+5, locationB.y+5,  locationB.x-5, locationB.y-5);
				backg.drawLine( locationB.x-5, locationB.y+4,  locationB.x+4, locationB.y-5);
				backg.drawLine( locationB.x+5, locationB.y+4,  locationB.x-4, locationB.y-5);
				backg.drawLine( locationB.x-4, locationB.y+5,  locationB.x+5, locationB.y-4);
				backg.drawLine( locationB.x+4, locationB.y+5,  locationB.x-5, locationB.y-4);
			}
			else{
//draw daughters of A
				backg.setColor(Color.red);
				backg.fillOval(AA.x-3,AA.y-3,7,7);
				backg.fillOval(AB.x-3,AB.y-3,7,7);
				backg.fillOval(AC.x-3,AC.y-3,7,7);
//draw red cross indicating current location of location A
				backg.setColor( Color.red );
				backg.drawLine( locationA.x-5, locationA.y+5,  locationA.x+5, locationA.y-5);
				backg.drawLine( locationA.x+5, locationA.y+5,  locationA.x-5, locationA.y-5);
				backg.drawLine( locationA.x-5, locationA.y+4,  locationA.x+4, locationA.y-5);
				backg.drawLine( locationA.x+5, locationA.y+4,  locationA.x-4, locationA.y-5);
				backg.drawLine( locationA.x-4, locationA.y+5,  locationA.x+5, locationA.y-4);
				backg.drawLine( locationA.x+4, locationA.y+5,  locationA.x-5, locationA.y-4);
			}
		}

//output the picture
		repaint();
	}
//
//  The following function is to test if a point is in the triangle
//
	public boolean testInTriangle (Point P) {
		if(P.y>BUFFER+T_HEIGHT){ return false; }  // below triangle
		else if(T_WIDTH*P.y<-2*T_HEIGHT*P.x+2*T_HEIGHT*BUFFER+BUFFER*T_WIDTH+T_HEIGHT*T_WIDTH){ return false; }  // off to left side
		else if(T_WIDTH*P.y<2*T_HEIGHT*P.x-2*T_HEIGHT*BUFFER-T_HEIGHT*T_WIDTH+BUFFER*T_WIDTH){ return false; } // off to right side
		else{ return true; }
	}

//
//  The following function projects to a point in the triangle
//
	public Point nearestInTriangle(Point P) {
		int newX,newY;
		
		if(P.y>BUFFER+T_HEIGHT){ //  below the triangle
			newY=BUFFER+T_HEIGHT;
			if(P.x<BUFFER) { newX=BUFFER; }
			else if(P.x>BUFFER+T_WIDTH) { newX=BUFFER+T_WIDTH; }
			else{ newX=P.x; }
		}
		else if(T_WIDTH*P.y<-2*T_HEIGHT*P.x+2*T_HEIGHT*BUFFER+BUFFER*T_WIDTH+T_HEIGHT*T_WIDTH){  //project onto left side
			double m=-2.0*(double)(T_HEIGHT)/(double)(T_WIDTH);
			double b=(double)(2*T_HEIGHT*BUFFER+T_WIDTH*BUFFER+T_HEIGHT*T_WIDTH)/(double)(T_WIDTH);
			newX=(int)(((double)(P.x)+m*((double)(P.y)-b))/(m*m+1.0));
			newY=(int)((b+m*(double)(P.x)+(double)(P.y)*m*m)/(m*m+1.0));
			if(newX<BUFFER){
				newX=BUFFER;
				newY=BUFFER+T_HEIGHT;
			}
			if(newY<BUFFER){
				newX=BUFFER+(T_WIDTH/2);
				newY=BUFFER;
			}
		}
		else if(T_WIDTH*P.y<2*T_HEIGHT*P.x-2*T_HEIGHT*BUFFER-T_HEIGHT*T_WIDTH+BUFFER*T_WIDTH){  //project onto right side
			double m=2.0*(double)(T_HEIGHT)/(double)(T_WIDTH);
			double b=(double)(-2*T_HEIGHT*BUFFER+T_WIDTH*BUFFER-T_HEIGHT*T_WIDTH)/(double)(T_WIDTH);
			newX=(int)(((double)(P.x)+m*((double)(P.y)-b))/(m*m+1.0));
			newY=(int)((b+m*(double)(P.x)+(double)(P.y)*m*m)/(m*m+1.0));
			if(newX>BUFFER+T_WIDTH){
				newX=BUFFER+T_WIDTH;
				newY=BUFFER+T_HEIGHT;
			}
			if(newY<BUFFER){
				newX=BUFFER+(T_WIDTH/2);
				newY=BUFFER;
			}
		}
		else{ // we are in the triangle
			newX=P.x;
			newY=P.y;
		}
		return new Point(newX,newY);
	}

//
//  The following two functions translate back and forth between points on the screen and triangles
//
	public Point findPoint(double X, double Y, double Z) { 
		return new Point(BUFFER+(int)((0.5*Y+Z)*(double)(T_WIDTH)/Math.PI),
							BUFFER+(int)((X+Z)*(double)(T_HEIGHT)/Math.PI));
	}

	public double[] findTriangle(Point P) { 
		double angles[]={0.0,0.0,0.0};
		double alpha=(double)(T_HEIGHT+BUFFER-P.y)/(double)(T_HEIGHT);
		double beta=(double)(P.x-BUFFER)/(T_WIDTH);
		angles[0]=(1-0.5*alpha-beta)*Math.PI;
		angles[1]=alpha*Math.PI;
		angles[2]=(beta-0.5*alpha)*Math.PI;
//  if "close" to an edge push back a bit
		if( Math.min(angles[0],Math.min(angles[1],angles[2])) < 0.01 ) {
			angles[0]=Math.PI*(angles[0]+0.01)/(Math.PI+0.03);
			angles[1]=Math.PI*(angles[1]+0.01)/(Math.PI+0.03);
			angles[2]=Math.PI*(angles[2]+0.01)/(Math.PI+0.03);
			}
		return angles;
	}

//
//  The following function checks to see if point A is the closest to the current cursor
//
	public static boolean AisClosest(Point P) {
		if((P.x-locationA.x)*(P.x-locationA.x)+(P.y-locationA.y)*(P.y-locationA.y)<
			(P.x-locationB.x)*(P.x-locationB.x)+(P.y-locationB.y)*(P.y-locationB.y)){ return true; }
		else { return false; }
	}

//
//  Distance between two points
//
	public static double Distance(Point A, Point B) {
		return Math.sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
	}

//
//  The following functions take a triangle with angles at points (X,Y,Z)
//  and generates the angle <PXY where P is the point of interest.
//
	public static double bisector(double X, double Y, double Z){
		return (X/2.0);
	}
	
	public static double median(double X, double Y, double Z){
		double y_part=Math.sin(X)*Math.sin(Y);
		double x_part=Math.sin(Z)+Math.cos(X)*Math.sin(Y);
		if(x_part<0){
			return (Math.PI+Math.atan(y_part/x_part));
		}
		else if(x_part>0){
			return Math.atan(y_part/x_part);
		}
		else{
			return (Math.PI/2.0);
		}
	}
	
	public static double Gergonne(double X, double Y, double Z){
		double p,q,r,s,t;
		p=Math.sin(Z)/Math.sin(X);
		q=Math.cos(Y)*p;
		r=Math.sin(Y)*p;
		s=Math.cos(0.5*Y)*Math.sin(0.5*Z)/Math.sin(0.5*(Y+Z));
		t=(q-s)*(q-s)+r*r;

		return Math.acos((p*p+t-s*s)/(2.0*p*Math.sqrt(t)));
	}

	public static double Lemoine(double X, double Y, double Z){
		double y_part=Math.sin(X)*Math.sin(Y);
		double x_part=Math.sin(Z)+Math.cos(X)*Math.sin(Y);
		double median;
		if(x_part<0){
			median=Math.PI+Math.atan(y_part/x_part);
		}
		else if(x_part>0){
			median=Math.atan(y_part/x_part);
		}
		else{
			median=Math.PI/2.0;
		}
		return X-median;
	}

	public static double Spieker(double X, double Y, double Z) {
		double p,q,r,s,t,u,v,x;
		double SpiekerX,SpiekerY;
		p=Math.sin(Y)/Math.sin(X+Y);
		q=Math.cos(X)*p;
		r=Math.sin(X)*p;
		s=Math.sin(0.5*Y)/Math.sin(0.5*X+0.5*Y);
		t=Math.cos(0.5*X)*s;
		u=Math.sin(0.5*X)*s;
		SpiekerX=0.5*(1.0+q-t);
		SpiekerY=0.5*(r-u);
		v=SpiekerX*SpiekerX+SpiekerY*SpiekerY;
		x=(1.0-SpiekerX)*(1.0-SpiekerX)+SpiekerY*SpiekerY;

		return Math.acos((v+1.0-x)/(2.0*Math.sqrt(v)));
	}

	public static double isoSpieker(double X, double Y, double Z) {
		double p,q,r,s,t,u,v,x;
		double SpiekerX,SpiekerY;
		p=Math.sin(Y)/Math.sin(X+Y);
		q=Math.cos(X)*p;
		r=Math.sin(X)*p;
		s=Math.sin(0.5*Y)/Math.sin(0.5*X+0.5*Y);
		t=Math.cos(0.5*X)*s;
		u=Math.sin(0.5*X)*s;
		SpiekerX=0.5*(1.0+q-t);
		SpiekerY=0.5*(r-u);
		v=SpiekerX*SpiekerX+SpiekerY*SpiekerY;
		x=(1.0-SpiekerX)*(1.0-SpiekerX)+SpiekerY*SpiekerY;

		return X-Math.acos((v+1.0-x)/(2.0*Math.sqrt(v)));
	}
}
