Suppose I have a two-dimensional grid of pixels (4 by 4 pixels) - and I have an image the size of my sketch that has been cut into 16 parts.
Now I load all 16 parts into an array. I want to map this array onto the 2D grid in turn, so that my overall image is put together again correctly. That is, top left image 0.png and bottom right image 16.png.
I just can't find the formula that allows me to do this. For example, I know that with x+y*width you can run trough all pixels – from top left to bottom right - so I tried that. Without *width it doesn't sit together properly - with x+y*width- ArrayIndexOutOfBoundsException (for sure).
So I thought I needed a 2D array - but with images[x][y] i get a NullPointerException. I attached you an image of what I am trying to create:
This is my code so far – without the 2D Array…
float pixelamount = 4;
float pixelsize;
PImage[] images = new PImage [16];
void setup() {
size(1080, 1080);
pixelsize = width/pixelamount;
for (int i = 0; i < images.length; i++) {
images[i] = loadImage(i + ".png");
}
imageMode(CENTER);
}
void draw() {
background(0);
pushMatrix();
translate(pixelsize/2, pixelsize/2);
for (int x = 0; x < pixelamount; x++) {
for (int y = 0; y < pixelamount; y++) {
pushMatrix();
translate(pixelsize*x, pixelsize*y);
image(images[x+y], 0, 0, pixelsize, pixelsize);
popMatrix();
}
}
popMatrix();
}
As I said – in the line image(images[x+y], 0, 0, pixelsize, pixelsize); I just do not get the math right. Do I need a 2D Array to solve this? Or something totally different?
This should be resolved without 2D array.
If the dimensions of the field are known 4x4, then possibly the loop should run from 0 to 4 something like this:
void draw() {
background(0);
pushMatrix();
translate(pixelsize/2, pixelsize/2);
for (int x = 0; x < 4; x++) {
for (int y = 0; y < 4; y++) {
pushMatrix();
translate(pixelsize * x, pixelsize * y);
image(images[4 * x + y], 0, 0, pixelsize, pixelsize);
popMatrix();
}
}
popMatrix();
}
Alex is correct.
Cyrill, you're on the right track but seem to get confused between 3 ways at looking at your data:
The images array is a 1D array (indices 0 to 15)
The for loop is nested therefore you need to convert 2D indices to 1D. You are right: x+y*width would give you the correct array index, but in this case width is not the full width of your sketch in pixels but the width of the grid (i.e. the number of columns in the 4x4 grid: 4)
You are getting a null pointer pointer because you're trying to access elements in a 1D array as if it's 2D.
Something like this should work:
float pixelamount = 4;
float pixelsize;
PImage[] images = new PImage [16];
void setup() {
size(1080, 1080);
pixelsize = width/pixelamount;
for (int i = 0; i < images.length; i++) {
images[i] = loadImage(i + ".png");
}
//imageMode(CENTER);
}
void draw() {
background(0);
pushMatrix();
translate(pixelsize/2, pixelsize/2);
for (int x = 0; x < pixelamount; x++) {
for (int y = 0; y < pixelamount; y++) {
pushMatrix();
translate(pixelsize*x, pixelsize*y);
image(images[x + y * pixelamount], 0, 0, pixelsize, pixelsize);
popMatrix();
}
}
popMatrix();
}
If you want to loop with a single for loop (instead of a nested for loop) which would match how you store your data you can use this formula to go from 1D index to 2D indices:
x = index % gridColumns
y = index / gridColumns
(Bare in mind these are integers (so in other languages (like Python/JS/etc.) you'd pay attention to the division operation))
Here's a basic example to illustrate this:
size(1080, 1080);
textAlign(CENTER, CENTER);
textFont(createFont("Courier New Bold", 12));
int pixelAmount = 4;
int pixelSize = width/pixelAmount;
int gridColumns = 4;
// iterate once
for(int i = 0; i < 16; i++){
// calculate 2D grid indices
int xIndex = i % gridColumns;
int yIndex = i / gridColumns;
// convert from index to pixel size
int x = xIndex * pixelSize;
int y = yIndex * pixelSize;
// render debug data
String debugText = "1D index:" + i +
"\n2D indices:[" + xIndex + "][" + yIndex + "]" +
"\nx, y pixels::" + x + "," + y;
fill(255);
rect(x, y, pixelSize, pixelSize);
fill(0);
text(debugText, x + pixelSize / 2, y + pixelSize / 2);
}
Here's the same example as the above using a 2D array and nested loops:
size(1080, 1080);
textAlign(CENTER, CENTER);
textFont(createFont("Courier New Bold", 12));
int pixelAmount = 4;
int pixelSize = width/pixelAmount;
int[][] grid = new int[pixelAmount][pixelAmount];
// mimick image loading (storing 1D index)
int index = 0;
for(int y = 0; y < pixelAmount; y++)
for(int x = 0; x < pixelAmount; x++)
grid[x][y] = index++;
// mimick reading 2D array data
for(int y = 0; y < pixelAmount; y++){
for(int x = 0; x < pixelAmount; x++){
int xPixels = x * pixelSize;
int yPixels = y * pixelSize;
// manually copute index
// index = x + y * pixelAmount;
// or retrieve stored index
index = grid[x][y];
String debugText = "1D index:" + index + ".png" +
"\n2D indices:[" + x + "][" + y + "]" +
"\nx, y pixels::" + xPixels + "," + yPixels;
fill(255);
rect(xPixels, yPixels, pixelSize, pixelSize);
fill(0);
text(debugText, xPixels + pixelSize / 2, yPixels + pixelSize / 2);
}
}
My answer is more for the sake of completeness: displaying both 1D/2D ways at looking at the data.
Based on the latest answer – this is my code – working perfectly!
float pixelamount = 4;
float pixelsize;
PImage[] images = new PImage [16];
void setup() {
size(1080, 1080);
pixelsize = width/pixelamount;
for (int i = 0; i < images.length; i++) {
images[i] = loadImage(i + ".png");
}
imageMode(CENTER);
}
void draw() {
background(0);
pushMatrix();
translate(pixelsize/2, pixelsize/2);
for (int x = 0; x < pixelamount; x++) {
for (int y = 0; y < pixelamount; y++) {
pushMatrix();
translate(pixelsize * x, pixelsize * y);
image(images[x + y * int(pixelamount)], 0, 0, pixelsize, pixelsize);
popMatrix();
}
}
popMatrix();
}
Related
I want to pixelize a Image with JavaFx.
My problem is that I only have one written pixel in the end, so that it works for just one time.
i tried a
Here is my code:
Image img = imgView.getImage();
PixelReader pixelReader = img.getPixelReader();
WritableImage wImage = new WritableImage(
(int) img.getWidth(),
(int) img.getHeight());
PixelWriter pixelWriter = wImage.getPixelWriter();
for (int y = 1; y < img.getHeight(); y += 3) {
for (int x = 1; x < img.getWidth(); x += 3) {
Color px = pixelReader.getColor(x, y);
float red = (float) px.getRed();
float green = (float) px.getGreen();
float blue = (float) px.getBlue();
Color all = new Color(red / 3, green / 3, blue / 3, 1);
for (int u = 0; u <= 3; u++) {
for (int i = 0; i <= 3; i++) {
pixelWriter.setColor(u, i, all);
}
}
}
}
Just check the part where you set the color:
for (int u = 0; u <= 3; u++) {
for (int i = 0; i <= 3; i++) {
pixelWriter.setColor(u, i, all);
}
}
As you can see you always set the color of pixel at (0,0) - (3,3).
You need to use
pixelWriter.setColor(x + u, y + i, all);
However, you need to be sure that you won't try to set color of some pixels outside the image. Check the boundaries of loops by x, y, u and i.
I am trying to make Space Invaders in Processing. I am currently working on getting the enemy to move correctly. I have got them to be drawn in the right spot but I haven't gotten them to be moved correctly. Here is my code:
PImage mainPlayer;
PImage enemyPlayer;
float Xspeed = 60;
float Yspeed = 60;
float X;
float Y;
Enemy EnemyPlayer = new Enemy("EnemyPlayerSpaceInvaders.png", 10, 10, 6);
void setup() {
size(1400, 800);
//enemyPlayer = loadImage("EnemyPlayerSpaceInvaders.png");
mainPlayer = loadImage("MainPlayerSpaceInvaders.png");
}
void draw() {
background(0);
Enemy[] enemyPlayer = new Enemy[60];
for (int i = 0; i < 5; i += 1) {
for (int j = 0; j < 11; j += 1) {
enemyPlayer[j *i] = new Enemy("EnemyPlayerSpaceInvaders.png", 50 + j * 100, 5 + 75 * i, 6);
}
}
for (int i = 0; i < 5; i += 1) {
for (int j = 0; j < 11; j += 1) {
if(enemyPlayer[j * i].alive){
enemyPlayer[j * i].Draw();
}
enemyPlayer[j *i].moveAndDraw(6);
}
}
}
class Enemy {
boolean alive = true;
float x;
float y;
float speed;
String playerTexFile;
PImage playerTex;
Enemy(String PlayerTexFile, float X, float Y, float Speed){
x = X;
y = Y;
speed = Speed;
playerTexFile = PlayerTexFile;
}
void Draw(){
playerTex = loadImage(playerTexFile);
image(playerTex, x, y);
}
void moveAndDraw(float Speed){
playerTex = loadImage(playerTexFile);
if(alive){
x += Speed;
if (x >= 1300) {
x = 100;
y += 50;
}
}
}
}
Here is my result:
The Draw function works but what you're seeing that is messing it up is the moveAndDraw() function.
And the enemy drawings aren't moving. I have made this before with c++ SFML but in that there is a very basic getPosition function. The reason I want to get position is that right now I'm having to use inaccurate numbers as the X and Y position and for the enemy to move correctly I need to know exactly what it's position is. I have checked multiple pages on processing.org but none of them helped. I haven't found any getPosition void and all the ones I've seen other people using a void to do this I just haven't been able to get it to work. If there is some code that could get me this to work or just some function I've looked over and even a website page I could look at I'd be open to it. Please tell me anything I can do to get this working.
The issue is that you recreate the enemies in every frame at it's initial position:
void draw() {
background(0);
Enemy[] enemyPlayer = new Enemy[60];
for (int i = 0; i < 5; i += 1) {
for (int j = 0; j < 11; j += 1) {
enemyPlayer[j *i] = new Enemy("EnemyPlayerSpaceInvaders.png", 50 + j * 100, 5 + 75 * i, 6);
}
}
// [...]
}
You've to:
Create a global array of enemies Enemy[] enemyPlayer (and delete PImage enemyPlayer).
Create and initialize the enemies in setup.
Use and move the existing enemies in draw:
Further note, that your loops doesn't do what you expect it to do. Create the enemies in 2 nested loops. If i runs from o to 6 and j from 0 to 10, the the index of an enemy is i*10 + j.
The enemies can be moved in a single loop from 0 to enemyPlayer.length.
//PImage enemyPlayer; <--- DELETE
// global array of enemies
Enemy[] enemyPlayer = new Enemy[60];
// [...]
void setup() {
size(1400, 800);
mainPlayer = loadImage("MainPlayerSpaceInvaders.png");
// create enemies
for (int i = 0; i < 6; i += 1) {
for (int j = 0; j < 10; j += 1) {
enemyPlayer[i*10 + j] = new Enemy("rocket64.png", 50 + j * 100, 5 + 75 * i, 6);
}
}
}
void draw() {
background(0);
// move enemies
for(int i = 0; i < enemyPlayer.length; ++i ) {
if(enemyPlayer[i].alive){
enemyPlayer[i].Draw();
}
enemyPlayer[i].moveAndDraw(6);
}
}
I didn't exactly know how to word the title, but here is my problem:
I'm making the Conway's Game of Life in JavaFX, and I have two 2D arrays in different sizes. One array works as the game-board/grid, and the other works as the pattern I want to insert onto the board. Say the first array is 60x60, and the second is 3x3.
To make the board and the pattern, I use something like this:
//Board
for (int x = 0; x < 60; x++) {
for (int y = 0; y < 60; y++)
board[x][y].setNextState(false);
gc.setFill(Color.LIGHTGRAY);
gc.fillRect(x * 10, y * 10, 9, 9);
}
}
//Pattern
for (int x = 0; x < pattern.length; x++)
for (int y = 0; y < pattern[x].length; y++)
if (pattern[x][y] == 1) {
board[x][y].setNextState(true);
} else {
board[x][y].setNextState(false);
}
gc.setFill(board[x][y].getState() ? Color.rgb(244, 92, 66) : Color.LIGHTGRAY);
gc.fillRect(x * 10, y * 10, 9, 9);
}
}
When done as above, the pattern gets placed top left on the board. But say I want it placed in the middle, I would have to use something like x = 30 and y = 30 in the pattern loop, but that wouldn't work in the if (pattern[x][y] == 1) line. How can I make this work?
I'm terrible at explaining, but I hope I made my question clear!
Apply an offset to the xy position when refering to the board and keep unchanged when refering to the pattern. Be aware that offsetX cannot be > of board width - pattern width, same for Y :
int offsetX = 30;
int offsetY = 30;
for (int x = 0; x < pattern.length; x++)
for (int y = 0; y < pattern[x].length; y++)
if (pattern[x][y] == 1) {
board[x + offsetX][y + offsetY].setNextState(true);
} else {
board[x + offsetX][y + offsetY].setNextState(false);
}
gc.setFill(board[x + offsetX][y + offsetY].getState() ? Color.rgb(244, 92, 66) : Color.LIGHTGRAY);
gc.fillRect((x + offsetX) * 10, (y + offsetY) * 10, 9, 9);
}
}
You can achieve it like this:
for (int x = 0; x < pattern.length; x++) {
for (int y = 0; y < pattern[x].length; y++) {
int bx = x + 30,
by = y + 30;
if (pattern[x][y] == 1) {
board[bx][by].setNextState(true);
} else {
board[bx][by].setNextState(false);
}
...
and so on.
I need to create a simple Java program, that draws a bezier curve pixel by pixel through any amount of points. At the moment, everything seems to be ok except that the curve always ends at x=0 y=0 coordinates.
Screenshot 1
Screenshot 2
I need it to end at the last point. My brain is not quite working today, so I'm looking for some help.
Here is what I have:
private void drawScene(){
precision = Float.parseFloat(this.jTextField4.getText());
//Clears the screen and draws X and Y lines
g.setColor(Color.white);
g.fillRect(0, 0, pWidth, pHeight);
g.setColor(Color.gray);
g.drawLine(0, offsetY, pWidth, offsetY);
g.drawLine(offsetX, 0, offsetX, pHeight);
//Drawing the points
if(pointCount > 0){
for(int i = 0;i<pointCount;i++){
g.setColor(Color.red);
g.drawString(String.valueOf(i+1), points[i].x + offsetX, points[i].y - 6 + offsetY);
g.drawOval(points[i].x + offsetX, points[i].y - 6 + offsetY, 3, 3);
}
}
//Drawing the curve
if(pointCount > 1){
float t = 0;
while(t <= 1){
g.setColor(Color.gray);
this.besierCurvePixel(t);
t += precision;
}
}
}
//Factorial
private static int fact(int n) {
int fact = 1;
for (int i = 1; i <= n; i++) {
fact *= i;
}
return fact;
}
//Bernstein polynomial
private static double bernstein(float t, int n, int i){
return (fact(n) / (fact(i) * fact(n-i))) * Math.pow(1-t, n-i) * Math.pow(t, i);
}
private void besierCurvePixel(float t){
double bPoly[] = new double[pointCount];
for(int i = 0; i < pointCount; i++){
bPoly[i] = bernstein(t, pointCount, i+1);
}
double sumX = 0;
double sumY = 0;
for(int i = 0; i < pointCount; i++){
sumX += bPoly[i] * points[i].x;
sumY += bPoly[i] * points[i].y;
}
int x, y;
x = (int) Math.round(sumX);
y = (int) Math.round(sumY);
g.drawLine(x + offsetX, y + offsetY, x + offsetX, y + offsetY);
}
This is the method for adding the points (pointCount is 0 initially):
points[pointCount] = new Point();
points[pointCount].x = evt.getX() - this.offsetX;
points[pointCount].y = evt.getY() - this.offsetY;
pointCount++;
this.drawScene();
The problem was here
for(int i = 0; i < pointCount; i++){
bPoly[i] = bernstein(t, pointCount, i+1);
}
The second parameter in the bernstein method was incorrect. Basically If I have 3 points, it should be 2 not 3;
bPoly[i] = bernstein(t, pointCount-1, i+1);
Where does "pointcount" get set (and to what)?
Have you tried stepping through your code to see why it continues after reaching the last point?
Is it possible that you are stepping through a loop 1 extra time, which is why the last point would have a destination set to (0,0)?
Could you set the number of steps for the app to make to each point?
Hopefully I am bringing up points to help you find your answer
*Edit: If I had to guess- you are accidentally adding an additional point of (0,0) to points[]; Here is where I am seeing it go to (0,0) after the last point:
for(int i = 0; i < pointCount; i++){
sumX += bPoly[i] * **points[i]**.x;
sumY += bPoly[i] * **points[i]**.y;
}
Edit: Glad you were able to fix it, and hopefully i helped with finding that issue. Best of luck in the future!
I've been trying to write some code to generate a maze of any given size (made up of 32x32 tiles), but have run into an odd issue with the rendering code, in that only square mazes will be textured correctly.
I have a single .png file with all possible wall textures, and the floor texture, and dependent on the placement of walls around the currently selected wall during the texturing methods, the correct part of this .png should be selected to make walls blend together nicely. However, as mentioned before, this only works with square mazes (note, rendering is being done with vertex buffer objexts).
Here's the code for generating the maze (currently, it just randomly fills the space with walls, I plan to adjust this part to actually generate a solvable maze once I fix this issue):
public void run() { // The maze is part of a thread called World, which runs alongside a Renderer thread
mazeWidth = 20;
mazeHeight = 15;
maze = new byte[mazeWidth][mazeHeight];
}
public static void setUpMaze() {
for (int x = 0; x < mazeWidth; x++) {
for (int y = 0; y < mazeHeight; y++) {
// TODO Make proper maze generation code
maze[x][y] = (byte) mazeGenerator.nextInt(2);
}
}
}
The code to generate the vertices for the triangles to be drawn:
private float[] getMazeGrid() { // The 12 comes from the number of coordinates needed to define a square/single tile - 2 triangles, 6 vertices, 2 coordinates per vertex
float[] mazeGrid = new float[12 * World.mazeWidth * World.mazeHeight];
int yOffset = 0;
int xOffset = 0;
// The if statements adjust the minimum x/y coordinates for each tile, the for iterates through the tiles
for (int i = 0; i < World.mazeWidth * World.mazeHeight; i++) {
if (i % World.mazeWidth == 0) {
xOffset = 0;
} else {
xOffset += 32;
}
if (i % World.mazeWidth == 0 && i != 0) {
yOffset += 32;
}
// The code below defines one square of the grid
mazeGrid[12 * i + 0] = xOffset;
mazeGrid[12 * i + 1] = yOffset;
mazeGrid[12 * i + 2] = xOffset;
mazeGrid[12 * i + 3] = yOffset + 32;
mazeGrid[12 * i + 4] = xOffset + 32;
mazeGrid[12 * i + 5] = yOffset + 32;
mazeGrid[12 * i + 6] = xOffset + 32;
mazeGrid[12 * i + 7] = yOffset + 32;
mazeGrid[12 * i + 8] = xOffset + 32;
mazeGrid[12 * i + 9] = yOffset;
mazeGrid[12 * i + 10] = xOffset;
mazeGrid[12 * i + 11] = yOffset;
}
return mazeGrid;
}
And the code for determining which part of the texture should be used:
private float[] getTexCoords(int x, int y) {
texNumKey = 0;
if (World.maze[x][y] == 1) {
if (y > 0) {
if (World.maze[x][y - 1] == 1) texNumKey += 1;
}
if (x > 0) {
if (World.maze[x - 1][y] == 1) texNumKey += 2;
}
if (x < World.mazeWidth - 1) {
if (World.maze[x + 1][y] == 1) texNumKey += 4;
}
if (y < World.mazeHeight - 1) {
if (World.maze[x][y + 1] == 1) texNumKey += 8;
}
} else if (World.maze[x][y] == 0) {
texNumKey = 16;
}
return texMap.get(texNumKey);
}
NB: texMap is a HashMap which contains float arrays with texture coordinates to be used by the texture coordinate buffer, keyed with a number from 0-16. The code above iterates through the grid and checks the spaces around the current tile, and selects the correct texture coordinates for that wall type.
Finally, the vertex buffer object code - setting up the VBOs:
public void setUp() {
initialiseTextureMap();
vertexData = BufferUtils.createFloatBuffer(12 * World.mazeWidth * World.mazeHeight);
vertexData.put(getMazeGrid());
vertexData.flip();
textureData = BufferUtils.createFloatBuffer(12 * World.mazeWidth * World.mazeHeight);
for (int x = 0; x < World.mazeWidth; x++) {
for (int y = 0; y < World.mazeHeight; y++) {
textureData.put(getTexCoords(x, y));
}
}
textureData.flip();
vboVertexHandle = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
vboTextureCoordHandle = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboTextureCoordHandle);
glBufferData(GL_ARRAY_BUFFER, textureData, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
And drawing the VBOs:
public void draw() { // Draws the entity
glBindTexture(GL_TEXTURE_2D, loadTexture(this.textureKey).getTextureID());
glBindBuffer(GL_ARRAY_BUFFER, this.vboVertexHandle);
glVertexPointer(2, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, this.vboTextureCoordHandle);
glTexCoordPointer(2, GL_FLOAT, 0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 12 * World.mazeWidth * World.mazeHeight);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Most unexplained variable names should be fairly self explanatory, they are defined in the abstract superclass, or in the "Maze" class constructor.
So, for clarity, the above code works perfectly when I set the values of mazeWidth, and mazeHeight to the same value as one another, but if they are different, then textures are not assigned to tiles correctly - here is are examples of the code working, and failing, the top is a 10 x 10 maze, and the bottom 10 x 11: Maze Examples
EDIT: After switching the x and y for loops in the texture coordinate buffer setup:
Maze Example 2
If you need any other information/I've missed something important out, let me know.
Your problem is the combination of the for loops for x and y and the push method. You were looping down columns first, rather than across rows first. - the put assumes row first looping.
This will fix the problem quickly:
for (int y = 0; y < World.mazeHeight; y++) {
for (int x = 0; x < World.mazeWidth; x++) {
textureData.put(getTexCoords(x, y));
}
}
Your texture selection will have to be updated too, mirrored on the diagonal. For example, if you were selecting a texture with the path going South, it will now be drawn with the path going East.
in setup
try changing
for (int x = 0; x < World.mazeWidth; x++) {
for (int y = 0; y < World.mazeHeight; y++) {
textureData.put(getTexCoords(x, y));
}
}
to
for (int y = 0; y < World.mazeHeight; y++) {
for (int x = 0; x < World.mazeWidth; x++) {
textureData.put(getTexCoords(x, y));
}
}