Drawing a circle based from user input in Java

user3768057 picture user3768057 · Sep 21, 2014 · Viewed 8.6k times · Source

I have tried over and over to find a solution to this problem, but I seem to can't understand how to solve it.

I'm trying to write a simple program to draw a circle with these specifications in the program:

  1. Ask the user for the radius of a circle using an input box (JOptionPane).
  2. Ask the user for the x and y coordinates of the circle in an input box (JOptionPane).
  3. Calculate circumference of the circle.
  4. Calculate area of the circle.
  5. Display area and circumference below drawing of circle.

Somehow, it won't draw the circle, and calculate the circumference and area. Can you please help me find a solution to this problem?

Here are my codes:

------- main -------

package circleSquarePackage;

import circleSquarePackage.Circle;

import javax.swing.JOptionPane;
import javax.swing.JFrame;

public class CircleTester {

    public static void main(String[] args) 
        {

        JFrame frame = new JFrame();

        // Display Circle in the frame
        frame.setSize(600, 500);
        frame.setTitle("CircleSquare");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Create Circle Components
        Circle round = new Circle();
        frame.add(round);

        frame.setVisible(true);

    }

}

-------- other class ---------

package circleSquarePackage;

import javax.swing.JComponent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Ellipse2D.Double;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;

import javax.swing.JOptionPane;


public class Circle extends JComponent {
    // Member instance field
    private Ellipse2D.Double sphere;
    private int radius;
    double circumference, area;

    String x1; // starting x co-ordinate
    String y1; // starting y co-ordinate
    String r1; // radius for circle
    String draw;

    int x = 0;
    int y = 0;
    int r = 0;


    // Default constructor
    public Circle()
    {
        sphere = new Ellipse2D.Double();
    }

    // User defined constructor
    public Circle(int xAxis, int yAxis, int rad)
    {
        rad = r;
        xAxis = x;
        yAxis = y;
        sphere = new Ellipse2D.Double(xAxis, yAxis, rad, rad);
    }

    // Accessor methods
    public double calcCircumference()
    {
    return circumference = 2 * Math.PI * radius;
    }

    public double calcArea()
    {
        return area = Math.PI * radius * radius;
    }

    // Methods
    public void inputX()
    {
        x1 = JOptionPane.showInputDialog(null, "Input center (x value): ");
        x = Integer.parseInt(x1);
    }

    public void inputY()
    {
        y1 = JOptionPane.showInputDialog(null, "Input center (y value): ");
        y = Integer.parseInt(y1);

    }

    public void inputRadius()
    {
        r1 = JOptionPane.showInputDialog(null, "Input radius: ");
        r = Integer.parseInt(r1);
    }

    public void paintComponent(Graphics g)
    {
        // Cast 1D to 2D graphics
        Graphics2D g2 = (Graphics2D) g;

        g2.setColor(Color.BLUE); // set circle color to blue
        g2.fill(sphere);
        g2.draw(sphere);

        g2.setColor(Color.BLUE);
        g2.drawString("Circumference = " + calcCircumference(), 5, 450);
        g2.drawString("Area = " + calcCircumference(), 200, 450);
    }
}

UPDATE BASED ON PEESKILLET'S ANSWER

Circle class

package circleSquarePackage;

import javax.swing.JComponent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Ellipse2D.Double;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.JOptionPane;


public class Circle extends JComponent {
    // Member instance field
    private Ellipse2D.Double sphere;
    private int radius;
    double circumference, area;


    // Default constructor
    public Circle()
    {
        sphere = new Ellipse2D.Double();
    }

    public void setSphere(Ellipse2D.Double sphere) {
        this.sphere = sphere;
        repaint();
    }

    // User defined constructor
    public Circle(int xAxis, int yAxis, int rad)
    {
        sphere = new Ellipse2D.Double(xAxis, yAxis, rad, rad);
    }

    // Accessor methods
    public double calcCircumference()
    {
    return circumference = 2 * Math.PI * radius;
    }

    public double calcArea()
    {
        return area = Math.PI * radius * radius;
    }

    // Methods
    public void inputX()
    {
        int x = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter x"));
        double y = sphere.y; // why is there a double y here when it asks for x?
        Ellipse2D.Double newSphere = new Ellipse2D.Double(x, y, size, size);
        setSphere(newSphere);
    }

    public void inputY()
    {
        int y = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter y"));
        double x = sphere.x; 
        Ellipse2D.Double newSphere = new Ellipse2D.Double(x, y, size, size);
        setSphere(newSphere);
    }

