如何将2D景观分割成最小的矩形?

时间:2019-04-14 10:12:18

标签: arrays algorithm multidimensional-array

world是一个二维数组,填充有0(AIR)或1(BLOCK)。我们的任务是将world拆分为不重叠的矩形,以便将这些矩形粘合在一起时,可以得到原始的world。矩形的数量应最小化(如果这会增加很多的计算时间,就不必最小,只要足够小即可),并且矩形应看起来更像正方形而不是条纹(边长的比例应为比1更接近0)。

我能够提出一种算法,但是它有一些问题。

center_of_mass函数返回矩形区域的质心。如果该区域中没有任何块,则返回None

def center_of_mass(grid, x1,y1, x2,y2):
    cx = 0
    cy = 0
    mass = 0
    for x in range(x1,x2+1):
        for y in range(y1,y2+1):
            if 0<=x<w and 0<=y<h and grid[x][y] == BLOCK:
                cx += x
                cy += y
                mass += 1
    if mass == 0:
        return None
    else:
        cx = round(cx/mass)
        cy = round(cy/mass)
        return cx, cy

scom函数在矩形区域中找到第一个suitable center of mass(SCoM):

if world[center_of_mass] is filled with a block, return coordinates from center_of_mass
else{
    split the grid into 4 rectangles with the division point at center_of_mass
    foreach rect in split_rectangles{
        if scom of rect != None{
            return center_of_mass(rect)
        }
    }
    if wasn't able to find a SCoM or the region is 0-sized, return None
}
def scom(grid, x1,y1, x2,y2):
    p = center_of_mass(grid, x1,y1, x2,y2)
    if p is None:
        return None
    else:
        cx, cy = p
        if grid[cx][cy] != BLOCK:
            if abs(x1-x2)==0 and abs(y1-y2)==0:
                return None
            else:
                px = x1 + (x2-x1)//2
                py = y1 + (x2-x1)//2
                r = [(x1,y1,px,py),(px+1,y1,x2,py),
                     (px+1,py+1,x2,y2),(x1,py+1,px,y2)]
                for rect in r:
                    x = scom(grid, *rect)
                    if x is not None:
                        return x
        else:
            return cx, cy

下一个函数stretch采用2d网格grid和一个矩形,该矩形适合某些块,并尝试在所有4个方向上扩展该矩形,直到不可能。然后返回展开的矩形。

def stretch(grid, x1, y1, x2, y2):
    can_grow = True
    while can_grow:
        can_grow = False
        for vec in ((1,0),(0,1),(-1,0),(0,-1)):
            for x,y in extension(x1, y1, x2, y2, vec):
                if (x>=w or x<0 or y>=h or y<0)\
                   or grid[x][y] == AIR or grid[x][y]>BLOCK:
                    break
            else:
                x1,y1,x2,y2 = add_to_rect(x1,y1,x2,y2, vec)
                can_grow = True
    return (x1, y1, x2, y2)

apply_rect函数采用一个grid和一个rect角,并用给定的数字替换给定矩形内的网格中的所有内容。数字不同,因为我想稍后可视化该划分。

def apply_rect(grid, rect, n=MARKED):
    for x,y in all_points(*rect):
        grid[x][y] = n

算法的主要部分是:

基本上,我搜索一个合适的重心,以使矩形具有很多可以生长的区域,从而使它们最终更像正方形。相反,如果我只是扫描网格以找到其中有一个块的第一个单元,那么我将得到很多条纹(我已经检查过了。令人惊讶的是,矩形的数量通常几乎是相同的)。

number_of_rects = 0
found = True

while found:
    found = True
    p = scom(world, 0,0, w-1,h-1)

    if p is None:
        found = False
    else:
        x,y = p

    if found:
        rect = stretch(world, x,y, x,y)
        number_of_rects += 1
        apply_rect(world, rect, number_of_rects+3)

这是输入:

Input image

这是输出:

Output image

这几乎是我所需要的。但是,有两个问题:

  1. 计算需要2秒钟以上的时间。如果我为算法提供320x240的图像,则它会在40秒内给出答案。在世界尺寸可能为1000x800或更大的游戏中需要该算法。我一开始会将世界划分为这些矩形,并可能在游戏过程中随着玩家修改世界而重新排列一些区域。在世代之间仅花几分钟时间就太久了。仅仅是Python的问题还是算法效率低下?

2。左角由像素大小的正方形而不是单个条纹组成。我该如何解决?显而易见的解决方案是扫描最左边的列并将所有像素收集到不同的矩形中。但是,如果我将游戏世界划分为较小的块(例如32x32甚至更小的块),然后将这些块划分为矩形,则左侧的方形意大利面条可能会破坏有效的划分。 —已修复。 / p>

0 个答案:

没有答案