]> git.sesse.net Git - kdenlive/blob - src/beziercurve/cubicbezierspline.cpp
Bezier Spline:
[kdenlive] / src / beziercurve / cubicbezierspline.cpp
1 /***************************************************************************
2  *   Copyright (C) 2010 by Till Theato (root@ttill.de)                     *
3  *   This file is part of Kdenlive (www.kdenlive.org).                     *
4  *                                                                         *
5  *   Kdenlive is free software: you can redistribute it and/or modify      *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation, either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   Kdenlive is distributed in the hope that it will be useful,           *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with Kdenlive.  If not, see <http://www.gnu.org/licenses/>.     *
17  ***************************************************************************/
18
19 #include "cubicbezierspline.h"
20
21 #include <KDebug>
22
23 static bool pointLessThan(const BPoint &a, const BPoint &b)
24 {
25     return a.p.x() < b.p.x();
26 }
27
28 CubicBezierSpline::CubicBezierSpline(QObject* parent) :
29         QObject(parent),
30         m_validSpline(false),
31         m_precision(100)
32 {
33     BPoint start;
34     start.p.setX(0);
35     start.p.setY(0);
36     start.h1.setX(0);
37     start.h1.setY(0);
38     start.h2.setX(0.1);
39     start.h2.setY(0.1);
40     m_points.append(start);
41
42     BPoint end;
43     end.p.setX(1);
44     end.p.setY(1);
45     end.h1.setX(0.9);
46     end.h1.setY(0.9);
47     end.h2.setX(1);
48     end.h2.setY(1);
49     m_points.append(end);
50 }
51
52 CubicBezierSpline::CubicBezierSpline(const CubicBezierSpline& spline, QObject* parent) :
53         QObject(parent)
54 {
55     m_precision = spline.m_precision;
56     m_points = spline.m_points;
57     m_validSpline = false;
58 }
59
60 CubicBezierSpline& CubicBezierSpline::operator=(const CubicBezierSpline& spline)
61 {
62     m_precision = spline.m_precision;
63     m_points = spline.m_points;
64     return *this;
65 }
66
67 void CubicBezierSpline::fromString(const QString& spline)
68 {
69     m_points.clear();
70     m_validSpline = false;
71
72     QStringList bpoints = spline.split('|');
73     foreach(const QString &bpoint, bpoints) {
74         QStringList points = bpoint.split('#');
75         QList <QPointF> values;
76         foreach(const QString &point, points) {
77             QStringList xy = point.split(';');
78             if (xy.count() == 2)
79                 values.append(QPointF(xy.at(0).toDouble(), xy.at(1).toDouble()));
80         }
81         if (values.count() == 3) {
82             BPoint bp;
83             bp.h1 = values.at(0);
84             bp.p  = values.at(1);
85             bp.h2 = values.at(2);
86             m_points.append(bp);
87         }
88     }
89
90     keepSorted();
91     validatePoints();
92 }
93
94 QString CubicBezierSpline::toString() const
95 {
96     QStringList spline;
97     foreach(const BPoint &p, m_points) {
98         spline << (QString::number(p.h1.x()) + ";" + QString::number(p.h1.y())
99                         + "#" + QString::number(p.p.x())  + ";" + QString::number(p.p.y())
100                         + "#" + QString::number(p.h2.x()) + ";" + QString::number(p.h2.y()));
101     }
102     return spline.join("|");
103 }
104
105 int CubicBezierSpline::setPoint(int ix, const BPoint& point)
106 {
107     m_points[ix] = point;
108     keepSorted();
109     validatePoints();
110     m_validSpline = false;
111     return m_points.indexOf(point); // in case it changed
112 }
113
114 QList <BPoint> CubicBezierSpline::points()
115 {
116     return m_points;
117 }
118
119 void CubicBezierSpline::removePoint(int ix)
120 {
121     m_points.removeAt(ix);
122     m_validSpline = false;
123 }
124
125 int CubicBezierSpline::addPoint(const BPoint& point)
126 {
127     m_points.append(point);
128     keepSorted();
129     validatePoints();
130     m_validSpline = false;
131     if (m_points.indexOf(point) == -1) {
132         // point changed during validation process
133         for (int i = 0; i < m_points.count(); ++i) {
134             // this condition should be sufficient, too
135             if (m_points.at(i).p == point.p)
136                 return i;
137         }
138     } else {
139         return m_points.indexOf(point);
140     }
141 }
142
143 void CubicBezierSpline::setPrecision(int pre)
144 {
145     if (pre != m_precision) {
146         m_precision = pre;
147         m_validSpline = false;
148     }
149 }
150
151 int CubicBezierSpline::getPrecision()
152 {
153     return m_precision;
154 }
155
156 qreal CubicBezierSpline::value(qreal x, bool cont)
157 {
158     update();
159
160     //x = qBound(m_spline.constBegin().key(), x, m_spline.constEnd().key());
161     //kDebug() << "....x" << x<<"bounddown"<<m_spline.constBegin().key()<<"up"<<m_spline.constEnd().key();
162
163     if (!cont)
164         m_i = m_spline.constBegin();
165     if (m_i != m_spline.constBegin())
166         --m_i;
167
168     double diff = qAbs(x - m_i.key());
169     double y = m_i.value();
170     while (m_i != m_spline.constEnd()) {
171         if (qAbs(x - m_i.key()) > diff)
172             break;
173
174         diff = qAbs(x - m_i.key());
175         y = m_i.value();
176         ++m_i;
177     }
178     return qBound((qreal)0.0, y, (qreal)1.0);
179 }
180
181 void CubicBezierSpline::validatePoints()
182 {
183     BPoint p1, p2;
184     for (int i = 0; i < m_points.count() - 1; ++i) {
185         p1 = m_points.at(i);
186         p2 = m_points.at(i+1);
187         p1.h2.setX(qBound(p1.p.x(), p1.h2.x(), p2.p.x()));
188         p2.h1.setX(qBound(p1.p.x(), p2.h1.x(), p2.p.x()));
189         m_points[i] = p1;
190         m_points[i+1] = p2;
191     }
192 }
193
194 void CubicBezierSpline::keepSorted()
195 {
196     qSort(m_points.begin(), m_points.end(), pointLessThan);
197 }
198
199 QPointF CubicBezierSpline::point(double t, const QList< QPointF >& points)
200 {
201     // coefficients from Bernstein basis polynomial of degree 3
202     double c1 = (1-t) * (1-t) * (1-t);
203     double c2 = 3 * t * (1-t) * (1-t);
204     double c3 = 3 * t * t * (1-t);
205     double c4 = t * t * t;
206     
207     return QPointF(points[0].x()*c1 + points[1].x()*c2 + points[2].x()*c3 + points[3].x()*c4,
208                    points[0].y()*c1 + points[1].y()*c2 + points[2].y()*c3 + points[3].y()*c4);
209 }
210
211 void CubicBezierSpline::update()
212 {
213     if (m_validSpline)
214         return;
215
216     m_validSpline = true;
217     m_spline.clear();
218
219     QList <QPointF> points;
220     QPointF p;
221     for (int i = 0; i < m_points.count() - 1; ++i) {
222         points.clear();
223         points << m_points.at(i).p
224                 << m_points.at(i).h2
225                 << m_points.at(i+1).h1
226                 << m_points.at(i+1).p;
227
228         int numberOfValues = (int)((points[3].x() - points[0].x()) * m_precision * 5);
229         if (numberOfValues == 0)
230             numberOfValues = 1;
231         double step = 1 / (double)numberOfValues;
232         double t = 0;
233         while (t <= 1) {
234             p = point(t, points);
235             m_spline.insert(p.x(), p.y());
236             t += step;
237         }
238     }
239     /*QMap<double, double>::const_iterator i = m_spline.constBegin();
240     kDebug()<<"////////////spline/////////////";
241     while (i != m_spline.constEnd()) {
242         kDebug() << i.key() << i.value();
243         ++i;
244     }
245     kDebug()<<"////////////spline/////////////end";*/
246 }
247
248 #include "cubicbezierspline.moc"