QPainterPath - 创建以圆弧为边的凹面

QPainterPath - create concave areas with arcs as edges

我希望能够绘制由直线和弧线组成的填充区域。我对凸图形没有问题,但对凹图形有问题。这就是我要画的:

我可以很容易地画出轮廓。我使用我创建的 QPainterPath 中的笔画如下:

path = QPainterPath()
path.moveTo(v1)
path.lineTo(v2)
path.lineTo(v3)
path.arcTo(v1) #this is simplified 

但我不能用同样的方法创建填充区域。因为 v3v1 之间的圆弧圆心在图形之外。所以填充区域在左侧。

我已经尝试使用来自 this question 的弧的三次贝塞尔近似来创建它并取得了一些成功(左图)。不幸的是,这种近似并不完美,当圆弧超过圆的一半时,它会变得疯狂(再次在左侧)。 (右图)

如何使用 QPainterPath 创建第一张图片中的形状?

我无法重现该问题,因此在此答案中我将发布我用于测试的代码。也许你错误地构建了 P1P3 曲线。

#include <cmath>
#include <QtWidgets>


class Widget: public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent=nullptr): 
        QWidget(parent),
        path_item(new QGraphicsPathItem),
        circle_item(new QGraphicsEllipseItem)
    {
        QDoubleSpinBox *radius_spinbox = new QDoubleSpinBox;
        radius_spinbox->setMinimum(0);
        radius_spinbox->setMaximum(10000);
        connect(radius_spinbox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &Widget::updateRadius);

        QCheckBox *circle_checkbox = new QCheckBox("Circle visibility");

        QGraphicsScene *scene = new QGraphicsScene(this);
        QGraphicsView *view = new QGraphicsView(scene);
        view->scale(1, -1);
        view->setRenderHints(QPainter::Antialiasing);

        path_item->setBrush(QBrush(QColor("gray")));
        path_item->setPen(QPen(QColor("black"), 5));

        circle_item->setBrush(QBrush(QColor("salmon")));
        circle_item->setPen(QPen(QColor("red"), 5));

        scene->addItem(path_item);
        scene->addItem(circle_item);

        connect(circle_checkbox, &QCheckBox::toggled, [this](bool checked){
            circle_item->setVisible(checked);
        });

        circle_checkbox->setChecked(true);

        QVBoxLayout *lay = new QVBoxLayout(this);
        lay->addWidget(radius_spinbox);
        lay->addWidget(circle_checkbox);
        lay->addWidget(view);

        radius_spinbox->setValue(550);
    }
private:
    Q_SLOT void updateRadius(double radius){

        QPointF v1(100, 100);
        QPointF v2(0, 0);
        QPointF v3(400, -100);

        QPointF uu = (v1 + v3) / 2;

        QLineF nv = QLineF(uu, v3).normalVector();
        double d2 = radius * radius - nv.length() * nv.length();
        if(d2 < 0){
            qDebug() << "radius < d(v1, v3)";
            return;
        }
        QPointF c = nv.p1() + sqrt(d2) * (nv.p1() - nv.p2()) / nv.length();

        QRectF rectangle = QRectF(QPointF(), 2 * radius * QSizeF(1, 1));
        rectangle.moveCenter(c);

        double angle1 = QLineF(c, v3).angle();
        double angle2 = QLineF(c, v1).angle();

        QPainterPath path;
        path.moveTo(v1);
        path.lineTo(v2);
        path.lineTo(v3);
        path.arcTo(rectangle, angle1, angle2 - angle1);

        path_item->setPath(path);
        circle_item->setRect(rectangle.adjusted(5, 5, -5, -5));
    }
    QGraphicsPathItem *path_item;
    QGraphicsEllipseItem *circle_item;

};

int main(int argc, char *argv[]) {
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);
    Widget w;
    w.resize(640, 480);
    w.show();
    return app.exec();
}

#include "main.moc"