平行四边形内的随机点

Posted

技术标签:

【中文标题】平行四边形内的随机点【英文标题】:Random points inside a parallelogram 【发布时间】:2010-09-19 10:32:56 【问题描述】:

我有一个由 2D 中的 4 个点定义的 4 边凸多边形,我希望能够在其中生成随机点。

如果它真的简化了问题,我可以将多边形限制为平行四边形,但更一般的答案是首选。

生成随机点直到一个点位于多边形内是行不通的,因为它所花费的时间真的无法预测。

【问题讨论】:

随机是什么意思?您可以选择位于对角线上的随机点。或者如果你产生足够多的随机点,你想完成整个多边形的填充吗? 如果我生产的足够多,我想填满整个多边形 这再简单不过了:画一个简单的矩形,它的大小刚好可以包围你的多边形。 (或者实际上,任何“形状或事物”。)现在创建随机分布在这个封闭的普通正方形中的点。对于每一个,测试它是否在你的形状内。丢弃那些在形状之外的。就是这么简单。希望对您有所帮助! 【参考方案1】:

OP的问题有点模棱两可,所以我要回答的问题是:如何从任意四边形内的均匀分布生成一个点,这实际上是的概括如何从任意(凸)多边形内的均匀分布生成一个点。答案基于从三角形中的均匀分布生成样本的情况(参见http://mathworld.wolfram.com/TrianglePointPicking.html,其中有一个非常好的解释)。

为了做到这一点,我们:

    对多边形进行三角剖分(即生成一组覆盖多边形的非重叠三角形区域)。对于四边形,创建一条边 任意两个不相邻的顶点。对于其他多边形,请参阅http://en.wikipedia.org/wiki/Polygon_triangulation 作为起点,如果您只需要一个库,请参阅 http://www.cgal.org/。

    要随机选择其中一个三角形,让我们为每个三角形分配一个索引(即 0、1、2、...)。对于四边形,它们将是 0,1。对于每个三角形,我们分配的权重如下:

    然后根据给定权重的索引的有限分布生成随机索引 i。对于四边形,这是一个伯努利分布:

    设 v0,v1,v2 为三角形的顶点(用它们的点位置表示,使得 v0 = (x0,y0) 等。然后我们生成两个随机数 a0 和 a1,均从区间 [ 0,1]。然后我们通过 x = a0 (v1-v0) + a1 (v2-v0) 来计算随机点 x。

    1234563 ,v2) (图像中的虚线)。在这种情况下,我们可以生成一个新点 x' = v0 + R(pi)(x - v3),其中 R(pi) 是 pi (180 度) 的旋转。点 x' 将在三角形内。

    进一步注意,如果四边形已经是平行四边形,那么我们不必随机选择一个三角形,我们可以确定性地选择任何一个,然后选择点 x 而无需测试它是否在它的内部源三角形。

【讨论】:

很好的答案。可爱的图片。 我正在尝试实现这一点,我认为它应该是x' = v0 + (v3 - x) 我完全不在基地吗?再看一遍我不确定我是对的,但我的 v0 = [0,0] 测试用例将 x' 放在三角形之外。 @gabriel_littman。我相信你是对的。在等式的图形中,缺少 R(pi),它出现在文本中……即旋转 180 度。我认为旋转矩阵是 [-1, 0; 0, -1] 也就是说我们取其操作数的负数。 这是问题的实际答案! 我尝试在 python 中实现它,但我认为有些东西坏了。见gist.github.com/astromme/599de466236adc534bc6e33cf2af8e7b。对于具有点 [0, 1]、[1, 0]、[1,0] 的三角形,v3 是 [2, -1],这似乎没有意义。此外,我得到了四边形之外的点。有什么想法吗?【参考方案2】:

A.如果您可以将输入限制为平行四边形,这真的很简单:

    取两个介于 0 和 1 之间的随机数。然后我们将调用 uv

    如果你的平行四边形由 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-uple 来描述,这样:

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

在您的情况下,您可以绘制 4 个随机数并将它们归一化,以便它们加起来为 1。这会给您一分。请注意,在这种情况下,点的分布不会是均匀的。

C.您还可以按照其他地方的建议将四边形分解为两个三角形并使用半平行四边形方法(即,作为平行四边形,但添加条件u+v=1)或三角形的重心坐标。但是,如果您想要均匀分布,则在其中一个三角形中有一个点的概率必须等于三角形的面积除以四边形的面积。

【讨论】:

重心方法是否适用于带孔的多边形? @Pranav 不,它不会...重心坐标需要连续域,我猜可能是凸的(待检查)。【参考方案3】:

假设您想要一个均匀分布:从您的多边形中形成两个三角形。根据面积比选择生成点的三角形。

调用三角形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 的公式,除了预处理和将点折回三角形的最后一步,因此它可以处理平行四边形以外的其他形状)。

