Error ArrayOutOfBoundException in 2D array - java

I get the error ArrayOutOfBoundException: 15 at line 110:
System.out.println(coordinates[k][l]);
When trying to run this code :
import TUIO.*;
TuioProcessing tuioClient;
int cols = 15, rows = 10;
boolean[][] states = new boolean[cols][rows];
String[][] coordinates = new String[cols][rows];
int videoScale = 50;
// these are some helper variables which are used
// to create scalable graphical feedback
int x, y, i, j, k, l;
float cursor_size = 15;
float object_size = 60;
float table_size = 760;
float scale_factor = 1;
PFont font;
boolean verbose = false; // print console debug messages
boolean callback = true; // updates only after callbacks
void setup(){
size(500,500);
noCursor();
noStroke();
fill(0);
// periodic updates
if (!callback) {
frameRate(60); //<>//
loop();
} else noLoop(); // or callback updates
font = createFont("Arial", 18);
scale_factor = height/table_size;
// finally we create an instance of the TuioProcessing client
// since we add "this" class as an argument the TuioProcessing class expects
// an implementation of the TUIO callback methods in this class (see below)
tuioClient = new TuioProcessing(this);
}
void draw(){
// Begin loop for columns
for ( k = 0; k < cols; k++) {
// Begin loop for rows
for ( l = 0; l < rows; l++) {
// Scaling up to draw a rectangle at (x,y)
int x = k*videoScale;
int y = l*videoScale;
fill(255);
stroke(0);
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
coordinates[i][j] = String.valueOf((char)(i+65)) + String.valueOf(j).toUpperCase();
}
}
/*
//check if coordinates are within a box (these are mouse x,y but could be fiducial x,y)
//simply look for bounds (left,right,top,bottom)
if( (mouseX >= x && mouseX <= x + videoScale) && //check horzontal
(mouseY >= y && mouseY <= y + videoScale)){
//coordinates are within a box, do something about it
System.out.println(coordinates[k][l]);
//you can keep track of the boxes states (contains x,y or not)
states[k][l] = true;
if(mousePressed) println(k+"/"+l);
}else{
states[k][l] = false;
}
*/
rect(x,y,videoScale,videoScale);
}
}
textFont(font,18*scale_factor);
float obj_size = object_size*scale_factor;
float cur_size = cursor_size*scale_factor;
ArrayList<TuioObject> tuioObjectList = tuioClient.getTuioObjectList();
for (int i=0;i<tuioObjectList.size();i++) {
TuioObject tobj= tuioObjectList.get(i);
stroke(0);
fill(0,0,0);
pushMatrix();
translate(tobj.getScreenX(width),tobj.getScreenY(height));
rotate(tobj.getAngle());
rect(-obj_size/2,-obj_size/2,obj_size,obj_size);
popMatrix();
fill(255);
text(""+tobj.getSymbolID(), tobj.getScreenX(width), tobj.getScreenY(height));
System.out.println(tobj.getSymbolID ()+ " " + tobj.getX());
if( ( tobj.getX()>= x && tobj.getX() <= x + videoScale) && //check horzontal
(tobj.getY() >= y && tobj.getY() <= y + videoScale)){
//coordinates are within a box, do something about it
System.out.println(coordinates[k][l]);
}
rect(x,y,videoScale,videoScale);
}
}
// --------------------------------------------------------------
// these callback methods are called whenever a TUIO event occurs
// there are three callbacks for add/set/del events for each object/cursor/blob type
// the final refresh callback marks the end of each TUIO frame
// called when an object is added to the scene
/* void addTuioObject(TuioObject tobj) {
if (verbose) println("add obj "+tobj.getSymbolID()+" ("+tobj.getSessionID()+") "+tobj.getX()+" "+tobj.getY()+" "+tobj.getAngle());
}
// called when an object is moved
void updateTuioObject (TuioObject tobj) {
if (verbose) println("set obj "+tobj.getSymbolID()+" ("+tobj.getSessionID()+") "+tobj.getX()+" "+tobj.getY());
}
// called when an object is removed from the scene
void removeTuioObject(TuioObject tobj) {
if (verbose) println("del obj "+tobj.getSymbolID()+" ("+tobj.getSessionID()+")");
}
*/
// --------------------------------------------------------------
// called at the end of each TUIO frame
void refresh(TuioTime frameTime) {
if (verbose) println("frame #"+frameTime.getFrameID()+" ("+frameTime.getTotalMilliseconds()+")");
if (callback) redraw();
}
Does it mean that it assigns a value at a greater place than the number of elements this array can contain ? How can I modify it ?
I can't find in the code where I specified the size of the array. I created it based on cols and rows (the 15 comes from there since when I modify it, say to 4, the error becomes ArrayOutOfBoundException: 4, but even with 1 there is an error so I don't get it) so I changed these values but I still get the error.
Thanks for your help

