Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

efficiently calculate locations for rectangles in a unit grid

I'm working on a specific layout algorithm to display photos in a unit based grid. The desired behaviour is to have every photo placed in the next available space line by line.

animation of what the algorithm should achieve

Since there could easily be a thousand photos whose positions need to be calculated at once, efficiency is very important.

Has this problem maybe been solved with an existing algorithm already? If not, how can I approach it to be as efficient as possible?

Edit Regarding the positioning: What I'm basically doing right now is iterating every line of the grid cell by cell until I find room to fit the element. That's why 4 is placed next to 2.

like image 738
matteok Avatar asked Jul 06 '15 14:07

matteok


People also ask

How do you find the number of rectangles in a grid?

If the grid is 1×1, there is 1 rectangle. If it grid is 3×1, there will be 3 + 2 + 1 = 6 rectangles. If we add one more column to N×1, firstly we will have as many rectangles in the 2nd column as the first, and then we have that same number of 2×M rectangles.

How many rectangles can you find in a 2x2 grid of squares?

Hence, the total number of rectangles in a 2 x2 grid = 4 + 4 +1 = 9.

How many rectangles are in a 4 by 4 grid?

We discovered several different methods of counting rectrangles of various dimensions. Each of the square rectangles follow the same pattern as last week's challenge so the 1x1 was 25 rectangles, the 2x2 was 16 rectangles, the 3x3 was 9 rectangles, the 4x4 was 4 rectangles, and the 5x5 was 1 rectangle.

How many rectangles are there in a 3x5 grid?

of squares and rectangles of height Unit 5 = 1(3+2+1) = 6, So, total no. of squares and rectangles = 90.


1 Answers

How about keeping a list of next available row by width? Initially the next-available-row list looks like:

(0,0,0,0,0)

When you've added the first photo, it looks like

(0,0,0,0,1)

Then

(0,0,0,2,2)

Then

(0,0,0,3,3)

Then

(1,1,1,4,4)

And the final photo doesn't change the list.

This could be efficient because you're only maintaining a small list, updating a little bit at each iteration (versus searching the entire space every time. It gets a little complicated - there could be a situation (with a tall photo) where the nominal next available row doesn't work, and then you could default to the existing approach. But overall I think this should save a fair amount of time, at the cost of a little added complexity.

Update In response to @matteok's request for a coordinateForPhoto(width, height) method:

Let's say I called that array "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);
}

Update #2 In response to @matteok's request for how to update the nextAvailableRowByWidth array:

OK, so you've just placed a new photo of height H and width W at row R. Any elements in the array which are less than R don't change (because this change didn't affect their row, so if there were 3 consecutive spaces available in the row before placing the photo, there are still 3 consecutive spaces available in it after). Every element which is in the range (R, R+H) needs to be checked, because it might have been affected. Let's postulate a method maxConsecutiveBlocksInRow() - because that's easy to write, right?

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;
    }
}

I think that should do it.

like image 143
Carl Manaster Avatar answered Nov 13 '22 17:11

Carl Manaster