QPainterPath - создать вогнутые области с дугами в качестве ребер - PullRequest
0 голосов
/ 31 октября 2019

Я хотел бы иметь возможность рисовать заполненные области, которые состоят из ребер, которые являются линиями и дугами. У меня нет проблем с выпуклыми фигурами, но у меня есть с вогнутыми. Вот что я хочу нарисовать:

What I want to get

Я могу очень легко нарисовать контур. Я использую штрих из QPainterPath, который я создаю следующим образом:

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

Но я не могу создать заполненную область таким же образом. Потому что дуга между v3 и v1 имеет центр снаружи фигуры. Таким образом, заполненная область находится на левой стороне.

What I've got

Я пытался создать это с кубической аппроксимацией Безье дуги из этого вопроса с некоторым успехом (левая картинка),К сожалению, это приближение не является идеальным и сходит с ума (снова с левой стороны), когда дуга больше половины окружности. (правая картинка)

What I achieved with Bezier

Как создавать фигуры, как на первом изображении, используя QPainterPath?

1 Ответ

1 голос
/ 01 ноября 2019

Я не смог воспроизвести проблему, поэтому в этом ответе я опубликую код, который я использую для своего теста. Возможно, вы неправильно строите кривую 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"

enter image description here

enter image description here

...