Understanding Three-Dimensional Arrays - java

I'm trying to wrap my head around three-dimensional arrays. I understand that they are arrays of two-dimensional arrays, but the book I'm reading said something that confuses me.
In an exercise for the book I'm reading, it asks me to make a three-dimensional array for a full-color image. It gives a small example saying this:
If we decide to choose a three-dimensional array, here's how the array might be declared:
int[][][] colorImage = new int[numRows][numColumns][3];
However, wouldn't it be more effective like this?
int[][][] colorImage = new int[3][numRows][numColumns];
Where 3 is the rgb values, 0 being red, 1 being green, and 2 being blue. With the latter, each two-dimensional array would be storing the color value of the row and column, right? I just want to make sure I understand how to effectively use a three-dimensional array.
Any help will be greatly appreciated, thanks.

Order doesn't matter, and in fact the former form is more readable:
final const int RED = 0;
final const int GREEN = 1;
final const int BLUE = 2;
int[][][] colorImage = new int[numRows][numColumns][3];
//...
int x = getSomeX();
int y = getSomeY();
int redComponent = colorImage[x][y][RED];
int greenComponent = colorImage[x][y][GREEN];
int blueComponent = colorImage[x][y][BLUE];

The order shouldn't matter, so one isn't more effective than the other. The only thing that matters is that whatever accesses colorImage knows which dimension is used for what. Bit more context on multidimensional arrays here.

I'm not sure if its a good idea to put everything in an 3dimensional array of int.
Your first mistake is the dataytpe:
RGB is a int.
But R is a byte, G is a byte, B is a byte too.. (Color.getXXX() delivers an int, I dont know why because its a byte 0-255)
You need an int because you want to address more than 256 cols&rows. (Thats okay).
But i think that its much better to encapsulate the color information in a extra object. Perhaps a private datastructure like
class MyColor {
public byte r, g, b; //public for efficient access;
public int color; //public for efficient access;
public MyColor(final int rgb) {
this(new Color(rgb));
}
public MyColor(final Color c) {
this((byte) c.getRed(), (byte) c.getGreen(), (byte) c.getBlue(), c.getRGB());
}
public MyColor(final byte red, final byte green, final byte blue, final int c) {
this.r = red;
this.g = green;
this.b = blue;
this.color = c;
}
}
and put this in an 2dim array of MyColor[numRows][numColumns]
But if you make the class MyColor public to your whole app - i would change the design of the class to be more secure.

Related

Create smooth gradient with HSB in java

I'm trying to create a sorting algorithm visualiser in greenfoot java, with an array shown as a column graph. I want it to be colourful, so I'm using a colour gradient like shown in this video https://www.youtube.com/watch?v=qlvBsYyGIDo
This is the non-working code:
Pseudocode{
int[50] arr={1,2,3...}
for(int i:arr)
rect.color=getColor(i,arr.length)
}
private static Color getColor(int value, int length){
float h=((float)value/(float)length);//goes from 0-1
System.out.println(value+" "+length+" "+h);
java.awt.Color rgb=java.awt.Color.getHSBColor(h,255,255);
int r=rgb.getRed();
int g=rgb.getGreen();
int b=rgb.getBlue();
System.out.println(rgb+" "+r+" "+g+" "+b);
return new Color(r,g,b);//greenfoot.Color, not java.awt.Color
}
But the colours it produces look like this:
How can I create a smoth gradient, from red->yellow->green->cyan->blue->magenta?
Thanks #Thomas. Turns out all three of the values for Color.getHSBColor() range from 0-1, and values higher than that produce ~undefined behaviour~.

Spread an array into multiple arguments for a function

