]> git.sesse.net Git - kdenlive/commitdiff
Bezier Spline: Allow to link the handles of a point. They will then always remain...
authorTill Theato <root@ttill.de>
Tue, 4 Jan 2011 23:36:11 +0000 (23:36 +0000)
committerTill Theato <root@ttill.de>
Tue, 4 Jan 2011 23:36:11 +0000 (23:36 +0000)
svn path=/trunk/kdenlive/; revision=5266

src/CMakeLists.txt
src/beziercurve/beziersplineeditor.cpp
src/beziercurve/beziersplinewidget.cpp
src/beziercurve/beziersplinewidget.h
src/beziercurve/bpoint.cpp [new file with mode: 0644]
src/beziercurve/bpoint.h [new file with mode: 0644]
src/beziercurve/cubicbezierspline.cpp
src/beziercurve/cubicbezierspline.h
src/widgets/bezierspline_ui.ui

index a50e08ffba3ac2016d27c305320368d3ee643350..eaf1f9211ba0ed5f1772165b2173801ebaf3b1ad 100644 (file)
@@ -263,9 +263,10 @@ set(kdenlive_SRCS
   kiss_fft/_kiss_fft_guts.h
   kiss_fft/kiss_fft.c
   kiss_fft/tools/kiss_fftr.c
-  beziercurve/cubicbezierspline.cpp
   beziercurve/beziersplineeditor.cpp
   beziercurve/beziersplinewidget.cpp
+  beziercurve/bpoint.cpp
+  beziercurve/cubicbezierspline.cpp
 )
 
 add_definitions(${KDE4_DEFINITIONS})
index a58f782d76e89835020e153a62ec75ffcac5c4a2..d50d87c78d337cd88906875d01f3f6298cb4a88d 100644 (file)
@@ -74,6 +74,7 @@ void BezierSplineEditor::updateCurrentPoint(const BPoint& p)
         m_spline.setPoint(m_currentPointIndex, p);
         // during validation the point might have changed
         emit currentPoint(m_spline.points()[m_currentPointIndex]);
+        emit modified();
         update();
     }
 }
@@ -382,12 +383,12 @@ void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event)
         case PTypeH1:
             rightX = point.p.x();
             if (m_currentPointIndex == 0)
-                leftX = -1000;
+                leftX = -4;
             else
                 leftX = m_spline.points()[m_currentPointIndex - 1].p.x();
 
             x = qBound(leftX, x, rightX);
-            point.h1 = QPointF(x, y);
+            point.setH1(QPointF(x, y));
             break;
 
         case PTypeP:
@@ -403,22 +404,23 @@ void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event)
             // try to restore
             point.h1 = m_grabPOriginal.h1;
             point.h2 = m_grabPOriginal.h2;
-            // and then move them by same offset
+            // and move by same offset
+            // (using update handle in point.setP won't work because the offset between new and old point is very small)
             point.h1 += QPointF(x, y) - m_grabPOriginal.p;
             point.h2 += QPointF(x, y) - m_grabPOriginal.p;
 
-            point.p = QPointF(x, y);
+            point.setP(QPointF(x, y), false);
             break;
 
         case PTypeH2:
             leftX = point.p.x();
             if (m_currentPointIndex == m_spline.points().count() - 1)
-                rightX = 1001;
+                rightX = 5;
             else
                 rightX = m_spline.points()[m_currentPointIndex + 1].p.x();
 
             x = qBound(leftX, x, rightX);
-            point.h2 = QPointF(x, y);
+            point.setH2(QPointF(x, y));
         };
 
         int index = m_currentPointIndex;
index 695efbb9b5ab4dec6aedd581df47f68ef3df3c74..5fc7fd1ad2c2ad77d56fcdd8574b2c15e97b5b3e 100644 (file)
@@ -35,7 +35,6 @@ BezierSplineWidget::BezierSplineWidget(const QString& spline, QWidget* parent) :
     layout->addWidget(widget);
 
     m_ui.buttonLinkHandles->setIcon(KIcon("insert-link"));
-    m_ui.buttonLinkHandles->setEnabled(false);
     m_ui.buttonZoomIn->setIcon(KIcon("zoom-in"));
     m_ui.buttonZoomOut->setIcon(KIcon("zoom-out"));
     m_ui.buttonGridChange->setIcon(KIcon("view-grid"));
