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