简体   繁体   English

Java Applets:绘制彼此不重叠的圆吗?

[英]Java Applets: Drawing circles that don't overlap one another?

This program draws 10 circles, in a random position with a random diameter. 该程序在直径随机的位置绘制10个圆。 If the circle's diameter is less than 25 it fills it yellow, more than 50, fills it green otherwise draws it red. 如果圆的直径小于25,则将其填充为黄色,大于50则将其填充为绿色,否则将其绘制为红色。

My problem is I cannot figure out how to stop the circles from overlapping one another. 我的问题是我无法弄清楚如何阻止圆圈彼此重叠。

This is what I've done so far: 到目前为止,这是我所做的:

import java.util.Random;
import java.awt.*;
import javax.swing.JApplet;

public class Circles extends JApplet{   

public void paint(Graphics g) {

    Random rand = new Random();
    int diameter = 0;
    int posX = 0;
    int posY = 0;

    setBackground(Color.CYAN);

    for(int i=1; i <= 5 ; i++ )
    {
        posX = rand.nextInt(500);
        posY = rand.nextInt(500);
        diameter = rand.nextInt(75)+1;

        if (diameter < 25)
        {
            g.setColor(Color.YELLOW);
            g.fillOval(posX, posY,diameter,diameter);
        }

        else if(diameter > 50)
        {
            g.setColor(Color.GREEN);
            g.fillOval(posX, posY,diameter,diameter);
        }
        else
        {
            g.setColor(Color.RED);
            g.drawOval(posX, posY,diameter,diameter);
        }


    }
}
}

Also if you notice my for loop only counts to 5 but I get 10 circles. 另外,如果您注意到我的for循环仅计为5,但我得到10个圆圈。 I was just curious to know why that is? 我只是想知道为什么会这样?

There are several issues here. 这里有几个问题。

  • don't call setBackground(Color) in the paint method. 不要在paint方法中调用setBackground(Color) This method is solely intended for painting. 此方法仅用于绘画。 Other modifications should be done in the constructor or the init method 其他修改应在构造函数或init方法中完成
  • Call super.paint(g) as the first line of your overridden paint method. 调用super.paint(g)作为重写的paint方法的第一行。 This will clear the background 这将清除背景
  • (Minor: Make the scope of variables as small as possible. So declare posX etc. only where they are really needed ) (次要的:使变量的范围尽可能小。因此,仅在真正需要它们的地方声明posX等。)

Concerning the placement of the circles: 关于圆的位置:

At the moment, you are computing new positions and diameters for the circles each time the applet is repainted . 目前, 每次重新绘制小程序时 ,您都在为圆计算新的位置和直径。 So when you resize the window of the applet viewer, new, randomly placed circles will flicker all around. 因此,当您调整小程序查看器的窗口大小时,随机放置的新圆圈将在周围闪烁。 You should create the circles only once (in the constructor, probably). 您只能创建一次圆(可能在构造函数中)。 In the paint method, you should only paint them. paint方法中,只应绘制它们。

More importantly: How to you know that it is possible to place the circles without overlaps? 更重要的是:您如何知道可以放置没有重叠的圆圈? In the end, this leads into the complex world of Circle Packing where you may epmploy some really sophisticated techniques to make sure that you place the circles as tightly as possible. 最后,这导致了Circle Packing的复杂世界,您可以采用一些非常复杂的技术来确保尽可能紧密地放置圆圈。

But for now, you can use a very simple approach: Each time that you create a new circle, you can try (several times) to assign a new position and diameter, until you found a configuration that does not overlap an already existing circle. 但是现在,您可以使用一种非常简单的方法:每次创建一个新的圆时,都可以尝试(几次)指定一个新的位置和直径,直到找到与现有圆不重叠的配置为止。 Although it may seem tempting to use a method like 尽管使用类似的方法似乎很诱人

while (existingCircles.size() < 5)
{
    Circle circle = newRandomCircle();
    if (!circle.overlapsAny(existingCircles))
    {
        existingCircles.add(circle);
    }
}

