# 3d line offset?

Hello!

Perhaps, attached file will explain best what I want to achieve. I want to write procedure in Delphi which would create tunnel “around” line. I know that I can do this with GL_QUADS, but the problem is that I don’t know how to compute arguments for GL_QUADS. My function would have (x1, y1, z1, x2, y2, z2, width of the tunnel, height of the tunnel). Horizontal edges would be parallel to X-Y plane and begining and ending quad would be perpendicular to the line (if one can call it this way). Just like on the picture.

Thank You for answer,

Cheers,

Perhaps something like this ?
(ok, it’s a C source but I have never coded in the Delphi language)

void Tunnel(float x1, float y1, float z1, float width, float height, float x2, float y2, float z2)
{
float x=x1;
float y=y1;
float dz=z2-z1;
float dx=(x2-x1)/dz;
float dy=(y2-y1)/dz;
for( z=z1 ; z<z2 ;x+=dx, y+=dy, z++)
{
glVertex3f(x-width/2,y-height/2,z);
glVertex3f(x+width/2,y-height/2,z);
glVertex3f(x-width/2,y+height/2,z);
glVertex3f(x+width/2,y+height/2,z);
}
glEnd();
}

(this work only if z1 < z2)

Thanks :),

I will check it. No problem. OpenGL is the same in C and in Delphi. The only difference is with for command. Thank You again, I’ll let You know if it works for me (At the moment I don’t have Delphi at hand).

Happy Weekend!,

Mac

But I think to best understand your problem now …

You don’t want that the quads are alls parallel to the Z plane but follow the normal at each point on the line ?
(cf. cross product with (1,0,0) and (nextPoint - currentPoint) )

Yes, I would like to create one quad at the begining and one at the end of the line. And, yes, it would be best that they would be normal to the line. Exactly that.

Mac.

For the line case, I think that it isn’t “too difficult” (because the normal is constant on the line)

if you want use a curve (and not only a line) between P1 and P2, this can begin very more difficult …

=> so I see how to resolve the more general/difficult case

For me, line is quite enough :). If I could do this, that would solve my problem.

Mac.

