Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing a circle based from user input in Java

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);
                */
            }
        });

    }

}
like image 467
user3768057 Avatar asked Feb 11 '23 23:02

user3768057


2 Answers

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.

like image 140
Paul Samsotha Avatar answered Feb 15 '23 11:02

Paul Samsotha


  1. Get all UI out of the Circle class, meaning remove all those JOptionPane calls.
  2. Instead all user interaction should be in your CircleTester class.
  3. The Circle class should instead focus not on user IO, but simply on drawing a Circle that is defined by its properties.
  4. Give your Circle class a constructor that takes meaningful parameters.
  5. It seems to me that Ellipse2D is not needed as you can simply draw your circle with a Graphics object by calling g.drawOval(....)`, thereby simplifying your program.
  6. After you get the input parameters inside of CircleTester, create Circle objects passing in the parameters obtained from CircleTest, and display your Circle.
  7. Also, don't forget to call the super.paintComponent(g) method within your paintComponent method override.
  8. Minor nitpick, but I would not declare circumference and area variables, but rather give your Circle class getCircumference() and getArea() methods that would calculate these on the spot when needed. Otherwise if you give Circle a setRadius(...) method, you'll have to remember to change the other fields within the setter method, and if you make radius or the other fields public, all heck can break loose. Play safe and just have accessor methods for those two properties and not fields.
like image 27
Hovercraft Full Of Eels Avatar answered Feb 15 '23 09:02

Hovercraft Full Of Eels