You are trying to use the variables k and l outside the following loop -
for ( k = 0; k < cols; k++) {
// Begin loop for rows
for ( l = 0; l < rows; l++) {
Seems like k and l are defined in instance variable scope. After the above loop has ended the values of k is cols and the value of l is rows, and i am assuming that the length of coordinates is (rows, cols) .
Hence, you are getting the issue, when you try to print that within the if condition - if( ( tobj.getX()>= x && tobj.getX() <= x + videoScale) && (tobj.getY() >= y && tobj.getY() <= y + videoScale))
Maybe you want to print it before the loop for k and l`l variables are over. That is before the following lines -
rect(x,y,videoScale,videoScale);
} // <-- Here 'l' loop ends.
} // <---- Here 'k' loop ends.
textFont(font,18*scale_factor);
float obj_size = object_size*scale_factor;
float cur_size = cursor_size*scale_factor;
Or maybe you do not want to end the k and l loop (the ones i mentioned in that start of this post) there? Did you miss commenting that part out?

Value for variable k is 15 at the end of this for loop
for ( k = 0; k < cols; k++) {
// Begin loop for rows
for ( l = 0; l < rows; l++) {
// Scaling up to draw a rectangle at (x,y)
int x = k*videoScale;
int y = l*videoScale;
fill(255);
stroke(0);
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
coordinates[i][j] = String.valueOf((char)(i+65)) + String.valueOf(j).toUpperCase();
}
}
rect(x,y,videoScale,videoScale);
}
}
then you trying to access coordinates for index number coordinates[15][11] at this line :
System.out.println(coordinates[k][l]);
that's why you are getting Exception.
Hope this helps.

Related

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();

JavaFX transition animation fail to place the children of gridpane at the right place

I am writing a game similar to Bejeweled, and after running the application for some time the blocks fail to be exactly at the right place visually but logically they are at the right place. The situation is as the picture below.
enter image description here
I don't know the cause of the bug.
here are the codes for the method erase and descend. Logically these methods do well.
public void erase()
{
for (int i = 0; i < BlockManager.length; i++)
{
Block block = BlockManager.blocks[BlockManager.erased[i][0]][BlockManager.erased[i][1]];
BlockManager.blocks[BlockManager.erased[i][0]][BlockManager.erased[i][1]] = null;
FadeTransition transition = new FadeTransition(Duration.seconds(1), block);
transition.setFromValue(1);
transition.setToValue(0);
transition.setOnFinished(e ->
{
blockGridPan.getChildren().remove(block);
descend();
BlockManager.resetArrays();
});
transition.play();
}
}
int[][] descend = new int[HEIGHT * WIDE][2];
int descendLength = 0;
public void descend()
{
int[] columnX = new int[10];
int[] theUpperOne = new int[10];
Arrays.fill(theUpperOne, 10);
Arrays.fill(columnX, 0);
for (int j = 0; j < BlockManager.length; j++)
{
columnX[BlockManager.erased[j][0]]++;
if (BlockManager.erased[j][1] < theUpperOne[BlockManager.erased[j][0]])
{
theUpperOne[BlockManager.erased[j][0]] = BlockManager.erased[j][1];
}
}
TranslateTransition transition = null;
for (int i = 0; i < 10; i++)
{
if (columnX[i] == 0)
continue;
for (int j = WIDE - 1; j >= 0; j--)
{
if (BlockManager.blocks[i][j] == null)
continue;
int dY = 0;
for (int k = j + 1; k < WIDE; k++)
{
if (BlockManager.blocks[i][k] == null)
dY++;
}
if (dY != 0)
{
int deltaY = dY * 60;
transition = new TranslateTransition(Duration.seconds(1), BlockManager.blocks[i][j]);
transition.setByY(deltaY);
BlockManager.blocks[i][j + dY] = BlockManager.blocks[i][j];
BlockManager.blocks[i][j + dY].setPosition(i, j + dY);
BlockManager.blocks[i][j] = null;
BlockManager.blocks[i][j + dY].setDescended(true);
transition.play();
}
}
for (int j = columnX[i] - 1; j >= 0; j--)
{
int deltYJ = j * 60;
createOneBlock(i, j);
BlockManager.setBlockColorWithoutCheck(BlockManager.blocks[i][j]);
blockGridPane.add(BlockManager.blocks[i][j], i, 0);
System.out.println("show it");
BlockManager.blocks[i][j].setDescended(true);
transition = new TranslateTransition(Duration.seconds(1), BlockManager.blocks[i][j]);
transition.setByY(deltYJ);
transition.play();
}
}
if (transition != null)
transition.setOnFinished(e ->
{
BlockManager.resetArrays();
if (BlockManager.check())
erase();
});
}
}
You didn't paste your codes for your layout, so I'm going to make a guess.
There is a potential problem when you do translate and your layout depends on boundsInParent. This is an abstract from the JavaDoc:
The rectangular bounds of this Node which include its transforms.
boundsInParent is calculated by taking the local bounds (defined by
boundsInLocal) and applying the transform created by setting the
following additional variables
transforms ObservableList
scaleX, scaleY
rotate
layoutX, layoutY
translateX, translateY
When your TranslateTransition animates, it is changing the block's translateY value, and if your layout is called at this time, this translate value is going to affect how the block's is going to be positioned. If your layout is managed by some kind of layout manager (i.e. pane), you need to check how they position their children.
I use gridpane to position my blocks, there are 10 rows and 10 columns initially and the gridpane automatically adds one row after the blocks have descended, (because the block is not exactly translated to the position that it is supposed to and I still do not know the reason of it). I change the html document to avoid the growth of the columns. And it works.

implement game of life with only 2 array

I want to memory-efficient this (the game of life code of shiffman in the nature of code book). how can change the below code to have only two arrays and constantly swap them, writing the next set of states into whichever one isn’t the current array?
class GOL {
int w = 8;
int columns, rows;
int[][] board;
GOL() {
// Initialize rows, columns and set-up arrays
columns = width / w;
rows = height / w;
board = new int[columns][rows];
//next = new int[columns][rows];
// Call function to fill array with random values 0 or 1
init();
}
void init() {
for (int i = 1; i < columns - 1; i++) {
for (int j = 1; j < rows - 1; j++) {
board[i][j] = (int) random(2);
}
}
}
// The process of creating the new generation
void generate() {
int[][] next = new int[columns][rows];
// Loop through every spot in our 2D array and check spots neighbors
for (int x = 1; x < columns - 1; x++) {
for (int y = 1; y < rows - 1; y++) {
// Add up all the states in a 3x3 surrounding grid
int neighbors = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
neighbors += board[x + i][y + j];
}
}
// A little trick to subtract the current cell's state since
// we added it in the above loop
neighbors -= board[x][y];
// Rules of Life
if ((board[x][y] == 1) && (neighbors < 2)) next[x][y] = 0;
else if ((board[x][y] == 1) && (neighbors > 3)) next[x][y] = 0;
else if ((board[x][y] == 0) && (neighbors == 3)) next[x][y] = 1;
else next[x][y] = board[x][y];
}
}
// Next is now our board
board = next;
}
// This is the easy part, just draw the cells, fill 255 for '1', fill 0 for '0'
void display() {
for (int i = 0; i < columns; i++) {
for (int j = 0; j < rows; j++) {
if ((board[i][j] == 1)) fill(0);
else fill(255);
stroke(0);
rect(i * w, j * w, w, w);
}
}
}
}
You might not like this, but the honest answer is: don't bother.
how can change the below code to have only two arrays and constantly swap them, writing the next set of states into whichever one isn’t the current array
This is already exactly what the code does.
The Game of Life requires two arrays. If you're coming up against real performance issues, then look for other areas of improvement. Focusing on the array is a red herring.
There's an old saying: premature optimization is the root of all evil. In other words, you shouldn't waste time trying to fix code before it's broken.
One obvious thing you might improve is: why are you using an int[] array instead of a boolean[] array? You only need to store two states: alive or dead, so using int values seems unnecessary. You'll save a little bit of memory if you switch to a boolean[] array, but again, you probably won't even notice the improvement.

Paint Fill multidimensional array

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
}

Change order of for loops?

I have a situation where i need to loop though xyz coordinates in different orders depending on a users input. So i an area in 3D space then a set of for loops like so.
for(int x = 0; x < build.getWidth(); x++){
for(int y = 0; y < build.getHeight(); y++){
for(int z = 0; z < build.getLength(); z++){
//do stuff
}
}
}
but depending on the users input, the order may be like this.
for(int z = 0; z < build.getLenght(); z++){
for(int y = 0; y < build.getHeight(); y++){
for(int x = 0; x < build.getWidth(); x++){
//do stuff
}
}
}
or even negative.
for(int x = build.getWidth(); x > 0; x--){
for(int y = 0; y < build.getHeight(); y++){
for(int z = 0; z < build.getLength(); z++){
//do stuff
}
}
}
Is there any way to do this without hard coding every case?
Here's an n-dimensional stepper that can step in any number of dimensions in any order from any start locations to any limits. See the test code for an example.
public class Test {
public void test() {
int[] limits = {3, -5, 7};
int[] order = {0, 2, 1};
int[] starts = {0, 0, 0};
int[] steps = {1, -1, 2};
NDimensionalStepper nds = new NDimensionalStepper(limits, order, starts, steps);
do {
System.out.println(nds);
} while (nds.step());
}
public static void main(String args[]) {
new Test().test();
}
public static class NDimensionalStepper {
// The current positions in each dimension.
// Note that i[order[0]] is the fastest mover.
final int[] i;
// Starts.
final int[] starts;
// Steps.
final int[] steps;
// Limits.
final int[] limits;
// Order.
final int[] order;
// The (unordered) dimension we last stepped.
int d = 0;
// Full constructor.
public NDimensionalStepper(int[] limits, int[] order, int[] starts, int[] steps) {
// Should parameter check to ensure all are the same length.
// Should also check that each dimension will terminate.
this.i = Arrays.copyOf(starts, starts.length);
this.starts = Arrays.copyOf(starts, starts.length);
this.steps = Arrays.copyOf(steps, steps.length);
this.limits = Arrays.copyOf(limits, limits.length);
this.order = Arrays.copyOf(order, order.length);
}
// Default steps to 1.
public NDimensionalStepper(int[] limits, int[] order, int[] starts) {
this(limits, order, starts, defaultSteps(limits, starts));
}
// Default steps - 1 Towards limits.
private static int[] defaultSteps(int[] limits, int[] starts) {
int[] steps = new int[limits.length];
for (int i = 0; i < limits.length; i++) {
// Step towrds limits.
steps[i] = (int) Math.signum(limits[i] - starts[i]);
}
return steps;
}
// Default starts to 0.
public NDimensionalStepper(int[] limits, int[] order) {
this(limits, order, defaultStarts(limits.length));
}
// Default starts - 0, 0, ...
private static int[] defaultStarts(int d) {
int[] starts = new int[d];
Arrays.fill(starts, 0);
return starts;
}
// Default order to normal.
public NDimensionalStepper(int[] limits) {
this(limits, defaultOrder(limits.length));
}
// Default order - ..., 1, 0
private static int[] defaultOrder(int d) {
int[] order = new int[d];
for (int i = 0; i < d; i++) {
order[i] = d - i - 1;
}
return order;
}
// Get the current position in dimension d.
public int get(int d) {
return i[d];
}
// Take just one step. Return false if cant.
public boolean step() {
boolean stepped = false;
boolean finished = false;
while (!stepped && !finished) {
// Which dimension should be stepped (depends on order).
int o = order[d];
// Can we step in the current dimension?
while (finished(o) && d < order.length - 1) {
// Reached a limit! - Move up one dimension.
o = order[++d];
}
if (d < order.length && !finished(o)) {
// Step it.
i[o] += steps[o];
stepped = true;
// Zero all lower dimensions.
while (d > 0) {
d -= 1;
i[order[d]] = starts[order[d]];
}
} else {
// Got to the last without finding one below limit. Finished!
finished = true;
}
}
return !finished;
}
// Equal or passed the limits.
private boolean finished(int o) {
int sign = (int) Math.signum(steps[o]);
return sign * (i[o] + steps[o]) >= sign * limits[o];
}
#Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("{");
for (int d = 0; d < order.length; d++) {
s.append(get(d));
if (d < order.length - 1) {
s.append(",");
}
}
s.append("}");
return s.toString();
}
}
}
My tests of the equivalents of your three scenarios look like:
private void testBuild1(Build build) {
System.out.println("Build: x,y,z");
for (int x = 0; x < build.getWidth(); x++) {
for (int y = 0; y < build.getHeight(); y++) {
for (int z = 0; z < build.getLength(); z++) {
System.out.println("{" + x + "," + y + "," + z + "}");
}
}
}
int[] limits = {build.getWidth(), build.getHeight(), build.getLength()};
testNDS(new NDimensionalStepper(limits));
}
private void testBuild2(Build build) {
System.out.println("Build: z,y,x");
for (int z = 0; z < build.getLength(); z++) {
for (int y = 0; y < build.getHeight(); y++) {
for (int x = 0; x < build.getWidth(); x++) {
System.out.println("{" + x + "," + y + "," + z + "}");
}
}
}
int[] limits = {build.getWidth(), build.getHeight(), build.getLength()};
int[] order = {0,1,2};
testNDS(new NDimensionalStepper(limits, order));
}
private void testBuild3(Build build) {
System.out.println("Build: x--,y,z");
for (int x = build.getWidth(); x > 0; x--) {
for (int y = 0; y < build.getHeight(); y++) {
for (int z = 0; z < build.getLength(); z++) {
System.out.println("{" + x + "," + y + "," + z + "}");
}
}
}
int[] limits = {0, build.getHeight(), build.getLength()};
int[] order = {2,1,0};
int[] starts = {build.getWidth(), 0, 0};
int[] steps = {-1, 1, 1};
testNDS(new NDimensionalStepper(limits, order, starts, steps));
}
private void testNDS(NDimensionalStepper nds) {
System.out.println("--nds--");
do {
System.out.println(nds);
} while (nds.step());
}
You said depending on user input the order of loop changes. The logic for handling user input will have to be written.
You can code like this:
//Code to populate XInit, XEnd, YInit, YEnd, ZInit, ZEnd based on user input
for(int x = XInit; x < XEnd; x=XInit<XEnd?x+1:x-1){
for(int y = YInit; y < YEnd; y=YInit<YEnd?y+1:y-1){
for(int z = ZInit; z < ZEnd; z=ZInit<ZEnd?z+1:z-1){
//do stuff
}
}
}
Note: You may even want to abstract the calculation of XInit, XEnd etc. parameters in a separate method.
Your "stuff" is likely accessing the values of x, y, and z, so the way you're hard-coding is probably the easiest to follow. Your method names could clearly indicate the ordering. For the three examples you gave, it would look similar to:
public void somethingXYZ(Build build, Stuff stuff) {...}
public void somethingZYX(Build build, Stuff stuff) {...}
public void somethingXnYZ(Build build, Stuff stuff) {...}
When you're coding and want to select one of those methods, your IDE will even help you by listing the available options for that class. I think the way you're organizing it would already work well.

Categories