I’ve thought over Your solution. But what if line is vertical? (parallel to Z axis)?. I would, definitely, have to have quads normal to the line and I, completely, don’t know how to set about this task :(.

Mac.

Mac,

I have make an basic example that draw a tunnel in wireframe mode.

But it don’t handle the “normal to the line” for the instant, this is only the implementation of my first response.

On the other side, it handle already multiples segments
(you have only to modify the curve[] array for to generate another tunnel)

``````

gcc lineoffset.c -o lineoffset -lGL -lm -lglut -lGLU

/* lineoffset.c

YLP : 16/04/20011

basic draft for to draw a tunnel

*/

#include <GL/glut.h>

typedef struct {
float x;
float y;
float z;
} Point3D;

#define WIDTH 800
#define HEIGHT 600
#define SCALE  4.0f
#define TUNNELSIZE 5

Point3D curve[]=
{
{0,0,0},
{7,3,1},
{11,13,2},
{13,17,3},
{22,21,4},
{27,25,5},
{31,32,6},
{32,37,7},
{45,42,8},
{47,42,9}
};

#define NB_CONTROLPOINTS (sizeof(curve)/sizeof(Point3D))

void Tunnel_v0(float x1, float y1, float z1, float width, float height, float x2, float y2, float z2)
{
float x=x1;
float y=y1;
float dz=z2-z1;
float dx=(x2-x1)/dz;
float dy=(y2-y1)/dz;
float z;

for( z=z1 ; z<z2 ;x+=dx, y+=dy, z++)
{
glVertex3f(x-width/2,y-height/2,z);
glVertex3f(x+width/2,y-height/2,z);
glVertex3f(x+width/2,y+height/2,z);
glVertex3f(x-width/2,y+height/2,z);
}
glEnd();
}

void Tunnel_v1(float x1, float y1, float z1, float width, float height, float x2, float y2, float z2)
{

glVertex3f(x1-width/2,y1-height/2,z1);
glVertex3f(x1+width/2,y1-height/2,z1);
glVertex3f(x2+width/2,y2-height/2,z2);
glVertex3f(x2-width/2,y2-height/2,z2);

glVertex3f(x1-width/2,y1-height/2,z1);
glVertex3f(x2-width/2,y2-height/2,z2);
glVertex3f(x2-width/2,y2+height/2,z2);
glVertex3f(x1-width/2,y1+height/2,z1);

glVertex3f(x1+width/2,y1-height/2,z1);
glVertex3f(x2+width/2,y2-height/2,z2);
glVertex3f(x2+width/2,y2+height/2,z2);
glVertex3f(x1+width/2,y1+height/2,z1);

glVertex3f(x1-width/2,y1+height/2,z1);
glVertex3f(x1+width/2,y1+height/2,z1);
glVertex3f(x2+width/2,y2+height/2,z2);
glVertex3f(x2-width/2,y2+height/2,z2);

glEnd();
}

void InitOpenGL()
{

/* attributes */

glClearColor(0.0, 0.0, 0.0, 0.0); /* black background */
glColor3f(1.0, 1.0, 1.0);  /* white color */
glPolygonMode(GL_FRONT,GL_LINE);
//glEnable(GL_CULL_FACE);
//glCullFace(GL_BACK);

/* set up viewing */
glViewport(0, 0, WIDTH, HEIGHT);

glMatrixMode(GL_PROJECTION);
glOrtho(0.0, WIDTH, 0.0, HEIGHT,-10,10);
glMatrixMode(GL_MODELVIEW);

glTranslatef(WIDTH/2, HEIGHT/2, 0.0f);
glScalef(SCALE,SCALE,1.0f);
}

void display()
{
int i;

glClear(GL_COLOR_BUFFER_BIT);

// temporally used for to determine the good glOrtho/glMatrixMode/glTranslate/glScale values ...
/*
glBegin(GL_LINES);
glVertex3f(-WIDTH/10,-HEIGHT/10,1);
glVertex3f(WIDTH/10,HEIGHT/10,1);
glEnd();
*/

/* Draw the path */
glColor3f(1.0f,1.0f,1.0f);
glBegin(GL_LINES);
for(i=0;i<NB_CONTROLPOINTS-1;i++)
{
glVertex3fv(&curve[i].x);
glVertex3fv(&curve[i+1].x);
}
glEnd();

/* draw the tunnel */
glColor3f(1.0f,0.0f,0.0f); /* red color */
for(i=0;i<NB_CONTROLPOINTS-2;i++)
{
Tunnel_v1(
curve[i].x, curve[i].y, curve[i].z,
TUNNELSIZE, TUNNELSIZE,
curve[i+1].x, curve[i+1].y, curve[i+1].z
);
}

glFlush();

}

void main(int argc, char** argv)
{

/* Standard GLUT initialization */

glutInit(&argc,argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); /* default, not needed */
glutInitWindowSize(WIDTH,HEIGHT); /* WIDTH  x HEIGHT pixel window */
glutInitWindowPosition(0,0); /* place window top left on display */
glutCreateWindow("Line offset"); /* window title */
glutDisplayFunc(display); /* display callback invoked when window opened */

InitOpenGL(); /* set attributes */

glutMainLoop(); /* enter event loop */
}

``````

I study about the implementation of the “normal to the line” version but this isn’t as trivial as it

But I have already the idea about the method for to make this
(a cross product for to have the normal, this is easy + rotations of the faces that I find not as easy as this)

Thanks :). Yor’re great :). I wondered. Everything revolves around pure 3d mathematics. Perhaps if I created, on basis of points, for example, (0,0,0) (1,1,1) mathematical formula (like x = y = z or x - y - z = 0), and make the program create each formula for each line, perhaps that would help?. If, for example, I would project this line on X-Y plane, I would get
y = x function. Now, the problem would be how to create offset from this function?. y = -x would be normal to it. Now, for example, f(1/2) = - 1/2 and f(-1/2) = 1/2. I would get width of the tunnel equal 2 * sqare root of (1/2 * 1/2 + 1/2 * 1/2) (If I didn’t make any mistake). I could do it with X-Z and Y-Z planes as well. But how to put it all together in 3d?. And how to write it in programming language?. No idea :(.

Mac.

I don’t think to have all understand to your last post …

You want to use different formulas for each axis and/or plane ???

Note that a function can return a 3D vector, it isn’t limited to return only a float …

Well, on basis of x1,y1,z1 and x2,y2,z2 I could create mathematical formula of line, like, for example x=y=z, or, let’s say: 1/2x=y=z and so on. With lines only coefficients would change (like 1 in the first example and 1/2 in the second). x=y=z we can express as x=y, x=z and y=z. Normal to x=y would be expressed by formula: x=-y, and respectively x=-z and y=-z. You can see it if You write it on charts (like f(x)=y). It is mathematical notation. But how to express it in programming language?. I am completely devoid of ideas :).

Cheers,

Mac.

I have assembled somes variations of tunnels versions and use a tunnel_version variable that select the wanted version :

``````

int  tunnel_version = 3;

void Tunnel(float x1, float y1, float z1, float width, float height, float x2, float y2, float z2)
{
switch(tunnel_version)
{
case 1  : Tunnel_v1(x1, y1, z1, width, height, x2, y2, z2); break;
case 2  : Tunnel_v2(x1, y1, z1, width, height, x2, y2, z2); break;
case 3  : Tunnel_v3(x1, y1, z1, width, height, x2, y2, z2); break;
default : Tunnel_v0(x1, y1, z1, width, height, x2, y2, z2); break;
}
// return;
}

