Solved

How can i find a negitive angle from three points

Posted on 2009-04-22
378 Views
I am trying to rotate a box on screen in the same way it is done in many applications like powerpoint etc, by clicking and dragging around the object in the direction i want it to rotate.

Using the code below i have managed to make it rotate, by working out the angle between the three points; current mouse position, center of shape to be rotated and the old mouse position. Unfortunatly since i am working out the lengths of the sides with Pythagoras, i only get a positive result, wich leads to my box rotating in one direction only, regardless of the direction i move my mouse

I'm not sure if i'm going about this in completely the wrong way ( i am using a matrix to actually rotate the points, but i don't know if its possible to reverse the operation ), or whether i'm just bing a idiot and forgetting somthing simple i could do, but any help will be appreciated

``````public void Rotate(PointF p)

{

// Center

float cx = Center.X;

float cy = Center.Y;

// New Position

float nx = p.X;

float ny = p.Y;

// Old Position

float ox = m_pnt_MouseTrack.X;

float oy = m_pnt_MouseTrack.Y;

// Work Out the Lengths of the Sides.

// a == new -> old , b == center -> new , c == center -> old

float a = (float)(Math.Sqrt(Math.Pow((nx - ox), 2) + Math.Pow((ny - oy), 2)));

float b = (float)(Math.Sqrt(Math.Pow((nx - cx), 2) + Math.Pow((ny - cy), 2)));

float c = (float)(Math.Sqrt(Math.Pow((cx - ox), 2) + Math.Pow((cy - oy), 2)));

// Work out the Direction of the rotation

// This is the bit i don't know how to do ...

// Cos-1(( a^2 - b^2 - c^2 ) / -4bc) == A

float A = (a * a) - (b * b) - (c * c);

A /= (-2) * b * c;

A = (float)Math.Acos(A);

// Rotate the shape using a method i have written elsewhere

Rotate(A - (float)Math.PI);

}
``````
0
Question by:ClearRock

LVL 25

Expert Comment

By the looks of it, your code is only rotating the box around one axis.
If you only want to rotate around (say) the y-axis, then just use (x_new - x_old) to calculate the angle of rotation.

If you wish to rotate around an arbitrary axis, I'd recommend using quaternions rather than matrices.
In the case of matrices, you need to calculate the Euler angles around each axis (and perform them in the right order so as to avoid Gimbal lock etc) - it's quite tedious.

Quaternions are much easier to implement (although a little more abstract). This method of rotating with quaternions is sometimes called 'Arc ball'.

The idea is that you define the axis u to be the line from the centre of rotation, to the 'old' mouse position. Then use the difference in the old and new mouse positions to compute the angle of rotation (one method is to find u x (p_new-p_old)).

See here:
http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
http://cgmath.blogspot.com/2009/03/arc-ball-rotation-using-quaternion.html
and equation (30) onwards, here:
http://mathworld.wolfram.com/Quaternion.html

Ask if you have further questions.
0

Author Comment

I'll be honest a lot of that went a little over my head ( i'm just looking at the info in the links you posted ), so i may have gotten you wrong here.

To start with  i'm only dealing in 2d here, so i'm not sure if that makes a difference, and i'm rotating around a fixed point, but thats all working fine

Its not that i'm having any trouble rotateing the box, that works fine, i'm having trouble working out wich way to rotate it

your final paragraph looks like it might be what i'm looking for, but i'll just have to read about quaternions before i can be sure
0

Author Comment

Just noticed another mistake in the code! Replaceing the last three lines...
``````// Rotate the shape using a method i have written elsewhere

Rotate(A);
``````
0

Author Comment

It seems to me like quaternions are a very clever way of rotating somthing in 3d space, wich i'll remember , but i don't think they really help me ( unless i totally misunderstand your proposed soloution ... wich is unfortunatly likely to be the case )

I think what i really need is a way of finding the angle formed between the three points, but using a method that either gives me results within the range -180>ang>180 or 0>ang>360
0

Author Comment

I've solved my own problem now, using a bit of a hack, bet hey it works. Thanks for your suggestion anyway though
0

