ControllerEmu: Improve and simplify UpdateCalibrationData.
This commit is contained in:
parent
a393a18f51
commit
0ca73355a8
|
@ -856,6 +856,7 @@ void CalibrationWidget::SetupActions()
|
||||||
|
|
||||||
void CalibrationWidget::StartCalibration()
|
void CalibrationWidget::StartCalibration()
|
||||||
{
|
{
|
||||||
|
m_prev_point = {};
|
||||||
m_calibration_data.assign(m_input.CALIBRATION_SAMPLE_COUNT, 0.0);
|
m_calibration_data.assign(m_input.CALIBRATION_SAMPLE_COUNT, 0.0);
|
||||||
|
|
||||||
// Cancel calibration.
|
// Cancel calibration.
|
||||||
|
@ -888,7 +889,9 @@ void CalibrationWidget::Update(Common::DVec2 point)
|
||||||
|
|
||||||
if (IsCalibrating())
|
if (IsCalibrating())
|
||||||
{
|
{
|
||||||
m_input.UpdateCalibrationData(m_calibration_data, point - *m_new_center);
|
const auto new_point = point - *m_new_center;
|
||||||
|
m_input.UpdateCalibrationData(m_calibration_data, m_prev_point, new_point);
|
||||||
|
m_prev_point = new_point;
|
||||||
|
|
||||||
if (IsCalibrationDataSensible(m_calibration_data))
|
if (IsCalibrationDataSensible(m_calibration_data))
|
||||||
{
|
{
|
||||||
|
|
|
@ -204,4 +204,5 @@ private:
|
||||||
ControllerEmu::ReshapableInput::CalibrationData m_calibration_data;
|
ControllerEmu::ReshapableInput::CalibrationData m_calibration_data;
|
||||||
QTimer* m_informative_timer;
|
QTimer* m_informative_timer;
|
||||||
std::optional<Common::DVec2> m_new_center;
|
std::optional<Common::DVec2> m_new_center;
|
||||||
|
Common::DVec2 m_prev_point;
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,18 +25,24 @@ constexpr auto CALIBRATION_CONFIG_SCALE = 100;
|
||||||
constexpr auto CENTER_CONFIG_NAME = "Center";
|
constexpr auto CENTER_CONFIG_NAME = "Center";
|
||||||
constexpr auto CENTER_CONFIG_SCALE = 100;
|
constexpr auto CENTER_CONFIG_SCALE = 100;
|
||||||
|
|
||||||
// Calculate distance to intersection of a ray with a line defined by two points.
|
// Calculate distance to intersection of a ray with a line segment defined by two points.
|
||||||
double GetRayLineIntersection(Common::DVec2 ray, Common::DVec2 point1, Common::DVec2 point2)
|
std::optional<double> GetRayLineIntersection(Common::DVec2 ray, Common::DVec2 point1,
|
||||||
|
Common::DVec2 point2)
|
||||||
{
|
{
|
||||||
const auto diff = point2 - point1;
|
const auto diff = point2 - point1;
|
||||||
|
|
||||||
const auto dot = diff.Dot({-ray.y, ray.x});
|
const auto dot = diff.Dot({-ray.y, ray.x});
|
||||||
if (std::abs(dot) < 0.00001)
|
if (std::abs(dot) < 0.00001)
|
||||||
{
|
{
|
||||||
// Handle situation where both points are on top of eachother.
|
// Both points are on top of eachother.
|
||||||
// This could occur if the user configures a single calibration value
|
return std::nullopt;
|
||||||
// or when updating calibration.
|
}
|
||||||
return point1.Length();
|
|
||||||
|
const auto segment_position = point1.Dot({ray.y, -ray.x}) / dot;
|
||||||
|
if (segment_position < -0.00001 || segment_position > 1.00001)
|
||||||
|
{
|
||||||
|
// Ray does not pass through segment.
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff.Cross(-point1) / dot;
|
return diff.Cross(-point1) / dot;
|
||||||
|
@ -147,9 +153,13 @@ ControlState ReshapableInput::GetCalibrationDataRadiusAtAngle(const CalibrationD
|
||||||
const double sample1_angle = sample1_index * MathUtil::TAU / data.size();
|
const double sample1_angle = sample1_index * MathUtil::TAU / data.size();
|
||||||
const double sample2_angle = sample2_index * MathUtil::TAU / data.size();
|
const double sample2_angle = sample2_index * MathUtil::TAU / data.size();
|
||||||
|
|
||||||
return GetRayLineIntersection(GetPointFromAngleAndLength(angle, 1.0),
|
const auto intersection =
|
||||||
|
GetRayLineIntersection(GetPointFromAngleAndLength(angle, 1.0),
|
||||||
GetPointFromAngleAndLength(sample1_angle, data[sample1_index]),
|
GetPointFromAngleAndLength(sample1_angle, data[sample1_index]),
|
||||||
GetPointFromAngleAndLength(sample2_angle, data[sample2_index]));
|
GetPointFromAngleAndLength(sample2_angle, data[sample2_index]));
|
||||||
|
|
||||||
|
// Intersection has no value when points are on top of eachother.
|
||||||
|
return intersection.value_or(data[sample1_index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlState ReshapableInput::GetDefaultInputRadiusAtAngle(double angle) const
|
ControlState ReshapableInput::GetDefaultInputRadiusAtAngle(double angle) const
|
||||||
|
@ -173,46 +183,16 @@ void ReshapableInput::SetCalibrationFromGate(const StickGate& gate)
|
||||||
val = gate.GetRadiusAtAngle(MathUtil::TAU * i++ / m_calibration.size());
|
val = gate.GetRadiusAtAngle(MathUtil::TAU * i++ / m_calibration.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReshapableInput::UpdateCalibrationData(CalibrationData& data, Common::DVec2 point)
|
void ReshapableInput::UpdateCalibrationData(CalibrationData& data, Common::DVec2 point1,
|
||||||
|
Common::DVec2 point2)
|
||||||
{
|
{
|
||||||
const auto angle_scale = MathUtil::TAU / data.size();
|
for (u32 i = 0; i != data.size(); ++i)
|
||||||
|
|
||||||
const u32 calibration_index =
|
|
||||||
std::lround((std::atan2(point.y, point.x) + MathUtil::TAU) / angle_scale) % data.size();
|
|
||||||
const double calibration_angle = calibration_index * angle_scale;
|
|
||||||
auto& calibration_sample = data[calibration_index];
|
|
||||||
|
|
||||||
// Update closest sample from provided x,y.
|
|
||||||
calibration_sample = std::clamp(point.Length(), calibration_sample,
|
|
||||||
SquareStickGate(1).GetRadiusAtAngle(calibration_angle));
|
|
||||||
|
|
||||||
// Here we update all other samples in our calibration vector to maintain
|
|
||||||
// a convex polygon containing our new calibration point.
|
|
||||||
// This is required to properly fill in angles that cannot be gotten.
|
|
||||||
// (e.g. Keyboard input only has 8 possible angles)
|
|
||||||
|
|
||||||
// Note: Loop assumes an even sample count, which should not be a problem.
|
|
||||||
for (auto sample_offset = u32(data.size() / 2 - 1); sample_offset > 1; --sample_offset)
|
|
||||||
{
|
{
|
||||||
const auto update_at_offset = [&](u32 offset1, u32 offset2) {
|
const auto angle = i * MathUtil::TAU / data.size();
|
||||||
const u32 sample1_index = (calibration_index + offset1) % data.size();
|
const auto intersection =
|
||||||
const double sample1_angle = sample1_index * angle_scale;
|
GetRayLineIntersection(GetPointFromAngleAndLength(angle, 1.0), point1, point2);
|
||||||
auto& sample1 = data[sample1_index];
|
|
||||||
|
|
||||||
const u32 sample2_index = (calibration_index + offset2) % data.size();
|
data[i] = std::max(data[i], intersection.value_or(data[i]));
|
||||||
const double sample2_angle = sample2_index * angle_scale;
|
|
||||||
auto& sample2 = data[sample2_index];
|
|
||||||
|
|
||||||
const double intersection =
|
|
||||||
GetRayLineIntersection(GetPointFromAngleAndLength(sample2_angle, 1.0),
|
|
||||||
GetPointFromAngleAndLength(sample1_angle, sample1),
|
|
||||||
GetPointFromAngleAndLength(calibration_angle, calibration_sample));
|
|
||||||
|
|
||||||
sample2 = std::max(sample2, intersection);
|
|
||||||
};
|
|
||||||
|
|
||||||
update_at_offset(sample_offset, sample_offset - 1);
|
|
||||||
update_at_offset(u32(data.size() - sample_offset), u32(data.size() - sample_offset + 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,8 @@ public:
|
||||||
void SetCalibrationToDefault();
|
void SetCalibrationToDefault();
|
||||||
void SetCalibrationFromGate(const StickGate& gate);
|
void SetCalibrationFromGate(const StickGate& gate);
|
||||||
|
|
||||||
static void UpdateCalibrationData(CalibrationData& data, Common::DVec2 point);
|
static void UpdateCalibrationData(CalibrationData& data, Common::DVec2 point1,
|
||||||
|
Common::DVec2 point2);
|
||||||
static ControlState GetCalibrationDataRadiusAtAngle(const CalibrationData& data, double angle);
|
static ControlState GetCalibrationDataRadiusAtAngle(const CalibrationData& data, double angle);
|
||||||
|
|
||||||
const CalibrationData& GetCalibrationData() const;
|
const CalibrationData& GetCalibrationData() const;
|
||||||
|
|
Loading…
Reference in New Issue