//CircleSegment.java //calculate segment, sector, chord etc of circle 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 CircleSegment extends JApplet implements ActionListener { double r, a, rad, c, h, arc, sectorArea, triangleArea, segmentArea; JPanel controlPanel; JLabel rLabel, aLabel, radLabel, cLabel, arcLabel, hLabel, sectorAreaLabel,triangleAreaLabel,segmentAreaLabel; JTextField rTextField, aTextField, radTextField, cTextField, arcTextField, hTextField, sectorAreaTextField, triangleAreaTextField, segmentAreaTextField; int endX, endY; //so mouseListener has access int rLen; int midX, midY; //int midY = h/2; DecimalFormat d4, i1; public void init() { String input; d4 = new DecimalFormat ("0.0000"); 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); aLabel = new JLabel( "ANGLE \u03B1\u00B0" ); controlPanel.add( aLabel ); aTextField = new JTextField("90.0",4); a = Double.parseDouble(aTextField.getText()); aTextField.setHorizontalAlignment(JTextField.RIGHT); aTextField.addActionListener( this ); controlPanel.add( aTextField ); radLabel = new JLabel( "rad" ); controlPanel.add( radLabel ); rad = Math.toRadians(a); radTextField = new JTextField(""+d4.format(rad),4); radTextField.setHorizontalAlignment(JTextField.RIGHT); radTextField.addActionListener( this ); controlPanel.add( radTextField ); 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 ); cLabel = new JLabel( "chord" ); controlPanel.add( cLabel ); c = chordLength(r,a); cTextField = new JTextField(""+d4.format(c),4); cTextField.setBackground(Color.CYAN); cTextField.setHorizontalAlignment(JTextField.RIGHT); cTextField.addActionListener( this ); controlPanel.add( cTextField ); hLabel = new JLabel( "h" ); controlPanel.add( hLabel ); h = heightLength(r,c); hTextField = new JTextField(""+d4.format(h),4); hTextField.setBackground(Color.YELLOW); hTextField.setHorizontalAlignment(JTextField.RIGHT); hTextField.addActionListener( this ); controlPanel.add( hTextField ); arcLabel = new JLabel( "arc" ); controlPanel.add( arcLabel ); arc = arcLength(r,a); 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 (a < 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 a = Math.toDegrees(Math.acos(x/r)); //if (a < 0) // ?? nope // a = 0; } else if (a > 180) { //left right: opp. of upper half circle x = (double)(midX-px)/rLen; if (x <= r && x >= -r) //avoid going under?? at 360 a = 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; a = 180 + Math.toDegrees(Math.asin(y/r)); } } //showStatus("("+px+","+(getHeight()-py)+") x="+x+" y="+y+" a="+a); aTextField.setText(""+i1.format(a)); aTextField.postActionEvent(); } } } ); } public void setTextFields(double a, double rad, double r, double c, double h, double arc, double sectorArea, double triangleArea, double segmentArea) { aTextField.setText(""+i1.format(a)); radTextField.setText(""+d4.format(rad)); rTextField.setText(""+d4.format(r)); cTextField.setText(""+d4.format(c)); hTextField.setText(""+d4.format(h)); arcTextField.setText(""+d4.format(arc)); sectorAreaTextField.setText(""+d4.format(sectorArea)); triangleAreaTextField.setText(""+d4.format(triangleArea)); segmentAreaTextField.setText(""+d4.format(segmentArea)); } public void actionPerformed( ActionEvent event ) { try { double aTry = Double.parseDouble(aTextField.getText()); if (aTry<0 || aTry>360) { JOptionPane.showMessageDialog(null, "angle must be between 0 and 360 degrees", "CircleSegment: Invalid ANGLE DEGREES input", JOptionPane.ERROR_MESSAGE); return; } double radTry = Double.parseDouble(radTextField.getText()); if (radTry<0 || radTry>2*Math.PI+.001) { JOptionPane.showMessageDialog(null, "angle must be between 0 and 2\u03C0 radians", "CircleSegment: Invalid ANGLE RADIANS input", JOptionPane.ERROR_MESSAGE); return; } double rTry = Double.parseDouble(rTextField.getText()); if (rTry<0) { JOptionPane.showMessageDialog(null, "radius must be greater than 0", "CircleSegment: Invalid RADIUS input", JOptionPane.ERROR_MESSAGE); return; } double cTry = Double.parseDouble(cTextField.getText()); if (cTry<0 || cTry>2*r) { JOptionPane.showMessageDialog(null, "chord length must be between 0 and diameter", "CircleSegment: Invalid CHORD LENGTH input", JOptionPane.ERROR_MESSAGE); return; } double hTry = Double.parseDouble(hTextField.getText()); if (hTry<0 || hTry>r) { JOptionPane.showMessageDialog(null, "height length must be between 0 and radius", "CircleSegment: Invalid HEIGHT input", JOptionPane.ERROR_MESSAGE); return; } double arcTry = Double.parseDouble(arcTextField.getText()); if (arcTry<0 || arcTry>2*Math.PI*r +0.001) { JOptionPane.showMessageDialog(null, "arc length must be between 0 and circumference", "CircleSegment: Invalid ARC LENGTH input", JOptionPane.ERROR_MESSAGE); return; } double sectorTry = Double.parseDouble(sectorAreaTextField.getText()); if (sectorTry<0 ||sectorTry>Math.PI*r*r +0.001) { JOptionPane.showMessageDialog(null, "sector area must be between 0 and circle's area", "CircleSegment: Invalid SECTOR AREA input", JOptionPane.ERROR_MESSAGE); return; } double triangleTry = Double.parseDouble(triangleAreaTextField.getText()); if (triangleTry<0 || triangleTry>r*r/2 +0.001) { // max possible triangle at 90 deg JOptionPane.showMessageDialog(null, "triangle area must be between 0 and 90\u00B0 max", "CircleSegment: Invalid TRIANGLE AREA input", JOptionPane.ERROR_MESSAGE); return; } double segmentTry = Double.parseDouble(segmentAreaTextField.getText()); if (segmentTry<0 || segmentTry>Math.PI*r*r/2 +0.001) { JOptionPane.showMessageDialog(null, "segment area must be between 0 and semicircle area", "CircleSegment: Invalid SEGMENT AREA input", JOptionPane.ERROR_MESSAGE); return; } } catch (NumberFormatException e) { JOptionPane.showMessageDialog(null, "all values must be numbers", "CircleSegment: Invalid input", JOptionPane.ERROR_MESSAGE); return; } if ( event.getSource() == aTextField ) { a = Double.parseDouble(aTextField.getText()); rad = Math.toRadians(a); arc = arcLength(r,a); sectorArea = sectorArea(r,a); if (a <= 180) { c = chordLength(r,a); h = heightLength(r,c); triangleArea = triangleArea(r,a); segmentArea = sectorArea - triangleArea; setTextFields(a,rad,r,c,h,arc,sectorArea,triangleArea,segmentArea); } else { c = 0; h = 0; triangleArea = 0; segmentArea = 0; setTextFields(a,rad,r,c,h,arc,sectorArea,triangleArea,segmentArea); } repaint(); } else if ( event.getSource() == radTextField ) { rad = Double.parseDouble(radTextField.getText()); a = Math.toDegrees(rad); aTextField.setText(""+i1.format(a)); aTextField.postActionEvent(); //fires its listener } else if ( event.getSource() == rTextField ) { r = Double.parseDouble(rTextField.getText()); c = chordLength(r,a); h = heightLength(r,c); arc = arcLength(r,a); sectorArea = sectorArea(r,a); triangleArea = triangleArea(r,a); segmentArea = sectorArea - triangleArea; setTextFields(a,rad,r,c,h,arc,sectorArea,triangleArea,segmentArea); repaint(); } else if ( event.getSource() == cTextField ) { c = Double.parseDouble(cTextField.getText()); a = Math.toDegrees(Math.acos(1-c*c/(2*r*r))); aTextField.setText(""+i1.format(a)); aTextField.postActionEvent(); //fires its listener } else if ( event.getSource() == hTextField ) { h = Double.parseDouble(hTextField.getText()); a = Math.toDegrees(Math.acos(2*h*h/(r*r)-1)); aTextField.setText(""+i1.format(a)); aTextField.postActionEvent(); //fires its listener } else if ( event.getSource() == arcTextField ) { arc = Double.parseDouble(arcTextField.getText()); a = 180*arc/(Math.PI*r); aTextField.setText(""+i1.format(a)); aTextField.postActionEvent(); //fires its listener } else if ( event.getSource() == sectorAreaTextField ) { sectorArea = Double.parseDouble(sectorAreaTextField.getText()); a = 360*sectorArea / (Math.PI*r*r); aTextField.setText(""+i1.format(a)); aTextField.postActionEvent(); //fires its listener } else if ( event.getSource() == triangleAreaTextField ) { triangleArea = Double.parseDouble(triangleAreaTextField.getText()); a = Math.toDegrees(Math.asin(2*triangleArea/(r*r))); aTextField.setText(""+i1.format(a)); aTextField.postActionEvent(); //fires its listener } else if ( event.getSource() == segmentAreaTextField ) { /*JOptionPane.showMessageDialog(null, "Sorry, not yet implemented!\nReverting to previous value.", "CircleSegment: Unimplemented", JOptionPane.WARNING_MESSAGE); segmentAreaTextField.setText(""+d4.format(segmentArea)); */ segmentArea = Double.parseDouble(segmentAreaTextField.getText()); a = NewtonsAlpha(r,segmentArea); aTextField.setText(""+i1.format(a)); aTextField.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.drawArc(midX-rLen,midY-rLen,2*rLen,2*rLen, (int)a,360-(int)a); 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); //int endX, endY; endX = (int)(Math.cos(Math.toRadians(a)) * rLen + midX); endY = h - (int)(Math.sin(Math.toRadians(a)) * rLen + midY); g.drawLine(midX,midY, endX,endY); if (a<=90 || a>=270) g.drawString("("+d4.format(Math.cos(Math.toRadians(a))*r)+","+ d4.format(Math.sin(Math.toRadians(a))*r)+")", endX,endY); else g.drawString("("+d4.format(Math.cos(Math.toRadians(a))*r)+","+ d4.format(Math.sin(Math.toRadians(a))*r)+")", endX-rLen/2,endY); //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); } //arc g.setColor(Color.PINK); g.drawArc(midX-rLen,midY-rLen,2*rLen,2*rLen, 0,(int)a); //data on screen g.setColor( Color.BLACK ); g.drawString("area="+d4.format(Math.PI*r*r),w-200,45); g.drawString("circumference="+d4.format(2*Math.PI*r),w-200,60); if (a < 180) { g.setColor( Color.CYAN); g.drawString("base midpoint ("+d4.format((r+Math.cos(Math.toRadians(a))*r)/2)+ ","+d4.format(Math.sin(Math.toRadians(a))*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 a) { return 2*Math.PI*r * (a/360); } 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; } }