PDA

View Full Version : 3D rotation



kiattisak
01-06-2016, 02:29 AM
How do we rotate the line from position, let's say for example, [{0,0,0}->{0,0,1}] to [{0,0,0}->{1,1,1}],
where
[{x1,y1,z1}->{x2,y2,z2}] is the line traced from {x1,y1,z1} to {x2,y2,z2}.

Thank you.

Carmine
01-06-2016, 12:52 PM
This is a more of a math question than a GL question.
It is more than a simple rotation because your destination line segment (1,1,1) is longer than your starting segment (0,0,1).
Therefore it is a rotation and a scaling of the original segment.

kiattisak
01-06-2016, 06:10 PM
This is a more of a math question than a GL question.
It is more than a simple rotation because your destination line segment (1,1,1) is longer than your starting segment (0,0,1).
Therefore it is a rotation and a scaling of the original segment.

Firs of all, thank you very much for your reply.

I agree with this "your destination line segment (1,1,1) is longer than your starting segment (0,0,1)"

So do not concern about length, just the orientation of destination. How can I do this with OpenGL command?
So you said it is more about maths. Could you/anyone please give me some sort of examples if you have.

Thank you.

GClements
01-06-2016, 06:55 PM
How do we rotate the line from position, let's say for example, [{0,0,0}->{0,0,1}] to [{0,0,0}->{1,1,1}],
where
[{x1,y1,z1}->{x2,y2,z2}] is the line traced from {x1,y1,z1} to {x2,y2,z2}.

If one line is p0->p1 and the other is q0->q1:

1. Let n0=normalize(q0-p0) and n1=normalize(q1-p1).
These are the directions in which the endpoints will be moved by the rotation.

2. Let m0=(p0+q0)/2 and m1=(p1+q1)/2.
These are the midpoints between the two rotations of each endpoint.

3. Let h0=dot(n0,m0) and h1=dot(n1,m1).
The planes n0.p=h0 and n1.p=h1 lie midway between the rotations and are perpendicular to the directions.
Both must pass through the axis of rotation; thus the axis is the intersection of these planes.

5. Let v=normalize(cross(n0,n1)).
v is the direction of the rotation axis.

6. Let k=dot(n0,n1), c0=(h0-h1*k)/(1-k2) and c1=(h1-h0*k)/(1-k2).

7. Let c=c0*n0+c1*n1.
The point c lies on the rotation axis.
Thus, the rotation axis is the set of points c+t*v for all t.

8. Let d=dot(p0,v)=dot(q0',v) (the two should be equal)
This is the distance along the axis between c and p0/q0

9. Let p0'=p0-c-d*v, q0'=q0-c-d*v
I.e. translate the coordinate system so that the centre of rotation is the origin.

10. Let x=cross(normalize(p0'),normalize(q0'))

11. Let a=arcsin(length(x)).
This is the angle of rotation.
The dot product between p0'' and q0'' can be used to distinguish between rotations of more or less than 90 degrees.
If the dot product between x and v is negative, either a or v needs to be negated.

Thus, the overall transformation is a rotation of angle a about an axis with direction v passing through c.


glTranslatef(c[0], c[1], c[2]);
glRotatef(degrees(a), v[0], v[1], v[2]);
glTranslatef(-c[0], -c[1], -c[2]);


One caveat: if the projections of the lines intersect, then the two planes will be coincident and v will be zero. That corresponds to the case where the problem is under-constrained: it's possible to transform one line to the other by rotating about any axis passing through the point of intersection.

Carmine
01-07-2016, 06:35 PM
So do not concern about length, just the orientation of destination. How can I do this with OpenGL command?
So you said it is more about maths. Could you/anyone please give me some sort of examples if you have.
Let's call your first vector 'A' and the destination vector 'B'. Only one OpenGL command is needed to rotate 'A' to 'B'. It's glRotatef (angle, u,v,w), where 'angle' is the angle between 'A' and 'B', and (u,v,w) are the components of the rotation vector. Some simple vector operations must be performed to get 'angle' and (u,v,w). I've written a short to program to demonstrate. Code and figure are attached. Remember that glRotate rotates objects around ANY vector. The vector needed in this situation is the normal to the plane containing 'A' and 'B'. It's shown in red in the diagram (labelled 'N'). 'N' is the vector cross product of 'A' and 'B'. (It's hard to tell from the diagram, but 'N' is actually pointed into the screen). The sample code contains a few vector routines needed for the demo. The code is kept brief for the purpose of clarity. If this were part of an actual project I was working on, some things would be done differently. Also, the code doesn't contain the routines which initialize GL, GLUT, open the window, set up the projections, allow interactive viewing transformations, etc. I leave that up to you. Bottom line here is that glRotate is called within a loop to get the yellow points. You only really have to call it once if the goal is to graphically rotate 'A' to 'B'. Let me know if you have any questions.

2220




//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//----------------------------------------- vec_mag --------------------------------------------

float vec_mag (float v[3]) // Return sthe magnitude of a 3D vector. */
{
return ((float)sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]));
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//--------------------------------------- Vector_Angle -----------------------------------------

float Vector_Angle (float a[3], float b[3]) // Returns the angle between two vectors in degrees.
{
float num, den;

num = a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
den = vec_mag(a) * vec_mag(b);

return ((float)acos(num/den) * RADDEG);
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//--------------------------------------- unit_vector ------------------------------------------

void unit_vector (float v[3]) // Normalize incoming vector (make it have length = 1.0)
{
float mag;

mag = (float)sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);

v[0] = v[0] / mag;
v[1] = v[1] / mag;
v[2] = v[2] / mag;
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//---------------------------------------- vec_cross -------------------------------------------

void vec_cross (float a[3], float b[3], float c[3]) // c is the vector cross product of a and b
{
c[0] = a[1] * b[2] - a[2] * b[1];
c[1] = a[2] * b[0] - a[0] * b[2];
c[2] = a[0] * b[1] - a[1] * b[0];
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//-------------------------------------- Line_Segment ------------------------------------------

/* Draw line segment from point fr to point to
lw: line width,
co: color,
aa: anti-alias flag,
fr: from vector,
to: to vector */

void Line_Segment (float lw, float co[3], short aa, float fr[3], float to[3])
{
glDisable (GL_LIGHTING);
glColor3fv (co);
glLineWidth (lw);

if (aa) {
glEnable (GL_BLEND);
glEnable (GL_LINE_SMOOTH);
glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} else {
glDisable (GL_LINE_SMOOTH);
}

glBegin (GL_LINES);
glVertex3fv (fr);
glVertex3fv (to);
glEnd ();
}


//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//-------------------------------------- Draw_Vectors ------------------------------------------

void Draw_Vectors (void)
{
float a, x, y, ang, radius = 0.05, nrm[3];

static float RAD_DEG = 57.296, org[3] = {0,0,0}, aaa[3] = {0,0.1,1}, bbb[3] = {0.7, 0.7, 0.7},
orange[3] = {0.8, 0.4, 0.2}, yellow[3] = {0.8, 0.8, 0.2}, green[3] = {0.2, 0.9, 0.5},
red[3] = {0.9, 0.2, 0.2};

Line_Segment (3.0, orange, 1, org, aaa); // 'aaa' is start vector
glRasterPos3fv (aaa);
glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, ' ');
glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, 'A'); // Label vector 'aaa' as 'A'

Line_Segment (2.0, green, 1, org, bbb); // 'bbb' is end vector
glRasterPos3fv (bbb);
glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, ' ');
glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, 'B'); // Label vector 'bbb' as 'B'

