It’s a little more complicated that it seems, it’s probably better to walk through what to do than try to explain.
One thing I hope is clear is that 1D arrays and 2D arrays are basically the same thing, so int[3][3]
is similar to int[9]
. So even though I said we needed a “grid”, it’s fine to create a 1D grid, and we just have to know the width in order to convert from 1D to 2D. For example, if you wanted to set the pixel (3,3) to red without using the built in set() function, you can convert (3,3) to a 1D index with pixels[y*width + x]
and fill the y, width, and x values:
pixels[3*480 + 3] = color(255, 0, 0);
So yeah, making the indexBuilder is a bit of a pickle. The easiest way would just be to write the whole list out:
int[] index = {0, 600, 1, 700, ...};
But as you saw, it’s ridiculously long. So instead of just putting them in, I just waved my hands and said
int[] index = indexBuilder(...);
that way, I didn’t have to go to the trouble of actually making it, I can just pretend I have already made it. When processing gets to that line, it will look for a function called “indexBuilder()
”, and run that code before moving to the next line. The function call will get replaced by whatever the function returns, like {0, 1, 2, …}. What we want now is to make the function that builds the index. We can place our function anywhere in the file, I put mine beneath draw(){...}
, and it will look something like this:
type name(){ }
where “type” is what it will give us back, and “name” is whatever we want to call it. I called mine indexBuilder:
type indexBuilder(){ }
We know we want it to return us an array, because on the line:
int[] index = indexBuilder(width, height);
there is an array to the left of the equals sign. So we should make the function match that:
int[] indexBuilder(){ }
and then since we said it will return a type of integer array, it will complain at us until we actually do it:
int[] indexBuilder(){
return new int[288000];
}
When you make a new array, it is just full of zeros. If we wanted to make the top grid, we could just use a for loop:
int[] indexBuilder(){
int[] array = new int[288000];
for(int i=0; i<array.length; i++){
array[i] = i;
}
return array; // returns {0, 1, 2, ...}
}
You can think of the line array[i] = i;
as being like the math function “y = x”, the input matches the output.
f(0) = 0,
f(1) = 1,
f(2) = 2, etc.
So first, I used pen and paper to see if I could find the formula for the bottom grid, where we could just straight replace i
. The left column pattern is clearly “y = sum from 1 to x”, and the top row is the same sum, just -1. So I tried summing the x and y rows together like so:
int[] indexBuilder(){
int[] array = new int[288000];
for (int x=0; x<5; x++) {
for (int y=0; y<5; y++) {
int y_sum = sum(1, y+1) - 1;
int x_sum = sum(y, y+x-1) + x;
array[i] = x_sum + y_sum;
}
}
return array; // returns {0, 2, 5, 9, ...}
}
It looks correct, but it only works for the top-left half of the array, and things get inverted when you cross the diagonal. I’m sure there is a nice function to generate the numbers we need, but at this point, it might take a while to find it. And it is probably easier to just create and fill in a table by following what you would do by hand. So I wrote this instead:
int[] indexBuilder(int wide, int high) {
int len = wide * high;
int[] index = new int[len];
int index_position = 0; // current index being calculated
int w=wide, h=high;
for (int i=0; i<h; i++) { // go down left side from top to bottom
int x = 0, y = i;
while (y >= 0 && x < w) { // stop if on top row or right column
index[index_position++] = (y * w) + x; // convert position from 1d to 2d
y--; // move up
x++; // move right
}
}
for (int i=1; i<w; i++) { // go across bottom row from left to right
int x = i, y = h-1;
while (y >= 0 && x < w) { // stop if on top row or right column
index[index_position++] = (y * w) + x; // convert position from 1d to 2d
y--; // move up
x++; // move right
}
}
return index;
}
If you find this a little hard to read, that makes two of us. I realise I just pulled a “how to draw an owl” on you, but as you can see, it’s a little intricate. I don’t know how to explain that to somebody. You might find a simpler version that will be easier to read by searching for “traverse array diagonal”. Hopefully you get the gist of what is going on by seeing a solution though.
One more thing is that although sorting only takes a minute or two for me, it is possible to sort all the numbers in milliseconds by using a different sorting algorithm (ideally it would take n*log(n) steps on average). I just mention this in case you want to do bigger images. I would add a print statement to your for loop though, so you can track your progress:
for (int i ...) {
for (int j ...) {...}
// swap pixels, etc.
println(i*100f/sorted.pixels.length + " %");
}
Oh yeah, and if the output looks fuzzy, you can swap from saving a jpg to a png.