【问题标题】:optimized grid for rectangular items矩形项目的优化网格
【发布时间】:2019-11-07 00:42:25
【问题描述】:

我有 N 个长宽比为 Aitem (X:Y) 的矩形项目。
我有一个长宽比为 Aview 的矩形显示区域

项目应排列在类似表格的布局中(即 r 行,c 列)。

什么是理想的网格行 x 列,以便单个项目最大? (当然,行 * 列 >= N - 即可能存在“未使用”的网格位置)。

一个简单的算法可以遍历行 = 1..N,计算所需的列数,并保留最大项目的行/列对。

我想知道是否存在非迭代算法(例如,对于 Aitem = Aview = 1,rows / cols 可以近似为 sqrt(N))。

【问题讨论】:

    标签: algorithm math layout mathematical-optimization


    【解决方案1】:

    注意:我不太明白 Frédéric 的回答,所以我自己解决了这个问题,并想出了似乎相同的解决方案。我想我不妨解释一下我做了什么以防万一。

    首先,我将视图的纵横比标准化为项目的纵横比。 (我假设您不想旋转这些项目。)

    a = (view_width/view_height) / (item_width/item_height)
    

    现在用正方形包装一个宽/高比a 的矩形相当于用项目包装视图。理想的情况是我们的网格(现在的正方形)完全填充矩形,这会给我们

    a = c/r
    

    其中rc 是行数和列数:

    N = r*c
    

    将这两个等式相乘/除以得到

    N*a = c^2              N/a = r^2
    c = sqrt(N*a)          r = sqrt(N/a)
    

    如果网格是完美的,rc 将是整数,但如果不是,您必须尝试 Frédéric 提到的三个选项并保留 r*c 最小但仍大于 N 的选项:

    • floor(r), ceil(c)
    • ceil(r), floor(c)
    • ceil(r), ceil(c)

    【讨论】:

    • 感谢您提供详细的 cmets。在某些情况下,蛮力搜索“看起来更好”比您/弗雷德里克建议的这些结果。不过,也许我仍然对四舍五入有些错误……我只是在玩这个兼职。
    • 十年后,但这个答案为我省去了很多麻烦,并使我脑海中的程序成为可能。非常感谢。
    【解决方案2】:

    您的解决方案可以轻松改进以处理一般情况:

    如果我们(暂时)忘记了需要整数的行和列,我们有

    行 * 列 = N

    x = aitem * y

    aview = rows * x = rows * aitem * y

    1 = 列 * y = (N/行) * (aview / [aitem*rows]) = N * aview /(aitem * rows²)

    因此 rows=sqrt(N *aview/aitem) 和 columns = N/rows = sqrt(N * aitem / aview)

    那么 ceil(rows) 和 ceil(columns) 是一个解决方案,而 floor(rows) 和 floor(columns) 太小而不能一起成为一个解决方案(如果行和列不是整数)。这留下了 3 种可能的解决方案:

    • 楼层(行)天花板(列)
    • ceil(rows) floor(columns)
    • ceil(行) ceil(列)

    已编辑以更正方程式。第一个结果是错误的(见 cmets)

    【讨论】:

    • 我认为您最终方程式中的 [aitem*aview] 是一个错字。那N=2,aview=6,aitem=3的情况呢。你最终会得到太多的列。
    • 这不是错字。这是第一个方程式中的错误,我对 aview 和 aitem 采用了相反的定义。这很尴尬,因为我的初始公式不是同质的(是的,我是物理学家)并且错误出现在一个简单的测试用例中。谢谢你指点。现在,正如预期的那样,我们为您的测试用例提供了 2 行 1 列
    【解决方案3】:

    好问题。如果您的视图具有尺寸 A x B(固定)并且您的项目具有尺寸 a x b(可变,要最大化),那么您需要:

    trunc(A / a) * trunc(B / b) >= N

    我不知道如何解决这个问题 - trunc 是棘手的部分,因为它是非线性的。

    【讨论】:

      【解决方案4】:

      我无法得到对我有用的这个问题的答案,但我从Neptilo 找到了一个类似问题的简洁实现。但它不适用于矩形,仅适用于正方形。所以我应用mckeed 的想法来规范化矩形,然后按照正方形的算法。

      结果是fitToContainer() 函数。给它适合ncontainerWidthcontainerHeight 以及原始itemWidthitemHeight 的矩形数量。如果项目没有原始宽度和高度,请使用itemWidthitemHeight 指定所需的项目比例。

      例如,fitToContainer(10, 1920, 1080, 16, 9) 的结果是 {nrows: 4, ncols: 3, itemWidth: 480, itemHeight: 270},因此 480 x 270(像素或任何单位)的四列和 3 行。

      要在 1920x1080 的同一示例区域中放置 10 个正方形,您可以调用 fitToContainer(10, 1920, 1080, 1, 1) 得到 {nrows: 2, ncols: 5, itemWidth: 384, itemHeight: 384}

      function fitToContainer(n, containerWidth, containerHeight, itemWidth, itemHeight) {
          // We're not necessarily dealing with squares but rectangles (itemWidth x itemHeight),
          // temporarily compensate the containerWidth to handle as rectangles
          containerWidth = containerWidth * itemHeight / itemWidth;
          // Compute number of rows and columns, and cell size
          var ratio = containerWidth / containerHeight;
          var ncols_float = Math.sqrt(n * ratio);
          var nrows_float = n / ncols_float;
      
          // Find best option filling the whole height
          var nrows1 = Math.ceil(nrows_float);
          var ncols1 = Math.ceil(n / nrows1);
          while (nrows1 * ratio < ncols1) {
              nrows1++;
              ncols1 = Math.ceil(n / nrows1);
          }
          var cell_size1 = containerHeight / nrows1;
      
          // Find best option filling the whole width
          var ncols2 = Math.ceil(ncols_float);
          var nrows2 = Math.ceil(n / ncols2);
          while (ncols2 < nrows2 * ratio) {
              ncols2++;
              nrows2 = Math.ceil(n / ncols2);
          }
          var cell_size2 = containerWidth / ncols2;
      
          // Find the best values
          var nrows, ncols, cell_size;
          if (cell_size1 < cell_size2) {
              nrows = nrows2;
              ncols = ncols2;
              cell_size = cell_size2;
          } else {
              nrows = nrows1;
              ncols = ncols1;
              cell_size = cell_size1;
          }
      
          // Undo compensation on width, to make squares into desired ratio
          itemWidth = cell_size * itemWidth / itemHeight;
          itemHeight = cell_size;
          return { nrows: nrows, ncols: ncols, itemWidth: itemWidth, itemHeight: itemHeight }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-28
        • 1970-01-01
        • 1970-01-01
        • 2015-01-21
        • 2022-07-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多