vec_cross (aaa, bbb, nrm); // 'nrm' -> vector normal to plane defined by aaa and bbb.
unit_vector (nrm); // Normalize vector 'nrm' (i.e. make it have length = 1.0)
Line_Segment (3.0, red, 1, org, nrm); // Draw 'nrm' as a red line.
glRasterPos3fv (nrm);
glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, ' ');
glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, 'N'); // Label vector 'nrm' as 'N'


// Draw points showing rotation path of 'aaa' to 'bbb'

ang = Vector_Angle (aaa, bbb);
glPointSize (5.0);
glColor3f (1.0, 1.0, 0.3);
for (a = 0.0; a <= ang; a += 2.0) {
glPushMatrix ();
glRotatef (a, nrm[0], nrm[1], nrm[2]);
glBegin (GL_POINTS);
glVertex3fv (aaa);
glEnd ();
glPopMatrix ();
}
}

kiattisak
01-07-2016, 07:00 PM
Thank you very much, GClements, for quite a complicated maths.

Actually I want to rotate a solid rectangle from direction [(0,0,0)-(0,0,1)] to any arbitrary direction, let me say for example to the direction [(0,0,0)-(1,1,1)].
I tried myself by playing around with the glRotate command in this way:

Let vector V1 is [(0,0,0)->(0,0,1)] and vector V2 is [(0,0,0)->(1,1,1)].
I determined an angle of rotation by angle = arccos(dot(V1*V2))
and an axis of rotation by V3 = normalize{cross(V1XV2)}.

Then I did a rotation with:
glRotate(degree(angle), V3[0], V3[1], V3[2]).

Firstly, it worked quite well, but there was still some problem.
The cross section rotated about its axis. That is the orientation of cross section was changed.
How come? Can anyone explain me? And what should I do if I want to keep the cross section orientation the same.

By the way, I created a solid rectangle by a series of
glBegin(GL_QUADS);
...
glEnd();

kiattisak
01-07-2016, 07:06 PM
Thanks again, Carmine.
Your second reply come just before my latest post.
I'll try to read what you've written.

Carmine
01-07-2016, 09:33 PM
What U did is exactly what my post suggests. I'll take a look at your rectangle problem.

To maintain the axial orientation of the box, you can do two rotations as shown in the code and figure below.
Vector 'bbb' is the destination vector (what you call V2).

2221



unit_vector (bbb);
del = asin (bbb[1]) * RADDEG;
alf = atan2 (bbb[0],bbb[1]) * RADDEG;

Draw_Box (orange);
glPushMatrix ();
glRotatef ( alf, 0,1,0);
glRotatef (-del, 1,0,0);
Draw_Box (green);
glPopMatrix ();

kiattisak
01-08-2016, 12:40 AM
It was my lunch time, Carmine.
While I was eating, I was thinking of going back to two-step rotation with correct-projected angle. I did the two-step rotation before, but it didn't work because of incorrect-projected angles.

When I come back, I just exactly follow your step and then it works! What a great help!
Thanks again, Carmine, have a nice weekend.