From 6a0963908dbb316f00818822065e26777621bdcc Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Thu, 29 May 2025 23:28:16 -0500 Subject: [PATCH] DolphinQt: Make Calibration autocomplete when data is "sensible" and stick is returned to neutral position. --- .../Config/Mapping/MappingIndicator.cpp | 37 ++++++++++++------- .../Config/Mapping/MappingIndicator.h | 12 +++--- .../ControllerInterface/MappingCommon.cpp | 11 ++++++ .../ControllerInterface/MappingCommon.h | 4 ++ 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp index 8e4964e190..4bb3534dde 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp @@ -980,12 +980,17 @@ void CalibrationWidget::DrawInProgressCalibration(QPainter& p, Common::DVec2 poi 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. p.save(); p.rotate(elapsed_seconds * -360.0); - DrawPushedStick( - p, m_indicator, - -QEasingCurve(QEasingCurve::OutCirc).valueForProgress(std::min(elapsed_seconds * 2, 1.0))); + DrawPushedStick(p, m_indicator, -stick_pushed_amount); p.restore(); const auto center = m_calibrator->GetCenter(); @@ -1031,8 +1036,6 @@ void ReshapableInputIndicator::SetCalibrationWidget(CalibrationWidget* widget) m_calibration_widget = widget; } -CalibrationWidget::~CalibrationWidget() = default; - CalibrationWidget::CalibrationWidget(MappingWidget& mapping_widget, ControllerEmu::ReshapableInput& input, ReshapableInputIndicator& indicator) @@ -1132,13 +1135,7 @@ void CalibrationWidget::StartCalibration(std::optional center) // i18n: A button to finalize a game controller calibration process. auto* const finish_action = new QAction(tr("Finish Calibration"), this); - connect(finish_action, &QAction::triggered, this, [this]() { - 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); }); + connect(finish_action, &QAction::triggered, this, &CalibrationWidget::FinishCalibration); DeleteAllActions(); @@ -1147,6 +1144,13 @@ void CalibrationWidget::StartCalibration(std::optional center) 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) { // FYI: The "StateLock" is always held when this is called. @@ -1194,9 +1198,14 @@ void CalibrationWidget::Update(Common::DVec2 point) else if (IsCalibrating()) { 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)) diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h index 50e4df8201..8486656241 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h +++ b/Source/Core/DolphinQt/Config/Mapping/MappingIndicator.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "Core/HW/WiimoteEmu/Dynamics.h" @@ -238,11 +239,9 @@ private: class CalibrationWidget : public QToolButton { - Q_OBJECT public: CalibrationWidget(MappingWidget& mapping_widget, ControllerEmu::ReshapableInput& input, ReshapableInputIndicator& indicator); - ~CalibrationWidget() override; void Update(Common::DVec2 point); @@ -250,9 +249,6 @@ public: bool IsActive() const; -signals: - void CalibrationIsSensible(); - private: void DrawInProgressMapping(QPainter& p); void DrawInProgressCalibration(QPainter& p, Common::DVec2 point); @@ -263,6 +259,8 @@ private: void StartMappingAndCalibration(); void StartCalibration(std::optional center = Common::DVec2{}); + void FinishCalibration(); + void ResetActions(); void DeleteAllActions(); @@ -277,4 +275,8 @@ private: void RestartAnimation(); Clock::time_point m_animation_start_time{}; + + static constexpr auto STOP_SPINNING_DURATION = std::chrono::seconds{2}; + + Clock::time_point m_stop_spinning_time{}; }; diff --git a/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp b/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp index 2852291021..8b8a22e400 100644 --- a/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp +++ b/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -277,6 +278,16 @@ bool CalibrationBuilder::IsCalibrationDataSensible() const 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 { return ControllerEmu::ReshapableInput::GetCalibrationDataRadiusAtAngle(m_calibration_data, angle); diff --git a/Source/Core/InputCommon/ControllerInterface/MappingCommon.h b/Source/Core/InputCommon/ControllerInterface/MappingCommon.h index a72549c458..838afcc2a7 100644 --- a/Source/Core/InputCommon/ControllerInterface/MappingCommon.h +++ b/Source/Core/InputCommon/ControllerInterface/MappingCommon.h @@ -85,6 +85,10 @@ public: // Used to update the UI to encourage the user to click the "Finish" button. 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. // Used to render the calibration in the UI while it's in progress. ControlState GetCalibrationRadiusAtAngle(double angle) const;