View Full Version : A cone pointer for a line

I have a line in space going from x1,y1,z1 to x2,y2,z2 and would like to add a cone pointer to the end. I have built a cone on the Y axis at the origin and tried to rotate and then translate it to x2,y2,z2. I can not seem to get the cone to point along the line as new points are added. How can I do this? I seem to have a brain lock getting this seemingly easy task completed.

nexusone

03-11-2003, 06:53 AM

Are you using gl(Push/Pop)Matrix in your code?

// Draw line

glPushMatrix()

draw_line()

glPopMatrix()

// Draw cone

glPushMatrix()

glTranslatef(.....) location of cone

glRotatef(.....) rotate cone to point to correct direction

draw_cone()

glPopMatrix()

I forget the non-rotated direction of the cone, but I think pointing down the -z axis. So you have to adjustment for that when you draw, to get it the position you want.

Originally posted by beaver:

I have a line in space going from x1,y1,z1 to x2,y2,z2 and would like to add a cone pointer to the end. I have built a cone on the Y axis at the origin and tried to rotate and then translate it to x2,y2,z2. I can not seem to get the cone to point along the line as new points are added. How can I do this? I seem to have a brain lock getting this seemingly easy task completed.

Yes, I am using push/pop. Maybe a little code will help. I am really drawing a pyramid on the line, but the axis of the pyrmid does not lay along the line between the points.

double radius = 5;

double radius2 = radius/4.0;

// Calculate vector

double dx = tX-prev_x;

double dy = tY-prev_y;

double dz = tZ-prev_z;

// calc angles

double rotz = -atan2(dx,dy) * R_2_D; // +y around z

double roty = -atan2(dz,dx) * R_2_D; // -z around y

glPushMatrix(); // Push The Matrix

glBegin(GL_LINES);

glColor3f(0,1,0); // green

glVertex3d(prev_x, prev_y, prev_z);

glVertex3d(tX, tY, tZ);

glEnd();

glPopMatrix(); // Pop The Matrix

glPushMatrix(); // Push The Matrix

glTranslated(tX, tY, tZ); // move to display site

glRotated(rotz, 0,0,1); // rotate to position

glRotated(roty, 0,1,0); // rotate to position

// draw a pyramid on the +y axis centered at origin

glBegin(GL_POLYGON);

glVertex3d(0, radius, 0);

glVertex3d(radius2, -radius2, 0);

glVertex3d(0, -radius2, -radius2);

glVertex3d(0, radius, 0);

glVertex3d(0, -radius2, -radius2);

glVertex3d(-radius2, -radius2, 0);

glVertex3d(0, radius, 0);

glVertex3d(-radius2, -radius2, 0);

glVertex3d(0, -radius2, radius2);

glVertex3d(0, radius, 0);

glVertex3d(0, -radius2, radius2);

glVertex3d(radius2, -radius2, 0);

glVertex3d(radius2, -radius2, 0);

glVertex3d(0, -radius2, -radius2);

glVertex3d(-radius2, -radius2, 0);

glVertex3d(0, -radius2, radius2);

glEnd();

glPopMatrix(); // Pop The Matrix

nexusone

03-11-2003, 09:49 AM

When rotating the pryamid, Try rotating the the x axis, instead of the z axis.

Originally posted by beaver:

Yes, I am using push/pop. Maybe a little code will help. I am really drawing a pyramid on the line, but the axis of the pyrmid does not lay along the line between the points.

double radius = 5;

double radius2 = radius/4.0;

// Calculate vector

double dx = tX-prev_x;

double dy = tY-prev_y;

double dz = tZ-prev_z;

// calc angles

double rotz = -atan2(dx,dy) * R_2_D; // +y around z

double roty = -atan2(dz,dx) * R_2_D; // -z around y

glPushMatrix(); // Push The Matrix

glBegin(GL_LINES);

glColor3f(0,1,0); // green

glVertex3d(prev_x, prev_y, prev_z);

glVertex3d(tX, tY, tZ);

glEnd();

glPopMatrix(); // Pop The Matrix

glPushMatrix(); // Push The Matrix

glTranslated(tX, tY, tZ); // move to display site

glRotated(rotz, 0,0,1); // rotate to position

glRotated(roty, 0,1,0); // rotate to position

// draw a pyramid on the +y axis centered at origin

glBegin(GL_POLYGON);

