Случайные точки внутри параллелограмма - PullRequest
48 голосов
/ 27 октября 2008

У меня есть 4-сторонний выпуклый многоугольник, определяемый 4 точками в 2D, и я хочу иметь возможность генерировать случайные точки внутри него.

Если это действительно упрощает проблему, я могу ограничить полигон параллелограммом, но предпочтительнее более общий ответ.

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

Ответы [ 11 ]

43 голосов
/ 07 декабря 2011

Вопрос ОП несколько двусмыслен, поэтому я отвечу на вопрос: Как создать точку из равномерного распределения в произвольном четырехугольнике , что на самом деле является обобщением Как создать точку из равномерного распределения в произвольном (выпуклом) многоугольнике . Ответ основан на случае генерации выборки из равномерного распределения в треугольнике (см. http://mathworld.wolfram.com/TrianglePointPicking.html,, который имеет очень хорошее объяснение).

Для этого мы:

  1. Триангулируйте многоугольник (то есть создайте набор неперекрывающихся треугольных областей, которые покрывают многоугольник). В случае четырехугольника создайте ребро поперек любые две несмежные вершины. Для других полигонов см. http://en.wikipedia.org/wiki/Polygon_triangulation для начальной точки или http://www.cgal.org/, если вам просто нужна библиотека.

    enter image description here

  2. Чтобы выбрать один из треугольников случайным образом, давайте назначим индекс каждому треугольнику (то есть 0,1,2, ...). Для четырехугольника они будут 0,1. Для каждого треугольника присваиваем вес, равный следующим образом:

    weight calculation

  3. Затем сгенерируйте случайный индекс i из конечного распределения по индексам с учетом их весов. Для четырехугольника это распределение Бернулли:

    enter image description here

  4. Пусть v0, v1, v2 - вершины треугольника (представленные их точечными местоположениями, так что v0 = (x0, y0) и т. Д. Затем мы генерируем два случайных числа a0 и a1, оба одинаково взятые из интервал [0,1]. Затем мы вычисляем случайную точку x по x = a0 (v1-v0) + a1 (v2-v0).

    enter image description here

  5. Обратите внимание, что с вероятностью 0,5 х лежит снаружи вне треугольника, однако, если это так, он лежит внутри параллелограмма, состоящего из объединения треугольника с его изображением после поворота пи вокруг средней точки (v1 , v2) (пунктирные линии на изображении). В этом случае мы можем сгенерировать новую точку x '= v0 + R (pi) (x - v3), где R (pi) - поворот на pi (180 градусов). Точка х 'будет внутри треугольника.

  6. Далее отметим, что если четырехугольник уже был параллелограммом, то нам не нужно выбирать треугольник случайным образом, мы можем выбрать один из них детерминистически, а затем выбрать точку x, не проверяя, что она находится внутри него. исходный треугольник.

30 голосов
/ 27 октября 2008

A. Если вы можете ограничить свой ввод параллелограммом, это действительно просто:

  1. Возьмите два случайных числа от 0 до 1. Затем мы позвоним u и v.
  2. Если ваш параллелограмм определен точками ABCD, так что AB, BC, CD и DA являются сторонами, то примите вашу точку зрения:

     p = A + (u * AB) + (v * AD)
    

, где AB - вектор от A до B и AD - вектор от A до D.

B. Теперь, если вы не можете, вы все равно можете использовать барицентрические координаты. Барицентрические координаты для четырех квадратов соответствуют 4 координатам (a,b,c,d), таким что a+b+c+d=1. Тогда любая точка P в квадре может быть описана 4-мя степенями, что:

P = a A + b B + c C + d D

В вашем случае вы можете нарисовать 4 случайных числа и нормализовать их так, чтобы они складывались в 1. Это даст вам очко. Обратите внимание, что в этом случае распределение точек НЕ будет равномерным.

C. Вы также можете, как предложено в другом месте, разложить квад на два треугольника и использовать метод полупараллелограмма (то есть в качестве параллелограмма, но вы добавляете условие u+v=1) или барицентрические координаты для треугольников. Однако, если вы хотите равномерное распределение, вероятность наличия точки в одном из треугольников должна быть равна площади треугольника, деленной на площадь четырехугольника.

19 голосов
/ 27 октября 2008

Предполагая, что вы хотите равномерное распределение: сформируйте два треугольника из вашего многоугольника. Укажите, в каком треугольнике генерировать точку в соответствии с их отношением площади.

Назовите углы треугольника A, B, C, боковые векторы AB, BC, AC и сгенерируйте два случайных числа в [0,1], называемые u и v. Пусть p = u * AB + v * AC.

Если A + p находится внутри треугольника, вернуть A + p

Если A + p находится за пределами треугольника, вернуть A + AB + AC - p

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

4 голосов
/ 27 октября 2008

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

Возможно, не лучшее решение, но оно бы сработало.

2 голосов
/ 14 января 2011

Это работает для общих выпуклых четырехугольников:

Некоторые методы можно позаимствовать из метода конечных элементов, в частности, для четырехугольных (4-сторонних) элементов ( см. Раздел 16.5 здесь ). По существу, существует билинейная параметризация, которая отображает квадрат в ультрафиолетовом пространстве (для u, v \ in [-1, 1] в этом случае) в ваш четырехугольник, который состоит из точек p_i (для i = 1,2,3,4 ). Обратите внимание, что в предоставленной ссылке параметры называются \ eta и \ xi.

Основной рецепт:

  1. Выберите подходящий генератор случайных чисел для генерации хорошо распределенных точек в квадратной двумерной области
  2. Генерация случайных пар u-v в диапазоне [-1, 1]
  3. Для каждой ультрафиолетовой пары соответствующая случайная точка в вашем квадре = 1/4 * ((1-u) (1-v) * p_1 + (1 + u) (1-v) * p_2 + (1+) u) (1 + v) * p_3 + (1-u) (1 + v) * p_4)

Единственная проблема заключается в том, что равномерно распределенные точки в пространстве u-v не будут давать равномерно распределенные точки в вашем квадре (в евклидовом смысле). Если это важно, вы можете работать непосредственно в 2D в пределах ограничительной рамки квадратора и написать тест «точка в квадре» (возможно, разделив задачу на две точки в трисе), чтобы отбирать случайные точки, находящиеся снаружи.

2 голосов
/ 27 октября 2008

Под "общим" вы подразумеваете все непараллелограммные 4-сторонние многоугольники в целом или все возможные многоугольники?

Как насчет рисования случайной линии, соединяющей 4 стороны, например Если у вас есть это:

.BBBB.
A    C
A    C
.DDDD.

Затем создайте случайную точку на единичном квадрате, затем отметьте точку на линии B и D в процентах от расстояния по оси X. Сделайте то же самое на линиях A и C, используя значение от оси Y.

Затем соедините точку на линии A с линией C, а линию B с линией D, точка пересечения будет использоваться в качестве случайной точки.

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

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

Вот быстрый псевдокод:

void GetRandomPoint(Polygon p, ref float x, ref float y) {

    float xrand = random();
    float yrand = random();

    float h0 = p.Vertices[0] + xrand * p.Vertices[1];
    float h1 = p.Vertices[2] + yrand * p.Vertices[3];

    float v0 = p.Vertices[0] + xrand * p.Vertices[2];
    float v1 = p.Vertices[1] + yrand * p.Vertices[3];

    GetLineIntersection(h0, h1, v0, v1, x, y);

}
2 голосов
/ 27 октября 2008

Несколько менее " наивный " подход заключается в использовании алгоритма заполнения полигонов , а затем случайным образом выбирает точки из линий заполнения.

Пример кода C

//  public-domain code by Darel Rex Finley, 2007

int  nodes, nodeX[MAX_POLY_CORNERS], pixelX, pixelY, i, j, swap ;

//  Loop through the rows of the image.
for (pixelY=IMAGE_TOP; pixelY<IMAGE_BOT; pixelY++) {

  //  Build a list of nodes.
  nodes=0; j=polyCorners-1;
  for (i=0; i<polyCorners; i++) {
    if (polyY[i]<(double) pixelY && polyY[j]>=(double) pixelY
    ||  polyY[j]<(double) pixelY && polyY[i]>=(double) pixelY) {
      nodeX[nodes++]=(int) (polyX[i]+(pixelY-polyY[i])/(polyY[j]-polyY[i])
      *(polyX[j]-polyX[i])); }
    j=i; }

  //  Sort the nodes, via a simple “Bubble” sort.
  i=0;
  while (i<nodes-1) {
    if (nodeX[i]>nodeX[i+1]) {
      swap=nodeX[i]; nodeX[i]=nodeX[i+1]; nodeX[i+1]=swap; if (i) i--; }
    else {
      i++; }}

  //  Fill the pixels between node pairs.
  //  Code modified by SoloBold 27 Oct 2008
  //  The flagPixel method below will flag a pixel as a possible choice.
  for (i=0; i<nodes; i+=2) {
    if   (nodeX[i  ]>=IMAGE_RIGHT) break;
    if   (nodeX[i+1]> IMAGE_LEFT ) {
      if (nodeX[i  ]< IMAGE_LEFT ) nodeX[i  ]=IMAGE_LEFT ;
      if (nodeX[i+1]> IMAGE_RIGHT) nodeX[i+1]=IMAGE_RIGHT;
      for (j=nodeX[i]; j<nodeX[i+1]; j++) flagPixel(j,pixelY); }}}

   // TODO pick a flagged pixel randomly and fill it, then remove it from the list.
   // Repeat until no flagged pixels remain.
1 голос
/ 07 января 2012

Функция MATLAB cprnd генерирует точки из равномерного распределения на общем выпуклом многограннике. На ваш вопрос более специализированный алгоритм, основанный на разложении четырехугольника на треугольники, более эффективен.

1 голос
/ 27 октября 2008

Какой тип распределения вы хотите получить? Если вам все равно, вышеперечисленные методы будут работать нормально. Если вы хотите равномерное распределение, сработает следующая процедура: Разделите многоугольник на два треугольника, a и b. Пусть A (a) и A (b) - их площади. Выберите точку p из равномерного распределения на интервале между 0 и A (a) + A (b). Если p

1 голос
/ 27 октября 2008

Должны ли точки распределяться равномерно или все в порядке?

Может ли многоугольник быть вогнутым или гарантированно выпуклым?

Если ответ на оба вышеприведенных вопроса - нет, то выберите любые две вершины и выберите случайную точку на отрезке линии между ними. Это ограничено линейными сегментами, соединяющими вершины (т. Е. ОЧЕНЬ неоднородно); Вы можете сделать немного лучше, выбрав третью вершину, а затем выбрав точку между ней и первой точкой - все еще неравномерно, но по крайней мере любая точка в многоугольнике возможна

Легко выбрать случайную точку на линии между двумя точками, просто A + p (B-A), где A и B - точки, а p - случайное число от 0,0 до 1,0

.
...