@@ -50,13 +49,14 @@ BezierSplineWidget::BezierSplineWidget(const QString& spline, QWidget* parent) :
     connect(&m_edit, SIGNAL(modified()), this, SIGNAL(modified()));
     connect(&m_edit, SIGNAL(currentPoint(const BPoint&)), this, SLOT(slotUpdatePoint(const BPoint&)));
 
-    connect(m_ui.spinPX, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline()));
-    connect(m_ui.spinPY, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline()));
-    connect(m_ui.spinH1X, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline()));
-    connect(m_ui.spinH1Y, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline()));
-    connect(m_ui.spinH2X, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline()));
-    connect(m_ui.spinH2Y, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline()));
+    connect(m_ui.spinPX, SIGNAL(editingFinished()), this, SLOT(slotUpdatePointP()));
+    connect(m_ui.spinPY, SIGNAL(editingFinished()), this, SLOT(slotUpdatePointP()));
+    connect(m_ui.spinH1X, SIGNAL(editingFinished()), this, SLOT(slotUpdatePointH1()));
+    connect(m_ui.spinH1Y, SIGNAL(editingFinished()), this, SLOT(slotUpdatePointH1()));
+    connect(m_ui.spinH2X, SIGNAL(editingFinished()), this, SLOT(slotUpdatePointH2()));
+    connect(m_ui.spinH2Y, SIGNAL(editingFinished()), this, SLOT(slotUpdatePointH2()));
 
+    connect(m_ui.buttonLinkHandles, SIGNAL(toggled(bool)), this, SLOT(slotSetHandlesLinked(bool)));
     connect(m_ui.buttonZoomIn, SIGNAL(clicked()), &m_edit, SLOT(slotZoomIn()));
     connect(m_ui.buttonZoomOut, SIGNAL(clicked()), &m_edit, SLOT(slotZoomOut()));
     connect(m_ui.buttonGridChange, SIGNAL(clicked()), this, SLOT(slotGridChange()));
@@ -110,32 +110,43 @@ void BezierSplineWidget::slotUpdatePoint(const BPoint &p)
         m_ui.spinH1Y->setValue(qRound(p.h1.y() * 255));
         m_ui.spinH2X->setValue(qRound(p.h2.x() * 255));
         m_ui.spinH2Y->setValue(qRound(p.h2.y() * 255));
+        m_ui.buttonLinkHandles->setChecked(p.handlesLinked);
     }
     blockSignals(false);
 }
 
-void BezierSplineWidget::slotUpdateSpline()
+void BezierSplineWidget::slotUpdatePointP()
 {
     BPoint p = m_edit.getCurrentPoint();
 
-    // check for every value, so we do not lose too much info through rounding
-    if (m_ui.spinPX->value() != qRound(p.p.x() * 255))
-        p.p.setX(m_ui.spinPX->value() / 255.);
-    if (m_ui.spinPY->value() != qRound(p.p.y() * 255))
-        p.p.setY(m_ui.spinPY->value() / 255.);
+    p.setP(QPointF(m_ui.spinPX->value() / 255., m_ui.spinPY->value() / 255.));
 
-    if (m_ui.spinH1X->value() != qRound(p.h1.x() * 255))
-        p.h1.setX(m_ui.spinH1X->value() / 255.);
-    if (m_ui.spinH1Y->value() != qRound(p.h1.y() * 255))
-        p.h1.setY(m_ui.spinH1Y->value() / 255.);
+    m_edit.updateCurrentPoint(p);
+}
+
+void BezierSplineWidget::slotUpdatePointH1()
+{
+    BPoint p = m_edit.getCurrentPoint();
 
-    if (m_ui.spinH2X->value() != qRound(p.h2.x() * 255))
-        p.h2.setX(m_ui.spinH2X->value() / 255.);
-    if (m_ui.spinH2Y->value() != qRound(p.h2.y() * 255))
-        p.h2.setY(m_ui.spinH2Y->value() / 255.);
+    p.setH1(QPointF(m_ui.spinH1X->value() / 255., m_ui.spinH1Y->value() / 255.));
 
     m_edit.updateCurrentPoint(p);
-    emit modified();
+}
+
+void BezierSplineWidget::slotUpdatePointH2()
+{
+    BPoint p = m_edit.getCurrentPoint();
+
+    p.setH2(QPointF(m_ui.spinH2X->value() / 255., m_ui.spinH2Y->value() / 255.));
+
+    m_edit.updateCurrentPoint(p);
+}
+
+void BezierSplineWidget::slotSetHandlesLinked(bool linked)
+{
+    BPoint p = m_edit.getCurrentPoint();
+    p.handlesLinked = linked;
+    m_edit.updateCurrentPoint(p);
 }
 
 void BezierSplineWidget::slotResetSpline()
