C++ 创建随机形状的“blob”对象

8

我需要在离散的网格地图上定义一个“blob”形状的对象(或区域)。它应该看起来像这样:enter image description here

其中红色区域表示中心点(这只是一些想法,任何blob形状都可以,只要可以随机变化即可)。到目前为止,我的想法是从起始点(= 0度)开始迭代递增角度到360度,并使用三角函数计算圆的外部点(如果半径= 1 = const,则将得到单位圆)。然后使用Bresenham线算法(记住:我们正在移动离散网格上)来计算连接圆心和我刚刚想出的外部点的线。我的想法是,如果我能稍微改变半径,我就可以创建这些blob形状。到目前为止,我想出来的东西给了我很好的形状,但它们实际上并不是“blobby”。这是我的代码(请注意,x0y0标记了我的网格地图的中心点,plotBresenham只是将所有1s放在区域中,以便可以可视化网格地图):

double radius = 10; 
for(int alpha=0; alpha<360; alpha++) {
   double x = cos(alpha*M_PI/180.0)*radius;
   double y = sin(alpha*M_PI/180.0)*radius;

   if(alpha<45) radius+=0.5;
   else if(alpha<90) radius-=0.5; 
   else if(alpha<135) radius+=0.5; 
   else if(alpha<180) radius-=0.5; 
   else if(alpha<225) radius+=0.5; 
   else if(alpha<270) radius-=0.5; 
   else if(alpha<315) radius+=0.5; 
   else radius-=0.5; 

   plotBresenhamLine(x0,y0,x,y)

}

结果如下所示:

在此输入图片描述

抱歉画得有些粗糙。编程语言是C++,但我认为这种方法并不依赖于使用的语言。您可以给我一些关于如何创建更像我需要的形状的提示/帮助/指导吗?或者有没有能够自动完成这样工作的框架?对我来说,重要的是获得图形中各点的坐标,以将它们放入我的网格地图中。

1
为什么不在其中添加一些rand()呢? - Layne Bernardo
我确实这样做了 - 但是rand()的值变化太大了,结果的形状看起来有点像海胆!也许一种随机抽取分布样本的方法,同时确保它们之间不会相距太远,可能是正确的方法! - masterBroesel
4个回答

13

使用角度来改变半径是可行的方法。但是,您可以使用预定振幅和相位的多个周期函数的总和,而不是随机游走。这保证:

  1. 在旋转360°后,半径将返回其原始值,
  2. 您可以轻松控制遇到的半径范围。(您需要避免小于零的情况)。

选择一个正弦或余弦函数,其中将角度乘以整数并加上随机相位。通过一个随机(预定)振幅来缩放每一个函数。将它们相加再加上比所有振幅之和都大的常数。赚取利润。

我不打算用C++写这个,因为正如您所说,它对算法不会有任何重要性。可能像这样:

  1. 取N,您想要的波的数量。
  2. 定义float数组amps[N]phases[N]
  3. 为每个amps[i]选择介于0和1/(2N)之间的随机数字,并为每个phases[i]选择介于0和2π之间的随机数字。
  4. 对于每个角度alpha(以弧度表示),计算
radius = 1 + sum[i=0 to N-1] amps[i] * cos((i+1)*alpha + phases[i])
x = cos(alpha)*radius;
y = sin(alpha)*radius;
  1. 按照以前的方式进行。

结果(来自 Wolfram Mathematica):

为了使其更有趣,通过一些负幂次方限制第k个振幅(或k+1,因为我们从零开始索引)。 当步骤3中的随机数除以pow(i+1,1.5)时,N = 30,如下所示:


太好了,这正是我在寻找的!不过,我并不真正理解这些图表背后的理论 - 你能给我指点一些相关的文献或关键词吗?谢谢! - masterBroesel
1
@masterBroesel 起点是傅里叶级数 。它们可以表示任何周期函数(而周期性是你想要的,见第一段),当然随机傅里叶 = 随机结果。功率定律部分有一些根源于理论的深层属性,但你可以简单地说它限制了快速振荡的影响(尝试不使用功率定律的 N=30 ,就会发现这是一个问题)。 - The Vee
顺便在第四步进行小修正,y = sin(alpha)*radius; - pyrometer
这看起来真的很有用。我会试着玩一下,看看它能否产生更多像溅墨画那样的效果。就像是几乎分离但又没有完全分开的斑点。也许这只是强制某些参数[]变高?但我觉得返回的曲线不会变细。我所描述的更像是由贝塞尔曲线连接的两个或更多这样的斑点。 - Eradicatore

1
对于一个blobby形状,一组(相当密集的)metaballs可能会产生漂亮的形状。

1
这是对The Vee's great answer的后续。
对于任何偶然发现这个问题并正在寻找实现方法的人来说,可以用Python代码编写如下。
from random import random
from math import cos, sin, pi, radians

N = 10
amps = [random() * (1 / (2*N)) for _ in range(N)]
phases = [random() * 2 * pi for _ in range(N)]

x = []
y = []
for deg in range(360):
    alpha = radians(deg)
    radius = 1 + sum([amps[i] * cos((i+1)*alpha + phases[i]) for i in range(N)])
    x.append(cos(alpha) * radius)
    y.append(sin(alpha) * radius)


这并没有回答问题。一旦你拥有足够的声望,你就可以评论任何帖子;相反,提供不需要提问者澄清的答案。- 来自审核 - Nol4635

1
这是一个 C 语言版本的解决方案,接近我想要的效果,但我正在试图找到一种方法来反转某些部分的曲线,这将使得这些拉伸出去的“节点”看起来像是开始收紧。
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main() {
   time_t t;
   uint16_t alpha, i;
   uint16_t N = 12;
   float amps[N];
   float phases[N];
   float radius, x, y;

   srand((unsigned) time(&t));
   srand((unsigned) rand());

   for (i=0; i<N; i++) {
     float s = (rand() / (float) RAND_MAX);
     amps[i] = 1.0/(2.0*N) * s * 4;
     phases[i] = s * 2.0 * M_PI;
   }

   for (alpha=0; alpha<360; alpha++) {
     float radian = alpha * ( M_PI / 180.0 );
     radius = 1;
     for (i=0; i<N-1; i++) {
       radius = radius + (amps[i] * cos((i+1)*radian + phases[i]));
     }
     x = cos(radian)*radius;
     y = sin(radian)*radius;
     printf("%2.16f, %2.16f\n", x, y);
   }
}

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接