package com.vitco.util.graphic;
import java.awt.*;
import java.util.LinkedList;
/**
* Handles finding of a maximal rectangle in a binary 2D array.
*
* Maximum is defined as "Math.min(width, height) * 16384 + Math.max(width, height)"
*
* Hence the maximum image is 16384 ^ 2
*
* Idea taken from:
* www.drdobbs.com/database/the-maximal-rectangle-problem/184410529
*/
public class MaxRectFinder {
// defines the size (for comparison)
private static int getSize(short width, short height) {
return Math.min(width, height) * 16384 + Math.max(width, height);
}
// takes as input a matrix with ones/zeros
public static Rectangle maximalRectangle(short[][] matrix) {
// extract static length information
short lenX = (short) matrix.length;
short lenY = (short) matrix[0].length;
// loop over columns and fill in numbers that count how many
// ones are above (# + 1)
short previous;
for (short x = 0; x < lenX; x++) {
previous = matrix[x][0];
for (short y = 1; y < lenY; y++) {
if (matrix[x][y] == 1) {
previous++;
matrix[x][y] = previous;
} else {
previous = 0;
}
}
}
// loop over rows and keep track of the rectangles that we find
LinkedList<short[]> stack = new LinkedList<short[]>();
// tmp variable
short[] info;
int size = 0;
Rectangle result = null;
// loop over rows
short nHeight;
for (short y = 0; y < lenY; y++) {
short height = 0;
for (short x = 0; x < lenX; x++) {
nHeight = matrix[x][y];
if (height < nHeight) {
// open rectangle (start position and height)
while (height < nHeight) {
height++;
stack.push(new short[]{x, height});
}
} else if (height > nHeight) {
// close rectangles
height = nHeight;
do {
// close rectangles until the next doesn't need closing
info = stack.pop();
if (info[1] > height) {
int sizeTmp = getSize((short) (x - info[0]), info[1]);
if (sizeTmp > size) {
result = new Rectangle(info[0], y - info[1] + 1, x - info[0], info[1]);
size = sizeTmp;
}
}
} while (!stack.isEmpty() && info[1] > height);
// add missing rectangle
if (!(info[1] > height)) {
stack.push(info);
}
}
}
// handle remaining stack rectangles
while (!stack.isEmpty()) {
info = stack.pop();
int sizeTmp = getSize((short) (lenX - info[0]), info[1]);
if (sizeTmp > size) {
result = new Rectangle(info[0], y - info[1] + 1, lenX - info[0], info[1]);
size = sizeTmp;
}
}
}
return result;
}
}