mirror of
https://github.com/orange-cpp/omath.git
synced 2026-04-18 19:23:26 +00:00
Compare commits
2 Commits
e99ca0bc2b
...
d737aee1c5
| Author | SHA1 | Date | |
|---|---|---|---|
| d737aee1c5 | |||
| ef422f0a86 |
@@ -7,6 +7,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
namespace omath::algorithm
|
namespace omath::algorithm
|
||||||
{
|
{
|
||||||
@@ -47,4 +48,56 @@ namespace omath::algorithm
|
|||||||
return best_target;
|
return best_target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class CameraType, std::ranges::range RangeType, class FilterT>
|
||||||
|
requires std::is_invocable_r_v<bool, std::function<FilterT>,
|
||||||
|
std::ranges::range_reference_t<const RangeType>>
|
||||||
|
[[nodiscard]]
|
||||||
|
auto get_closest_target_by_fov(const RangeType& range, const CameraType& camera,
|
||||||
|
auto get_position,
|
||||||
|
const std::optional<std::function<FilterT>>& filter_func = std::nullopt)
|
||||||
|
{
|
||||||
|
return get_closest_target_by_fov<CameraType, decltype(std::ranges::begin(range)), FilterT>(
|
||||||
|
std::ranges::begin(range), std::ranges::end(range), camera, get_position, filter_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── By world-space distance ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
template<std::input_or_output_iterator IteratorType, class FilterT>
|
||||||
|
requires std::is_invocable_r_v<bool, std::function<FilterT>, std::iter_reference_t<IteratorType>>
|
||||||
|
[[nodiscard]]
|
||||||
|
IteratorType get_closest_target_by_distance(const IteratorType& begin, const IteratorType& end,
|
||||||
|
const Vector3<float>& origin, auto get_position,
|
||||||
|
const std::optional<std::function<FilterT>>& filter_func = std::nullopt)
|
||||||
|
{
|
||||||
|
auto best_target = end;
|
||||||
|
|
||||||
|
for (auto current = begin; current != end; current = std::next(current))
|
||||||
|
{
|
||||||
|
if (filter_func && !filter_func.value()(*current))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (best_target == end)
|
||||||
|
{
|
||||||
|
best_target = current;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (origin.distance_to(get_position(*current)) < origin.distance_to(get_position(*best_target)))
|
||||||
|
best_target = current;
|
||||||
|
}
|
||||||
|
return best_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::ranges::range RangeType, class FilterT>
|
||||||
|
requires std::is_invocable_r_v<bool, std::function<FilterT>,
|
||||||
|
std::ranges::range_reference_t<const RangeType>>
|
||||||
|
[[nodiscard]]
|
||||||
|
auto get_closest_target_by_distance(const RangeType& range, const Vector3<float>& origin,
|
||||||
|
auto get_position,
|
||||||
|
const std::optional<std::function<FilterT>>& filter_func = std::nullopt)
|
||||||
|
{
|
||||||
|
return get_closest_target_by_distance<decltype(std::ranges::begin(range)), FilterT>(
|
||||||
|
std::ranges::begin(range), std::ranges::end(range), origin, get_position, filter_func);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace omath::algorithm
|
} // namespace omath::algorithm
|
||||||
@@ -33,6 +33,12 @@ namespace
|
|||||||
return omath::algorithm::get_closest_target_by_fov<Camera, Iter, FilterSig>(
|
return omath::algorithm::get_closest_target_by_fov<Camera, Iter, FilterSig>(
|
||||||
begin, end, camera, get_pos);
|
begin, end, camera, get_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Iter find_nearest(const Iter begin, const Iter end, const omath::Vector3<float>& origin)
|
||||||
|
{
|
||||||
|
return omath::algorithm::get_closest_target_by_distance<Iter, FilterSig>(
|
||||||
|
begin, end, origin, get_pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(unit_test_targeting, returns_end_for_empty_range)
|
TEST(unit_test_targeting, returns_end_for_empty_range)
|
||||||
@@ -152,3 +158,103 @@ TEST(unit_test_targeting, many_targets_picks_best)
|
|||||||
ASSERT_NE(result, targets.cend());
|
ASSERT_NE(result, targets.cend());
|
||||||
EXPECT_EQ(result, targets.cbegin() + 4);
|
EXPECT_EQ(result, targets.cbegin() + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── get_closest_target_by_distance tests ────────────────────────────────────
|
||||||
|
|
||||||
|
TEST(unit_test_targeting, distance_returns_end_for_empty_range)
|
||||||
|
{
|
||||||
|
Targets targets;
|
||||||
|
|
||||||
|
EXPECT_EQ(find_nearest(targets.cbegin(), targets.cend(), {0, 0, 0}), targets.cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_targeting, distance_single_target)
|
||||||
|
{
|
||||||
|
Targets targets = {{50.f, 0.f, 0.f}};
|
||||||
|
|
||||||
|
EXPECT_EQ(find_nearest(targets.cbegin(), targets.cend(), {0, 0, 0}), targets.cbegin());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_targeting, distance_picks_nearest)
|
||||||
|
{
|
||||||
|
const omath::Vector3<float> origin{0.f, 0.f, 0.f};
|
||||||
|
|
||||||
|
Targets targets = {
|
||||||
|
{100.f, 0.f, 0.f}, // distance = 100
|
||||||
|
{10.f, 0.f, 0.f}, // distance = 10 (closest)
|
||||||
|
{50.f, 0.f, 0.f}, // distance = 50
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto result = find_nearest(targets.cbegin(), targets.cend(), origin);
|
||||||
|
|
||||||
|
ASSERT_NE(result, targets.cend());
|
||||||
|
EXPECT_EQ(result, targets.cbegin() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_targeting, distance_considers_all_axes)
|
||||||
|
{
|
||||||
|
const omath::Vector3<float> origin{0.f, 0.f, 0.f};
|
||||||
|
|
||||||
|
Targets targets = {
|
||||||
|
{30.f, 30.f, 30.f}, // distance = sqrt(2700) ~ 51.96
|
||||||
|
{50.f, 0.f, 0.f}, // distance = 50
|
||||||
|
{0.f, 0.f, 10.f}, // distance = 10 (closest)
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto result = find_nearest(targets.cbegin(), targets.cend(), origin);
|
||||||
|
|
||||||
|
ASSERT_NE(result, targets.cend());
|
||||||
|
EXPECT_EQ(result, targets.cbegin() + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_targeting, distance_from_nonzero_origin)
|
||||||
|
{
|
||||||
|
const omath::Vector3<float> origin{100.f, 100.f, 100.f};
|
||||||
|
|
||||||
|
Targets targets = {
|
||||||
|
{0.f, 0.f, 0.f}, // distance = sqrt(30000) ~ 173
|
||||||
|
{105.f, 100.f, 100.f}, // distance = 5 (closest)
|
||||||
|
{200.f, 200.f, 200.f}, // distance = sqrt(30000) ~ 173
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto result = find_nearest(targets.cbegin(), targets.cend(), origin);
|
||||||
|
|
||||||
|
ASSERT_NE(result, targets.cend());
|
||||||
|
EXPECT_EQ(result, targets.cbegin() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_targeting, distance_equidistant_returns_first)
|
||||||
|
{
|
||||||
|
const omath::Vector3<float> origin{0.f, 0.f, 0.f};
|
||||||
|
|
||||||
|
// Both targets at distance 100, symmetric
|
||||||
|
Targets targets = {
|
||||||
|
{100.f, 0.f, 0.f},
|
||||||
|
{-100.f, 0.f, 0.f},
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto result = find_nearest(targets.cbegin(), targets.cend(), origin);
|
||||||
|
|
||||||
|
ASSERT_NE(result, targets.cend());
|
||||||
|
EXPECT_EQ(result, targets.cbegin());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_targeting, distance_many_targets)
|
||||||
|
{
|
||||||
|
const omath::Vector3<float> origin{0.f, 0.f, 0.f};
|
||||||
|
|
||||||
|
Targets targets = {
|
||||||
|
{500.f, 0.f, 0.f},
|
||||||
|
{200.f, 200.f, 0.f},
|
||||||
|
{100.f, 100.f, 100.f},
|
||||||
|
{50.f, 50.f, 50.f},
|
||||||
|
{1.f, 1.f, 1.f}, // distance = sqrt(3) ~ 1.73 (closest)
|
||||||
|
{10.f, 10.f, 10.f},
|
||||||
|
{80.f, 0.f, 0.f},
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto result = find_nearest(targets.cbegin(), targets.cend(), origin);
|
||||||
|
|
||||||
|
ASSERT_NE(result, targets.cend());
|
||||||
|
EXPECT_EQ(result, targets.cbegin() + 4);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user