-
Notifications
You must be signed in to change notification settings - Fork 41
Add automated unit tests for ray intersection and collision functions #151
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,35 +1,62 @@ | ||
| # Description | ||
|
|
||
| _Please include a summary of the change and which issue is fixed. Please also include relevant | ||
| motivation and context. List any dependencies that are required for this change._ | ||
| Added automated unit tests for ray intersection functions, migrating interactive tests from `test_geometry.cpp` to proper unit tests that can run in CI/CD pipelines. | ||
|
|
||
| **Changes:** | ||
| - Added unit tests for `rectangle_ray_intersection` in `unit_test_geometry.cpp` | ||
| - Added unit tests for `circle_ray_intersection` in `unit_test_geometry.cpp` | ||
| - Added unit tests for `triangle_ray_intersection` in `unit_test_geometry.cpp` | ||
| - Added unit tests for `quad_ray_intersection` in `unit_test_geometry.cpp` | ||
| - Added unit tests for `bitmap_ray_collision` in `unit_test_bitmap.cpp` | ||
| - Added test for detecting closest intersection among multiple shapes | ||
| - Added necessary includes for geometry headers and physics | ||
|
|
||
| **Motivation:** | ||
| The ray intersection functionality previously only had interactive visual tests that required manual inspection. These new automated tests enable continuous integration testing and prevent regressions. | ||
|
|
||
| Fixes # (issue) | ||
|
|
||
| ## Type of change | ||
|
|
||
| _Please delete options that are not relevant._ | ||
|
|
||
| - [ ] Bug fix (non-breaking change which fixes an issue) | ||
| - [ ] New feature (non-breaking change which adds functionality) | ||
| - [ ] Breaking change (fix or feature that would cause existing functionality to not work as | ||
| expected) | ||
| - [x] New feature (non-breaking change which adds functionality) | ||
| - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) | ||
| - [ ] Documentation (update or new) | ||
|
|
||
| ## How Has This Been Tested? | ||
|
|
||
| _Please describe the tests that you ran to verify your changes. Provide instructions so we can | ||
| reproduce. Please also list any relevant details for your test configuration_ | ||
| **Test Details:** | ||
| - All tests are automated using Catch2 framework | ||
| - Tests validate both boolean return values and output parameters (hit points, distances) | ||
| - Edge cases tested: rays pointing away, parallel rays, rays from inside shapes | ||
| - Multiple shape priority testing validates distance-based collision detection | ||
|
|
||
| **To reproduce:** | ||
| ```bash | ||
| # From MSYS2 MinGW64 terminal | ||
| cd projects/cmake | ||
| cmake -G "Unix Makefiles" . | ||
| make | ||
| cd ../../bin | ||
|
|
||
| # Run all unit tests | ||
| ./skunit_tests | ||
|
|
||
| # Run only ray intersection tests | ||
| ./skunit_tests "[ray_intersection]" | ||
| ./skunit_tests "[ray_collision]" | ||
| ``` | ||
|
|
||
| ## Testing Checklist | ||
|
|
||
| - [ ] Tested with sktest | ||
| - [ ] Tested with skunit_tests | ||
| - [ ] Tested with sktest (not applicable - these are unit tests) | ||
| - [x] Tested with skunit_tests (syntax validated, ready for build/test) | ||
|
|
||
| ## Checklist | ||
|
|
||
| - [ ] My code follows the style guidelines of this project | ||
| - [ ] I have performed a self-review of my own code | ||
| - [ ] I have commented my code in hard-to-understand areas | ||
| - [x] My code follows the style guidelines of this project | ||
| - [x] I have performed a self-review of my own code | ||
| - [x] I have commented my code in hard-to-understand areas | ||
| - [ ] I have made corresponding changes to the documentation | ||
| - [ ] My changes generate no new warnings | ||
| - [x] My changes generate no new warnings | ||
| - [ ] I have requested a review from ... on the Pull Request |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,10 @@ | |
|
|
||
| #include "types.h" | ||
| #include "point_geometry.h" | ||
| #include "rectangle_geometry.h" | ||
| #include "circle_geometry.h" | ||
| #include "triangle_geometry.h" | ||
| #include "quad_geometry.h" | ||
|
|
||
| using namespace splashkit_lib; | ||
|
|
||
|
|
@@ -984,3 +988,172 @@ TEST_CASE("can perform trigonometric calculations", "[trigonometry]") | |
| REQUIRE(tangent(360.0f) == Catch::Detail::Approx(0.0f).margin(__FLT_EPSILON__)); | ||
| } | ||
| } | ||
|
|
||
| TEST_CASE("can perform rectangle ray intersection", "[geometry][ray_intersection]") | ||
| { | ||
| rectangle r1 = rectangle_from(100.0, 100.0, 100.0, 100.0); | ||
|
|
||
| SECTION("can detect ray intersection with rectangle") | ||
| { | ||
| // Ray from left that intersects | ||
| REQUIRE(rectangle_ray_intersection(point_at(90.0, 110.0), vector_to(1.0, 0.0), r1)); | ||
|
|
||
| // Ray that misses (goes above the rectangle) | ||
| REQUIRE_FALSE(rectangle_ray_intersection(point_at(95.0, 95.0), vector_to(1.0, 0.0), r1)); | ||
|
|
||
| // Ray from top that intersects | ||
| REQUIRE(rectangle_ray_intersection(point_at(150.0, 50.0), vector_to(0.0, 1.0), r1)); | ||
|
|
||
| // Ray pointing away from rectangle | ||
| REQUIRE_FALSE(rectangle_ray_intersection(point_at(50.0, 150.0), vector_to(-1.0, 0.0), r1)); | ||
| } | ||
|
|
||
| SECTION("can get hit point and distance for rectangle ray intersection") | ||
| { | ||
| point_2d hit_point; | ||
| double distance; | ||
|
|
||
| // Ray from left hitting the left edge | ||
| bool intersects = rectangle_ray_intersection(point_at(50.0, 150.0), vector_to(1.0, 0.0), r1, hit_point, distance); | ||
| REQUIRE(intersects); | ||
| REQUIRE(hit_point.x == Catch::Detail::Approx(100.0).margin(EPSILON)); | ||
| REQUIRE(hit_point.y == Catch::Detail::Approx(150.0).margin(EPSILON)); | ||
| REQUIRE(distance == Catch::Detail::Approx(50.0).margin(EPSILON)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional improvement: The |
||
|
|
||
| // Ray from top hitting the top edge | ||
| intersects = rectangle_ray_intersection(point_at(150.0, 50.0), vector_to(0.0, 1.0), r1, hit_point, distance); | ||
| REQUIRE(intersects); | ||
| REQUIRE(hit_point.x == Catch::Detail::Approx(150.0).margin(EPSILON)); | ||
| REQUIRE(hit_point.y == Catch::Detail::Approx(100.0).margin(EPSILON)); | ||
| REQUIRE(distance == Catch::Detail::Approx(50.0).margin(EPSILON)); | ||
|
|
||
| // Ray that doesn't intersect | ||
| intersects = rectangle_ray_intersection(point_at(50.0, 50.0), vector_to(-1.0, -1.0), r1, hit_point, distance); | ||
| REQUIRE_FALSE(intersects); | ||
|
Comment on lines
+1030
to
+1032
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional improvement: This test was already done in the previous test case. Instead, consider covering the case where the ray starts from inside the rectangle (as noted in the function documentation) |
||
| } | ||
| } | ||
|
|
||
| TEST_CASE("can perform circle ray intersection", "[geometry][ray_intersection]") | ||
| { | ||
| circle c1 = circle_at(300.0, 200.0, 60.0); | ||
|
|
||
| SECTION("can detect ray intersection with circle") | ||
| { | ||
| // Ray from left that intersects center | ||
| REQUIRE(circle_ray_intersection(point_at(200.0, 200.0), vector_to(1.0, 0.0), c1)); | ||
|
|
||
| // Ray from top that intersects | ||
| REQUIRE(circle_ray_intersection(point_at(300.0, 100.0), vector_to(0.0, 1.0), c1)); | ||
|
|
||
| // Ray that misses the circle | ||
| REQUIRE_FALSE(circle_ray_intersection(point_at(200.0, 100.0), vector_to(0.0, 1.0), c1)); | ||
|
|
||
| // Ray pointing away from circle | ||
| REQUIRE_FALSE(circle_ray_intersection(point_at(200.0, 200.0), vector_to(-1.0, 0.0), c1)); | ||
| } | ||
|
|
||
| SECTION("can get hit point and distance for circle ray intersection") | ||
| { | ||
| point_2d hit_point; | ||
| double distance; | ||
|
|
||
| // Ray from left hitting circle | ||
| bool intersects = circle_ray_intersection(point_at(200.0, 200.0), vector_to(1.0, 0.0), c1, hit_point, distance); | ||
| REQUIRE(intersects); | ||
| REQUIRE(hit_point.x == Catch::Detail::Approx(240.0).margin(EPSILON)); | ||
| REQUIRE(hit_point.y == Catch::Detail::Approx(200.0).margin(EPSILON)); | ||
| REQUIRE(distance == Catch::Detail::Approx(40.0).margin(EPSILON)); | ||
|
|
||
| // Ray from inside the circle | ||
| intersects = circle_ray_intersection(point_at(300.0, 200.0), vector_to(1.0, 0.0), c1, hit_point, distance); | ||
| REQUIRE(intersects); | ||
| REQUIRE(hit_point.x == Catch::Detail::Approx(300.0).margin(EPSILON)); | ||
| REQUIRE(distance == Catch::Detail::Approx(0.0).margin(EPSILON)); | ||
|
|
||
| // Ray that doesn't intersect | ||
| intersects = circle_ray_intersection(point_at(200.0, 100.0), vector_to(0.0, 1.0), c1, hit_point, distance); | ||
| REQUIRE_FALSE(intersects); | ||
| } | ||
| } | ||
|
|
||
| TEST_CASE("can perform triangle ray intersection", "[geometry][ray_intersection]") | ||
| { | ||
| // Axis-aligned right triangle for simpler calculations | ||
| // (400,400) - bottom-left corner | ||
| // (500,400) - bottom-right corner | ||
| // (400,500) - top-left corner | ||
| triangle t1 = triangle_from(400.0, 400.0, 500.0, 400.0, 400.0, 500.0); | ||
|
|
||
| SECTION("can detect ray intersection with triangle") | ||
| { | ||
| // Ray from left that intersects vertical side (x=400) | ||
| REQUIRE(triangle_ray_intersection(point_at(350.0, 450.0), vector_to(1.0, 0.0), t1)); | ||
|
|
||
| // Ray from bottom that intersects horizontal side (y=400) | ||
| REQUIRE(triangle_ray_intersection(point_at(450.0, 350.0), vector_to(0.0, 1.0), t1)); | ||
|
|
||
| // Ray that misses the triangle | ||
| REQUIRE_FALSE(triangle_ray_intersection(point_at(300.0, 300.0), vector_to(1.0, 1.0), t1)); | ||
|
|
||
| // Ray pointing away from triangle | ||
| REQUIRE_FALSE(triangle_ray_intersection(point_at(350.0, 450.0), vector_to(-1.0, 0.0), t1)); | ||
| } | ||
|
|
||
| SECTION("can get hit point and distance for triangle ray intersection") | ||
| { | ||
| point_2d hit_point; | ||
| double distance; | ||
|
|
||
| // Ray from left hitting vertical edge x=400 | ||
| bool intersects = triangle_ray_intersection(point_at(350.0, 450.0), vector_to(1.0, 0.0), t1, hit_point, distance); | ||
| REQUIRE(intersects); | ||
| REQUIRE(hit_point.x == Catch::Detail::Approx(400.0).margin(EPSILON)); | ||
| REQUIRE(hit_point.y == Catch::Detail::Approx(450.0).margin(EPSILON)); | ||
| REQUIRE(distance == Catch::Detail::Approx(50.0).margin(EPSILON)); | ||
|
|
||
| // Ray that doesn't intersect | ||
| intersects = triangle_ray_intersection(point_at(300.0, 300.0), vector_to(0.0, 1.0), t1, hit_point, distance); | ||
| REQUIRE_FALSE(intersects); | ||
| } | ||
| } | ||
|
|
||
| TEST_CASE("can perform quad ray intersection", "[geometry][ray_intersection]") | ||
| { | ||
| // Axis-aligned rectangular quad | ||
| // (100,300) TL, (200,300) TR, (200,500) BR, (100,500) BL | ||
| quad q1 = quad_from(100.0, 300.0, 200.0, 300.0, 200.0, 500.0, 100.0, 500.0); | ||
|
|
||
| SECTION("can detect ray intersection with quad") | ||
| { | ||
| // Ray from left that intersects | ||
| REQUIRE(quad_ray_intersection(point_at(50.0, 400.0), vector_to(1.0, 0.0), q1)); | ||
|
|
||
| // Ray from top that intersects | ||
| REQUIRE(quad_ray_intersection(point_at(150.0, 200.0), vector_to(0.0, 1.0), q1)); | ||
|
|
||
| // Ray that misses the quad | ||
| REQUIRE_FALSE(quad_ray_intersection(point_at(50.0, 200.0), vector_to(0.0, 1.0), q1)); | ||
|
|
||
| // Ray pointing away from quad | ||
| REQUIRE_FALSE(quad_ray_intersection(point_at(50.0, 400.0), vector_to(-1.0, 0.0), q1)); | ||
| } | ||
|
|
||
| SECTION("can get hit point and distance for quad ray intersection") | ||
| { | ||
| point_2d hit_point; | ||
| double distance; | ||
|
|
||
| // Ray from left hitting quad vertical side (x=100) | ||
| bool intersects = quad_ray_intersection(point_at(50.0, 400.0), vector_to(1.0, 0.0), q1, hit_point, distance); | ||
| REQUIRE(intersects); | ||
| REQUIRE(hit_point.x == Catch::Detail::Approx(100.0).margin(EPSILON)); | ||
| REQUIRE(hit_point.y == Catch::Detail::Approx(400.0).margin(EPSILON)); | ||
| REQUIRE(distance == Catch::Detail::Approx(50.0).margin(EPSILON)); | ||
|
|
||
| // Ray that doesn't intersect | ||
| intersects = quad_ray_intersection(point_at(50.0, 200.0), vector_to(0.0, 1.0), q1, hit_point, distance); | ||
| REQUIRE_FALSE(intersects); | ||
| } | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.