Let’s say you have a 2D grid with distinct, arbitrary shapes on it (polygons of any sort), and you’d like to isolate the distinct shapes. For example, this 2D grid could represent a game’s map with a system of different shaped buildings. If your player is on top of one building, the game logic requires the application to distinguish the isolated shapes for pathfinding purposes etc.

E.g.:

{0,0,0,0,0,0}
{0,1,1,0,0,0}
{0,1,1,0,0,1}
{1,1,0,1,0,1}
{0,0,0,1,0,1}
{0,0,1,1,1,1}

You can see two distinct shapes on this grid, one on the top left, the other in the bottom right (we ignore touching diagonals).

To isolate the shapes, iterate the grid left-to-right, top-to-bottom. For each cell (xi, yi) that has a value vi where i > 0, check the cell directly above i.e. (xi, yi-1), and directly behind i.e. (xi-1, yi), where i-1 > 0. If either of these has a value vi where i > 0, assign (xi, yi) the value vi. If they don’t, this cell must be part of a new shape. Assign (xi, yi) the value vi+1.

The output will be:

{0,0,0,0,0,0}
{0,1,1,0,0,0}
{0,1,1,0,0,2}
{1,1,0,2,0,2}
{0,0,0,2,0,2}
{0,0,2,2,2,2}

As mentioned, this algorithm ignores touching diagonals. In the example, if we considered (xi, yi) and (x4, y4) to be touching, then there’d just be a single shape on this grid.

Here’s an implementation in Lua:

-- og is "old gird"
-- ng is "new grid"
local function createPolyObjects (og)
    local objNum = 0
    local ng = {}
    for y = 1, #og, 1 do
        ng[y] = {}
        for x = 1, #og[y], 1 do
            local tile = {}
            tile.val = og[y][x];
            tile.x = x
            tile.y = y
            if tile.val == 1 then
                --Tile above
                local tileAbove = {}
                tileAbove.x = x
                tileAbove.y = y-1
                if tileAbove.y > 0 then
                    tileAbove.val = ng[tileAbove.y][tileAbove.x]
                else
                    tileAbove = nil
                end
 
                --Tile behind
                local tileBehind = {}
                tileBehind.x = x-1
                tileBehind.y = y
                if tileBehind.x > 0 then
                    tileBehind.val = ng[tileBehind.y][tileBehind.x]
                else
                    tileBehind = nil
                end
 
                --Determine the correct objNum to assign
                if tileAbove == nil or tileAbove.val == 0 then
                    if tileBehind == nil or tileBehind.val == 0 then
                        objNum = objNum + 1
                    end
                else
                    if tileBehind.val > tileAbove.val then
                        ng = obj.changeObjNums(ng, x, y, objNum, tileAbove.val)
                        objNum = tileAbove.val
                    else
                        objNum = tileAbove.val
                    end
                end
 
                --Populate ng
                ng[y][x] = objNum
            else
                ng[y][x] = 0
            end
        end
    end
    obj.max = objNum
    return ng
end
 
--Helper function:
--Changes the "object number" used on the grid
local function changeObjNums (grid, x, y, oldNum, newNum)
    for j = y, 1, -1 do
        for i = x, 1, -1 do
            if grid[j][i] == oldNum then
                grid[j][i] = newNum
            end
        end
    end
    return grid
end

This code is part of my Lua Grid Tools repo on Bitbucket.

Next I’ll explain how to break up the polygons into distinct rectangles. This is useful if you’re aiming to add each shape to Box2D, as convex shapes cannot be used in Box2D. Check that out here.