How to get Bezier points from a curve

128 views Asked by At

I am drawing a Bezier curve in Qt using the inbuilt Qt functions:

void OpacityCurveWidget::paintEvent(QPaintEvent* event) {
    QPainter painter(this);
    painter.fillRect(0, 0, width(), height(), Qt::white);

    painter.setPen(Qt::blue);
    QPainterPath path;
    glm::vec2 startPoint(0, (1.0 - opacityCurve[0].opacity) * height()); // Invert y-coordinate
    path.moveTo(startPoint.x, startPoint.y);

    for (size_t i = 1; i < opacityCurve.size(); ++i) {
        int x1 = static_cast<int>(opacityCurve[i - 1].position.x * width());
        int x2 = static_cast<int>(opacityCurve[i].position.x * width());
        int y1 = static_cast<int>((1.0 - opacityCurve[i - 1].position.y) * height()); // Invert y-coordinate
        int y2 = static_cast<int>((1.0 - opacityCurve[i].position.y) * height());     // Invert y-coordinate
        int cx1 = static_cast<int>(opacityCurve[i - 1].handle2.x * width());
        int cy1 = static_cast<int>((1.0 - opacityCurve[i - 1].handle2.y) * height()); // Invert y-coordinate
        int cx2 = static_cast<int>(opacityCurve[i].handle1.x * width());
        int cy2 = static_cast<int>((1.0 - opacityCurve[i].handle1.y) * height()); // Invert y-coordinate

        QPointF startPointF(startPoint.x, startPoint.y);
        QPointF controlPoint1F(cx1, cy1);
        QPointF controlPoint2F(cx2, cy2);
        QPointF endPointF(x2, y2);
        path.cubicTo(controlPoint1F, controlPoint2F, endPointF);

After drawing them, I am able to move points and their handles save the new points and handles (x,y) position data to a vector.

Based on that data, I try to get the curve y position at given x position:

float OpacityCurveWidget::getYPositionAtX(float x) const {
    if (opacityCurve.size() < 2) {
        return 0.0f;
    }

    // Find the segment of the Bezier curve that contains the given x-coordinate
    size_t segmentIndex = 0;
    while (segmentIndex < opacityCurve.size() - 1 && x > opacityCurve[segmentIndex + 1].position.x) {
        segmentIndex++;
    }

   
    // Calculate t, the parameter for the cubic Bezier curve formula
    float t = (x - opacityCurve[segmentIndex].position.x) /
        (opacityCurve[segmentIndex + 1].position.x - opacityCurve[segmentIndex].position.x);

    // Use the cubic Bezier formula to calculate the y-coordinate
    float y = (1 - t) * (1 - t) * (1 - t) * opacityCurve[segmentIndex].position.y +
        3 * (1 - t) * (1 - t) * t * opacityCurve[segmentIndex].handle2.y +
        3 * (1 - t) * t * t * opacityCurve[segmentIndex + 1].handle1.y +
        t * t * t * opacityCurve[segmentIndex + 1].position.y;

    // Invert the y-coordinate
    y = 1.0f - y;

    // Return the y-coordinate without multiplying by height
    return y;
}

The issue I am facing is that when I move the x Position of the curve point handle that also affects the y position of curve points in the Qt graph, but in my calculation, moving of any handle point only in x direction does not affect the y position of the points.

This is the QT Curve. enter image description here

When i move any handle in x direction it also affects the y Position of curve Points. enter image description here

How can i calculate the Y Position at at X 0.5 when total X length is 0 - 1.0 ?

1

There are 1 answers

0
Nedim Memišević On

If I understood your question correctly, I might have a solution. I don't think you were focused on your real problem and you were occupied by GUI problems(been there, done that).

There is a function in QPainterPath which could maybe help you solve your problem and that is:

path.pointAtPercent(percentageOfPath)

If you could combine this with some kind of approximation algorithm, maybe you can easily get y value for given x:

From the top of the head:

#define APPROXIMATION_TRESHOLD 0.001
bool approximately(qreal inputX, qreal targetX){
     return (inputX - targetX < APPROXIMATION_TRESHOLD);

For smaller approximation_treshold you should get more precise y-value.

Then you should call this from your custom curve widget on paint event:

qreal targetX = 0.5; // let's say you are searching for x = 0.5;
qreal step = 0.001; // more the zeros, combined with approximation, you get more precise value.

while(!approximately(path.pointAtPercent(step))) step += 0.001;
// while loop will end when it finds your value.
qreal yValue = path.pointAtPercent(step);

On the other hand if you really need bezier points, you should create custom GraphicsItem for example inherit QGraphicsEllipseItem and with them control your resulting bezier curve.