I am working on a game in Android and I have Bitmap objects being drawn. They are rotated using the Matrix class. The problem I am having is being able to access the pixels of the rotated Bitmap. When I access them after rotation the pixels are still representing the unrotated version. Does anyone have any ideas? Maybe I can rotate the pixel array based on an arbitrary number of degrees? I have seen solutions that create a new bitmaps on the fly based on the newly rotated images, but I can't do this because of performance issues. Thanks!
Here is how I draw the bitmap currently (The bitmap is drawn using the matrix as its position)...
canvas.drawBitmap(bitmapPlayer, positionMatrix, null);
Here is how I create the positionMatrix variable...
Matrix m = new Matrix();
m.postRotate(degrees, bitmapCenterX, bitmapCenterY);
positionMatrix.set(m);
Here is how I access the pixels currently (This accesses the pixel array I created below)...
getPixel(x, y);
Here is how I build the pixel array that I have tried to modify...
// Build pixel 2d array
pixelData = new int[bitmapPlayer.getWidth()][bitmapPlayer.getHeight()];
for(int x=0; x<bitmapPlayer.getWidth(); x++) {
for(int y=0; y<bitmapPlayer.getHeight(); y++) {
pixelData[x][y] = bitmapPlayer.getPixel(x, y);
}
}
Here is how I have tried to manipulate the array...
public void rotatePixels(int degrees) {
int[][] newPixelData = new int[bitmapPlayer.getWidth()][bitmapPlayer.getHeight()];
double cos = Math.cos(Math.toRadians(degrees));
double sin = Math.sin(Math.toRadians(degrees));
for(int x=0; x<bitmapPlayer.getWidth(); x++) {
for(int y=0; y<bitmapPlayer.getHeight(); y++) {
if(pixelData[x][y] != 0) {
int currentX = x + getTopLeftX();
int currentY = y + getTopLeftY();
int nextX = (int)((currentX * cos) - (currentY * sin));
int nextY = (int)((currentX * sin) + (currentY * cos));
if(nextX >= 0 && nextX < bitmapPlayer.getWidth() && nextY >= 0 && nextY < bitmapPlayer.getHeight()) {
newPixelData[nextX][nextY] = 1;
}
}
}
}
this.pixelData = newPixelData;
}
Have you tried:
Matrix mat = new Matrix();
mat.postRotate(45);
Bitmap newBm = Bitmap.createBitmap(bitmapPlayer, 0, 0, bitmapPlayer.width(), bitmapPlayer.height(), mat, true);
and then accessing the pixels of the new Bitmap?
I think right now you just draw a rotated bitmap on the canvas, you're not actually rotating the bitmap
EDIT:
The way you do it in your orinigal post won't work because you start at (0,0) and work your way down the left column... Depending on the rotation degree you'd have to start at a different place and then go down a different column. i.e. a small ctrclkwise rotation and you start at the top right index.
Bitmap bm = someImage;
int startx, starty;
int degree = rotateDegree % 360; // counterclockwise
if (degree >= 0 && degree < 90) {
startx = bm.getWidth();
starty = 0;
} else if (degree >= 90 && degree < 180) {
startx = bm.getWidth();
starty = bm.getWidth();
} else if (degree >= 180 && degree < 270) {
startx = 0();
starty = bm.getWidth();
} else {
startx = 0;
starty = 0;
}
And then try traversing the image that way, starting at (startx, starty)
and adding or subtracting the correct angle (you can use some boolean to keep track
of whether you're adding or subtracting from x or y respectively). Let me know if this works,
and if it doesn't can you be a little more specific about where you think the problem might be?
Related
In my game, my entities turn like a piece of paper, as shown here at half-speed: https://imgur.com/a/u2suen6
I want to give the entities a bit of thickness when they turn, making them more cardboard-thin than paper-thin.
I thought about using a Pixmap to detect and extend the edge pixels and give the image some Three-Dimensionality. I also considered duplicating the image along the x-axis to give the same effect. Of the two ideas, the Pixmap holds out the most promise in my mind. However, I'm wondering if there's a better solution.
I'm using a GLSL shader to give the entities highlights and shadows while turning, as you saw in the gif. I think that with the right knowledge, I could achieve what I'm going for using the same shader program.
My shader looks like this:
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform vec2 u_resolution;
uniform vec3 color;
void main()
{
vec4 col = vec4(color, 0.0);
gl_FragColor = texture2D(u_texture, v_texCoords) * v_color + col;
}
I think that one might be able to make calculations based on the uniform vec3 color that I pass it (with its values ranging from 0, 0, 0 to 1, 1, 1. 1's being highlight and 0's being shadow). Unfortunately, I don't have the understanding of shaders to do so.
If any of you have the know-how, could you steer me in the right direction? Or let me know if I should just stick to the Pixmap idea.
Edit: I'm trying to stay away from using a 3D model because I'm 6.4k lines of code deep using a 2d Orthographic Camera.
Edit 2: I figured that the reflection shader wouldn't look good if I tried making the sprite look 3D. I scrapped the shader, went with the Pixmap idea, and plan on implementing shadows and reflections to the pixmap without any shader. Though it looks good so far without reflections.
I ended up going with my pixmap idea. I want to share my code so that others can know how I got 2D thickness to work.
Please note that in the following code:
dir is a floating point value in the range -1.0 to 1.0. It tells the program where the sprite is in its turn. -1 means facing fully left. 1 meaning right. 0 means that it's 'facing' the camera.
right is a boolean that tells the program which direction the entity is turning. true means that the entity is turning from left to right. false means from right to left.
The Code:
private Texture getTurningImage(TextureRegion input, int thickness)
{
if(Math.abs(dir) < 0.1)
dir = (right ? 1 : -1) * 0.1f;
Texture texture = input.getTexture();
if (!texture.getTextureData().isPrepared())
{
texture.getTextureData().prepare();
}
Pixmap pixmap = texture.getTextureData().consumePixmap();
Pixmap p = new Pixmap(64, 64, Pixmap.Format.RGBA8888);
p.setFilter(Pixmap.Filter.NearestNeighbour);
Pixmap texCopy = new Pixmap(input.getRegionWidth(), input.getRegionHeight(), Pixmap.Format.RGBA8888);
// getting a texture out of the input region. I can't use input.getTexture()
// because it's an animated sprite sheet
for (int x = 0; x < input.getRegionWidth(); x++)
{
for (int y = 0; y < input.getRegionHeight(); y++)
{
int colorInt = pixmap.getPixel(input.getRegionX() + x, input.getRegionY() + y);
Color c = new Color(colorInt);
colorInt = Color.rgba8888(c);
texCopy.drawPixel(x, y, colorInt);
}
}
pixmap.dispose();
float offsetVal = Math.round(thickness/2.0) * (float) -Math.cos((dir * Math.PI)/2);
if(offsetVal > -1.23/Math.pow(10, 16))
{
offsetVal = 0;
}
// generate the pixel colors we'll use for the side view
Pixmap sideProfile = new Pixmap(1, 64, Pixmap.Format.RGBA8888);
for (int y = 0; y < texCopy.getHeight(); y++)
{
for (int x = 0; x < texCopy.getWidth(); x++)
{
int colorInt = texCopy.getPixel(x, y);
if(new Color(colorInt).a != 0 && new Color(texCopy.getPixel(x + 1, y)).a == 0)
{
Color c = new Color(colorInt);
c.mul(.8f); // darken the color
c.a = 1;
colorInt = Color.rgba8888(c);
sideProfile.drawPixel(0, y, colorInt);
continue;
}
}
}
// drawing the bottom layer
p.drawPixmap(texCopy, 0, 0, 64, 64, (int) (Math.round(-offsetVal) + (64 - texCopy.getWidth()*Math.abs(dir))/2), 0, (int)(64*Math.abs(dir)), 64);
// drawing the middle (connecting) layer
// based on the edge pixels of the bottom layer, then translated to be in the middle
for (int y = 0; y < p.getHeight(); y++)
{
int colorInt = sideProfile.getPixel(0, y);
for (int x = 0; x < p.getWidth(); x++)
{
if(new Color(p.getPixel(x, y)).a != 0 && new Color(p.getPixel(x + 1, y)).a == 0)
{
for(int i = 0; i <= 2 * Math.round(Math.abs(offsetVal)); i++) // the for the length between the top and bottom
{
p.drawPixel(x + i - 2 * (int)Math.round(Math.abs(offsetVal)), y, colorInt);
}
}
}
}
// drawing the top layer
p.drawPixmap(texCopy, 0, 0, 64, 64, (int) (Math.round(offsetVal) + (64 - texCopy.getWidth()*Math.abs(dir))/2), 0, (int)(64*Math.abs(dir)), 64);
// flip if facing left
if(dir < 0)
{
p = flipPixmap(p);
}
return new Texture(p);
}
My flipPixmap method looks like this (stolen from stack overflow):
private Pixmap flipPixmap(Pixmap src)
{
final int width = src.getWidth();
final int height = src.getHeight();
Pixmap flipped = new Pixmap(width, height, src.getFormat());
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
flipped.drawPixel(x, y, src.getPixel(width - x - 1, y));
}
}
return flipped;
}
Here's the result :D https://imgur.com/a/wGeHg9D
I am trying to make a program where there are lines in a grid pointing towards the mouse like magnets. I am a beginner in Processing, can someone point me towards a tutorial on how to do that or give me some code and explain what it does?
int x1 = 0;
int x2 = 0;
int y1 = 0;
int y2 = 0;
void setup() {
size(200, 200);
}
void draw() {
background(255, 255, 0);
x1 = (mouseX + 100) / 2;
y1 = (mouseY + 100) / 2;
x2 = -1 * x1 + 200;
y2 = -1 * y1 + 200;
line(x1, y1, x2, y2);
}
There's plenty of solutions for this project. One of the easiest is to use Processing's PVector class.
The PVector class can be used for two or three dimensional vectors. A vector is an entity that has both magnitude and direction. The PVector class, however, stores the components of the vector (x,y for 2D, and x,y,z for 3D). The magnitude and direction are calculated from the components and can be accessed via the methods mag() and heading().
A two dimensional vector in Processing is defined through x and y components:
PVector v = new PVector(xComponent, yComponent);
With some mathematical formulae, you can determine magnitude and direction using the x- and y-components. But we don't need to determine these.
Below, I've attached completed solution code. Most of it should make sense to you. But it's worth understanding what is going on with PVector.
A nested for loop within void draw() contains x and y variables that represent the coordinates of each grid vertex.
We first define PVector v as a vector given by an x-component of mouseX - x, or the difference between the x-positions of the mouse and each grid point. Similarly, the y-component given by mouseY - y has the same difference.
Creating a variable PVector u initialized from v.setMag(15) holds a PVector that has the same direction as v, but with a length of just 15.
Now to draw the lines. Vectors represent an offset, not a position (in this case), so drawing a line from a grid point to an offset of a grid point is key.
Hence line(x, y, x + u.x, y + u.y), where u.x and u.y are the x- and y-components of the vector u.
void setup() {
size(600, 600); // Set the size of the canvas to 600x600.
}
void draw() {
background(255);
stroke(200); // Set the stroke color to black
int distVertLine = width / 10; // This variable defines the distance between each subsequent vertical line.
for(int i = 0; i < width; i += distVertLine) {
line(i, 0, i, height); // Draw a line at x=i starting at the top of the canvas (y=0) and going to the bottom (y=height)
}
int distHorizLine = height / 10; // This variable defines the distance between each subsequent vertical line.
for(int i = 0; i < width; i += distHorizLine) {
line(0, i, width, i); // Draw a line at y=i starting at the left of the canvas (x=0) and going to the right (x=width)
}
stroke(0); // Set the stroke to black.
// Use a nested for loop to iterate through all grid vertices.
for(int x = 0; x <= width; x += width/10) {
for(int y = 0; y <= height; y += height/10) {
PVector v = new PVector(mouseX - x, mouseY - y); // Define a vector that points in the direction of the mouse from each grid point.
PVector u = v.setMag(15); // Make the vector have a length of 15 units.
line(x, y, x + u.x, y + u.y); // Draw a line from the grid vertex to the terminal point given by the vector.
}
}
}
The answer already given by Ben Myers is excellent! The code below has a few small modifications:
the two for loops for the grid lines have been combined (since width and height are equal);
the construction of the vector is combined with setting the magnitude;
some minor changes to colors and comments.
Modified code:
void setup() {
// Set the size of the canvas to 600x600 pixels.
size(600, 600);
}
void draw() {
// There are 10x10 grid cells that each have a size of 60x60 pixels.
int gridSize = width / 10;
// Set the background color to anthracite and the stroke color to orange.
background(56, 62, 66);
stroke(235, 113, 52);
// Draw vertical and horizontal grid lines.
for (int lineIndex = 0; lineIndex < gridSize; lineIndex++) {
line(lineIndex * gridSize, 0, lineIndex * gridSize, height);
line(0, lineIndex * gridSize, width, lineIndex * gridSize);
}
// Set the stroke color to blue.
stroke(0, 139, 225);
// Use a nested for loop to iterate through all grid cells.
for (int x = 0; x <= width; x += gridSize) {
for (int y = 0; y <= height; y += gridSize) {
// Define a vector that points in the direction of the mouse from
// each grid point and set the vector length to 15 units.
PVector vector = new PVector(mouseX - x, mouseY - y).setMag(15);
// Draw a line from the grid point to the end point using the vector.
line(x, y, x + vector.x, y + vector.y);
}
}
}
I'm currently working on a Top-Down-Shooter and having some issues with collision.
My world is made of tiles (64x64). The tiles and the entities are rectangles. The player moves with a speed of e.g 2.74 (and not in pixels for smoother movement). But when it comes to the collision between the player (an entity) and a wall i have some issues. To check if there is a collision i take the current position of my player and his movement speed to calculate where his next position would be and if there is any collision. But i check every pixel on the way, so i cant skip an obstacle even if the movement speed is very high. Let's just say the players current position is X:200 Y:200 and he moves 2.74 Pixels a tick in the x direction. My game now checks if there is any collision at X:201 Y:200, X:202 Y:200 or X:202.74 Y:200 and if not moves the player to that position. If I now try to move the player further in the x direction and there is a wall 0.26 Pixels away the player wont move and leave a tiny gap. I tried to calculate the distance between player and wall and add this amount to the players position but for that I need to know which side of the wall the player hits. Also I want the player to be able to move up and down when the wall he hits is in front of him and the other way around.
Here is my collision method (in Java):
public static boolean collision(float ex, float ey, int width, int height) { // ex, ey would be the next position of the player
if (ex < 0 || ex + width > worldWidth || ey < 0 || ey + height > worldHeight) return true; // checks if this position is in the world
int firstTileX = (int) (ex / Tile.TILE_SIZE); // calculates tiles he could possible collide width
int firstTileY = (int) (ey / Tile.TILE_SIZE);
int lastTileX = (int) ((ex + width - 1) / Tile.TILE_SIZE);
int lastTileY = (int) ((ey + height - 1) / Tile.TILE_SIZE);
for (int y = firstTileY; y <= lastTileY; y++) {
if (y < 0) continue; // checks for out of bounds
if (y >= worldTileHeight) break;
for (int x = firstTileX; x <= lastTileX; x++) {
if (x < 0) continue;
if (x >= worldTileWidth) break;
if (tiles[y][x].solid) return true; // if the tile is solid -> collision found
}
}
return false; // no collision found
}
And my movement method:
public void move(float xa, float ya) {
float nx, ny;
while (xa != 0 || ya != 0) {
nx = x;
ny = y;
if (xa != 0) {
if (Math.abs(xa) > 1) { // if the x-speed is greater than 1
nx = x + MathUtil.abs(xa); // returns -1 for negative numbers and 1 for positiv
xa -= MathUtil.abs(xa);
} else { // less than 1
nx = x + xa;
xa = 0;
}
}
if (ya != 0) { // same here
if (Math.abs(ya) > 1) {
ny = y + MathUtil.abs(ya);
ya -= MathUtil.abs(ya);
} else {
ny = y + ya;
ya = 0;
}
}
if (!Level.collision(nx, ny, width, height)) setPosition(nx, ny); // checks if there is an collision and sets the new position if not
else if (!Level.collision(nx, y, width, height)) x = nx; // if there was a collision check if the player can walk in x direction
else if (!Level.collision(x, ny, width, height)) y = ny; // or in y direction
}
}
My problem is the pretty much the same as CoderMusgrove's problem in his post (Pixel-perfect collision and doubles):
Summary & Question
I have a problem where if the speed of an entity isgreater thanthe distance from the tile it is going into, it will leave at least a pixel in between itself and the tile, and I really don't like this. What kind of algorithm could I use that will find the tiniest difference between the entity and the tile?
If you need any additional information, I will be glad to add it.
Thanks for your help!
Easily resolvable by changing your interpretation.
You are retaining a fractional position for the purpose of fine grained speed. Ignore the fraction for the purpose of collision detection and display (if you were to do sub-pixel rendering, do the collision on the subpixel rendering accurarcy level).
int screenX = (int) Math.round(objX);
int screenY = (int) Math.round(objY);
// rendering and collision detection based on rounded position
I have an int[] representing a small bitmap that I want to copy into another int[] representing a larger bitmap. My code thus far looks like this:
private int[] copyToOffsetCentered(int[] src,
Rectangle srcDim, int[] dest, Rectangle destDim, int dx, int dy)
{
int startx = dx - srcDim.width / 2;
int endx = startx + srcDim.width;
int starty = dy - srcDim.height / 2;
int endy = starty + srcDim.height;
for (int x = Math.max(startx, 0); x < Math.min(endx, destDim.width); x++)
{
for (int y = Math.max(starty, 0); y < Math.min(endy, destDim.height); y++)
{
dest[y*destDim.width + x] = src[???];
}
}
return dest;
}
The idea is that the source image array could be clipped when copied to the target array if the offset is close enough to the edge of the image. As an example, if I passed in a 2x2 source image
src=[1,2,3,4]
and a 4x4 dest image
dest=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
with dx=0 and dy=1, I would expect the return array to be
dest=[2,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0]
This is due to the "edges" of the source being clipped based on the target center point.
I know that the solution is most likely pretty simple, but I am having a hard time wrapping my brain around what the math should look like to figure out the proper index I should be using for the source array inside the loops. Any help is much appreciated.
dx and dy are the destination coordinates of the src image at the center. The row of the image is index/Dimension.width and the column of the image is index%Dimension.width.
While you're iterating the starting coordinate in the source image will be.
int xSrc = x - start_x;
int ySrc = y - start_y;
int srcIndex = ySrc*srcDim.getWidth() + xSrc;
I have an image and I am extracting a subimage to feed to my neural network. I am attempting to calculate the average output of all the subimages in that same neighborhood.
So if I have an original image (m X n pixels) and I found a subimage at (sub_x, sub_y) with size (sub_width and sub_height), I also need to extract subimages with the same size (sub_width and sub_height) at (sub_x + m, sub_y + n) where m and n go from 1 - 3.
I already have a working solution:
for (int subX = (x-3); subX < (x+4); subX++)
for (int subY = (y-3); subY < (y+4); subY++)
if ( (subX > 0) && (subY > 0) )
if ( ((subX + width) < img.getWidth()) && ((subY + height) < img.getHeight()) ){
counter++;
testingImage = img.getSubimage(subX, subY, width, height);
}
x,y, width, and height are all integers of the original subimage I found.
img is the original full sized BufferedImage.
I'm not too happy about the performance though. Is there a faster/smarter way to do this?
Here's one simple thing you can do: get rid of those conditions inside loops. Calculate your ranges first, just once and then run the loops without range checks.
int subXStart = max(x-3, 0);
int subYStart = max(y-3, 0);
int subXEnd = min(x+4, img.getWidth() - width);
int subYEnd = min(y+4, img.getHeight() - height);
for (int subX = subXStart; subX < subXEnd; subX++) {
for (int subY = subYStart; subY < subYEnd; subY++) {
counter++;
testingImage = img.getSubimage(subX, subY, width, height);
// run your neural net
}
}
You can also try switching the order of loops. The order that matches the memory ordering should be considerably faster.