    public void inputRadius() 
    {
        // is this how I do for radius?
        int r = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter radius"));
        int size = r * 2;
        Ellipse2D.Double newSphere = new Ellipse2D.Double(x, y, size, size);
        setSphere(newSphere);
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;

        g2.setColor(Color.BLUE); // set circle color to blue
        g2.fill(sphere);
        g2.draw(sphere);

        g2.drawString("Circumference: " + calcCircumference(), 5, 490);

    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(600, 500);
    }
}

-------- CircleTester class --------

package circleSquarePackage;

import circleSquarePackage.Circle;

import javax.swing.JOptionPane;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Ellipse2D.Double;

public class CircleTester {
    /*
    private static Ellipse2D.Double getCircle() {
        int x = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter integer for x-coordinates:"));
        int y = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter integer for y-coordinates:"));
        int radius = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter radius of circle:"));
        int size = radius * 2;
        return new Ellipse2D.Double(x, y, size, size);
    }*/

    public static void main(String[] args) 
    {
        // Is there a way to call the inputX(), inputY(), and inputRadius() here?

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame();


                frame.setTitle("CircleSquare");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                Circle round = new Circle();
                frame.add(round);

                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                /*
                Ellipse2D.Double newCircle = getCircle();
                round.setSphere(newCircle);
                */
            }
        });

    }

}

Answer

Paul Samsotha picture Paul Samsotha · Sep 21, 2014

In your no-arg Circle constructor, you're creating a sphere (default Ellipse2D.Double) before you get the values. You should create the sphere based on the those values, like you are in the three-arg constructor

From Ellipse2D.Double

Ellipse2D.Double()
Constructs a new Ellipse2D, initialized to location (0, 0) and size (0, 0).

Another Design Possibility

  1. Have a setSphere method in the Circle class, that you can set the ellipse and repaint

    public void setSphere(Ellepse2D.Double sphere) {
        this.sphere = sphere;
        repaint();
    }
    
  2. Do all your JOptionPane still after the frame is shown. Just seems right. Then when you get the values, you can call setSphere on the Circle class, with a new Ellipse2D.Double and it will show in the panel.

Other Notes:

  • When doing custom painting, better to override getPreferredSize() on the painted component, and just call pack() on the frame, instead of setSize()

  • See Initial Threads. Swing apps should be run on the Launched on the Event Dispatch Thread.

  • Also you don't need redundant x, y, etc values in the Circle class. They are already being held by the Ellipse object. If you need to get some values, just get it from that, i.e. sphere.x, sphere.y., etc

Here's a refactor of the suggestions I mentioned above. (You will also want to do some checks to make sure an number is actually type. I was being lazy)

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class CircleTester {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame();

                frame.setTitle("CircleSquare");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                Circle round = new Circle();
                frame.add(round);

                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                Ellipse2D.Double newCircle = getCircle();
                round.setSphere(newCircle);
            }
        });

    }

    private static Ellipse2D.Double getCircle() {
        int x = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter x"));
        int y = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter x"));
        int radius = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter x"));
        int size = radius * 2;
        return new Ellipse2D.Double(x, y, size, size);
    }
}

class Circle extends JComponent {

    private Ellipse2D.Double sphere;

    public Circle() {
        sphere = new Ellipse2D.Double();
    }

    public void setSphere(Ellipse2D.Double sphere) {
        this.sphere = sphere;
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;

        g2.setColor(Color.BLUE); // set circle color to blue
        g2.fill(sphere);
        g2.draw(sphere);

    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(300, 300);
    }
}

UPDATE

I was wondering how can I put the JOptionPane asking for user input in the Circle class under public void inputX(), public void inputY(), and public void inputRadius(), and then call those in the main in the CircleTester class?

What you can do is just call the JOPtionPane in each method. Say you want to just get x, then call the JOptionPane, and based on that value, create a new Ellipse from the old Ellipse using the old values and just using the new x. Then call setSphere. Something like

public void inputX() {
    int x = Integer.parseInt(JOptionPane.showInputDialog(null, "Enter x"));
    double y = sphere.y;
    double height = sphere.height;
    double width = sphere.width;
    Ellips2D.Double newSpehere = new Ellipse2D.Double(x, y, width, height);
    setSphere(newSphere);
}

You can do this will the others as well. This way, when you call the method, once you input, it will only change the one variable.

You can also have one method to get all the variable, kind of like I did in my example.