glVertex3d(0, radius, 0);

glVertex3d(radius2, -radius2, 0);

glVertex3d(0, -radius2, -radius2);

glVertex3d(0, radius, 0);

glVertex3d(0, -radius2, -radius2);

glVertex3d(-radius2, -radius2, 0);

glVertex3d(0, radius, 0);

glVertex3d(-radius2, -radius2, 0);

glVertex3d(0, -radius2, radius2);

glVertex3d(0, radius, 0);

glVertex3d(0, -radius2, radius2);

glVertex3d(radius2, -radius2, 0);

glVertex3d(radius2, -radius2, 0);

glVertex3d(0, -radius2, -radius2);

glVertex3d(-radius2, -radius2, 0);

glVertex3d(0, -radius2, radius2);

glEnd();

glPopMatrix(); // Pop The Matrix

"When rotating the pryamid, Try rotating the the x axis, instead of the z axis."

I am not sure what you mean here. If I take the angle rotz and do a rotation about x axis, the view is even worse. Is this what you ment? Or do I need a different calculation? If I change the order of the rotations, the view looks fine in the first quadrant, but rotates downward in the second quadrant, backwards in the third, and up in the forth. When you rotate, do you rotate the pyramid vector (+y) to the data vector, or do you rotate the data vector to the pyramid? What angles and what order should I use to get the correct rotation? I am really confused at this point. Hope someone can explain. BTW, the test data being plotted is a circle about a point in space in the x,y,z direction. I am looking down (to 0,0,0) to see what rotations are going on.

nexusone

03-11-2003, 01:14 PM

If it is to be a arrow at the end of a line, then maybe this approch would be better.

In openGL the rotate/translate work in reverse order.

glTranslate // 2nd move object

glRotate // 1st Rotate of Draw_arrow axis, rotation based on 0,0,0 as center of rotate

Draw_arrow();

you can draw you arrow in the direction of zero degrees rotation.

Then do the following:

glRotate(.....) // 3rd Rotate based on center of 0,0,0

glTranslate(.....) // 2nd move object to x point from 0,0,0

glRotate(.....) // 1st Rotate based on center of 0,0,0

Draw_arrow();

When you get to the 3rd rotate your object has been translated out some x units, which cause it to be moved in a arc, with x units as it's radius.

Also you should draw with the object center being 0,0,0 will help in the positioning.

oliii

03-12-2003, 02:42 AM

You can create a "reorientation" matrix. Given the original direction of your cone (on the y axis), you give the function the direction it is supposed to point to. a bit complicated, but it's quite flexible.

Vectors need to be normalised (length = 1.0f).

void glReOrientate(const Vector& OriginalDir, const Vector& NewDir)

{

vector N;

N.Cross(OriginalDir, NewDir);

float cosa = OriginalDir * NewDir;

float sina = N.GetLength();

float a = atan2(cosa, sina) / PI * 180.0f;

N /= sina; // normalising the axis

glRotatef(a, N);

}

there are some special cases, when the directions are aligned on the same axis (N will be then equal to (0, 0, 0)), then you rotate aroubnd a perpendicular axis by 180 degrees if cosa is negative, if not do nothing (basically NewDir = OriginalDir).

also, in C++,

Vector& Vector::Cross(const Vector& A, const Vector& B)

{

x = A.y*B.z - A.z*B.y;

y = A.z*B.x - A.x*B.z;

z = A.x*B.y - A.y*B.x;

return *this;

}

float Vector::GetLength(void) const

{

return (float)sqrt(x*x + y*y + z*z);

}

float Vector:: operator*(const Vector& A) const

{

return x*A.x + y*A.y+z*A.z;

}

Vector Vector:: operator-(const Vector& A, const Vector& B) const

{

Vector C;

C.x = A.x-B.x;

C.y = A.y-B.y;

C.z = A.z-B.z;

return C;

}

bool Vector:: operator/=(float k)

{

if (fabs(k) < 0.000001f) return false;

float i = 1.0f / k;

x /= i;

y /= i;

z /= i;

return true;

}

bool Normalise(void)

{

float l = GetLength();

if (l < 0.00001f) return false;

x /= l;

y /= l;

z /= l;

return true;

}

I haven't tried that reorientation code really, but it should work. Anyone spots any mistakes?

