Merge pull request #13759 from jordan-woyak/calibrate-autocomplete

DolphinQt: Make Calibration autocomplete when data is "sensible" and stick is returned to neutral position.
This commit is contained in:
JMC47
2025-06-24 18:22:48 -04:00
committed by GitHub
4 changed files with 45 additions and 19 deletions

View File

@ -980,12 +980,17 @@ void CalibrationWidget::DrawInProgressCalibration(QPainter& p, Common::DVec2 poi
const auto elapsed_seconds = GetAnimationElapsedSeconds(); const auto elapsed_seconds = GetAnimationElapsedSeconds();
const auto stop_spinning_amount =
std::max(DT_s{m_stop_spinning_time - Clock::now()} / STOP_SPINNING_DURATION, 0.0);
const auto stick_pushed_amount =
QEasingCurve(QEasingCurve::OutCirc).valueForProgress(std::min(elapsed_seconds * 2, 1.0)) *
stop_spinning_amount;
// Clockwise spinning stick starting from center. // Clockwise spinning stick starting from center.
p.save(); p.save();
p.rotate(elapsed_seconds * -360.0); p.rotate(elapsed_seconds * -360.0);
DrawPushedStick( DrawPushedStick(p, m_indicator, -stick_pushed_amount);
p, m_indicator,
-QEasingCurve(QEasingCurve::OutCirc).valueForProgress(std::min(elapsed_seconds * 2, 1.0)));
p.restore(); p.restore();
const auto center = m_calibrator->GetCenter(); const auto center = m_calibrator->GetCenter();
@ -1031,8 +1036,6 @@ void ReshapableInputIndicator::SetCalibrationWidget(CalibrationWidget* widget)
m_calibration_widget = widget; m_calibration_widget = widget;
} }
CalibrationWidget::~CalibrationWidget() = default;
CalibrationWidget::CalibrationWidget(MappingWidget& mapping_widget, CalibrationWidget::CalibrationWidget(MappingWidget& mapping_widget,
ControllerEmu::ReshapableInput& input, ControllerEmu::ReshapableInput& input,
ReshapableInputIndicator& indicator) ReshapableInputIndicator& indicator)
@ -1132,13 +1135,7 @@ void CalibrationWidget::StartCalibration(std::optional<Common::DVec2> center)
// i18n: A button to finalize a game controller calibration process. // i18n: A button to finalize a game controller calibration process.
auto* const finish_action = new QAction(tr("Finish Calibration"), this); auto* const finish_action = new QAction(tr("Finish Calibration"), this);
connect(finish_action, &QAction::triggered, this, [this]() { connect(finish_action, &QAction::triggered, this, &CalibrationWidget::FinishCalibration);
const auto lock = m_mapping_widget.GetController()->GetStateLock();
m_calibrator->ApplyResults(&m_input);
ResetActions();
});
connect(this, &CalibrationWidget::CalibrationIsSensible, finish_action,
[this, finish_action]() { setDefaultAction(finish_action); });
DeleteAllActions(); DeleteAllActions();
@ -1147,6 +1144,13 @@ void CalibrationWidget::StartCalibration(std::optional<Common::DVec2> center)
setDefaultAction(cancel_action); setDefaultAction(cancel_action);
} }
void CalibrationWidget::FinishCalibration()
{
const auto lock = m_mapping_widget.GetController()->GetStateLock();
m_calibrator->ApplyResults(&m_input);
ResetActions();
}
void CalibrationWidget::Update(Common::DVec2 point) void CalibrationWidget::Update(Common::DVec2 point)
{ {
// FYI: The "StateLock" is always held when this is called. // FYI: The "StateLock" is always held when this is called.
@ -1194,9 +1198,14 @@ void CalibrationWidget::Update(Common::DVec2 point)
else if (IsCalibrating()) else if (IsCalibrating())
{ {
m_calibrator->Update(point); m_calibrator->Update(point);
if (m_calibrator->IsCalibrationDataSensible())
if (!m_calibrator->IsCalibrationDataSensible())
{ {
emit CalibrationIsSensible(); m_stop_spinning_time = Clock::now() + STOP_SPINNING_DURATION;
}
else if (m_calibrator->IsComplete())
{
FinishCalibration();
} }
} }
else if (IsPointOutsideCalibration(point, m_input)) else if (IsPointOutsideCalibration(point, m_input))

View File

@ -6,6 +6,7 @@
#include <QToolButton> #include <QToolButton>
#include <QWidget> #include <QWidget>
#include <chrono>
#include <deque> #include <deque>
#include "Core/HW/WiimoteEmu/Dynamics.h" #include "Core/HW/WiimoteEmu/Dynamics.h"
@ -238,11 +239,9 @@ private:
class CalibrationWidget : public QToolButton class CalibrationWidget : public QToolButton
{ {
Q_OBJECT
public: public:
CalibrationWidget(MappingWidget& mapping_widget, ControllerEmu::ReshapableInput& input, CalibrationWidget(MappingWidget& mapping_widget, ControllerEmu::ReshapableInput& input,
ReshapableInputIndicator& indicator); ReshapableInputIndicator& indicator);
~CalibrationWidget() override;
void Update(Common::DVec2 point); void Update(Common::DVec2 point);
@ -250,9 +249,6 @@ public:
bool IsActive() const; bool IsActive() const;
signals:
void CalibrationIsSensible();
private: private:
void DrawInProgressMapping(QPainter& p); void DrawInProgressMapping(QPainter& p);
void DrawInProgressCalibration(QPainter& p, Common::DVec2 point); void DrawInProgressCalibration(QPainter& p, Common::DVec2 point);
@ -263,6 +259,8 @@ private:
void StartMappingAndCalibration(); void StartMappingAndCalibration();
void StartCalibration(std::optional<Common::DVec2> center = Common::DVec2{}); void StartCalibration(std::optional<Common::DVec2> center = Common::DVec2{});
void FinishCalibration();
void ResetActions(); void ResetActions();
void DeleteAllActions(); void DeleteAllActions();
@ -277,4 +275,8 @@ private:
void RestartAnimation(); void RestartAnimation();
Clock::time_point m_animation_start_time{}; Clock::time_point m_animation_start_time{};
static constexpr auto STOP_SPINNING_DURATION = std::chrono::seconds{2};
Clock::time_point m_stop_spinning_time{};
}; };

View File

@ -5,6 +5,7 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <cmath>
#include <ranges> #include <ranges>
#include <string> #include <string>
#include <vector> #include <vector>
@ -277,6 +278,16 @@ bool CalibrationBuilder::IsCalibrationDataSensible() const
return stats.StandardDeviation() < REASONABLE_DEVIATION; return stats.StandardDeviation() < REASONABLE_DEVIATION;
} }
bool CalibrationBuilder::IsComplete() const
{
if (!IsCalibrationDataSensible())
return false;
const auto half_calibration =
0.5 * GetCalibrationRadiusAtAngle(std::atan2(m_prev_point.y, m_prev_point.x) + MathUtil::TAU);
return m_prev_point.LengthSquared() < (half_calibration * half_calibration);
}
ControlState CalibrationBuilder::GetCalibrationRadiusAtAngle(double angle) const ControlState CalibrationBuilder::GetCalibrationRadiusAtAngle(double angle) const
{ {
return ControllerEmu::ReshapableInput::GetCalibrationDataRadiusAtAngle(m_calibration_data, angle); return ControllerEmu::ReshapableInput::GetCalibrationDataRadiusAtAngle(m_calibration_data, angle);

View File

@ -85,6 +85,10 @@ public:
// Used to update the UI to encourage the user to click the "Finish" button. // Used to update the UI to encourage the user to click the "Finish" button.
bool IsCalibrationDataSensible() const; bool IsCalibrationDataSensible() const;
// Returns true when the calibration data seems sensible,
// and the input then approaches the center position.
bool IsComplete() const;
// Grabs the calibration value at the provided angle. // Grabs the calibration value at the provided angle.
// Used to render the calibration in the UI while it's in progress. // Used to render the calibration in the UI while it's in progress.
ControlState GetCalibrationRadiusAtAngle(double angle) const; ControlState GetCalibrationRadiusAtAngle(double angle) const;