File indexing completed on 2025-01-26 04:05:53

0001 /*
0002  *  SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisStrokeSpeedMeasurer.h"
0008 
0009 #include <QQueue>
0010 #include <QVector>
0011 
0012 #include "kis_global.h"
0013 
0014 struct KisStrokeSpeedMeasurer::Private
0015 {
0016     struct StrokeSample {
0017         StrokeSample() {}
0018         StrokeSample(int _time, qreal _distance) : time(_time), distance(_distance) {}
0019 
0020         int time = 0; /* ms */
0021         qreal distance = 0;
0022     };
0023 
0024     int timeSmoothWindow = 0;
0025 
0026     QList<StrokeSample> samples;
0027     QPointF lastSamplePos;
0028     int startTime = 0;
0029 
0030     qreal maxSpeed = 0;
0031 
0032     void purgeOldSamples();
0033     void addSampleImpl(const QPointF &pt, int time);
0034 };
0035 
0036 KisStrokeSpeedMeasurer::KisStrokeSpeedMeasurer(int timeSmoothWindow)
0037     : m_d(new Private())
0038 {
0039     m_d->timeSmoothWindow = timeSmoothWindow;
0040 }
0041 
0042 KisStrokeSpeedMeasurer::~KisStrokeSpeedMeasurer()
0043 {
0044 }
0045 
0046 void KisStrokeSpeedMeasurer::Private::addSampleImpl(const QPointF &pt, int time)
0047 {
0048     if (samples.isEmpty()) {
0049         lastSamplePos = pt;
0050         startTime = time;
0051         samples.append(Private::StrokeSample(time, 0));
0052     } else {
0053         Private::StrokeSample &lastSample = samples.last();
0054 
0055         const qreal newStrokeDistance = lastSample.distance + kisDistance(lastSamplePos, pt);
0056         lastSamplePos = pt;
0057 
0058         if (lastSample.time >= time) {
0059             lastSample.distance = newStrokeDistance;
0060         } else {
0061             samples.append(Private::StrokeSample(time, newStrokeDistance));
0062         }
0063     }
0064 }
0065 
0066 void KisStrokeSpeedMeasurer::addSample(const QPointF &pt, int time)
0067 {
0068     m_d->addSampleImpl(pt, time);
0069     m_d->purgeOldSamples();
0070     sampleMaxSpeed();
0071 }
0072 
0073 void KisStrokeSpeedMeasurer::addSamples(const QVector<QPointF> &points, int time)
0074 {
0075     const int lastSampleTime = !m_d->samples.isEmpty() ? m_d->samples.last().time : 0;
0076 
0077     const int timeSmoothBase = qMin(lastSampleTime, time);
0078     const qreal timeSmoothStep = qreal(time - timeSmoothBase) / points.size();
0079 
0080     for (int i = 0; i < points.size(); i++) {
0081         const int sampleTime = timeSmoothBase + timeSmoothStep * (i + 1);
0082         m_d->addSampleImpl(points[i], sampleTime);
0083     }
0084 
0085     m_d->purgeOldSamples();
0086     sampleMaxSpeed();
0087 }
0088 
0089 qreal KisStrokeSpeedMeasurer::averageSpeed() const
0090 {
0091     if (m_d->samples.isEmpty()) return 0;
0092 
0093     const Private::StrokeSample &lastSample = m_d->samples.last();
0094 
0095     const int timeDiff = lastSample.time - m_d->startTime;
0096     if (!timeDiff) return 0;
0097 
0098     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(timeDiff > 0, 0);
0099 
0100     return lastSample.distance / timeDiff;
0101 }
0102 
0103 void KisStrokeSpeedMeasurer::Private::purgeOldSamples()
0104 {
0105     if (samples.size() <= 1) return;
0106 
0107     const Private::StrokeSample lastSample = samples.last();
0108 
0109     auto lastValueToKeep = samples.end();
0110 
0111     for (auto it = samples.begin(); it != samples.end(); ++it) {
0112         KIS_SAFE_ASSERT_RECOVER_RETURN(lastSample.time - it->time >= 0);
0113 
0114         if (lastSample.time - it->time < timeSmoothWindow) break;
0115         lastValueToKeep = it;
0116     }
0117 
0118     if (lastValueToKeep != samples.begin() &&
0119         lastValueToKeep != samples.end()) {
0120 
0121         samples.erase(samples.begin(), lastValueToKeep - 1);
0122     }
0123 }
0124 
0125 qreal KisStrokeSpeedMeasurer::currentSpeed() const
0126 {
0127     if (m_d->samples.size() <= 1) return 0;
0128 
0129     const Private::StrokeSample firstSample = m_d->samples.first();
0130     const Private::StrokeSample lastSample = m_d->samples.last();
0131 
0132     const int timeDiff = lastSample.time - firstSample.time;
0133     if (!timeDiff) return 0;
0134 
0135     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(timeDiff > 0, 0);
0136 
0137     return (lastSample.distance - firstSample.distance) / timeDiff;
0138 }
0139 
0140 qreal KisStrokeSpeedMeasurer::maxSpeed() const
0141 {
0142     return m_d->maxSpeed;
0143 }
0144 
0145 void KisStrokeSpeedMeasurer::reset()
0146 {
0147     m_d->samples.clear();
0148     m_d->lastSamplePos = QPointF();
0149     m_d->startTime = 0;
0150     m_d->maxSpeed = 0;
0151 }
0152 
0153 void KisStrokeSpeedMeasurer::sampleMaxSpeed()
0154 {
0155     if (m_d->samples.size() <= 1) return;
0156 
0157     const Private::StrokeSample firstSample = m_d->samples.first();
0158     const Private::StrokeSample lastSample = m_d->samples.last();
0159 
0160     const int timeDiff = lastSample.time - firstSample.time;
0161     if (timeDiff < m_d->timeSmoothWindow) return;
0162 
0163     const qreal speed = currentSpeed();
0164     if (speed > m_d->maxSpeed) {
0165         m_d->maxSpeed = speed;
0166     }
0167 }