如何从曲线获取贝塞尔点

问题描述 投票:0回答:1

我正在使用内置的 Qt 函数在 Qt 中绘制贝塞尔曲线:

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);

绘制它们后,我可以移动点,它们的手柄将新点和手柄 (x,y) 位置数据保存到向量中。

根据该数据,我尝试获取给定 x 位置处的曲线 y 位置:

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;
}

我面临的问题是,当我移动曲线点手柄的 x 位置时,也会影响 Qt 图中曲线点的 y 位置,但在我的计算中,仅在 x 方向上移动任何手柄点不会影响点的 y 位置。

这就是 QT 曲线。

当我在 x 方向移动任何手柄时,它也会影响曲线点的 y 位置。

当 X 总长度为 0 - 1.0 时,如何计算 X 0.5 处的 Y 位置?

qt math qpainter cubic-bezier qpainterpath
1个回答
0
投票

如果我正确理解你的问题,我可能会有解决方案。我不认为你专注于真正的问题,而是被 GUI 问题占据了(去过那里,做过那件事)。

QPainterPath 中有一个函数可以帮助你解决你的问题,那就是:

path.pointAtPercent(percentageOfPath)

如果你可以将其与某种近似算法结合起来,也许你可以轻松获得给定 x 的 y 值:

从头顶看:

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

对于较小的 approximation_treshold,您应该获得更精确的 y 值。

然后您应该在绘画事件上从自定义曲线小部件中调用它:

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);

另一方面,如果您确实需要贝塞尔点,您应该创建自定义 GraphicsItem 例如继承 QGraphicsEllipseItem 并用它们控制生成的贝塞尔曲线。

© www.soinside.com 2019 - 2024. All rights reserved.