Обобщение 3D выборки дисков

Я хочу создать выборку диска Пуассона для трехмерной сетки. Я использовал реализацию https://github.com/emulbreh/bridson, чтобы обобщить ее на 3D. Однако кажется, что я что-то упускаю, и я не могу найти проблему. Эта реализация следует алгоритму Роберта Бридсона (https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf)

Что я сделал, так это превратил каждый 2D-объект в 3D и создал новую точку со сферическими координатами.

from random import random
from math import cos, sin, floor, sqrt, pi, ceil


def euclidean_distance(a, b):
    dx = a[0] - b[0]
    dy = a[1] - b[1]
    dz = a[2] - b[2]

    return sqrt(dx * dx + dy * dy + dz * dz)


def poisson_disc_samples(width, height,thick, r, k=5, distance=euclidean_distance, random=random):
    tau = 2 * pi
    cellsize = r / sqrt(2)

    grid_width = int(ceil(width / cellsize))
    grid_height = int(ceil(height / cellsize))
    grid_thick = int(ceil(thick / cellsize))

    grid = [None] * (grid_width * grid_height * grid_thick)
    def grid_coords(p):
        return int(floor(p[0] / cellsize)), int(floor(p[1] / cellsize)), int(floor(p[2] / cellsize))

    def fits(p, gx, gy, gz):
        yrange = list(range(max(gy - 2, 0), min(gy + 3, grid_height)))
        zrange = list(range(max(gz - 2, 0), min(gz + 3, grid_thick)))
        for x in range(max(gx - 2, 0), min(gx + 3, grid_width)):
            for y in yrange:
                for z in zrange:
                    g = grid[x + y + z  * grid_width]
                    if g is None:
                        continue
                    if distance(p, g) <= r:
                        return False
        return True
    p = width * random(), height * random(), thick * random()
    queue = [p]
    grid_x, grid_y, grid_z = grid_coords(p)
    grid[grid_x + grid_y + grid_z * grid_width] = p

    while queue:
        qi = int(random() * len(queue))
        qx, qy, qz = queue[qi]
        queue[qi] = queue[-1]
        queue.pop()
        for _ in range(k):
            alpha = tau * random()
            theta = tau * random()
            d = r * sqrt(3 * random() + 1)
            px = qx + d * cos(alpha) * sin(theta)
            py = qy + d * sin(alpha) * sin(theta)
            pz = qz + d * cos(theta)
            if not (0 <= px < width and 0 <= py < height):
                continue
            p = (px, py, pz)
            grid_x, grid_y, grid_z = grid_coords(p)
            if not fits(p, grid_x, grid_y,grid_z):
                continue
            queue.append(p)
            grid[grid_x + grid_y + grid_z * grid_width] = p
    return [p for p in grid if p is not None]

r = 10
samples = poisson_disc_samples(100, 100, 100, r=10, random=random)

print(samples)

Результаты, которые я ищу, - это просто печать выбранных 3D-точек, однако программа не печатает никаких ошибок, и я чувствую это, потому что она не находит образцы, и после этого компьютер дает сбой.

Я думаю, что это может быть функция fits (она отвечает за проверку того, пусты ли сетки или нет), которая вызывает проблему.


person Adrián Briceño Aguilar    schedule 25.06.2019    source источник


Ответы (2)


Я думаю, что ваши изменения в коде в порядке ... моя единственная проблема с оригиналом - это вызов queue.pop(), а не выход из цикла break, когда точка была найдена, выглядит немного подозрительно. если вас волнует порядок, в котором выдаются сэмплы, то это будет отличаться от опубликованного алгоритма, но в остальном это, вероятно, не имеет значения.

ваша основная проблема заключается в том, что вы просто пытаетесь сгенерировать много точек, и метод должен выполнять довольно много работы для каждой точки, поэтому для завершения требуется время! если я изменю код на yield точки по мере их создания (т.е. добавлю yield p рядом с queue.append(p)) вместо того, чтобы просто возвращать их все в конце, я получу то, что выглядит разумным выводом

person Sam Mason    schedule 25.06.2019

Я наткнулся на этот код через поиск в Google 3D-реализации этого алгоритма на Python. Выложенный код действительно не работает! Есть как минимум две проблемы:

    grid[grid_x + grid_y + grid_z * grid_width] = p

В этой строке (и подобных строках) неправильно индексируется сетка, которая должна быть трехмерным массивом (или списком, заменяющим трехмерный массив). Самое простое решение - использовать фактический трехмерный массив numpy и хранить индексы в плоском списке точек в нем (это не то, что предлагается в статье)

d = r * sqrt(3 * random() + 1)
px = qx + d * cos(alpha) * sin(theta)
py = qy + d * sin(alpha) * sin(theta)
pz = qz + d * cos(theta)

это должно привести к однородной выборке между r и 2r в трех измерениях, но я считаю, что эти выборки будут смещены в сторону меньших расстояний.

person Felix Zimmermann    schedule 31.08.2020