forked from qt-creator/qt-creator
MathUtils: Add tangential interpolation
Reuse it in TaskProgress and in ProgressTimer. Rename MathUtils::interpolate() into interpolateLinear() Change-Id: Iff4cda1e3b8782cd26277ec75046ca5526be92c0 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -12,7 +12,7 @@ namespace Utils::MathUtils {
|
||||
For x = x1 it returns y1.
|
||||
For x = x2 it returns y2.
|
||||
*/
|
||||
int interpolate(int x, int x1, int x2, int y1, int y2)
|
||||
int interpolateLinear(int x, int x1, int x2, int y1, int y2)
|
||||
{
|
||||
if (x1 == x2)
|
||||
return y1; // or the middle point between y1 and y2?
|
||||
@@ -27,4 +27,21 @@ int interpolate(int x, int x1, int x2, int y1, int y2)
|
||||
return qRound((double)numerator / denominator);
|
||||
}
|
||||
|
||||
/*!
|
||||
Tangential interpolation:
|
||||
For x = 0 it returns y1.
|
||||
For x = xHalfLife it returns 50 % of the distance between y1 and y2.
|
||||
For x = infinity it returns y2.
|
||||
*/
|
||||
int interpolateTangential(int x, int xHalfLife, int y1, int y2)
|
||||
{
|
||||
if (x == 0)
|
||||
return y1;
|
||||
if (y1 == y2)
|
||||
return y1;
|
||||
const double mapped = atan2(double(x), double(xHalfLife));
|
||||
const double progress = y1 + (y2 - y1) * mapped * 2 / M_PI;
|
||||
return qRound(progress);
|
||||
}
|
||||
|
||||
} // namespace Utils::Math
|
||||
|
@@ -7,6 +7,7 @@
|
||||
|
||||
namespace Utils::MathUtils {
|
||||
|
||||
QTCREATOR_UTILS_EXPORT int interpolate(int x, int x1, int x2, int y1, int y2);
|
||||
QTCREATOR_UTILS_EXPORT int interpolateLinear(int x, int x1, int x2, int y1, int y2);
|
||||
QTCREATOR_UTILS_EXPORT int interpolateTangential(int x, int xHalfLife, int y1, int y2);
|
||||
|
||||
} // namespace Utils::Math
|
||||
|
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/mathutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/stylehelper.h>
|
||||
#include <utils/theme/theme.h>
|
||||
@@ -27,7 +28,6 @@
|
||||
#include <QStyle>
|
||||
#include <QStyleOption>
|
||||
#include <QTimer>
|
||||
#include <QtMath>
|
||||
#include <QVariant>
|
||||
|
||||
static const char kSettingsGroup[] = "Progress";
|
||||
@@ -779,13 +779,8 @@ ProgressTimer::ProgressTimer(const QFutureInterfaceBase &futureInterface,
|
||||
void ProgressTimer::handleTimeout()
|
||||
{
|
||||
++m_currentTime;
|
||||
|
||||
// This maps expectation to atan(1) to Pi/4 ~= 0.78, i.e. snaps
|
||||
// from 78% to 100% when expectations are met at the time the
|
||||
// future finishes. That's not bad for a random choice.
|
||||
const double mapped = atan2(double(m_currentTime) * TimerInterval / 1000.0,
|
||||
double(m_expectedTime));
|
||||
const double progress = 100 * 2 * mapped / M_PI;
|
||||
m_futureInterface.setProgressValue(int(progress));
|
||||
const int halfLife = qRound(1000.0 * m_expectedTime / TimerInterval);
|
||||
const int progress = MathUtils::interpolateTangential(m_currentTime, halfLife, 0, 100);
|
||||
m_futureInterface.setProgressValue(progress);
|
||||
}
|
||||
|
||||
|
@@ -6,12 +6,12 @@
|
||||
#include "futureprogress.h"
|
||||
#include "progressmanager.h"
|
||||
|
||||
#include <utils/mathutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/tasktree.h>
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QTimer>
|
||||
#include <QtMath>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
@@ -80,13 +80,11 @@ void TaskProgressPrivate::advanceProgress(int newValue)
|
||||
|
||||
void TaskProgressPrivate::updateProgress()
|
||||
{
|
||||
// This maps expectation to atan(1) to Pi/4 ~= 0.78, i.e. snaps
|
||||
// from 78% to 100% when expectations are met at the time the
|
||||
// future finishes. That's not bad for a random choice.
|
||||
const double mapped = atan2(double(m_currentTick) * TimerInterval / 1000.0,
|
||||
double(m_expectedTime));
|
||||
const double progress = ProgressResolution * 2 * mapped / M_PI;
|
||||
m_futureInterface.setProgressValue(ProgressResolution * m_currentProgress + progress);
|
||||
const int halfLife = qRound(1000.0 * m_expectedTime / TimerInterval);
|
||||
const int pMin = ProgressResolution * m_currentProgress;
|
||||
const int pMax = ProgressResolution * (m_currentProgress + 1);
|
||||
const int newValue = MathUtils::interpolateTangential(m_currentTick, halfLife, pMin, pMax);
|
||||
m_futureInterface.setProgressValue(newValue);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@@ -365,7 +365,7 @@ SideBySideDiffOutput SideDiffData::diffOutput(QFutureInterface<void> &fi, int pr
|
||||
diffText[RightSide].replace('\r', ' ');
|
||||
output.side[LeftSide].diffText += diffText[LeftSide];
|
||||
output.side[RightSide].diffText += diffText[RightSide];
|
||||
fi.setProgressValue(MathUtils::interpolate(++i, 0, count, progressMin, progressMax));
|
||||
fi.setProgressValue(MathUtils::interpolateLinear(++i, 0, count, progressMin, progressMax));
|
||||
if (fi.isCanceled())
|
||||
return {};
|
||||
}
|
||||
@@ -952,7 +952,7 @@ void SideBySideDiffEditorWidget::showDiff()
|
||||
const QString package = output.side[side].diffText.mid(currentPos, packageSize);
|
||||
cursor.insertText(package);
|
||||
currentPos += package.size();
|
||||
fi.setProgressValue(MathUtils::interpolate(currentPos, 0, diffSize, progressMin, progressMax));
|
||||
fi.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize, progressMin, progressMax));
|
||||
if (fi.isCanceled())
|
||||
return;
|
||||
}
|
||||
|
@@ -436,7 +436,7 @@ UnifiedDiffOutput UnifiedDiffData::diffOutput(QFutureInterface<void> &fi, int pr
|
||||
output.diffData.m_chunkInfo.setChunkIndex(oldBlock, blockNumber - oldBlock, j);
|
||||
}
|
||||
}
|
||||
fi.setProgressValue(MathUtils::interpolate(++i, 0, count, progressMin, progressMax));
|
||||
fi.setProgressValue(MathUtils::interpolateLinear(++i, 0, count, progressMin, progressMax));
|
||||
if (fi.isCanceled())
|
||||
return {};
|
||||
}
|
||||
@@ -511,7 +511,7 @@ void UnifiedDiffEditorWidget::showDiff()
|
||||
const QString package = output.diffText.mid(currentPos, packageSize);
|
||||
cursor.insertText(package);
|
||||
currentPos += package.size();
|
||||
fi.setProgressValue(MathUtils::interpolate(currentPos, 0, diffSize, firstPartMax, progressMax));
|
||||
fi.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize, firstPartMax, progressMax));
|
||||
if (futureInterface.isCanceled())
|
||||
return;
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ add_subdirectory(fileutils)
|
||||
add_subdirectory(fsengine)
|
||||
add_subdirectory(fuzzymatcher)
|
||||
add_subdirectory(indexedcontainerproxyconstiterator)
|
||||
add_subdirectory(mathutils)
|
||||
add_subdirectory(multicursor)
|
||||
add_subdirectory(persistentsettings)
|
||||
add_subdirectory(qtcprocess)
|
||||
|
4
tests/auto/utils/mathutils/CMakeLists.txt
Normal file
4
tests/auto/utils/mathutils/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
add_qtc_test(tst_utils_mathutils
|
||||
DEPENDS Utils
|
||||
SOURCES tst_mathutils.cpp
|
||||
)
|
7
tests/auto/utils/mathutils/mathutils.qbs
Normal file
7
tests/auto/utils/mathutils/mathutils.qbs
Normal file
@@ -0,0 +1,7 @@
|
||||
import qbs
|
||||
|
||||
QtcAutotest {
|
||||
name: "MathUtils autotest"
|
||||
Depends { name: "Utils" }
|
||||
files: "tst_mathutils.cpp"
|
||||
}
|
77
tests/auto/utils/mathutils/tst_mathutils.cpp
Normal file
77
tests/auto/utils/mathutils/tst_mathutils.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "utils/mathutils.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
class tst_MathUtils : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void interpolateLinear_data();
|
||||
void interpolateLinear();
|
||||
void interpolateTangential_data();
|
||||
void interpolateTangential();
|
||||
};
|
||||
|
||||
void tst_MathUtils::interpolateLinear_data()
|
||||
{
|
||||
QTest::addColumn<int>("x");
|
||||
QTest::addColumn<int>("x1");
|
||||
QTest::addColumn<int>("x2");
|
||||
QTest::addColumn<int>("y1");
|
||||
QTest::addColumn<int>("y2");
|
||||
QTest::addColumn<int>("result");
|
||||
|
||||
QTest::newRow("x1") << 2 << 2 << 8 << 10 << 20 << 10;
|
||||
QTest::newRow("middleValue") << 5 << 2 << 8 << 10 << 20 << 15;
|
||||
QTest::newRow("x2") << 8 << 2 << 8 << 10 << 20 << 20;
|
||||
QTest::newRow("belowX1") << -1 << 2 << 8 << 10 << 20 << 5;
|
||||
QTest::newRow("aboveX2") << 11 << 2 << 8 << 10 << 20 << 25;
|
||||
}
|
||||
|
||||
void tst_MathUtils::interpolateLinear()
|
||||
{
|
||||
QFETCH(int, x);
|
||||
QFETCH(int, x1);
|
||||
QFETCH(int, x2);
|
||||
QFETCH(int, y1);
|
||||
QFETCH(int, y2);
|
||||
QFETCH(int, result);
|
||||
|
||||
const int y = MathUtils::interpolateLinear(x, x1, x2, y1, y2);
|
||||
QCOMPARE(y, result);
|
||||
}
|
||||
|
||||
void tst_MathUtils::interpolateTangential_data()
|
||||
{
|
||||
QTest::addColumn<int>("x");
|
||||
QTest::addColumn<int>("xHalfLife");
|
||||
QTest::addColumn<int>("y1");
|
||||
QTest::addColumn<int>("y2");
|
||||
QTest::addColumn<int>("result");
|
||||
|
||||
QTest::newRow("zero") << 0 << 8 << 10 << 20 << 10;
|
||||
QTest::newRow("halfLife") << 8 << 8 << 10 << 20 << 15;
|
||||
QTest::newRow("approxInfinity") << 1000 << 8 << 10 << 20 << 20;
|
||||
}
|
||||
|
||||
void tst_MathUtils::interpolateTangential()
|
||||
{
|
||||
QFETCH(int, x);
|
||||
QFETCH(int, xHalfLife);
|
||||
QFETCH(int, y1);
|
||||
QFETCH(int, y2);
|
||||
QFETCH(int, result);
|
||||
|
||||
const int y = MathUtils::interpolateTangential(x, xHalfLife, y1, y2);
|
||||
QCOMPARE(y, result);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_MathUtils)
|
||||
|
||||
#include "tst_mathutils.moc"
|
@@ -11,6 +11,7 @@ Project {
|
||||
"fsengine/fsengine.qbs",
|
||||
"fuzzymatcher/fuzzymatcher.qbs",
|
||||
"indexedcontainerproxyconstiterator/indexedcontainerproxyconstiterator.qbs",
|
||||
"mathutils/mathutils.qbs",
|
||||
"multicursor/multicursor.qbs",
|
||||
"persistentsettings/persistentsettings.qbs",
|
||||
"qtcprocess/qtcprocess.qbs",
|
||||
|
Reference in New Issue
Block a user