Java Image manipulation curve-bend image - java

I have two images
the first image of a cup ->
the second image is the label I want to wrap around the cup (this image is just an example, the actual app works with dynamic images).
I'm planning to clip the label image several time and place it on the cup, therefor creating a 36 jpegs from different angles, I'll use a 360 viewer to display the result on my website.
so far I've managed to take a trapezoid area of the lable image to create this
(code is here)
The last step is to create this (used GIMP for this - using distort -> curve-bending)
Thank you

What comes in my mind, is a technique called uv-mapping.
See https://en.wikipedia.org/wiki/UV_mapping as a starting point.
See also J3D - How to use a UV mapping of an object to convert 2D coordinates to 3D ones

I've managed to do some work.
At first, I use ImageMagick , it took 50 sec to generate 36 images... way to long for me.
So I turned to Java, I've managed to do the simple transformation using a simple pixel by pixel and apply a Sin function
for(int y = 0; y < bounds.height ; y++)
{
for (int x = 0; x < bounds.width ; x++) {
int pixelRGB = img.getRGB(x, y);
int newX = x;
int newY = ((int)(Math.sin(x*Math.PI/bounds.width) * 40 )) + y ;
newImg.setRGB(newX, newY, pixelRGB);
}
}
main line is -> (Math.sin(x * Math.PI / bounds.width) * 40) + y
Math.PI / bounds.width -> make sure I get only the positive values for Y from the Sin function.
bounds.width - is the original image width, for the Sin function it tells to start from 0,0 go to positive Y values and intersec with th X axis back at bounds.width.
40 -> is the level of curvature you'll want to use
y - just a Y axis offset. if not applied you'll get a single line in the target image

Related

LibGdx - Make sprite follow a line path

I have an isometric map drawn.
I take the current position of my sprite and the target position of where my sprite should be at after the move:
// region is my TextureRegion.
int x1 = getIsometricX(1,1,region);
int x2 = getIsometricX(1,2,region);
int y1= getIsometricY(1,1,region);
int y2 = getIsometricY(1,2,region);
And then I draw a simple line using ShapeRenderer to see if the local/target points are correctly set, and the rectangle so you can see where the sprite rendering starts.
renderer.setProjectionMatrix(camera.combined);
renderer.begin(ShapeRenderer.ShapeType.Line);
renderer.setColor(Color.RED);
renderer.line(x1 + location.getOffsetx(), y1 + location.getOffsety(), x2 + location.getOffsetx(), y2 + location.getOffsety());
renderer.rect(x1 + location.getOffsetx(), y1 + location.getOffsety(), region.getRegionWidth(), region.getRegionHeight());
renderer.end();
Every sprite of mine has set offsetX and offsetY to adjust its location on the isometric tile, because every sprite is different.
Output looks like this:
what you can see here, is the starting point of where the sprite starts to draw (you see that offsets adjusted it so the sprite looks like its on the 1,1 tile.
and you can see the line which starts at the starting draw point of the sprite, and ends at the target draw point of the sprite.
Now my question is, how can I make that sprite move on that line's path, so it will look like the ship is moving forward?
So the main concept of the question is.. How can you make a sprite move in a straight line, from local point to target point?
Some functions you might need to see:
public int getIsometricX(int x, int y, TextureRegion region) {
return (x * GameTile.TILE_WIDTH / 2) - (y * GameTile.TILE_WIDTH / 2) - (region.getRegionWidth() / 2);
}
public int getIsometricY(int x, int y, TextureRegion region) {
return (x * GameTile.TILE_HEIGHT / 2) + (y * GameTile.TILE_HEIGHT / 2) - (region.getRegionHeight() / 2);
}
Tiles are drawn using the same method, just with Tile's texture.
I would like to answer myself this question, because other people might have the same issue and just over-complicate it like me.
If you want to perform any move on your isometric map, do not follow my misunderstandings and calculate it on the isometric coordinates.
You have to calculate it on your flat screen matrix coordinates, and then convert it to isometric coordinates.
For example, I want to move up like this line, all I need to do is this:
ship.y += 0.1f // when it reaches 1, then it will be at 0, 1
So you know that you want to be at 0,1 on your non-isometric map.
So you do this increment, and then for last, you have to convert it to isometric coordinates before drawing:
float x = (ship.x * GameTile.TILE_WIDTH / 2) - (ship.y * GameTile.TILE_WIDTH / 2) - (textyure.getWidth() / 2);
float y = (ship.x * GameTile.TILE_HEIGHT / 2) + (ship.y * GameTile.TILE_HEIGHT / 2) - (texture.getHeight() / 2);
And that will draw it on the isometric map, exactly like on your screen-coordinates, but on an isometric format.
for(float x : new float[targetx - currentx]) {ship.setPosition(ship.getX + x, ship.getY); }
And you would do the same for y.
Edit:
I guess this is wrong since you're calling this every frame I assume.
Instead you would keep track of your distance traveled between target and ship and you would increment sip position by 1 each time to x and y until it reached the distance.

Get Isometric Tile Under Mouse

Ok, so I have my isometric map where the width of each tile is 64 and the height is 32.
This is the equation I came up with to place the tiles
xPos = (this.getPos().getX() - this.getPos().getY()) * (64 / 2) - Main.gameWindow.getCamera().getxOffset().intValue();
xPos -= 32;
yPos = (this.getPos().getX() + this.getPos().getY()) * (32 / 2) - Main.gameWindow.getCamera().getyOffset().intValue();
I subtract xPos by 32 to make up for the fact that the origin point of the tile is in the far left corner.
What I've been trying to do is find the tile that is beneath my mouse.
So first I tried simply reversing the equation (I was sure it would work)
And this is the code I ended up with when I reversed it.
int yMouseTile = ( (cursorY / (32 / 2) - (cursorX / 32)) / 2 );
int xMouseTile = ( (cursorX / 32) + yMouseTile);
This only sort of works. But as it turns out, this code actually treats each tile as if it were a square, not a diamond.
The next weird part is that when my mouse passes over the center of the tile, the tile changes. So what should happen, is that my mouse should go over the edge of the tile, and then it changes to the next one. But instead, it acts as if the center of tile is actually the edge.
But really, all I want is the equation that will cause my mouse to work like this
http://www.tonypa.pri.ee/tbw/tut18.html
On that link, click the "Click Here to Start" Button, and watch how the mouse interacts with the tiles. That is my goal :), thanks
P.S. I've tried a myriad of different equations, many of which have the exact same result as the equation I have above
Refactor your variable names.
int TILE_WIDTH = 64;
int TILE_HEIGHT = TILE_WIDTH / 2;
int xMap = this.getPos().getX();
int yMap = this.getPos().getY();
int xScreenCameraOffset = Main.gameWindow.getCamera().getxOffset().intValue();
int yScreenCameraOffset = Main.gameWindow.getCamera().getyOffset().intValue();
xScreen = (xMap - yMap) * (TILE_WIDTH / 2) - yScreenCameraOffset;
yScreen = (xMap + yMap) * (TILE_HEIGHT / 2) - yScreenCameraOffset;
This might seem excessive, but in my opinion is way easier to read and reason about. According to this tutorial If you try to derive The reverse equation you would get:
xMouseTile = (xCursor / TILE_WIDTH / 2 + yCursor / TILE_HEIGHT / 2) / 2;
yMouseTile = (yCursor / TILE_HEIGHT / 2 - xCursor / TILE_WIDTH / 2) / 2;
This doesn't take into account the camera offset.

Tracking a specific points' x & y position on an image during rotation in Java