index 761df80d0befff7a71b36cac9ca35f19a91cd1fb..061784488505a08ce1146282ab362e8fb3ba0ee2 100644 (file)
@@ -40,10 +40,15 @@ public:
 
 private slots:
     void slotUpdatePoint(const BPoint &p);
-    void slotUpdateSpline();
+
+    void slotUpdatePointP();
+    void slotUpdatePointH1();
+    void slotUpdatePointH2();
+
     void slotGridChange();
     void slotShowPixmap(bool show = true);
     void slotResetSpline();
+    void slotSetHandlesLinked(bool linked);
 
 private:
     Ui::BezierSpline_UI m_ui;
diff --git a/src/beziercurve/bpoint.cpp b/src/beziercurve/bpoint.cpp
new file mode 100644 (file)
index 0000000..c335b4c
--- /dev/null
@@ -0,0 +1,84 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Till Theato (root@ttill.de)                     *
+ *   This file is part of Kdenlive (www.kdenlive.org).                     *
+ *                                                                         *
+ *   Kdenlive is free software: you can redistribute it and/or modify      *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation, either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   Kdenlive is distributed in the hope that it will be useful,           *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with Kdenlive.  If not, see <http://www.gnu.org/licenses/>.     *
+ ***************************************************************************/
+
+#include "bpoint.h"
+
+
+BPoint::BPoint() :
+        h1(QPointF(-1, -1)),
+        p(QPointF(-1, -1)),
+        h2(QPointF(-1, -1)),
+        handlesLinked(true)
+{
+}
+
+BPoint::BPoint(QPointF handle1, QPointF point, QPointF handle2) :
+        h1(handle1),
+        p(point),
+        h2(handle2)
+{
+    autoSetLinked();
+}
+
+bool BPoint::operator==(const BPoint& point) const
+{
+    return      point.h1 == h1 &&
+                point.p  == p  &&
+                point.h2 == h2;
+}
+
+void BPoint::setP(QPointF point, bool updateHandles)
+{
+    QPointF offset = point - p;
+    p = point;
+    if (updateHandles) {
+        h1 += offset;
+        h2 += offset;
+    }
+}
+
+void BPoint::setH1(QPointF handle1)
+{
+    h1 = handle1;
+    if (handlesLinked) {
+        qreal angle = QLineF(h1, p).angle();
+        QLineF l = QLineF(p, h2);
+        l.setAngle(angle);
+        h2 = l.p2();
+    }    
+}
+
+void BPoint::setH2(QPointF handle2)
+{
+    h2 = handle2;
+    if (handlesLinked) {
+        qreal angle = QLineF(h2, p).angle();
+        QLineF l = QLineF(p, h1);
+        l.setAngle(angle);
+        h1 = l.p2();
+    }
+}
+
+void BPoint::keepInRange(qreal xMin, qreal xMax)
+{
+}
+
+void BPoint::autoSetLinked()
+{
+    handlesLinked = !QLineF(h1, p).angleTo(QLineF(p, h2));
+}
diff --git a/src/beziercurve/bpoint.h b/src/beziercurve/bpoint.h
new file mode 100644 (file)
index 0000000..4619dc9
--- /dev/null
@@ -0,0 +1,67 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Till Theato (root@ttill.de)                     *
+ *   This file is part of Kdenlive (www.kdenlive.org).                     *
+ *                                                                         *
+ *   Kdenlive is free software: you can redistribute it and/or modify      *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation, either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   Kdenlive is distributed in the hope that it will be useful,           *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with Kdenlive.  If not, see <http://www.gnu.org/licenses/>.     *
+ ***************************************************************************/
+
+#ifndef BPOINT_H
+#define BPOINT_H
+
+#include <QtCore>
+
+/**
+ * @brief Represents a point in a cubic Bézier spline.
+ */
+
+class BPoint
+{
+public:
+    /** @brief Sets the point to -1, -1 to mark it as unusable (until point + handles have proper values) */
+    BPoint();
+    /** @brief Sets up according to the params. Linking detecting is done using autoSetLinked(). */
+    BPoint(QPointF handle1, QPointF point, QPointF handle2);
+
+    bool operator==(const BPoint &point) const;
+
+    /** @brief Sets p to @param point.
+     * @param updateHandles (default = true) Whether to make sure the handles keep their position relative to p. */
+    void setP(QPointF point, bool updateHandles = true);
+
+    /** @brief Sets h1 to @param handle1.
+     *
+     * If handlesLinked is true h2 is updated. */
+    void setH1(QPointF handle1);
+
+    /** @brief Sets h2 to @param handle2.
+     * 
+     * If handlesLinked is true h1 is updated. */
+    void setH2(QPointF handle2);
+    void keepInRange(qreal xMin, qreal xMax);
+
+    /** @brief Sets handlesLinked to true if the handles are in a linked state (line through h1, p, h2) otherwise to false. */
+    void autoSetLinked();
+
+    /** handle 1 */
+    QPointF h1;
+    /** point */
+    QPointF p;
+    /** handle 2 */
+    QPointF h2;
+
+    /** handles are locked to achieve a natural locking spline => PH1 = -r*PH2 ; a line can be drawn through h1, p, h2 */
+    bool handlesLinked;
+};
+
+#endif
index ba95d3a80d4379da967be99b711ef6f05559737b..db5ff6fd8b74ff2603e9bf4bb9a58e384555d05b 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "cubicbezierspline.h"
 