``````
``````

void Tunnel_v0(float x1, float y1, float z1, float width, float height, float x2, float y2, float z2)
{
float x=x1;
float y=y1;
float dz=z2-z1;
float dx=(x2-x1)/dz;
float dy=(y2-y1)/dz;
float z;

glColor3f(1.0f,0.0f,0.0f);
for( z=z1 ; z<z2 ;x+=dx, y+=dy, z++)
{
glVertex3f(x-width/2,y-height/2,z);
glVertex3f(x+width/2,y-height/2,z);
glVertex3f(x+width/2,y+height/2,z);
glVertex3f(x-width/2,y+height/2,z);
}
glEnd();

}

void Tunnel_v1(float x1, float y1, float z1, float width, float height, float x2, float y2, float z2)
{
// draw only quads at curve control points
glColor3f(1.0f,0.0f,0.0f);
glVertex3f(x1-width/2,y1-height/2,z1);
glVertex3f(x1+width/2,y1-height/2,z1);
glVertex3f(x1+width/2,y1+height/2,z1);
glVertex3f(x1-width/2,y1+height/2,z1);
glEnd();
}

void Tunnel_v2(float x1, float y1, float z1, float width, float height, float x2, float y2, float z2)
{

// draw walls in wireframe
glColor3f(1.0f,0.0f,0.0f);

glVertex3f(x1-width/2,y1-height/2,z1);
glVertex3f(x1+width/2,y1-height/2,z1);
glVertex3f(x2+width/2,y2-height/2,z2);
glVertex3f(x2-width/2,y2-height/2,z2);

glVertex3f(x1-width/2,y1-height/2,z1);
glVertex3f(x2-width/2,y2-height/2,z2);
glVertex3f(x2-width/2,y2+height/2,z2);
glVertex3f(x1-width/2,y1+height/2,z1);

glVertex3f(x1+width/2,y1-height/2,z1);
glVertex3f(x2+width/2,y2-height/2,z2);
glVertex3f(x2+width/2,y2+height/2,z2);
glVertex3f(x1+width/2,y1+height/2,z1);

glVertex3f(x1-width/2,y1+height/2,z1);
glVertex3f(x1+width/2,y1+height/2,z1);
glVertex3f(x2+width/2,y2+height/2,z2);
glVertex3f(x2-width/2,y2+height/2,z2);

glEnd();
}

void Tunnel_v3(float x1, float y1, float z1, float width, float height, float x2, float y2, float z2)
{

// draw the normal
float n[3], invlen;

n[0] = x2 - x1;
n[1] = y2 - y1;
n[2] = z2 - z1;
invlen = NORMALSIZE / sqrt( n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
n[0] *= invlen;
n[1] *= invlen;
n[2] *= invlen;

glColor3f(0.0f,1.0f,0.0f);
glBegin(GL_LINES);
glVertex3f(x1,y1,z1);
glVertex3f(x1+n[0], y1+n[1], z1+n[2]);
glEnd();

Tunnel_v2(x1, y1, z1, width, height, x2, y2, z2);
}

``````

It’s from the P0(x1,y1,z1) and P1(x2,y2,z2) positions used into the Tunnel() switch function that you want to use for the generation of the “x=y=z mathematical formula” ?

Or alls (x,y,z) positions that satisfy the conditions x=y x=z and y=z ???
(cf. this seem intersection of 3D planes or something like this)

You want to generate quads at regular space along the path such as this ?

``````

#define STEPSIZE 0.2f

void Tunnel_v4(float x1, float y1, float z1, float width, float height, float x2, float y2, float z2)
{
float x=x1;
float y=y1;
float dz=(z2-z1);
float dx=((x2-x1)/dz)*STEPSIZE;
float dy=((y2-y1)/dz)*STEPSIZE;
float z;

glColor3f(1.0f,0.0f,0.0f);
for( z=z1 ; z<z2 ;x+=dx, y+=dy, z+=dz*STEPSIZE)
{
glVertex3f(x-width/2,y-height/2,z);
glVertex3f(x+width/2,y-height/2,z);
glVertex3f(x+width/2,y+height/2,z);
glVertex3f(x-width/2,y+height/2,z);
}
glEnd();

}

``````

But with the quads/planes “oriented” ?

What about interpolate the normal in the similar way that with the P0 and P1 positions ?
(cf. interpolate a curved “rectangular cylinder” between two oriented planes)

With “trues interpolations” of course, not only with steps at differents z values

Hello The Little Body :)!,

Man, You’re just great :). I compiled Your program in gcc, in linux and I was really, really impressed :). I implemented Your procedure in Delphi, but changed QUADS to LINES. In my program QUADS are not shown properly in wireframe mode, but
there’s no such problem with LINES :). It’s working like a treat :). Thank You very, very much :). All the best :). French rulez :).

Cheers :),

Mac.