Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Math::ClampAngle 追加 #1271

Merged
merged 4 commits into from
Nov 17, 2024
Merged

Conversation

sashi0034
Copy link
Member

@sashi0034 sashi0034 commented Nov 10, 2024

Discord 文脈: https://discord.com/channels/443310697397354506/999983621408567326/1305003589588226211

最小値と最大値の範囲にクランプした角度を返す Math::ClampAngle を実装しました。

検索エンジンで ClampAngle を検索したとき上位に出てくる以下の実装を参考にしています。
https://gist.github.com/johnsoncodehk/2ecb0136304d4badbb92bd0c1dbd8bae

以下の Main.cpp で動作を確認できます。

# include <Siv3D.hpp> // Siv3D v0.6.15

namespace
{
	void Test_ClampAngle(double angleDeg, double minDeg, double maxDeg)
	{
		const double angleRad = ToRadians(angleDeg);
		const double minRad = ToRadians(minDeg);
		const double maxRad = ToRadians(maxDeg);

		Print << U"ClampAngle({}_deg, {}_deg, {}_deg) = {:.4f} (= {:.4f}_deg)"_fmt(
			angleDeg, minDeg, maxDeg,
			Math::ClampAngle(angleRad, minRad, maxRad),
			ToDegrees(Math::ClampAngle(angleRad, minRad, maxRad)));
	}
}

void Main()
{
	Window::Resize(1280, 720);

	Print << U"360_deg - 15_deg = {:.04f}"_fmt(360_deg - 15_deg);
	Print << U"720_deg - 15_deg = {:.04f}"_fmt(720_deg - 15_deg);

	Print << U"-----------------------------------------------";

	Test_ClampAngle(10, -15, 15);
	Test_ClampAngle(20, -15, 15);

	Print << U"-----------------------------------------------";

	Test_ClampAngle(350, -15, 15);
	Test_ClampAngle(340, -15, 15);
	Test_ClampAngle(700, -15, 15);

	while (System::Update())
	{
	}
}

実行結果

image

@sashi0034
Copy link
Member Author

sashi0034 commented Nov 10, 2024

失礼しました、先程の実装に誤りがあったので修正しました (上の動作確認用のコードは偶然 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);
	}
}

image

@Reputeless
Copy link
Member

ありがとうございます。近日中に確認します。

@Reputeless
Copy link
Member

追加の計算コストが発生しますが、

	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

@Reputeless
Copy link
Member

  • 11/17 実装会で NormalizeAngle() は含めないことを決定。

@Reputeless Reputeless merged commit 762eb79 into Siv3D:v6_develop Nov 17, 2024
2 checks passed
@Reputeless
Copy link
Member

Merged. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants