如何清除我的 BezierCurve 向量以停止在彼此之上写入坐标?

How to clear my BezierCurve vector to stop writing coordinates on top of each other?

我构建了一个贝塞尔曲线工具,每次计算每个曲线段的坐标时,它们都存储在一个向量中。每一帧我都一遍又一遍地在上面添加整条曲线的点。也就是说,第 1 帧可能有 { p1, p2, p3 },然后第 2 帧可能有 { p1, p2, p3, p1, p2, p3 } 等等。当渲染函数末尾的循环在点 p3 和 p1 之间绘制线时,这将导致线自行回环。我正在努力寻找应该在哪里以及如何清除帧之间的 BezierCurve 矢量。在glClear()之后或glSwapBuffers()之前清空,只显示之前绘制的曲线段,点与点之间有一条直线。 我基本上希望点之间的直线消失,我知道为什么会这样。我的代码如下:

#include <iostream>
#include <stdlib.h>
#include <GL/glut.h>
#include <vector>
#include <math.h>

using namespace std;

//Point class for taking the points
class Point {
public:
    float x, y;

    void setxy(float x2, float y2)
    {
        x = x2; y = y2;
    }

    //operator overloading for '=' sign
    const Point& operator=(const Point& rPoint)
    {
        x = rPoint.x;
        y = rPoint.y;
        return *this;
    }

};

int SCREEN_HEIGHT = 500;
vector<Point> Points;
Point Tangent;
Point inverseTangent;
Point cursorLocationLive;
int TangentsSize = 0;
vector<Point> Tangents(TangentsSize);
vector<Point> inverseTangents(TangentsSize);
vector<Point> BezierCurve;
bool MouseReleased = false;

void drawDot(Point p1)
{
    glBegin(GL_POINTS);
    glVertex2i(p1.x, p1.y);
    glEnd();
}

void drawLine(Point p1, Point p2)
{
    glBegin(GL_LINE_STRIP);
    glVertex2f(p1.x, p1.y);
    glVertex2f(p2.x, p2.y);
    glEnd();
}

float interpolate(float n1, float n2, float perc)
{
    float diff = n2 - n1;

    return n1 + (diff * perc);
}

void myMouse(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON)
    {
        if (state == GLUT_DOWN)
        {
            MouseReleased = false;
            // Store points into Points vector on click
            Point point;
            point.setxy(x, SCREEN_HEIGHT - y);
            Points.push_back(point);

            // Tangents are set to the cursor position
            Tangent.setxy(x, SCREEN_HEIGHT - y);
            inverseTangent.x = (2 * Points[Points.size() - 1].x) - Tangent.x;
            inverseTangent.y = (2 * Points[Points.size() - 1].y) - Tangent.y;

            /*Add new element to Tangent & inverseTangent so when we draw the curve
            the tangents are accessed at the right index*/
            TangentsSize++;
        }
        else if (state == GLUT_UP)
        {
            MouseReleased = true;
            // Upon mouse release store tangent and inverse tangent into separate vectors
            Tangents.push_back(Tangent);
            inverseTangents.push_back(inverseTangent);
        }
    }
}

void passiveMotion(int x, int y)
{
    // Sets the location of cursor while moving with no buttons pressed
    cursorLocationLive.setxy(x, SCREEN_HEIGHT - y);
}

void motion(int x, int y)
{
    // Sets the coordinates of the tangents when mouse moves with a button held down
    Tangent.setxy(x, SCREEN_HEIGHT - y);
    inverseTangent.x = (2 * Points[Points.size() - 1].x) - Tangent.x;
    inverseTangent.y = (2 * Points[Points.size() - 1].y) - Tangent.y;
}

