DolphinQt: Clean up mapping indicator code and draw lines on "pixel centers".

This commit is contained in:
Jordan Woyak 2020-02-23 20:00:44 -06:00
parent f79ca680cb
commit d80fd13b17
5 changed files with 206 additions and 214 deletions

View file

@ -37,11 +37,32 @@ const QColor TILT_GATE_COLOR = 0xa2d9ce;
const QColor SWING_GATE_COLOR = 0xcea2d9;
constexpr int INPUT_DOT_RADIUS = 2;
constexpr int NORMAL_INDICATOR_WIDTH = 100;
constexpr int NORMAL_INDICATOR_HEIGHT = 100;
constexpr int NORMAL_INDICATOR_PADDING = 2;
// Per trigger.
constexpr int TRIGGER_INDICATOR_HEIGHT = 32;
QPen GetCosmeticPen(QPen pen)
{
pen.setCosmetic(true);
return pen;
}
QPen GetInputDotPen(QPen pen)
{
pen.setWidth(INPUT_DOT_RADIUS * 2);
pen.setCapStyle(Qt::PenCapStyle::RoundCap);
return GetCosmeticPen(pen);
}
} // namespace
QPen MappingIndicator::GetBBoxPen() const
{
return palette().shadow().color();
return QPen(palette().shadow().color(), 0);
}
QBrush MappingIndicator::GetBBoxBrush() const
@ -56,7 +77,7 @@ QColor MappingIndicator::GetRawInputColor() const
QPen MappingIndicator::GetInputShapePen() const
{
return QPen{GetRawInputColor(), 1.0, Qt::DashLine};
return QPen{GetRawInputColor(), 0.0, Qt::DashLine};
}
QColor MappingIndicator::GetAdjustedInputColor() const
@ -80,12 +101,14 @@ QColor MappingIndicator::GetDeadZoneColor() const
QPen MappingIndicator::GetDeadZonePen() const
{
return GetDeadZoneColor();
return QPen(GetDeadZoneColor(), 0);
}
QBrush MappingIndicator::GetDeadZoneBrush() const
QBrush MappingIndicator::GetDeadZoneBrush(QPainter& painter) const
{
return QBrush{GetDeadZoneColor(), Qt::BDiagPattern};
QBrush brush{GetDeadZoneColor(), Qt::FDiagPattern};
brush.setTransform(painter.transform().inverted());
return brush;
}
QColor MappingIndicator::GetTextColor() const
@ -105,22 +128,17 @@ void MappingIndicator::AdjustGateColor(QColor* color)
color->setHsvF(color->hueF(), color->saturationF(), 1 - color->valueF());
}
MappingIndicator::MappingIndicator()
SquareIndicator::SquareIndicator()
{
// TODO: Make these magic numbers less ugly.
const int required_height = 106;
setFixedHeight(required_height);
// Additional pixel for border.
setFixedWidth(NORMAL_INDICATOR_WIDTH + (NORMAL_INDICATOR_PADDING + 1) * 2);
setFixedHeight(NORMAL_INDICATOR_HEIGHT + (NORMAL_INDICATOR_PADDING + 1) * 2);
}
MixedTriggersIndicator::MixedTriggersIndicator(ControllerEmu::MixedTriggers& group) : m_group(group)
{
const int required_height = 64 + 1;
setFixedHeight(required_height);
}
double MappingIndicator::GetScale() const
{
return height() / 2 - 2;
setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Ignored);
setFixedHeight(TRIGGER_INDICATOR_HEIGHT * int(group.GetTriggerCount()) + 1);
}
namespace
@ -131,8 +149,7 @@ constexpr int SPHERE_POINT_COUNT = 200;
// Constructs a polygon by querying a radius at varying angles:
template <typename F>
QPolygonF GetPolygonFromRadiusGetter(F&& radius_getter, double scale,
Common::DVec2 center = {0.0, 0.0})
QPolygonF GetPolygonFromRadiusGetter(F&& radius_getter, Common::DVec2 center = {0.0, 0.0})
{
// A multiple of 8 (octagon) and enough points to be visibly pleasing:
constexpr int shape_point_count = 32;
@ -142,10 +159,9 @@ QPolygonF GetPolygonFromRadiusGetter(F&& radius_getter, double scale,
for (auto& point : shape)
{
const double angle = MathUtil::TAU * p / shape.size();
const double radius = radius_getter(angle) * scale;
const double radius = radius_getter(angle);
point = {std::cos(angle) * radius + center.x * scale,
std::sin(angle) * radius + center.y * scale};
point = {std::cos(angle) * radius + center.x, std::sin(angle) * radius + center.y};
++p;
}
@ -227,38 +243,43 @@ void CursorIndicator::Draw()
std::nullopt);
}
qreal SquareIndicator::GetContentsScale() const
{
return (NORMAL_INDICATOR_WIDTH - 1.0) / 2;
}
void SquareIndicator::DrawBoundingBox(QPainter& p)
{
p.setBrush(GetBBoxBrush());
p.setPen(GetBBoxPen());
p.drawRect(QRectF{NORMAL_INDICATOR_PADDING + 0.5, NORMAL_INDICATOR_PADDING + 0.5,
NORMAL_INDICATOR_WIDTH + 1.0, NORMAL_INDICATOR_HEIGHT + 1.0});
}
void SquareIndicator::TransformPainter(QPainter& p)
{
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
p.translate(width() / 2, height() / 2);
const auto scale = GetContentsScale();
p.scale(scale, scale);
}
void ReshapableInputIndicator::DrawReshapableInput(
ControllerEmu::ReshapableInput& stick, QColor gate_brush_color,
std::optional<ControllerEmu::ReshapableInput::ReshapeData> adj_coord)
{
const auto center = stick.GetCenter();
QColor gate_pen_color = gate_brush_color.darker(125);
AdjustGateColor(&gate_brush_color);
AdjustGateColor(&gate_pen_color);
const auto raw_coord = stick.GetReshapableState(false);
UpdateCalibrationWidget(raw_coord);
// Bounding box size:
const double scale = GetScale();
QPainter p(this);
p.translate(width() / 2, height() / 2);
// Bounding box.
p.setBrush(GetBBoxBrush());
p.setPen(GetBBoxPen());
p.drawRect(-scale - 1, -scale - 1, scale * 2 + 1, scale * 2 + 1);
DrawBoundingBox(p);
TransformPainter(p);
// UI y-axis is opposite that of stick.
p.scale(1.0, -1.0);
// Enable AA after drawing bounding box.
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
const auto raw_coord = stick.GetReshapableState(false);
UpdateCalibrationWidget(raw_coord);
if (IsCalibrating())
{
@ -266,43 +287,47 @@ void ReshapableInputIndicator::DrawReshapableInput(
return;
}
QColor gate_pen_color = gate_brush_color.darker(125);
AdjustGateColor(&gate_brush_color);
AdjustGateColor(&gate_pen_color);
// Input gate. (i.e. the octagon shape)
p.setPen(gate_pen_color);
p.setPen(QPen(gate_pen_color, 0));
p.setBrush(gate_brush_color);
p.drawPolygon(GetPolygonFromRadiusGetter(
[&stick](double ang) { return stick.GetGateRadiusAtAngle(ang); }, scale));
p.drawPolygon(
GetPolygonFromRadiusGetter([&stick](double ang) { return stick.GetGateRadiusAtAngle(ang); }));
const auto center = stick.GetCenter();
// Deadzone.
p.setPen(GetDeadZonePen());
p.setBrush(GetDeadZoneBrush());
p.setBrush(GetDeadZoneBrush(p));
p.drawPolygon(GetPolygonFromRadiusGetter(
[&stick](double ang) { return stick.GetDeadzoneRadiusAtAngle(ang); }, scale, center));
[&stick](double ang) { return stick.GetDeadzoneRadiusAtAngle(ang); }, center));
// Input shape.
p.setPen(GetInputShapePen());
p.setBrush(Qt::NoBrush);
p.drawPolygon(GetPolygonFromRadiusGetter(
[&stick](double ang) { return stick.GetInputRadiusAtAngle(ang); }, scale, center));
[&stick](double ang) { return stick.GetInputRadiusAtAngle(ang); }, center));
// Center.
if (center.x || center.y)
{
p.setPen(Qt::NoPen);
p.setBrush(GetCenterColor());
p.drawEllipse(QPointF{center.x, center.y} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetCenterColor()));
p.drawPoint(QPointF{center.x, center.y});
}
// Raw stick position.
p.setPen(Qt::NoPen);
p.setBrush(GetRawInputColor());
p.drawEllipse(QPointF{raw_coord.x, raw_coord.y} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetRawInputColor()));
p.drawPoint(QPointF{raw_coord.x, raw_coord.y});
// Adjusted stick position.
if (adj_coord)
{
p.setPen(Qt::NoPen);
p.setBrush(GetAdjustedInputColor());
p.drawEllipse(QPointF{adj_coord->x, adj_coord->y} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetAdjustedInputColor()));
p.drawPoint(QPointF{adj_coord->x, adj_coord->y});
}
}
@ -350,15 +375,15 @@ void MixedTriggersIndicator::Draw()
triggers.GetState(&button_state, button_masks.data(), adj_analog_state.data(), true);
// Rectangle sizes:
const int trigger_height = 32;
const int trigger_height = TRIGGER_INDICATOR_HEIGHT;
const int trigger_width = width() - 1;
const int trigger_button_width = 32;
const int trigger_button_width = trigger_height;
const int trigger_analog_width = trigger_width - trigger_button_width;
// Bounding box background:
p.setPen(Qt::NoPen);
p.setBrush(GetBBoxBrush());
p.drawRect(0, 0, trigger_width, trigger_height * TRIGGER_COUNT);
p.drawRect(QRectF(0.5, 0.5, trigger_width, trigger_height * TRIGGER_COUNT));
for (int t = 0; t != TRIGGER_COUNT; ++t)
{
@ -368,35 +393,35 @@ void MixedTriggersIndicator::Draw()
auto const analog_name = QString::fromStdString(triggers.controls[TRIGGER_COUNT + t]->ui_name);
auto const button_name = QString::fromStdString(triggers.controls[t]->ui_name);
const QRectF trigger_rect(0, 0, trigger_width, trigger_height);
const QRectF trigger_rect(0.5, 0.5, trigger_width, trigger_height);
const QRectF analog_rect(0, 0, trigger_analog_width, trigger_height);
const QRectF analog_rect(0.5, 0.5, trigger_analog_width, trigger_height);
// Unactivated analog text:
p.setPen(GetTextColor());
p.drawText(analog_rect, Qt::AlignCenter, analog_name);
const QRectF adj_analog_rect(0, 0, adj_analog * trigger_analog_width, trigger_height);
const QRectF adj_analog_rect(0.5, 0.5, adj_analog * trigger_analog_width, trigger_height);
// Trigger analog:
p.setPen(Qt::NoPen);
p.setBrush(GetRawInputColor());
p.drawEllipse(QPoint(raw_analog * trigger_analog_width, trigger_height - INPUT_DOT_RADIUS),
INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setBrush(GetAdjustedInputColor());
p.drawRect(adj_analog_rect);
p.setPen(GetInputDotPen(GetRawInputColor()));
p.drawPoint(QPoint(raw_analog * trigger_analog_width, trigger_height - INPUT_DOT_RADIUS));
// Deadzone:
p.setPen(GetDeadZonePen());
p.setBrush(GetDeadZoneBrush());
p.drawRect(0, 0, trigger_analog_width * deadzone, trigger_height);
p.setBrush(GetDeadZoneBrush(p));
p.drawRect(QRectF(1.5, 1.5, trigger_analog_width * deadzone, trigger_height - 2));
// Threshold setting:
const int threshold_x = trigger_analog_width * threshold;
p.setPen(GetInputShapePen());
p.drawLine(threshold_x, 0, threshold_x, trigger_height);
const QRectF button_rect(trigger_analog_width, 0, trigger_button_width, trigger_height);
const QRectF button_rect(trigger_analog_width + 0.5, 0.5, trigger_button_width, trigger_height);
// Trigger button:
p.setPen(GetBBoxPen());
@ -443,24 +468,13 @@ void SwingIndicator::Draw()
UpdateCalibrationWidget({raw_coord.x, raw_coord.y});
// Bounding box size:
const double scale = GetScale();
QPainter p(this);
p.translate(width() / 2, height() / 2);
// Bounding box.
p.setBrush(GetBBoxBrush());
p.setPen(GetBBoxPen());
p.drawRect(-scale - 1, -scale - 1, scale * 2 + 1, scale * 2 + 1);
DrawBoundingBox(p);
TransformPainter(p);
// UI y-axis is opposite that of stick.
p.scale(1.0, -1.0);
// Enable AA after drawing bounding box.
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
if (IsCalibrating())
{
DrawCalibration(p, {raw_coord.x, raw_coord.y});
@ -472,15 +486,13 @@ void SwingIndicator::Draw()
if (deadzone > 0.0)
{
p.setPen(GetDeadZonePen());
p.setBrush(GetDeadZoneBrush());
p.drawRect(QRectF(-scale, -deadzone * scale, scale * 2, deadzone * scale * 2));
p.setBrush(GetDeadZoneBrush(p));
p.drawRect(QRectF(-1, -deadzone, 2, deadzone * 2));
}
// Raw Z:
p.setPen(Qt::NoPen);
p.setBrush(GetRawInputColor());
p.drawRect(
QRectF(-scale, raw_coord.z * scale - INPUT_DOT_RADIUS / 2, scale * 2, INPUT_DOT_RADIUS));
p.setPen(GetCosmeticPen(QPen(GetRawInputColor(), INPUT_DOT_RADIUS)));
p.drawLine(QLineF(-1, raw_coord.z, 1, raw_coord.z));
// Adjusted Z:
const auto curve_point =
@ -489,52 +501,48 @@ void SwingIndicator::Draw()
{
// Show off the angle somewhat with a curved line.
QPainterPath path;
path.moveTo(-scale, (adj_coord.y + curve_point) * -scale);
path.quadTo({0, (adj_coord.y - curve_point) * -scale},
{scale, (adj_coord.y + curve_point) * -scale});
path.moveTo(-1.0, (adj_coord.y + curve_point) * -1);
path.quadTo({0, (adj_coord.y - curve_point) * -1}, {1, (adj_coord.y + curve_point) * -1});
p.setBrush(Qt::NoBrush);
p.setPen(QPen(GetAdjustedInputColor(), INPUT_DOT_RADIUS));
p.setPen(GetCosmeticPen(QPen(GetAdjustedInputColor(), INPUT_DOT_RADIUS)));
p.drawPath(path);
}
// Draw "gate" shape.
p.setPen(gate_pen_color);
p.setPen(QPen(gate_pen_color, 0));
p.setBrush(gate_brush_color);
p.drawPolygon(GetPolygonFromRadiusGetter(
[&force](double ang) { return force.GetGateRadiusAtAngle(ang); }, scale));
p.drawPolygon(
GetPolygonFromRadiusGetter([&force](double ang) { return force.GetGateRadiusAtAngle(ang); }));
// Deadzone.
p.setPen(GetDeadZoneColor());
p.setBrush(GetDeadZoneBrush());
p.setPen(GetDeadZonePen());
p.setBrush(GetDeadZoneBrush(p));
p.drawPolygon(GetPolygonFromRadiusGetter(
[&force](double ang) { return force.GetDeadzoneRadiusAtAngle(ang); }, scale, center));
[&force](double ang) { return force.GetDeadzoneRadiusAtAngle(ang); }, center));
// Input shape.
p.setPen(GetInputShapePen());
p.setBrush(Qt::NoBrush);
p.drawPolygon(GetPolygonFromRadiusGetter(
[&force](double ang) { return force.GetInputRadiusAtAngle(ang); }, scale, center));
[&force](double ang) { return force.GetInputRadiusAtAngle(ang); }, center));
// Center.
if (center.x || center.y)
{
p.setPen(Qt::NoPen);
p.setBrush(GetCenterColor());
p.drawEllipse(QPointF{center.x, center.y} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetCenterColor()));
p.drawPoint(QPointF{center.x, center.y});
}
// Raw stick position.
p.setPen(Qt::NoPen);
p.setBrush(GetRawInputColor());
p.drawEllipse(QPointF{raw_coord.x, raw_coord.y} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetRawInputColor()));
p.drawPoint(QPointF{raw_coord.x, raw_coord.y});
// Adjusted position:
if (adj_coord.x || adj_coord.z)
{
p.setPen(Qt::NoPen);
p.setBrush(GetAdjustedInputColor());
p.drawEllipse(QPointF{-adj_coord.x, adj_coord.z} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetAdjustedInputColor()));
p.drawPoint(QPointF{-adj_coord.x, adj_coord.z});
}
}
@ -551,37 +559,24 @@ void ShakeMappingIndicator::Draw()
if (m_position_samples.size() > HISTORY_COUNT + 1)
m_position_samples.pop_back();
// Bounding box size:
const double scale = GetScale();
QPainter p(this);
p.translate(width() / 2, height() / 2);
// Bounding box.
p.setBrush(GetBBoxBrush());
p.setPen(GetBBoxPen());
p.drawRect(-scale - 1, -scale - 1, scale * 2 + 1, scale * 2 + 1);
DrawBoundingBox(p);
TransformPainter(p);
// UI y-axis is opposite that of acceleration Z.
p.scale(1.0, -1.0);
// Enable AA after drawing bounding box.
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
// Deadzone.
p.setPen(GetDeadZonePen());
p.setBrush(GetDeadZoneBrush());
p.drawRect(-scale, 0, scale * 2, m_shake_group.GetDeadzone() * scale);
p.setBrush(GetDeadZoneBrush(p));
p.drawRect(-1.0, 0, 2, m_shake_group.GetDeadzone());
// Raw input.
const auto raw_coord = m_shake_group.GetState(false);
p.setPen(Qt::NoPen);
p.setBrush(GetRawInputColor());
p.setPen(GetInputDotPen(GetRawInputColor()));
for (std::size_t c = 0; c != raw_coord.data.size(); ++c)
{
p.drawEllipse(QPointF{-0.5 + c * 0.5, raw_coord.data[c]} * scale, INPUT_DOT_RADIUS,
INPUT_DOT_RADIUS);
p.drawPoint(QPointF{-0.5 + c * 0.5, raw_coord.data[c]});
}
// Grid line.
@ -593,8 +588,8 @@ void ShakeMappingIndicator::Draw()
m_grid_line_position = (m_grid_line_position + 1) % HISTORY_COUNT;
}
const double grid_line_x = 1.0 - m_grid_line_position * 2.0 / HISTORY_COUNT;
p.setPen(GetRawInputColor());
p.drawLine(QPointF{grid_line_x, -1.0} * scale, QPointF{grid_line_x, 1.0} * scale);
p.setPen(QPen(GetRawInputColor(), 0));
p.drawLine(QPointF{grid_line_x, -1.0}, QPointF{grid_line_x, 1.0});
// Position history.
const QColor component_colors[] = {Qt::blue, Qt::green, Qt::red};
@ -606,11 +601,11 @@ void ShakeMappingIndicator::Draw()
int i = 0;
for (auto& sample : m_position_samples)
{
polyline.append(QPointF{1.0 - i * 2.0 / HISTORY_COUNT, sample.data[c]} * scale);
polyline.append(QPointF{1.0 - i * 2.0 / HISTORY_COUNT, sample.data[c]});
++i;
}
p.setPen(component_colors[c]);
p.setPen(QPen(component_colors[c], 0));
p.drawPolyline(polyline);
}
}
@ -620,41 +615,30 @@ void AccelerometerMappingIndicator::Draw()
const auto accel_state = m_accel_group.GetState();
const auto state = accel_state.value_or(Common::Vec3{});
// Bounding box size:
const double scale = GetScale();
QPainter p(this);
p.translate(width() / 2, height() / 2);
// Bounding box.
p.setBrush(GetBBoxBrush());
p.setPen(GetBBoxPen());
p.drawRect(-scale - 1, -scale - 1, scale * 2 + 1, scale * 2 + 1);
p.setRenderHint(QPainter::TextAntialiasing, true);
DrawBoundingBox(p);
TransformPainter(p);
// UI axes are opposite that of Wii remote accelerometer.
p.scale(-1.0, -1.0);
// Enable AA after drawing bounding box.
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
const auto rotation = WiimoteEmu::GetMatrixFromAcceleration(state);
// Draw sphere.
p.setPen(Qt::NoPen);
p.setBrush(GetRawInputColor());
p.setPen(GetCosmeticPen(QPen(GetRawInputColor(), 0.5)));
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);
p.drawPoint(QPointF(pt.x, pt.z) * SPHERE_SIZE);
});
// Sphere outline.
p.setPen(GetRawInputColor());
p.setPen(QPen(GetRawInputColor(), 0));
p.setBrush(Qt::NoBrush);
p.drawEllipse(QPointF{}, scale * SPHERE_SIZE, scale * SPHERE_SIZE);
p.drawEllipse(QPointF{}, SPHERE_SIZE, SPHERE_SIZE);
p.setPen(Qt::NoPen);
@ -662,27 +646,32 @@ void AccelerometerMappingIndicator::Draw()
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.setBrush(GetAdjustedInputColor());
p.drawEllipse(QPointF(point.x, point.z) * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetAdjustedInputColor()));
p.drawPoint(QPointF(point.x, point.z));
}
// Blue dot.
const auto point2 = -point;
if (point2.y > 0 || Common::Vec2(point2.x, point2.z).Length() > SPHERE_SIZE)
{
p.setBrush(Qt::blue);
p.drawEllipse(QPointF(point2.x, point2.z) * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetCenterColor()));
p.drawPoint(QPointF(point2.x, point2.z));
}
p.setBrush(Qt::NoBrush);
p.resetTransform();
p.translate(width() / 2, height() / 2);
// 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(GetAdjustedInputColor());
p.drawEllipse(QPointF{0, -SPHERE_INDICATOR_DIST} * GetContentsScale(), 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);
p.setPen(GetCenterColor());
p.drawEllipse(QPointF{0, SPHERE_INDICATOR_DIST} * GetContentsScale(), INPUT_DOT_RADIUS,
INPUT_DOT_RADIUS);
// Only draw g-force text if acceleration data is present.
if (!accel_state.has_value())
@ -690,8 +679,8 @@ void AccelerometerMappingIndicator::Draw()
// G-force text:
p.setPen(GetTextColor());
p.scale(-1.0, -1.0);
p.drawText(QRectF(-2, 0, scale, scale), Qt::AlignBottom | Qt::AlignRight,
p.drawText(QRect(0, 0, NORMAL_INDICATOR_WIDTH / 2 - 2, NORMAL_INDICATOR_HEIGHT / 2 - 1),
Qt::AlignBottom | Qt::AlignRight,
QString::fromStdString(
// i18n: "g" is the symbol for "gravitational force equivalent" (g-force).
fmt::format("{:.2f} g", state.Length() / WiimoteEmu::GRAVITY_ACCELERATION)));
@ -724,20 +713,9 @@ void GyroMappingIndicator::Draw()
// Use an empty rotation matrix if gyroscope data is not present.
const auto rotation = (gyro_state.has_value() ? m_state : Common::Matrix33{});
// Bounding box size:
const double scale = GetScale();
QPainter p(this);
p.translate(width() / 2, height() / 2);
// Bounding box.
p.setBrush(GetBBoxBrush());
p.setPen(GetBBoxPen());
p.drawRect(-scale - 1, -scale - 1, scale * 2 + 1, scale * 2 + 1);
// Enable AA after drawing bounding box.
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
DrawBoundingBox(p);
TransformPainter(p);
// Deadzone.
if (const auto deadzone_value = m_gyro_group.GetDeadzone(); deadzone_value)
@ -746,10 +724,8 @@ void GyroMappingIndicator::Draw()
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);
p.setBrush(GetDeadZoneBrush(p));
p.drawRect(QRectF{-1, DEADZONE_DRAW_BOTTOM, 2, -DEADZONE_DRAW_SIZE});
if (gyro_state.has_value())
{
@ -757,34 +733,33 @@ void GyroMappingIndicator::Draw()
std::max({std::abs(jitter.x), std::abs(jitter.y), std::abs(jitter.z)});
const auto jitter_line_y =
std::min(max_jitter / deadzone_value * DEADZONE_DRAW_SIZE - DEADZONE_DRAW_BOTTOM, 1.0);
p.setPen(QPen(GetRawInputColor(), INPUT_DOT_RADIUS));
p.drawLine(-scale, jitter_line_y * -scale, scale, jitter_line_y * -scale);
p.setPen(GetCosmeticPen(QPen(GetRawInputColor(), INPUT_DOT_RADIUS)));
p.drawLine(-1.0, jitter_line_y * -1.0, 1.0, jitter_line_y * -1.0);
// Sphere background.
p.setPen(Qt::NoPen);
p.setBrush(GetBBoxBrush());
p.drawEllipse(QPointF{}, scale * SPHERE_SIZE, scale * SPHERE_SIZE);
p.drawEllipse(QPointF{}, SPHERE_SIZE, SPHERE_SIZE);
}
}
// Sphere dots.
p.setPen(Qt::NoPen);
p.setBrush(GetRawInputColor());
p.setPen(GetCosmeticPen(QPen(GetRawInputColor(), 0.5)));
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);
p.drawPoint(QPointF(pt.x, pt.z) * SPHERE_SIZE);
});
// Sphere outline.
const auto outline_color = is_stable ?
(m_gyro_group.IsCalibrating() ? Qt::blue : GetRawInputColor()) :
GetAdjustedInputColor();
p.setPen(outline_color);
const auto outline_color =
is_stable ? (m_gyro_group.IsCalibrating() ? GetCenterColor() : GetRawInputColor()) :
GetAdjustedInputColor();
p.setPen(QPen(outline_color, 0));
p.setBrush(Qt::NoBrush);
p.drawEllipse(QPointF{}, scale * SPHERE_SIZE, scale * SPHERE_SIZE);
p.drawEllipse(QPointF{}, SPHERE_SIZE, SPHERE_SIZE);
p.setPen(Qt::NoPen);
@ -792,32 +767,35 @@ void GyroMappingIndicator::Draw()
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.setBrush(GetAdjustedInputColor());
p.drawEllipse(QPointF(point.x, point.z) * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetAdjustedInputColor()));
p.drawPoint(QPointF(point.x, point.z));
}
// 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.setBrush(Qt::blue);
p.drawEllipse(QPointF(point2.x, point2.z) * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetCenterColor()));
p.drawPoint(QPointF(point2.x, point2.z));
}
p.setBrush(Qt::NoBrush);
p.resetTransform();
p.translate(width() / 2, height() / 2);
// 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(GetAdjustedInputColor());
p.drawEllipse(QPointF{0, -SPHERE_INDICATOR_DIST} * GetContentsScale(), INPUT_DOT_RADIUS,
INPUT_DOT_RADIUS);
// Blue dot target.
p.setPen(QPen(Qt::blue, INPUT_DOT_RADIUS / 2));
p.setPen(GetCenterColor());
p.drawEllipse(QPointF{}, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
}
void ReshapableInputIndicator::DrawCalibration(QPainter& p, Common::DVec2 point)
{
// Bounding box size:
const double scale = GetScale();
const auto center = m_calibration_widget->GetCenter();
// Input shape.
@ -825,20 +803,18 @@ void ReshapableInputIndicator::DrawCalibration(QPainter& p, Common::DVec2 point)
p.setBrush(Qt::NoBrush);
p.drawPolygon(GetPolygonFromRadiusGetter(
[this](double angle) { return m_calibration_widget->GetCalibrationRadiusAtAngle(angle); },
scale, center));
center));
// Center.
if (center.x || center.y)
{
p.setPen(Qt::NoPen);
p.setBrush(GetCenterColor());
p.drawEllipse(QPointF{center.x, center.y} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetCenterColor()));
p.drawPoint(QPointF{center.x, center.y});
}
// Stick position.
p.setPen(Qt::NoPen);
p.setBrush(GetAdjustedInputColor());
p.drawEllipse(QPointF{point.x, point.y} * scale, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
p.setPen(GetInputDotPen(GetAdjustedInputColor()));
p.drawPoint(QPointF{point.x, point.y});
}
void ReshapableInputIndicator::UpdateCalibrationWidget(Common::DVec2 point)

