Skip to content

Commit

Permalink
WIP: Add routine to test circle-rectangle collision
Browse files Browse the repository at this point in the history
I'm doing this because Raylib's built-in routine for collision detection seems to be erroneous. (Additionally, it's never performed well. In my 1,000 particle test runs, circle testing has been one of the more consistent bottlenecks despite the O(n^2) vs. O(n) time complexities [n = number of particles]. According to my inexpert reading of the profiler's reports, because of the time the CPU spends in converting between integers and floating-points as part of the implementation detail of the routine, which may be causing certain unknown effects that propagate outward.)

Or, more precisely, disk-rectangle collision (two-dimensional figures), which means that, if one figure is contained in the other, they are said to be colliding (intersecting).

(As opposed to the one-dimensional case where it would be considered not intersecting because no point of intersection is created on the respective curves.)

Remaining:
1. At least one affirmative case.
2. Coverage of every branch.
3. Rotated version of each test.

Later, I will compute the branch coverage for all tests.
  • Loading branch information
axionbuster committed Feb 12, 2024
1 parent adf49c6 commit e462fb4
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 1 deletion.
33 changes: 33 additions & 0 deletions dyn/circle.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,39 @@ template <typename F = float> struct Circle : public std::complex<F> {
: std::complex<F>(center), radius(radius) {}
};

/// @brief Decide whether at least one intersection (point) exists between a
/// circular disk and a rectangle (degenerate cases are unspecified).
template <typename F = float>
constexpr bool disk_rect_intersect(Circle<F> circ, std::complex<F> ll,
std::complex<F> gg) {
ll -= circ, gg -= circ;
auto rad = circ.radius;

// Suppose that the circle and the rectangle intersect, then:

// 1. The circle's bounding square and the rectangle must intersect.
if (ll.real() > rad || ll.imag() > rad || gg.real() < -rad ||
gg.imag() < -rad)
// (They don't. -> No intersection.)
return false;
// 2. Otherwise, if either side of the rectangle cut through a coordinate
// axis then they intersect.
if (std::signbit(ll.real()) != std::signbit(gg.real()) ||
std::signbit(ll.imag()) != std::signbit(gg.imag()))
// (It does. -> Intersection.)
return true;
// 3. Lastly, otherwise, if any of the four corners are in the disk bounded
// by the circle then the circle and the rectangle intersect.
F cc[4] = {std::abs(ll), std::abs(gg),
std::abs(std::complex<F>{ll.real(), gg.imag()}),
std::abs(std::complex<F>{ll.imag(), gg.real()})};
for (auto &&c : cc)
if (c < rad)
return true;
// No other intersecting cases exist.
return false;
}

} // namespace dyn

#endif // GRASS_CIRCLE_H
3 changes: 2 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ include(GoogleTest)

# Now simply link against gtest or gtest_main as needed. Eg
add_executable(units yoshida_test.cpp
newton_test.cpp)
newton_test.cpp
circle_test.cpp)
target_precompile_headers(units INTERFACE "gtest/gtest.h")
target_link_libraries(units gtest_main dyn)
gtest_discover_tests(units)
Binary file added tests/CircleTest0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions tests/circle_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include "gtest/gtest.h"

#include <complex>

#include <circle.h>

/// @brief See `CircleTest0.png` for reference.
class CircleTest0 : public testing::Test {
protected:
const dyn::Circle<float> circle{0, 2.4f};
// Rectangle's less-less corner
const std::complex<float> less_less{-4.0f, -4.0f};
// Rectangle's greater-greater corner
const std::complex<float> greater_greater{-2.0f, -2.0f};
};

TEST_F(CircleTest0, Disjoint0) {
ASSERT_FALSE(dyn::disk_rect_intersect(circle, less_less, greater_greater));
}

0 comments on commit e462fb4

Please sign in to comment.