4
votes

I have a Java Swing assignment with the following objectives:

  1. When the program starts, it draws 20 unfilled circles, with radius and location of each determined at random.
  2. If the perimeter line of a circle does NOT intersect any other circle, draw the outline of the circle in RED. If it does intersect at least one other circle, draw it in BLACK.
  3. Add a JButton that, each time it is pressed, creates a new set of circles as described above.

I've completed objectives #1 and #3 above, but I'm stumped on objective #2.

Before I present the code, let me give my understanding of the math behind it. There are two ways a circle can NOT intersect another circle:

  1. The circles are too far apart to share a perimeter point, i.e. the distance between their centers is greater than the sum of their radii (d > r1 + r2). Example.
  2. One circle is completely inside another circle, and their perimeters do not touch, i.e. the distance between their centers is less than the difference between their radii (d < |r1 - r2|). Example.

What I've got so far:

  1. To compare circles, they must be specified before they are drawn, so I used a for-loop to store 20 values in arrays for the center coordinates (int[] x, int[] y) and the radius (double[] radius).
  2. Next, I used nested for-loops to iterate through the array and compare two circles, except when a circle is compared with itself (index j = index k). If the circles intersect, g.setColor(Color.RED). If not, g.setColor(Color.BLACK).

When I execute my code, the circles without any overlap are properly colored red. However, some of the overlapping circles are colored red as well. I assume that they were non-overlapping at the time they were drawn, but were intersected thereafter. How do I fix the code to account for this discrepancy in time? (Problem area located near the bottom, in IntersectingCircles class)

import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JButton;

public class ButtonFrame extends JFrame 
{
    private final JButton resetButton = new JButton("Reset");

    public ButtonFrame()
    {
        super("Drawing Random Circles");
        setLayout(new BorderLayout());

        IntersectingCircles intersectingCircles = new IntersectingCircles();

        this.add(intersectingCircles, BorderLayout.CENTER);
        this.add(resetButton, BorderLayout.SOUTH);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setSize(1400, 1400);

        ButtonHandler handler = new ButtonHandler();
        resetButton.addActionListener(handler);
    }

    private class ButtonHandler implements ActionListener
    {       
        @Override
        public void actionPerformed(ActionEvent event)
        {
            reset();
        }
    }

    public static void main(String[] args)
    {
        ButtonFrame buttonFrame = new ButtonFrame();
        buttonFrame.setVisible(true);
    }

    public void reset()
    {
        ButtonFrame buttonFrame = new ButtonFrame();
        buttonFrame.setVisible(true);
    }
}

class IntersectingCircles extends JPanel
{   
    private static final JButton resetButton = new JButton("Reset Circles");
    private static final JFrame frame = new JFrame("Intersecting Circles");

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        this.setBackground(Color.WHITE);

        int[] x = new int[20];
        int[] y = new int[20];
        int[] diameter = new int[20];
        double[] radius = new double[20]; 

        for (int i = 0; i < 20; i++)
        {
            int xCoord = (int)(Math.random() * 600);
            int yCoord = (int)(Math.random() * 600);
            int circleSize = (int)(Math.random() * 550);
            x[i] = xCoord;
            y[i] = yCoord;
            diameter[i] = circleSize;
            radius[i] = circleSize / 2.0;
        }

        for (int j = 0; j < 20; j++)
        {   
            for (int k = 0; k < 20; k++)
            {   
                if (k != j)
                {
                    if (((Math.sqrt((x[k] - x[j]) * (x[k] - x[j]) + (y[k] - y[j])
                        * (y[k] - y[j]))) > (radius[j] + radius[k])) ||
                        ((Math.sqrt((x[k] - x[j]) * (x[k] - x[j]) + (y[k] - y[j])
                        * (y[k] - y[j]))) < (Math.abs(radius[j] - radius[k]))))
                        g.setColor(Color.RED);
                    else
                        g.setColor(Color.BLACK);

                g.drawOval(x[j], y[j], diameter[j], diameter[j]);
                }
                else
                    continue;
            }
        }
    }
}
1
Since it's an assignment I will give only a hint. Separate the logic of identifying circle overlaps and marking circle overlaps true/false (Step 2) from drawing the color boundaries (step 3) completely. Don't try to do Step 2 and 3 together.paisanco
An example of such separation is seen here.trashgod
"I've completed objectives #1 and #3 above, but I'm stumped on objective #2." Presuming there is no limit on how you determine intersection, I would recommend doing it the easy way. See Collision detection with complex shapes for a working example.Andrew Thompson

1 Answers

2
votes

You have logic mistake in if statement inside the cycle - you can set black color then revert to red for some other pair circle. Possible solution draft:

  for (int j = 0; j < 20; j++)
    {   
        g.setColor(Color.RED);  //set non-intersect state
        for (int k = j + 1; k < 20; k++)  //avoid excessive work
        {   
                if (intersect test)
                  {
                    g.setColor(Color.BLACK);
                    break; //can stop here
                  };
            g.drawOval(x[j], y[j], diameter[j], diameter[j]);
        }
}