View file

@ -30,8 +30,6 @@ class CalibrationWidget;
class MappingIndicator : public QWidget
{
public:
MappingIndicator();
QPen GetBBoxPen() const;
QBrush GetBBoxBrush() const;
QColor GetRawInputColor() const;
@ -40,21 +38,29 @@ public:
QColor GetAdjustedInputColor() const;
QColor GetDeadZoneColor() const;
QPen GetDeadZonePen() const;
QBrush GetDeadZoneBrush() const;
QBrush GetDeadZoneBrush(QPainter&) const;
QColor GetTextColor() const;
QColor GetAltTextColor() const;
void AdjustGateColor(QColor*);
protected:
double GetScale() const;
virtual void Draw() {}
private:
void paintEvent(QPaintEvent*) override;
};
class ReshapableInputIndicator : public MappingIndicator
class SquareIndicator : public MappingIndicator
{
protected:
SquareIndicator();
qreal GetContentsScale() const;
void DrawBoundingBox(QPainter&);
void TransformPainter(QPainter&);
};
class ReshapableInputIndicator : public SquareIndicator
{
public:
void SetCalibrationWidget(CalibrationWidget* widget);
@ -129,7 +135,7 @@ private:
WiimoteEmu::MotionState m_motion_state{};
};
class ShakeMappingIndicator : public MappingIndicator
class ShakeMappingIndicator : public SquareIndicator
{
public:
explicit ShakeMappingIndicator(ControllerEmu::Shake& shake) : m_shake_group(shake) {}
@ -143,7 +149,7 @@ private:
int m_grid_line_position = 0;
};
class AccelerometerMappingIndicator : public MappingIndicator
class AccelerometerMappingIndicator : public SquareIndicator
{
public:
explicit AccelerometerMappingIndicator(ControllerEmu::IMUAccelerometer& accel)
@ -157,7 +163,7 @@ private:
ControllerEmu::IMUAccelerometer& m_accel_group;
};
class GyroMappingIndicator : public MappingIndicator
class GyroMappingIndicator : public SquareIndicator
{
public:
explicit GyroMappingIndicator(ControllerEmu::IMUGyroscope& gyro) : m_gyro_group(gyro) {}

View file

@ -96,7 +96,10 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
if (indicator)
{
form_layout->addRow(indicator);
const auto indicator_layout = new QBoxLayout(QBoxLayout::Direction::Down);
indicator_layout->addWidget(indicator);
indicator_layout->setAlignment(Qt::AlignCenter);
form_layout->addRow(indicator_layout);
connect(this, &MappingWidget::Update, indicator, QOverload<>::of(&MappingIndicator::update));

View file

@ -74,4 +74,9 @@ ControlState MixedTriggers::GetThreshold() const
return m_threshold_setting.GetValue() / 100;
}
size_t MixedTriggers::GetTriggerCount() const
{
return controls.size() / 2;
}
} // namespace ControllerEmu

View file

@ -22,6 +22,8 @@ public:
ControlState GetDeadzone() const;
ControlState GetThreshold() const;
size_t GetTriggerCount() const;
private:
SettingValue<double> m_threshold_setting;
SettingValue<double> m_deadzone_setting;