Генерация случайных точек в шестиугольнике для процедурного игрового контента - PullRequest
9 голосов
/ 13 июля 2010

Я использую процедурные методы для создания графики для игры, которую я пишу.

Чтобы создать несколько деревьев, я хотел бы разбросать деревья случайным образом в пределах правильной гексагональной области с центром в <0,0>.

Каков наилучший способ получить эти очки единообразным способом?

Ответы [ 8 ]

13 голосов
/ 13 июля 2010

Если вы можете найти хорошую прямоугольную ограничивающую рамку для своего шестиугольника, самый простой способ создать равномерно случайные точки - это выборка отклонения (http://en.wikipedia.org/wiki/Rejection_sampling)

То есть найдите прямоугольник, который полностью содержит ваш шестиугольник,и затем генерировать равномерно случайные точки внутри прямоугольника (это легко, просто независимо генерируйте случайные значения для каждой координаты в нужном диапазоне). Проверьте, попадает ли случайная точка в шестиугольник. Если да, оставьте его. Если нет, нарисуйте другую точку.

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

8 голосов
/ 13 июля 2010

Возможно, простой способ заключается в следующем:

    F ____ B
     /\  /\
  A /__\/__\ E
    \  /\  /
     \/__\/
     D     C

Рассмотрим параллелограммы ADCO (центр - O) и AOBF.

Любая точка в этом может быть записана как линейная комбинациядва вектора AO и AF.

Точка P в этих двух параллелограммах удовлетворяет

P = x * AO + y * AF или x AO + y AD.

где 0 <= x <1 и 0 <= y <= 1 (мы отбрасываем ребра, совместно используемые с BECO). </p>

Аналогичным образом любую точку Q в параллелограмме BECO можно записать как линейную комбинациювекторов BO и BE такие, что

Q = x BO + y BE, где 0 <= x <= 1 и 0 <= y <= 1. </p>

Таким образомчтобы выбрать случайную точку

, мы выбираем

A с вероятностью 2/3 и B с вероятностью 1 / 3.

Если вы выбрали A, выберите x в [0,1) (обратите внимание, полуоткрытый интервал [0,1)) и y в [-1,1] и выберите точку P = x AO + y AF, если y> 0, в противном случае выберите P = x *AO + | y ​​| * AD.

Если вы выбрали B, выберите x в [0,1] и y в [0,1] и cрабочая точка Q = x BO + y BE.

Таким образом, для выбора одной точки потребуется три случайных вызова, что может быть достаточно в зависимости от вашей ситуации.

6 голосов
/ 14 июля 2010

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

from math import sqrt
from random import randrange, random
from matplotlib import pyplot

vectors = [(-1.,0),(.5,sqrt(3.)/2.),(.5,-sqrt(3.)/2.)]

def randinunithex():
    x = randrange(3);
    (v1,v2) = (vectors[x], vectors[(x+1)%3])
    (x,y) = (random(),random())
    return (x*v1[0]+y*v2[0],x*v1[1]+y*v2[1])

for n in xrange(500):
    v = randinunithex()
    pyplot.plot([v[0]],[v[1]],'ro')

pyplot.show()

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

from math import sqrt
from random import randrange, random
from matplotlib import pyplot

size = 10

vectors = [(-1.,0),(.5,sqrt(3.)/2.),(.5,-sqrt(3.)/2.)]

def randinunithex():
    if not randrange(3*size*size+1): return (0,0)
    t = randrange(3);
    (v1,v2) = (vectors[t], vectors[(t+1)%3])
    (x,y) = (randrange(0,size),randrange(1,size))
    return (x*v1[0]+y*v2[0],x*v1[1]+y*v2[1])

# Plot 500 random points in the hexagon
for n in xrange(500):
    v = randinunithex()
    pyplot.plot([v[0]],[v[1]],'ro')

# Show the trimmed rhombuses
for t in xrange(3):
    (v1,v2) = (vectors[t], vectors[(t+1)%3])
    corners = [(0,1),(0,size-1),(size-1,size-1),(size-1,1),(0,1)]
    corners = [(x*v1[0]+y*v2[0],x*v1[1]+y*v2[1]) for (x,y) in corners]
    pyplot.plot([x for (x,y) in corners],[y for (x,y) in corners],'b')

pyplot.show()

А вот и картинка.

альтернативный текст http://www.freeimagehosting.net/uploads/0f80ad5d9a.png

2 голосов
/ 13 июля 2010

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

1) Выберите случайную трапецию из разложения. Каждая трапеция выбирается с вероятностью, пропорциональной ее площади.

2) Выберите произвольно выбранную точку на трапеции, выбранной на шаге 1.

Вы можете использовать триангуляцию вместо трапецеидальной декомпозиции, если вы предпочитаете это делать.

1 голос
/ 03 октября 2013

Вы можете проверить мою статью 2009 года, где я получил «точный» подход для генерации «случайных точек» внутри различных форм решетки: «шестиугольный», «ромбический» и «треугольный». Насколько я знаю, это «наиболее оптимизированный подход», потому что для каждой 2D-позиции вам нужны только две случайные выборки. Другие работы, полученные ранее, требуют 3 образца для каждой 2D-позиции!

Надеюсь, что это отвечает на вопрос!

http://arxiv.org/abs/1306.0162

1 голос
/ 13 июля 2010

Разделите его на шесть треугольников (следовательно, это относится к любому правильному многоугольнику), случайным образом выберите один треугольник и случайным образом выберите точку в выбранном треугольнике .

Выбор случайных точек в треугольнике является хорошо документированной проблемой .

И, конечно, это довольно быстро, и вам нужно будет сгенерировать только 3 случайных числа в каждой точке - без отклонений и т. Д.

Обновление:

Поскольку вам нужно сгенерировать два случайных числа, это то, как вы это делаете :

R = random(); //Generate a random number called R between 0-1

S = random(); //Generate a random number called S between 0-1

if(R + S >=1)
{
R = 1 – R;
S = 1 – S;
}
0 голосов
/ 04 марта 2013

Приведенное выше решение для отбора проб является интуитивно понятным и простым, но в нем используются прямоугольник и (предположительно) евклидовы координаты X / Y. Вы можете сделать это немного более эффективным (хотя все еще неоптимальным), используя окружность с радиусом r, и вместо этого генерировать случайные точки, используя полярные координаты из центра, где расстояние будет rand () * r, а theta (в радианах) будет рандов () * 2 * PI.

0 голосов
/ 13 июля 2010

1) делить пополам от точек к числам (просто перечислить их), получить случайное число -> получить точку.

Другое решение.

2) если N - длина стороны шестиугольника,получите 3 случайных числа из [1..N], начните с какого-то угла и двигайтесь 3 раза с этими числами в 3 направлениях.

...