i have a problem regarding pixel analysis for an image.
I am trying to analyse every pixel that is white (R=255,G=255,B=255).
The problem is the storing/ reading of these data.
for (int i = 0; i <= Map.getHeight(); i++) {
for (int j = 0; j <= Map.getWidth(); j++) {
if (Map.getColor(j, i).getBlue() == 255 && Map.getColor(j, i).getRed() == 255
&& Map.getColor(j, i).getGreen() == 255)
{
// coordsX = new HashMap<>();
coordsX.put(j, new Rectangle(j, i, 5, 5));
}
}
coordsY.put(i, coordsX);
}
System.out.println();
}
The reading function is the following:
for (Entry<Integer, HashMap<Integer, Rectangle>> e : coordsY.entrySet()) {
// HashMap<Integer, Rectangle> coordsX = coordsY.get(y);
HashMap<Integer, Rectangle> coordsX = e.getValue();
if (coordsX != null) {
for (Entry<Integer, Rectangle> entry : coordsX.entrySet()) {
Rectangle rect = entry.getValue();
g.setColor(Color.red);
g.draw(rect);
if (this.car2.intersects(rect)) {
intersectsTrack = true;
}
}
}
}
The problem is that when i outline:
coordsX = new HashMap<>();
like done above, i only get all one x value for one y value
example.
If i dont outline this line it is the other way around.
example.
Can you help me fixing this problem?
Kind Regards
You're creating a new coordsX everytime you've discovered a new white pixel. That's probably not what you intended. So for each y there will be one map coordsX with only one entry, any previous entry is discarded.
Also, I like to suggest to create a class for representing a 2D coordinate, let's call it Coordinate, then your algorithm gets much easier to implement. (or maybe there's already such a thing, for instance Point?)
class Coordinate {
private int x, y; // plus getter, setter, etc.
public int hashCode() {
return Objects.hash(x, y);
}
public boolean equals(Object obj) {
if (obj == this)
return true;
else if (!(obj instanceof Coordinate))
return false;
Coordinate that = (Coordinate) obj;
return this.x == that.x && this.y == that.y;
}
public String toString() {
return "(" + x + ", " + y + ")";
}
}
// ...
Map<Coordinate, Rectangle> coords = new HashMap<>();
for (int y = 0; y <= Map.getHeight(); y++) {
for (int x = 0; x <= Map.getWidth(); x++) {
Color color = Map.getColor(x, y);
if (color.getBlue() == 255 && color.getRed() == 255 && color.getGreen() == 255) {
Coordinate coordinate = new Coordinate(x, y);
Rectangle rectangle = new Rectangle(x, y, 5, 5);
coords.put(coordinate, rectangle);
}
}
}
for (Rectangle rectangle : coords.values()) {
g.setColor(Color.red);
g.draw(rect);
}
What you're saying makes sense since your code coordsX.put(j, new Rectangle(j, i, 5, 5)); associates the x coordinate j with a Rectangle, while your code coordsY.put(i, coordsX); associates the final y coordinate i with the coordsX HashMap.
However, due to associating with only an x or y value, each put() call will overwrite if you put to the same coordinate, which you do with j i times. It might be better given your intention to have a single HashMap of an x/y pair class (such as a Point2D) that you map to a rectangle.
You can further optimize this by using some knowledge about a rectangle. If you want to store some x/y pairs for certain colors of an image, you can use a one Dimensional array, knowing you can index each coordinate as x + y * width, or in your case j + i * Map.getWidth(). I would design your array "hashmap" like so:
// put values in the array.
Rectangle[] coords = new Rectangle[Map.getWidth() * Map.getHeight()];
for (int i = 0; i <= Map.getHeight(); i++) {
for (int j = 0; j <= Map.getWidth(); j++) {
int index = j + i * map.getWidth();
if (/* i,j has the correct color */) {
coords[index] = new Rectangle(j, i, 5, 5);
}
}
}
// read values from the array
for (int i = 0; i <= Map.getHeight(); i++) {
for (int j = 0; j <= Map.getWidth(); j++) {
Rectangle rect = coords[j + i * Map.getWidth()];
if(rect == null) continue;
//Your read logic here
g.setColor(Color.red);
g.draw(rect);
if (this.car2.intersects(rect)) {
intersectsTrack = true;
}
}
}
Related
It's a simplified code snippet I need to use in a Conway's Game of Life implementation. I have a HashMap where the Coord Objects ( cells in Game of Life ) are stored. Coord objects are pairs of X, Y coordinates which I want to print on a simple 2D grid.
I have problems with the printGrid(HashMap<Coord, Integer> map) method, mainly:
Is there a simpler and more elegant way to print the Coord objects on a 2-dimensinal grid?
If not, is there a way to check if there is a Coord object with given X,Y parameters in the HashMap without creating an instance of the Coord object for every X,Y position on the grid like I'm doing here:
if (map.containsKey(new Coord(column,row))) {
grid += map.get(new Coord(column,row));
} else {
grid += "#";
}
The code:
public class Main {
static class Coord {
int x;
int y;
public boolean equals(Object o) {
Coord c = (Coord) o;
return c.x == x && c.y == y;
}
public Coord(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int hashCode() {
return x * 3 + y * 5;
}
}
public static String printGrid(HashMap<Coord, Integer> map) {
int grid_width = 10;
int grid_height = 10;
String grid = "";
for(int row =0; row < grid_height; row++) {
for(int column=0; column< grid_width; column++) {
if(map.containsKey(new Coord(column,row))) {
grid += map.get(new Coord(column,row));
} else {
grid += "#";
}
}
grid += "\n"; // next row
}
return grid;
}
public static void main(String args[]) {
HashMap<Coord, Integer> map = new HashMap<Coord, Integer>();
map.put(new Coord(0, 0), 1);
map.put(new Coord(1, 2), 5);
map.put(new Coord(3, 4), 1);
map.put(new Coord(4, 5), 3);
map.put(new Coord(4, 6), 2);
System.out.println(printGrid(map));
}
}
I think the following code match your requirements.Basically you can create a StringBuilder object and initialize it with all '#'. Then, based on Coord objects in the map replace the corresponding character in the builder with the value in the map.It is simple and you don't need to create a Coord object for each grid cell.
public static String printGrid(HashMap<Coord, Integer> map) {
int width = 10;
int height = 10;
int lenght = height * width ;
StringBuilder grid = new StringBuilder(lenght);
// Initialize the builder
int i = 0 ;
while(i<lenght){
if(i >= width && i%(width)==0){
grid.append('\n');
}
grid.append('#');
i++;
}
for(Coord c : map.keySet()){
int index = c.x *(width + 1) + c.y;
grid.setCharAt(index, map.get(c).toString().charAt(0));
}
return grid.toString();
}
output:
1#########
##5#######
##########
####1#####
#####32###
##########
##########
##########
##########
##########
hope this can help.
Your code is close to what you want, but:
Since the grid is very big, how would printGrid() know the size? You should pass the size in as parameters too.
Don't create the Coord object twice. Only create it once and assign to variable, so it can be used twice.
This point is negated by the next bullet, but point well made, right?
Don't call containsKey(), then get(). Since your map will not have null values (according to the way you used it), the null returned by get() is enough of an existence test.
Do not do string += string in a loop. Use a StringBuilder. Huge performance difference when string gets large.
So, your code becomes:
public static String printGrid(HashMap<Coord, Integer> map, int width, int height) {
StringBuilder grid = new StringBuilder(height * (width + 1)); // Pre-sizing is optional, but
// may improve performance a bit
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
Integer value = map.get(new Coord(column, row));
if (value != null) {
grid.append(value);
} else {
grid.append('#');
}
}
grid.append('\n'); // next row
}
return grid.toString();
}
Considering Andreas' suggestion, here is my proposition
Add int neighbours in the Coord class
static class Coord {
int x;
int y;
int neighbours;
public Coord(int x, int y) {
this(x, y, 0);
}
public Coord(int x, int y, int neighbours) {
this.x = x;
this.y = y;
this.neighbours = neighbours;
}
// getters and setters here
// Equals method here
}
Add a method to retrieve a Coord object from coordinates
Coord getCoord(HashSet<Coord> data, int x, int y) {
Coord coord = new Coord(x, y);
if (data.contains(coord)) {
for (Coord c : data) {
if (c.equals(coord)){
return c;
}
}
}
return null;
}
Your printGrid method will be like this
String printGrid(HashSet<Coord> data, int width, int height) {
StringBuilder grid = new StringBuilder(height * (width + 1));
Coord coord;
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
coord = getCoord(data, row, column);
if(coord != null) {
grid.append(coord.getNeighbours());
} else {
grid.append('#');
}
}
grid.append('\n'); // next row
}
return grid.toString();
}
And in your main method you will have this
HashSet<Coord> data = new HashSet<>();
data.add(new Coord(0, 0, 1));
data.add(new Coord(1, 2, 5));
data.add(new Coord(3, 4, 1));
data.add(new Coord(4, 5, 3));
data.add(new Coord(4, 6, 2));
System.out.println(printGrid(data, 10, 10));
You can also use ArrayList like this
Coord getCoordWithList(ArrayList<Coord> data, int x, int y) {
Coord coord = new Coord(x, y);
int index = data.indexOf(coord);
if (index > -1) {
return data.get(index);
}
return null;
}
Well instead of having a map with Coord object as key, you can use a String as key. Your String key will be the concatenation of x and y values of your Coord.
So you will have this
for (int row = 0; row < grid_height; row++) {
for (int column = 0; column < grid_width; column++) {
String key = column + "#" + row;
if(map.containsKey(key)) {
grid += map.get(key) + "";
} else {
grid += "#";
}
}
grid += "\n"; // next row
}
And for the initialisation of the map you will have this
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("0#0", 1);
map.put("1#2", 5);
map.put("3#4", 1);
map.put("4#5", 3);
map.put("4#6", 2);
System.out.println(printGrid(map));
Edit
Instead of using a HashMap to store your data, you can just use a HashSet for it. The Integer value of the map will be an attribute of your Coord class
I'm writing the drawing system for a roguelike game based on ascii characters (graphics similar to dwarf fortress). I'm using the AsciiPanel from here. My problem is that when I draw entities on my map, they seem to blink, when they should be solid.
In this gif, the r characters in the top row are the entities.
This is the map's draw method that is called every frame.
public void draw(final Display display) {
for (int x = getViewportX(); x < getViewportX() + viewportWidthInTiles; x++) {
for (int y = viewportY; y < viewportY + viewportHeightInTiles; y++) {
final char character = background[x][y].getCharacter();
final Color foreground = background[x][y].getForeground();
final Color backgroundColor = background[x][y].getBackground();
final AsciiCharacterData data = new AsciiCharacterData(
character, foreground, backgroundColor);
display.setCharacterAt(x - getViewportX(), y - viewportY,
background[x][y].getDrawingLayer(), data);
}
}
display.clearLayer(DrawingLayer.PRIMARY);
for (int i = 0; i < entities.size(); i++) {
final Entity e = entities.get(i);
final char character = e.getCharacter();
final Color foreground = e.getForeground();
final Color backgroundColor = e.getBackground();
final AsciiCharacterData data = new AsciiCharacterData(character,
foreground, backgroundColor);
display.setCharacterAt(e.getX() - getViewportX(), e.getY()
- viewportY, e.getDrawingLayer(), data);
}
}
I think I know what causes the problem, because if I write display.clearLayer(DrawingLayer.BACKGROUND); (the layer the tiles are drawn to) before I draw the background tiles, it creates something even more ridiculous.
This is the Display class, where I think I am making some mistake.
public class Display {
private static final char TRANSPARENT_CHARACTER = ' ';
private final AsciiPanel displayPanel;
private final int widthInCharacters, heightInCharacters;
private final static int Z_LEVELS = DrawingLayer.values().length;
private final AsciiCharacterData[][][] characterMap;
public Display(final AsciiPanel panel) {
displayPanel = panel;
widthInCharacters = panel.getWidthInCharacters();
heightInCharacters = panel.getHeightInCharacters();
characterMap = new AsciiCharacterData[widthInCharacters][heightInCharacters][Z_LEVELS];
for (int x = 0; x < widthInCharacters; x++) {
for (int y = 0; y < heightInCharacters; y++) {
for (int z = 0; z < Z_LEVELS; z++) {
characterMap[x][y][z] = new AsciiCharacterData(
TRANSPARENT_CHARACTER,
displayPanel.getDefaultForegroundColor(),
displayPanel.getDefaultBackgroundColor());
}
}
}
}
public void setCharacterAt(final int x, final int y, final DrawingLayer z,
final AsciiCharacterData c) {
if (x < 0 || x >= widthInCharacters || y < 0 || y >= heightInCharacters)
return;
characterMap[x][y][z.layer] = c;
// if z is not the top level
if (z.layer != Z_LEVELS - 1) {
// check all levels above
for (int i = z.layer + 1; i < Z_LEVELS; i++) {
// if there is an opaque character
if (characterMap[x][y][i].character != TRANSPARENT_CHARACTER)
// we dont need to draw anything
return;
}
}
if (c.character == TRANSPARENT_CHARACTER) {
// loop through all characters under the transparent character
for (int i = z.layer - 1; i >= 0; i--) {
// if we find a non transparent character
if (characterMap[x][y][i].character != TRANSPARENT_CHARACTER) {
// display that one instead
displayPanel.write(characterMap[x][y][i].character, x, y,
characterMap[x][y][i].foregroundColor,
characterMap[x][y][i].backgroundColor);
return;
}
}
// if there were no non trasparent characters
displayPanel.write(TRANSPARENT_CHARACTER, x, y);
// if we are a highlighter, we draw the below character and then
// just draw on top
} else {
displayPanel.write(c.character, x, y, c.foregroundColor,
c.backgroundColor);
}
displayPanel.repaint();
}
public AsciiCharacterData getCharacterAt(final int x, final int y,
final DrawingLayer z) {
return characterMap[x][y][z.layer];
}
public int getWidth() {
return widthInCharacters;
}
public int getHeight() {
return heightInCharacters;
}
public void clearLayer(final DrawingLayer layer) {
for (int x = 0; x < widthInCharacters; x++) {
for (int y = 0; y < heightInCharacters; y++) {
setCharacterAt(x, y, layer,
new AsciiCharacterData(TRANSPARENT_CHARACTER,
displayPanel.getDefaultForegroundColor(),
displayPanel.getDefaultBackgroundColor()));
}
}
}
}
Solved! It was one line in the setCharacterAt method. I was repainting every time I set a character which (while inefficient) also creates that flicker.
I am creating a battle ship game, where each round the player can select 4 ship routes by selecting the 4 closest tiles to him, with a mouse.
Now in the following gif, you can see if I will hover on the very edge of a tile, it will select two tiles at once, or even 4 at once if you're holding your mouse in middle of 4 tiles:
(source: gyazo.com)
This should not really be happening in the game, how can I prevent that?
This is the updating code:
/**
*
* #param x mouse X
* #param y mouse Y
*/
public void checkHover(int x, int y) {
for (int i = 0; i < tiles[0].length; i++) {
for (int j = 0; j < tiles[1].length; j++) {
// get coordinates from tile
int x1 = i * (sprite.getWidth() - 1);
int y1 = j * (sprite.getHeight() - 1);
// If we have screen translate, we can cancel it for
// this situation
int realX = x - this.translate.getX();
int realY = y - this.translate.getY();
// checking if mouse inside tile
if (realX >= x1 && realX <= x1 + sprite.getWidth() &&
realY >= y1 && realY <= y1 + sprite.getHeight()) {
// set tile to hovered sprite
this.tiles[i][j].setHover(true);
}
else {
// cancel hovered sprite
this.tiles[i][j].setHover(false);
}
}
}
}
How can I prevent that?
Instead of the two loops I would calculate the index of the 2d array based off the x and y position of the mouse, by dividing the x and y position by how much space there is from one tile to the next (which looks to be the tile width plus 1, the empty border). This will cause border hovers to favor a certain side, but it looks like you don't want to select two tiles anyways.
Once you have the index of the tile, switch the highlight on, and keep a reference to it with an instance variable like lastHighlightedTile, but before you do that, also call lastHighlightedTile.setHover(false);.
If the index of the tile is invalid, as if the mouse is out of the tile area, don't access the array and still call lastHighlightedTile.setHover(false);.
This should fix highlighting multiple tiles, and will also allow you to turn off the previous highlight (the solution of breaking out of the current loop might not turn off the previous highlight).
Assuming width of sprite is equal to 50, I see that first title has x1 to x2 coordinate (0 - 50), second title has (49 - 100), third has (99 - 150)... Same for y dimension.
So when pointer is at (x) = (50), it hovers (x1 - x2) (0-50) and (49-100) titles.
Why are you subtracting 1 from sprite width / height?
try with this:
// get coordinates from tile
int x1 = i * (sprite.getWidth());
int y1 = j * (sprite.getHeight());
Edit: I wrote sample app, which shoved me solution:
public class App {
Tile[][] tiles;
Sprite sprite;
public static void main(String[] args) {
App app = new App();
app.init();
app.checkHover(50, 50);
app.printHovers();
}
private void init() {
sprite = new Sprite();
tiles = new Tile[10][10];
for (int i = 0; i < tiles[0].length; i++) {
for (int j = 0; j < tiles[1].length; j++) {
tiles[i][j] = new Tile();
}
}
}
public void checkHover(int x, int y) {
for (int i = 0; i < tiles[0].length; i++) {
for (int j = 0; j < tiles[i].length; j++) {
// get coordinates from tile
int x1 = i * (sprite.getWidth());
int y1 = j * (sprite.getHeight());
// If we have screen translate, we can cancel it for
// this situation
// int realX = x - this.translate.getX();
// int realY = y - this.translate.getY();
int realX = x;
int realY = y;
// checking if mouse inside tile
if (realX >= x1 && realX < x1 + sprite.getWidth()
&& realY >= y1 && realY < y1 + sprite.getHeight()) {
// set tile to hovered sprite
this.tiles[i][j].setHover(true);
} else {
// cancel hovered sprite
this.tiles[i][j].setHover(false);
}
}
}
}
public void printHovers() {
for (int i = 0; i < tiles[0].length; i++) {
for (int j = 0; j < tiles[i].length; j++) {
System.out.print((tiles[i][j].isHover() ? "Y" : "O") + " ");
}
System.out.println();
}
}
public class Sprite {
public int getWidth() {
return 50;
}
public int getHeight() {
return 50;
}
}
public class Tile {
private boolean hover;
public boolean isHover() {
return hover;
}
public void setHover(boolean hover) {
this.hover = hover;
}
}
}
It works fine for (49, 49), (49, 50), (50, 49) and (50, 50) coords.
So, first you have to remove -1 subtractions, and change <= test to <.
EDIT: Different solution:
Simply declare a boolean at the beginning of the method, and set only if that boolean is still false. Once you set the tile on, also set the boolean to true, which will prevent others from being set on.
public void checkHover(int x, int y) {
boolean hasTurnedOneOn = false;
for (int i = 0; i < tiles[0].length; i++) {
for (int j = 0; j < tiles[1].length; j++) {
// get coordinates from tile
int x1 = i * (sprite.getWidth() - 1);
int y1 = j * (sprite.getHeight() - 1);
// If we have screen translate, we can cancel it for
// this situation
int realX = x - this.translate.getX();
int realY = y - this.translate.getY();
// checking if mouse inside tile
if (realX >= x1 && realX <= x1 + sprite.getWidth() &&
realY >= y1 && realY <= y1 + sprite.getHeight() && !hasTurnedOneOn) {
// set tile to hovered sprite
this.tiles[i][j].setHover(true);
hasTurnedOneOn = true;
}
else {
// cancel hovered sprite
this.tiles[i][j].setHover(false);
}
}
}
}
I am lookin for an algorithmn to get the fastest way to find all points 2D (x,y) that are in a box (a box is defined by 2 points: lowerLeft and upperRight).
Imagine we have 2 million points in a 2D space.
In that 2D space I create a box somewhere from 2 points, one is lower left and the other is upper right.
What is the fastest way to get all the points that are in the box?
Here is the java test with the worst scenario: loop each point (2 millions!) and determine if it's inside the box.
I am sure we can get really faster if the list of points is ordered first...
Do you have ideas?
public class FindPointsInBox {
public static void main(String[] args) throws Exception {
// List of 2,000,000 points (x,y)
List<Point> allPoints = new ArrayList<Point>();
for(int i=0; i<2000000; i++) {
allPoints.add(new Point(46 - (Math.random()), -74 - (Math.random())));
}
// Box defined by 2 points: lowerLeft and upperRight
List<Point> pointsInBox = new ArrayList<Point>();
Point lowerLeft = new Point(44.91293325430085, -74.25107363281245);
Point upperRight = new Point(45.3289676752705, -72.93820742187495);
Date t1 = new Date();
// TODO: What is the fastest way to find all points contained in box
for(int i=0; i<allPoints.size(); i++) {
if(isPointInBox(allPoints.get(i), lowerLeft, upperRight))
pointsInBox.add(allPoints.get(i));
}
Date t2 = new Date();
System.out.println(pointsInBox.size() + " points in box");
System.out.println(t2.getTime()-t1.getTime() + "ms");
}
private static boolean isPointInBox(Point p, Point lowerLeft, Point upperRight) {
return (
p.getX() >= lowerLeft.getX() &&
p.getX() <= upperRight.getX() &&
p.getY() >= lowerLeft.getY() &&
p.getY() <= upperRight.getY());
}
}
Improving on Mikhails answer (I can't comment yet) you could utilise quadtrees http://en.wikipedia.org/wiki/Quadtree. This is what Mikhail is talking about, I think, and works by partitioning space into a grid. If there are many points in a partition it is itself partitioned into a small grid.
When selecting points one can then compare the extents of the partitions to quickly exclude several points if their containing rectangle does not intersect with your selection rectangle.
The quadtree requires O(n log n) operations for creation on average while a selecting a bunch of points requires O(log n).
Split your space into square cells. For each cell store list of points that sit in the cell. For given rectangle first find all cells that intersect with it, then iterate through points in these cells and test which of them are in the rectangle. Here is code demonstrating this approach:
public class PointsIndex {
private final int width;
private final int height;
private final int rows;
private final int cols;
private final List<Point> [][] cells;
#SuppressWarnings("unchecked")
public PointsIndex (
int width, int height, int rows, int cols)
{
this.width = width;
this.height = height;
this.rows = rows;
this.cols = cols;
cells = (List<Point> [][])new List<?> [rows][];
for (int i = 0; i < rows; i++)
cells [i] = (List<Point> [])new List<?> [cols];
}
public void addPoint (int x, int y)
{
int r = x * rows / width;
int c = y * cols / height;
List <Point> cell = cells [r][c];
if (cell == null)
{
cell = new ArrayList<Point>();
cells [r][c] = cell;
}
cell.add (new Point (x, y));
}
public Point [] getPoints (int x, int y, int w, int h)
{
int r1 = x * rows / width;
int r2 = (x + w - 1) * rows / width;
int c1 = y * cols / height;
int c2 = (y + h - 1) * cols / height;
ArrayList<Point> result = new ArrayList<Point>();
for (int r = r1; r <= r2; r++)
for (int c = c1; c <= c2; c++)
{
List <Point> cell = cells [r][c];
if (cell != null)
{
if (r == r1 || r == r2 || c == c1 || c == c2)
{
for (Point p: cell)
if (p.x > x && p.x < x + w && p.y > y && p.y < y + h)
result.add (p);
}
else result.addAll (cell);
}
}
return result.toArray(new Point [result.size()]);
}
public static void main(String[] args) {
Random r = new Random ();
PointsIndex index = new PointsIndex(1000000, 1000000, 100, 100);
List <Point> points = new ArrayList<Point>(1000000);
for (int i = 0; i < 1000000; i++)
{
int x = r.nextInt(1000000);
int y = r.nextInt(1000000);
index.addPoint(x, y);
points.add (new Point (x, y));
}
long t;
t = System.currentTimeMillis();
Point [] choosen1 = index.getPoints(456789, 345678, 12345, 23456);
System.out.println (
"Fast method found " + choosen1.length + " points in " +
(System.currentTimeMillis() - t) + " ms");
Rectangle rect = new Rectangle (456789, 345678, 12345, 23456);
List <Point> choosen2 = new ArrayList<Point>();
t = System.currentTimeMillis();
for (Point p: points)
{
if (rect.contains(p))
choosen2.add (p);
}
System.out.println(
"Slow method found " + choosen2.size () + " points in " +
(System.currentTimeMillis() - t) + " ms");
}
}
Your solution is linear, and you have no way to do better, because you have at least to read the input data.
I'm using Processing to divide a large image into a series of smaller, rectangular nodes.
Processing stores the color value for the pixels of a PImage in a pixels array, which I am accessing to break up the image into smaller parts. For some reason, I am getting this output, when my intent was for the entire image to be displayed when the nodes are arranged in draw().
Here is my main class:
ArrayList node = new ArrayList();
PImage grid;
PVector nodeDimensions = new PVector(210, 185);
PVector gridDimensions = new PVector(2549, 3300);
String name = "gridscan.jpeg";
void setup() {
size(500, 500);
grid = loadImage(name);
grid.loadPixels();
fillPixels();
noLoop();
}
void fillPixels() {
int nodeNum = 0;
for (int startX = 0; startX < 2549 - nodeDimensions.x; startX += nodeDimensions.x) {
for (int startY = 0; startY < 3300 - nodeDimensions.y; startY += nodeDimensions.y) {
node.add(new Node());
sendPixels(new PVector(startX, startY), nodeNum);
nodeNum++;
}
}
}
void sendPixels(PVector start, int nodeNum) {
for (int x = int(start.x); x < start.x + nodeDimensions.x; x++) {
for (int y = int(start.y); y < start.x + nodeDimensions.y; y++) {
Node _node = (Node) node.get(node.size() - 1);
_node.fillPixel(new PVector(x, y), grid.pixels[int(y*gridDimensions.x+x)]);
}
}
}
void draw() {
drawNodes();
}
void drawNodes() {
int nodeNum = 0;
for (int x = 0; x < width; x += nodeDimensions.x) {
for (int y = 0; y < height; y += nodeDimensions.y) {
Node _node = (Node) node.get(nodeNum);
_node.drawMe(new PVector(x - (nodeDimensions.x/2), y - (nodeDimensions.y/2)));
nodeNum++;
}
}
}
And here is the Node class:
class Node {
color[] pixel;
Node() {
pixel = new color[int(nodeDimensions.x * nodeDimensions.y)];
}
void fillPixel(PVector pos, color pixelValue) {
if(int(pos.y * nodeDimensions.y + pos.x) < 38850) pixel[int(pos.y * nodeDimensions.y + pos.x)] = pixelValue;
}
void drawMe(PVector centerPos) {
pushMatrix();
translate(centerPos.x, centerPos.y);
for(int x = 0; x < nodeDimensions.x; x++) {
for(int y = 0; y < nodeDimensions.y; y++) {
stroke(getPixelColor(new PVector(x, y)));
point(x,y);
}
}
popMatrix();
}
color getPixelColor(PVector pos) {
return pixel[int(pos.y * nodeDimensions.x + pos.x)];
}
}
Hopefully my code makes sense. I suspect the issue is in the sendPixels() method of the main class.
I used this this page from the Processing reference as a guide for creating that function, and I'm not sure where my logic is wrong.
Any help would be appreciated, and please let me know if I can clarify something.
According to getPixelColor(), it seems that it uses rows.
So if you have a 5x5 square image then 2x2 would be 7.
To get the index you use this formula:
index = (y - 1) * width + x
Explained this way it's look pretty simple, doesn't it?
Alternatively, you may be able to use getSubimage() on the BufferedImage returned by the getImage method of PImage. There's a related example here.