]> git.sesse.net Git - kdenlive/blobdiff - src/beziercurve/beziersplineeditor.cpp
Const'ref
[kdenlive] / src / beziercurve / beziersplineeditor.cpp
index 0b39b11e083409a215b9da93c920e245567efa69..2ea438302bf49929eb2ed5bfd16dbc628a93edca 100644 (file)
  ***************************************************************************/
 
 #include "beziersplineeditor.h"
+#include "kdenlivesettings.h"
 
 #include <QPainter>
 #include <QMouseEvent>
 
 
 BezierSplineEditor::BezierSplineEditor(QWidget* parent) :
-        QWidget(parent),
-        m_mode(ModeNormal),
-        m_zoomLevel(0),
-        m_gridLines(3),
-        m_pixmapCache(NULL),
-        m_pixmapIsDirty(true),
-        m_currentPointIndex(-1)
+        QWidget(parent)
+        , m_mode(ModeNormal)
+        , m_zoomLevel(0)
+        , m_gridLines(3)
+        , m_showAllHandles(true)
+        , m_pixmapCache(NULL)
+        , m_pixmapIsDirty(true)
+        , m_currentPointIndex(-1)
+       , m_currentPointType(PTypeP)
+       , m_grabOffsetX(0)
+       , m_grabOffsetY(0)
 {
     setMouseTracking(true);
     setAutoFillBackground(false);
@@ -40,20 +45,17 @@ BezierSplineEditor::BezierSplineEditor(QWidget* parent) :
 
 BezierSplineEditor::~BezierSplineEditor()
 {
-    if (m_pixmapCache)
-        delete m_pixmapCache;
+    delete m_pixmapCache;
 }
 
-CubicBezierSpline BezierSplineEditor::spline()
+CubicBezierSpline BezierSplineEditor::spline() const
 {
     return m_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());
@@ -64,18 +66,19 @@ void BezierSplineEditor::setSpline(const CubicBezierSpline& spline)
 BPoint BezierSplineEditor::getCurrentPoint()
 {
     if (m_currentPointIndex >= 0)
-        return m_spline.points()[m_currentPointIndex];
+        return m_spline.getPoint(m_currentPointIndex);
     else
         return BPoint();
 }
 
-void BezierSplineEditor::updateCurrentPoint(const BPoint& p)
+void BezierSplineEditor::updateCurrentPoint(const BPoint& p, bool final)
 {
     if (m_currentPointIndex >= 0) {
         m_spline.setPoint(m_currentPointIndex, p);
         // during validation the point might have changed
-        emit currentPoint(m_spline.points()[m_currentPointIndex]);
-        emit modified();
+        emit currentPoint(m_spline.getPoint(m_currentPointIndex));
+        if (final)
+            emit modified();
         update();
     }
 }
@@ -101,7 +104,15 @@ void BezierSplineEditor::slotZoomOut()
     update();
 }
 
-int BezierSplineEditor::gridLines()
+void BezierSplineEditor::setShowAllHandles(bool show)
+{
+    if (m_showAllHandles != show) {
+        m_showAllHandles = show;
+        update();
+    }
+}
+
+int BezierSplineEditor::gridLines() const
 {
     return m_gridLines;
 }
