Python Pygame randomly draw non overlapping circle

2019-07-09 09:51发布

问题:

Im very new to python and seem to be missing something.
I want to randomly draw circles on a pygame display but only if the circles don't overlap each other.
I believe I must find the distance between all circle centers and only draw it if the distance is bigger than circle radius * 2.

I've tried many different things but all without success, I always get the same result - circles drawn overlapping.

#!/usr/bin/env python

import pygame, random, math

red = (255, 0, 0)
width = 800
height = 600
circle_num = 10
tick = 2
speed = 5

pygame.init()
screen = pygame.display.set_mode((width, height))

class circle():
    def __init__(self):
        self.x = random.randint(0,width)
        self.y = random.randint(0,height)
        self.r = 100

    def new(self):
        pygame.draw.circle(screen, red, (self.x,self.y), self.r, tick)

c = []
for i in range(circle_num):
    c.append('c'+str(i))
    c[i] = circle()
    for j in range(len(c)):
        dist = int(math.hypot(c[i].x - c[j].x, c[i].y - c[j].y))
        if dist > int(c[i].r*2 + c[j].r*2):
            c[j].new()
            pygame.display.update()

        else:
            continue

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            quit()

回答1:

You did not check against all other circles. I added a variable shouldprint which gets set to false if any other circle is too close.

import pygame, random, math

red = (255, 0, 0)
width = 800
height = 600
circle_num = 20
tick = 2
speed = 5

pygame.init()
screen = pygame.display.set_mode((width, height))

class circle():
    def __init__(self):
        self.x = random.randint(0,width)
        self.y = random.randint(0,height)
        self.r = 100

    def new(self):
        pygame.draw.circle(screen, red, (self.x,self.y), self.r, tick)

c = []
for i in range(circle_num):
    c.append('c'+str(i))
    c[i] = circle()
    shouldprint = True
    for j in range(len(c)):
        if i != j:
            dist = int(math.hypot(c[i].x - c[j].x, c[i].y - c[j].y))
            if dist < int(c[i].r*2):
                shouldprint = False
    if shouldprint:
        c[i].new()
        pygame.display.update()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            quit()


回答2:

The for loop has been changed to a while loop. It will keep trying to generate circles until the target number is reached. A circle is first generated. Then, it checks if it intersects with any existing circle using the formula from this answer.

It iterates through every existing circle (store in the list circles) and performs the check using the formula. any() returns True if the formula evaluates to True for any iteration. If it's True, it means it found an intersection. Thus, it continues to the next iteration to try again with a new circle.

circles = []
while len(circles) < circle_num:
    new = circle()

    if any(pow(c.r - new.r, 2) <=
           pow(c.x - new.x, 2) + pow(c.y - new.y, 2) <=
           pow(c.r + new.r, 2)
       for c in circles):
        continue

    circles.append(new)
    new.new()
    pygame.display.update()