4

I managed to draw a bezier curve like:

glColor3f(0,1,0);
glBegin(GL_LINE_STRIP);
for (int i = 3; i < nPt; i+=3) {
    glColor3f(0,0,0);
    for (float k = 0; k < NLINESEGMENT+1; k++) {
        float x = pow(1.0-k/NLINESEGMENT,3)*ptList[i-3].x +
            3*(k/NLINESEGMENT)*pow(1.0-k/NLINESEGMENT, 2) * ptList[i-2].x +
            3*(1.0-k/NLINESEGMENT)*pow(k/NLINESEGMENT, 2) * ptList[i-1].x +
            pow(k/NLINESEGMENT, 3)*ptList[i].x;
        float y = pow(1.0-k/NLINESEGMENT,3)*ptList[i-3].y +
            3*(k/NLINESEGMENT)*pow(1.0-k/NLINESEGMENT, 2) * ptList[i-2].y +
            3*(1.0-k/NLINESEGMENT)*pow(k/NLINESEGMENT, 2) * ptList[i-1].y +
            pow(k/NLINESEGMENT, 3)*ptList[i].y;
        glVertex2d(x,y);
    }
}
glEnd();

Now I want to add tangent arrows for each point, how can I do that? I am given a function that draws an arrow. So I believe I need to just rotate the reference frame and draw that arrow. But how do I compute the rotation? I think I need to differenciate the equations, but the question still remains, how do I use that?

UPDATE

enter image description here

As every 4th point is put, a curve is drawn.

I am supposed to achieve something like below

enter image description here

Full Source

UPDATE 2

Ok I made an attempt at drawing the tangents like:

glColor3f(0,1,0);
for (int i = 3; i < nPt; i+=3) {
    for (int n = 0; n < NOBJECTONCURVE; n++) {
        float t = (float)n/NOBJECTONCURVE;
        float x0 = points[i-3].x,
                x1 = points[i-2].x,
                x2 = points[i-1].x, 
                x3 = points[i].x;
        float y0 = points[i-3].y,
                y1 = points[i-2].y,
                y2 = points[i-1].y, 
                y3 = points[i].y;

        float x = pow(1.0-t, 3) * points[i-3].x +
            3 * t * pow(1.0 - t, 2) * points[i-2].x +
            3 * (1.0 - t) * pow(t, 2) * points[i-1].x +
            pow(t, 3)*points[i].x;
        float y = pow(1.0-t, 3) * points[i-3].y +
            3 * t * pow(1.0 - t, 2) * points[i-2].y +
            3 * (1.0 - t) * pow(t, 2) * points[i-1].y +
            pow(t, 3)*points[i].y;

        float dx = -3*(1-t)*x0 + 3*x1*((2*t)*(t-1)+pow((1-t),2)) + 3*x2*(2*t*(1-t)-pow(t,2)) + 3*pow(t,2)*x3;
        float dy = -3*(1-t)*y0 + 3*y1*((2*t)*(t-1)+pow((1-t),2)) + 3*y2*(2*t*(1-t)-pow(t,2)) + 3*pow(t,2)*y3;
        float angle = atan(dy/dx);

        glPushMatrix();
        glTranslatef(x, y, 0);
        glRotatef(angle * 180 / 3.14159265, 0, 0, 1);
        drawRightArrow();
        glPopMatrix();
    }
}

enter image description here

But as you can see the tangents appear to be incorrect especially in the middle of a bezier curve?

Jiew Meng
  • 84,767
  • 185
  • 495
  • 805
  • 1
    IMO in this type of questions a screenshot of what you done, can make the question better. – masoud Oct 26 '13 at 09:54
  • 1
    @JiewMeng In `x0, y0` tangent angle is `ang=arctan(dy/dx)`. Arrow's coordinates (arrow of length `l`) then: `(x0,y0) - (l * cos(ang), l * sin(ang))`. – lapk Oct 26 '13 at 10:17
  • @PetrBudnik: Why do you calculate an angle if you convert it to a vector again right afterwards? – Nico Schertler Oct 26 '13 at 15:45
  • @NicoSchertler Because angle is independent of `dx` and `dy` which, generally, vary from point to point. – lapk Oct 26 '13 at 16:42

1 Answers1

5

Because we don't want to interrupt the line strip, it is reasonable to make another loop that draws the arrows. In this loop, we can skip some steps, because we probably don't want arrows after each step. The arrows can be drawn as a 'GL_LINES' primitive.

For readability I'd recommend to define the parameter t in the inner loop as

float t = (float)k/NLINESEGMENT;

Now we need to calculate the derivative of the curve with respect to t at this point. This derivative can be calculated for each coordinate independently. It looks like this question is a homework, so I'll leave that to you

float dx = ... //derivative of x-component with respect to t
float dy = ... //derivative of y-component with respect to t

We will also need the curve point. Ideally you have saved it in the previous loop in an array or similar structure.

