根据n项,总面积和H:W比创建最佳网格

时间:2010-08-18 14:22:36

标签: algorithm math grid

我正在创建一个应用程序,它采用许多大小相等的矩形并将它们放在屏幕上的网格中。我有大部分逻辑用于调整单元格中矩形的大小和居中,但我遇到了定义矩形必须符合的网格的实际部分的问题。

理想情况下,最后我会有这样的函数(伪代码):


function getGridDimensions (rect surface, int numItems, float hwRatio) {
    // do something to determine grid-height and grid-width
    return gridDimensions;
}

我最初的抨击涉及到这样的事情:


gridHeight = surface.width / sqrt(numItems);
gridWidth = surface.height / sqrt(numItems);

如果我的项目都是完美的正方形,这可以很好地工作,但由于它们是矩形,每个单元格中都有很多未使用的空白区域。

对谷歌的任何想法或条款可能会指引我朝着正确的方向发展?

2 个答案:

答案 0 :(得分:15)

我对你的一些输入参数有点不清楚,但我假设你有矩形的高度和宽度,矩形的数量和理想的高宽比(即首选的gridheight / gridwidth)。

如果是这种情况,那么我可能会从“标准化”您的尺寸开始,因此为了以下计算的目的,我们说宽度单位与矩形的宽度相同,同样对于单位高度。如果以实际单位表示的高度/宽度比为k,那么以Rectange为单位的高度/宽度比将为k * RectWidth / RectHeight。我会称之为K。

所以现在每个矩形的面积都是1,所以我们的总面积是N,其中N是项目数。然后我们可以通过说gridHeight * gridWidth = N和gridHeight / gridWidth = K

来近似我们的高度添加宽度来给自己提供我们首选的网格宽高比。

通过这些,我们得到gridHeight = sqrt(KN)和gridWidth = sqrt(N / K)。

如果你将其中一个关闭到一个合适的整数(我不确定哪一个最接近整数舍入将给你最好的结果,或者如果它的任何四舍五入将给出该值的最小百分比变化是最好的 - 如果你非常在意的话,你可以随时尝试所有这四种方法。一旦你有一个整数值,你就可以通过找到可以乘以另一个并且仍然大于N的最小整数来计算另一个,以确保你适合网格中的所有矩形。)

然后,您可以通过将高度乘以rectHeight并将wdith乘以RectWidth来将整数值更改回实数值。

希望一切都有道理。 :)

编辑工作示例:

所需的最终网格宽高比= 1024/768(k)(假设768是宽度,1024是高度 - 我一直想把它作为标准屏幕分辨率反过来:))

“标准化”宽高比=(1024/768)*(300/109)= 3.6697(K)

网格高度因此是sqrt(KN)= sqrt(366.97)= 19.16

网格宽度为sqrt(N / K)= 5.22

观察这一点,我们可以直观地看到宽度5和高度20将是我们的最佳匹配。其他选项可能是6和19.但是这会浪费更多空间(我认为实际上可能实际上最小化宽度和高度的乘积,这是最好的计算,但我不确定)。

这是我们在单元格中的网格大小。然后将其缩放到1500乘以2180的像素尺寸。缩小到适合768x1024意味着将两者除以2.129(1500/768和2180/1024中的较大者)。所以你的图像将缩小2.129倍到141x51(ish),你使用的总面积实际上是705x1020,这应该给出最小的空白。

希望现在更有意义。我承认,我错了几次把真正的价值放进去,所以我完全理解为什么你想要一个有效的例子。 ; - )

答案 1 :(得分:0)

所以我认为我有一个可以通过蛮力获得最佳答案的算法。最多100-1000个项目仍应相当快。这是python中的算法。基本上,它会通过所有方式将项目分散到行和列中,然后选择效率最高的方法。

def calculate_best_screen_packing(N, img_resolution = (800,600), screen_resolution = (1920, 1080)):    
    screen_x = screen_resolution[0]
    screen_y = screen_resolution[1]
    screen_area = screen_x * screen_y

    img_x = img_resolution[0]
    img_y = img_resolution[1]
    img_aspect = img_x / img_y

    best_dims = (None,None)
    best_eff = 0.0

    for n_rows in range(1,N//2 +1):
        #print(i)
        n_cols = N // n_rows
        if N % n_rows != 0: n_cols = n_cols+1

        #print(n_rows, n_cols)

        # Test by maximising image height        
        img_y_scaled = screen_y / n_rows
        img_x_scaled = img_y_scaled * img_aspect
        img_area_scaled = img_x_scaled * img_y_scaled
        eff = img_area_scaled * N / screen_area
        #print(img_x_scaled, img_y_scaled, eff)

        if eff <= 1.0 and eff > best_eff:
            best_eff = eff
            best_dims = (n_rows, n_cols)

        # Test by maximising image width                
        img_x_scaled = screen_x / n_cols
        img_y_scaled = img_x_scaled / img_aspect
        img_area_scaled = img_x_scaled * img_y_scaled
        eff = img_area_scaled * N / screen_area
        #print(img_x_scaled, img_y_scaled, eff)
        if eff <= 1.0 and eff > best_eff:
            best_eff = eff
            best_dims = (n_rows, n_cols)

    #print("Best dims:",best_dims,best_eff)
    return best_dims