so, for you, OriginalDir = Vector(0, 1, 0), and NewDir = your line (A, B) direction

Vector NewDir = B - A; NewDir.Normalise();

and yeah, make sure the base of your pyramid is centred at (0, 0, 0) originaly, it will helps. Then all you do is....

Vector NewDir = B-A;

NewDir /= NewDir.GetLength();

glBegin(GL_LINES);

glVertex3f(A.x, A.y, A.z);

glVertex3f(B.x, B.y, B.z);

glEnd();

glPushMatrix();

glTranslatef(B.x, B.y, B.z);

glReOrientate(Vector(0, 1, 0), NewDir);

DrawPyramid();

glPopMatrix();

[This message has been edited by oliii (edited 03-12-2003).]

Well, thank you nexusone and oliii for the hints and sample code. It still does not work :-(. I tried all 24 combinations of rotations and some were better than others, but not correct. Oliii, I put in your glReOrientation code, and it does the same thing. The pyramid slowly rotates about the point as I plot the sample data.

It seems to me this problem is the same as having an airplane fly through the sky. I am just using a cone and have a long tail. I know there is code out there someplace that can do that. So, why is this so hard? Any more suggestions or sample code would be welcome.

JustHanging

03-12-2003, 09:16 AM

Hi, maybe this will help you: http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/008501.html

Unless I've misunderstood your problem. I haven't tested that either, so good luck!

-Ilkka

Ilkka, that did the trick! It sure is simple after someone points in the rigth direction.

This is the explanation in the referenced URL for others to see.

Quote on:

Damn, I stink at this kind of math, but since nobody else is answering, I'll give it a try.

So you have to rotate cylinder's original axis (0, 0, 1) to a new one, in your case (1, 1, 1). You get the axis around which to rotate by calculating a cross product between these two. The angle is simply the angle between the two vectors, which is

arcCos(dot(a1, a2)/(|a1|*|a2|).

Then just use glRotatef(angle, axis[0], axis[1], axis[2]). Use at own risk.

-Ilkka

Quote off:

Here is the code I used and it works great.

I revised the glOrientate() function from Oliii.

....

Vector Old = Vector(prev_x, prev_y, prev_z);

Vector New = Vector(tX, tY, tZ);

Vector OrigDir = Vector(0, 1, 0); // direction of pyramid

Vector NewDir = New - Old; // line direction

NewDir.Normalize();

NewDir /= NewDir.GetLength();

glPushMatrix(); // Push The Matrix

glTranslated(tX, tY, tZ); // move to display site

glReOrientate(OrigDir, NewDir);

// draw a pyramid on the +y axis centered at origin

.....

Here is the modified glReOrientate.

void glReOrientate(const Vector& OrigDir, const Vector& NewDir)

{

Vector N;

// Cross product to get rotation axis

N.x = OrigDir.y*NewDir.z - OrigDir.z*NewDir.y;

N.y = OrigDir.z*NewDir.x - OrigDir.x*NewDir.z;

N.z = OrigDir.x*NewDir.y - OrigDir.y*NewDir.x;

// Dot product

double dot = OrigDir.X()*NewDir.X() +

OrigDir.Y()*NewDir.Y() + OrigDir.Z()*NewDir.Z();

// abs length of vectors

double lenOrig = fabs(OrigDir.GetLength());

double lenNew = fabs(NewDir.GetLength());

// rotation angle

double a = acos(dot/(lenOrig * lenNew))*R_2_D;

// do the rotation

glRotated(a, N.x, N.y, N.z);

}

Thanks again everybody for the help.

Jim

oliii

03-12-2003, 06:03 PM

it rotates? That's what it's suppose to do http://www.opengl.org/discussion_boards/ubb/smile.gif. glReorientate() Rotates the pyramid around (0, 0, 0). Well, it works for me anyway. Are you sure you for the matrix order working?

works for me...

void glReOrientate(const Vector& OriginalDir, const Vector& NewDir)

{

Vector N;

N.Cross(OriginalDir, NewDir);

float cosa = OriginalDir * NewDir;

if (cosa > 0.9999999f) // both vectors aligned

return;

if (cosa < -0.9999999f) // vectors are opposite

{

Vector M(0.0f, 1.0f, 1.0f);

float max(fabs(OriginalDir.x));

if (fabs(OriginalDir.x) > fabs(OriginalDir.y))

{

max = fabs(OriginalDir.y);

M = Vector(1.0f, 0.0f, 1.0f);

}

if (fabs(OriginalDir.z) > max)

{

M = Vector(1.0f, 1.0f, 0.0f);

}

N.Cross(OriginalDir, M);

N.Normalise();

glRotatef(180.0f, N.x, N.y, N.z); // rotate to opposite direction

return;

}

float sina = N.GetLength();

float a = (atan2(sina, cosa)) / PI * 180.0f;

N /= sina; // normalising the axis

glRotatef(a, N.x, N.y, N.z);

}

//----------------------------------------------------------------

// Projection matrix with far plane to infinity (for z-fail stencil shadows)

//----------------------------------------------------------------

void SetupProjection(void)

{

float fov = 90.0f;

float nearp = 1.0f;

float aspect = 4.0f / 3.0f;

float pinf[4][4] = { { 0, 0, 0, 0 },

{ 0, 0, 0, 0 },

{ 0, 0, 0, 0 },

{ 0, 0, 0, 0 } };

pinf[0][0] = atan(fov) / aspect;

pinf[1][1] = atan(fov);

pinf[3][2] = -2.0f * nearp;

pinf[2][2] = -1.0f;

pinf[2][3] = -1.0f;

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glLoadMatrixf(&pinf[0][0]);

}

void SetupCamera(void)

{

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

gluLookAt(100.0f, 100.0f, 100.0f,

800.0f, 100.0f, 800.0f,

0.0f, 1.0f, 0.0f);

}

void Display(void)

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

SetupProjection();

SetupCamera();

Vector A(500.0f, 0.0f, 500.0f);

Vector B(300.0f, 300.0f, 700.0f);

Vector NewDir = B-A;

NewDir /= NewDir.GetLength();

glLineWidth(4.0f);

glColor3f(1.0f, 1.0f, 1.0f);

glBegin(GL_LINES);

glVertex3f(A.x, A.y, A.x);

glVertex3f(B.x, B.y, B.z);

glEnd();

glPushMatrix();

glTranslatef(B.x, B.y, B.z);

glReOrientate(Vector(0, 1, 0), NewDir);

glScalef(30.0f, 30.0f, 30.0f);

glBegin(GL_LINES);

glVertex3f( 0.0f, 0.0f, 0.0f);

glVertex3f( 1.0f,-2.0f, 0.0f);

glVertex3f( 0.0f, 0.0f, 0.0f);

glVertex3f(-1.0f,-2.0f, 0.0f);

glVertex3f( 0.0f, 0.0f, 0.0f);

glVertex3f( 0.0f,-2.0f, 1.0f);

glVertex3f( 0.0f, 0.0f, 0.0f);

glVertex3f( 0.0f,-2.0f,-1.0f);

glEnd();

glPopMatrix();

glutSwapBuffers();

}

[This message has been edited by oliii (edited 03-12-2003).]

oliii

03-12-2003, 06:08 PM

watch out for that acosine() sign, and for times where the cross product if 0.0f. not sure if it works then. but my code works! http://www.opengl.org/discussion_boards/ubb/biggrin.gif

JustHanging

03-13-2003, 03:15 AM

Glad to see it works. oliii, I don't think those cases cause problems. When the arccos needs to change sign, the direction of the cross product changes, so it's taken care of. When the cross product=0, the angle is also 0, so again, no problem.

-Ilkka

oliii

03-13-2003, 05:14 AM

cool.

if the vectors are opposite, the matrix won't rotate it, so it's wrong. Is that right? http://www.opengl.org/discussion_boards/ubb/biggrin.gif

JustHanging

03-13-2003, 05:48 AM

Damn, that's right. I lose http://www.opengl.org/discussion_boards/ubb/frown.gif

So in case someone's using my algo, if the cross product is 0, replace it with some vector, for example (1, 0, 0). That should fix it.

-Ilkka

Oliii,

I found the problem with your first response that didn't work. You provided the following line of code that was incorrect.

Old:

float a = (atan2(cosa, sina)) / PI*180.0f;

New:

float a = (atan2(sina, cosa)) / PI*180.0f;

I tried your last version and it does work fine! Again, thanks for all the help.

Jim

Powered by vBulletin® Version 4.2.2 Copyright © 2014 vBulletin Solutions, Inc. All rights reserved.