Javascript 用大小不等的圆填充空间
我的问题是:Javascript 用大小不等的圆填充空间,javascript,ruby,layout,geometry,Javascript,Ruby,Layout,Geometry,我的问题是: 我需要在画布中显示一组圆圈 有任意数量的圆,每个圆具有预定义的半径 圆的总面积始终小于画布的面积 我想定位这些圆,以便它们在画布内占据最大的可用空间,而不相互接触。我的目标是实现一个视觉上令人愉悦的效果,圆圈在画布中均匀分布。我不知道这是否真的是“空间填充”,因为我的目标不是最小化元素之间的距离,而是最大化它 以下是我努力实现的一个例子: 我的第一个“暴力”想法如下: 对于每个圆:计算其边界与其他圆边界之间的最短距离;将所有这些距离相加,称之为X 计算所有X的总和 随机更改圆
- 我需要在画布中显示一组圆圈李>
- 有任意数量的圆,每个圆具有预定义的半径李>
- 圆的总面积始终小于画布的面积李>
这是一个公认的答案,它使用拉斐尔来画圆圈。也许一些应用会有用。我会尝试在球体之后插入球体(首先是最大的)。每一个都添加到最大的可用空间中,并带有一些随机抖动 找到(或多或少)最大可用空间的一种相对简单的方法是,想象视图上的点栅格,并为每个栅格点(在2D阵列中)存储与任何项目最近的距离:边或球体,以最接近的为准。此阵列将随着每个新球体的添加而更新 要添加一个新球体,只需取距离最大的网格点并应用一些随机抖动(实际上您知道可以抖动多少,因为您知道到最近项目的距离)。(我将随机化不超过(d-r)/2,其中d是阵列中的距离,r是要添加的球体的半径 在添加另一个圆后更新此阵列不是火箭科学:您可以为每个网格点计算到新添加球体的距离,如果该距离更大,则替换存储的值 可能是栅格太粗糙,无法再添加任何圆(当2D阵列包含的距离不大于要添加的圆的半径时)。然后,在继续之前,必须增加栅格分辨率(例如,增加一倍) 下面是这个实现的一些结果(我花了大约100行代码)
- 100个大小不等的圆圈
- 500个大小不等的圆圈
- 100个同样大小的圆圈
这里是一些粗糙C++代码(只是算法,不要指望它编译)< /P>
//初始化
//画布尺寸
浮动宽度=768;
浮子高度=1004;
//该算法在画布上创建网格
浮动网格大小=10;
int gridColumns,gridRows;
浮动*距离;
void init距离()
{
//确定网格维度并分配数组
gridColumns=宽度/网格大小;
网格行=高度/网格大小;
//我们将二维数组存储为一维数组:
dist=新浮点[gridColumns*gridRows];
//到边缘距离最短的初始距离数组
浮动y=网格大小/2.0;
对于(int row=0;row,因为你的目标只是“达到令人愉快的效果”,而不是解决数学问题,你应该先尝试最简单的算法,看看它是否合适。不需要使用非常复杂的数学
我知道你希望球体“填满”可用空间,而不是在其他区域拥挤时留下大的空白区域。你还希望布局看起来是随机的,而不是在网格或类似的东西上排列
要达到这一目的,最简单的方法就是将球体一个接一个地放置在随机位置。如果一个球体落在已放置球体的顶部,则生成另一个随机位置,直到找到合适的位置为止
在显示的图像中,似乎有大约40个球体。40个球体全部降落在图像的同一区域,留下图像其余部分为空的几率非常非常小。随着球体数量的增加,获得非常不平衡布局的几率将接近于零
首先尝试一下,看看它是否满足你的需要。如果它不是“甚至”足够了,你应该能够使用一些非常简单的数学来偏向随机选择的位置,以选择空白区域。应该不需要使用复杂的算法。不是圆,而是路易丝,看看克里斯的答案!这些图像看起来很棒——我想你应该接受它。@KrisVanBael,在GitHub上发布代码怎么样?+1开始时很简单。但随机确实不好看。你确实激励我编辑我的答案。是的,这是一个很好的开始建议。我只是尝试了随机的非重叠位置,它已经比手动定位好,但仍然远离我想要达到的视觉效果。我会在尝试其他解决方案时发布一些更新。我喜欢它,+1。我认为这可能是最好的解决方案。谢谢你,亚历克斯。但是自从Loisty没有接受我的回答,我就一直在那里实现它。非常好的克里斯,太棒了!你介意把代码发布到Git上吗?我发布了我的C++项目的相关位。但是它是一个相当快和肮脏的算法的概念证明,所以它不是很好。安。这非常有帮助,克里斯。我会尽快给你奖金。这个解决方案对动画布局来说是最好的。
// INITIALIZATION
// Dimension of canvas
float width = 768;
float height = 1004;
// The algorithm creates a grid on the canvas
float gridSize=10;
int gridColumns, gridRows;
float *dist;
void initDistances()
{
// Determine grid dimensions and allocate array
gridColumns = width/gridSize;
gridRows = height/gridSize;
// We store a 2D array as a 1D array:
dist = new float[ gridColumns * gridRows ];
// Init dist array with shortest distances to the edges
float y = gridSize/2.0;
for (int row=0; row<gridRows; row++)
{
float distanceFromTop = y;
float distanceFromBottom = height-y;
for (int col=0; col<gridColumns; col++)
{
int i = row*gridColumns+col;
dist[i]=(distanceFromTop<distanceFromBottom?distanceFromTop:distanceFromBottom);
}
y+=gridSize;
}
float x = gridSize/2.0;
for (int col=0; col<gridColumns; col++)
{
float distanceFromLeft = x;
float distanceFromRight = width-x;
for (int row=0; row<gridRows; row++)
{
int i = row*gridColumns+col;
if (dist[i]>distanceFromLeft) dist[i] = distanceFromLeft;
if (dist[i]>distanceFromRight) dist[i] = distanceFromRight;
}
x+=gridSize;
}
}
void drawCircles()
{
for (int circle = 0; circle<getNrOfCircles(); circle++)
{
// We assume circles are sorted large to small!
float radius = getRadiusOfCircle( circle );
// Find gridpoint with largest distance from anything
int i=0;
int maxR = 0;
int maxC = 0;
float maxDist = dist[0];
for (int r=0; r<gridRows; r++)
for (int c=0; c<gridColumns; c++)
{
if (maxDist<dist[i]) {
maxR= r; maxC= c; maxDist = dist[i];
}
i++;
}
// Calculate position of grid point
float x = gridSize/2.0 + maxC*gridSize;
float y = gridSize/2.0 + maxR*gridSize;
// Apply some random Jitter
float offset = (maxDist-radius)/2.0;
x += (rand()/(float)RAND_MAX - 0.5) * 2 * offset;
y += (rand()/(float)RAND_MAX - 0.5) * 2 * offset;
drawCircle(x,y,radius);
// Update Distance array with new circle;
i=0;
float yy = gridSize/2.0;
for (int r=0; r<gridRows; r++)
{
float xx = gridSize/2.0;
for (int c=0; c<gridColumns; c++)
{
float d2 = (xx-x)*(xx-x)+(yy-y)*(yy-y);
// Naive implementation
// float d = sqrt(d2) - radius;
// if (dist[i]>d) dist[i] = d;
// Optimized implementation (no unnecessary sqrt)
float prev2 = dist[i]+radius;
prev2 *= prev2;
if (prev2 > d2)
{
float d = sqrt(d2) - radius;
if (dist[i]>d) dist[i] = d;
}
xx += gridSize;
i++;
}
yy += gridSize;
}
}
}