From: Till Theato Date: Tue, 18 Jan 2011 20:53:58 +0000 (+0000) Subject: Use QPainterPath's cubicTo to draw cubic bezier spline X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=b2f9e475053bd6719b74f718a4385a1edd9c1795;p=kdenlive Use QPainterPath's cubicTo to draw cubic bezier spline svn path=/trunk/kdenlive/; revision=5332 --- diff --git a/src/beziercurve/beziersplineeditor.cpp b/src/beziercurve/beziersplineeditor.cpp index 6316fbfb..4ef7ea12 100644 --- a/src/beziercurve/beziersplineeditor.cpp +++ b/src/beziercurve/beziersplineeditor.cpp @@ -52,9 +52,7 @@ CubicBezierSpline BezierSplineEditor::spline() 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()); @@ -125,6 +123,9 @@ void BezierSplineEditor::paintEvent(QPaintEvent* event) QPainter p(this); + /* + * Zoom + */ int wWidth = width() - 1; int wHeight = height() - 1; int offset = 1/8. * m_zoomLevel * (wWidth > wHeight ? wWidth : wHeight); @@ -180,38 +181,34 @@ void BezierSplineEditor::paintEvent(QPaintEvent* event) */ 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 @@ -219,34 +216,35 @@ void BezierSplineEditor::paintEvent(QPaintEvent* event) #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 } @@ -258,7 +256,6 @@ void BezierSplineEditor::paintEvent(QPaintEvent* event) void BezierSplineEditor::resizeEvent(QResizeEvent* event) { - m_spline.setPrecision(width() > height() ? width() : height()); m_pixmapIsDirty = true; QWidget::resizeEvent(event); } diff --git a/src/beziercurve/bpoint.cpp b/src/beziercurve/bpoint.cpp index 15bb9112..e879e721 100644 --- a/src/beziercurve/bpoint.cpp +++ b/src/beziercurve/bpoint.cpp @@ -86,6 +86,8 @@ void BPoint::setH2(QPointF handle2) void BPoint::keepInRange(qreal xMin, qreal xMax) { + Q_UNUSED(xMin); + Q_UNUSED(xMax); } void BPoint::autoSetLinked() diff --git a/src/beziercurve/cubicbezierspline.cpp b/src/beziercurve/cubicbezierspline.cpp index 9a2be606..175aed21 100644 --- a/src/beziercurve/cubicbezierspline.cpp +++ b/src/beziercurve/cubicbezierspline.cpp @@ -26,9 +26,7 @@ static bool pointLessThan(const BPoint &a, const BPoint &b) } 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))); @@ -37,23 +35,18 @@ CubicBezierSpline::CubicBezierSpline(QObject* parent) : 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) { @@ -89,7 +82,6 @@ int CubicBezierSpline::setPoint(int ix, const BPoint& point) m_points[ix] = point; keepSorted(); validatePoints(); - m_validSpline = false; return indexOf(point); // in case it changed } @@ -101,7 +93,6 @@ QList CubicBezierSpline::points() void CubicBezierSpline::removePoint(int ix) { m_points.removeAt(ix); - m_validSpline = false; } int CubicBezierSpline::addPoint(const BPoint& point) @@ -109,43 +100,19 @@ 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() @@ -166,52 +133,6 @@ void CubicBezierSpline::keepSorted() 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 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) { diff --git a/src/beziercurve/cubicbezierspline.h b/src/beziercurve/cubicbezierspline.h index d28b0ae8..ee21c987 100644 --- a/src/beziercurve/cubicbezierspline.h +++ b/src/beziercurve/cubicbezierspline.h @@ -50,11 +50,6 @@ public: /** @brief Returns a list of the points defining the spline. */ QList 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); @@ -63,28 +58,22 @@ public: /** @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 &points); void validatePoints(); void keepSorted(); - void update(); int indexOf(const BPoint &p); QList m_points; - /** x, y pairs */ - QMap m_spline; - /** iterator used when searching for a value in in continue mode (see @function value). */ - QMap ::const_iterator m_i; - /** Whether the spline represents the points and the precision. */ - bool m_validSpline; - int m_precision; }; #endif