I'm trying to create rope physics for a 2D game, so as a starting point I have a small rotating image and I need to add another piece of rope to the end of it. Unfortunately I'm having trouble trying to track the bottom part of the image as the rotation occurs at the top of it. I've managed to track the (0,0) coordinate of the image using the following code but I need to be able to track point (32,57). This is what I have so far:
xr = xm + (xPos - xm) * Math.cos(a) - (yPos - ym) * Math.sin(a);
yr = ym + (xPos - xm) * Math.sin(a) + (yPos - ym) * Math.cos(a);
Any help is appreciated!
EDIT:
So hey, I got it working =D Using polar coordinates turned out to be a lot easier then whatever I had going on before.
The top 2 variables are constant and stay the same:
theta0 = Math.atan2(y, x);
r = 25;
theta = theta0 + a;
xr = (r * Math.cos(theta)) + xm;
yr = (r * Math.sin(theta)) + ym;
xm and ym are the positions of my image.
Use polar coordinates. Set your origin at the point of rotation of your image, and pick your favorite angular reference (say 0 degrees is directly to the right, and positive rotations go counterclockwise from there).
Compute the polar coordinates of your desired point (32, 57) relative to this coordinate system. Say the answer is (r, theta).
Now, the only thing that's changing as you spin the image around is the value of theta. Now you can go back to x-y coordinates with your new value of theta.
Hope this helps.

How do I draw thick lines with closely spaced points properly with Java2D graphics?

I'm trying to draw maps using Java2D. When my map is zoomed out my roads are full of drawing artefacts. This is a small part of the screen when drawing a complete US state:
This is a similar section of the road when zoomed closer in:
The line style used is a solid blue line with width scaled to be equivalent to 2 pixels.
I've tried various rendering hints and line joining rules and nothing seems to help.
I'm using Open JDK 1.7 on a Mac running the OS/X 10.8 and this is also reproducible on a Linux machine with a Sun JDK 1.6.
All shapes and transforms are double precision as far as possible with Java2D.
The geometry of the line has many closely spaced points and I suspect that the cause of the drawing artefacts is that the renderer is getting confused by consecutive points that are closer than a single pixel.
Is there a way to improve the appearance of the zoomed out shapes without thinning the points?
Edit
The drawing artefacts are at the points where separate line segments meet, so the missing pixels are something to do with the line caps (ends) not meeting, even when the end points are identical. This image shows the meeting point between two line segments. I have highlighted each line segment in a 7 pixel scaled line style (XOR-ed with white) but if you look very closely you can still see part of the original blue line (this is due to the rounded caps overlapping and the XOR draw mode.) At ordinary scales the ends seem to overlap, but when zoomed out and back in ordinary paint mode there is a broken line effect.
One workaround would be to join all the contiguous line segments together before drawing them, but I would still like to know the real cause of the drawing artefacts.
I am unable to recreate the situation you have using the OS X 1.6 JDK, but I still have some suggestions for you.
If you are just using this to outline states, consider using the GeneralPath class. You can use the lineTo(x,y) method to establish each of your points on the line. Again, because I can't recreate your problem using Line2D.Double, I don't know if this will actually be any different.
Second, and possibly more importantly, is how you are zooming in and out. I am using an AffineTransform (with setScaleTo(x,y)) on my Graphics2D object, and everything is working swimmingly. Compared to the alternative of scaling the points in your data by a zoom factor (or whatever else you could do), this is fairly easy. You'll also have to adjust the stroke of the lines by the factor, because it will scale everything down. I can post screenshots if you'd like.
Please check Xiaolin Wu's line algorithm it should answer you question!
Basic Concept
function plot(x, y, c) is
plot the pixel at (x, y) with brightness c (where 0 ≤ c ≤ 1)
function ipart(x) is
return integer part of x
function round(x) is
return ipart(x + 0.5)
function fpart(x) is
return fractional part of x
function rfpart(x) is
return 1 - fpart(x)
function drawLine(x1,y1,x2,y2) is
dx = x2 - x1
dy = y2 - y1
if abs(dx) < abs(dy) then
swap x1, y1
swap x2, y2
swap dx, dy
end if
if x2 < x1
swap x1, x2
swap y1, y2
end if
gradient = dy / dx
// handle first endpoint
xend = round(x1)
yend = y1 + gradient * (xend - x1)
xgap = rfpart(x1 + 0.5)
xpxl1 = xend // this will be used in the main loop
ypxl1 = ipart(yend)
plot(xpxl1, ypxl1, rfpart(yend) * xgap)
plot(xpxl1, ypxl1 + 1, fpart(yend) * xgap)
intery = yend + gradient // first y-intersection for the main loop
// handle second endpoint
xend = round (x2)
yend = y2 + gradient * (xend - x2)
xgap = fpart(x2 + 0.5)
xpxl2 = xend // this will be used in the main loop
ypxl2 = ipart (yend)
plot (xpxl2, ypxl2, rfpart (yend) * xgap)
plot (xpxl2, ypxl2 + 1, fpart (yend) * xgap)
// main loop
for x from xpxl1 + 1 to xpxl2 - 1 do
plot (x, ipart (intery), rfpart (intery))
plot (x, ipart (intery) + 1, fpart (intery))
intery = intery + gradient
end function

