Нашёл код реализации кривой Безье. У меня он не запускался, немного подшаманил его. В этом коде часть кривой Безье описывается через другую кривую Безье. И эта другая кривая Безье уже отрисовывается с помощью обычных средств, как я понял. Можно ли как-то попроще реализовать этот алгоритм, например, сразу рисовать кривую? Пытался кардинально всё изменить, но не получается.
Вот сам код:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QSlider>
#include <QLayout>
#include <QPainter>
#include <QMouseEvent>
#include <QSignalMapper>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
public slots:
// from 0 to 100%
void setT0(int val) { setPart(0, 0.01*val); }
void setT1(int val) { setPart(1, 0.01*val); }
protected:
virtual void paintEvent(QPaintEvent *event);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
private:
int pointIdx(const QPointF& p);
void updatePart();
void setPart(int idx, qreal val);
QList<QPointF> pts;
QList<QPointF> ptsPart;
qreal t0, t1;
QPointF prev;
int movingIdx;
int overIdx;
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
setMouseTracking(true);
pts << QPointF(50, 50) << QPointF(450, 100)
<< QPointF(350, 450) << QPointF(750, 500);
ptsPart = pts;
updatePart();
ui->setupUi(this);
}
const qreal r = 5.0;
void Widget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter painter(this);
painter.fillRect(0, 0, width(), height(), Qt::darkGray);
painter.setRenderHint(QPainter::Antialiasing);
// draw original bezier
painter.setPen(QPen(QBrush(Qt::white), 1.5));
painter.setBrush(Qt::NoBrush);
QPainterPath path(pts[0]);
path.cubicTo(pts[1], pts[2], pts[3]);
painter.drawPath(path);
// draw path for control points
path = QPainterPath(pts[0]);
path.lineTo(pts[1]);
path.lineTo(pts[2]);
path.lineTo(pts[3]);
QBrush b(Qt::yellow);
painter.setPen(QPen(b, 0.75));
painter.setBrush(Qt::NoBrush);
painter.drawPath(path);
// draw control points
QBrush b1(Qt::cyan);
painter.setPen(Qt::NoPen);
for (int idx = 0; idx < pts.size(); ++idx) {
painter.setBrush(idx == overIdx ? b1 : b);
painter.drawEllipse(pts[idx], r, r);
// draw part of original bezier
painter.setPen(QPen(QBrush(QColor(0, 255, 0, 100)), 12.0));
painter.setBrush(Qt::NoBrush);
path = QPainterPath(ptsPart[0]);
path.cubicTo(ptsPart[1], ptsPart[2], ptsPart[3]);
painter.drawPath(path);
}
}
void Widget::mousePressEvent(QMouseEvent *event)
{
if (movingIdx == -1 && event->button() == Qt::LeftButton) {
movingIdx = pointIdx(event->pos());
prev = event->pos();
}
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
if (movingIdx != -1 && event->button() == Qt::LeftButton)
movingIdx = -1;
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
if (movingIdx != -1) {
pts[movingIdx] += (event->pos() - prev);
updatePart();
prev = event->pos();
update();
} else {
int idx = pointIdx(event->pos());
if (idx != overIdx)
update();
overIdx = idx;
}
}
int Widget::pointIdx(const QPointF& p)
{
for (int idx = 0; idx < pts.size(); ++idx) {
if (QLineF(p, pts[idx]).length() <= 1.2*r)
return idx;
}
return -1;
}
void Widget::updatePart()
{
qreal u0 = 1.0 - t0;
qreal u1 = 1.0 - t1;
qreal x1 = pts[0].x();
qreal y1 = pts[0].y();
qreal x2 = pts[3].x();
qreal y2 = pts[3].y();
qreal bx1 = pts[1].x();
qreal by1 = pts[1].y();
qreal bx2 = pts[2].x();
qreal by2 = pts[2].y();
qreal qxa = x1*u0*u0 + bx1*2*t0*u0 + bx2*t0*t0;
qreal qxb = x1*u1*u1 + bx1*2*t1*u1 + bx2*t1*t1;
qreal qxc = bx1*u0*u0 + bx2*2*t0*u0 + x2*t0*t0;
qreal qxd = bx1*u1*u1 + bx2*2*t1*u1 + x2*t1*t1;
qreal qya = y1*u0*u0 + by1*2*t0*u0 + by2*t0*t0;
qreal qyb = y1*u1*u1 + by1*2*t1*u1 + by2*t1*t1;
qreal qyc = by1*u0*u0 + by2*2*t0*u0 + y2*t0*t0;
qreal qyd = by1*u1*u1 + by2*2*t1*u1 + y2*t1*t1;
ptsPart[0].setX(qxa*u0 + qxc*t0);
ptsPart[1].setX(qxa*u1 + qxc*t1);
ptsPart[2].setX(qxb*u0 + qxd*t0);
ptsPart[3].setX(qxb*u1 + qxd*t1);
ptsPart[0].setY(qya*u0 + qyc*t0);
ptsPart[1].setY(qya*u1 + qyc*t1);
ptsPart[2].setY(qyb*u0 + qyd*t0);
ptsPart[3].setY(qyb*u1 + qyd*t1);
}
void Widget::setPart(int idx, qreal val)
{
qreal& t = (idx == 0) ? t0 : t1;
t = val;
updatePart();
update();
}
Widget::~Widget()
{
delete ui;
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
QGridLayout* layout = new QGridLayout(&w);
layout->addWidget(new QLabel("t0:"), 0, 0);
layout->addWidget(new QLabel("t1:"), 1, 0);
QSlider* s0 = new QSlider(Qt::Horizontal);
QSlider* s1 = new QSlider(Qt::Horizontal);
layout->addWidget(s0, 0, 1);
layout->addWidget(s1, 1, 1);
s0->setRange(0, 100);
s1->setRange(0, 100);
Widget* widget = new Widget();
layout->addWidget(widget, 2, 0, 1, 2);
QObject::connect(s0, SIGNAL(valueChanged(int)), widget, SLOT(setT0(int)));
QObject::connect(s1, SIGNAL(valueChanged(int)), widget, SLOT(setT1(int)));
s0->setSliderPosition(20);
s1->setSliderPosition(75);
w.resize(800, 600);
w.show();
return a.exec();
}