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 }