void BezierSplineEditor::setSpline(const CubicBezierSpline& spline)
{
- int precision = m_spline.getPrecision();
m_spline = spline;
- m_spline.setPrecision(precision);
m_currentPointIndex = -1;
m_mode = ModeNormal;
emit currentPoint(BPoint());
QPainter p(this);
+ /*
+ * Zoom
+ */
int wWidth = width() - 1;
int wHeight = height() - 1;
int offset = 1/8. * m_zoomLevel * (wWidth > wHeight ? wWidth : wHeight);
*/
p.drawLine(QLineF(0, wHeight, wWidth, 0));
+
+ /*
+ * Prepare Spline, Points
+ */
+ int max = m_spline.points().count() - 1;
+ if (max < 1)
+ return;
+ BPoint point(m_spline.getPoint(0, wWidth, wHeight, true));
+
/*
* Spline
*/
- double prevY = wHeight - m_spline.value(0.) * wHeight;
- double prevX = 0.;
- double curY;
- double normalizedX = -1;
- int x;
+ BPoint next;
- p.setPen(QPen(Qt::black, 1, Qt::SolidLine));
- for (x = 0 ; x < wWidth ; ++x) {
- normalizedX = x / (double)wWidth;
- curY = wHeight - m_spline.value(normalizedX, true) * wHeight;
-
- /*
- * Keep in mind that QLineF rounds doubles
- * to ints mathematically, not just rounds down
- * like in C
- */
- p.drawLine(QLineF(prevX, prevY, x, curY));
- prevX = x;
- prevY = curY;
+ QPainterPath splinePath(QPointF(point.p.x(), point.p.y()));
+ for (int i = 0; i < max; ++i) {
+ point = m_spline.getPoint(i, wWidth, wHeight, true);
+ next = m_spline.getPoint(i + 1, wWidth, wHeight, true);
+ splinePath.cubicTo(point.h2, next.h1, next.p);
}
- p.drawLine(QLineF(prevX, prevY ,
- x, wHeight - m_spline.value(1.0, true) * wHeight));
+ p.setPen(QPen(Qt::black, 1, Qt::SolidLine));
+ p.drawPath(splinePath);
+
/*
* Points + Handles
*/
- int max = m_spline.points().count() - 1;
p.setPen(QPen(Qt::red, 1, Qt::SolidLine));
- BPoint point;
QPolygonF handle = QPolygonF() << QPointF(0, -3) << QPointF(3, 0) << QPointF(0, 3) << QPointF(-3, 0);
#if QT_VERSION < 0x040600
#endif
for (int i = 0; i <= max; ++i) {
- point = m_spline.points().at(i);
+ point = m_spline.getPoint(i, wWidth, wHeight, true);
+
if (i == m_currentPointIndex) {
// selected point: fill p and handles
p.setBrush(QBrush(QColor(Qt::red), Qt::SolidPattern));
// connect p and handles with lines
if (i != 0)
- p.drawLine(QLineF(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight, point.p.x() * wWidth, wHeight - point.p.y() * wHeight));
+ p.drawLine(QLineF(point.h1.x(), point.h1.y(), point.p.x(),point.p.y()));
if (i != max)
- p.drawLine(QLineF(point.p.x() * wWidth, wHeight - point.p.y() * wHeight, point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight));
+ p.drawLine(QLineF(point.p.x(), point.p.y(), point.h2.x(), point.h2.y()));
}
- p.drawEllipse(QRectF(point.p.x() * wWidth - 3,
- wHeight - 3 - point.p.y() * wHeight, 6, 6));
+ p.drawEllipse(QRectF(point.p.x() - 3,
+ point.p.y() - 3, 6, 6));
if (i != 0 && (i == m_currentPointIndex || m_showAllHandles)) {
#if QT_VERSION >= 0x040600
- p.drawConvexPolygon(handle.translated(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight));
+ p.drawConvexPolygon(handle.translated(point.h1.x(), point.h1.y()));
#else
tmp = handle;
- tmp.translate(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight);
+ tmp.translate(point.h1.x(), point.h1.y());
p.drawConvexPolygon(tmp);
#endif
}
if (i != max && (i == m_currentPointIndex || m_showAllHandles)) {
#if QT_VERSION >= 0x040600
- p.drawConvexPolygon(handle.translated(point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight));
+ p.drawConvexPolygon(handle.translated(point.h2.x(), point.h2.y()));
#else
tmp = handle;
- tmp.translate(point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight);
+ tmp.translate(point.h2.x(), point.h2.y());
p.drawConvexPolygon(tmp);
#endif
}
void BezierSplineEditor::resizeEvent(QResizeEvent* event)
{
- m_spline.setPrecision(width() > height() ? width() : height());
m_pixmapIsDirty = true;
QWidget::resizeEvent(event);
}
void BPoint::keepInRange(qreal xMin, qreal xMax)
{
+ Q_UNUSED(xMin);
+ Q_UNUSED(xMax);
}
void BPoint::autoSetLinked()
}
CubicBezierSpline::CubicBezierSpline(QObject* parent) :
- QObject(parent),
- m_validSpline(false),
- m_precision(100)
+ QObject(parent)
{
m_points.append(BPoint(QPointF(0, 0), QPointF(0, 0), QPointF(.1, .1)));
m_points.append(BPoint(QPointF(.9, .9), QPointF(1, 1), QPointF(1, 1)));
CubicBezierSpline::CubicBezierSpline(const CubicBezierSpline& spline, QObject* parent) :
QObject(parent)
{
- m_precision = spline.m_precision;
m_points = spline.m_points;
- m_validSpline = false;
}
CubicBezierSpline& CubicBezierSpline::operator=(const CubicBezierSpline& spline)
{
- m_precision = spline.m_precision;
m_points = spline.m_points;
- m_validSpline = false;
return *this;
}
void CubicBezierSpline::fromString(const QString& spline)
{
m_points.clear();
- m_validSpline = false;
QStringList bpoints = spline.split('|');
foreach(const QString &bpoint, bpoints) {
m_points[ix] = point;
keepSorted();
validatePoints();
- m_validSpline = false;
return indexOf(point); // in case it changed
}
void CubicBezierSpline::removePoint(int ix)
{
m_points.removeAt(ix);
- m_validSpline = false;
}
int CubicBezierSpline::addPoint(const BPoint& point)
m_points.append(point);
keepSorted();
validatePoints();
- m_validSpline = false;
return indexOf(point);
}
-void CubicBezierSpline::setPrecision(int pre)
-{
- if (pre != m_precision) {
- m_precision = pre;
- m_validSpline = false;
- }
-}
-
-int CubicBezierSpline::getPrecision()
+BPoint CubicBezierSpline::getPoint(int ix, int normalisedWidth, int normalisedHeight, bool invertHeight)
{
- return m_precision;
-}
-
-qreal CubicBezierSpline::value(qreal x, bool cont)
-{
- update();
-
- if (!cont)
- m_i = m_spline.constBegin();
- if (m_i != m_spline.constBegin())
- --m_i;
-
- double diff = qAbs(x - m_i.key());
- double y = m_i.value();
- while (m_i != m_spline.constEnd()) {
- if (qAbs(x - m_i.key()) > diff)
- break;
-
- diff = qAbs(x - m_i.key());
- y = m_i.value();
- ++m_i;
+ BPoint p = m_points.at(ix);
+ for (int i = 0; i < 3; ++i) {
+ p[i].rx() *= normalisedWidth;
+ p[i].ry() *= normalisedHeight;
+ if (invertHeight)
+ p[i].ry() = normalisedHeight - p[i].y();
}
- return qBound((qreal)0.0, y, (qreal)1.0);
+ return p;
}
void CubicBezierSpline::validatePoints()
qSort(m_points.begin(), m_points.end(), pointLessThan);
}
-QPointF CubicBezierSpline::point(double t, const QList< QPointF >& points)
-{
- /*
- * Calculating a point on the bezier curve using the coefficients from Bernstein basis polynomial of degree 3.
- * Using the De Casteljau algorithm would be slightly faster for when calculating a lot of values
- * but the difference is far from noticable in this needcase
- */
- double c1 = (1-t) * (1-t) * (1-t);
- double c2 = 3 * t * (1-t) * (1-t);
- double c3 = 3 * t * t * (1-t);
- double c4 = t * t * t;
-
- return QPointF(points[0].x()*c1 + points[1].x()*c2 + points[2].x()*c3 + points[3].x()*c4,
- points[0].y()*c1 + points[1].y()*c2 + points[2].y()*c3 + points[3].y()*c4);
-}
-
-void CubicBezierSpline::update()
-{
- if (m_validSpline)
- return;
-
- m_validSpline = true;
- m_spline.clear();
-
- QList <QPointF> points;
- QPointF p;
- for (int i = 0; i < m_points.count() - 1; ++i) {
- points.clear();
- points << m_points.at(i).p
- << m_points.at(i).h2
- << m_points.at(i+1).h1
- << m_points.at(i+1).p;
-
- int numberOfValues = (int)((points[3].x() - points[0].x()) * m_precision * 10);
- if (numberOfValues == 0)
- numberOfValues = 1;
- double step = 1 / (double)numberOfValues;
- double t = 0;
- while (t <= 1) {
- p = point(t, points);
- m_spline.insert(p.x(), p.y());
- t += step;
- }
- }
-}
-
int CubicBezierSpline::indexOf(const BPoint& p)
{
if (m_points.indexOf(p) == -1) {
/** @brief Returns a list of the points defining the spline. */
QList <BPoint> points();
- /** @brief Finds the closest point in the pre-calculated spline to @param x.
- * @param x x value
- * @param cont (default = false) Whether to start searching at the previously requested x and skip all values before it.
- This makes requesting a lot of increasing x's faster. */
- qreal value(qreal x, bool cont = false);
/** @brief Sets the point at index @param ix to @param point and returns its index (it might have changed during validation). */
int setPoint(int ix, const BPoint &point);
/** @brief Removes the point at @param ix. */
void removePoint(int ix);
- /** @brief Sets the precision to @param pre.
- *
- * The precision influences the number of points that are calculated for the spline:
- * number of values = precision * 10 */
- void setPrecision(int pre);
- int getPrecision();
+ /** @brief Returns the point at @param ix.
+ * @param ix Index of the point
+ * @param normalisedWidth (default = 1) Will be multiplied will all x values to change the range from 0-1 into another one
+ * @param normalisedHeight (default = 1) Will be multiplied will all y values to change the range from 0-1 into another one
+ * @param invertHeight (default = false) true => y = 0 is at the very top
+ */
+ BPoint getPoint(int ix, int normalisedWidth = 1, int normalisedHeight = 1, bool invertHeight = false);
+
+
private:
- QPointF point(double t, const QList<QPointF> &points);
void validatePoints();
void keepSorted();
- void update();
int indexOf(const BPoint &p);
QList <BPoint> m_points;
- /** x, y pairs */
- QMap <double, double> m_spline;
- /** iterator used when searching for a value in in continue mode (see @function value). */
- QMap <double, double>::const_iterator m_i;
- /** Whether the spline represents the points and the precision. */
- bool m_validSpline;
- int m_precision;
};
#endif