the problem here is that this algorithm will not terminate when it is not possible to place the circles at all! 这里的问题是,当根本无法放置圆圈时,该算法将不会终止 (Note: This is pseudocode. But ... you should consider writing your code in a way so that it looks like the code above. That is, consider creating a Circle class, for example). (注意:这是伪代码。但是...您应该考虑以某种方式编写代码,使其看起来像上面的代码。例如,考虑创建一个Circle类。)

However, here is a very pragmatic solution, that only tries to place a new circle for 10 times, and simply does not place it when it does not succeed. 但是,这是一个非常实用的解决方案,它仅尝试放置一个新的圆10次,而在不成功时就不会放置它。 While this is not very elegant, it's at least guaranteed to terminate. 虽然这不是很优雅,但至少可以保证终止。

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.JApplet;

public class Circles extends JApplet
{   
    private final List<Point> positions;
    private final List<Integer> diameters;

    public Circles()
    {
        positions = new ArrayList<Point>();
        diameters = new ArrayList<Integer>();

        Random rand = new Random();

        for(int i=1; i <= 5 ; i++ )
        {
            int tries = 10;
            for (int t=0; t<tries; t++)
            {
                int diameter = rand.nextInt(75)+1;
                int posX = rand.nextInt(500);;
                int posY = rand.nextInt(500);
                Point position = new Point(posX, posY);

                if (!overlapsOther(position, diameter))
                {
                    positions.add(new Point(posX, posY));
                    diameters.add(diameter);
                    break;
                }
            }
        }
    }

    private boolean overlapsOther(Point position, int diameter)
    {
        int radius = diameter/2;
        int centerX = position.x + radius;
        int centerY = position.y + radius;

        for (int i=0; i<positions.size(); i++)
        {
            Point otherPosition = positions.get(i);
            int otherDiameter = diameters.get(i);
            int otherRadius = otherDiameter/2;
            int otherCenterX = otherPosition.x + otherRadius;
            int otherCenterY = otherPosition.y + otherRadius;

            int dx = centerX - otherCenterX;
            int dy = centerY - otherCenterY;
            double distance = Math.hypot(dx, dy);
            if (distance < radius + otherRadius)
            {
                return true;
            }
        }
        return false;
    }

    @Override
    public void paint(Graphics g) 
    {
        super.paint(g);

        for(int i=0; i<positions.size(); i++)
        {
            int posX = positions.get(i).x;
            int posY = positions.get(i).y;
            int diameter = diameters.get(i);

            if (diameter < 25)
            {
                g.setColor(Color.YELLOW);
                g.fillOval(posX, posY,diameter,diameter);
            }

            else if(diameter > 50)
            {
                g.setColor(Color.GREEN);
                g.fillOval(posX, posY,diameter,diameter);
            }
            else
            {
                g.setColor(Color.RED);
                g.drawOval(posX, posY,diameter,diameter);
            }

            System.out.println(diameter);
        }
    }
}

To draw non-overlapping circles, you need to check if a new one would intersect one of the already defined circles. 要绘制不重叠的圆,您需要检查一个新的圆是否与已经定义的圆之一相交。

  1. Initialize an empty stack 初始化一个空栈
  2. Pick a random x,y, and radius (1/2 of your diameter). 随机选择x,y和半径(直径的1/2)。
  3. Loop over the stack and use (simple!) maths to calculate the distance to the center of each of the other circles. 在堆栈上循环并使用(简单!)数学计算到每个其他圆心的距离。 If this distance is less than its radius + the new radius, it does not touch. 如果此距离小于其半径+新半径,则不会接触。
  4. If it does, go to 2. 如果是,请执行2。
  5. If it touches none of the other circles, add it to the stack. 如果没有其他圆圈,请将其添加到堆栈中。
  6. If the length of the stack is less than 5, go to 2. 如果堆叠长度小于5,请执行2。
  7. Loop over the stack and draw each circle. 在堆栈上循环并绘制每个圆圈。

You might want to add a #Tries counter to #2/3 -- eg, if it fails to find a good place for the fourth or fifth circle, it may loop forever. 您可能想在#2/3上添加一个#Tries计数器-例如,如果它找不到第四或第五个圆的合适位置,则它可能永远循环。 If the #Tries counter exceeds some limit, go to #1; 如果#Tries计数器超过某个限制,则转到#1; essentially, start from scratch. 本质上是从头开始。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM