Closed sashi0034 closed 1 week ago
失礼しました、先程の実装に誤りがあったので修正しました (上の動作確認用のコードは偶然 Clamp が成功していたようでした) 修正確認を兼ねて、GUI で動作を確認できる Main.cpp を作成しました。
# include <Siv3D.hpp> // Siv3D v0.6.15
void Main()
{
double angleRad{};
double minRad{};
double maxRad{Math::HalfPi};
while (System::Update())
{
constexpr double range = Math::TwoPi * 2;
SimpleGUI::Headline(U"angleRad: {:.4f}"_fmt(angleRad), {20, 20});
SimpleGUI::Slider(angleRad, -range, range, {200, 20}, 280);
SimpleGUI::Headline(U"minRad: {:.4f}"_fmt(minRad), {20, 60});
SimpleGUI::Slider(minRad, -range, range, {200, 60}, 280);
SimpleGUI::Headline(U"maxRad: {:.4f}"_fmt(maxRad), {20, 100});
SimpleGUI::Slider(maxRad, -range, range, {200, 100}, 280);
const double clampedRad = Math::ClampAngle(angleRad, minRad, maxRad);
SimpleGUI::Headline(U"ClampAngle(angleRad, minRad, maxRad) -> {:.4f}"_fmt(clampedRad), {20, 140});
Circle(Scene::Center(), 100)
.drawPie(minRad, maxRad - minRad, Palette::Gray)
.drawFrame(1.0, Palette::White);
Line{Scene::Center(), Circular{100, clampedRad}.toVec2() + Scene::Center()}.draw(5.0, Palette::Yellow);
Line{Scene::Center(), Circular{100, angleRad}.toVec2() + Scene::Center()}.draw(2.0, Palette::Red);
Line{Scene::Center(), Circular{100, minRad}.toVec2() + Scene::Center()}.draw(2.0, Palette::Green);
Line{Scene::Center(), Circular{100, maxRad}.toVec2() + Scene::Center()}.draw(2.0, Palette::Blue);
}
}
ありがとうございます。近日中に確認します。
追加の計算コストが発生しますが、
double ClampAngle2(const double angle, const double min, double max) noexcept
{
const auto start = (min + max) * 0.5 - Pi;
const auto floor = Floor((angle - start) / TwoPi) * TwoPi;
return NormalizeAngle(Clamp(angle, min + floor, max + floor), (min + max) * 0.5);
}
のように NormalizeAngle
して、戻り値が必ず min
以上 max
以下になるようにするのはどうでしょう。戻り値に対する驚きが少なくなるような気がします。
一方で失われる情報もあるので、そうしたい場合はユーザが自前で NoarmalizeAngle()
を書くのも選択肢です。
Print << Math::ToDegrees(Math::ClampAngle(-3610_deg, -60_deg, 60_deg)); // -3610
Print << Math::ToDegrees(Math::ClampAngle2(-3610_deg, -60_deg, 60_deg)); // -10
Print << Math::ToDegrees(Math::ClampAngle(-350_deg, -60_deg, 60_deg)); // -350
Print << Math::ToDegrees(Math::ClampAngle2(-350_deg, -60_deg, 60_deg)); // 10
Print << Math::ToDegrees(Math::ClampAngle(10_deg, 340_deg, 380_deg)); // 10
Print << Math::ToDegrees(Math::ClampAngle2(10_deg, 340_deg, 380_deg)); // 370
Print << Math::ToDegrees(Math::ClampAngle(90_deg, 340_deg, 380_deg)); // 20
Print << Math::ToDegrees(Math::ClampAngle2(90_deg, 340_deg, 380_deg)); // 380
↓ クランプ範囲が 2 π 以上のとき ClampAngle2
の結果が直感的でなくなるのは欠点です。
Print << Math::ToDegrees(Math::ClampAngle(10_deg, 380_deg, 3580_deg)); // 10
Print << Math::ToDegrees(Math::ClampAngle2(10_deg, 380_deg, 3580_deg)); // 1810
NormalizeAngle()
は含めないことを決定。Merged. Thank you!
Discord 文脈: https://discord.com/channels/443310697397354506/999983621408567326/1305003589588226211
最小値と最大値の範囲にクランプした角度を返す Math::ClampAngle を実装しました。
検索エンジンで ClampAngle を検索したとき上位に出てくる以下の実装を参考にしています。 https://gist.github.com/johnsoncodehk/2ecb0136304d4badbb92bd0c1dbd8bae
以下の Main.cpp で動作を確認できます。
実行結果