Случайные точки внутри параллелограмма

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

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

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

11 ответов


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

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

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

здесь AB является вектором от A до B и AD вектор от A до D.

Б. Теперь, если вы не можете, вы все еще можете использовать барицентрические координаты. Барицентрические координаты соответствуют для квадрата 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) или барицентрические координаты для треугольников. Однако, если вы хотите равномерного распределения, вероятность наличия точки в одном из треугольников должна быть равна площади треугольника, деленной на площадь квадрата.


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

для этого мы:

  1. триангулировать многоугольник (т. е. генерировать коллекцию неперекрывающихся треугольных областей, которые охватывают многоугольник). Для случая четырехугольника создайте ребро поперек любые две несмежные вершины. Для других полигонов см. http://en.wikipedia.org/wiki/Polygon_triangulation для a отправная точка, или 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, x лежит снаружи вне треугольника, однако, если это так,он лежит внутри параллелограмма, состоящего из объединения треугольника с его изображением после вращения pi вокруг средней точки (v1, v2) (пунктирные линии на изображении). В этом случае мы можем создать новую точку x ' = v0 + R(pi) (x - v3), где R (pi) - вращение на pi (180°). Точка х будет находиться внутри треугольника.

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


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

назовите углы треугольника 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 за исключением предварительной обработки и последнего шага, который складывает точку обратно в треугольник, поэтому она может обрабатывать другие формы, чем параллелограммы).


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

вероятно, не лучшее решение, но это сработает.


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

Образец Кода 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.

под "общим" вы подразумеваете все непараллелограммовые 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);

}

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

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

базовый рецепт:

  1. выберите подходящий генератор случайных чисел для генерации хорошо распределенных точек в квадратной 2D-области
  2. генерировать случайные пары u-v в диапазоне [-1, 1]
  3. для каждой пары u-v соответствующая случайная точка в вашем квадроцикле = 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 в ограничивающем поле квадроцикла и написать тест "точка в квадрате" (возможно, разделив проблему на две точки в Трис), чтобы отбирать случайные точки, которые находятся снаружи.


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

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

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

выбор случайной точки На линии между двумя точками легко, просто A + p (B-A), где A и B-точки, А p-случайное число между 0.0 и 1.0


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


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


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

CREATE or replace FUNCTION random_point(geometry)
RETURNS geometry
AS $$
DECLARE 
    env geometry;
    corner1 geometry;
    corner2 geometry;
    minx real;
    miny real;
    maxx real;
    maxy real;
    x real;
    y real;
    ret geometry;
begin

select ST_Envelope() into env;
select ST_PointN(ST_ExteriorRing(env),1) into corner1;
select ST_PointN(ST_ExteriorRing(env),3) into corner2;
select st_x(corner1) into minx;
select st_x(corner2) into maxx;
select st_y(corner1) into miny;
select st_y(corner2) into maxy;
loop
    select minx+random()*(maxx-minx) into x;
    select miny+random()*(maxy-miny) into y;
    select ST_SetSRID(st_point(x,y), st_srid()) into ret;
    if ST_Contains(,ret) then
        return ret ;
    end if;
end loop;
end;
$$
LANGUAGE plpgsql
volatile
RETURNS NULL ON NULL INPUT;