Reputation: 485
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:
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.
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)
How to create shapes like on the first picture using QPainterPath
?
Upvotes: 1
Views: 414
Reputation: 244162
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"
Upvotes: 2