How can I pass an array as three arguments to a function in Java? (Forgive me, I'm very new to Java).
I have the following function which takes float r, float g, float b, float a as arguments.
renderer.prepare(r, g, b, 1);
And I want to pass the output from this function in. (Or figure out how to return three separate unpacked floats).
public static float[] rgbToFloat(int r, int g, int b) {
return new float[] {(float) r / 255f, (float) g / 255f, (float) b / 255f};
}
How can I do this? In some other languages it would look something like this:
renderer.prepare(...rgbToFloat(25, 60, 245), 1);
This is a typical example of an "X-Y Problem". Your original quest was to somehow group the 3 different parameters that you want to pass to a function. That's "problem X". Then you came up with the idea of using an array for this, but you were still unsure how to go about it, so you posted this question asking how to best use an array to achieve what you want. But that's "problem Y", not "problem X".
Using an array may and may not be the right way of solving "problem X". (Spoiler: it isn't.)
Honoring the principle of the least surprise, the best way of solving your problem "X" in my opinion is by declaring a new class, FloatRgba which encapsulates the four floats in individual float members: final float r; final float g; final float b; final float a;.
So, then your rgbToFloat() method does not have to return an unidentified array of float, instead it becomes a static factory method of FloatRgba:
public static FloatRgba fromIntRgb( int r, int g, int b )
{
return new FloatRgba( r / 255f, g / 255f, b / 255f, 1.0f );
}
Finally, you introduce a utility function prepareRenderer which accepts a Renderer and a FloatRgba and invokes renderer.prepare(float, float, float, float) passing it the individual members of FloatRgba.
This way you keep everything clear, self-documenting, and strongly typed. Yes, it is a bit inconveniently verbose, but that's java for you.
Maybe late for the original question, might help late readers stumbling over here (like me), though: I rather recommend converting just one single parameter to float:
public static float rgbToFloat(int value)
{
return value / 255.0f;
// don't need the cast, value will be promoted to float anyway
// as second parameter is already
}
Call now gets to
renderer.prepare(rgbToFloat(25), rgbToFloat(60), rgbToFloat(245), 1);
Sure, the draw back is that you have to call it three times now (as the other way round, you would have had to store the array in a temporary as shown in the comments, you wouldn't, in comparison, have gained much either), in the end, you gain flexibility for and additionally avoid the temporary array object when none is needed.
If you still insist on the array, you'll need a temporary
float[] rgb = rgbToFloat(r, g, b);
renderer.prepare(rgb[0], rgb[1], rgb[2], 1.0f);
But then I wonder why you don't consider alpha as well:
public static float[] rgbToFloat(int r, int g, int b)
{
return rgbToFloat(r, g, b, 255);
}
public static float[] rgbToFloat(int r, int g, int b, int a)
{
return new float[] { r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
}

Creating a large 2D array and populating it from a LinkedHashMap of smaller 2D Arrays

I'm trying to create a large 2D Array int[][] from a LinkedHashMap which contains a number of smaller Arrays for an A* Pathfinder I'm working on.
The Map the Pathfinder is using is streamed in smaller chunks to the client and converted into a simplified version for the Pathfinder.
Map<Coord, int[][]> pfmapcache = new LinkedHashMap<Coord, int[][]>(9, 0.75f, true);
The Coord look like this: Coord(0,0) or Coord(-1,0).... etc. and the int[][] are always int[100][100] big.
Now I would like to create a new large int[][] that would encompass all the smaller Array where the small Array Coord(0,0) would be in the center of the new Large Array.
int[][] largearray = [-1,1][0,1][1,1]
[-1,0][0,0][1,0]
[-1,-1][0,-1][1,-1]
So that the large array would be int[300][300] big in this example.
2. I would like to expand the new large Array if a new small array gets added to the pfmapcache.
int[][] largearray = [][][1,2]
[-1,1][0,1][1,1]
[-1,0][0,0][1,0]
[-1,-1][0,-1][1,-1]
I don't have to store the smaller Arrays in pfmapcache I could add them as they are created with a 2 small arrays combining etc.. but with the negative Position of the Arrays in relation to the original I have no idea how to combine them and preserve their relative postion.
First time posting here, if I need to clarify something pls let me know.
You're wondering how to use your existing pathfinder algo with a chunked map.
This is when you need to place an abstraction layer between your data representation, and your data usage (like a Landscape class).
Q: Does a pathfinding algorithm need to know it works on a grid, on a chunked grid, on a sparse matrix, or on a more exotic representation?
A: No. A Pathfinder only needs to know one thing: 'Where can I get from here?'
Ideally
you should drop any reference to the fact that your world is on a grid by working with only a class like:
public interface IdealLandscape {
Map<Point, Integer> neighbours(Point location); // returns all neighbours, and the cost to get there
}
Easy alternative
However I understood your existing implementation 'knows' about grids, with the added value that adjacency is implicit and you're working with points as (x, y). You however lost this when introducing chunks, so working with the grid doesn't work anymore. So let's make the abstraction as painless as possible. Here's the plan:
1. Introduce a Landscape class
public interface Landscape {
public int getHeight(int x, int y); // Assuming you're storing height in your int[][] map?
}
2. Refactor your Pathfinder
It's reaaally easy:
just replace map[i][j] with landscape.getHeight(i, j)
3. Test your refactoring
Use a very simple GridLandscape implementation like:
public class GridLandscape implements Landscape {
int[][] map;
public GridLandscape(...){
map = // Build it somehow
}
#Override
public int getHeight(int x, int y){
return map[x][y]; // Maybe check bounds here ?
}
}
4. Use your ChunkedGridLandscape
Now your map is abstracted away, and you know your Pathfinder works on it, you can replace it with your chunked map!
public class ChunkedGridLandscape implements Landscape {
private static final int CHUNK_SIZE = 300;
Map<Coord, int[][]> mapCache = new LinkedHashMap<>(9, 0.75f, true);
Coord centerChunkCoord;
public ChunkedGridLandscape(Map<Coord, int[][]> pfmapcache, Coord centerChunkCoord){
this.mapCache = pfmapcache;
this.centerChunkCoord = centerChunkCoord;
}
#Override
public int getHeight(int x, int y){
// compute chunk coord
int chunkX = x / CHUNK_SIZE - centerChunkCoord .getX();
int chunkX = y / CHUNK_SIZE - centerChunkCoord .getY();
Coord chunkCoord = new Coord(chunkX, chunkY);
// Now retrieve the correct chunk
int[][] chunk = mapCache.get(chunkCoord); // Careful: is the chunk already loaded?
// Now retrieve the height within the chunk
int xInChunk = (x + chunkX*CHUNK_SIZE) % CHUNK_SIZE; // Made positive again!
int yInChunk = (y + chunkY*CHUNK_SIZE) % CHUNK_SIZE; // Made positive again!
// We have everything !
return chunk[xInChunk][yInChunk];
}
}
Gotcha: Your Coord class NEEDS to have a equals and hashCode methods properly overloaded!
5. It just works
This should just immediately work with your pathfinder. Enjoy!

Convert color object to readable array

I want to make a negative of an image in Java, but I'm not sure how to convert a Color object into an array which can be manipulated. Here's a snippet of my code:
Color col;
col = picture.getPixel(x,y).getColor();
//x and y are from a for loop
picture.getPixel(x,y).setColor(~~~);
setColor takes three integers, one for each color channel RBG. I want to convert Color col to an array which I can read. Something like the below:
picture.getPixel(x,y).setColor(255-col[0],255-col[1],255-col[2]);
255-col[n] of course creates a negative of the pixel, but Color col is not an array when I'd like to access it as one. How can I cast a Color object as an array?
I could do something like the below and not use a Color object at all,
r = picture.getPixel(x,y).getRed(); //r is now an integer 0-255
//repeat the above for green and blue
picture.getPixel(x,y).setColor(r,g,b);
But I'd much rather do it in one line.
What about :
int [] arrayRGB = new int[3];
arrayRGB[0] = col.getRed();
arrayRGB[1] = col.getGreen();
arrayRGB[2] = col.getBlue();
Or directly :
picture.getPixel(x,y).setColor(255-col.getRed(),255-col.getGreen(),255-col.getBlue());
Take a look at the Color class.
You cannot cast Color as an array, but you can get it's components as an array:
int[] rgb = new int[] { col.getRed(), col.getGreen(), col.getBlue() };
You might want to just use these directly.

What's the best way to "round" a Color object to the nearest Color Constant?

I will be retrieving the exact color of a pixel and would like to relate that exact color to a constant like Color.blue. Is there an easy way to "round" to the nearest Color Constant? Additionally, is there a way to define your own Color Constants?
The basic approach is to find the closest standard color to your sample by simply comparing the sample to each of them. The problem, of course, is in defining "closest." The most obvious would be use the Euclidean distance in RGB space. The problem is that this distance does not correspond very well with our perceptual sense of "closest color". A discussion of this problem, along with a nice (easily computed) metric (including pseudocode!) can be found in this paper.
EDIT: Just in case the link to that paper goes dead (or if you're lazy and are willing to use code without understanding what it does), here's my Java version of the "color distance function" the paper suggests as a "low-cost approximation" to their recommended distance function (a weighted Euclidean distance in RGB space):
double colorDistance(Color c1, Color c2)
{
int red1 = c1.getRed();
int red2 = c2.getRed();
int rmean = (red1 + red2) >> 1;
int r = red1 - red2;
int g = c1.getGreen() - c2.getGreen();
int b = c1.getBlue() - c2.getBlue();
return Math.sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8));
}
Note that if you're just going to rank color distances, you can dispense with the call to Math.sqrt(), saving some computation costs.
Probably the best way would be to loop over every constant, and comparing their respective RGB channels (getRed, getGreen, getBlue). Keep track of the one that is closest.
Color color = new Color(...);
Color[] constantColors = new Color[] { Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray, Color.green, Color.lightGray, Color.magenta, Color.orange, Color.pink, Color.red, Color.white, Color.yellow };
Color nearestColor = null;
Integer nearestDistance = new Integer(Integer.MAX_VALUE);
for (Color constantColor : constantColors) {
if (nearestDistance > Math.sqrt(
Math.pow(color.getRed() - constantColor.getRed(), 2)
- Math.pow(color.getGreen() - constantColor.getGreen(), 2)
- Math.pow(color.getBlue() - constantColor.getBlue(), 2)
)
) {
nearestColor = color;
}
}
No, you can't add color constants to the class, but you can create a class of your own to hold constants.
class MyColors {
public static final Color heliotrope = new Color(...);
}
Edit: added difference algorithm, thanks to #Ted's link.
You can use Java's built-in color conversion with an IndexColorModel containing the palette of possible colors. Internally, the class uses Euclidean distance over the color components to determine the closest color.
import java.awt.Color;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
public class ColorConverter {
private final Color[] colors;
private final IndexColorModel colorModel;
public ColorConverter(Color[] colors) {
this.colors = colors;
this.colorModel = createColorModel(colors);
}
private static IndexColorModel createColorModel(Color[] colors) {
final int[] cmap = new int[colors.length];
for (int i = 0; i<colors.length; i++) {
cmap[i] = colors[i].getRGB();
}
final int bits = (int) Math.ceil(Math.log(cmap.length)/Math.log(2));
return new IndexColorModel(bits, cmap.length, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
}
public Color nearestColor(Color color) {
final byte index = ((byte[])colorModel.getDataElements(color.getRGB(), null))[0];
return colors[index];
}
}

Categories

Resources