@@ -114,22 +125,26 @@ void BezierSplineEditor::setGridLines(int lines)
 
 void BezierSplineEditor::paintEvent(QPaintEvent* event)
 {
-    Q_UNUSED(event);
+    Q_UNUSED(event)
 
     QPainter p(this);
 
+    /*
+     * Zoom
+     */
     int wWidth = width() - 1;
     int wHeight = height() - 1;
-    int offset = 1/8. * m_zoomLevel * (wWidth > wHeight ? wWidth : wHeight);
-    wWidth -= 2 * offset;
-    wHeight -= 2 * offset;
+    int offsetX = 1/8. * m_zoomLevel * wWidth;
+    int offsetY = 1/8. * m_zoomLevel * wHeight;
+    wWidth -= 2 * offsetX;
+    wHeight -= 2 * offsetY;
 
-    p.translate(offset, offset);
+    p.translate(offsetX, offsetY);
 
     /*
      * Background
      */
-    p.fillRect(rect().translated(-offset, -offset), palette().background());
+    p.fillRect(rect().translated(-offsetX, -offsetY), palette().background());
     if (!m_pixmap.isNull()) {
         if (m_pixmapIsDirty || !m_pixmapCache) {
             if (m_pixmapCache)
@@ -173,76 +188,70 @@ 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;
-    QPolygon handle(4);
-    handle.setPoints(4,
-                     1,  -2,
-                     4,  1,
-                     1,  4,
-                     -2, 1);
+
+    QPolygonF handle = QPolygonF() << QPointF(0, -3) << QPointF(3, 0) << QPointF(0, 3) << QPointF(-3, 0);
 #if QT_VERSION < 0x040600
-    QPolygon tmp;
+    QPolygonF tmp;
 #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));
-        if (i != 0) {
+        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) {
+        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
         }
@@ -254,7 +263,6 @@ void BezierSplineEditor::paintEvent(QPaintEvent* event)
 
 void BezierSplineEditor::resizeEvent(QResizeEvent* event)
 {
-    m_spline.setPrecision(width() > height() ? width() : height());
     m_pixmapIsDirty = true;
     QWidget::resizeEvent(event);
 }
@@ -263,12 +271,13 @@ void BezierSplineEditor::mousePressEvent(QMouseEvent* event)
 {
     int wWidth = width() - 1;
     int wHeight = height() - 1;
-    int offset = 1/8. * m_zoomLevel * (wWidth > wHeight ? wWidth : wHeight);
-    wWidth -= 2 * offset;
-    wHeight -= 2 * offset;
+    int offsetX = 1/8. * m_zoomLevel * wWidth;
+    int offsetY = 1/8. * m_zoomLevel * wHeight;
+    wWidth -= 2 * offsetX;
+    wHeight -= 2 * offsetY;
 
-    double x = (event->pos().x() - offset) / (double)(wWidth);
-    double y = 1.0 - (event->pos().y() - offset) / (double)(wHeight);
+    double x = (event->pos().x() - offsetX) / (double)(wWidth);
+    double y = 1.0 - (event->pos().y() - offsetY) / (double)(wHeight);
 
     point_types selectedPoint;
     int closestPointIndex = nearestPointInRange(QPointF(x, y), wWidth, wHeight, &selectedPoint);
@@ -281,7 +290,7 @@ void BezierSplineEditor::mousePressEvent(QMouseEvent* event)
             --m_currentPointIndex;
         update();
         if (m_currentPointIndex >= 0)
-            emit currentPoint(m_spline.points()[m_currentPointIndex]);
+            emit currentPoint(m_spline.getPoint(m_currentPointIndex));
         else
             emit currentPoint(BPoint());
         emit modified();
@@ -300,13 +309,13 @@ void BezierSplineEditor::mousePressEvent(QMouseEvent* event)
         m_currentPointType = selectedPoint;
     }
 
-    BPoint point = m_spline.points()[m_currentPointIndex];
+    BPoint point = m_spline.getPoint(m_currentPointIndex);
 
     m_grabPOriginal = point;
     if (m_currentPointIndex > 0)
-        m_grabPPrevious = m_spline.points()[m_currentPointIndex - 1];
+        m_grabPPrevious = m_spline.getPoint(m_currentPointIndex - 1);
     if (m_currentPointIndex < m_spline.points().count() - 1)
-        m_grabPNext = m_spline.points()[m_currentPointIndex + 1];
+        m_grabPNext = m_spline.getPoint(m_currentPointIndex + 1);
     m_grabOffsetX = point[(int)m_currentPointType].x() - x;
     m_grabOffsetY = point[(int)m_currentPointType].y() - y;
 
@@ -335,12 +344,13 @@ void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event)
 {
     int wWidth = width() - 1;
     int wHeight = height() - 1;
-    int offset = 1/8. * m_zoomLevel * (wWidth > wHeight ? wWidth : wHeight);
-    wWidth -= 2 * offset;
-    wHeight -= 2 * offset;
+    int offsetX = 1/8. * m_zoomLevel * wWidth;
+    int offsetY = 1/8. * m_zoomLevel * wHeight;
+    wWidth -= 2 * offsetX;
+    wHeight -= 2 * offsetY;
 
-    double x = (event->pos().x() - offset) / (double)(wWidth);
-    double y = 1.0 - (event->pos().y() - offset) / (double)(wHeight);
+    double x = (event->pos().x() - offsetX) / (double)(wWidth);
+    double y = 1.0 - (event->pos().y() - offsetY) / (double)(wHeight);
     
     if (m_mode == ModeNormal) {
         // If no point is selected set the the cursor shape if on top
@@ -360,14 +370,14 @@ void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event)
 
         double leftX = 0.;
         double rightX = 1.;
-        BPoint point = m_spline.points()[m_currentPointIndex];
+        BPoint point = m_spline.getPoint(m_currentPointIndex);
         switch (m_currentPointType) {
         case PTypeH1:
             rightX = point.p.x();
             if (m_currentPointIndex == 0)
                 leftX = -4;
             else
-                leftX = m_spline.points()[m_currentPointIndex - 1].p.x();
+                leftX = m_spline.getPoint(m_currentPointIndex - 1).p.x();
 
             x = qBound(leftX, x, rightX);
             point.setH1(QPointF(x, y));
@@ -399,7 +409,7 @@ void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event)
             if (m_currentPointIndex == m_spline.points().count() - 1)
                 rightX = 5;
             else
-                rightX = m_spline.points()[m_currentPointIndex + 1].p.x();
+                rightX = m_spline.getPoint(m_currentPointIndex + 1).p.x();
 
             x = qBound(leftX, x, rightX);
             point.setH2(QPointF(x, y));
@@ -421,30 +431,42 @@ void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event)
                     m_spline.setPoint(index, m_grabPPrevious);
                     m_grabPNext = m_grabPPrevious;
                     if (m_currentPointIndex > 0)
-                        m_grabPPrevious = m_spline.points()[m_currentPointIndex - 1];
+                        m_grabPPrevious = m_spline.getPoint(m_currentPointIndex - 1);
                 } else {
                     m_spline.setPoint(index, m_grabPNext);
                     m_grabPPrevious = m_grabPNext;
                     if (m_currentPointIndex < m_spline.points().count() - 1)
-                        m_grabPNext = m_spline.points()[m_currentPointIndex + 1];
+                        m_grabPNext = m_spline.getPoint(m_currentPointIndex + 1);
                 }
             }
         }
 
         emit currentPoint(point);
