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 = x1 it returns y1.
|
||||||
For x = x2 it returns y2.
|
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)
|
if (x1 == x2)
|
||||||
return y1; // or the middle point between y1 and y2?
|
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);
|
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
|
} // namespace Utils::Math
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
namespace Utils::MathUtils {
|
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
|
} // namespace Utils::Math
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include <extensionsystem/pluginmanager.h>
|
#include <extensionsystem/pluginmanager.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
|
#include <utils/mathutils.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/stylehelper.h>
|
#include <utils/stylehelper.h>
|
||||||
#include <utils/theme/theme.h>
|
#include <utils/theme/theme.h>
|
||||||
@@ -27,7 +28,6 @@
|
|||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
#include <QStyleOption>
|
#include <QStyleOption>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QtMath>
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
static const char kSettingsGroup[] = "Progress";
|
static const char kSettingsGroup[] = "Progress";
|
||||||
@@ -779,13 +779,8 @@ ProgressTimer::ProgressTimer(const QFutureInterfaceBase &futureInterface,
|
|||||||
void ProgressTimer::handleTimeout()
|
void ProgressTimer::handleTimeout()
|
||||||
{
|
{
|
||||||
++m_currentTime;
|
++m_currentTime;
|
||||||
|
const int halfLife = qRound(1000.0 * m_expectedTime / TimerInterval);
|
||||||
// This maps expectation to atan(1) to Pi/4 ~= 0.78, i.e. snaps
|
const int progress = MathUtils::interpolateTangential(m_currentTime, halfLife, 0, 100);
|
||||||
// from 78% to 100% when expectations are met at the time the
|
m_futureInterface.setProgressValue(progress);
|
||||||
// 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,12 +6,12 @@
|
|||||||
#include "futureprogress.h"
|
#include "futureprogress.h"
|
||||||
#include "progressmanager.h"
|
#include "progressmanager.h"
|
||||||
|
|
||||||
|
#include <utils/mathutils.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/tasktree.h>
|
#include <utils/tasktree.h>
|
||||||
|
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QtMath>
|
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
@@ -80,13 +80,11 @@ void TaskProgressPrivate::advanceProgress(int newValue)
|
|||||||
|
|
||||||
void TaskProgressPrivate::updateProgress()
|
void TaskProgressPrivate::updateProgress()
|
||||||
{
|
{
|
||||||
// This maps expectation to atan(1) to Pi/4 ~= 0.78, i.e. snaps
|
const int halfLife = qRound(1000.0 * m_expectedTime / TimerInterval);
|
||||||
// from 78% to 100% when expectations are met at the time the
|
const int pMin = ProgressResolution * m_currentProgress;
|
||||||
// future finishes. That's not bad for a random choice.
|
const int pMax = ProgressResolution * (m_currentProgress + 1);
|
||||||
const double mapped = atan2(double(m_currentTick) * TimerInterval / 1000.0,
|
const int newValue = MathUtils::interpolateTangential(m_currentTick, halfLife, pMin, pMax);
|
||||||
double(m_expectedTime));
|
m_futureInterface.setProgressValue(newValue);
|
||||||
const double progress = ProgressResolution * 2 * mapped / M_PI;
|
|
||||||
m_futureInterface.setProgressValue(ProgressResolution * m_currentProgress + progress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@@ -365,7 +365,7 @@ SideBySideDiffOutput SideDiffData::diffOutput(QFutureInterface<void> &fi, int pr
|
|||||||
diffText[RightSide].replace('\r', ' ');
|
diffText[RightSide].replace('\r', ' ');
|
||||||
output.side[LeftSide].diffText += diffText[LeftSide];
|
output.side[LeftSide].diffText += diffText[LeftSide];
|
||||||
output.side[RightSide].diffText += diffText[RightSide];
|
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())
|
if (fi.isCanceled())
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -952,7 +952,7 @@ void SideBySideDiffEditorWidget::showDiff()
|
|||||||
const QString package = output.side[side].diffText.mid(currentPos, packageSize);
|
const QString package = output.side[side].diffText.mid(currentPos, packageSize);
|
||||||
cursor.insertText(package);
|
cursor.insertText(package);
|
||||||
currentPos += package.size();
|
currentPos += package.size();
|
||||||
fi.setProgressValue(MathUtils::interpolate(currentPos, 0, diffSize, progressMin, progressMax));
|
fi.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize, progressMin, progressMax));
|
||||||
if (fi.isCanceled())
|
if (fi.isCanceled())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -436,7 +436,7 @@ UnifiedDiffOutput UnifiedDiffData::diffOutput(QFutureInterface<void> &fi, int pr
|
|||||||
output.diffData.m_chunkInfo.setChunkIndex(oldBlock, blockNumber - oldBlock, j);
|
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())
|
if (fi.isCanceled())
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -511,7 +511,7 @@ void UnifiedDiffEditorWidget::showDiff()
|
|||||||
const QString package = output.diffText.mid(currentPos, packageSize);
|
const QString package = output.diffText.mid(currentPos, packageSize);
|
||||||
cursor.insertText(package);
|
cursor.insertText(package);
|
||||||
currentPos += package.size();
|
currentPos += package.size();
|
||||||
fi.setProgressValue(MathUtils::interpolate(currentPos, 0, diffSize, firstPartMax, progressMax));
|
fi.setProgressValue(MathUtils::interpolateLinear(currentPos, 0, diffSize, firstPartMax, progressMax));
|
||||||
if (futureInterface.isCanceled())
|
if (futureInterface.isCanceled())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ add_subdirectory(fileutils)
|
|||||||
add_subdirectory(fsengine)
|
add_subdirectory(fsengine)
|
||||||
add_subdirectory(fuzzymatcher)
|
add_subdirectory(fuzzymatcher)
|
||||||
add_subdirectory(indexedcontainerproxyconstiterator)
|
add_subdirectory(indexedcontainerproxyconstiterator)
|
||||||
|
add_subdirectory(mathutils)
|
||||||
add_subdirectory(multicursor)
|
add_subdirectory(multicursor)
|
||||||
add_subdirectory(persistentsettings)
|
add_subdirectory(persistentsettings)
|
||||||
add_subdirectory(qtcprocess)
|
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",
|
"fsengine/fsengine.qbs",
|
||||||
"fuzzymatcher/fuzzymatcher.qbs",
|
"fuzzymatcher/fuzzymatcher.qbs",
|
||||||
"indexedcontainerproxyconstiterator/indexedcontainerproxyconstiterator.qbs",
|
"indexedcontainerproxyconstiterator/indexedcontainerproxyconstiterator.qbs",
|
||||||
|
"mathutils/mathutils.qbs",
|
||||||
"multicursor/multicursor.qbs",
|
"multicursor/multicursor.qbs",
|
||||||
"persistentsettings/persistentsettings.qbs",
|
"persistentsettings/persistentsettings.qbs",
|
||||||
"qtcprocess/qtcprocess.qbs",
|
"qtcprocess/qtcprocess.qbs",
|
||||||
|
Reference in New Issue
Block a user