【讨论】:

对于其他人来说,这里是如何找到一个点是否在三角形内:***.com/questions/2049582/…【参考方案4】:

你的多边形是两个三角形,所以为什么不随机选择其中一个,然后在三角形中找到一个随机点。

可能不是最好的解决方案,但它会起作用。

【讨论】:

如果您需要随机点的均匀分布,请确保考虑到两个三角形的面积和适当的权重。【参考方案5】:

稍微少一点“naïve”的方法是使用polygon fill algorithm,然后从填充线中随机选择点。

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.

【讨论】:

我怀疑这不是 Turambar 需要的,但它会起作用。有些线条比其他线条长,因此要获得均匀分布,不要选择线条,然后选择像素。计算像素,然后随机选择一个,然后从列表中找到它的位置...【参考方案6】:

“一般”是指一般的所有非平行四边形 4 边多边形还是所有可能的多边形?

如何绘制一条连接 4 条边的随机线,例如如果你有这个:

.BBBB.
A    C
A    C
.DDDD.

然后在一个单位正方形上生成一个随机点,然后在B和D线上以X轴上距离的百分比标记该点。使用 Y 轴的值在 A 线和 C 线上执行相同操作。

然后将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);


【讨论】:

【参考方案7】:

这适用于一般的凸四边形:

您可以从有限元法中借用一些概念,特别是对于四边形(4 边)元素 (refer to section 16.5 here)。基本上,有一个双线性参数化,将 uv 空间中的一个正方形(对于 u, v \in [-1, 1] 在这种情况下)映射到由点 p_i 组成的四边形(对于 i = 1,2,3,4 )。请注意,在提供的参考中,参数称为 \eta 和 \xi。

基本配方:

    选择合适的随机数生成器在二维正方形域中生成分布良好的点 在 [-1, 1] 范围内生成随机 u-v 对 对于每个 uv 对,quad 中对应的随机点 = 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 中工作,并编写一个四边形中的点(可能通过将问题分成三中的两个点)测试来剔除外部的随机点。

【讨论】:

【参考方案8】:

点数是否需要均匀分布,或者任何分布都可以?

多边形可以是凹的,还是保证是凸的?

如果以上两个问题的答案都是否定的,那么选择任意两个顶点并在它们之间的线段上选择一个随机点。这仅限于连接顶点的线段(即非常不均匀);您可以通过选择第三个顶点然后在该点和第一个点之间选择一个点来做得更好——仍然不均匀,但至少多边形中的任何点都是可能的

在两点之间的直线上选择一个随机点很容易,只需 A + p(B-A),其中 A 和 B 是点,p 是 0.0 到 1.0 之间的随机数

【讨论】:

【参考方案9】:

您希望积分具有什么样的分布?如果您不在乎,上述方法将正常工作。如果你想要一个均匀分布,下面的过程将起作用:将多边形分成两个三角形,a 和 b。设 A(a) 和 A(b) 为它们的面积。从 0 和 A(a)+A(b) 之间的区间上的均匀分布中采样一个点 p。如果 p

【讨论】:

【参考方案10】:

MATLAB 函数cprnd 根据一般凸多面体上的均匀分布生成点。对于您的问题,基于将四边形分解为三角形的更专业的算法更有效。

【讨论】:

【参考方案11】:

对于 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($1) 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($1)) into ret;
    if ST_Contains($1,ret) then
        return ret ;
    end if;
end loop;
end;
$$
LANGUAGE plpgsql
volatile
RETURNS NULL ON NULL INPUT;

【讨论】:

以上是关于平行四边形内的随机点的主要内容,如果未能解决你的问题,请参考以下文章

RSSI 平面 三点定位算法(C语言JS源码例程)

RSSI 平面 三点定位算法(C语言JS源码例程)

算法均匀的生成圆内的随机点

在python中生成多边形范围内的随机点数

在 3D 空间中沿 Y 轴的一个角度在随机点处旋转单位向量

给定时间内的 Cron 作业和随机时间