void myDisplay()
{
    glClear(GL_COLOR_BUFFER_BIT);

    // Draw main points in red
    glColor3f(255, 0, 0);
    for (int i = 0; i < Points.size(); i++)
    {
        drawDot(Points[i]);
    }

    // If there is a starting point draw a line to cursor from last drawn point in passive motion
    if (Points.size() > 0)
    {
        glColor3f(0, 0, 0);
        drawLine(Points[Points.size() - 1], cursorLocationLive);
    }

    // Draw live tangent dots in green
    glColor3f(0, 255, 0);
    drawDot(Tangent);
    drawDot(inverseTangent);
    // Draw live tangent lines in blue
    glColor3f(0, 0, 255);
    drawLine(Tangent, inverseTangent);

    for (int i = 0; i < Tangents.size(); i++)
    {
        // Draw stored tangent dots in green
        glColor3f(0, 255, 0);
        drawDot(Tangents[i]);
        drawDot(inverseTangents[i]);
        // Draw stored tangent lines in blue
        glColor3f(0, 0, 255);
        drawLine(Tangents[i], inverseTangents[i]);
    }

    // Loop through all points
    for (int i = 0; i < Points.size(); i++)
    {
        // If there are two points draw the first segment
        if (Points.size() == 2)
        {
            // p1 is the start of the curve set at first point
            Point p1;
            p1 = Points[0];

            float i;
            // Calculate curve coordinates
            for (float j = 0; j <= 100; j++)
            {
                i = j / 100;
                // The Green Lines
                float xa = interpolate(Points[0].x, inverseTangents[0].x, i);
                float ya = interpolate(Points[0].y, inverseTangents[0].y, i);
                float xb = interpolate(inverseTangents[0].x, inverseTangent.x, i);
                float yb = interpolate(inverseTangents[0].y, inverseTangent.y, i);
                float xc = interpolate(inverseTangent.x, Points[1].x, i);
                float yc = interpolate(inverseTangent.y, Points[1].y, i);

                // The Blue Line
                float xm = interpolate(xa, xb, i);
                float ym = interpolate(ya, yb, i);
                float xn = interpolate(xb, xc, i);
                float yn = interpolate(yb, yc, i);

                // The Black Dot
                float x2 = interpolate(xm, xn, i);
                float y2 = interpolate(ym, yn, i);

                Point p2;
                p2.setxy(x2, y2);

                drawLine(p1, p2);

                p1 = p2;

                // Prevents curves generated during mouse motion from being stored
                if (MouseReleased)
                {
                    // Store curvature into Bezier Points
                    BezierCurve.push_back(p2);
                }
            }
        }
        // Second segment onwards
        else if (Points.size() > 2)
        {
            // p1 is the start of the curve set to second last point
            Point p1;
            p1 = Points[Points.size() - 2];

            float i;
            // Calculate curve coordinates
            for (float j = 0; j <= 100; j++)
            {
                i = j / 100;
                // The Green Lines
                float xa = interpolate(Points[Points.size() - 2].x, Tangents[TangentsSize - 2].x, i);
                float ya = interpolate(Points[Points.size() - 2].y, Tangents[TangentsSize - 2].y, i);
                float xb = interpolate(Tangents[TangentsSize - 2].x, inverseTangent.x, i);
                float yb = interpolate(Tangents[TangentsSize - 2].y, inverseTangent.y, i);
                float xc = interpolate(inverseTangent.x, Points[Points.size() - 1].x, i);
                float yc = interpolate(inverseTangent.y, Points[Points.size() - 1].y, i);

                // The Blue Line
                float xm = interpolate(xa, xb, i);
                float ym = interpolate(ya, yb, i);
                float xn = interpolate(xb, xc, i);
                float yn = interpolate(yb, yc, i);

                // The Black Dot
                float x2 = interpolate(xm, xn, i);
                float y2 = interpolate(ym, yn, i);

                Point p2;
                p2.setxy(x2, y2);

                drawLine(p1, p2);

                p1 = p2;

                // Prevents curves generated during mouse motion from being stored
                if (MouseReleased)
                {
                    // Store curvature into Bezier Points
                    BezierCurve.push_back(p2);
                }
            }
        }
    }

    // Draw all bezier curvature
    for (int i = 1; i < BezierCurve.size(); i++)
    {
        drawLine(BezierCurve[i - 1], BezierCurve[i]);
    }

    glutSwapBuffers();
}

