2 * Copyright (c) 2005 Casper Boemann <cbr@boemann.dk>
3 * Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
4 * Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "kis_cubic_curve.h"
25 #include <QSharedData>
26 #include <QStringList>
30 class KisTridiagonalSystem
34 * |b0 c0 0 0 0| |x0| |f0|
35 * |a0 b1 c1 0 0| |x1| |f1|
36 * |0 a1 b2 c2 0|*|x2|=|f2|
37 * |0 0 a2 b3 c3| |x3| |f3|
38 * |0 0 0 a3 b4| |x4| |f4|
44 * @return - vector that is storing x[]
47 QVector<T> calculate(QList<T> &a,
58 Q_ASSERT(a.size() == size - 1 &&
59 c.size() == size - 1 &&
65 * Check for special case when
66 * order of the matrix is equal to 1
81 alpha[1] = -c[0] / b[0];
82 beta[1] = f[0] / b[0];
84 for (i = 1; i < size - 1; ++i) {
86 (a[i-1] * alpha[i] + b[i]);
88 beta[i+1] = (f[i] - a[i-1] * beta[i])
90 (a[i-1] * alpha[i] + b[i]);
93 x.last() = (f.last() - a.last() * beta.last())
95 (b.last() + a.last() * alpha.last());
97 for (i = size - 2; i >= 0; --i)
98 x[i] = alpha[i+1] * x[i+1] + beta[i+1];
104 template <typename T_point, typename T>
110 * 1/2 * c[i] * (x-x[i])^2 +
111 * 1/6 * d[i] * (x-x[i])^3
129 KisCubicSpline() : m_begin(0), m_end(0), m_intervals(0) {}
130 KisCubicSpline(const QList<T_point> &a) : m_begin(0), m_end(0),
136 * Create new spline and precalculate some values
139 * @a - base points of the spline
141 void createSpline(const QList<T_point> &a) {
142 int intervals = m_intervals = a.size() - 1;
144 m_begin = a.first().x();
145 m_end = a.last().x();
148 m_b.resize(intervals);
150 m_d.resize(intervals);
151 m_h.resize(intervals);
153 for (i = 0; i < intervals; ++i) {
154 m_h[i] = a[i+1].x() - a[i].x();
155 m_a.append(a[i].y());
157 m_a.append(a.last().y());
162 QList<T> tri_a; /* equals to @tri_c */
164 for (i = 0; i < intervals - 1; ++i) {
165 tri_b.append(2.*(m_h[i] + m_h[i+1]));
167 tri_f.append(6.*((m_a[i+2] - m_a[i+1]) / m_h[i+1] - (m_a[i+1] - m_a[i]) / m_h[i]));
169 for (i = 1; i < intervals - 1; ++i)
170 tri_a.append(m_h[i]);
173 KisTridiagonalSystem<T> tridia;
174 m_c = tridia.calculate(tri_a, tri_b, tri_a, tri_f);
179 for (i = 0; i < intervals; ++i)
180 m_d[i] = (m_c[i+1] - m_c[i]) / m_h[i];
182 for (i = 0; i < intervals; ++i)
183 m_b[i] = -0.5 * (m_c[i] * m_h[i]) - (1 / 6.0) * (m_d[i] * m_h[i] * m_h[i]) + (m_a[i+1] - m_a[i]) / m_h[i];
187 * Get value of precalculated spline in the point @x
189 T getValue(T x) const {
191 int i = findRegion(x, x0);
192 /* TODO: check for asm equivalent */
195 0.5 * m_c[i] *(x - x0) *(x - x0) +
196 (1 / 6.0)* m_d[i] *(x - x0) *(x - x0) *(x - x0);
210 * findRegion - Searches for the region containing @x
211 * @x0 - out parameter, containing beginning of the region
212 * @return - index of the region
214 int findRegion(T x, T &x0) const {
217 for (i = 0; i < m_intervals; ++i) {
218 if (x >= x0 && x < x0 + m_h[i])
223 x0 -= m_h[m_intervals-1];
224 return m_intervals - 1;
227 qDebug("X value: %f\n", x);
228 qDebug("m_begin: %f\n", m_begin);
229 qDebug("m_end : %f\n", m_end);
230 Q_ASSERT_X(0, "findRegion", "X value is outside regions");
231 /* **never reached** */
236 static bool pointLessThan(const QPointF &a, const QPointF &b)
238 return a.x() < b.x();
241 struct KisCubicCurve::Data : public QSharedData {
245 Data(const Data& data) : QSharedData() {
247 points = data.points;
251 validU16Transfer = false;
252 validFTransfer = false;
256 mutable KisCubicSpline<QPointF, qreal> spline;
257 QList<QPointF> points;
258 mutable bool validSpline;
259 mutable QVector<quint16> u16Transfer;
260 mutable bool validU16Transfer;
261 mutable QVector<qreal> fTransfer;
262 mutable bool validFTransfer;
265 qreal value(qreal x);
267 template<typename _T_, typename _T2_>
268 void updateTransfer(QVector<_T_>* transfer, bool& valid, _T2_ min, _T2_ max, int size);
271 void KisCubicCurve::Data::updateSpline()
273 if (validSpline) return;
275 spline.createSpline(points);
278 void KisCubicCurve::Data::invalidate()
281 validFTransfer = false;
282 validU16Transfer = false;
285 void KisCubicCurve::Data::keepSorted()
287 qSort(points.begin(), points.end(), pointLessThan);
290 qreal KisCubicCurve::Data::value(qreal x)
293 /* Automatically extend non-existing parts of the curve
294 * (e.g. before the first point) and cut off big y-values
296 x = qBound(spline.begin(), x, spline.end());
297 qreal y = spline.getValue(x);
298 return qBound((qreal)0.0, y, (qreal)1.0);
301 template<typename _T_, typename _T2_>
302 void KisCubicCurve::Data::updateTransfer(QVector<_T_>* transfer, bool& valid, _T2_ min, _T2_ max, int size)
304 if (!valid || transfer->size() != size) {
305 if (transfer->size() != size) {
306 transfer->resize(size);
308 qreal end = 1.0 / (size - 1);
309 for (int i = 0; i < size; ++i) {
310 /* Direct uncached version */
311 _T2_ val = value(i * end) * max;
312 val = qBound(min, val, max);
313 (*transfer)[i] = val;
319 struct KisCubicCurve::Private {
320 QSharedDataPointer<Data> data;
323 KisCubicCurve::KisCubicCurve() : d(new Private)
329 d->data->points.append(p);
332 d->data->points.append(p);
335 KisCubicCurve::KisCubicCurve(const QList<QPointF>& points) : d(new Private)
338 d->data->points = points;
339 d->data->keepSorted();
342 KisCubicCurve::KisCubicCurve(const KisCubicCurve& curve) : d(new Private(*curve.d))
346 KisCubicCurve::~KisCubicCurve()
351 KisCubicCurve& KisCubicCurve::operator=(const KisCubicCurve & curve)
357 bool KisCubicCurve::operator==(const KisCubicCurve& curve) const
359 if (d->data == curve.d->data) return true;
360 return d->data->points == curve.d->data->points;
363 qreal KisCubicCurve::value(qreal x) const
365 return d->data->value(x);
368 QList<QPointF> KisCubicCurve::points() const
370 return d->data->points;
373 void KisCubicCurve::setPoints(const QList<QPointF>& points)
376 d->data->points = points;
377 d->data->invalidate();
380 void KisCubicCurve::setPoint(int idx, const QPointF& point)
383 d->data->points[idx] = point;
384 d->data->keepSorted();
385 d->data->invalidate();
388 int KisCubicCurve::addPoint(const QPointF& point)
391 d->data->points.append(point);
392 d->data->keepSorted();
393 d->data->invalidate();
394 return d->data->points.indexOf(point);
397 void KisCubicCurve::removePoint(int idx)
400 d->data->points.removeAt(idx);
401 d->data->invalidate();
404 QString KisCubicCurve::toString() const
408 foreach(const QPointF & pair, d->data->points) {
409 sCurve += locale.toString(pair.x());
411 sCurve += QString::number(pair.y());
417 void KisCubicCurve::fromString(const QString& string)
419 QStringList data = string.split(';');
421 QList<QPointF> points;
423 foreach(const QString & pair, data) {
424 if (pair.indexOf('/') > -1) {
426 p.rx() = pair.section('/', 0, 0).toDouble();
427 p.ry() = pair.section('/', 1, 1).toDouble();
434 QVector<quint16> KisCubicCurve::uint16Transfer(int size) const
436 d->data->updateTransfer<quint16, int>(&d->data->u16Transfer, d->data->validU16Transfer, 0x0, 0xFFFF, size);
437 return d->data->u16Transfer;
440 QVector<qreal> KisCubicCurve::floatTransfer(int size) const
442 d->data->updateTransfer<qreal, qreal>(&d->data->fTransfer, d->data->validFTransfer, 0.0, 1.0, size);
443 return d->data->fTransfer;