1

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?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
JD.
  • 455
  • 6
  • 15
  • Lines v1v2 and v2v3 are easy to understand because they are straight lines, but in the case of v1v3 it is complicated. Does that curve have any special requirements? Or is it any curve that makes the triangular area non-convex? – eyllanesc Oct 31 '19 at 19:51
  • @eyllanesc By now, the curve can be just an arc (any flavor). I plan cubic Bezier curves in the future. – JD. Oct 31 '19 at 20:01
  • did you try windingfill rule? it does not related to your issue but give it a try – Soheil Armin Oct 31 '19 at 20:35
  • @SoheilArmin I've just tried it. Not difference, but thanks. – JD. Oct 31 '19 at 20:46

1 Answers1

2

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

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • @eyallansec Thanks, you were right. I was using wrong angles but also was wrongly convinced that QPainterPath "must" draw to the inside of the circle. Now I think that a path is checked against CCW or CW points direction. – JD. Nov 02 '19 at 11:06