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.

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

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

Originally posted by beaver:
[b]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[/b]

“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.

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.

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.yB.z - A.zB.y;
y = A.zB.x - A.xB.z;
z = A.xB.y - A.yB.x;

return *this;
}

float Vector::GetLength(void) const
{
return (float)sqrt(xx + yy + z*z);
}

float Vector:: operator*(const Vector& A) const
{
return xA.x + yA.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.

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.yNewDir.z - OrigDir.zNewDir.y;
N.y = OrigDir.zNewDir.x - OrigDir.xNewDir.z;
N.z = OrigDir.xNewDir.y - OrigDir.yNewDir.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

it rotates? That’s what it’s suppose to do . 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).]

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!

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

cool.

if the vectors are opposite, the matrix won’t rotate it, so it’s wrong. Is that right?

Damn, that’s right. I lose

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)) / PI180.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