qtqpainterpath

QPainterPath - create concave areas with arcs as edges


I would like to be able to paint filled areas which consist of edges that are lines and arcs. I don't have a problem with convex figures, but I have with concave ones. This is what I want to paint:

What I want to get

I can get the outline painted very easily. I use stroke from QPainterPath which I create as following:

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

But I can't create a filled area this same way. Because the arc between v3 and v1 has the center outside of the figure. So the filled area is on the left side.

What I've got

I've tried creating this with cubic Bezier approximation of an arc from this question with some success (left picture). Unfortunately, this approximation isn't perfect and goes crazy (on the left side again) when the arc is more than half of the circle. (right picture)

What I achieved with Bezier

How to create shapes like on the first picture using QPainterPath?


Solution

  • I could not reproduce the problem so in this answer I will publish the code I use for my test. Maybe you are incorrectly building the P1P3 curve.

    #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"
    

    enter image description here

    enter image description here