-#include <KDebug>
 
 /** @brief For sorting a Bezier spline. Whether a is before b. */
 static bool pointLessThan(const BPoint &a, const BPoint &b)
@@ -31,23 +30,8 @@ CubicBezierSpline::CubicBezierSpline(QObject* parent) :
         m_validSpline(false),
         m_precision(100)
 {
-    BPoint start;
-    start.p.setX(0);
-    start.p.setY(0);
-    start.h1.setX(0);
-    start.h1.setY(0);
-    start.h2.setX(0.1);
-    start.h2.setY(0.1);
-    m_points.append(start);
-
-    BPoint end;
-    end.p.setX(1);
-    end.p.setY(1);
-    end.h1.setX(0.9);
-    end.h1.setY(0.9);
-    end.h2.setX(1);
-    end.h2.setY(1);
-    m_points.append(end);
+    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) :
@@ -81,11 +65,7 @@ void CubicBezierSpline::fromString(const QString& spline)
                 values.append(QPointF(xy.at(0).toDouble(), xy.at(1).toDouble()));
         }
         if (values.count() == 3) {
-            BPoint bp;
-            bp.h1 = values.at(0);
-            bp.p  = values.at(1);
-            bp.h2 = values.at(2);
-            m_points.append(bp);
+            m_points.append(BPoint(values.at(0), values.at(1), values.at(2)));
         }
     }
 
index dc25fb90d6afb779093c181a96178ce3529dcbae..ebf15e3d9f94506cc702a74c2abb9be387384c2a 100644 (file)
 #ifndef CUBICBEZIERSPLINE_H
 #define CUBICBEZIERSPLINE_H
 
+#include "bpoint.h"
 
 #include <QtCore>
 
-class BPoint
-{
-public:
-    QPointF h1;     // handle 1
-    QPointF p;      // point
-    QPointF h2;     // handle 2
-
-    BPoint() { p = QPointF(-1,-1); } // makes it illegal -> cannot be equal any point
-    bool operator==(const BPoint &point) const { return point.h1 == h1 && point.p == p && point.h2 == h2; }
-};
 
 class CubicBezierSpline : public QObject
 {
index 773c2269212c05133e58f6004516399235eb909b..8727ad9f1af51db2b6b95d4c3d6e1c093e480528 100644 (file)
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>315</width>
-    <height>83</height>
+    <width>351</width>
+    <height>78</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -20,6 +20,9 @@
       <property name="margin">
        <number>0</number>
       </property>
+      <property name="spacing">
+       <number>0</number>
+      </property>
       <item row="0" column="0" colspan="9">
        <widget class="QWidget" name="widget" native="true">
         <layout class="QHBoxLayout" name="horizontalLayout">
       <item row="1" column="4">
        <widget class="QToolButton" name="buttonLinkHandles">
         <property name="toolTip">
-         <string>Link the Handles' position so you draw a line through Handle -&gt; Point -&gt; Handle.&lt;br /&gt;Results in a natural spline.</string>
+         <string>Link the handles' position.&lt;br /&gt;Results in a natural spline.</string>
         </property>
         <property name="text">
          <string>...</string>