+        if (KdenliveSettings::dragvalue_directupdate())
+            emit modified();
         update();
     }
 }
 
+void BezierSplineEditor::mouseDoubleClickEvent(QMouseEvent* /*event*/)
+{
+    if (m_currentPointIndex >= 0) {
+        BPoint p = m_spline.getPoint(m_currentPointIndex);
+        p.handlesLinked = !p.handlesLinked;
+        m_spline.setPoint(m_currentPointIndex, p);
+        emit currentPoint(p);
+    }
+}
+
 void BezierSplineEditor::leaveEvent(QEvent* event)
 {
     QWidget::leaveEvent(event);
 }
 
-int BezierSplineEditor::nearestPointInRange(QPointF p, int wWidth, int wHeight, BezierSplineEditor::point_types* sel)
+int BezierSplineEditor::nearestPointInRange(const QPointF &p, int wWidth, int wHeight, BezierSplineEditor::point_types* sel)
 {
     double nearestDistanceSquared = 1000;
-    point_types selectedPoint;
+    point_types selectedPoint = PTypeP;
     int nearestIndex = -1;
     int i = 0;
 
@@ -462,8 +484,9 @@ int BezierSplineEditor::nearestPointInRange(QPointF p, int wWidth, int wHeight,
         ++i;
     }
 
-    if (nearestIndex >= 0) {
-        BPoint point = m_spline.points()[nearestIndex];
+    if (nearestIndex >= 0 && (nearestIndex == m_currentPointIndex || selectedPoint == PTypeP || m_showAllHandles)) {
+        // a point was found and it is not a hidden handle
+        BPoint point = m_spline.getPoint(nearestIndex);
         if (qAbs(p.x() - point[(int)selectedPoint].x()) * wWidth < 5 && qAbs(p.y() - point[(int)selectedPoint].y()) * wHeight < 5) {
             *sel = selectedPoint;
             return nearestIndex;