Paint Fill multidimensional array - java

My goal is a "paint fill" function that one might see on many image editing programs. That is, given a screen (represented by a two-dimensional array of colors), a point, and a new color, fill in the surrounding area until the color changes from the original color.
I've implemented it for a 2D array, and here is the code :
public static void paint (int [][] screen,int OldColor,int NewColor,int y,int x)
{
if(y>screen.length-1||y<0||x>screen[0].length||x<0||screen[y][x]!=OldColor)
return;
screen[y][x]=NewColor;
paint(screen,OldColor,NewColor,y-1,x);
paint(screen, OldColor, NewColor, y+1, x);
paint(screen, OldColor, NewColor, y, x-1);
paint(screen, OldColor, NewColor, y, x+1);
}
But I want to implement it for multidimensional arrays like 3D that could be solved by adding:
paint(screen, OldColor, NewColor, y, x,z-1);
paint(screen, OldColor, NewColor, y, x,z+1);
But imagine the array is 100 D... How can I solve this problem?

Thanks to #Spektre's suggestion about the structure of the points, I managed to write a simple N-Dimensional floodfill.
Instead of images, I used char matrix to simplify the coding. Changing it to int as color value and some changes in other matrix's data type, will do the 100D for you :)
In this simple program, I try to fill all "A"'s with "B" and it fill all of connected char values similar to ants nest. You can trace the connections between A's using other layers to see the fill path.
In second image (Im1, add intentionally added a B and then added an A above it which is not accessible from fill point) and it worked fine as well.
package test;
import java.awt.Point;
import java.util.LinkedList;
import java.util.Queue;
/**
*
* #author Pasban
*/
public class NDFloodFill {
public int N1 = 8; // width
public int N2 = 6; // height
public int N = 3; // number of layers
public ImageData[] images = new ImageData[N];
public static void main(String[] args) {
NDFloodFill ndf = new NDFloodFill();
//print original data
//ndf.print();
ndf.fill(0, 0, 0, 'A', 'B');
ndf.print();
}
public NDFloodFill() {
String im0 = ""
+ "AA...A..\n"
+ ".....A..\n"
+ "....AA..\n"
+ "........\n"
+ "........\n"
+ "...AA.AA";
String im1 = ""
+ ".A..A...\n"
+ "....B...\n"
+ "..AAA...\n"
+ "........\n"
+ "...AA.A.\n"
+ "..AA..A.";
String im2 = ""
+ ".A......\n"
+ ".AA.....\n"
+ "..A.....\n"
+ "..A.....\n"
+ "..A.AAA.\n"
+ "..A.....";
images[0] = new ImageData(im0, 0);
images[1] = new ImageData(im1, 1);
images[2] = new ImageData(im2, 2);
}
private void print() {
for (int i = 0; i < N; i++) {
System.out.println(images[i].getImage());
}
}
private void fill(int x, int y, int index, char original, char fill) {
Queue<PixFill> broadCast = new LinkedList<>();
broadCast.add(new PixFill(new Point(x, y), index));
for (int i = 0; i < N; i++) {
images[i].reset();
}
while (!broadCast.isEmpty()) {
PixFill pf = broadCast.remove();
Queue<PixFill> newPoints = images[pf.index].fillArea(pf.xy, original, fill);
if (newPoints != null) {
broadCast.addAll(newPoints);
}
}
}
public class PixFill {
Point xy;
int index;
public PixFill(Point xy, int index) {
this.xy = xy;
this.index = index;
}
#Override
public String toString() {
return this.xy.x + " : " + this.xy.y + " / " + this.index;
}
}
public class ImageData {
char[][] pix = new char[N1][N2];
boolean[][] done = new boolean[N1][N2];
int index;
public ImageData(String image, int index) {
int k = 0;
this.index = index;
for (int y = 0; y < N2; y++) { // row
for (int x = 0; x < N1; x++) { // column
pix[x][y] = image.charAt(k++);
}
k++; // ignoring the \n char
}
}
public void reset() {
for (int y = 0; y < N2; y++) {
for (int x = 0; x < N1; x++) {
done[x][y] = false;
}
}
}
public String getImage() {
String ret = "";
for (int y = 0; y < N2; y++) { // row
String line = "";
for (int x = 0; x < N1; x++) { // column
line += pix[x][y];
}
ret += line + "\n";
}
return ret;
}
public Queue<PixFill> fillArea(Point p, char original, char fill) {
if (!(p.x >= 0 && p.y >= 0 && p.x < N1 && p.y < N2) || !(pix[p.x][p.y] == original)) {
return null;
}
// create queue for efficiency
Queue<Point> list = new LinkedList<>();
list.add(p);
// create broadcasting to spread filled points to othwer layers
Queue<PixFill> broadCast = new LinkedList<>();
while (!list.isEmpty()) {
p = list.remove();
if ((p.x >= 0 && p.y >= 0 && p.x < N1 && p.y < N2) && (pix[p.x][p.y] == original) && (!done[p.x][p.y])) {
//fill
pix[p.x][p.y] = fill;
done[p.x][p.y] = true;
//look for neighbors
list.add(new Point(p.x - 1, p.y));
list.add(new Point(p.x + 1, p.y));
list.add(new Point(p.x, p.y - 1));
list.add(new Point(p.x, p.y + 1));
// there will not be a duplicate pixFill as we always add the filled points that are not filled yet,
// so duplicate fill will never happen, so do pixFill :)
// add one for upper layer
if (index < N - 1) {
broadCast.add(new PixFill(p, index + 1));
}
// add one for lower layer
if (index > 0) {
broadCast.add(new PixFill(p, index - 1));
}
//layers out of range <0, N> can be filtered
}
}
return broadCast;
}
}
}

