1 /***************************************************************************
2 * Copyright (C) 2010 by Till Theato (root@ttill.de) *
3 * This file is part of Kdenlive (www.kdenlive.org). *
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. *
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. *
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 ***************************************************************************/
19 #include "cubicbezierspline.h"
22 /** @brief For sorting a Bezier spline. Whether a is before b. */
23 static bool pointLessThan(const BPoint &a, const BPoint &b)
25 return a.p.x() < b.p.x();
28 CubicBezierSpline::CubicBezierSpline(QObject* parent) :
33 m_points.append(BPoint(QPointF(0, 0), QPointF(0, 0), QPointF(.1, .1)));
34 m_points.append(BPoint(QPointF(.9, .9), QPointF(1, 1), QPointF(1, 1)));
37 CubicBezierSpline::CubicBezierSpline(const CubicBezierSpline& spline, QObject* parent) :
40 m_precision = spline.m_precision;
41 m_points = spline.m_points;
42 m_validSpline = false;
45 CubicBezierSpline& CubicBezierSpline::operator=(const CubicBezierSpline& spline)
47 m_precision = spline.m_precision;
48 m_points = spline.m_points;
49 m_validSpline = false;
53 void CubicBezierSpline::fromString(const QString& spline)
56 m_validSpline = false;
58 QStringList bpoints = spline.split('|');
59 foreach(const QString &bpoint, bpoints) {
60 QStringList points = bpoint.split('#');
61 QList <QPointF> values;
62 foreach(const QString &point, points) {
63 QStringList xy = point.split(';');
65 values.append(QPointF(xy.at(0).toDouble(), xy.at(1).toDouble()));
67 if (values.count() == 3) {
68 m_points.append(BPoint(values.at(0), values.at(1), values.at(2)));
76 QString CubicBezierSpline::toString() const
79 foreach(const BPoint &p, m_points) {
80 spline << QString("%1;%2#%3;%4#%5;%6").arg(p.h1.x()).arg(p.h1.y())
81 .arg(p.p.x()).arg(p.p.y())
82 .arg(p.h2.x()).arg(p.h2.y());
84 return spline.join("|");
87 int CubicBezierSpline::setPoint(int ix, const BPoint& point)
92 m_validSpline = false;
93 return indexOf(point); // in case it changed
96 QList <BPoint> CubicBezierSpline::points()
101 void CubicBezierSpline::removePoint(int ix)
103 m_points.removeAt(ix);
104 m_validSpline = false;
107 int CubicBezierSpline::addPoint(const BPoint& point)
109 m_points.append(point);
112 m_validSpline = false;
113 return indexOf(point);
116 void CubicBezierSpline::setPrecision(int pre)
118 if (pre != m_precision) {
120 m_validSpline = false;
124 int CubicBezierSpline::getPrecision()
129 qreal CubicBezierSpline::value(qreal x, bool cont)
134 m_i = m_spline.constBegin();
135 if (m_i != m_spline.constBegin())
138 double diff = qAbs(x - m_i.key());
139 double y = m_i.value();
140 while (m_i != m_spline.constEnd()) {
141 if (qAbs(x - m_i.key()) > diff)
144 diff = qAbs(x - m_i.key());
148 return qBound((qreal)0.0, y, (qreal)1.0);
151 void CubicBezierSpline::validatePoints()
154 for (int i = 0; i < m_points.count() - 1; ++i) {
156 p2 = m_points.at(i+1);
157 p1.h2.setX(qBound(p1.p.x(), p1.h2.x(), p2.p.x()));
158 p2.h1.setX(qBound(p1.p.x(), p2.h1.x(), p2.p.x()));
164 void CubicBezierSpline::keepSorted()
166 qSort(m_points.begin(), m_points.end(), pointLessThan);
169 QPointF CubicBezierSpline::point(double t, const QList< QPointF >& points)
172 * Calculating a point on the bezier curve using the coefficients from Bernstein basis polynomial of degree 3.
173 * Using the De Casteljau algorithm would be slightly faster for when calculating a lot of values
174 * but the difference is far from noticable in this needcase
176 double c1 = (1-t) * (1-t) * (1-t);
177 double c2 = 3 * t * (1-t) * (1-t);
178 double c3 = 3 * t * t * (1-t);
179 double c4 = t * t * t;
181 return QPointF(points[0].x()*c1 + points[1].x()*c2 + points[2].x()*c3 + points[3].x()*c4,
182 points[0].y()*c1 + points[1].y()*c2 + points[2].y()*c3 + points[3].y()*c4);
185 void CubicBezierSpline::update()
190 m_validSpline = true;
193 QList <QPointF> points;
195 for (int i = 0; i < m_points.count() - 1; ++i) {
197 points << m_points.at(i).p
199 << m_points.at(i+1).h1
200 << m_points.at(i+1).p;
202 int numberOfValues = (int)((points[3].x() - points[0].x()) * m_precision * 10);
203 if (numberOfValues == 0)
205 double step = 1 / (double)numberOfValues;
208 p = point(t, points);
209 m_spline.insert(p.x(), p.y());
215 int CubicBezierSpline::indexOf(const BPoint& p)
217 if (m_points.indexOf(p) == -1) {
218 // point changed during validation process
219 for (int i = 0; i < m_points.count(); ++i) {
220 // this condition should be sufficient, too
221 if (m_points.at(i).p == p.p)
225 return m_points.indexOf(p);
230 #include "cubicbezierspline.moc"