void timer(int)
{
    glutTimerFunc(1000 / 60, timer, 0);
    glutPostRedisplay();
}

int main(int argc, char* argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(640, 500);
    glutInitWindowPosition(100, 150);
    glutCreateWindow("Bezier Curve");
    glutDisplayFunc(myDisplay);
    glutIdleFunc(myDisplay);
    glutTimerFunc(0, timer, 0);
    glutMouseFunc(myMouse);
    glutPassiveMotionFunc(passiveMotion);
    glutMotionFunc(motion);
    glClearColor(255, 255, 255, 0.0);
    glPointSize(3);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 640.0, 0.0, 500.0);
    glutMainLoop();

    return 0;
}

您需要检测鼠标点击何时从向下变为向上:

bool prevMouse;
...
// at the end of Display()
prevMouse = MouseReleased;

然后我们检查鼠标点击何时从按下变为释放并添加行 BezierCurve:

if (PrevMouse == 0 && MouseReleased)
{
    // Store curvature into Bezier Points
    BezierCurve.push_back(p2);
}

for 循环中的两个代码路径,if (Points.size() == 2)else if (Points.size() > 2) 可以简化为 if (Points.size() >= 2),就此而言,for 循环是无关紧要的,我们不需要要更新之前任何点的贝塞尔曲线,只需更新两个最新点 Points[Points.size() - 2]Points[Points.size() - 1].

之间的曲线

最终代码:

#include <iostream>
#include <stdlib.h>
#include <GL/glut.h>
#include <vector>
#include <math.h>

using namespace std;

//Point class for taking the points
class Point {
public:
    float x, y;

    void setxy(float x2, float y2)
    {
        x = x2; y = y2;
    }

    //operator overloading for '=' sign
    const Point& operator=(const Point& rPoint)
    {
        x = rPoint.x;
        y = rPoint.y;
        return *this;
    }

};

int SCREEN_HEIGHT = 500;
vector<Point> Points;
Point Tangent;
Point inverseTangent;
Point cursorLocationLive;
int TangentsSize = 0;
vector<Point> Tangents(TangentsSize);
vector<Point> inverseTangents(TangentsSize);
vector<Point> BezierCurve;
bool MouseReleased = false;
bool PrevMouse = false;

void drawDot(Point p1)
{
    glBegin(GL_POINTS);
    glVertex2i(p1.x, p1.y);
    glEnd();
}

void drawLine(Point p1, Point p2)
{
    glBegin(GL_LINE_STRIP);
    glVertex2f(p1.x, p1.y);
    glVertex2f(p2.x, p2.y);
    glEnd();
}

float interpolate(float n1, float n2, float perc)
{
    float diff = n2 - n1;

    return n1 + (diff * perc);
}

void myMouse(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON)
    {
        if (state == GLUT_DOWN)
        {
            MouseReleased = false;
            // Store points into Points vector on click
            Point point;
            point.setxy(x, SCREEN_HEIGHT - y);
            Points.push_back(point);

            // Tangents are set to the cursor position
            Tangent.setxy(x, SCREEN_HEIGHT - y);
            inverseTangent.x = (2 * Points[Points.size() - 1].x) - Tangent.x;
            inverseTangent.y = (2 * Points[Points.size() - 1].y) - Tangent.y;

            /*Add new element to Tangent & inverseTangent so when we draw the curve
            the tangents are accessed at the right index*/
            TangentsSize++;
        }
        else if (state == GLUT_UP)
        {
            MouseReleased = true;
            // Upon mouse release store tangent and inverse tangent into separate vectors
            Tangents.push_back(Tangent);
            inverseTangents.push_back(inverseTangent);
        }
    }
}

void passiveMotion(int x, int y)
{
    // Sets the location of cursor while moving with no buttons pressed
    cursorLocationLive.setxy(x, SCREEN_HEIGHT - y);
}