So we can draw the arrow's base line (where s is a custom scale factor):

glVertex2d(x, y);
glVertex2d(x + s * dx, y + s * dy);

We will also need the actual arrow. An orthogonal vector to the arrow direction is (-dy, dx). So we can just combine the direction and the orthogonal direction to get the arrow:

glVertex2d(x + s * dx, y + s * dy);
glVertex2d(x + 0.9 * s * dx - 0.1 * dy, y + 0.9 * s * dy + 0.1 * dx);
glVertex2d(x + s * dx, y + s * dy);
glVertex2d(x + 0.9 * s * dx + 0.1 * dy, y + 0.9 * s * dy - 0.1 * dx);

This will result in a 90° arrow. You can change the angle by adjusting the coefficients (0.9 and 0.1).

Update

Your derivative is closer to the actual solution, but still contains some mistakes: Bezier derivative

Furthermore, when calculating the angle, use the atan2 function. This allows you to get angles greater than PI/2:

float angle = atan2(dy,dx);
Nico Schertler
  • 32,049
  • 4
  • 39
  • 70
  • `dx` and `dy`, generally, vary from point to point. In this case your arrows will have different lengths. Unless you normalize on `sqrt(dx * dx + dy * dy)`. – lapk Oct 26 '13 at 16:43
  • It was my intention to make the arrows' lengths proportional to the derivative's length. But that depends on the task, of course. – Nico Schertler Oct 26 '13 at 17:15
  • This will not make them proportional to the _"derivative length"_. Derivative is `dy/dx` (it's actually `lim dy/dx with dx->0`). Its value is the same for, say, `dx = 1, dy = 0.5` and `dx = 10000, dy = 5000`, and is equal to `0.5`. But your arrows' lengths in this case will differ 10000 times. That's why you have to work with the angle one way or another (you can use `dy/dx = tan(ang)` if you like). – lapk Oct 26 '13 at 17:39
  • I'm talking about the derivatives of `x` and `y` with respect to `t` (`dx/dt` and `dy/dt`). The derivative of `y` with respect to `x` is somewhat harder to calculate and may contain discontinuities (for vertical line parts). Therefore, it is not a good idea to use it for displaying the tangent vector. Furthermore, `y` can usually not be described as a function of `x`. There may be more than 1 `y`-value for a given `x`. – Nico Schertler Oct 26 '13 at 17:52
  • IMHO, you do not need `t`. You have an array of **discrete** 2D points representing a function. You need to find derivative `dy/dx` in each point numerically to get the tangent line. Again, the function is not differentiable analytically - it's a set of discrete points... For `dx = 0` we have infinite derivative, so we just draw a vertical line... Furthermore, the fact that a function has more than one `y` for a given `x` does not make it non-differentiable. Think of the equation of a circle. Derivative exits everywhere **except**, ironically, for two points with a single `y` value. – lapk Oct 26 '13 at 22:17
  • I suppose, I was talking about the actual points which make up the plot line while you were talking about the defining the Bezier curve. – lapk Oct 26 '13 at 23:58
  • Regarding the derivative, do I differenciate wrt. `k`? If I do ur method I need to apply chain rule, differenciating `k/NSEGMENTS` wrt `k`? My attempt at differenciating yielded something quite complex? http://i.imgur.com/hY07Wtb.png – Jiew Meng Oct 27 '13 at 12:54
  • @JiewMeng: It is simpler to substitute `k/NLINESEGMENT` with `t` and then differentiate wrt. `t`. I think you got the basic point, but there are some mistakes in your formula. E.g. the first part `d/dt (1-t)^3*P0 = -3P0*(1-t)^2`. The constant factor `P0` stays, then use the chain rule. Derivative of inner function is `-1`, derivative of outer function is `3*(...)^2` where `...` is the original inner function. – Nico Schertler Oct 27 '13 at 16:58
  • @NicoSchertler, thanks for your help so far. I have attempted an implementation of ur suggestion but the result appears wrong. See update 2 in OP, did I make a mistake in differenciation or something else? – Jiew Meng Oct 29 '13 at 06:11
  • @NicoSchertler Can you tell me why are we adding x+dx and why not simply dx. since dx/dt is the equation of the tangent wrt to t. I tired using the value of dx before was at a diif position altogether. I then used your logic of adding x to it and it worked fine. But i am not able to get the logic behind adding x to dx. If you could please let me know. Thanks – sss999 Sep 30 '16 at 01:30
  • @NicoSchertlerActually i also needed to make a normal and binormal at each point so if i can get the logic behind this I can implement those too Thanks – sss999 Sep 30 '16 at 01:43
  • @sss999 We add `x` and `dx` because the arrow starts at `x` and then goes `dx` further. I.e. `x + dx` is just the end point of the arrow. If you need binormals, you are likely to be in 3D. So this is somewhat different. – Nico Schertler Sep 30 '16 at 11:56