From 23f53dcbda305d3e2f593b4e0c566375af7dc0fb Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 25 Nov 2022 18:34:28 +0100 Subject: MathUtils: Add tangential interpolation Reuse it in TaskProgress and in ProgressTimer. Rename MathUtils::interpolate() into interpolateLinear() Change-Id: Iff4cda1e3b8782cd26277ec75046ca5526be92c0 Reviewed-by: Eike Ziller --- src/libs/utils/mathutils.cpp | 19 +++++- src/libs/utils/mathutils.h | 3 +- .../coreplugin/progressmanager/progressmanager.cpp | 13 ++-- .../coreplugin/progressmanager/taskprogress.cpp | 14 ++-- .../diffeditor/sidebysidediffeditorwidget.cpp | 4 +- src/plugins/diffeditor/unifieddiffeditorwidget.cpp | 4 +- tests/auto/utils/CMakeLists.txt | 1 + tests/auto/utils/mathutils/CMakeLists.txt | 4 ++ tests/auto/utils/mathutils/mathutils.qbs | 7 ++ tests/auto/utils/mathutils/tst_mathutils.cpp | 77 ++++++++++++++++++++++ tests/auto/utils/utils.qbs | 1 + 11 files changed, 124 insertions(+), 23 deletions(-) create mode 100644 tests/auto/utils/mathutils/CMakeLists.txt create mode 100644 tests/auto/utils/mathutils/mathutils.qbs create mode 100644 tests/auto/utils/mathutils/tst_mathutils.cpp diff --git a/src/libs/utils/mathutils.cpp b/src/libs/utils/mathutils.cpp index 465d4d5f99..d9e18b18a3 100644 --- a/src/libs/utils/mathutils.cpp +++ b/src/libs/utils/mathutils.cpp @@ -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 diff --git a/src/libs/utils/mathutils.h b/src/libs/utils/mathutils.h index 20ae6cd342..8a9af9e4e6 100644 --- a/src/libs/utils/mathutils.h +++ b/src/libs/utils/mathutils.h @@ -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 diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.cpp b/src/plugins/coreplugin/progressmanager/progressmanager.cpp index 9891a2b3f4..1b4166df5a 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager.cpp +++ b/src/plugins/coreplugin/progressmanager/progressmanager.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -27,7 +28,6 @@ #include #include #include -#include #include 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); } diff --git a/src/plugins/coreplugin/progressmanager/taskprogress.cpp b/src/plugins/coreplugin/progressmanager/taskprogress.cpp index 13ad40b7fa..ba0000a115 100644 --- a/src/plugins/coreplugin/progressmanager/taskprogress.cpp +++ b/src/plugins/coreplugin/progressmanager/taskprogress.cpp @@ -6,12 +6,12 @@ #include "futureprogress.h" #include "progressmanager.h" +#include #include #include #include #include -#include 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); } /*! diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp index 70d9e2ee55..010f656be8 100644 --- a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp +++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp @@ -365,7 +365,7 @@ SideBySideDiffOutput SideDiffData::diffOutput(QFutureInterface &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; } diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp index c082db78ec..6e8abb9c24 100644 --- a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp +++ b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp @@ -436,7 +436,7 @@ UnifiedDiffOutput UnifiedDiffData::diffOutput(QFutureInterface &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; } diff --git a/tests/auto/utils/CMakeLists.txt b/tests/auto/utils/CMakeLists.txt index ee2f8e475a..34532e8f91 100644 --- a/tests/auto/utils/CMakeLists.txt +++ b/tests/auto/utils/CMakeLists.txt @@ -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) diff --git a/tests/auto/utils/mathutils/CMakeLists.txt b/tests/auto/utils/mathutils/CMakeLists.txt new file mode 100644 index 0000000000..d2ec2d5194 --- /dev/null +++ b/tests/auto/utils/mathutils/CMakeLists.txt @@ -0,0 +1,4 @@ +add_qtc_test(tst_utils_mathutils + DEPENDS Utils + SOURCES tst_mathutils.cpp +) diff --git a/tests/auto/utils/mathutils/mathutils.qbs b/tests/auto/utils/mathutils/mathutils.qbs new file mode 100644 index 0000000000..36fa42ffc4 --- /dev/null +++ b/tests/auto/utils/mathutils/mathutils.qbs @@ -0,0 +1,7 @@ +import qbs + +QtcAutotest { + name: "MathUtils autotest" + Depends { name: "Utils" } + files: "tst_mathutils.cpp" +} diff --git a/tests/auto/utils/mathutils/tst_mathutils.cpp b/tests/auto/utils/mathutils/tst_mathutils.cpp new file mode 100644 index 0000000000..30d2d8d6c5 --- /dev/null +++ b/tests/auto/utils/mathutils/tst_mathutils.cpp @@ -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 + +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("x"); + QTest::addColumn("x1"); + QTest::addColumn("x2"); + QTest::addColumn("y1"); + QTest::addColumn("y2"); + QTest::addColumn("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("x"); + QTest::addColumn("xHalfLife"); + QTest::addColumn("y1"); + QTest::addColumn("y2"); + QTest::addColumn("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" diff --git a/tests/auto/utils/utils.qbs b/tests/auto/utils/utils.qbs index 327d35d607..138b1b72ed 100644 --- a/tests/auto/utils/utils.qbs +++ b/tests/auto/utils/utils.qbs @@ -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", -- cgit v1.2.3