Skip to content

Commit

Permalink
InputCommon: Add "Dead Zone" setting to raw gyro inputs.
Browse files Browse the repository at this point in the history
  • Loading branch information
jordan-woyak committed Feb 17, 2020
1 parent 2d6a72e commit 82a3aa5
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 39 deletions.
88 changes: 53 additions & 35 deletions Source/Core/DolphinQt/Config/Mapping/MappingIndicator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -754,33 +754,34 @@ void AccelerometerMappingIndicator::paintEvent(QPaintEvent*)
p.setBrush(Qt::NoBrush);
p.drawEllipse(QPointF{}, scale * SPHERE_SIZE, scale * SPHERE_SIZE);

// Red dot upright target.
p.setPen(QPen(GetAdjustedInputColor(), INPUT_DOT_RADIUS / 2));
p.drawEllipse(QPointF{0, SPHERE_INDICATOR_DIST} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(Qt::NoPen);

// Red dot.
const auto point = rotation * Common::Vec3{0, 0, SPHERE_INDICATOR_DIST};
if (point.y > 0 || Common::Vec2(point.x, point.z).Length() > SPHERE_SIZE)
{
p.setPen(Qt::NoPen);
p.setBrush(GetAdjustedInputColor());
p.drawEllipse(QPointF(point.x, point.z) * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
}

// Blue dot target.
p.setPen(QPen(Qt::blue, INPUT_DOT_RADIUS / 2));
p.setBrush(Qt::NoBrush);
p.drawEllipse(QPointF{0, -SPHERE_INDICATOR_DIST} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);

// Blue dot.
const auto point2 = -point;
if (point2.y > 0 || Common::Vec2(point2.x, point2.z).Length() > SPHERE_SIZE)
{
p.setPen(Qt::NoPen);
p.setBrush(Qt::blue);
p.drawEllipse(QPointF(point2.x, point2.z) * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
}

p.setBrush(Qt::NoBrush);

// Red dot upright target.
p.setPen(QPen(GetAdjustedInputColor(), INPUT_DOT_RADIUS / 2));
p.drawEllipse(QPointF{0, SPHERE_INDICATOR_DIST} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);

// Blue dot target.
p.setPen(QPen(Qt::blue, INPUT_DOT_RADIUS / 2));
p.drawEllipse(QPointF{0, -SPHERE_INDICATOR_DIST} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);

// Only draw g-force text if acceleration data is present.
if (!accel_state.has_value())
return;
Expand All @@ -802,6 +803,7 @@ GyroMappingIndicator::GyroMappingIndicator(ControllerEmu::IMUGyroscope* group)
void GyroMappingIndicator::paintEvent(QPaintEvent*)
{
const auto gyro_state = m_gyro_group.GetState();
const auto raw_gyro_state = m_gyro_group.GetRawState();
const auto angular_velocity = gyro_state.value_or(Common::Vec3{});

m_state *= Common::Matrix33::FromQuaternion(angular_velocity.x / -INDICATOR_UPDATE_FREQ / 2,
Expand All @@ -810,8 +812,8 @@ void GyroMappingIndicator::paintEvent(QPaintEvent*)

// Reset orientation when stable for a bit:
constexpr u32 STABLE_RESET_STEPS = INDICATOR_UPDATE_FREQ;
// This works well with my DS4 but a potentially noisy device might not behave.
const bool is_stable = angular_velocity.Length() < MathUtil::TAU / 30;
// Consider device stable when data (with deadzone applied) is zero.
const bool is_stable = !angular_velocity.LengthSquared();

if (!is_stable)
m_stable_steps = 0;
Expand Down Expand Up @@ -839,60 +841,76 @@ void GyroMappingIndicator::paintEvent(QPaintEvent*)
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);

// Deadzone.
if (const auto deadzone_value = m_gyro_group.GetDeadzone(); deadzone_value)
{
static constexpr auto DEADZONE_DRAW_SIZE = 1 - SPHERE_SIZE;
static constexpr auto DEADZONE_DRAW_BOTTOM = 1;

p.setPen(GetDeadZonePen());
p.setBrush(GetDeadZoneBrush());
p.scale(-1.0, 1.0);
p.drawRect(-scale, DEADZONE_DRAW_BOTTOM * scale, scale * 2, -scale * DEADZONE_DRAW_SIZE);
p.scale(-1.0, 1.0);

if (gyro_state.has_value())
{
const auto max_velocity = std::max(
{std::abs(raw_gyro_state.x), std::abs(raw_gyro_state.y), std::abs(raw_gyro_state.z)});
const auto max_velocity_line_y =
std::min(max_velocity / deadzone_value * DEADZONE_DRAW_SIZE - DEADZONE_DRAW_BOTTOM, 1.0);
p.setPen(QPen(GetRawInputColor(), INPUT_DOT_RADIUS));
p.drawLine(-scale, max_velocity_line_y * -scale, scale, max_velocity_line_y * -scale);

// Sphere background.
p.setPen(Qt::NoPen);
p.setBrush(GetBBoxBrush());
p.drawEllipse(QPointF{}, scale * SPHERE_SIZE, scale * SPHERE_SIZE);
}
}

// Sphere dots.
p.setPen(Qt::NoPen);
p.setBrush(GetRawInputColor());

GenerateFibonacciSphere(SPHERE_POINT_COUNT, [&, this](const Common::Vec3& point) {
GenerateFibonacciSphere(SPHERE_POINT_COUNT, [&](const Common::Vec3& point) {
const auto pt = rotation * point;

if (pt.y > 0)
p.drawEllipse(QPointF(pt.x, pt.z) * scale * SPHERE_SIZE, 0.5f, 0.5f);
});

// Sphere outline.
p.setPen(GetRawInputColor());
p.setPen(is_stable ? GetRawInputColor() : GetAdjustedInputColor());
p.setBrush(Qt::NoBrush);
p.drawEllipse(QPointF{}, scale * SPHERE_SIZE, scale * SPHERE_SIZE);

// Red dot upright target.
p.setPen(QPen(GetAdjustedInputColor(), INPUT_DOT_RADIUS / 2));
p.drawEllipse(QPointF{0, -SPHERE_INDICATOR_DIST} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(Qt::NoPen);

// Red dot.
const auto point = rotation * Common::Vec3{0, 0, -SPHERE_INDICATOR_DIST};
if (point.y > 0 || Common::Vec2(point.x, point.z).Length() > SPHERE_SIZE)
{
p.setPen(Qt::NoPen);
p.setBrush(GetAdjustedInputColor());
p.drawEllipse(QPointF(point.x, point.z) * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
}

// Blue dot target.
p.setPen(QPen(Qt::blue, INPUT_DOT_RADIUS / 2));
p.setBrush(Qt::NoBrush);
p.drawEllipse(QPointF{}, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);

// Blue dot.
const auto point2 = rotation * Common::Vec3{0, SPHERE_INDICATOR_DIST, 0};
if (point2.y > 0 || Common::Vec2(point2.x, point2.z).Length() > SPHERE_SIZE)
{
p.setPen(Qt::NoPen);
p.setBrush(Qt::blue);
p.drawEllipse(QPointF(point2.x, point2.z) * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
}

// Only draw text if data is present.
if (!gyro_state.has_value())
return;
p.setBrush(Qt::NoBrush);

// Angle of red dot from starting position.
const auto angle = std::acos(point.Normalized().Dot({0, 0, -1}));
// Red dot upright target.
p.setPen(QPen(GetAdjustedInputColor(), INPUT_DOT_RADIUS / 2));
p.drawEllipse(QPointF{0, -SPHERE_INDICATOR_DIST} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);

// Angle text:
p.setPen(GetTextColor());
p.drawText(QRectF(-2, 0, scale, scale), Qt::AlignBottom | Qt::AlignRight,
// i18n: "°" is the symbol for degrees (angular measurement).
QString::fromStdString(fmt::format("{:.2f} °", angle / MathUtil::TAU * 360)));
// Blue dot target.
p.setPen(QPen(Qt::blue, INPUT_DOT_RADIUS / 2));
p.drawEllipse(QPointF{}, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
}

void MappingIndicator::DrawCalibration(QPainter& p, Common::DVec2 point)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <memory>

#include "Common/Common.h"
#include "Common/MathUtil.h"

#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
Expand All @@ -23,18 +24,40 @@ IMUGyroscope::IMUGyroscope(std::string name, std::string ui_name)
AddInput(Translate, _trans("Roll Right"));
AddInput(Translate, _trans("Yaw Left"));
AddInput(Translate, _trans("Yaw Right"));

AddSetting(&m_deadzone_setting,
{_trans("Dead Zone"),
// i18n: "°/s" is the symbol for degrees (angular measurement) divided by seconds.
_trans("°/s"),
// i18n: Refers to the dead-zone setting of gyroscope input.
_trans("Angular velocity to ignore.")},
1, 0, 180);
}

auto IMUGyroscope::GetRawState() const -> StateData
{
return StateData(controls[1]->GetState() - controls[0]->GetState(),
controls[2]->GetState() - controls[3]->GetState(),
controls[4]->GetState() - controls[5]->GetState());
}

std::optional<IMUGyroscope::StateData> IMUGyroscope::GetState() const
{
if (controls[0]->control_ref->BoundCount() == 0)
return std::nullopt;

StateData state;
state.x = (controls[1]->GetState() - controls[0]->GetState());
state.y = (controls[2]->GetState() - controls[3]->GetState());
state.z = (controls[4]->GetState() - controls[5]->GetState());
auto state = GetRawState();

// Apply "deadzone".
for (auto& c : state.data)
c *= std::abs(c) > GetDeadzone();

return state;
}

ControlState IMUGyroscope::GetDeadzone() const
{
return m_deadzone_setting.GetValue() / 360 * MathUtil::TAU;
}

} // namespace ControllerEmu
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "Common/Matrix.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"

namespace ControllerEmu
{
Expand All @@ -19,6 +20,13 @@ class IMUGyroscope : public ControlGroup

IMUGyroscope(std::string name, std::string ui_name);

StateData GetRawState() const;
std::optional<StateData> GetState() const;

// Value is in rad/s.
ControlState GetDeadzone() const;

private:
SettingValue<double> m_deadzone_setting;
};
} // namespace ControllerEmu

0 comments on commit 82a3aa5

Please sign in to comment.