Avoid recursive functions! Use a queue instead to flood fill the image ith.
On which image you want to start filling?
check the image color on ith image and add that point to your list.
Later on check if you can go up or down from the stored point to (i+1)th or (i-1)th image and repeat this process from there.
This is a raw idea, but all you may need is this.
Plus, you need to have an array for each level to check if you have filled that pixel for that image or not. So you will escape from infinite loop :)
Check this for flood fill using queue:
Flood Fill Optimization: Attempting to Using a Queue

Salivan is right with his suggestions but he did not grasp the real problem you are asking about. For arbitrary dimensionality you need to change the point structure from notation like pnt.x,pnt.y,pnt.z to pnt[0],pnt[1],pnt[2] then there are few approaches how to handle this:
fixed limit size padded with zeros
so handle all like 10D (if 10D is maximal dimensionality used) and fill unused axises with zeros. This is slow ugly,painfully demanding on memory and limiting max dimensionality.
use nested for loop (for initializations and more)
look here: rasterize and fill a hypersphere
many multidimensional operations require nested loops this one has arbitrary depth. You can look at it as an increment function of multi digit number where each digit represents axis in your space.
use normal for loop for neighbors generation in N-D
// point variables
int p[N],q[N];
// here you have actual point p and want to find its neighbors
for (int i=0;i<N;i++)
{
for (int j=0;i<N;i++) q[j]=p[j]; // copy point
q[i]--;
// add q to flood fill
q[i]+=2;
// add q to flood fill
}

Related

The code isn't printing anything, and I can't find the problem

