Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate number of columns and rows needed depending on space available

Tags:

java

algorithm

I need to distribute "n" amount of images in a given container. It should optimize the space depending if the container's aspect ratio is landscape, portrait or a square. The intention is that the images are rendered the biggest size possible and all of them have the same space available. For this, I plan to create a grid but I need to know how many columns and how many rows it must have according to the aspect ratio of the container.

I took a look at this question but it is not exactly what I need.

This image with n = 8 should clarify a bit:

Calculate columns and rows

If the container is vertical, then 4 rows and 2 columns are needed, if the container is a square, then 3 rows and 3 columns are needed, if the container is horizontal, then 2 rows and 4 columns are needed.

I am writing a function but I am stuck in the middle:

private int[] calculateRowsAndColumnsNeeded(int numberOfImages, Dimension containerSize){
int numberOfColumns = 0;
int numberOfRows = 0;

int containerArea = containerSize.height * containerSize.width;
float singleCellArea = containerArea / numberOfImages;
double cellSideLength = Math.sqrt(singleCellArea);

// What to do with cellSideLength to get the right number of columns and rows?

return new int[]{numberOfColumns, numberOfRows};}

I would really appreciate some help here.

Thanks in advance,

Diego

like image 623
DiegoSahagun Avatar asked Jan 22 '14 15:01

DiegoSahagun


2 Answers

I came to a solution, it might be not the best algorithm but it works, at least, for 1 - 20 elements which is what I need. I didn't test further. I will improve it later if I find a way.

    private static int[] calculateRowsAndColumnsNeeded(int numberOfImages, Dimension containerSize){
    int colsAttempt = 0;
    int rowsAttempt = 0;
    // Calculate the length of one side from a single cell
    int containerArea = containerSize.height * containerSize.width;
    float singleCellArea = containerArea / numberOfImages;
    double cellSideLength = Math.sqrt(singleCellArea);

    colsAttempt = (int) Math.floor(containerSize.width / cellSideLength);
    rowsAttempt =  (int) Math.floor(containerSize.height / cellSideLength);

    if (colsAttempt * rowsAttempt >= numberOfImages){

        return new int[]{rowsAttempt, colsAttempt};

    }
    // If the container is a square or bigger horizontally than vertically
    else if (containerSize.height <= containerSize.width){

        colsAttempt = (int) Math.ceil(containerSize.width / cellSideLength);
        rowsAttempt =  (int) Math.floor(containerSize.height / cellSideLength);

        if (colsAttempt * rowsAttempt >= numberOfImages){
            // 
            return new int[]{rowsAttempt, colsAttempt};

        }else{

            colsAttempt = (int) Math.floor(containerSize.width / cellSideLength);
            rowsAttempt =  (int) Math.ceil(containerSize.height / cellSideLength);

            if (colsAttempt * rowsAttempt >= numberOfImages){
                return new int[]{rowsAttempt, colsAttempt};
            }else{
                colsAttempt = (int) Math.ceil(containerSize.width / cellSideLength);
                rowsAttempt =  (int) Math.ceil(containerSize.height / cellSideLength);

                if (colsAttempt * rowsAttempt >= numberOfImages){
                    return new int[]{rowsAttempt, colsAttempt};
                }else{
                    return null;
                }
            }
        }
    } 
    // If the container is bigger vertically than horizontally
    else {

        colsAttempt = (int) Math.floor(containerSize.width / cellSideLength);
        rowsAttempt =  (int) Math.ceil(containerSize.height / cellSideLength);

        if (colsAttempt * rowsAttempt >= numberOfImages){
            // 
            return new int[]{rowsAttempt, colsAttempt};

        }else{

            colsAttempt = (int) Math.ceil(containerSize.width / cellSideLength);
            rowsAttempt =  (int) Math.floor(containerSize.height / cellSideLength);

            if (colsAttempt * rowsAttempt >= numberOfImages){
                return new int[]{rowsAttempt, colsAttempt};
            }else{
                colsAttempt = (int) Math.ceil(containerSize.width / cellSideLength);
                rowsAttempt =  (int) Math.ceil(containerSize.height / cellSideLength);

                if (colsAttempt * rowsAttempt >= numberOfImages){
                    return new int[]{rowsAttempt, colsAttempt};
                }else{
                    return null;
                }
            }
        }
    }
}
like image 162
DiegoSahagun Avatar answered Oct 18 '22 15:10

DiegoSahagun


Here's something in JavaScript. Does this work for you?

var height = 30, //pixels
    width = 30,  //pixels
    n = 8,
    cellSideLength = Math.floor(Math.sqrt(height * width / n)),
    targetColumns = Math.floor(width / cellSideLength) == width / cellSideLength 
                  ? width/cellSideLength 
                  : Math.floor(width / cellSideLength) + 1,
    cellSideLengthTemp = cellSideLength 

targetColumns = Math.min(targetColumns,n)

while (width / cellSideLengthTemp < targetColumns)
    cellSideLengthTemp-- //decrease by one pixel

while (Math.floor(height / cellSideLengthTemp) * targetColumns < n)
    cellSideLengthTemp-- //decrease by one pixel

var numColumns = Math.floor(width / cellSideLengthTemp),
    numRows = 1

while (numColumns * numRows < n)
    numRows++

console.log(numColumns,numRows,cellSideLengthTemp,cellSideLength)
like image 2
גלעד ברקן Avatar answered Oct 18 '22 17:10

גלעד ברקן