//Trigonometry.java //basic unit circle value of trig functions import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.border.*; import java.util.*; import java.text.*; public class Trigonometry extends JApplet implements ActionListener { double r, theta, x, y, tan, arc; JPanel controlPanel; JLabel rLabel, thetaLabel, xLabel, yLabel, tanLabel, arcLabel; JTextField rTextField, thetaTextField, xTextField, yTextField, tanTextField, arcTextField; int endX, endY; //so mouseListener has access int rLen; int midX, midY; DecimalFormat d4, d2, i1; public void init() { String input; d4 = new DecimalFormat ("0.0000"); d2 = new DecimalFormat ("0.00"); i1 = new DecimalFormat ("0.000"); Container container = getContentPane(); container.setLayout( new BorderLayout() ); controlPanel = new JPanel(); controlPanel.setLayout(new FlowLayout()); controlPanel.setBackground(Color.MAGENTA); controlPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED)); container.add(controlPanel,BorderLayout.NORTH); rLabel = new JLabel( "r" ); controlPanel.add( rLabel ); r = 1.0; rTextField = new JTextField(""+d2.format(r),4); rTextField.setHorizontalAlignment(JTextField.RIGHT); rTextField.addActionListener( this ); controlPanel.add( rTextField ); thetaLabel = new JLabel( "ANGLE \u0398\u00B0" ); controlPanel.add( thetaLabel ); thetaTextField = new JTextField("30.0",4); thetaTextField.setBackground(Color.GREEN); theta = Double.parseDouble(thetaTextField.getText()); thetaTextField.setHorizontalAlignment(JTextField.RIGHT); thetaTextField.addActionListener( this ); controlPanel.add( thetaTextField ); /* rLabel = new JLabel( " LENGTHS: radius" ); controlPanel.add( rLabel ); rTextField = new JTextField("1.0",4); r = Double.parseDouble(rTextField.getText()); rTextField.setBackground(new Color(200,200,255)); //rTextField.setForeground(Color.WHITE); rTextField.setHorizontalAlignment(JTextField.RIGHT); rTextField.addActionListener( this ); controlPanel.add( rTextField ); */ xLabel = new JLabel( "x =r cos \u0398" ); controlPanel.add( xLabel ); x = r * Math.cos(Math.toRadians(theta)); xTextField = new JTextField(""+d2.format(x),4); xTextField.setBackground(Color.CYAN); xTextField.setHorizontalAlignment(JTextField.RIGHT); xTextField.addActionListener( this ); controlPanel.add( xTextField ); yLabel = new JLabel( "y =r sin \u0398" ); controlPanel.add( yLabel ); y = r * Math.sin(Math.toRadians(theta)); yTextField = new JTextField(""+d2.format(y),4); yTextField.setBackground(Color.YELLOW); yTextField.setHorizontalAlignment(JTextField.RIGHT); yTextField.addActionListener( this ); controlPanel.add( yTextField ); tanLabel = new JLabel( "tan =y/x=slope=sin\u0398/cos\u0398" ); controlPanel.add( tanLabel ); tan = y / x; tanTextField = new JTextField(""+d2.format(tan),4); tanTextField.setBackground(Color.BLUE); tanTextField.setHorizontalAlignment(JTextField.RIGHT); tanTextField.addActionListener( this ); controlPanel.add( tanTextField ); arcLabel = new JLabel( "arc" ); controlPanel.add( arcLabel ); arc = arcLength(r,theta); arcTextField = new JTextField(""+d4.format(arc),4); arcTextField.setBackground(Color.PINK); arcTextField.setHorizontalAlignment(JTextField.RIGHT); arcTextField.addActionListener( this ); controlPanel.add( arcTextField ); /* sectorAreaLabel = new JLabel( " AREAS: sector" ); controlPanel.add( sectorAreaLabel ); sectorArea = sectorArea(r,a); sectorAreaTextField = new JTextField(""+d4.format(sectorArea),4); sectorAreaTextField.setBackground(Color.PINK); //sectorAreaTextField.setForeground(Color.WHITE); sectorAreaTextField.setHorizontalAlignment(JTextField.RIGHT); sectorAreaTextField.addActionListener( this ); controlPanel.add( sectorAreaTextField ); triangleAreaLabel = new JLabel( "- triangle" ); controlPanel.add( triangleAreaLabel ); triangleArea = triangleArea(r,a); triangleAreaTextField = new JTextField(""+d4.format(triangleArea),4); triangleAreaTextField.setBackground(Color.PINK); triangleAreaTextField.setHorizontalAlignment(JTextField.RIGHT); triangleAreaTextField.addActionListener( this ); controlPanel.add( triangleAreaTextField ); segmentAreaLabel = new JLabel( "= segment" ); controlPanel.add( segmentAreaLabel ); segmentArea = sectorArea - triangleArea; segmentAreaTextField = new JTextField(""+d4.format(segmentArea),4); segmentAreaTextField.setBackground(Color.PINK); segmentAreaTextField.setHorizontalAlignment(JTextField.RIGHT); //segmentAreaTextField.setEditable(false); segmentAreaTextField.addActionListener( this ); controlPanel.add( segmentAreaTextField ); */ rLen = getHeight()/3; //mouseListener needs midX = getWidth()/2; midY = getHeight()/2; rTextField.setText("1.0"); rTextField.postActionEvent(); //fires listener //incantation that responds to mouse move events: addMouseMotionListener( new MouseMotionAdapter() { public void mouseDragged( MouseEvent event) { int px = event.getX(); int py = event.getY(); double x=-999, y=-999; //inits to fake out compiler for showStatus double mouseDistance = distanceFormula(px,py, endX,endY); if (mouseDistance < 50) { //within 50 of endPoint if (theta < 180) { //left right: px change /*if (px < endX) //leftward //scale px to actual x, then a=acos(x/r) x = (double)endX/rLen - (double)px/rLen; else x = (double)px/rLen - (double)endX/rLen; */ x = (double)(px-midX)/rLen; if (x <= r && x >= -r) //avoid going under?? at 0 theta = Math.toDegrees(Math.acos(x/r)); //if (a < 0) // ?? nope // a = 0; } else if (theta > 180) { //left right: opp. of upper half circle x = (double)(midX-px)/rLen; if (x <= r && x >= -r) //avoid going under?? at 360 theta = 180 + Math.toDegrees(Math.acos(x/r)); } else { //a==180 ?? if (py != midY) { //has to be at least one pixel above or below y = (double)(py-midY)/rLen; theta = 180 + Math.toDegrees(Math.asin(y/r)); } } //showStatus("("+px+","+(getHeight()-py)+") x="+x+" y="+y+" a="+a); thetaTextField.setText(""+d2.format(theta)); thetaTextField.postActionEvent(); } } } ); } public void setTextFields( double r, double theta, double x, double y, double tan, double arc) { rTextField.setText(""+d2.format(r)); thetaTextField.setText(""+d2.format(theta)); xTextField.setText(""+d2.format(x)); yTextField.setText(""+d2.format(y)); tanTextField.setText(""+d2.format(tan)); arcTextField.setText(""+d2.format(arc)); } public void actionPerformed( ActionEvent event ) { try { double thetaTry = Double.parseDouble(thetaTextField.getText()); if (thetaTry<0 || thetaTry>360) { JOptionPane.showMessageDialog(null, "theta angle must be between 0 and 360 degrees", "Trigonometry: Invalid ANGLE DEGREES input", JOptionPane.ERROR_MESSAGE); return; } double rTry = Double.parseDouble(rTextField.getText()); if (rTry<0) { JOptionPane.showMessageDialog(null, "radius must be greater than 0", "Trigonometry: Invalid RADIUS input", JOptionPane.ERROR_MESSAGE); return; } /* double xTry = Double.parseDouble(xTextField.getText()); if (xTry2*r) { JOptionPane.showMessageDialog(null, "x must be between 0 and diameter", "Trigonometry: Invalid X input", JOptionPane.ERROR_MESSAGE); return; } double yTry = Double.parseDouble(yTextField.getText()); if (yTry<0 || yTry>r) { JOptionPane.showMessageDialog(null, "y must be between 0 and radius", "Trigonometry: Invalid Y input", JOptionPane.ERROR_MESSAGE); return; } */ double tanTry = Double.parseDouble(tanTextField.getText()); //check it's a number double arcTry = Double.parseDouble(arcTextField.getText()); if (arcTry<0) { // || arcTry>2*Math.PI+.001) { JOptionPane.showMessageDialog(null, "arc must be between 0 and 2\u03C0 radians", "Trigonometry: Invalid ARC LENGTH input", JOptionPane.ERROR_MESSAGE); return; } } catch (NumberFormatException e) { JOptionPane.showMessageDialog(null, "all values must be numbers", "Trigonometry: Invalid input", JOptionPane.ERROR_MESSAGE); return; } if ( event.getSource() == thetaTextField ) { theta = Double.parseDouble(thetaTextField.getText()); //rad = Math.toRadians(theta); arc = arcLength(r,theta); //sectorArea = sectorArea(r,a); x = r * Math.cos(Math.toRadians(theta)); y = r * Math.sin(Math.toRadians(theta)); tan = y / x; //if (theta <= 180) { //c = chordLength(r,a); //h = heightLength(r,c); //triangleArea = triangleArea(r,a); //segmentArea = sectorArea - triangleArea; setTextFields(r,theta,x,y,tan,arc); // } /* else { c = 0; h = 0; triangleArea = 0; segmentArea = 0; setTextFields(r,theta,x,y); } */ repaint(); } else if ( event.getSource() == rTextField ) { r = Double.parseDouble(rTextField.getText()); thetaTextField.postActionEvent(); //fires its listener //setTextFields(r,theta,x,y,tan,arc); //repaint(); } else if ( event.getSource() == xTextField ) { x = Double.parseDouble(xTextField.getText()); theta = Math.toDegrees(Math.acos(x)); thetaTextField.setText(""+d2.format(theta)); thetaTextField.postActionEvent(); //fires its listener } else if ( event.getSource() == yTextField ) { y = Double.parseDouble(yTextField.getText()); theta = Math.toDegrees(Math.asin(y)); thetaTextField.setText(""+d2.format(theta)); thetaTextField.postActionEvent(); //fires its listener } else if ( event.getSource() == tanTextField ) { tan = Double.parseDouble(tanTextField.getText()); theta = Math.toDegrees(Math.atan(tan)); thetaTextField.setText(""+d2.format(theta)); thetaTextField.postActionEvent(); //fires its listener } else if ( event.getSource() == arcTextField ) { arc = Double.parseDouble(arcTextField.getText()); theta = thetaFromArcLength(r, arc); thetaTextField.setText(""+d2.format(theta)); thetaTextField.postActionEvent(); //fires its listener } } public void paint (Graphics g) { super.paint( g ); //erase g.setFont( new Font( "SansSerif", Font.BOLD, 12 ) ); g.drawString("Set any parameter ",5,50); g.drawString("OR Drag endpoint of radius around circle",5,70); int w = getWidth(); int h = getHeight(); //int midX = w/2; //int midY = h/2; //int rLen = h/3; //g.drawOval(midX-rLen, midY-rLen, 2*rLen, 2*rLen); g.drawLine(0,midY, w ,midY); g.drawLine(midX,0, midX,h); g.drawArc(midX-rLen,midY-rLen,2*rLen,2*rLen, (int)theta,360-(int)theta); g.fillRect(midX-1,midY-1, 2,2); g.drawString("(0,0)",midX-5,midY+10); //g.setColor(Color.BLUE); //g.drawLine(midX,midY, midX+rLen ,midY); g.drawString("("+r+",0)",midX+rLen+1,midY+10); //int endX, endY; endX = (int)(Math.cos(Math.toRadians(theta)) * rLen + midX); endY = h - (int)(Math.sin(Math.toRadians(theta)) * rLen + midY); g.setColor(Color.CYAN); g.drawLine(midX,midY, endX ,midY); g.setColor(Color.YELLOW); g.drawLine(endX,midY, endX ,endY); g.setColor(Color.BLUE); g.drawLine(midX,midY, endX,endY); if (theta<=90 || theta>=270) { g.setColor(Color.CYAN); g.drawString("("+d2.format(Math.cos(Math.toRadians(theta))*r)+",", endX,endY); g.setColor(Color.YELLOW); g.drawString(""+d2.format(Math.sin(Math.toRadians(theta))*r)+")", endX+30,endY); } else { g.setColor(Color.CYAN); g.drawString("("+d2.format(Math.cos(Math.toRadians(theta))*r)+",", endX-rLen/2,endY); g.setColor(Color.YELLOW); g.drawString(""+d2.format(Math.sin(Math.toRadians(theta))*r)+")", endX-rLen/2+30,endY); } //arc g.setColor(Color.PINK); g.drawArc(midX-rLen,midY-rLen,2*rLen,2*rLen, 0,(int)theta); //theta g.setColor(Color.GREEN); g.drawArc(midX-25,midY-25,50,50, 0,(int)theta); g.drawString("\u0398",midX+30,midY-20); //chord /* if (c != 0) { g.setColor(Color.CYAN); g.drawLine(endX,endY, midX+rLen,midY); //midpoint on chord int chordMidX= (endX+(midX+rLen)) / 2; int chordMidY= (endY+midY) / 2; //height h g.setColor(Color.YELLOW); g.drawLine(midX,midY, chordMidX,chordMidY); } */ //data on screen g.setColor( Color.BLACK ); g.drawString("area="+d2.format(Math.PI*r*r),w-200,45); g.drawString("circumference="+d2.format(2*Math.PI*r),w-200,60); /* if (theta < 180) { g.setColor( Color.CYAN); g.drawString("base midpoint ("+d2.format((r+Math.cos(Math.toRadians(theta))*r)/2)+ ","+d2.format(Math.sin(Math.toRadians(theta))*r/2)+")",w-200,75); } */ } /* public double chordLength(double r, double a) { return Math.sqrt(2*r*r - 2*r*r*Math.cos(Math.toRadians(a))); } */ //height of isocelese triangle /* public double heightLength(double r, double c) { return Math.sqrt(r*r - c*c/4); } */ public double arcLength(double r, double theta) { return 2*Math.PI*r * (theta/360); } public double thetaFromArcLength(double r, double arc) { return (arc*360)/(2*Math.PI*r); } /* public double triangleArea(double r, double a) { return r*r/2 * Math.sin(Math.toRadians(a)); } */ /* public double sectorArea(double r, double a) { return Math.PI*r*r * (a/360); } */ public double distanceFormula (double x1, double y1, double x2, double y2){ return Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); } //use Newton's method to solve g=PIr^2*Alpha/360 - r^2/2*sin(Alpha2PI/360) //letting x=Alpha2PI/360 K=2g/r^2. g is segmentArea // K=x-sin(x) //f(x)=K-x+sin(x) f'(x)=-1+cos(x) //x(n+1)=x(n) - (K-xn+sin(xn))/(-1+cos(xn) //Thanks to Andy Won for this math. //returns alpha in degrees. alpha=2PI/360 x public double NewtonsAlpha(double r, double g) { double xn, xn1=1, fx; //fakeout compiler double K=2*g/(r*r); xn = 1; //initial value. crucial choice. implies alpha=57 (1 rad) OK? fx = K - xn + Math.sin(xn); int iterations=1; while (Math.abs(fx) > .00001 && iterations<200) { //no more than 200 loops? .00001 OK? xn1 = xn - fx/(-1+Math.cos(xn)); fx = K - xn1 + Math.sin(xn1); xn = xn1; iterations++; } //seems to work for any segmentArea between 0 and semicircleArea (alpha=180) showStatus("iterations"+iterations); return 180/Math.PI * xn1; } }