LVL 22

Expert Comment

You need to learn the sine of the angle as well as the cosine.  Then you can use atan2 to get the angle.
``````public void Rotate(PointF p)

{

// Center

float cx = Center.X;

float cy = Center.Y;

// New Position

float nx = p.X;

float ny = p.Y;

// Old Position

float ox = m_pnt_MouseTrack.X;

float oy = m_pnt_MouseTrack.Y;

float dx_o = ox - cx;

float dy_o = oy - cy;

float dx_n = nx - cx;

float dy_n = ny - cy;

float cross = (dx_o * dy_n) - (dx_n * dy_o);

float dot = dx_o * dx_n + dy_o * dy_n;

// when vecs a and b are at angle theta,

// a cross b = |a| * |b| * sin(theta)

// a dot b = |a| * |b| * cos(theta)

float angle;

if (cross == 0 && dot == 0) {

// either old == center or new == center, so rotation

// is undefined

angle = 0;

} else {

angle = atan2(cross, dot);

}

// now use "angle" to rotate your shape

}
``````
0

LVL 25

Accepted Solution

Sorry, I assumed you were working in 3D.

Now that I know you're working in 2D, I see what you're doing with your code.
In your case, Pythagoras' is only an approximation for the lengths, as there's no guarantee the user is going to form a right-angled triangle. Furthermore (as was the problem) the cosine law does not give an idea for the direction of the angle.

I'd suggest using the cross product. Try,

A = arcsin[(a_x*b_y - b_x*a_y) / sqrt{(a_x*a_x+a_y*a_y)*(b_x*b_x+b_y*b_y)}]

where

a = mouse_old - centre
b = mouse_new - centre

0

LVL 26

Expert Comment

You appear to go (mouse down, mouse up, redraw box) rather than having box follow mouse.

A messy way is to transform box to coordinate system with center of box at (0,0) and mouse down at (ox,0) x>0. Now identify quadrant in which mouse up takes place. Cos(t) = absolute value (ny/nx). Rotate through angle tr.
IF quad 1    tr = t
quad 2    tr = t + 90
quad 3    tr = 180  t
Transform box to original cord system.
Draw
0

Author Comment

Thx Interactive, Sorry for the late reply, i finished work and just got in this morning.

I thought i'd managed to solve it with a really messy hack, involving using i statements to work out what qudrent the mouse was in reletive to the center, and then work out the direction of the mouse relitive to that quadrent, however after a bit more testing this morning, i broke it again ( and it's so messy and awful i'm ashamed of it ).

Your solution looks a lot better, so i'll try implement that and un close this question ( its the first time i've actually asked anything on here so if i do somthing wrong i apologize! ) so that i can award points properly.

Just one question, which points are A and B? I've assumed that they are the old and new mouse positions.

0

Author Comment

I Closed it thinking i had a solution, when further testing revealed that i did not.
0

Author Closing Comment

Thank you, worked first time ( after i actually read the bit at the bottom after 'where' ).
0

LVL 22

Expert Comment

Arcsin only returns an angle in the range (-pi/2, +pi/2), but rotation can happen at any angle.  That's why you need to look at both the cross-product and the dot-product.  Once you have both of those, you can use atan2 to get the relative angle, which will be anywhere in (-pi, +pi).  Just like in my response above. :)

Or if you don't quite follow the cross and dot product stuff, you could just use atan2 twice, once on the old vector and once on the new vector, then subtract to get the angle.  At this point the difference will be in the range (-2pi, +2pi) so you might want to normalize the angle.

Or you could use the old and new vectors to come up with the transform without ever going through any trig.
0

Featured Post

This article describes a simple method to resize a control at runtime.  It includes ready-to-use source code and a complete sample demonstration application.  We'll also talk about C# Extension Methods. Introduction In one of my applications…
This article is for Object-Oriented Programming (OOP) beginners. An Interface contains declarations of events, indexers, methods and/or properties. Any class which implements the Interface should provide the concrete implementation for each Inter…
how to add IIS SMTP to handle application/Scanner relays into office 365.
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…