The goal of this is to print out the size of the blob when given a set of coordinates within a 10 x 10 matrix. It's written in java and the point of the exercise was to use recursion to find each of the 1's in the blob. (Any 1's that are connected to the original coordinate by going up, down, left, or right.
Example:
The Blob
0010010010
0100100101
1001001010
0011110101
0111101010
1001010100
0010101101
0101010010
1010100100
0101001000
The Coordinates
1 1
2 3
5 7
The Output
1
10
3
import java.util.*;
public class BlobsRunner
{
public static void main(String[] args)
{
//test the Blob class to make sure
//it works as intended
Blobs bloop = new Blobs(10, 10);
//call both constructors
Scanner reader = new Scanner(System.in);
System.out.println("Row: ");
int r = reader.nextInt();
System.out.println("Colum: ");
int c = reader.nextInt();
bloop.recur(r, c);
bloop.getBlobCount();
//print the newly instantiated Blob
bloop.toString();
//call methods - print out the size of the blob
//bloop.getBlobCount();
}
}
//next file
import java.util.*;
public class Blobs
{
private int[][] mat; //grid of 1s and 0s
private int count;
private int[][] visited;
public Blobs( int rows, int cols )
{
//set count to 0
count = 0;
//point mat at new mat size rows X cols
mat = new int[rows][cols];
//loop through mat
for(int a = 0; a < rows; a ++)
{
for(int b = 0; b< cols; b++)
{
mat[a][b] = (int)Math.random();
}
}
//fill in mat with 1s and 0s
//use Math.random()
visited = new int[rows][cols];
}
public Blobs( int rows, int cols, String s )//what does s mean?
{
//set count to 0
count = 0;
//point mat at new mat size rows X cols
mat = new int[rows][cols];
for(int a = 0; a < rows; a ++)
{
for(int b = 0; b< cols; b++)
{
mat[a][b] = (int)Math.random();
}
}
visited = new int[rows][cols];
//loop through mat
//load in the 1s and 0s from s
}
public void recur(int r, int c)
{
//add a base case
if(mat[r][c]==0)
{
//return;
}
if(visited[r][c] == 5)//figure out, make sure it doesn't count if it does
{
//return;
}
visited[r][c] = 5;
//mark current pos as visited
count++;
//increase count by 1
//add in 4 recursive calls
//UP
if(mat[r+1][c] == 1 && visited[r+1][c] != 5)
{
recur(r+1, c);
}
//DOWN
if(mat[r-1][c] == 1 && visited[r-1][c] != 5)
{
recur(r-1,c);
}
//LEFT
if(mat[r][c-1] == 1 && visited[r][c-1] != 5)
{
recur(r,c-1);
}
//RIGHT
if(mat[r][c+1] == 1 && visited[r][c+1] != 5)
{
recur(r,c+1);
}
}
public int getBlobCount()
{
//return count
return 0;
}
public String toString()
{
//you will need nested loops
//you will need a local string variable
for(int a = 0; a < mat.length; a++)
{
for(int b = 0; b < mat[a].length; b++)
{
System.out.print(mat[a][b]);
}
System.out.println();
}
return "";
}
}
Math.random() function returns double values between 0 to 1 so when you try to convert it to (int) it becomes 0. So all of your matrix is 0. And since you are doing nothing at recur() function its print 10x10 0 matrix.
Try adding import java.util.Random; then Random r = new Random(); and when you assign random numbers mat[a][b] = r.nextInt(2);
As #Fatih Aslan noted, Math.random() produces a double between 0.0 and 1.0, not including 1.0. When parsed to an int, Java will always produce the value 0.
If you do not want to use the java.util.Random library you can multiply the value you are obtaining from Math.random() by the difference between the highest and lowest random numbers you would like to generate, plus 1. If you want your lowest possible random number to be greater than 0, add that number to the output of Math.random().
For example, (int)(2 + (Math.random() * 9)) will produce random integers from 2 to 10 including 2 and 10.

How would I change an object's value when calling a static method from a different class?

In this practice problem, a square matrix filled with 0s and 1s is instantiated. You can flip over values (ex: 0 becomes 1 and 1 becomes 0) in a rectangle of any size, as long as the topmost corner of the rectangle is [0, 0] in the matrix. The end goal is to find how many times you must flip values over to get all the values of the matrix as 0.
If you want a longer explanation, go to http://usaco.org/index.php?page=viewproblem2&cpid=689, but that's the basic outline.
This is my code:
import java.io.*;
import java.util.*;
public class CowTip {
static int[][] mat;
public static void main( String[] args) throws IOException, InterruptedException{
Scanner scan = new Scanner(new File("cowtip.in"));
int n = scan.nextInt();
scan.nextLine();
mat = new int[n][n];
for (int x = 0; x < n; x++) {
String str = scan.nextLine();
for (int y = 0; y < n; y++) {
mat[x][y] = Integer.parseInt(str.substring(y,y+1));
}
}
Checker c = new Checker(n-1, n-1);
int count = 0;
while (true) {
c.check();
for (int x = 0; x <= c.row; x++) {
for (int y = 0; y <= c.col; y++) {
if (mat[x][y] == 0) {
mat[x][y] = 1;
}
else if (mat[x][y] == 1) {
mat[x][y] = 0;
}
}
}
count++;
c.check();
if (c.row == -1 && c.col == -1) {
break;
}
}
System.out.println(count);
}
static class Checker {
int row;
int col;
public Checker(int r, int c) {
row = r;
col = c;
}
public Checker check() {
Checker check = new Checker(-1, -1);
for (int x = mat.length-1; x >= 0; x--) {
for (int y = mat[x].length-1; y >= 0; y--) {
if (mat[x][y] == 1) {
check = new Checker(x, y);
break;
}
}
if (check.row != -1 && check.col != -1) {
break;
}
}
return check;
}
}
}
and this is the input file (named cowtip.in) :
3
001
111
111
I've excluded my current debugging code, but the problem is that the row and col values inside my check() method are the correct values, but whenever I call the check() method in my main, the values reverts back to the default and doesn't give me the correct answer, which in turn makes the loop infinite.
Any ideas on how to fix this?
EDIT: I've figured it out, but thanks guys! It was actually extremely simple (c = c.ckeck() instead of c.check()) and honestly, I was pretty frustrated considering I spent around two hours trying to debug this...
Replace c.check() with c = c.check();

Is there a data structure to grab values by x or y?

I have an ArrayList with a lot of points (so x,y).
I sorted the points on XY.
Is there a datatype/algorithm to get all y points a certain x fast? (or the other way around, all x on a certain y).
At the moment I have something like this, which works ok but I have the feeling it's to complex for what I need.
int lastXPos;
int lastIndex;
int[] workArray = new int[4096];
int workArrayIndex;
// returns all the y values where x matches
public int[] grab(int xPos) {
workArrayIndex = 0;
int startIndex = 0;
// this can increase speed a lot
if (lastXPos+1 == xPos) {
startIndex = lastIndex+1;
}
PVector v;
for (int i = startIndex; i < vecs.size(); i++) {
v = vecs.get(i);
if (v.x > xPos) {
lastIndex = i-1;
break;
}
if (v.x == xPos) {
workArray[workArrayIndex++] = (int) v.y;
}
}
lastXPos = xPos;
int[] result = new int[workArrayIndex];
for (int i = 0; i < workArrayIndex; i++) {
result[i] = workArray[i];
}
return result;
}
Edit:
One more thing, it has to process a new list 60 times a second, so creating the data object also has to be fast.
Then as a bonus question, we have rows and columns, is there a way to describe both of those? (direction for example).

Sudoku generator algorithm optimization welcome

I created a recursive DFS algorithm to generate/solve sudoku boards in Java, but it's taking forever to terminate, and an explanation/optimization would be welcome. I can't imagine that generating a sudoku board would be so time-consuming, especially with all the apps around (although they might have a database.)
Basically, I traverse all cells, seeing whether any of [1-9] would satisfy the sudoku constraints, and backtrack on dead-end branches. To conserve memory and avoid copying the 2D array that serves as the board with each invocation of the recursive method (and there are potentially 81*9! leaves in that tree, if I'm not mistaken...), I created a 2D matrix of integer stacks, in which an element is pushed every time a branch is explored, and popped if it's a dead-end.
Below is the implementation. Any advice on speedup would be welcome. I'm doing this as a personal excercise, and I'm wondering if something asymptotically better exists.
Hope it's not a terrible read below.. Thank you!
1) The algorithm implementation: (note that values are in a "jumbled" array of [1-9] to create unique boards.)
/**
* Provides one solution to a board with an initial configuration, or <code>null</code> if there is none.
* The search is randomized, s.t. the algorithm can serve to populate an empty board.
*
* #param initial The initial board given to solve.
* #return The fully solved board, or null if no solution found.
*/
public static int[][] solveBoard (int[][] initial){
return solveBoard(new StackedBoard(initial), 0, 0);
}
private static int[][] solveBoard (StackedBoard board, int xPos, int yPos){
// base case - success
int remaining = 81;
for (int x = 0; x < 9; x++){
for (int y = 0; y < 9; y++){
if (board.peekAt(x, y) != Board.EMPTY){
remaining--;
}
}
}
if (remaining == 0){
return board.flatView();// the only creation of an array.
}
// recursive case
for (int x = 0; x < 9; x++){
for (int y = 0; y < 9; y++){
if (board.peekAt(x, y) == Board.EMPTY){
for (int val : getJumbledRandomVals()){
if (isMoveLegal(board, x, y, val)){
board.pushAt(x, y, val);
int[][] leafBoard = solveBoard(board, x, y);
if (leafBoard != null) {
return leafBoard;
}
}
}
}
}
}
// base case - dead branch
board.popAt(xPos, yPos);
return null;
}
2) The StackedBoard implementation:
/**
* Represents square boards with stacked int elements.
*/
class StackedBoard {
ArrayList<ArrayList<Stack<Integer>>> xaxis = new ArrayList<ArrayList<Stack<Integer>>>();
/**
*
* #param init A square array - both dimensions of equal length, or <code>null</code> if no initialization.
*/
public StackedBoard (int[][] init) {
for (int i = 0; i < 9; i++){
ArrayList<Stack<Integer>> yaxis = new ArrayList<Stack<Integer>>();
xaxis.add(yaxis);
for (int j = 0; j < 9; j++){
Stack<Integer> stack = new Stack<Integer>();
yaxis.add(stack);
}
}
if (init != null){
for (int x = 0; x < init.length; x++){
for (int y = 0; y < init.length; y++){
getStackAt(x, y).push(init[x][y]);
}
}
}
}
public Stack<Integer> getStackAt (int x, int y){
return xaxis.get(x).get(y);
}
public int peekAt (int x, int y){
return getStackAt(x, y).peek();
}
public void pushAt (int x, int y, int value){
getStackAt(x, y).push(value);
}
public Integer popAt (int x, int y){
try {
return getStackAt(x, y).pop();
} catch (EmptyStackException e){
// shhhhh!
return Board.EMPTY;
}
}
/**
* Flat view of the stacked-board; peek of the top elements.
*/
public int[][] flatView (){
int[][] view = new int[xaxis.size()][xaxis.size()];
for (int x = 0; x < xaxis.size(); x++){
for (int y = 0; y < xaxis.size(); y++){
view[x][y] = getStackAt(x, y).peek();
}
}
return view;
}
}
3) The constraints function implementation:
/**
* Is the move legal on the suggested board?
*
* #param board The board.
* #param x The X coordinate, starts with 0.
* #param y The Y coordinate, starts with 0.
* #param value The value.
* #return <code>true</code> iff the move is legal.
*/
private static boolean isMoveLegal (StackedBoard board, int x, int y, int value){
// by value
if (1 > value || value > 9){
return false;
}
// by column
for (int i = 0; i < 9; i++){
if (board.peekAt(i, y) == value){
return false;
}
}
// by row
for (int i = 0; i < 9; i++){
if (board.peekAt(x, i) == value){
return false;
}
}
// by lil square
int lowerX = x < 3 ? 0 : (x < 6 ? 3 : 6);
int upperX = lowerX + 2;
int lowerY = y < 3 ? 0 : (y < 6 ? 3 : 6);
int upperY = lowerY + 2;
for (int i = lowerX; i <= upperX; i++){
for (int j = lowerY; j <= upperY; j++){
if (board.peekAt(i, j) == value){
return false;
}
}
}
return true;
}
If you are willing to make a complete left turn, there are much better algorithms for generating / solving Sudokus. Don Knuth's dancing links algorithm is known to be extremely good at rapidly enumerating all Sudoku solutions (once they're phrased as instances of the exact cover problem) and is commonly used as the main algorithm in Sudoku solvers, and it's worth looking into. It requires a lot of pointer/reference gymnastics, but it relatively short to code up.
If you want to stick with your existing approach, one useful optimization would be to always choose the most constrained cell as the next value to fill in. This will likely cause a cascade of "forced moves" that will help you reduce the size of your search space, though it's only a heuristic.
Hope this helps!

