I can't seem to get the standard answer to work. I'm trying to find the new x,y coordinates of a point inside a larger rectangle, after rotating the larger rectangle around its center. (The end goal is to calculate the 4 corners of a collision box inside the larger rectangle.)
The rectangle is 50W x 128H, and the point inside the rectangle is at 15,111. If I rotate this same rectangle 90 degrees clockwise in Photoshop around its center, the point becomes 17,15 inside the rectangle that is now 128W and 50H.
However, if I use the formula I've found 100 times around here for the same question, I get a completely different answer.
import java.awt.*;
public class MyClass {
public static void main(String args[]) {
int width=50;
int height=128;
int cx = width/2;
int cy = height/2;
// should get 17,15
rotateXY(15,111,cx,cy,-90D); // returns 61,85, which is not even inside the image anymore
rotateXY(15,111,cx,cy,90D); // returns -11,66.
}
public static void rotateXY(int x, int y, int cx, int cy, double degrees) {
// x, y - coordinates of a corner point of the square
// cx, cy - center of square coordinates
double angle = Math.toRadians(degrees);
double x1 = x - cx;
double y1 = y - cy;
double x2 = x1 * Math.cos(angle) - y1 * Math.sin(angle);
double y2 = y1 * Math.cos(angle) + x1 * Math.sin(angle);
int rx = (int) x2 + cx;
int ry = (int) y2 + cy;
System.out.println(rx + "," + ry);
}
}
The method I used to rotate the image in java:
public static BufferedImage rotate(BufferedImage bimg, Double angle) {
double sin = Math.abs(Math.sin(Math.toRadians(angle))),
cos = Math.abs(Math.cos(Math.toRadians(angle)));
int w = bimg.getWidth();
int h = bimg.getHeight();
int neww = (int) Math.floor(w*cos + h*sin),
newh = (int) Math.floor(h*cos + w*sin);
BufferedImage rotated = new BufferedImage(neww, newh, bimg.getType());
Graphics2D graphic = rotated.createGraphics();
graphic.translate((neww-w)/2, (newh-h)/2);
graphic.rotate(Math.toRadians(angle), w/2, h/2);
graphic.drawRenderedImage(bimg, null);
graphic.dispose();
return rotated;
}
I assume you created an AffineTransform. With that you need three transformations:
translate the rectangle so it's center point is the origin
rotate the rectangle by some angle
translate the rectangle back to the position where it came from
Now with this you were able to transform the rectangle's coordinates (corners) into new screen coordinates. What you need is a transformation to get from new screen coordinates back to coordinates inside your rectangle. I think it looks like this:
translate the point the same as in step 1
rotate by the negative angle
undo step 1
These AffineTransforms can be applied quite efficiently, compared to your algorightm using trigonometry which even may have a limited range of validity.
See also Rotating Image with AffineTransform
(I am working in libgdx and my arrows are polygons)
I have some programming issue. I'm currently working on a level editor in my game, I've come very far, but now I have a scaling issue. I am working with 8 arrows. Picture below.
I want to drag the the arrow, or when hovered a huge square in the direction, on a straight line. (Orange line). Every arrow has a direction. The arrows should scale the green square.
My code, which is shown above, looks like this:
public void onDragged(int prevX, int prevY, int cX, int cY) {
super.onDragged(prevX, prevY, cX, cY);
int dX = cX - prevX;
int dY = cY - prevY;
int pX = x;
int pY = y;
Vector2f vector = new Vector2f((float) Math.cos(directionAngle + Math.PI / 2), (float) Math.sin(directionAngle + Math.PI / 2));
float sum = new Point2f(prevX, prevY).distance(new Point2f(cX, cY));
x += sum * Integer.signum(dX) * vector.x;
y -= sum * Integer.signum(dY) * vector.y;
polygon.setPosition(x, y);
gui.onArrowDragged(getID(), pX, pY, x, y);
}
This solution works very well at the 4 arrows on the squares side, but my problem is, at the other ones, I can still move the arrow in any direction, and not on the vector of the direction.
I have written an implementation of Bresenham's circle drawing algorithm. This algorithms takes advantage of the highly symmetrical properties of a circle (it only computes points from the 1st octant and draws the other points by taking advantage of symmetry). Therefore I was expecting it to be very fast. The Graphics programming black book, chapter #35 was titled "Bresenham is fast, and fast is good", and though it was about the line drawing algorithm, I could reasonably expect the circle drawing algorithm to also be fast (since the principle is the same).
Here is my java, swing implementation
public static void drawBresenhamsCircle(int r, double width, double height, Graphics g) {
int x,y,d;
y = r;
x = 0;
drawPoint(x, y, width, height,g);
d = (3-2*(int)r);
while (x <= y) {
if (d <= 0) {
d = d + (4*x + 6);
} else {
d = d + 4*(x-y) + 10;
y--;
}
x++;
drawPoint(x, y, width, height,g);
drawPoint(-x, y, width, height,g);
drawPoint(x, -y, width, height,g);
drawPoint(-x, -y, width, height,g);
drawPoint(y, x, width, height,g);
drawPoint(-y, x, width, height,g);
drawPoint(y, -x, width, height,g);
drawPoint(-y, -x, width, height,g);
}
}
This method uses the following drawPointmethod:
public static void drawPoint(double x, double y,double width,double height, Graphics g) {
double nativeX = getNativeX(x, width);
double nativeY = getNativeY(y, height);
g.fillRect((int)nativeX, (int)nativeY, 1, 1);
}
The two methods getNativeX and getNativeY are used to switch coordinates from originating in the upper left corner of the screen to a system that has it origin in the center of the panel with a more classic axis orientation.
public static double getNativeX(double newX, double width) {
return newX + (width/2);
}
public static double getNativeY(double newY, double height) {
return (height/2) - newY;
}
I have also created an implementation of a circle drawing algorithm based on trigonometrical formulaes (x=R*Math.cos(angle)and y= R*Math.sin(angle)) and a third implementation using a call to the standard drawArc method (available on the Graphics object). These additional implementations are for the sole purpose of comparing Bresenham's algorithm to them.
I then created methods to draw a bunch of circles in order to be able to get good measures of the spent time. Here is the method I use to draw a bunch of circles using Bresenham's algorithm
public static void drawABunchOfBresenhamsCircles(int numOfCircles, double width, double height, Graphics g) {
double r = 5;
double step = (300.0-5.0)/numOfCircles;
for (int i = 1; i <= numOfCircles; i++) {
drawBresenhamsCircle((int)r, width, height, g);
r += step;
}
}
Finally I override the paint method of the JPanel I am using, to draw the bunch of circles and to measure the time it took each type to draw. Here is the paint method:
public void paint(Graphics g) {
Graphics2D g2D = (Graphics2D)g;
g2D.setColor(Color.RED);
long trigoStartTime = System.currentTimeMillis();
drawABunchOfTrigonometricalCircles(1000, this.getWidth(), this.getHeight(), g);
long trigoEndTime = System.currentTimeMillis();
long trigoDelta = trigoEndTime - trigoStartTime;
g2D.setColor(Color.BLUE);
long bresenHamsStartTime = System.currentTimeMillis();
drawABunchOfBresenhamsCircles(1000, this.getWidth(), this.getHeight(), g);
long bresenHamsEndTime = System.currentTimeMillis();
long bresenDelta = bresenHamsEndTime - bresenHamsStartTime;
g2D.setColor(Color.GREEN);
long standardStarTime = System.currentTimeMillis();
drawABunchOfStandardCircles(1000, this.getWidth(), this.getHeight(),g);
long standardEndTime = System.currentTimeMillis();
long standardDelta = standardEndTime - standardStarTime;
System.out.println("Trigo : " + trigoDelta + " milliseconds");
System.out.println("Bresenham :" + bresenDelta + " milliseconds");
System.out.println("Standard :" + standardDelta + " milliseconds");
}
Here is the kind of rendering it would generate (drawing 1000 circles of each type)
Unfortunately my Bresenham's implementation is very slow. I took many comparatives measures, and the Bresenham's implementation is not only slower than the Graphics.drawArcbut also slower than the trigonometrical approach. Take a look at the following measures for a various number of circles drawn.
What part of my implementation is more time-consuming? Is there any workaround I could use to improve it? Thanks for helping.
[EDITION]: as requested by #higuaro, here is my trigonometrical algorithm for drawing a circle
public static void drawTrigonometricalCircle (double r, double width, double height, Graphics g) {
double x0 = 0;
double y0 = 0;
boolean isStart = true;
for (double angle = 0; angle <= 2*Math.PI; angle = angle + Math.PI/36) {
double x = r * Math.cos(angle);
double y = r * Math.sin(angle);
drawPoint((double)x, y, width, height, g);
if (!isStart) {
drawLine(x0, y0, x, y, width, height, g);
}
isStart = false;
x0 = x;
y0 = y;
}
}
And the method used to draw a bunch of trigonometrical circles
public static void drawABunchOfTrigonometricalCircles(int numOfCircles, double width, double height, Graphics g) {
double r = 5;
double step = (300.0-5.0)/numOfCircles;
for (int i = 1; i <= numOfCircles; i++) {
drawTrigonometricalCircle(r, width, height, g);
r += step;
}
}
Your Bresenham method isn't slow per se, it's just comparatively slow.
Swing's drawArc() implementation is machine-dependent, using native code. You'll never beat it using Java, so don't bother trying. (I'm actually surprised the Java Bresenham method is as fast as it is compared to drawArc(), a testament to the quality of the virtual machine executing the Java bytecode.)
Your trigonometric method, however, is unnecessarily fast, because you're not comparing it to Bresenham on an equal basis.
The trig method has a set angular resolution of PI/36 (~4.7 degrees), as in this operation at the end of the for statement:
angle = angle + Math.PI/36
Meanwhile, your Bresenham method is radius-dependent, computing a value at each pixel change. As each octant produces sqrt(2) points, multiplying that by 8 and dividing by 2*Pi will give you the equivalent angular resolution. So to be on equal footing with the Bresenham method, your trig method should therefore have:
resolution = 4 * r * Math.sqrt(2) / Math.PI;
somewhere outside the loop, and increment your for by it as in:
angle += resolution
Since we will now be back to pixel-level resolutions, you can actually improve the trig method and cut out the subsequent drawline call and assignments to x0 and y0, eliminate unnecessarily casts, and furthermore reduce calls to Math. Here's the new method in its entirety:
public static void drawTrigonometricalCircle (double r, double width, double height,
Graphics g) {
double localPi = Math.PI;
double resolution = 4 * r * Math.sqrt(2) / Math.PI;
for (double angle = 0; angle <= localPi; angle += resolution) {
double x = r * Math.cos(angle);
double y = r * Math.sin(angle);
drawPoint(x, y, width, height, g);
}
}
The trig method will now be executing more often by several orders of magnitude depending on the size of r.
I'd be interested to see your results.
Your problem lies in that Bresenham's algorithm does a variable number of iterations depending on the size of the circle whereas your trigonometric approach always does a fixed number of iterations.
This also means that Bresenham's algorithm will always produce a smooth looking circle whereas your trigonometric approach will produce worse looking circles as the radius increases.
To make it more even, change the trigonometric approach to produce approximately as many points as the Bresenham implementation and you'll see just how much faster it is.
I wrote some code to benchmark this and also print the number of points produced and here are the initial results:
Trigonometric: 181 ms, 73 points average
Bresenham: 120 ms, 867.568 points average
After modifying your trigonometric class to do more iterations for smoother circles:
int totalPoints = (int)Math.ceil(0.7 * r * 8);
double delta = 2 * Math.PI / totalPoints;
for (double angle = 0; angle <= 2*Math.PI; angle = angle + delta) {
These are the results:
Trigonometric: 2006 ms, 854.933 points average
Bresenham: 120 ms, 867.568 points average
I lately wrote a bresenham circle drawing implemenation myself for a sprite rasterizer and tried to optimize it a bit. I'm not sure if it will be faster or slower than what you did but i think it should have a pretty decent execution time.
Also unfortunately it is written in C++. If i have time tomorrow i might edit my answer with a ported Java version and an example picture for the result but for now you'd have to do it yourself if you want (or someone else who would want to take his time and edit it.)
Bascically, what it does is use the bresenham algorithm to aquire the positions for the outer edges of the circle, then perform the algorithm for 1/8th of the circle and mirror that for the the remaining 7 parts by drawing straight lines from the center to the outer edge.
Color is just an rgba value
Color* createCircleColorArray(const int radius, const Color& color, int& width, int& height) {
// Draw circle with custom bresenham variation
int decision = 3 - (2 * radius);
int center_x = radius;
int center_y = radius;
Color* data;
// Circle is center point plus radius in each direction high/wide
width = height = 2 * radius + 1;
data = new Color[width * height];
// Initialize data array for transparency
std::fill(data, data + width * height, Color(0.0f, 0.0f, 0.0f, 0.0f));
// Lambda function just to draw vertical/horizontal straight lines
auto drawLine = [&data, width, height, color] (int x1, int y1, int x2, int y2) {
// Vertical
if (x1 == x2) {
if (y2 < y1) {
std::swap(y1, y2);
}
for (int x = x1, y = y1; y <= y2; y++) {
data[(y * width) + x] = color;
}
}
// Horizontal
if (y1 == y2) {
if (x2 < x1) {
std::swap(x1, x2);
}
for (int x = x1, y = y1; x <= x2; x++) {
data[(y * width) + x] = color;
}
}
};
// Lambda function to draw actual circle split into 8 parts
auto drawBresenham = [color, drawLine] (int center_x, int center_y, int x, int y) {
drawLine(center_x + x, center_y + x, center_x + x, center_y + y);
drawLine(center_x - x, center_y + x, center_x - x, center_y + y);
drawLine(center_x + x, center_y - x, center_x + x, center_y - y);
drawLine(center_x - x, center_y - x, center_x - x, center_y - y);
drawLine(center_x + x, center_y + x, center_x + y, center_y + x);
drawLine(center_x - x, center_y + x, center_x - y, center_y + x);
drawLine(center_x + x, center_y - x, center_x + y, center_y - x);
drawLine(center_x - x, center_y - x, center_x - y, center_y - x);
};
for (int x = 0, y = radius; y >= x; x++) {
drawBresenham(center_x, center_y, x, y);
if (decision > 0) {
y--;
decision += 4 * (x - y) + 10;
}
else {
decision += 4 * x + 6;
}
}
return data;
}
//Edit
Oh wow, I just realized how old this question is.
Ok, I have a little problem: I'm trying to code an engine capable of rendering 3d lines onto a 2d plane, but I'm having some trouble with it.
I have a point3d class, which takes x y and z coords, a line3d class, which takes two point3d endpoints, and I have a world object containing an arraylist of line3d's. I also have point2d and line2d, which are just like their 3d counterparts, except that they lack a z coordinate.
Here's the render method:
public void render(){
for(Line3d line : world.lines){ //for every 3d line in the world
panel.l3d(line, world.main); //render that line
}
panel.repaint(); //repaint the graphic
}
Which calls upon the l3d method:
public void l3d(Line3d line, Camera view){
Point2d start = p3Top2(line.start, view), end = p3Top2(line.end, view); //convert 3d points to 2d points
if(start==null || end==null)return; //if either is null, return
lineAbs(new Line2d(start, end)); //draw the line
}
Which calls upon p3Top2:
public Point2d p3Top2(Point3d point, Camera view){ //convert 3d point to 2d point on screen
int relativeZed = point.z - view.z; //difference in z values between camera and point
if(relativeZed <= 0)return null; //if it's behind the camera, return
int sx, sy, ex, ey, tx, ty; //begin declaring rectangle formed from extending camera angle to the z coord of the point(angle of 1 = 90 degrees)
sx = (int) (view.x - (width / 2) - relativeZed * (width * view.angle)); //starting x of rectangle
ex = (int) (view.x + (width / 2) + relativeZed * (width * view.angle)); //ending x
sy = (int) (view.y - (height / 2) - relativeZed * (height * view.angle)); //starting y
ey = (int) (view.y + (height / 2) + relativeZed * (height * view.angle)); //ending y
tx = point.x - sx; //difference between point's x and start of rectangle
ty = point.y - sy; //same for y
float px = (float)tx / (float)(ex - sx), py = (float)ty / (float)(ey - sy); //px and py are the ratios of the point's x/y coords compared to the x/y of the rectangle their in
return new Point2d((int)(px * width), (int)(py * height)); //multiply it by the width and height to get positions on screen
}
And also on lineAbs:
public void lineAbs(Line2d line){
Point2d start = line.start;
Point2d end = line.end;
if(start.x>end.x){
start = line.end;
end = line.start;
}
int y = start.y; //starting y point
int white = 0xffffff;
for(int x = start.x; x<end.x; x++){ //for each x in the line
if(x < 0 || y < 0 || x > canvas.getWidth() || y > canvas.getHeight())continue; //if the point is outside of the screen, continue
y += line.getSlope().slope; //increment y by the slope
canvas.setRGB(x, y, white); //draw the point to the canvas
}
}
'canvas' is a BufferedImage being drawn to the screen. With an arbitrary camera and angle of 1, as well as a few arbitrary lines thrown in, I do see each line, but they don't appear to be rendered properly. For example, when I have three point3d's as vertices, and three lines, each with a different combination of two of the points, the lines don't appear to meet at all, although each one is visible.
I suspect the issue is in my p3Top2, but I'm not sure where, can you tell?
The problem:
I've got this "Shot" class. In the code, the target variables are the mouseX and mouseY.
So when i click the mouse button, my player class will create a new shot object.
But the shooting is inaccurate.
How can i calculate the correct dx and dy?
If i add the dx and dy to the "bullet's" x and y, the bullet will move to the mouse's direction.This is what i want. The mouse position is stored in targetX and targetY, when the object is created. This is the point what the oval wants to reach.
Links:
The game (finished)
The code (from Shot.java):
public class Shot extends Entity {
private float targetX, targetY;
public Shot(World world, float x, float y, int width, int height, Color color, float targetX, float targetY) {
super(world, x, y, width, height, color);
this.targetX = targetX;
this.targetY = targetY;
}
#Override
public void render(GameContainer gc, Graphics g, Camera camera) {
g.setColor(color);
g.fillOval(x - camera.getX(), y - camera.getY(), width, height);
}
#Override
public void update(GameContainer gc, int delta) {
float dx = targetX - x;
float dy = targetY - y;
x += dx * delta * .001f;
y += dy * delta * .001f;
}
}
I tried this, but still not work:
#Override
public void update(GameContainer gc, int delta) {
float length = (float) Math.sqrt((targetX - x) * (targetX - x) + (targetY - y) * (targetY - y));
double dx = (targetX - x) / length * delta;
double dy = (targetY - y) / length * delta;
x += dx;
y += dy;
}
I did it! Here is my solution:
The problem was that, the target was the window's mouse position, and not the world's mouse position.
This is how i calculated the world's mouse positions:
float mouseWorldX = x + (mouseX - screen_width / 2); // x = player's x position
float mouseWorldY = y + (mouseY - screen_height / 2); // y = player's y position
This is code from my game at the moment is used to move a unit to the mouse when the right mouse button is pressed:
length = Math.sqrt((target_X - player_X)*(target_X - player_X) + (target_Y - player_Y)*(target_Y - player_Y)); //calculates the distance between the two points
speed_X = (target_X - player_X) /length * player_Speed;
speed_Y = (target_Y - player_Y) /length * player_Speed;
This will move an object to the target in a line at a set speed.
Edit: this is the actual code right from my game
if(input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON))
{
length = (float) Math.sqrt((player_waypoint_X - player_X)*(player_waypoint_X - player_X) + (player_waypoint_Y - player_Y)*(player_waypoint_Y - player_Y));
velocityX = (float) (player_waypoint_X - player_X) /length * (float) PlayerStats.player.db_player_Speed;
velocityY = (float) (player_waypoint_Y - player_Y) /length * (float) PlayerStats.player.db_player_Speed;
player_waypoint_X = input.getMouseX() - 2;
player_waypoint_Y = input.getMouseY() - 2;
}
For testing purposes the velocity's are defined in the init method along with length. Every time the right mouse is pressed the waypoints's X and Y are changed to the mouse location.
I learned this from this question
velocity calculation algorithm.
in order to make the bullets not all change direction every shot, create an array list so that each bullet fired has its own x and y velocity