我对Bridson算法的Poisson-Disk Sampling算法的实现似乎陷入了无限循环

时间:2019-10-04 16:33:27

标签: python pygame

A video by Sebastion Lague explained the Bridson's algorithm really well.

为简化起见,

  
      
  1. 创建边长为radius / sqrt(2)的单元格。

  2.   
  3. 将初始点和列表作为生成点。

  4.   
  5. 将点放置到网格中的单元格中。

  6.   
  7. 对于任何生成点,请生成一个介于半径和2 *半径之间的点。

  8.   
  9. 在距离新点像元2个单位的地方看像元。

  10.   
  11. 如果包含其他点,请比较距离。

  12.   
  13. 如果有任何点比半径更接近新点,则新点无效。

  14.   
  15. 如果新点有效,则新点将作为生成点列出并放置在网格中的单元格中。

  16.   
  17. 如果生成点生成了太多的无效点,则删除生成点并变成点。

  18.   
  19. 重复,直到没有更多的生成点为止。

  20.   
  21. 返回点。

  22.   

我基本上在Python 3.7.2和pygame 1.7〜中写下了相同的内容,但是正如标题中所述,我陷入了递归炼狱中。

我为这个算法使用了一个Point()类,考虑到存在pygame.Vector2(),这似乎是多余的,但是我需要一些元素来使用一个单独的算法(具有无限顶点的德劳内),需要该类起作用。 / p>

为简单起见,我将删除所有Delaunay特定的元素,并显示该算法所需的此类的基本知识:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def DistanceToSquared(self,other):
        return (self.x-other.x)**2 + (self.y-other.y)**2

与Bridson算法相关的代码是:

def PoissonDiskSampling(width, height, radius, startPos = None, spawnAttempts = 10):

    if startPos == None:
        startPos = [width//2,height//2]

    cellSize = radius / math.sqrt(2)
    cellNumberX = int(width // cellSize + 1)  # Initialise a cells grid for optimisation
    cellNumberY = int(height // cellSize + 1)
    cellGrid = [[None for x in range(cellNumberX)] for y in range(cellNumberY)]

    startingPoint = Point(startPos[0],startPos[1]) # Add an iniial point for spawning purposes
    cellGrid[startingPoint.x//radius][startingPoint.y//radius] = startingPoint

    points = [startingPoint] # Initialise 2 lists tracking all points and active points
    spawnpoints = [startingPoint]

    while len(spawnpoints) > 0:

        spawnIndex = random.randint(0,len(spawnpoints)-1)
        spawnpoint = spawnpoints[spawnIndex]

        spawned = False
        for i in range(spawnAttempts):

            r = random.uniform(radius,2*radius)
            radian = random.uniform(0,2*math.pi)
            newPoint = Point(spawnpoint.x + r*math.cos(radian),
                            spawnpoint.y + r*math.sin(radian))
            if 0 <= newPoint.x <= width and 0 <= newPoint.y <= height:
                isValid = True
            else:
                continue

            newPointIndex = [int(newPoint.x//cellSize), int(newPoint.y//cellSize)]
            neighbours = FindNeighbours(cellNumberX,cellNumberY,newPointIndex,cellGrid)

            for neighbour in neighbours:
                if newPoint.DistanceToSquared(neighbour) < radius**2:
                    isValid = False
                    break

            if isValid:
                points.append(newPoint)
                spawnpoints.append(newPoint)
                spawned = True
                break
            else:
                continue

        if spawned == False:
            spawnpoints.remove(spawnpoint)

    return points

def FindNeighbours(cellNumberX, cellNumberY, index, cellGrid):
    neighbours = []
    for cellX in range(max(0,(index[0]-2)), min(cellNumberX,(index[1]+2))):
        for cellY in range(max(0,(index[0]-2)), min(cellNumberY,(index[1]+2))):
            if cellGrid[cellX][cellY] != None:
                neighbours.append(cellGrid[cellX][cellY])
    return neighbours

请帮助。

1 个答案:

答案 0 :(得分:1)

您的代码中可能缺少了最重要的步骤:

  
      
  1. 如果新点有效,则将新点列为生成点,并放置到网格中的单元格中
  2.   

我建议将点添加到longitude BETWEEN 30 AND 40(如果有效):

cellGrid

此外,您必须在添加点之前验证索引为if isValid: cellGrid[newPointIndex[0]][newPointIndex[1]] = newPoint points.append(newPoint) spawnpoints.append(newPoint) spawned = True break 的单元格是否尚未被占用:

newPointIndex

最后,函数newPointIndex = [int(newPoint.x/cellSize), int(newPoint.y/cellSize)] if cellGrid[newPointIndex[0]][newPointIndex[1]] != None: continue neighbours = FindNeighbours(cellNumberX,cellNumberY,newPointIndex,cellGrid) 中存在问题。 FindNeighboursrange(start, stop)中的x创建一个范围。
因此止损点必须为start <= x < stop,而不是index[0]+3

进一步控制2个嵌套index[0]+2循环的范围分别从forx-2,而不是{{1}到y+2 1}}至x-2

  
x+2

固定功能必须为:

y-2

查看结果,尺寸为300 x 300,半径为15:


如果始终使用y+2的第一个点而不是随机点,则可以获得更好的结果:

for cellX in range(max(0,(index[0]-2)), min(cellNumberX,(index[1]+2))):
   for cellY in range(max(0,(index[0]-2)), min(cellNumberY,(index[1]+2)))