void motion(int x, int y)
{
    // Sets the coordinates of the tangents when mouse moves with a button held down
    Tangent.setxy(x, SCREEN_HEIGHT - y);
    inverseTangent.x = (2 * Points[Points.size() - 1].x) - Tangent.x;
    inverseTangent.y = (2 * Points[Points.size() - 1].y) - Tangent.y;
}

void myDisplay()
{
    glClear(GL_COLOR_BUFFER_BIT);

    // Draw main points in red
    glColor3f(255, 0, 0);
    for (int i = 0; i < Points.size(); i++)
    {
        drawDot(Points[i]);
    }

    // If there is a starting point draw a line to cursor from last drawn point in passive motion
    if (Points.size() > 0)
    {
        glColor3f(0, 0, 0);
        drawLine(Points[Points.size() - 1], cursorLocationLive);
    }

    // Draw live tangent dots in green
    glColor3f(0, 255, 0);
    drawDot(Tangent);
    drawDot(inverseTangent);
    // Draw live tangent lines in blue
    glColor3f(0, 0, 255);
    drawLine(Tangent, inverseTangent);

    for (int i = 0; i < Tangents.size(); i++)
    {
        // Draw stored tangent dots in green
        glColor3f(0, 255, 0);
        drawDot(Tangents[i]);
        drawDot(inverseTangents[i]);
        // Draw stored tangent lines in blue
        glColor3f(0, 0, 255);
        drawLine(Tangents[i], inverseTangents[i]);
    }

    // Loop through all points
    if (Points.size() >= 2)
    {
        // p1 is the start of the curve set to second last point
        Point p1;
        p1 = Points[Points.size() - 2];

        float i;
        // Calculate curve coordinates
        for (float j = 0; j <= 100; j++)
        {
            i = j / 100;
            // The Green Lines
            float xa = interpolate(Points[Points.size() - 2].x, Tangents[TangentsSize - 2].x, i);
            float ya = interpolate(Points[Points.size() - 2].y, Tangents[TangentsSize - 2].y, i);
            float xb = interpolate(Tangents[TangentsSize - 2].x, inverseTangent.x, i);
            float yb = interpolate(Tangents[TangentsSize - 2].y, inverseTangent.y, i);
            float xc = interpolate(inverseTangent.x, Points[Points.size() - 1].x, i);
            float yc = interpolate(inverseTangent.y, Points[Points.size() - 1].y, i);

            // The Blue Line
            float xm = interpolate(xa, xb, i);
            float ym = interpolate(ya, yb, i);
            float xn = interpolate(xb, xc, i);
            float yn = interpolate(yb, yc, i);

            // The Black Dot
            float x2 = interpolate(xm, xn, i);
            float y2 = interpolate(ym, yn, i);

            Point p2;
            p2.setxy(x2, y2);

            drawLine(p1, p2);

            p1 = p2;

            // Prevents curves generated during mouse motion from being stored
            if (PrevMouse == 0 && MouseReleased)
            {
                // Store curvature into Bezier Points
                BezierCurve.push_back(p2);
            }
        }
    }
    std::cout << BezierCurve.size() << std::endl;
    
    PrevMouse = MouseReleased;

    // Draw all bezier curvature
    for (int i = 1; i < BezierCurve.size(); i++)
    {
        drawLine(BezierCurve[i - 1], BezierCurve[i]);
    }

    glutSwapBuffers();
}

void timer(int)
{
    glutTimerFunc(1000 / 60, timer, 0);
    glutPostRedisplay();
}

int main(int argc, char* argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(640, 500);
    glutInitWindowPosition(100, 150);
    glutCreateWindow("Bezier Curve");
    glutDisplayFunc(myDisplay);
    glutIdleFunc(myDisplay);
    glutTimerFunc(0, timer, 0);
    glutMouseFunc(myMouse);
    glutPassiveMotionFunc(passiveMotion);
    glutMotionFunc(motion);
    glClearColor(255, 255, 255, 0.0);
    glPointSize(3);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 640.0, 0.0, 500.0);
    glutMainLoop();

    return 0;
}