Rotate Image to Face Mouse - Loss of accuracy

I have following code to calculate rotation (theta) based on the mouse position and player position (player pos is always the same):


	private void calculatePlayerRotation(int x, int y) {
		double xd = x - this.x;
		double yd = y - this.y;
		if(xd == 0)
			xd = -90;
		if(yd==0)
			yd = 90;
		theta = atan2(yd,xd);
		theta = toDegrees(theta);
		theta+=180.0;  //This is because of the orientation of what is on the image.
	}

It seems, however, that when xd is negative, the image rotation is slightly off from where the mouse is.

There also seems to every now and then be some odd stuttering of the image while it rotates.

Does anyone have and idea of what might be causing this or how to improve it?

Oh and if it helps, theta is just passed to an AffineTransform which is used to draw the image.

  1. Math.atan2(dy, dx) handles the special cases you check for you, so you don’t have to do that yourself.
  2. AffineTransform uses radians, degrees.
    EDIT:
    3: xd and yd are not degrees. Setting them to 90 and -90 makes no sense for a special case in the first place. Just get rid of them and use Math.atan2(yd, xd);
    4: I’ve always called differences d, not d. dx, dy, dz, dDistance, dAngle, e.t.c.

Sorry I should’ve specified… the actual rotation is being done by code from an API I’ve made (am making) and it takes degrees. The rotation method converts the degree input to radians.

I changed the variable names and removed the checks. That helped but there still seems to be an accuracy problems.

Updated code:


		double dx = x - this.x;
		double dy = y - this.y;
		theta = atan2(dy, dx);
		theta = toDegrees(theta);
		theta+=180.0;

How bad are the accuracy problems? could you show us a screenshot of this?

http://dl.dropbox.com/u/34206572/acc_loss.png

The white box is where the mouse was

That’s about as bad as it gets. At some points in the rotation it’s fine, but then at others it looks like that.

The image had been rotated approximately 270 degrees in that shot.

Print out the degree that atan2 returns.

Around 1.5 radians

For what input? -_-
0S0
Never mind, can you post the minimal code needed to show the problem? Preferably a runnable version, as my head doesn’t like being javac.exe and java.exe. At least not in real-time. =S

If I post just the rotation and calculation code would that suffice? There “runnable” version would require quite a bit of my source…

You could whip up your own small test app to draw a couple shapes on a component I suppose.

AffineTransform based Rotation Method (from the 2DX-GL API)


	public static AffineTransform rotateImage(Image img, Point location, Graphics2D g2d, double degrees) {
		AffineTransform affine = new AffineTransform();
		affine.setToTranslation(location.getX(), location.getY());
		affine.rotate(Math.toRadians(degrees), img.getWidth(null)/2, img.getHeight(null)/2);
		g2d.drawImage(img, affine, null);
		return affine;
	}

Current Mouse-Image Relation Calculation Code (theta is a volatile double value being modified by separate threads)


	private void calculatePlayerRotation(int x, int y) {
		double dx = x - this.x;
		double dy = y - this.y;
		theta = atan2(dy, dx);
		theta = toDegrees(theta);
		theta+=180.0;
	}

Wowowowow! Modifying theta like that is dangerous as hell, even if it is volatile! You overwrite theta on every call to calculatePlayerRotation, but the value may be overwritten by another thread in between the 3 manipulations to theta, meaning that 180 may be added to a completely different value if it was overwritten after atan2() but before theta+=180.0; You need to use a synchronized block (with a non-volatile double) or set theta with a single call so the calculation can’t be modified in between the last 3 lines.

For the second solution:


	private void calculatePlayerRotation(int x, int y) {
		double dx = x - this.x;
		double dy = y - this.y;
		double th = atan2(dy, dx);
		th = toDegrees(th);
		th+=180.0;
                theta = th;
	}

This would work, but there are other solutions too.

Sadly, I doubt that this is the problem with your inaccurate angle, as the theta bug was a race condition. The code you posted looks fine otherwise, so it’s hard to determine where things go wrong. It’s your code, you know how it works (well, hopefully ;D), so it’s easier for you to debug it yourself rather than explaining everything. Add lots of System.out.println("Variable: " + variable); to your code to make sure that variables have the right values or even better, use a debugger so you can inspect all variables while it’s running. That should be enough to figure out where things are going wrong. In a wild guess, I’d suspect you treat the angle as degrees sometimes and as radians sometimes…

Adding lots of SOPs is going to get ugly, it is best to run the debugger.

WOW! Holy crap! How did I not see that concurrent modification bug?

Ok patched that up…


		double dx = x - this.x;
		double dy = y - this.y;
		double val = atan2(dy, dx);
		val = toDegrees(val);
		val+=180.0;
		theta = val;

But like you predicted it didn’t solve the issue.

Let me do some more investigating and I will post back here in a bit.

Ok I tried putting SOPs in that showed the current value of theta both when calculating and when drawing.

The modification always went through fine and the Drawing lines ALWAYS matched their last calculation line.

I’m so confused…

Well maybe in the end your math is correct but the rendering is wrong :slight_smile:

Well obviously, I thought we already established that? How can atan2(dy, dx) possibly be wrong?

He’s simply not rotating around the center of the image, or if he is, the image is not rotating around this.x and this.y.

Well his drawing code looks fine so maybe it might be his picture is offcenter.

Yep. That was it. Fixed the problem:


	private void calculatePlayerRotation(double x, double y) {
		double dx = x - (this.x+wt/2);
		double dy = y - (this.y+ht/2);
		double val = atan2(dy, dx);
		val+=Math.PI;
		theta = val;
	}