Converting a coordinate from one space to another

I seem to be having a bit of a math fail here... I need to convert an (x,y) point from one coordinate space to another - not in the sense of polar to cartesian or anything of the sort... just from one bound to another. i.e., for a particular (x,y) that falls in the rectangle with lower left (-100, -100) and upper right (100,100), I need to find out where that point would be in a rectangle with lower left (0,0) and upper right (500, 500).
I feel like this is just simple math but I'm having a heck of a time getting it right...
It is for a little computer graphics program written in java. Essentially there is a clip window that changes, and that clip window needs to fill the whole view window. Initial values for clip and view are given by the above rectangles, in that order. However, the clip could change to, for example, a rectangle with lower left (-80, -65) and upper right (75, 65). I would then need to convert a point that falls within that rectangle to a point that falls within the view window (lower left (0,0), upper right (500, 500))
Here is what I have for it right now:
public int normalizeX(float x) {
float clipWidth = clipRight - clipLeft;
int viewWidth = viewRight - viewLeft;
x += 100; //Get x into range [0, 200] instead of [-100, 100]
//First convert x to value within clip width, then "scale" to viewport width
return (int)(((clipWidth*x)/200) * (viewWidth/clipWidth));
}
public int normalizeY(float y) {
float clipHeight = clipTop - clipBottom;
int viewHeight = viewTop - viewBottom;
y += 100; //Get y into range [0, 200] instead of [-100, 100]
//First convert y to value within clip height, then "scale" to viewport height
return (int)(((clipHeight*y)/200) * (viewHeight/clipHeight));
}
Thanks for any help!
Assuming your old bounds are xLoOld and xHiOld (-80 and 75, respectively, in your example) and your new bounds are xLoNew and xHiNew (0 and 500, respectively, in your example), then you can normalize your xOld to your new coordinate system like this:
xNew = (xOld-xLoOld) / (xHiOld-xLoOld) * (xHiNew-xLoNew) + xLoNew
Same thing for y.
Something like this may help you.
public static float scale(
float x,
float old_min, float old_max,
float new_min, float new_max)
{
float old_range = old_max - old_min;
float new_range = new_max - new_min;
return new_min + (x - old_min) * new_range / old_range;
}
You will need to scale your coordinates in both dimensions. I've left out other computations such as casting to int, etc.
I also recommend creating suitable types such as Region or Rectangle to simplify your code by, at least, reducing the number of arguments to this function.
Simple as hell: you want a transformation which maps -100 to 0 and 100 to 500. Or put blindly, which maps the range [-100, 100] to the range [0, 500] :
[-100, 100] ----> [0, 500]
First step is to convert the range [-100, 100] to [0, 200]:
x ----> x + 100
Next step is to convert the range [0, 200] to [0, 500]
x ----> x * 500 / 200 = 2.5 * x
In fine, your transformation reads
x ----> 2.5 * (x + 100)
and likewise for y:
y ----> 2.5 * (y + 100).
Hope this makes it clear, and that you will be able to reuse the logic in more complex cases.
The slick and general way to do this is with an affine transform.
You'll have a 2 x 2 matrix (call it A) that characterizes the "stretchiness" of the original field to the second field, and a 2 x 1 matrix (call it b) characterizing the offset.
Then, with x as your (2 x 1) input and y as your (2 x 1) output, it's just y = Ax + b.
The technique also lets you do a lot more (like rotations) but those probably aren't important for your application.
http://en.wikipedia.org/wiki/Affine_transformation

Categories