让我们从轴对称圆柱体开始。我认为它应该是这样的:
1.定义:将
XY
平面作为基础,圆柱体从
(0,0,0)
开始向上生长到距离
l
,半径为
r
。同时,让我定义
l0,l1
为节点之间的最小和最大距离。
2.创建主路径:从圆柱体的起点到终点放置一系列相连的节点。后面会用到这些节点来扩展集群。这也确保了始末点之间有路径。只需在
<l0,~l1>
范围内添加一些随机增量到
z
,并使用x,y作为随机点,在比
r
小的某个较小圆内停留在路径上(我使用
r
的10%)。
3.增加集群:简单地取任意已放置点,为其添加大小为
<l0,l1>
的随机位移,并且如果仍然在圆柱体内且不太接近其他点,则将其添加到您的数据中并与选定的点连接。如果您使用按
z
排序的点,则可以加速此过程,因此您可以摆脱
O(n)
搜索,转而使用
O(log(n))
。
完成后,您只需将轴对齐数据转换为所需的最终位置和方向。例如,如果您将圆柱体定义为2个端点和半径,则可以将
l
计算为它们的距离,将
l0,l1
计算为其分数。此外,您可以使用简单的向量数学从中计算出3个垂直基向量(其中2个表示
XY
平面,一个表示圆柱轴
Z
),让我们称它们为
u,v,w
。从那时起,只要进行向量运算就可以完成... 您也可以根据这些构建4x4变换矩阵并使用它。
以下是这样的小型C++示例:
const int N=200;
double pnt[N][3];
int lnk[N];
void vector_mul(double *c,double *a,double *b)
{
double q[3];
q[0]=(a[1]*b[2])-(a[2]*b[1]);
q[1]=(a[2]*b[0])-(a[0]*b[2]);
q[2]=(a[0]*b[1])-(a[1]*b[0]);
for(int i=0;i<3;i++) c[i]=q[i];
}
void generate(double *p0,double *p1,double r)
{
int i,j,k,ok;
double u[3],v[3],w[3];
double a,dx,dy,dz,x,y,z,z0;
double l;
double l0=0.03;
double l1=0.06;
double ll0=l0*l0,ll1=l1*l1,rr=r*r;
Randomize();
for (l=0.0,i=0;i<3;i++){ w[i]=p1[i]-p0[i]; l+=w[i]*w[i]; }
l=sqrt(l); l0*=l; l1*=l;
for (i=0;i<3;i++) w[i]/=l;
if (fabs(w[0])<0.75){ u[0]=1.0; u[1]=0.0; u[2]=0.0; }
else { u[0]=0.0; u[1]=1.0; u[2]=0.0; }
vector_mul(v,u,w);
for (z0=0,i=0;i<N;)
{
x=2.0*r*Random()-r; x*=0.1;
y=2.0*r*Random()-r; y*=0.1;
z=z0+l0+(0.75*(l1-l0)*Random()); if (z>l) break;
if ((z<0)||(z>l)) continue;
if ((x*x)+(y*y)>rr) continue;
for (ok=1,j=0;j<i;j++)
{
dx=pnt[j][0]-x;
dy=pnt[j][1]-y;
dz=pnt[j][2]-z;
if ((dx*dx)+(dy*dy)+(dz*dz)<ll0){ ok=0; break; }
}
if (!ok) continue;
pnt[i][0]=x;
pnt[i][1]=y;
pnt[i][2]=z; lnk[i]=i-1; i++; z0=z;
}
for (;i<N;)
{
for (;;)
{
dx=Random()-0.5;
dy=Random()-0.5;
dz=Random()-0.5;
a=(dx*dx)+(dy*dy)+(dz*dz);
if (a>1e-3) break;
}
a=(l0+((l1-l0)*Random()))/sqrt(a); dx*=a; dy*=a; dz*=a;
for (k=0;k<10;k++)
{
j=Random(i); lnk[i]=j; ok=1;
x=pnt[j][0]+dx;
y=pnt[j][1]+dy;
z=pnt[j][2]+dz;
if ((z<0)||(z>l)){ ok=0; break; }
if ((x*x)+(y*y)>rr){ ok=0; break; }
for (j=0;j<i;j++)
{
dx=pnt[j][0]-x;
dy=pnt[j][1]-y;
dz=pnt[j][2]-z;
if ((dx*dx)+(dy*dy)+(dz*dz)<ll0){ ok=0; break; }
}
if (ok) break;
}
if (!ok) continue;
pnt[i][0]=x;
pnt[i][1]=y;
pnt[i][2]=z; i++;
}
for (i=0;i<N;i++)
{
x=pnt[i][0];
y=pnt[i][1];
z=pnt[i][2];
for (j=0;j<3;j++) pnt[i][j]=p0[j]+(x*u[j])+(y*v[j])+(z*w[j]);
}
}
使用和用途:
double p0[3]={-1.7,-0.5,-0.2};
double p1[3]={+1.7,+0.5,+0.4};
generate(p0,p1,0.5);
预览:
![preview](https://istack.dev59.com/ozUP5.gif)
如果设置的N
太大,第二个主循环可能会永远循环。因此,您可能需要添加一些结束条件,例如如果连续命中i
次而没有i
更改,则停止。这是因为l0
约束限制了点的最大密度,如果N
大于该密度,则无法添加更多点...
现在,如果您想要随机半径球而不是点,则只需添加一些随机半径,但不要忘记根据半径调整内部圆柱和最近距离测试...