1 /*
2 * Copyright (c) 2017 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "KisStrokeSpeedMeasurer.h"
20
21 #include <QQueue>
22 #include <QVector>
23
24 #include "kis_global.h"
25
26 struct KisStrokeSpeedMeasurer::Private
27 {
28 struct StrokeSample {
StrokeSampleKisStrokeSpeedMeasurer::Private::StrokeSample29 StrokeSample() {}
StrokeSampleKisStrokeSpeedMeasurer::Private::StrokeSample30 StrokeSample(int _time, qreal _distance) : time(_time), distance(_distance) {}
31
32 int time = 0; /* ms */
33 qreal distance = 0;
34 };
35
36 int timeSmoothWindow = 0;
37
38 QList<StrokeSample> samples;
39 QPointF lastSamplePos;
40 int startTime = 0;
41
42 qreal maxSpeed = 0;
43
44 void purgeOldSamples();
45 void addSampleImpl(const QPointF &pt, int time);
46 };
47
KisStrokeSpeedMeasurer(int timeSmoothWindow)48 KisStrokeSpeedMeasurer::KisStrokeSpeedMeasurer(int timeSmoothWindow)
49 : m_d(new Private())
50 {
51 m_d->timeSmoothWindow = timeSmoothWindow;
52 }
53
~KisStrokeSpeedMeasurer()54 KisStrokeSpeedMeasurer::~KisStrokeSpeedMeasurer()
55 {
56 }
57
addSampleImpl(const QPointF & pt,int time)58 void KisStrokeSpeedMeasurer::Private::addSampleImpl(const QPointF &pt, int time)
59 {
60 if (samples.isEmpty()) {
61 lastSamplePos = pt;
62 startTime = time;
63 samples.append(Private::StrokeSample(time, 0));
64 } else {
65 Private::StrokeSample &lastSample = samples.last();
66
67 const qreal newStrokeDistance = lastSample.distance + kisDistance(lastSamplePos, pt);
68 lastSamplePos = pt;
69
70 if (lastSample.time >= time) {
71 lastSample.distance = newStrokeDistance;
72 } else {
73 samples.append(Private::StrokeSample(time, newStrokeDistance));
74 }
75 }
76 }
77
addSample(const QPointF & pt,int time)78 void KisStrokeSpeedMeasurer::addSample(const QPointF &pt, int time)
79 {
80 m_d->addSampleImpl(pt, time);
81 m_d->purgeOldSamples();
82 sampleMaxSpeed();
83 }
84
addSamples(const QVector<QPointF> & points,int time)85 void KisStrokeSpeedMeasurer::addSamples(const QVector<QPointF> &points, int time)
86 {
87 const int lastSampleTime = !m_d->samples.isEmpty() ? m_d->samples.last().time : 0;
88
89 const int timeSmoothBase = qMin(lastSampleTime, time);
90 const qreal timeSmoothStep = qreal(time - timeSmoothBase) / points.size();
91
92 for (int i = 0; i < points.size(); i++) {
93 const int sampleTime = timeSmoothBase + timeSmoothStep * (i + 1);
94 m_d->addSampleImpl(points[i], sampleTime);
95 }
96
97 m_d->purgeOldSamples();
98 sampleMaxSpeed();
99 }
100
averageSpeed() const101 qreal KisStrokeSpeedMeasurer::averageSpeed() const
102 {
103 if (m_d->samples.isEmpty()) return 0;
104
105 const Private::StrokeSample &lastSample = m_d->samples.last();
106
107 const int timeDiff = lastSample.time - m_d->startTime;
108 if (!timeDiff) return 0;
109
110 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(timeDiff > 0, 0);
111
112 return lastSample.distance / timeDiff;
113 }
114
purgeOldSamples()115 void KisStrokeSpeedMeasurer::Private::purgeOldSamples()
116 {
117 if (samples.size() <= 1) return;
118
119 const Private::StrokeSample lastSample = samples.last();
120
121 auto lastValueToKeep = samples.end();
122
123 for (auto it = samples.begin(); it != samples.end(); ++it) {
124 KIS_SAFE_ASSERT_RECOVER_RETURN(lastSample.time - it->time >= 0);
125
126 if (lastSample.time - it->time < timeSmoothWindow) break;
127 lastValueToKeep = it;
128 }
129
130 if (lastValueToKeep != samples.begin() &&
131 lastValueToKeep != samples.end()) {
132
133 samples.erase(samples.begin(), lastValueToKeep - 1);
134 }
135 }
136
currentSpeed() const137 qreal KisStrokeSpeedMeasurer::currentSpeed() const
138 {
139 if (m_d->samples.size() <= 1) return 0;
140
141 const Private::StrokeSample firstSample = m_d->samples.first();
142 const Private::StrokeSample lastSample = m_d->samples.last();
143
144 const int timeDiff = lastSample.time - firstSample.time;
145 if (!timeDiff) return 0;
146
147 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(timeDiff > 0, 0);
148
149 return (lastSample.distance - firstSample.distance) / timeDiff;
150 }
151
maxSpeed() const152 qreal KisStrokeSpeedMeasurer::maxSpeed() const
153 {
154 return m_d->maxSpeed;
155 }
156
reset()157 void KisStrokeSpeedMeasurer::reset()
158 {
159 m_d->samples.clear();
160 m_d->lastSamplePos = QPointF();
161 m_d->startTime = 0;
162 m_d->maxSpeed = 0;
163 }
164
sampleMaxSpeed()165 void KisStrokeSpeedMeasurer::sampleMaxSpeed()
166 {
167 if (m_d->samples.size() <= 1) return;
168
169 const Private::StrokeSample firstSample = m_d->samples.first();
170 const Private::StrokeSample lastSample = m_d->samples.last();
171
172 const int timeDiff = lastSample.time - firstSample.time;
173 if (timeDiff < m_d->timeSmoothWindow) return;
174
175 const qreal speed = currentSpeed();
176 if (speed > m_d->maxSpeed) {
177 m_d->maxSpeed = speed;
178 }
179 }
180