Accessing ArrayList<ArrayList<SomeObject>> elements

I have an ArrayList of ArrayLists where i need to find a simple way of accessing the elements, to make it easier to understand i have drawn my goal of what i want to achieve:
As seen on the image above the main ArrayList consists of m ArrayList, where i wish to get an element by using a get method which goes from 0 to N elements, where N is the total elements of ArrayList1 and ArrayList2. More ArrayList's may occur.
I can of course iterate through the elements by using two for-loops, which is not what im searching for in this case.
You would need to basically have the the ArrayList members on your new wrapper class and implement them in a different manner. I whipped up an example that demonstrates the correct index being calculated in get().
import java.util.ArrayList;
public class ListHolder<T> {
public ArrayList<ArrayList<T>> list = new ArrayList<ArrayList<T>>();
public int size() {
int size = 0;
for (int i = 0; i < list.size(); i++) {
size += list.get(i).size();
}
return size;
}
public T get(int i) {
if (i >= size())
return null;
int listIndex = 0;
int valueIndex = i;
while (valueIndex >= list.get(listIndex).size()) {
valueIndex -= list.get(listIndex++).size();
}
return list.get(listIndex).get(valueIndex);
}
}
What I used to verify my methods:
public static void main(String[] args)
{
ListHolder<Object> listHolder = new ListHolder<Object>();
listHolder.list.add(new ArrayList<Object>());
listHolder.list.get(0).add("hello");
listHolder.list.get(0).add("world");
listHolder.list.add(new ArrayList<Object>());
listHolder.list.get(1).add("a");
listHolder.list.get(1).add("b");
listHolder.list.get(1).add("c");
System.out.println("Size: " + listHolder.size());
System.out.println("listHolder[0]: " + listHolder.get(0)); // "hello"
System.out.println("listHolder[1]: " + listHolder.get(1)); // "world"
System.out.println("listHolder[2]: " + listHolder.get(2)); // "a"
System.out.println("listHolder[3]: " + listHolder.get(3)); // "b"
System.out.println("listHolder[4]: " + listHolder.get(4)); // "c"
System.out.println("listHolder[5]: " + listHolder.get(5)); // "null"
}
You don't provide many details about what these lists are, and if they're mutable or not. But you could probably use an additional list containing all the elements of all the sublists:
private class Generation
private List<List<Element>> populations = new ArrayList<>();
private List<Element> allElements = new ArrayList<>();
public Element getElementAt(int elementIndex) {
return allElements.get(elementIndex);
}
public void addPopulation(List<Element> population) {
populations.add(new ArrayList<>(population));
allElements.addAll(population);
}
public List<Element> getPopulationAt(int populationIndex) {
return Collections.unmodifiableList(populations.get(populationIndex));
}
}
class Plot {
class Point {
int x;
int y;
}
List<List<Point>> area = new ArrayList<List<Point>>();
Point getPoint (int x, int y) throws IndexOutOfBoundsException {
if (x < 0 && x >= area.size())
throw new IndexOutOfBoundsException();
int l = area.get(x).size();
int i = (int)y/l;
int j = y % l;
return area.get(x+i).get(j);
}
void setPoint (int x, int y, Point p) throws IndexOutOfBoundsException {
if (x < 0 && x >= area.size())
throw new IndexOutOfBoundsException();
int l = area.get(x).size();
int i = (int)y/l;
int j = y % l;
area.get(x+i).set(j, p);
}
}

Categories