有效地计算单位网格中矩形的位置

时间:2015-07-06 14:30:27

标签: algorithm layout grid-layout

我正在研究特定的布局算法,以便在基于单位的网格中显示照片。所需的行为是让每张照片逐行放在下一个可用空间中。

animation of what the algorithm should achieve

由于很容易有一千张照片的位置需要立即计算,效率非常重要。

这个问题是否已经用现有算法解决了? 如果没有,我怎样才能使它尽可能高效?

编辑 关于定位: 我现在基本上做的是逐个单元格迭代网格的每一行,直到找到适合该元素的空间。这就是为什么将4放在2旁边的原因。

3 个答案:

答案 0 :(得分:2)

如何按宽度保留下一个可用行的列表?最初,下一个可用行列表如下所示:

  

(0,0,0,0,0)

当您添加第一张照片时,它看起来像

  

(0,0,0,0,1)

然后

  

(0,0,0,2,2)

然后

  

(0,0,0,3,3)

然后

  

(1,1,1,4,4)

最后的照片不会改变列表。

这可能是有效的,因为你只维护一个小列表,在每次迭代时更新一点(而不是每次搜索整个空间。它有点复杂 - 可能有一种情况(高照片)标称的下一个可用行不起作用,然后您可以默认使用现有方法。但总的来说,我认为这应该节省相当多的时间,但代价是增加一点复杂性。

<强>更新 响应@ matteok对coordinateForPhoto(width,height)方法的请求:

假设我将该数组称为“nextAvailableRowByWidth”。

public Coordinate coordinateForPhoto(width, height) {
    int rowIndex = nextAvailableRowByWidth[width + 1]; // because arrays are zero-indexed
    int[] row = space[rowIndex]
    int column = findConsecutiveEmptySpace(width, row);
    for (int i = 1; i < height; i++) {
        if (!consecutiveEmptySpaceExists(width, space[i], column)) {
            return null;
            // return and fall back on the slow method, starting at rowIndex
        }
    }
    // now either you broke out and are solving some other way,
    // or your starting point is rowIndex, column.  Done.
    return new Coordinate(rowIndex, column);
}

更新#2 响应@ matteok关于如何更新nextAvailableRowByWidth数组的请求:

好的,所以你刚刚在行R放置了一张高度为H,宽度为W的新照片。数组中任何小于R的元素都没有改变(因为这个改变不影响他们的行,所以如果在放置照片之前该行中有3个连续可用空间,则之后仍有3个连续可用空间。需要检查范围(R,R + H)中的每个元素,因为它可能已受到影响。让我们假设一个方法maxConsecutiveBlocksInRow() - 因为这很容易写,对吧?

public void updateAvailableAfterPlacing(int W, int H, int R) {
    for (int i = 0; i < nextAvailableRowByWidth.length; i++) {
        if (nextAvailableRowByWidth[i] < R) {
            continue;
        }
        int r = R;
        while (maxConsecutiveBlocksInRow(r) < i + 1) {
            r++;
        }
        nextAvailableRowByWidth[i] = r;
    }
}

我认为应该这样做。

答案 1 :(得分:1)

矩阵怎么样(你的例子是5x9),其中每个单元格都有一个表示左上角距离的值(例如(行+ 1)*(列+ 1)[仅当你的+1时才需要+1]第一行和值为0])。在此矩阵中,您将查找具有最低值的区域(当汇总空单元格的值时)。 第二矩阵(或第一矩阵的第三维)存储每个单元的状态。

编辑:

int[][] grid = new int[9][5];
int[] filledRows = new int [9];
int photowidth = 2;
int photoheight = 1;
int emptyRowCounter = 0;
boolean photoFits = true;

for(int i = 0; i < grid.length; i++){
    for(int m = 0; m < filledRows.length; m++){
        if(filledRows[m]-(photoHeight-1) > i || filledRows[m]+(photoHeight-1) < i){
            for(int j = 0; j < grid[i].length; j++){
                if(grid[i][j] == 0){
                    for(int k = 0; k < photowidth; k++){
                        for(int l = 0; k < photoheight){
                            if(grid[i+l][j+k]!=0){
                                photoFits = false;
                            }
                        }
                    }
                } else{
                    emptyRowCounter++;
                }
            }
            if(photoFits){
                //place Photo at i,j
            }
            if(emptyRowCounter == 5){
                filledRows[i] = 1;
            }
        }
    }
}

答案 2 :(得分:0)

在你上面的gif中,很好地说有一张照片(5)可以放入(1)和(2)左边的间隙。我的直觉表明我们希望避免创造这样的差距。这是一个应该避免这些差距的想法。

维护一个“开放区域”列表,其中一个开放区域有一个int leftBoundary,一个int topBoundary和一个可选的int bottomBoundary。第一个开放区域就是整个网格(leftBoundary:0,topBoundary:0,bottom:null)。

按照高度对照片进行排序,按宽度打破关系。

在您放置所有照片之前:

选择最高的照片(如果是领带,请选择最宽的最高照片)。找到它可以容纳的第一个开放区域(例如grid.Width - region.leftBoundary&gt; = photo.Width)。将照片放在该区域的左上角。放置此照片时,它可能会跨越该区域的整个宽度或高度。

  1. 如果它跨越区域的宽度和高度,则该区域被填充!从开放区域列表中删除此区域。

  2. 如果它跨越宽度而不是高度,请将照片的高度添加到该区域的topBoundary。

  3. 如果它跨越高度而不是宽度,请将照片的宽度添加到该区域的leftBoundary。

  4. 如果它没有跨越边界的高度或宽度,我们将在概念上将此区域划分为两个:一个区域将直接覆盖此照片右侧的空间(称之为rightRegion),并且另一个区域将覆盖该区域下方的空间(称之为区域)。

    rightRegion = {     leftBoundary = parentRegion.leftBoundary + photo.width,     topBoundary = parentRegion.topBoundary,     bottomBoundary = parentRegion.topBoundary + photo.height }

    belowRegion = {     leftBoundary = 0,     topBoundary = parentRegion.topBoundary + photo.height,     bottomBoundary = parentRegion.bottomBoundary }

    用rightRegion替换开放区域列表中的当前区域,并在rightRegion之后直接插入belowRegion。

  5. 您可以直观地看出这个算法如何对您的示例起作用:首先,它会对照片进行排序:(2,3,4,1,5)。

    它考虑2,它适合第一个区域(整个网格)。当它在左上角放置2时,它将该区域直接分割到2的右边空间和2以下的空间。

    然后,它考虑3.它依次考虑开放区域。第一个开放区域位于右侧2. 3适合那里,所以它就在那里。它跨越了该区域的宽度,因此该区域的topBoundary向下调整。

    然后,它考虑4.它再次适合第一个开放区域,所以它在那里放置4。 4跨越区域的高度,因此区域的leftBoundary向右调整。

    然后,1被放入4右边的1x1间隙,填补其区域。最后,5只低于2。