17#define DOCTEST_CONFIG_IMPLEMENT
22#include "doctest_utils.h"
25using namespace helios;
28 return helios::runDoctestWithValidation(argc, argv);
31namespace CollisionTests {
37 std::vector<uint> uuids;
38 for (
int i = 0; i < count; i++) {
39 float x = i * separation;
41 uuids.push_back(uuid);
50 std::vector<uint> uuids;
51 for (
int i = 0; i < count; i++) {
52 float angle = (2.0f *
M_PI * i) / count;
54 float x = center.x + radius * cos(angle);
55 float y = center.y + radius * sin(angle);
58 uuids.push_back(uuid);
73 std::vector<uint> uuids;
74 float half_gap = gap_width * 0.5f;
75 float half_height = wall_height * 0.5f;
78 float left_wall_end = gap_center.
x - half_gap;
79 if (left_wall_end > -5.0f) {
80 uint uuid1 = context->
addTriangle(
make_vec3(-5.0f, gap_center.
y - half_height, wall_distance),
make_vec3(left_wall_end, gap_center.
y - half_height, wall_distance),
make_vec3(-5.0f, gap_center.
y + half_height, wall_distance));
81 uint uuid2 = context->
addTriangle(
make_vec3(left_wall_end, gap_center.
y - half_height, wall_distance),
make_vec3(left_wall_end, gap_center.
y + half_height, wall_distance),
make_vec3(-5.0f, gap_center.
y + half_height, wall_distance));
82 uuids.push_back(uuid1);
83 uuids.push_back(uuid2);
87 float right_wall_start = gap_center.
x + half_gap;
88 if (right_wall_start < 5.0f) {
89 uint uuid3 = context->
addTriangle(
make_vec3(right_wall_start, gap_center.
y - half_height, wall_distance),
make_vec3(5.0f, gap_center.
y - half_height, wall_distance),
make_vec3(right_wall_start, gap_center.
y + half_height, wall_distance));
90 uint uuid4 = context->
addTriangle(
make_vec3(5.0f, gap_center.
y - half_height, wall_distance),
make_vec3(5.0f, gap_center.
y + half_height, wall_distance),
make_vec3(right_wall_start, gap_center.
y + half_height, wall_distance));
91 uuids.push_back(uuid3);
92 uuids.push_back(uuid4);
105 vec3 direction = gap_center - apex;
121 float dot = std::max(-1.0f, std::min(1.0f, actual * expected));
134 std::vector<uint> uuids;
135 float half_separation = gap_separation * 0.5f;
139 uuids.insert(uuids.end(), left_wall.begin(), left_wall.end());
143 uuids.insert(uuids.end(), right_wall.begin(), right_wall.end());
150DOCTEST_TEST_CASE(
"CollisionDetection Plugin Initialization") {
153 collision.disableMessages();
156 DOCTEST_CHECK_NOTHROW(collision.disableMessages());
157 DOCTEST_CHECK_NOTHROW(collision.enableMessages());
162 collision.enableGPUAcceleration();
163 bool gpu_enabled = collision.isGPUAccelerationEnabled();
167 collision.buildBVH();
170 DOCTEST_INFO(
"GPU acceleration capability test - actual hardware dependent");
172 DOCTEST_WARN(
"GPU acceleration is available and enabled on this system");
174 DOCTEST_WARN(
"GPU acceleration requested but not available - using CPU fallback");
178 DOCTEST_CHECK_NOTHROW(collision.disableGPUAcceleration());
179 DOCTEST_CHECK(collision.isGPUAccelerationEnabled() ==
false);
181 }
catch (std::exception &e) {
183 DOCTEST_WARN((std::string(
"GPU acceleration test failed (expected on non-NVIDIA systems): ") + e.what()).c_str());
186 DOCTEST_CHECK_NOTHROW(collision.disableGPUAcceleration());
187 DOCTEST_CHECK(collision.isGPUAccelerationEnabled() ==
false);
192DOCTEST_TEST_CASE(
"CollisionDetection BVH Construction") {
197 collision.disableMessages();
205 collision.buildBVH();
207 DOCTEST_CHECK(collision.isBVHValid() ==
true);
208 DOCTEST_CHECK(collision.getPrimitiveCount() == 3);
212DOCTEST_TEST_CASE(
"CollisionDetection Basic Collision Detection") {
217 collision.disableMessages();
218 collision.disableGPUAcceleration();
227 collision.buildBVH();
230 std::vector<uint> collisions1 = collision.findCollisions(UUID1);
233 bool found_UUID2 = std::find(collisions1.begin(), collisions1.end(), UUID2) != collisions1.end();
234 bool found_UUID3 = std::find(collisions1.begin(), collisions1.end(), UUID3) != collisions1.end();
236 DOCTEST_CHECK(found_UUID2 ==
true);
237 DOCTEST_CHECK(found_UUID3 ==
false);
241DOCTEST_TEST_CASE(
"CollisionDetection BVH Statistics") {
246 collision.disableMessages();
249 for (
int i = 0; i < 20; i++) {
253 collision.buildBVH();
255 size_t node_count, leaf_count, max_depth;
256 collision.getBVHStatistics(node_count, leaf_count, max_depth);
258 DOCTEST_CHECK(node_count > 0);
259 DOCTEST_CHECK(leaf_count > 0);
261 DOCTEST_CHECK(max_depth >= 0);
265DOCTEST_TEST_CASE(
"CollisionDetection Empty Geometry Handling") {
270 collision.disableMessages();
273 collision.buildBVH();
276 DOCTEST_CHECK(collision.getPrimitiveCount() == 0);
279 std::vector<uint> collisions = collision.findCollisions(std::vector<uint>{});
281 DOCTEST_CHECK(collisions.empty() ==
true);
285DOCTEST_TEST_CASE(
"CollisionDetection Invalid UUID Handling") {
290 collision.disableMessages();
295 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(999999), std::runtime_error);
299 collision.findCollisions(999999);
300 DOCTEST_FAIL(
"Expected exception was not thrown");
301 }
catch (
const std::runtime_error &e) {
302 std::string error_msg = e.what();
303 bool has_relevant_content = error_msg.find(
"UUID") != std::string::npos || error_msg.find(
"invalid") != std::string::npos;
304 DOCTEST_CHECK(has_relevant_content);
309DOCTEST_TEST_CASE(
"CollisionDetection GPU/CPU Mode Switching") {
314 collision.disableMessages();
319 collision.buildBVH();
322 collision.enableGPUAcceleration();
323 std::vector<uint> gpu_results = collision.findCollisions(UUID1);
326 collision.disableGPUAcceleration();
327 std::vector<uint> cpu_results = collision.findCollisions(UUID1);
330 std::sort(gpu_results.begin(), gpu_results.end());
331 std::sort(cpu_results.begin(), cpu_results.end());
333 DOCTEST_CHECK(gpu_results == cpu_results);
337DOCTEST_TEST_CASE(
"CollisionDetection Null Context Error Handling") {
344 DOCTEST_FAIL(
"Expected exception was not thrown");
345 }
catch (
const std::runtime_error &e) {
346 std::string error_msg = e.what();
347 bool has_relevant_content = error_msg.find(
"context") != std::string::npos || error_msg.find(
"Context") != std::string::npos || error_msg.find(
"null") != std::string::npos;
348 DOCTEST_CHECK(has_relevant_content);
353DOCTEST_TEST_CASE(
"CollisionDetection Invalid UUIDs in BuildBVH") {
358 collision.disableMessages();
361 std::vector<uint> invalid_UUIDs = {999999, 888888};
363 DOCTEST_CHECK_THROWS_AS(collision.buildBVH(invalid_UUIDs), std::runtime_error);
367 collision.buildBVH(invalid_UUIDs);
368 DOCTEST_FAIL(
"Expected exception was not thrown");
369 }
catch (
const std::runtime_error &e) {
370 std::string error_msg = e.what();
371 bool has_relevant_content = error_msg.find(
"UUID") != std::string::npos || error_msg.find(
"invalid") != std::string::npos;
372 DOCTEST_CHECK(has_relevant_content);
377DOCTEST_TEST_CASE(
"CollisionDetection Primitive/Object Collision Detection") {
382 collision.disableMessages();
383 collision.disableGPUAcceleration();
392 collision.buildBVH();
395 std::vector<uint> primitive_UUIDs = {UUID1};
396 std::vector<uint> object_IDs = {objID};
398 DOCTEST_CHECK_NOTHROW(collision.findCollisions(primitive_UUIDs, object_IDs));
402DOCTEST_TEST_CASE(
"CollisionDetection Empty Input Handling") {
407 collision.disableMessages();
410 std::vector<uint> empty_primitives;
411 std::vector<uint> collisions1 = collision.findCollisions(empty_primitives);
414 std::vector<uint> empty_objects;
415 std::vector<uint> collisions2 = collision.findCollisions(empty_primitives, empty_objects);
417 DOCTEST_CHECK(collisions1.empty() ==
true);
418 DOCTEST_CHECK(collisions2.empty() ==
true);
422DOCTEST_TEST_CASE(
"CollisionDetection Invalid Object ID Error Handling") {
427 collision.disableMessages();
430 std::vector<uint> empty_primitives;
431 std::vector<uint> invalid_objects = {999999};
433 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(empty_primitives, invalid_objects), std::runtime_error);
437 collision.findCollisions(empty_primitives, invalid_objects);
438 DOCTEST_FAIL(
"Expected exception was not thrown");
439 }
catch (
const std::runtime_error &e) {
440 std::string error_msg = e.what();
441 bool has_relevant_content = error_msg.find(
"object") != std::string::npos || error_msg.find(
"Object") != std::string::npos || error_msg.find(
"999999") != std::string::npos || error_msg.find(
"exist") != std::string::npos ||
442 error_msg.find(
"invalid") != std::string::npos;
443 DOCTEST_CHECK(has_relevant_content);
448DOCTEST_TEST_CASE(
"CollisionDetection Manual BVH Rebuild") {
453 collision.disableMessages();
457 collision.buildBVH();
459 size_t initial_count = collision.getPrimitiveCount();
465 collision.rebuildBVH();
467 size_t final_count = collision.getPrimitiveCount();
469 DOCTEST_CHECK(final_count == 2);
473DOCTEST_TEST_CASE(
"CollisionDetection Message Control") {
480 DOCTEST_CHECK_NOTHROW(collision.disableMessages());
481 DOCTEST_CHECK_NOTHROW(collision.enableMessages());
485DOCTEST_TEST_CASE(
"CollisionDetection Large Geometry Handling") {
490 collision.disableMessages();
491 collision.disableGPUAcceleration();
494 for (
int i = 0; i < 50; i++) {
498 collision.buildBVH();
502 std::vector<uint> collisions = collision.findCollisions(UUID);
505 size_t node_count, leaf_count, max_depth;
506 collision.getBVHStatistics(node_count, leaf_count, max_depth);
508 DOCTEST_CHECK(collision.getPrimitiveCount() == 50);
509 DOCTEST_CHECK(node_count > 0);
511 DOCTEST_CHECK(max_depth >= 0);
515DOCTEST_TEST_CASE(
"CollisionDetection Single Primitive Edge Case") {
520 collision.disableMessages();
521 collision.disableGPUAcceleration();
526 collision.buildBVH();
529 std::vector<uint> collisions = collision.findCollisions(UUID1);
532 size_t node_count, leaf_count, max_depth;
533 collision.getBVHStatistics(node_count, leaf_count, max_depth);
535 DOCTEST_CHECK(collision.getPrimitiveCount() == 1);
536 DOCTEST_CHECK(collision.isBVHValid() ==
true);
540DOCTEST_TEST_CASE(
"CollisionDetection Overlapping AABB Primitives") {
545 collision.disableMessages();
546 collision.disableGPUAcceleration();
553 collision.buildBVH();
556 std::vector<uint> collisions = collision.findCollisions(UUID1);
558 bool found_collision = std::find(collisions.begin(), collisions.end(), UUID2) != collisions.end();
560 DOCTEST_CHECK(found_collision ==
true);
564DOCTEST_TEST_CASE(
"CollisionDetection BVH Validity Persistence") {
569 collision.disableMessages();
572 DOCTEST_CHECK(collision.isBVHValid() ==
false);
576 collision.buildBVH();
579 DOCTEST_CHECK(collision.isBVHValid() ==
true);
583DOCTEST_TEST_CASE(
"CollisionDetection Soft/Hard Detection Integration - BVH Sharing") {
587 collision.disableMessages();
588 collision.disableGPUAcceleration();
596 std::vector<uint> all_geometry = {obstacle, soft_prim1, soft_prim2};
597 collision.buildBVH(all_geometry);
600 DOCTEST_CHECK(collision.isBVHValid() ==
true);
601 size_t initial_node_count, initial_leaf_count, initial_max_depth;
602 collision.getBVHStatistics(initial_node_count, initial_leaf_count, initial_max_depth);
605 std::vector<uint> soft_collisions = collision.findCollisions({soft_prim1, soft_prim2});
606 bool soft_detection_completed =
true;
612 vec3 obstacle_direction;
615 bool hard_hit = collision.findNearestSolidObstacleInCone(test_origin, test_direction, 0.52f, 1.0f, {obstacle}, distance, obstacle_direction);
618 DOCTEST_CHECK(soft_detection_completed ==
true);
619 DOCTEST_CHECK(hard_hit ==
true);
620 DOCTEST_CHECK(distance < 1.0f);
623 size_t final_node_count, final_leaf_count, final_max_depth;
624 collision.getBVHStatistics(final_node_count, final_leaf_count, final_max_depth);
626 DOCTEST_CHECK(initial_node_count == final_node_count);
627 DOCTEST_CHECK(initial_leaf_count == final_leaf_count);
628 DOCTEST_CHECK(collision.isBVHValid() ==
true);
632DOCTEST_TEST_CASE(
"CollisionDetection Soft/Hard Detection Integration - Sequential Calls") {
636 collision.disableMessages();
637 collision.disableGPUAcceleration();
644 std::vector<uint> all_obstacles = {ground, wall};
645 std::vector<uint> plant_parts = {plant_stem};
647 collision.buildBVH(all_obstacles);
650 std::vector<uint> soft_collisions = collision.findCollisions(plant_parts, {}, all_obstacles, {});
659 vec3 obstacle_direction;
660 bool hard_hit = collision.findNearestSolidObstacleInCone(growth_tip, growth_direction, 0.35f, 2.0f,
661 all_obstacles, distance, obstacle_direction);
664 std::vector<uint> soft_collisions_2 = collision.findCollisions(plant_parts, {}, all_obstacles, {});
670 bool hard_hit_2 = collision.findNearestSolidObstacleInCone(growth_tip, growth_direction_2, 0.35f, 0.5f, all_obstacles, distance, obstacle_direction);
673 DOCTEST_CHECK(soft_collisions.size() == soft_collisions_2.size());
674 DOCTEST_CHECK(hard_hit ==
true);
675 DOCTEST_CHECK(collision.isBVHValid() ==
true);
679DOCTEST_TEST_CASE(
"CollisionDetection Soft/Hard Detection Integration - Different Geometry Sets") {
683 collision.disableGPUAcceleration();
695 std::vector<uint> all_geometry = {hard_obstacle_1, hard_obstacle_2, soft_object_1, soft_object_2, shared_object};
696 collision.buildBVH(all_geometry);
703 std::vector<uint> hard_only = {hard_obstacle_1, hard_obstacle_2, shared_object};
705 vec3 obstacle_direction;
707 bool hard_hit = collision.findNearestSolidObstacleInCone(test_origin, test_direction, 0.4f, 2.0f, hard_only, distance, obstacle_direction);
710 std::vector<uint> soft_only = {soft_object_1, soft_object_2, shared_object};
711 std::vector<uint> soft_collisions = collision.findCollisions(soft_only);
717 bool hard_hit_2 = collision.findNearestSolidObstacleInCone(test_origin, test_direction_2, 0.4f, 10.0f,
718 soft_only, distance, obstacle_direction);
721 DOCTEST_CHECK(hard_hit ==
true);
722 DOCTEST_CHECK(hard_hit_2 ==
true);
723 DOCTEST_CHECK(collision.isBVHValid() ==
true);
726 DOCTEST_CHECK(collision.getPrimitiveCount() == all_geometry.size());
730DOCTEST_TEST_CASE(
"CollisionDetection Soft/Hard Detection Integration - BVH Rebuild Behavior") {
734 collision.disableMessages();
735 collision.disableGPUAcceleration();
739 std::vector<uint> initial_geometry = {obstacle1};
741 collision.buildBVH(initial_geometry);
747 vec3 obstacle_direction;
749 bool hit_initial = collision.findNearestSolidObstacleInCone(test_origin, test_direction, 0.3f, 1.0f, initial_geometry, distance, obstacle_direction);
755 std::vector<uint> expanded_geometry = {obstacle1, obstacle2, obstacle3};
758 collision.buildBVH(expanded_geometry);
761 std::vector<uint> soft_collisions = collision.findCollisions(expanded_geometry);
763 bool hit_after_rebuild = collision.findNearestSolidObstacleInCone(test_origin, test_direction, 0.3f, 1.0f, expanded_geometry, distance, obstacle_direction);
769 bool hit_subset = collision.findNearestSolidObstacleInCone(test_origin, test_direction_2, 0.5f, 2.0f, {obstacle2}, distance, obstacle_direction);
772 DOCTEST_CHECK(hit_initial ==
true);
773 DOCTEST_CHECK(hit_after_rebuild ==
true);
774 DOCTEST_CHECK(hit_subset ==
true);
775 DOCTEST_CHECK(collision.isBVHValid() ==
true);
776 DOCTEST_CHECK(collision.getPrimitiveCount() == expanded_geometry.size());
780DOCTEST_TEST_CASE(
"CollisionDetection GPU Acceleration") {
785 collision.disableMessages();
788 for (
int i = 0; i < 5; i++) {
794 collision.disableGPUAcceleration();
795 collision.buildBVH();
797 std::vector<uint> cpu_results = collision.findCollisions(UUID);
799 collision.enableGPUAcceleration();
800 collision.buildBVH();
801 std::vector<uint> gpu_results = collision.findCollisions(UUID);
804 std::sort(cpu_results.begin(), cpu_results.end());
805 std::sort(gpu_results.begin(), gpu_results.end());
808 DOCTEST_INFO(
"GPU/CPU result comparison - GPU may not be available on this system");
809 if (cpu_results.size() == gpu_results.size()) {
810 bool results_match =
true;
811 for (
size_t i = 0; i < cpu_results.size(); i++) {
812 if (cpu_results[i] != gpu_results[i]) {
813 results_match =
false;
818 if (!results_match) {
819 DOCTEST_WARN(
"GPU/CPU results differ - may be expected if no CUDA device");
822 }
catch (std::exception &e) {
824 DOCTEST_WARN((std::string(
"GPU test failed (may be expected): ") + e.what()).c_str());
829DOCTEST_TEST_CASE(
"CollisionDetection GPU/CPU Message Display") {
837 collision.buildBVH();
840 collision.enableMessages();
841 collision.disableGPUAcceleration();
845 std::vector<uint> cpu_results = collision.findCollisions(UUID1);
854 collision.enableGPUAcceleration();
857 std::vector<uint> gpu_results = collision.findCollisions(UUID1);
865 collision.disableMessages();
868 std::vector<uint> silent_results = collision.findCollisions(UUID1);
872 DOCTEST_CHECK(silent_output.find(
"Using GPU acceleration") == std::string::npos);
873 DOCTEST_CHECK(silent_output.find(
"Using CPU traversal") == std::string::npos);
877DOCTEST_TEST_CASE(
"CollisionDetection Automatic BVH Building") {
882 collision.disableMessages();
883 collision.disableGPUAcceleration();
889 DOCTEST_CHECK(collision.isBVHValid() ==
false);
892 std::vector<uint> results = collision.findCollisions(UUID1);
895 DOCTEST_CHECK(collision.isBVHValid() ==
true);
896 DOCTEST_CHECK(collision.getPrimitiveCount() == 1);
902 DOCTEST_CHECK(collision.isBVHValid() ==
false);
905 results = collision.findCollisions(UUID1);
908 DOCTEST_CHECK(collision.getPrimitiveCount() == 2);
909 DOCTEST_CHECK(collision.isBVHValid() ==
true);
912 size_t count_before = collision.getPrimitiveCount();
913 results = collision.findCollisions(UUID1);
914 size_t count_after = collision.getPrimitiveCount();
917 DOCTEST_CHECK(count_before == count_after);
921DOCTEST_TEST_CASE(
"CollisionDetection Restricted Geometry - UUIDs Only") {
926 collision.disableMessages();
927 collision.disableGPUAcceleration();
936 std::vector<uint> all_results = collision.findCollisions(UUID1);
939 bool found_UUID2_all = std::find(all_results.begin(), all_results.end(), UUID2) != all_results.end();
940 bool found_UUID3_all = std::find(all_results.begin(), all_results.end(), UUID3) != all_results.end();
942 DOCTEST_CHECK(found_UUID2_all ==
true);
943 DOCTEST_CHECK(found_UUID3_all ==
true);
946 std::vector<uint> query_UUIDs = {UUID1};
947 std::vector<uint> query_objects = {};
948 std::vector<uint> target_UUIDs = {UUID2};
949 std::vector<uint> target_objects = {};
951 std::vector<uint> restricted_results = collision.findCollisions(query_UUIDs, query_objects, target_UUIDs, target_objects);
954 bool found_UUID2_restricted = std::find(restricted_results.begin(), restricted_results.end(), UUID2) != restricted_results.end();
955 bool found_UUID3_restricted = std::find(restricted_results.begin(), restricted_results.end(), UUID3) != restricted_results.end();
957 DOCTEST_CHECK(found_UUID2_restricted ==
true);
958 DOCTEST_CHECK(found_UUID3_restricted ==
false);
962DOCTEST_TEST_CASE(
"CollisionDetection Restricted Geometry - Object IDs") {
967 collision.disableMessages();
968 collision.disableGPUAcceleration();
978 std::vector<uint> query_UUIDs = {UUID1};
979 std::vector<uint> query_objects = {};
980 std::vector<uint> target_UUIDs = {};
981 std::vector<uint> target_objects = {objID1};
983 DOCTEST_CHECK_NOTHROW(collision.findCollisions(query_UUIDs, query_objects, target_UUIDs, target_objects));
986 std::vector<uint> mixed_target_UUIDs = {UUID1};
987 std::vector<uint> mixed_target_objects = {objID1};
989 DOCTEST_CHECK_NOTHROW(collision.findCollisions(query_UUIDs, query_objects, mixed_target_UUIDs, mixed_target_objects));
993DOCTEST_TEST_CASE(
"CollisionDetection Restricted Geometry - Error Handling") {
998 collision.disableMessages();
1004 std::vector<uint> invalid_query_UUIDs = {999999};
1005 std::vector<uint> query_objects = {};
1006 std::vector<uint> valid_target_UUIDs = {UUID1};
1007 std::vector<uint> target_objects = {};
1009 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(invalid_query_UUIDs, query_objects, valid_target_UUIDs, target_objects), std::runtime_error);
1012 std::vector<uint> valid_query_UUIDs = {UUID1};
1013 std::vector<uint> invalid_target_UUIDs = {999999};
1015 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(valid_query_UUIDs, query_objects, invalid_target_UUIDs, target_objects), std::runtime_error);
1018 std::vector<uint> invalid_query_objects = {999999};
1020 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(valid_query_UUIDs, invalid_query_objects, valid_target_UUIDs, target_objects), std::runtime_error);
1023 std::vector<uint> invalid_target_objects = {999999};
1025 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(valid_query_UUIDs, query_objects, valid_target_UUIDs, invalid_target_objects), std::runtime_error);
1029 collision.findCollisions(invalid_query_UUIDs, query_objects, valid_target_UUIDs, target_objects);
1030 DOCTEST_FAIL(
"Expected exception was not thrown");
1031 }
catch (
const std::runtime_error &e) {
1032 std::string error_msg = e.what();
1033 bool has_relevant_content = error_msg.find(
"UUID") != std::string::npos || error_msg.find(
"invalid") != std::string::npos;
1034 DOCTEST_CHECK(has_relevant_content);
1038DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Basic Functionality") {
1043 collision.disableMessages();
1053 float half_angle =
M_PI / 4.0f;
1065DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Gap Detection") {
1070 collision.disableMessages();
1080 float half_angle =
M_PI / 3.0f;
1097 DOCTEST_CHECK(gap_result.
confidence >= 0.0f);
1098 DOCTEST_CHECK(gap_result.
confidence <= 1.0f);
1099 DOCTEST_CHECK(dense_result.
confidence >= 0.0f);
1100 DOCTEST_CHECK(dense_result.
confidence <= 1.0f);
1101 DOCTEST_CHECK(sparse_result.
confidence >= 0.0f);
1102 DOCTEST_CHECK(sparse_result.
confidence <= 1.0f);
1105 float gap_deviation = acosf(std::max(-1.0f, std::min(1.0f, gap_result.
direction * central_axis)));
1106 float dense_deviation = acosf(std::max(-1.0f, std::min(1.0f, dense_result.
direction * central_axis)));
1108 DOCTEST_CHECK(gap_deviation >= 0.0f);
1109 DOCTEST_CHECK(dense_deviation >= 0.0f);
1112DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Edge Cases") {
1117 collision.disableMessages();
1126 DOCTEST_CHECK(result.
direction * central_axis > 0.9f);
1132 DOCTEST_CHECK(result.
direction * central_axis > 0.9f);
1138 DOCTEST_CHECK(result.
direction * central_axis > 0.9f);
1154DOCTEST_TEST_CASE(
"CollisionDetection Finite Cone Height") {
1159 collision.disableMessages();
1169 float half_angle =
M_PI / 6.0f;
1188DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Accuracy - Single Gap Tests") {
1191 collision.disableMessages();
1195 float half_angle =
M_PI / 4.0f;
1207 DOCTEST_CHECK(angular_error < 0.1f);
1215 collision_reset.disableMessages();
1224 DOCTEST_CHECK(angular_error < 0.15f);
1232 collision_reset.disableMessages();
1240 DOCTEST_CHECK(angular_error < 0.2f);
1245DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Accuracy - Symmetric Twin Gaps") {
1248 collision.disableMessages();
1252 float half_angle =
M_PI / 3.0f;
1261 float deviation_from_center = fabsf(acosf(std::max(-1.0f, std::min(1.0f, result.
direction * central_axis))));
1262 DOCTEST_CHECK(deviation_from_center <
M_PI / 6.0f);
1270 collision_reset.disableMessages();
1282 DOCTEST_CHECK(angular_error < 0.3f);
1287DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Accuracy - Angular Precision") {
1290 collision.disableMessages();
1294 float half_angle =
M_PI / 4.0f;
1299 float expected_angle_from_center;
1300 std::string description;
1303 std::vector<TestCase> test_cases = {
1304 {
make_vec3(0, 0, 0), 0.0f,
"Center gap"},
1305 {
make_vec3(0.5f, 0, 0), 0.245f,
"15 degree gap"},
1306 {
make_vec3(-0.7f, 0, 0), -0.334f,
"Negative 19 degree gap"},
1307 {
make_vec3(0, 0.8f, 0), 0.381f,
"Vertical 22 degree gap"}
1310 for (
const auto &test_case: test_cases) {
1314 collision_fresh.disableMessages();
1322 vec3 gap_center_3d =
make_vec3(test_case.gap_position.x, test_case.gap_position.y, 2.0f);
1327 DOCTEST_CHECK_MESSAGE(angular_error < 0.35f, test_case.description.c_str());
1328 DOCTEST_CHECK_MESSAGE(result.
confidence > 0.1f, test_case.description.c_str());
1332DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Accuracy - Distance-Based Priority") {
1335 collision.disableMessages();
1339 float half_angle =
M_PI / 3.0f;
1356 DOCTEST_CHECK(angular_error_near < 0.5f);
1364 collision_reset.disableMessages();
1378 DOCTEST_CHECK(angular_error_near < 0.4f);
1386 collision_reset.disableMessages();
1408 DOCTEST_CHECK((error_near < 0.3f || error_far < 0.3f));
1412DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Accuracy - Edge Case Geometry") {
1415 collision.disableMessages();
1422 float half_angle =
M_PI / 6.0f;
1435 DOCTEST_CHECK(angular_error < 0.5f);
1442 collision_reset.disableMessages();
1444 float half_angle =
M_PI / 4.0f;
1446 float edge_angle = half_angle * 0.8f;
1447 float gap_x = 2.0f * tan(edge_angle);
1459 DOCTEST_CHECK(angular_error < 0.4f);
1466 collision_reset.disableMessages();
1468 float half_angle =
M_PI / 3.0f;
1486 DOCTEST_CHECK(angular_error < 0.6f);
1493 collision_reset.disableMessages();
1495 float half_angle =
M_PI / 2.5f;
1511 float deviation_from_center = acosf(std::max(-1.0f, std::min(1.0f, result.
direction * central_axis)));
1512 DOCTEST_CHECK(deviation_from_center <= half_angle + 0.01f);
1516DOCTEST_TEST_CASE(
"CollisionDetection Scale Test - 1000 Primitives") {
1521 collision.disableMessages();
1527 DOCTEST_CHECK_NOTHROW(collision.buildBVH());
1529 DOCTEST_CHECK(collision.isBVHValid() ==
true);
1530 DOCTEST_CHECK(collision.getPrimitiveCount() == 1000);
1533DOCTEST_TEST_CASE(
"CollisionDetection Scale Test - 10000 Primitives") {
1538 collision.disableMessages();
1544 DOCTEST_CHECK_NOTHROW(collision.buildBVH());
1546 DOCTEST_CHECK(collision.isBVHValid() ==
true);
1547 DOCTEST_CHECK(collision.getPrimitiveCount() == 10000);
1552DOCTEST_TEST_CASE(
"CollisionDetection CPU vs GPU Consistency - Small Scale") {
1563 cpu_collision.disableMessages();
1564 cpu_collision.disableGPUAcceleration();
1565 cpu_collision.buildBVH();
1569 gpu_collision.disableMessages();
1570 gpu_collision.enableGPUAcceleration();
1571 gpu_collision.buildBVH();
1574 for (
uint uuid: overlapping) {
1575 auto cpu_results = cpu_collision.findCollisions(uuid);
1576 auto gpu_results = gpu_collision.findCollisions(uuid);
1579 std::sort(cpu_results.begin(), cpu_results.end());
1580 std::sort(gpu_results.begin(), gpu_results.end());
1582 DOCTEST_CHECK(cpu_results == gpu_results);
1586DOCTEST_TEST_CASE(
"CollisionDetection CPU vs GPU Consistency - Large Scale") {
1591 collision.disableMessages();
1599 collision.disableGPUAcceleration();
1600 collision.buildBVH();
1601 auto cpu_results = collision.findCollisions(cluster1[0]);
1604 collision.enableGPUAcceleration();
1605 collision.buildBVH();
1606 auto gpu_results = collision.findCollisions(cluster1[0]);
1609 std::sort(cpu_results.begin(), cpu_results.end());
1610 std::sort(gpu_results.begin(), gpu_results.end());
1613 DOCTEST_CHECK(cpu_results == gpu_results);
1618DOCTEST_TEST_CASE(
"CollisionDetection Negative Test - Well Separated Primitives") {
1623 collision.disableMessages();
1630 collision.buildBVH();
1633 collision.disableGPUAcceleration();
1634 auto cpu_collisions = collision.findCollisions(triangle1);
1637 collision.enableGPUAcceleration();
1638 auto gpu_collisions = collision.findCollisions(triangle1);
1641 DOCTEST_CHECK(cpu_collisions.size() == 0);
1642 DOCTEST_CHECK(gpu_collisions.size() == 0);
1645DOCTEST_TEST_CASE(
"CollisionDetection Negative Test - Patch vs Distant Model") {
1650 collision.disableMessages();
1658 collision.buildBVH();
1661 collision.disableGPUAcceleration();
1662 auto cpu_collisions = collision.findCollisions(patch);
1664 collision.enableGPUAcceleration();
1665 auto gpu_collisions = collision.findCollisions(patch);
1668 DOCTEST_CHECK(cpu_collisions.size() == 0);
1669 DOCTEST_CHECK(gpu_collisions.size() == 0);
1670 DOCTEST_CHECK(cpu_collisions == gpu_collisions);
1675DOCTEST_TEST_CASE(
"CollisionDetection Edge Case - Boundary Touching") {
1680 collision.disableMessages();
1686 collision.buildBVH();
1689 collision.disableGPUAcceleration();
1690 auto cpu_results = collision.findCollisions(triangle1);
1692 collision.enableGPUAcceleration();
1693 auto gpu_results = collision.findCollisions(triangle1);
1695 std::sort(cpu_results.begin(), cpu_results.end());
1696 std::sort(gpu_results.begin(), gpu_results.end());
1698 DOCTEST_CHECK(cpu_results == gpu_results);
1701DOCTEST_TEST_CASE(
"CollisionDetection Edge Case - Very Small Overlaps") {
1706 collision.disableMessages();
1712 collision.buildBVH();
1715 collision.disableGPUAcceleration();
1716 auto cpu_results = collision.findCollisions(triangle1);
1718 collision.enableGPUAcceleration();
1719 auto gpu_results = collision.findCollisions(triangle1);
1721 std::sort(cpu_results.begin(), cpu_results.end());
1722 std::sort(gpu_results.begin(), gpu_results.end());
1724 DOCTEST_CHECK(cpu_results == gpu_results);
1729DOCTEST_TEST_CASE(
"CollisionDetection Real Geometry - PLY File Loading") {
1734 collision.disableMessages();
1738 std::vector<uint> complex_model;
1741 for (
int i = 0; i < 1000; i++) {
1742 float scale = 0.1f + (i % 10) * 0.05f;
1743 float angle = (i * 0.1f);
1744 float x = cos(angle) * (i * 0.01f);
1745 float y = sin(angle) * (i * 0.01f);
1746 float z = (i % 100) * 0.001f;
1749 complex_model.push_back(uuid);
1756 collision.buildBVH();
1759 collision.disableGPUAcceleration();
1760 auto cpu_intersecting = collision.findCollisions(patch_intersecting);
1761 auto cpu_non_intersecting = collision.findCollisions(patch_non_intersecting);
1763 collision.enableGPUAcceleration();
1764 auto gpu_intersecting = collision.findCollisions(patch_intersecting);
1765 auto gpu_non_intersecting = collision.findCollisions(patch_non_intersecting);
1768 std::sort(cpu_intersecting.begin(), cpu_intersecting.end());
1769 std::sort(gpu_intersecting.begin(), gpu_intersecting.end());
1770 std::sort(cpu_non_intersecting.begin(), cpu_non_intersecting.end());
1771 std::sort(gpu_non_intersecting.begin(), gpu_non_intersecting.end());
1773 DOCTEST_CHECK(cpu_intersecting == gpu_intersecting);
1774 DOCTEST_CHECK(cpu_non_intersecting == gpu_non_intersecting);
1775 DOCTEST_CHECK(cpu_non_intersecting.size() == 0);
1780DOCTEST_TEST_CASE(
"CollisionDetection Performance - BVH Construction Time") {
1785 collision.disableMessages();
1792 auto start = std::chrono::high_resolution_clock::now();
1793 collision.buildBVH();
1794 auto end = std::chrono::high_resolution_clock::now();
1796 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
1799 DOCTEST_CHECK(duration.count() < 5000);
1800 DOCTEST_CHECK(collision.isBVHValid() ==
true);
1805DOCTEST_TEST_CASE(
"CollisionDetection Memory Stress - Progressive Loading") {
1810 collision.disableMessages();
1813 std::vector<uint> all_uuids;
1815 for (
int batch = 0; batch < 10; batch++) {
1818 all_uuids.insert(all_uuids.end(), batch_uuids.begin(), batch_uuids.end());
1821 DOCTEST_CHECK_NOTHROW(collision.buildBVH());
1822 DOCTEST_CHECK(collision.isBVHValid() ==
true);
1823 DOCTEST_CHECK(collision.getPrimitiveCount() == all_uuids.size());
1829DOCTEST_TEST_CASE(
"CollisionDetection findNearestPrimitiveDistance - Basic Functionality") {
1832 collision.disableMessages();
1842 std::vector<uint> candidate_UUIDs = {triangle1, triangle2, triangle3};
1844 vec3 obstacle_direction;
1846 bool result = collision.findNearestPrimitiveDistance(origin, direction, candidate_UUIDs, distance, obstacle_direction);
1847 DOCTEST_CHECK(result ==
true);
1848 DOCTEST_CHECK(distance >= 4.0f);
1849 DOCTEST_CHECK(distance <= 6.0f);
1853 bool result_miss = collision.findNearestPrimitiveDistance(origin, direction_miss, candidate_UUIDs, distance, obstacle_direction);
1854 DOCTEST_CHECK(result_miss ==
false);
1857 std::vector<uint> subset_UUIDs = {triangle2, triangle3};
1858 bool result_subset = collision.findNearestPrimitiveDistance(origin, direction, subset_UUIDs, distance, obstacle_direction);
1859 DOCTEST_CHECK(result_subset ==
true);
1860 DOCTEST_CHECK(distance >= 9.0f);
1861 DOCTEST_CHECK(distance <= 11.0f);
1864DOCTEST_TEST_CASE(
"CollisionDetection findNearestPrimitiveDistance - Edge Cases") {
1867 collision.disableMessages();
1876 std::vector<uint> empty_UUIDs;
1877 vec3 obstacle_direction_unused;
1878 bool result_empty = collision.findNearestPrimitiveDistance(origin, direction, empty_UUIDs, distance, obstacle_direction_unused);
1879 DOCTEST_CHECK(result_empty ==
false);
1883 std::vector<uint> valid_UUIDs = {triangle1};
1884 bool result_non_norm = collision.findNearestPrimitiveDistance(origin, non_normalized_dir, valid_UUIDs, distance, obstacle_direction_unused);
1885 DOCTEST_CHECK(result_non_norm ==
false);
1888 std::vector<uint> invalid_UUIDs = {999999};
1889 bool result_invalid = collision.findNearestPrimitiveDistance(origin, direction, invalid_UUIDs, distance, obstacle_direction_unused);
1890 DOCTEST_CHECK(result_invalid ==
false);
1893 std::vector<uint> mixed_UUIDs = {triangle1, 999999};
1894 bool result_mixed = collision.findNearestPrimitiveDistance(origin, direction, mixed_UUIDs, distance, obstacle_direction_unused);
1895 DOCTEST_CHECK(result_mixed ==
true);
1896 DOCTEST_CHECK(distance >= 4.0f);
1897 DOCTEST_CHECK(distance <= 6.0f);
1900DOCTEST_TEST_CASE(
"CollisionDetection findNearestPrimitiveDistance - Complex Scenarios") {
1903 collision.disableMessages();
1912 vec3 obstacle_direction_unused;
1914 bool result = collision.findNearestPrimitiveDistance(origin, direction, cluster, distance, obstacle_direction_unused);
1916 DOCTEST_CHECK(result ==
false);
1922 bool result_near = collision.findNearestPrimitiveDistance(origin_near, direction_out, cluster, distance, obstacle_direction_unused);
1923 DOCTEST_CHECK(result_near ==
false);
1928 bool result_perpendicular = collision.findNearestPrimitiveDistance(origin_above, direction_down, cluster, distance, obstacle_direction_unused);
1929 DOCTEST_CHECK(result_perpendicular ==
true);
1930 DOCTEST_CHECK(distance >= 0.9f);
1931 DOCTEST_CHECK(distance <= 1.1f);
1934DOCTEST_TEST_CASE(
"CollisionDetection findNearestPrimitiveDistance - Directional Testing") {
1937 collision.disableMessages();
1945 std::vector<uint> all_triangles = {triangle_x, triangle_y, triangle_z, triangle_neg_x};
1950 struct DirectionTest {
1957 std::vector<DirectionTest> tests = {
1961 {
make_vec3(-1, 0, 0), 4.0f, 6.0f,
true},
1962 {
make_vec3(0.707f, 0.707f, 0), 6.0f, 8.0f,
false},
1965 vec3 obstacle_direction_unused;
1966 for (
const auto &test: tests) {
1967 bool result = collision.findNearestPrimitiveDistance(origin, test.direction, all_triangles, distance, obstacle_direction_unused);
1968 DOCTEST_CHECK(result == test.should_hit);
1969 if (result && test.should_hit) {
1970 DOCTEST_CHECK(distance >= test.expected_min);
1971 DOCTEST_CHECK(distance <= test.expected_max);
1976DOCTEST_TEST_CASE(
"CollisionDetection - findNearestPrimitiveDistance front/back face detection") {
1983 uint horizontal_patch = context.
addPatch(patch_center, patch_size);
1985 std::vector<uint> candidates = {horizontal_patch};
1987 vec3 obstacle_direction;
1993 bool found_below = collision.findNearestPrimitiveDistance(origin_below, direction_up, candidates, distance, obstacle_direction);
1994 DOCTEST_CHECK(found_below ==
true);
1995 DOCTEST_CHECK(distance >= 0.49f);
1996 DOCTEST_CHECK(distance <= 0.51f);
1998 DOCTEST_CHECK(obstacle_direction.
z > 0.9f);
1999 DOCTEST_CHECK(std::abs(obstacle_direction.
x) < 0.1f);
2000 DOCTEST_CHECK(std::abs(obstacle_direction.
y) < 0.1f);
2006 bool found_above = collision.findNearestPrimitiveDistance(origin_above, direction_down, candidates, distance, obstacle_direction);
2007 DOCTEST_CHECK(found_above ==
true);
2008 DOCTEST_CHECK(distance >= 0.49f);
2009 DOCTEST_CHECK(distance <= 0.51f);
2011 DOCTEST_CHECK(obstacle_direction.
z < -0.9f);
2012 DOCTEST_CHECK(std::abs(obstacle_direction.
x) < 0.1f);
2013 DOCTEST_CHECK(std::abs(obstacle_direction.
y) < 0.1f);
2019 bool found_away = collision.findNearestPrimitiveDistance(origin_below2, direction_away, candidates, distance, obstacle_direction);
2020 DOCTEST_CHECK(found_away ==
false);
2025DOCTEST_TEST_CASE(
"CollisionDetection Cone-Based Obstacle Detection - Basic Functionality") {
2031 std::vector<uint> obstacles = {obstacle_uuid};
2032 collision.buildBVH(obstacles);
2037 float half_angle =
deg2rad(30.0f);
2038 float height = 1.0f;
2041 vec3 obstacle_direction;
2043 bool found = collision.findNearestSolidObstacleInCone(apex, axis, half_angle, height, obstacles, distance, obstacle_direction);
2045 DOCTEST_CHECK(found ==
true);
2046 DOCTEST_CHECK(distance >= 0.49f);
2047 DOCTEST_CHECK(distance <= 0.51f);
2048 DOCTEST_CHECK(obstacle_direction.
z > 0.9f);
2052 bool found_far = collision.findNearestSolidObstacleInCone(apex_far, axis, half_angle, height, obstacles, distance, obstacle_direction);
2053 DOCTEST_CHECK(found_far ==
false);
2056 float narrow_angle =
deg2rad(5.0f);
2060 bool found_narrow = collision.findNearestSolidObstacleInCone(apex, axis_offset, narrow_angle, height, obstacles, distance, obstacle_direction);
2061 DOCTEST_CHECK(found_narrow ==
false);
2064DOCTEST_TEST_CASE(
"CollisionDetection Cone-Based vs Legacy Method Comparison") {
2070 std::vector<uint> obstacles = {obstacle_uuid};
2071 collision.buildBVH(obstacles);
2078 float legacy_distance;
2079 vec3 legacy_obstacle_direction;
2080 bool legacy_found = collision.findNearestPrimitiveDistance(origin, direction, obstacles, legacy_distance, legacy_obstacle_direction);
2083 float cone_distance;
2084 vec3 cone_obstacle_direction;
2085 float half_angle =
deg2rad(30.0f);
2086 float height = 2.0f;
2087 bool cone_found = collision.findNearestSolidObstacleInCone(origin, direction, half_angle, height, obstacles, cone_distance, cone_obstacle_direction);
2090 DOCTEST_CHECK(legacy_found ==
true);
2091 DOCTEST_CHECK(cone_found ==
true);
2094 DOCTEST_CHECK(std::abs(cone_distance - 0.8f) < 0.04f);
2095 DOCTEST_CHECK(std::abs(legacy_distance - 0.8f) < 0.04f);
2098 DOCTEST_CHECK(legacy_obstacle_direction.
magnitude() > 0.9f);
2099 DOCTEST_CHECK(cone_obstacle_direction.
magnitude() > 0.9f);
2102DOCTEST_TEST_CASE(
"CollisionDetection Cone-Based Triangle vs Patch Intersection") {
2112 std::vector<uint> triangle_obstacles = {triangle_uuid};
2113 std::vector<uint> patch_obstacles = {patch_uuid};
2115 collision.buildBVH({triangle_uuid, patch_uuid});
2119 float half_angle =
deg2rad(30.0f);
2120 float height = 1.0f;
2122 vec3 obstacle_direction;
2125 bool triangle_found = collision.findNearestSolidObstacleInCone(apex, axis, half_angle, height, triangle_obstacles, distance, obstacle_direction);
2126 DOCTEST_CHECK(triangle_found ==
true);
2127 DOCTEST_CHECK(distance > 0.4f);
2128 DOCTEST_CHECK(distance < 0.6f);
2132 bool patch_found = collision.findNearestSolidObstacleInCone(apex_patch, axis, half_angle, height, patch_obstacles, distance, obstacle_direction);
2133 DOCTEST_CHECK(patch_found ==
true);
2134 DOCTEST_CHECK(distance > 0.4f);
2135 DOCTEST_CHECK(distance < 0.6f);
2138DOCTEST_TEST_CASE(
"CollisionDetection Cone-Based Parameter Validation") {
2143 std::vector<uint> obstacles = {obstacle_uuid};
2144 collision.buildBVH(obstacles);
2149 vec3 obstacle_direction;
2152 bool result1 = collision.findNearestSolidObstacleInCone(apex, axis, -0.1f, 1.0f, obstacles, distance, obstacle_direction);
2153 DOCTEST_CHECK(result1 ==
false);
2155 bool result2 = collision.findNearestSolidObstacleInCone(apex, axis,
M_PI, 1.0f, obstacles, distance, obstacle_direction);
2156 DOCTEST_CHECK(result2 ==
false);
2158 bool result3 = collision.findNearestSolidObstacleInCone(apex, axis,
deg2rad(30.0f), -1.0f, obstacles, distance, obstacle_direction);
2159 DOCTEST_CHECK(result3 ==
false);
2162 std::vector<uint> empty_obstacles;
2163 bool result4 = collision.findNearestSolidObstacleInCone(apex, axis,
deg2rad(30.0f), 1.0f, empty_obstacles, distance, obstacle_direction);
2164 DOCTEST_CHECK(result4 ==
false);
2167 bool result5 = collision.findNearestSolidObstacleInCone(apex, axis,
deg2rad(30.0f), 1.0f, obstacles, distance, obstacle_direction);
2168 DOCTEST_CHECK(result5 ==
true);
2172DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Basic Functionality") {
2177 vec3 grid_center(0, 0, 0);
2178 vec3 grid_size(10, 10, 10);
2179 int3 grid_divisions(2, 2, 2);
2182 std::vector<vec3> ray_origins;
2183 std::vector<vec3> ray_directions;
2186 ray_origins.push_back(
make_vec3(-10, 0, 0));
2187 ray_directions.push_back(
make_vec3(1, 0, 0));
2190 ray_origins.push_back(
make_vec3(-10, -10, -10));
2191 ray_directions.push_back(normalize(
make_vec3(1, 1, 1)));
2194 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2197 int P_denom, P_trans;
2198 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2199 DOCTEST_CHECK(P_denom >= 0);
2200 DOCTEST_CHECK(P_trans >= 0);
2201 DOCTEST_CHECK(P_trans <= P_denom);
2204 float r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2205 DOCTEST_CHECK(r_bar >= 0.0f);
2208 collision.clearVoxelData();
2211DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Edge Cases") {
2216 vec3 grid_center(0, 0, 0);
2217 vec3 grid_size(5, 5, 5);
2218 int3 grid_divisions(1, 1, 1);
2220 std::vector<vec3> empty_origins;
2221 std::vector<vec3> empty_directions;
2224 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, empty_origins, empty_directions);
2227 int P_denom, P_trans;
2232 collision.getVoxelTransmissionProbability(
make_int3(-1, 0, 0), P_denom, P_trans);
2233 }
catch (
const std::exception &e) {
2235 DOCTEST_CHECK(
true);
2239 collision.getVoxelTransmissionProbability(
make_int3(1, 0, 0), P_denom, P_trans);
2240 }
catch (
const std::exception &e) {
2242 DOCTEST_CHECK(
true);
2246DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Data Consistency") {
2250 vec3 grid_center(0, 0, 0);
2251 vec3 grid_size(6, 6, 6);
2252 int3 grid_divisions(3, 3, 3);
2255 std::vector<vec3> ray_origins;
2256 std::vector<vec3> ray_directions;
2259 for (
int i = -1; i <= 1; i++) {
2260 for (
int j = -1; j <= 1; j++) {
2261 ray_origins.push_back(
make_vec3(i * 1.5f, j * 1.5f, -10));
2262 ray_directions.push_back(
make_vec3(0, 0, 1));
2267 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2270 bool found_data =
false;
2271 for (
int i = 0; i < grid_divisions.x; i++) {
2272 for (
int j = 0; j < grid_divisions.y; j++) {
2273 for (
int k = 0; k < grid_divisions.z; k++) {
2274 int P_denom, P_trans;
2275 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2280 DOCTEST_CHECK(P_trans <= P_denom);
2283 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2284 DOCTEST_CHECK(r_bar > 0.0f);
2290 DOCTEST_CHECK(found_data);
2293DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Manual Data Setting") {
2297 vec3 grid_center(0, 0, 0);
2298 vec3 grid_size(4, 4, 4);
2299 int3 grid_divisions(2, 2, 2);
2302 std::vector<vec3> init_origins;
2303 std::vector<vec3> init_directions;
2304 init_origins.push_back(
make_vec3(0, 0, -10));
2305 init_directions.push_back(
make_vec3(0, 0, 1));
2306 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, init_origins, init_directions);
2309 int3 test_voxel(0, 0, 0);
2310 collision.setVoxelTransmissionProbability(100, 75, test_voxel);
2311 collision.setVoxelRbar(2.5f, test_voxel);
2314 int P_denom, P_trans;
2315 collision.getVoxelTransmissionProbability(test_voxel, P_denom, P_trans);
2316 DOCTEST_CHECK(P_denom == 100);
2317 DOCTEST_CHECK(P_trans == 75);
2319 float r_bar = collision.getVoxelRbar(test_voxel);
2320 DOCTEST_CHECK(std::abs(r_bar - 2.5f) < 1e-6f);
2323 int3 test_voxel2(1, 1, 1);
2324 collision.setVoxelTransmissionProbability(200, 150, test_voxel2);
2325 collision.setVoxelRbar(3.7f, test_voxel2);
2327 collision.getVoxelTransmissionProbability(test_voxel2, P_denom, P_trans);
2328 DOCTEST_CHECK(P_denom == 200);
2329 DOCTEST_CHECK(P_trans == 150);
2331 r_bar = collision.getVoxelRbar(test_voxel2);
2332 DOCTEST_CHECK(std::abs(r_bar - 3.7f) < 1e-6f);
2335 collision.getVoxelTransmissionProbability(test_voxel, P_denom, P_trans);
2336 DOCTEST_CHECK(P_denom == 100);
2337 DOCTEST_CHECK(P_trans == 75);
2340DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Different Grid Sizes") {
2345 std::vector<int3> test_grids = {
2352 for (
const auto &grid_div: test_grids) {
2353 vec3 grid_center(0, 0, 0);
2354 vec3 grid_size(10, 10, 10);
2356 std::vector<vec3> ray_origins;
2357 std::vector<vec3> ray_directions;
2360 ray_origins.push_back(
make_vec3(0, 0, -10));
2361 ray_directions.push_back(
make_vec3(0, 0, 1));
2363 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_div, ray_origins, ray_directions);
2366 bool found_valid_voxel =
false;
2367 for (
int i = 0; i < grid_div.x; i++) {
2368 for (
int j = 0; j < grid_div.y; j++) {
2369 for (
int k = 0; k < grid_div.z; k++) {
2370 int P_denom, P_trans;
2371 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2372 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2375 DOCTEST_CHECK(P_denom >= 0);
2376 DOCTEST_CHECK(P_trans >= 0);
2377 DOCTEST_CHECK(r_bar >= 0.0f);
2378 found_valid_voxel =
true;
2382 DOCTEST_CHECK(found_valid_voxel);
2384 collision.clearVoxelData();
2388DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Ray Direction Variations") {
2392 vec3 grid_center(0, 0, 0);
2393 vec3 grid_size(8, 8, 8);
2394 int3 grid_divisions(2, 2, 2);
2397 std::vector<vec3> test_directions = {
2406 for (
const auto &direction: test_directions) {
2407 std::vector<vec3> ray_origins;
2408 std::vector<vec3> ray_directions;
2411 vec3 start_point = grid_center - direction * 10.0f;
2412 ray_origins.push_back(start_point);
2413 ray_directions.push_back(direction);
2415 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2418 bool found_intersections =
false;
2419 for (
int i = 0; i < grid_divisions.x; i++) {
2420 for (
int j = 0; j < grid_divisions.y; j++) {
2421 for (
int k = 0; k < grid_divisions.z; k++) {
2422 int P_denom, P_trans;
2423 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2425 found_intersections =
true;
2426 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2427 DOCTEST_CHECK(r_bar > 0.0f);
2433 DOCTEST_CHECK(found_intersections);
2434 collision.clearVoxelData();
2438DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - GPU/CPU Consistency") {
2442 vec3 grid_center(0, 0, 0);
2443 vec3 grid_size(6, 6, 6);
2444 int3 grid_divisions(3, 3, 3);
2447 std::vector<vec3> ray_origins;
2448 std::vector<vec3> ray_directions;
2450 for (
int i = 0; i < 5; i++) {
2451 ray_origins.push_back(
make_vec3(i - 2.0f, 0, -10));
2452 ray_directions.push_back(
make_vec3(0, 0, 1));
2456 collision.disableGPUAcceleration();
2457 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2460 std::vector<std::vector<std::vector<std::pair<int, float>>>> cpu_results(grid_divisions.x);
2461 for (
int i = 0; i < grid_divisions.x; i++) {
2462 cpu_results[i].resize(grid_divisions.y);
2463 for (
int j = 0; j < grid_divisions.y; j++) {
2464 cpu_results[i][j].resize(grid_divisions.z);
2465 for (
int k = 0; k < grid_divisions.z; k++) {
2466 int P_denom, P_trans;
2467 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2468 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2469 cpu_results[i][j][k] = std::make_pair(P_denom, r_bar);
2475 collision.enableGPUAcceleration();
2476 collision.clearVoxelData();
2477 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2480 for (
int i = 0; i < grid_divisions.x; i++) {
2481 for (
int j = 0; j < grid_divisions.y; j++) {
2482 for (
int k = 0; k < grid_divisions.z; k++) {
2483 int P_denom_gpu, P_trans_gpu;
2484 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom_gpu, P_trans_gpu);
2485 float r_bar_gpu = collision.getVoxelRbar(
make_int3(i, j, k));
2487 int P_denom_cpu = cpu_results[i][j][k].first;
2488 float r_bar_cpu = cpu_results[i][j][k].second;
2492 DOCTEST_CHECK(abs(P_denom_gpu - P_denom_cpu) <= 1);
2493 if (r_bar_cpu > 0 && r_bar_gpu > 0) {
2494 DOCTEST_CHECK(std::abs(r_bar_gpu - r_bar_cpu) < 1e-4f);
2501DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Parameter Validation") {
2504 collision.disableMessages();
2507 vec3 grid_center(0, 0, 0);
2508 vec3 negative_size(-5, 5, 5);
2509 int3 grid_divisions(2, 2, 2);
2510 std::vector<vec3> ray_origins = {
make_vec3(0, 0, -10)};
2511 std::vector<vec3> ray_directions = {
make_vec3(0, 0, 1)};
2515 collision.calculateVoxelRayPathLengths(grid_center, negative_size, grid_divisions, ray_origins, ray_directions);
2517 int P_denom, P_trans;
2518 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2519 DOCTEST_CHECK(P_denom >= 0);
2520 DOCTEST_CHECK(P_trans >= 0);
2521 }
catch (
const std::exception &e) {
2523 DOCTEST_CHECK(
true);
2527 int3 zero_divisions(0, 2, 2);
2529 collision.calculateVoxelRayPathLengths(grid_center,
make_vec3(5, 5, 5), zero_divisions, ray_origins, ray_directions);
2531 int P_denom_zero, P_trans_zero;
2533 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom_zero, P_trans_zero);
2535 DOCTEST_CHECK(P_denom_zero >= 0);
2536 }
catch (
const std::exception &inner_e) {
2538 DOCTEST_CHECK(
true);
2540 }
catch (
const std::exception &e) {
2541 DOCTEST_CHECK(
true);
2547 collision.calculateVoxelRayPathLengths(grid_center,
make_vec3(5, 5, 5),
make_int3(2, 2, 2), ray_origins, mismatched_directions);
2548 DOCTEST_CHECK(
false);
2549 }
catch (
const std::exception &e) {
2550 DOCTEST_CHECK(
true);
2554 vec3 valid_grid_size(4, 4, 4);
2555 int3 valid_divisions(2, 2, 2);
2556 collision.calculateVoxelRayPathLengths(grid_center, valid_grid_size, valid_divisions, ray_origins, ray_directions);
2559 collision.setVoxelTransmissionProbability(10, 15,
make_int3(0, 0, 0));
2560 int test_P_denom, test_P_trans;
2561 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), test_P_denom, test_P_trans);
2562 DOCTEST_CHECK(test_P_denom == 10);
2563 DOCTEST_CHECK(test_P_trans == 15);
2566 collision.setVoxelTransmissionProbability(-5, -3,
make_int3(0, 0, 0));
2567 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), test_P_denom, test_P_trans);
2568 DOCTEST_CHECK(test_P_denom == -5);
2569 DOCTEST_CHECK(test_P_trans == -3);
2572DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Mathematical Validation") {
2575 collision.disableMessages();
2578 vec3 grid_center(0, 0, 0);
2579 vec3 grid_size(2, 2, 2);
2580 int3 grid_divisions(1, 1, 1);
2583 std::vector<vec3> ray_origins = {
make_vec3(0, 0, -5)};
2584 std::vector<vec3> ray_directions = {
make_vec3(0, 0, 1)};
2586 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2588 float r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2591 DOCTEST_CHECK(std::abs(r_bar - 2.0f) < 0.1f);
2594 collision.clearVoxelData();
2595 std::vector<vec3> diagonal_origins = {
make_vec3(-2, -2, -2)};
2596 std::vector<vec3> diagonal_directions = {normalize(
make_vec3(1, 1, 1))};
2598 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, diagonal_origins, diagonal_directions);
2600 float diagonal_r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2603 float expected_diagonal = std::sqrt(3.0f) * 2.0f;
2604 DOCTEST_CHECK(std::abs(diagonal_r_bar - expected_diagonal) < 0.2f);
2607 collision.clearVoxelData();
2608 std::vector<vec3> multi_origins;
2609 std::vector<vec3> multi_directions;
2612 for (
int i = 0; i < 4; i++) {
2613 multi_origins.push_back(
make_vec3(0.5f * i - 0.75f, 0, -5));
2614 multi_directions.push_back(
make_vec3(0, 0, 1));
2617 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, multi_origins, multi_directions);
2619 int P_denom, P_trans;
2620 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2621 float multi_r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2624 DOCTEST_CHECK(P_denom == 4);
2626 DOCTEST_CHECK(P_trans == P_denom);
2628 DOCTEST_CHECK(std::abs(multi_r_bar - 2.0f) < 0.1f);
2631DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Numerical Precision") {
2634 collision.disableMessages();
2637 vec3 tiny_center(0, 0, 0);
2638 vec3 tiny_size(0.001f, 0.001f, 0.001f);
2639 int3 tiny_divisions(1, 1, 1);
2641 std::vector<vec3> tiny_origins = {
make_vec3(0, 0, -0.01f)};
2642 std::vector<vec3> tiny_directions = {
make_vec3(0, 0, 1)};
2644 collision.calculateVoxelRayPathLengths(tiny_center, tiny_size, tiny_divisions, tiny_origins, tiny_directions);
2645 float tiny_r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2648 DOCTEST_CHECK(tiny_r_bar > 0.0f);
2649 DOCTEST_CHECK(tiny_r_bar < 0.1f);
2652 vec3 large_center(0, 0, 0);
2653 vec3 large_size(1000.0f, 1000.0f, 1000.0f);
2654 int3 large_divisions(2, 2, 2);
2656 std::vector<vec3> large_origins = {
make_vec3(0, 0, -2000)};
2657 std::vector<vec3> large_directions = {
make_vec3(0, 0, 1)};
2659 collision.calculateVoxelRayPathLengths(large_center, large_size, large_divisions, large_origins, large_directions);
2660 float large_r_bar = collision.getVoxelRbar(
make_int3(1, 1, 1));
2663 DOCTEST_CHECK(large_r_bar > 0.0f);
2664 DOCTEST_CHECK(large_r_bar < 2000.0f);
2667 collision.clearVoxelData();
2668 vec3 precision_center(0, 0, 0);
2669 vec3 precision_size(10, 10, 10);
2670 int3 precision_divisions(5, 5, 5);
2672 std::vector<vec3> precision_origins;
2673 std::vector<vec3> precision_directions;
2674 for (
int i = 0; i < 10; i++) {
2675 precision_origins.push_back(
make_vec3(i - 5.0f, 0, -20));
2676 precision_directions.push_back(
make_vec3(0, 0, 1));
2680 collision.disableGPUAcceleration();
2681 collision.calculateVoxelRayPathLengths(precision_center, precision_size, precision_divisions, precision_origins, precision_directions);
2684 std::vector<float> cpu_rbars;
2685 for (
int i = 0; i < precision_divisions.x; i++) {
2686 for (
int j = 0; j < precision_divisions.y; j++) {
2687 for (
int k = 0; k < precision_divisions.z; k++) {
2688 cpu_rbars.push_back(collision.getVoxelRbar(
make_int3(i, j, k)));
2694 collision.enableGPUAcceleration();
2695 collision.clearVoxelData();
2696 collision.calculateVoxelRayPathLengths(precision_center, precision_size, precision_divisions, precision_origins, precision_directions);
2700 for (
int i = 0; i < precision_divisions.x; i++) {
2701 for (
int j = 0; j < precision_divisions.y; j++) {
2702 for (
int k = 0; k < precision_divisions.z; k++) {
2703 float gpu_rbar = collision.getVoxelRbar(
make_int3(i, j, k));
2704 float cpu_rbar = cpu_rbars[idx++];
2706 if (cpu_rbar > 0 && gpu_rbar > 0) {
2707 float relative_error = std::abs(gpu_rbar - cpu_rbar) / std::max(cpu_rbar, gpu_rbar);
2708 DOCTEST_CHECK(relative_error < 1e-5f);
2715DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Error Recovery and State Management") {
2718 collision.disableMessages();
2722 int P_denom, P_trans;
2723 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2725 DOCTEST_CHECK(P_denom == 0);
2726 DOCTEST_CHECK(P_trans == 0);
2727 }
catch (
const std::exception &e) {
2729 DOCTEST_CHECK(
true);
2733 float r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2735 DOCTEST_CHECK(r_bar == 0.0f);
2736 }
catch (
const std::exception &e) {
2738 DOCTEST_CHECK(
true);
2742 vec3 grid_center(0, 0, 0);
2743 vec3 grid_size(4, 4, 4);
2744 int3 grid_divisions(2, 2, 2);
2745 std::vector<vec3> ray_origins = {
make_vec3(0, 0, -10)};
2746 std::vector<vec3> ray_directions = {
make_vec3(0, 0, 1)};
2749 for (
int cycle = 0; cycle < 3; cycle++) {
2750 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2753 int P_denom, P_trans;
2754 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2755 DOCTEST_CHECK(P_denom >= 0);
2757 float r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2758 DOCTEST_CHECK(r_bar >= 0);
2761 collision.clearVoxelData();
2767 collision.setVoxelTransmissionProbability(10, 5,
make_int3(-1, 0, 0));
2768 DOCTEST_CHECK(
false);
2769 }
catch (
const std::exception &e) {
2771 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2773 int P_denom, P_trans;
2774 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2775 DOCTEST_CHECK(P_denom >= 0);
2779DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Memory and Performance Stress") {
2782 collision.disableMessages();
2785 vec3 stress_center(0, 0, 0);
2786 vec3 stress_size(50, 50, 50);
2787 int3 stress_divisions(10, 10, 10);
2790 std::vector<vec3> stress_origins;
2791 std::vector<vec3> stress_directions;
2792 for (
int i = 0; i < 100; i++) {
2793 for (
int j = 0; j < 10; j++) {
2794 stress_origins.push_back(
make_vec3(i - 50.0f, j - 5.0f, -100));
2795 stress_directions.push_back(
make_vec3(0, 0, 1));
2800 auto start_time = std::chrono::high_resolution_clock::now();
2801 collision.calculateVoxelRayPathLengths(stress_center, stress_size, stress_divisions, stress_origins, stress_directions);
2802 auto end_time = std::chrono::high_resolution_clock::now();
2803 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
2806 bool found_data =
false;
2807 for (
int i = 0; i < stress_divisions.x && !found_data; i++) {
2808 for (
int j = 0; j < stress_divisions.y && !found_data; j++) {
2809 for (
int k = 0; k < stress_divisions.z && !found_data; k++) {
2810 int P_denom, P_trans;
2811 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2814 DOCTEST_CHECK(P_trans <= P_denom);
2815 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2816 DOCTEST_CHECK(r_bar > 0.0f);
2821 DOCTEST_CHECK(found_data);
2824 collision.clearVoxelData();
2827 int P_denom, P_trans;
2828 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2829 DOCTEST_CHECK(P_denom == 0);
2830 DOCTEST_CHECK(P_trans == 0);
2832 float r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2833 DOCTEST_CHECK(r_bar == 0.0f);
2836 for (
int cycle = 0; cycle < 5; cycle++) {
2837 vec3 cycle_size(8 + cycle * 2, 8 + cycle * 2, 8 + cycle * 2);
2838 int3 cycle_divisions(2 + cycle, 2 + cycle, 2 + cycle);
2840 std::vector<vec3> cycle_origins = {
make_vec3(0, 0, -20)};
2841 std::vector<vec3> cycle_directions = {
make_vec3(0, 0, 1)};
2843 collision.calculateVoxelRayPathLengths(stress_center, cycle_size, cycle_divisions, cycle_origins, cycle_directions);
2846 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2847 DOCTEST_CHECK(P_denom >= 0);
2849 collision.clearVoxelData();
2853DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Integration with BVH") {
2856 collision.disableMessages();
2862 collision.buildBVH(sphere_UUIDs);
2865 vec3 grid_center(0, 0, 0);
2866 vec3 grid_size(10, 10, 10);
2867 int3 grid_divisions(5, 5, 5);
2869 std::vector<vec3> ray_origins;
2870 std::vector<vec3> ray_directions;
2871 for (
int i = 0; i < 8; i++) {
2872 ray_origins.push_back(
make_vec3(i - 4.0f, 0, -15));
2873 ray_directions.push_back(
make_vec3(0, 0, 1));
2877 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2880 bool found_voxel_data =
false;
2881 for (
int i = 0; i < grid_divisions.x; i++) {
2882 for (
int j = 0; j < grid_divisions.y; j++) {
2883 for (
int k = 0; k < grid_divisions.z; k++) {
2884 int P_denom, P_trans;
2885 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2887 found_voxel_data =
true;
2888 DOCTEST_CHECK(P_trans >= 0);
2889 DOCTEST_CHECK(P_trans <= P_denom);
2891 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2892 DOCTEST_CHECK(r_bar >= 0.0f);
2897 DOCTEST_CHECK(found_voxel_data);
2900 std::vector<uint> collision_results = collision.findCollisions(sphere_UUIDs[0]);
2901 DOCTEST_CHECK(collision_results.size() >= 0);
2904 collision.clearVoxelData();
2907 std::vector<uint> triangle_UUIDs;
2911 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2914 std::vector<uint> new_collisions = collision.findCollisions(triangle_UUIDs);
2915 DOCTEST_CHECK(new_collisions.size() >= 0);
2918 int final_P_denom, final_P_trans;
2919 collision.getVoxelTransmissionProbability(
make_int3(2, 2, 2), final_P_denom, final_P_trans);
2920 DOCTEST_CHECK(final_P_denom >= 0);
2921 DOCTEST_CHECK(final_P_trans >= 0);
2924DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Edge Case Ray Geometries") {
2927 collision.disableMessages();
2929 vec3 grid_center(0, 0, 0);
2930 vec3 grid_size(4, 4, 4);
2931 int3 grid_divisions(2, 2, 2);
2934 std::vector<vec3> grazing_origins = {
make_vec3(-3, 2.0f, 0)};
2935 std::vector<vec3> grazing_directions = {
make_vec3(1, 0, 0)};
2937 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, grazing_origins, grazing_directions);
2940 bool found_grazing_intersection =
false;
2941 for (
int i = 0; i < grid_divisions.x; i++) {
2942 for (
int j = 0; j < grid_divisions.y; j++) {
2943 for (
int k = 0; k < grid_divisions.z; k++) {
2944 int P_denom, P_trans;
2945 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2947 found_grazing_intersection =
true;
2948 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2949 DOCTEST_CHECK(r_bar >= 0.0f);
2956 collision.clearVoxelData();
2957 std::vector<vec3> corner_origins = {
make_vec3(-3, -3, -3)};
2958 std::vector<vec3> corner_directions = {normalize(
make_vec3(1, 1, 1))};
2960 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, corner_origins, corner_directions);
2962 bool found_corner_intersection =
false;
2963 for (
int i = 0; i < grid_divisions.x; i++) {
2964 for (
int j = 0; j < grid_divisions.y; j++) {
2965 for (
int k = 0; k < grid_divisions.z; k++) {
2966 int P_denom, P_trans;
2967 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2969 found_corner_intersection =
true;
2970 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2971 DOCTEST_CHECK(r_bar >= 0.0f);
2978 collision.clearVoxelData();
2979 std::vector<vec3> near_zero_origins = {
make_vec3(0, 0, -5)};
2980 std::vector<vec3> near_zero_directions = {normalize(
make_vec3(1e-6f, 1e-6f, 1.0f))};
2982 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, near_zero_origins, near_zero_directions);
2985 bool found_near_zero_intersection =
false;
2986 for (
int i = 0; i < grid_divisions.x; i++) {
2987 for (
int j = 0; j < grid_divisions.y; j++) {
2988 for (
int k = 0; k < grid_divisions.z; k++) {
2989 int P_denom, P_trans;
2990 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2992 found_near_zero_intersection =
true;
2993 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2994 DOCTEST_CHECK(r_bar >= 0.0f);
2995 DOCTEST_CHECK(std::isfinite(r_bar));
3004DOCTEST_TEST_CASE(
"CollisionDetection Generic Ray Casting - Basic Functionality") {
3007 collision.disableMessages();
3019 CollisionDetection::CollisionDetection::HitResult result = collision.castRay(ray_origin, ray_direction);
3021 DOCTEST_CHECK(result.hit ==
true);
3022 DOCTEST_CHECK(result.primitive_UUID == triangle_uuid);
3023 DOCTEST_CHECK(result.distance > 0.9f);
3024 DOCTEST_CHECK(result.distance < 1.1f);
3027 DOCTEST_CHECK(result.intersection_point.x > 0.4f);
3028 DOCTEST_CHECK(result.intersection_point.x < 0.6f);
3029 DOCTEST_CHECK(std::abs(result.intersection_point.y) < 1e-5f);
3030 DOCTEST_CHECK(result.intersection_point.z > 0.4f);
3031 DOCTEST_CHECK(result.intersection_point.z < 0.6f);
3038 DOCTEST_CHECK(miss_result.
hit ==
false);
3039 DOCTEST_CHECK(miss_result.
distance < 0);
3044 float max_distance = 1.5f;
3047 DOCTEST_CHECK(limited_result.
hit ==
false);
3050DOCTEST_TEST_CASE(
"CollisionDetection Generic Ray Casting - CollisionDetection::RayQuery Structure") {
3053 collision.disableMessages();
3065 DOCTEST_CHECK(result1.
hit ==
true);
3071 DOCTEST_CHECK(result2.hit ==
true);
3072 DOCTEST_CHECK(result2.primitive_UUID == triangle2);
3078 DOCTEST_CHECK(result3.hit ==
true);
3079 DOCTEST_CHECK(result3.primitive_UUID == triangle1);
3085 DOCTEST_CHECK(result4.hit ==
false);
3088DOCTEST_TEST_CASE(
"CollisionDetection Batch Ray Casting") {
3091 collision.disableMessages();
3098 std::vector<CollisionDetection::RayQuery> queries;
3106 std::vector<CollisionDetection::HitResult> results = collision.castRays(queries, &stats);
3109 DOCTEST_CHECK(results.size() == 4);
3115 DOCTEST_CHECK(results[0].hit ==
true);
3116 DOCTEST_CHECK(results[1].hit ==
false);
3117 DOCTEST_CHECK(results[2].hit ==
true);
3118 DOCTEST_CHECK(results[3].hit ==
true);
3121 for (
const auto &result: results) {
3123 DOCTEST_CHECK(result.primitive_UUID == triangle);
3124 DOCTEST_CHECK(result.distance > 0);
3129DOCTEST_TEST_CASE(
"CollisionDetection Grid Ray Intersection") {
3132 collision.disableMessages();
3143 std::vector<CollisionDetection::RayQuery> rays;
3148 auto grid_results = collision.performGridRayIntersection(grid_center, grid_size, grid_divisions, rays);
3150 DOCTEST_CHECK(grid_results.size() == 2);
3151 DOCTEST_CHECK(grid_results[0].size() == 2);
3152 DOCTEST_CHECK(grid_results[0][0].size() == 2);
3156 for (
int i = 0; i < 2; i++) {
3157 for (
int j = 0; j < 2; j++) {
3158 for (
int k = 0; k < 2; k++) {
3159 total_hits += grid_results[i][j][k].size();
3163 DOCTEST_CHECK(total_hits >= 1);
3166DOCTEST_TEST_CASE(
"CollisionDetection Ray Path Lengths Detailed") {
3169 collision.disableMessages();
3179 std::vector<vec3> ray_origins = {
3185 std::vector<CollisionDetection::HitResult> hit_results;
3186 collision.calculateRayPathLengthsDetailed(grid_center, grid_size, grid_divisions, ray_origins, ray_directions, hit_results);
3188 DOCTEST_CHECK(hit_results.size() == 3);
3191 DOCTEST_CHECK(hit_results[0].hit ==
true);
3192 DOCTEST_CHECK(hit_results[1].hit ==
true);
3193 DOCTEST_CHECK(hit_results[2].hit ==
false);
3196 DOCTEST_CHECK(hit_results[0].distance > 1.5f);
3197 DOCTEST_CHECK(hit_results[0].distance < 2.5f);
3198 DOCTEST_CHECK(hit_results[1].distance > 1.5f);
3199 DOCTEST_CHECK(hit_results[1].distance < 2.5f);
3202 int P_denom, P_trans;
3203 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
3204 DOCTEST_CHECK(P_denom >= 0);
3207DOCTEST_TEST_CASE(
"CollisionDetection Ray Casting - Normal Calculation") {
3210 collision.disableMessages();
3221 CollisionDetection::CollisionDetection::HitResult result = collision.castRay(ray_origin, ray_direction);
3223 DOCTEST_CHECK(result.hit ==
true);
3227 float dot_product = result.normal.x * expected_normal.
x + result.normal.y * expected_normal.
y + result.normal.z * expected_normal.
z;
3228 DOCTEST_CHECK(std::abs(dot_product) > 0.9f);
3238 DOCTEST_CHECK(patch_result.
hit ==
true);
3246DOCTEST_TEST_CASE(
"CollisionDetection Ray Casting - Edge Cases and Error Handling") {
3249 collision.disableMessages();
3257 DOCTEST_CHECK(zero_result.
hit ==
false);
3266 DOCTEST_CHECK(inf_result.
hit ==
true);
3270 DOCTEST_CHECK(neg_result.
hit ==
true);
3273 std::vector<uint> empty_targets;
3275 DOCTEST_CHECK(empty_result.
hit ==
true);
3278 std::vector<uint> invalid_targets = {99999, triangle, 88888};
3280 DOCTEST_CHECK(invalid_result.
hit ==
true);
3284DOCTEST_TEST_CASE(
"CollisionDetection Ray Casting - Performance and Scalability") {
3287 collision.disableMessages();
3290 std::vector<uint> triangles;
3291 for (
int i = 0; i < 100; i++) {
3294 triangles.push_back(triangle);
3298 std::vector<CollisionDetection::RayQuery> many_rays;
3299 for (
int i = 0; i < 200; i++) {
3300 float x = (i % 100) * 0.1f + 0.025f;
3304 auto start_time = std::chrono::high_resolution_clock::now();
3307 std::vector<CollisionDetection::HitResult> results = collision.castRays(many_rays, &stats);
3309 auto end_time = std::chrono::high_resolution_clock::now();
3310 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
3312 DOCTEST_CHECK(results.size() == 200);
3317 DOCTEST_CHECK(duration.count() < 1000);
3319 std::cout <<
"Batch ray casting performance: " << duration.count() <<
" ms for " << many_rays.size() <<
" rays (" << stats.
total_hits <<
" hits)" << std::endl;
3322DOCTEST_TEST_CASE(
"CollisionDetection Ray Casting - Integration with Existing BVH") {
3325 collision.disableMessages();
3332 collision.buildBVH();
3335 DOCTEST_CHECK(result1.
hit ==
true);
3339 DOCTEST_CHECK(result2.
hit ==
true);
3343 collision.enableAutomaticBVHRebuilds();
3350 DOCTEST_CHECK(result3.
hit ==
true);
3354DOCTEST_TEST_CASE(
"CollisionDetection Ray Casting - Compatibility with Other Plugin Methods") {
3357 collision.disableMessages();
3363 auto collisions = collision.findCollisions(triangles[0]);
3364 DOCTEST_CHECK(collisions.size() > 0);
3367 DOCTEST_CHECK(triangles.size() == 5);
3370 auto cone_result = collision.findOptimalConePath(
make_vec3(0, -2, 0),
make_vec3(0, 1, 0),
M_PI / 6, 3.0f);
3374 DOCTEST_CHECK(collisions.size() > 0);
3375 DOCTEST_CHECK(
true);
3382DOCTEST_TEST_CASE(
"CollisionDetection - BVH Optimization Mode Management") {
3385 collision.disableMessages();
3399 collision.buildBVH();
3406 auto memory_stats = collision.getBVHMemoryUsage();
3407 DOCTEST_CHECK(memory_stats.soa_memory_bytes > 0);
3410 DOCTEST_CHECK(memory_stats.quantized_memory_bytes == 0);
3411 DOCTEST_CHECK(memory_stats.quantized_reduction_percent == 0.0f);
3414DOCTEST_TEST_CASE(
"CollisionDetection - Optimized Ray Casting Correctness") {
3417 collision.disableMessages();
3424 std::vector<CollisionDetection::RayQuery> rays;
3430 std::vector<CollisionDetection::HitResult> legacy_results, soa_results;
3434 legacy_results = collision.castRays(rays);
3438 soa_results = collision.castRaysOptimized(rays);
3441 DOCTEST_REQUIRE(legacy_results.size() == 3);
3442 DOCTEST_REQUIRE(soa_results.size() == 3);
3444 for (
size_t i = 0; i < legacy_results.size(); i++) {
3446 DOCTEST_CHECK(legacy_results[i].hit == soa_results[i].hit);
3448 if (legacy_results[i].hit) {
3450 DOCTEST_CHECK(legacy_results[i].primitive_UUID == soa_results[i].primitive_UUID);
3453 DOCTEST_CHECK(std::abs(legacy_results[i].distance - soa_results[i].distance) < 0.001f);
3458 DOCTEST_CHECK(legacy_results[0].hit ==
true);
3459 DOCTEST_CHECK(legacy_results[1].hit ==
true);
3460 DOCTEST_CHECK(legacy_results[2].hit ==
false);
3463DOCTEST_TEST_CASE(
"CollisionDetection - Ray Streaming Interface") {
3466 collision.disableMessages();
3470 for (
int i = 0; i < 5; i++) {
3477 std::vector<CollisionDetection::RayQuery> batch;
3480 for (
int i = 0; i < 50; i++) {
3481 float x = (i % 5) * 2.0f + 0.5f;
3487 DOCTEST_CHECK(stream.
packets.size() > 0);
3491 bool success = collision.processRayStream(stream, &stats);
3492 DOCTEST_CHECK(success ==
true);
3497 DOCTEST_CHECK(results.size() == 50);
3500 size_t hit_count = 0;
3501 for (
const auto &result: results) {
3505 DOCTEST_CHECK(hit_count > 40);
3508DOCTEST_TEST_CASE(
"CollisionDetection - BVH Layout Conversion Methods") {
3511 collision.disableMessages();
3520 collision.buildBVH();
3523 std::vector<CollisionDetection::RayQuery> test_rays = {
3530 auto legacy_results = collision.castRays(test_rays);
3531 auto soa_results = collision.castRaysOptimized(test_rays);
3532 auto memory_stats = collision.getBVHMemoryUsage();
3535 DOCTEST_REQUIRE(legacy_results.size() == 3);
3536 DOCTEST_REQUIRE(soa_results.size() == 3);
3539 size_t legacy_hits = 0, soa_hits = 0;
3540 for (
size_t i = 0; i < 3; i++) {
3541 if (legacy_results[i].hit)
3543 if (soa_results[i].hit)
3548 DOCTEST_CHECK(legacy_hits == soa_hits);
3551 DOCTEST_CHECK(memory_stats.soa_memory_bytes > 0);
3552 DOCTEST_CHECK(memory_stats.quantized_memory_bytes == 0);
3553 DOCTEST_CHECK(memory_stats.quantized_reduction_percent == 0.0f);
3556DOCTEST_TEST_CASE(
"CollisionDetection - RayPacket Edge Cases and Functionality") {
3559 collision.disableMessages();
3563 DOCTEST_CHECK(empty_packet.
ray_count == 0);
3568 DOCTEST_CHECK_NOTHROW(empty_packet.
clear());
3575 std::vector<CollisionDetection::RayQuery> test_queries;
3576 for (
int i = 0; i < 150; i++) {
3579 test_queries.push_back(query);
3580 capacity_packet.
addRay(query);
3583 DOCTEST_CHECK(capacity_packet.
ray_count == 150);
3584 DOCTEST_CHECK(capacity_packet.
origins.size() == 150);
3585 DOCTEST_CHECK(capacity_packet.
directions.size() == 150);
3586 DOCTEST_CHECK(capacity_packet.
results.size() == 150);
3589 auto converted_queries = capacity_packet.
toRayQueries();
3590 DOCTEST_REQUIRE(converted_queries.size() == 150);
3592 for (
size_t i = 0; i < 150; i++) {
3593 DOCTEST_CHECK(converted_queries[i].origin.
magnitude() == test_queries[i].origin.magnitude());
3594 DOCTEST_CHECK(converted_queries[i].direction.magnitude() == test_queries[i].direction.magnitude());
3595 DOCTEST_CHECK(converted_queries[i].max_distance == test_queries[i].max_distance);
3599 size_t expected_memory = (150 * 2) *
sizeof(
helios::vec3) +
3600 150 *
sizeof(float) +
3603 DOCTEST_CHECK(actual_memory >= expected_memory);
3606 capacity_packet.
clear();
3607 DOCTEST_CHECK(capacity_packet.
ray_count == 0);
3608 DOCTEST_CHECK(capacity_packet.
origins.empty());
3609 DOCTEST_CHECK(capacity_packet.
directions.empty());
3610 DOCTEST_CHECK(capacity_packet.
results.empty());
3614DOCTEST_TEST_CASE(
"CollisionDetection - RayStream Batch Management") {
3617 collision.disableMessages();
3621 for (
int i = 0; i < 3; i++) {
3628 std::vector<CollisionDetection::RayQuery> large_batch;
3632 for (
size_t i = 0; i < total_rays; i++) {
3633 float x = (i % 3) * 3.0f + 0.5f;
3637 large_stream.
addRays(large_batch);
3638 DOCTEST_CHECK(large_stream.
total_rays == total_rays);
3639 DOCTEST_CHECK(large_stream.
packets.size() == 3);
3643 DOCTEST_CHECK(stream_memory_before > 0);
3646 bool large_success = collision.processRayStream(large_stream, &large_stats);
3647 DOCTEST_CHECK(large_success ==
true);
3652 DOCTEST_CHECK(all_results.size() == total_rays);
3655 size_t hit_count = 0;
3656 for (
const auto &result: all_results) {
3660 float hit_rate = float(hit_count) / float(total_rays);
3661 DOCTEST_CHECK(hit_rate > 0.8f);
3666 DOCTEST_CHECK(empty_stream.
packets.empty());
3670 bool empty_success = collision.processRayStream(empty_stream, &empty_stats);
3671 DOCTEST_CHECK(empty_success ==
true);
3675 large_stream.
clear();
3677 DOCTEST_CHECK(large_stream.
packets.empty());
3682DOCTEST_TEST_CASE(
"CollisionDetection - SoA Precision Validation") {
3685 collision.disableMessages();
3694 collision.buildBVH();
3696 std::vector<CollisionDetection::RayQuery> precision_test_rays = {
3703 CollisionDetection::RayQuery(
make_vec3(20, -1, 0),
make_vec3(0, 1, 0)),
CollisionDetection::RayQuery(
make_vec3(-5, -1, 0),
make_vec3(0, 1, 0))};
3705 auto legacy_results = collision.castRays(precision_test_rays);
3706 auto soa_results = collision.castRaysOptimized(precision_test_rays);
3708 DOCTEST_REQUIRE(legacy_results.size() == precision_test_rays.size());
3709 DOCTEST_REQUIRE(soa_results.size() == precision_test_rays.size());
3712 for (
size_t i = 0; i < precision_test_rays.size(); i++) {
3713 DOCTEST_CHECK(legacy_results[i].hit == soa_results[i].hit);
3715 if (legacy_results[i].hit && soa_results[i].hit) {
3716 DOCTEST_CHECK(legacy_results[i].primitive_UUID == soa_results[i].primitive_UUID);
3717 DOCTEST_CHECK(std::abs(legacy_results[i].distance - soa_results[i].distance) < 0.001f);
3722 auto memory_stats = collision.getBVHMemoryUsage();
3723 DOCTEST_CHECK(memory_stats.soa_memory_bytes > 0);
3724 DOCTEST_CHECK(memory_stats.quantized_memory_bytes == 0);
3725 DOCTEST_CHECK(memory_stats.quantized_reduction_percent == 0.0f);
3728DOCTEST_TEST_CASE(
"CollisionDetection - Error Handling and Edge Cases") {
3731 collision.disableMessages();
3739 auto initial_mode = collision.getBVHOptimizationMode();
3740 DOCTEST_CHECK_NOTHROW(collision.setBVHOptimizationMode(initial_mode));
3741 DOCTEST_CHECK(collision.getBVHOptimizationMode() == initial_mode);
3744 auto empty_memory_stats = collision.getBVHMemoryUsage();
3745 DOCTEST_CHECK(empty_memory_stats.soa_memory_bytes == 0);
3746 DOCTEST_CHECK(empty_memory_stats.quantized_memory_bytes == 0);
3751 DOCTEST_CHECK_NOTHROW(collision.castRays(empty_test_rays));
3752 DOCTEST_CHECK_NOTHROW(collision.castRaysOptimized(empty_test_rays));
3754 auto empty_results = collision.castRaysOptimized(empty_test_rays);
3755 DOCTEST_CHECK(empty_results.size() == 1);
3756 DOCTEST_CHECK(empty_results[0].hit ==
false);
3761 DOCTEST_CHECK_NOTHROW(collision.processRayStream(empty_stream, &empty_stats));
3765 collision.buildBVH();
3769 auto recovery_results = collision.castRaysOptimized(recovery_rays);
3770 DOCTEST_CHECK(recovery_results.size() == 1);
3771 DOCTEST_CHECK(recovery_results[0].hit ==
true);
3774DOCTEST_TEST_CASE(
"CollisionDetection - Memory and Statistics Validation") {
3777 collision.disableMessages();
3780 for (
int i = 0; i < 8; i++) {
3782 float y = (i % 2) * 2.0f;
3789 collision.buildBVH();
3790 auto legacy_memory = collision.getBVHMemoryUsage();
3793 auto memory_stats = collision.getBVHMemoryUsage();
3796 DOCTEST_CHECK(memory_stats.soa_memory_bytes > 0);
3797 DOCTEST_CHECK(memory_stats.quantized_memory_bytes == 0);
3798 DOCTEST_CHECK(memory_stats.quantized_reduction_percent == 0.0f);
3801 std::vector<CollisionDetection::RayQuery> stat_test_rays;
3802 for (
int i = 0; i < 20; i++) {
3803 float x = (i % 4) * 2.0f + 0.5f;
3804 float y = (i / 4) * 2.0f + 0.5f;
3809 auto stat_results = collision.castRaysOptimized(stat_test_rays, &stats);
3813 DOCTEST_CHECK(stat_results.size() == 20);
3823 stats_stream.
addRays(stat_test_rays);
3826 bool stream_success = collision.processRayStream(stats_stream, &stream_stats);
3827 DOCTEST_CHECK(stream_success ==
true);
3839DOCTEST_TEST_CASE(
"CollisionDetection Voxel Primitive Intersection - Basic Ray-AABB Tests") {
3842 collision.disableMessages();
3843 collision.disableGPUAcceleration();
3848 collision.buildBVH();
3857 auto results = collision.castRays({ray});
3858 DOCTEST_CHECK(results.size() == 1);
3859 DOCTEST_CHECK(results[0].hit ==
true);
3860 DOCTEST_CHECK(results[0].distance > 3.9f);
3861 DOCTEST_CHECK(results[0].distance < 4.1f);
3862 DOCTEST_CHECK(results[0].primitive_UUID == voxel_uuid);
3872 auto results = collision.castRays({ray});
3873 DOCTEST_CHECK(results.size() == 1);
3874 DOCTEST_CHECK(results[0].hit ==
false);
3884 auto results = collision.castRays({ray});
3885 DOCTEST_CHECK(results.size() == 1);
3886 DOCTEST_CHECK(results[0].hit ==
true);
3887 DOCTEST_CHECK(results[0].distance > 0.9f);
3888 DOCTEST_CHECK(results[0].distance < 1.1f);
3892DOCTEST_TEST_CASE(
"CollisionDetection Voxel Primitive Intersection - Multiple Voxels") {
3895 collision.disableMessages();
3896 collision.disableGPUAcceleration();
3903 collision.buildBVH();
3912 auto results = collision.castRays({ray});
3913 DOCTEST_CHECK(results.size() == 1);
3914 DOCTEST_CHECK(results[0].hit ==
true);
3915 DOCTEST_CHECK(results[0].primitive_UUID == voxel1);
3925 auto results = collision.castRays({ray});
3926 DOCTEST_CHECK(results.size() == 1);
3927 DOCTEST_CHECK(results[0].hit ==
true);
3928 DOCTEST_CHECK(results[0].primitive_UUID == voxel1);
3929 DOCTEST_CHECK(results[0].distance > 2.9f);
3930 DOCTEST_CHECK(results[0].distance < 3.1f);
3934DOCTEST_TEST_CASE(
"CollisionDetection Voxel Primitive Intersection - GPU vs CPU Consistency") {
3937 collision.disableMessages();
3940 std::vector<uint> voxel_uuids;
3946 std::vector<CollisionDetection::RayQuery> test_rays;
3949 for (
int i = 0; i < 5; i++) {
3950 for (
int j = 0; j < 3; j++) {
3955 test_rays.push_back(ray);
3959 collision.buildBVH();
3962 collision.disableGPUAcceleration();
3963 auto cpu_results = collision.castRays(test_rays);
3966 collision.enableGPUAcceleration();
3967 auto gpu_results = collision.castRays(test_rays);
3970 DOCTEST_CHECK(cpu_results.size() == gpu_results.size());
3971 DOCTEST_CHECK(cpu_results.size() == test_rays.size());
3973 for (
size_t i = 0; i < cpu_results.size(); i++) {
3974 DOCTEST_CHECK(cpu_results[i].hit == gpu_results[i].hit);
3976 if (cpu_results[i].hit && gpu_results[i].hit) {
3978 DOCTEST_CHECK(std::abs(cpu_results[i].distance - gpu_results[i].distance) < 0.01f);
3979 DOCTEST_CHECK(cpu_results[i].primitive_UUID == gpu_results[i].primitive_UUID);
3986DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Ray-Triangle Intersection Algorithms") {
3989 collision.disableMessages();
3990 collision.disableGPUAcceleration();
3999 vec3 ray_origin =
make_vec3(1.0f / 3.0f, 1.0f / 3.0f, -1.0f);
4004 DOCTEST_CHECK(result.
hit ==
true);
4008 DOCTEST_CHECK(std::abs(result.
distance - 1.0f) < 1e-6f);
4016 vec3 expected_normal = normalize(
cross(v1 - v0, v2 - v0));
4017 float normal_dot = result.
normal.
x * expected_normal.
x + result.
normal.
y * expected_normal.
y + result.
normal.
z * expected_normal.
z;
4018 DOCTEST_CHECK(std::abs(normal_dot - 1.0f) < 1e-6f);
4021DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Edge Case Intersections") {
4024 collision.disableMessages();
4025 collision.disableGPUAcceleration();
4039 DOCTEST_CHECK(result.
hit ==
true);
4051 if (vertex_result.
hit) {
4058DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Barycentric Coordinate Validation") {
4061 collision.disableMessages();
4062 collision.disableGPUAcceleration();
4065 float sqrt3 = std::sqrt(3.0f);
4072 vec3 centroid = (v0 + v1 + v2) * (1.0f / 3.0f);
4078 DOCTEST_CHECK(result.
hit ==
true);
4083 vec3 midpoint_v0_v1 = (v0 + v1) * 0.5f;
4084 vec3 midpoint_ray_origin =
make_vec3(midpoint_v0_v1.
x, midpoint_v0_v1.
y, -1);
4088 DOCTEST_CHECK(midpoint_result.
hit ==
true);
4095DOCTEST_TEST_CASE(
"CollisionDetection GPU-Specific - Direct castRaysGPU Testing") {
4098 collision.disableMessages();
4101 std::vector<uint> uuids;
4107 std::vector<CollisionDetection::RayQuery> queries;
4108 for (
int i = 0; i < 100; i++) {
4113 queries.push_back(query);
4118 collision.enableGPUAcceleration();
4119#ifdef HELIOS_CUDA_AVAILABLE
4120 if (collision.isGPUAccelerationEnabled()) {
4122 std::vector<CollisionDetection::HitResult> gpu_results = collision.castRaysGPU(queries, gpu_stats);
4124 DOCTEST_CHECK(gpu_results.size() == queries.size());
4128 collision.disableGPUAcceleration();
4130 std::vector<CollisionDetection::HitResult> cpu_results = collision.castRays(queries, &cpu_stats);
4133 DOCTEST_CHECK(cpu_results.size() == gpu_results.size());
4135 int consistent_hits = 0;
4136 for (
size_t i = 0; i < cpu_results.size(); i++) {
4137 if (cpu_results[i].hit == gpu_results[i].hit) {
4139 if (cpu_results[i].hit) {
4141 DOCTEST_CHECK(std::abs(cpu_results[i].distance - gpu_results[i].distance) < 0.001f);
4142 DOCTEST_CHECK(cpu_results[i].primitive_UUID == gpu_results[i].primitive_UUID);
4148 DOCTEST_CHECK(consistent_hits >= (
int) (0.95f * queries.size()));
4151 DOCTEST_WARN(
"GPU acceleration not available - skipping direct GPU tests");
4154 }
catch (std::exception &e) {
4155 DOCTEST_WARN((std::string(
"GPU test failed (expected on non-NVIDIA systems): ") + e.what()).c_str());
4159DOCTEST_TEST_CASE(
"CollisionDetection GPU-Specific - Error Handling and Edge Cases") {
4162 collision.disableMessages();
4165 collision.enableGPUAcceleration();
4166#ifdef HELIOS_CUDA_AVAILABLE
4167 if (collision.isGPUAccelerationEnabled()) {
4170 std::vector<CollisionDetection::RayQuery> empty_queries;
4172 std::vector<CollisionDetection::HitResult> results = collision.castRaysGPU(empty_queries, stats);
4173 DOCTEST_CHECK(results.empty());
4177 std::vector<CollisionDetection::RayQuery> large_batch;
4178 for (
int i = 0; i < 10000; i++) {
4183 large_batch.push_back(query);
4187 std::vector<CollisionDetection::HitResult> large_results = collision.castRaysGPU(large_batch, large_stats);
4188 DOCTEST_CHECK(large_results.size() == large_batch.size());
4192 std::vector<CollisionDetection::RayQuery> degenerate_queries;
4196 degenerate_queries.push_back(degenerate);
4199 std::vector<CollisionDetection::HitResult> degenerate_results = collision.castRaysGPU(degenerate_queries, degenerate_stats);
4200 DOCTEST_CHECK(degenerate_results.size() == 1);
4201 DOCTEST_CHECK(degenerate_results[0].hit ==
false);
4204 DOCTEST_WARN(
"GPU acceleration not available - skipping GPU error handling tests");
4207 }
catch (std::exception &e) {
4208 DOCTEST_WARN((std::string(
"GPU error handling test failed: ") + e.what()).c_str());
4214DOCTEST_TEST_CASE(
"CollisionDetection Floating-Point Precision - Extreme Values") {
4217 collision.disableMessages();
4218 collision.disableGPUAcceleration();
4221 float epsilon = 1e-6f;
4225 uint small_triangle = context.
addTriangle(v0_small, v1_small, v2_small);
4227 vec3 ray_origin =
make_vec3(epsilon / 3.0f, epsilon / 3.0f, -epsilon);
4232 DOCTEST_CHECK(std::isfinite(small_result.
distance));
4238 float large_scale = 1e6f;
4242 uint large_triangle = context.
addTriangle(v0_large, v1_large, v2_large);
4248 DOCTEST_CHECK(std::isfinite(large_result.
distance));
4249 if (large_result.
hit) {
4256DOCTEST_TEST_CASE(
"CollisionDetection Floating-Point Precision - Near-Parallel Rays") {
4259 collision.disableMessages();
4260 collision.disableGPUAcceleration();
4269 float tiny_angle = 1e-6f;
4271 vec3 near_parallel_direction = normalize(
make_vec3(0, tiny_angle, 1));
4276 DOCTEST_CHECK(std::isfinite(near_parallel_result.
distance));
4277 if (near_parallel_result.
hit) {
4281 DOCTEST_CHECK(near_parallel_result.
distance > 0);
4285DOCTEST_TEST_CASE(
"CollisionDetection Floating-Point Precision - Boundary Conditions") {
4288 collision.disableMessages();
4289 collision.disableGPUAcceleration();
4298 float boundary_offset = 1e-8f;
4300 std::vector<vec3> boundary_origins = {
4302 make_vec3(1 + boundary_offset, 0.5f, -1),
4304 make_vec3(0.5f + boundary_offset, 0.5f + boundary_offset, -1)
4307 for (
const auto &origin: boundary_origins) {
4312 DOCTEST_CHECK(std::isfinite(result.
distance));
4323DOCTEST_TEST_CASE(
"CollisionDetection Complex Geometry - Multi-Primitive Accuracy") {
4326 collision.disableMessages();
4327 collision.disableGPUAcceleration();
4330 std::vector<uint> uuids;
4333 for (
int i = 0; i < 5; i++) {
4334 for (
int j = 0; j < 5; j++) {
4338 uuids.push_back(uuid);
4343 int correct_predictions = 0;
4344 int total_predictions = 0;
4346 for (
int i = 0; i < 10; i++) {
4347 for (
int j = 0; j < 10; j++) {
4357 bool should_hit =
false;
4358 for (
int ti = 0; ti < 5; ti++) {
4359 for (
int tj = 0; tj < 5; tj++) {
4360 float tx = ti * 0.8f;
4361 float ty = tj * 0.8f;
4374 float dot00 = v0.
x * v0.
x + v0.
y * v0.
y + v0.
z * v0.
z;
4375 float dot01 = v0.
x * v1.
x + v0.
y * v1.
y + v0.
z * v1.
z;
4376 float dot02 = v0.
x * v2.
x + v0.
y * v2.
y + v0.
z * v2.
z;
4377 float dot11 = v1.
x * v1.
x + v1.
y * v1.
y + v1.
z * v1.
z;
4378 float dot12 = v1.
x * v2.
x + v1.
y * v2.
y + v1.
z * v2.
z;
4380 float inv_denom = 1.0f / (dot00 * dot11 - dot01 * dot01);
4381 float u = (dot11 * dot02 - dot01 * dot12) * inv_denom;
4382 float v = (dot00 * dot12 - dot01 * dot02) * inv_denom;
4385 const float EPSILON = 1e-6f;
4386 if ((u >= -EPSILON) && (v >= -EPSILON) && (u + v <= 1 + EPSILON)) {
4395 if (result.
hit == should_hit) {
4396 correct_predictions++;
4398 total_predictions++;
4403 float accuracy = (float) correct_predictions / (
float) total_predictions;
4404 DOCTEST_CHECK(accuracy == 1.0f);
4407DOCTEST_TEST_CASE(
"CollisionDetection Complex Geometry - Stress Test Validation") {
4410 collision.disableMessages();
4411 collision.disableGPUAcceleration();
4414 std::vector<uint> stress_uuids;
4415 for (
int i = 0; i < 1000; i++) {
4416 float x = (rand() % 100) * 0.01f;
4417 float y = (rand() % 100) * 0.01f;
4418 float z = (rand() % 10) * 0.01f;
4419 float size = 0.1f + (rand() % 10) * 0.01f;
4422 stress_uuids.push_back(uuid);
4426 std::vector<CollisionDetection::RayQuery> stress_queries;
4427 for (
int i = 0; i < 500; i++) {
4429 query.
origin =
make_vec3((rand() % 200) * 0.01f - 1.0f, (rand() % 200) * 0.01f - 1.0f, -1.0f);
4432 stress_queries.push_back(query);
4436 std::vector<CollisionDetection::HitResult> stress_results = collision.castRays(stress_queries, &stats);
4438 DOCTEST_CHECK(stress_results.size() == stress_queries.size());
4442 int valid_results = 0;
4443 for (
const auto &result: stress_results) {
4449 DOCTEST_CHECK(valid_results == (
int) stress_results.size());
4454DOCTEST_TEST_CASE(
"CollisionDetection Performance Regression - BVH Construction Timing") {
4457 collision.disableMessages();
4460 std::vector<uint> large_geometry;
4461 for (
int i = 0; i < 5000; i++) {
4464 large_geometry.push_back(uuid);
4468 auto start_time = std::chrono::high_resolution_clock::now();
4469 collision.buildBVH();
4470 auto end_time = std::chrono::high_resolution_clock::now();
4472 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
4475 DOCTEST_CHECK(duration.count() < 5000);
4478 DOCTEST_CHECK(collision.isBVHValid() ==
true);
4479 DOCTEST_CHECK(collision.getPrimitiveCount() == large_geometry.size());
4481 size_t node_count, leaf_count, max_depth;
4482 collision.getBVHStatistics(node_count, leaf_count, max_depth);
4485 DOCTEST_CHECK(node_count > 0);
4486 DOCTEST_CHECK(leaf_count > 0);
4487 DOCTEST_CHECK(max_depth > 0);
4488 DOCTEST_CHECK(max_depth < 50);
4491DOCTEST_TEST_CASE(
"CollisionDetection Performance Regression - Ray Casting Throughput") {
4494 collision.disableMessages();
4495 collision.disableGPUAcceleration();
4498 for (
int i = 0; i < 1000; i++) {
4499 float x = (i % 50) * 0.2f;
4500 float y = (i / 50) * 0.2f;
4505 std::vector<CollisionDetection::RayQuery> throughput_queries;
4506 for (
int i = 0; i < 10000; i++) {
4510 throughput_queries.push_back(query);
4514 auto start_time = std::chrono::high_resolution_clock::now();
4516 std::vector<CollisionDetection::HitResult> results = collision.castRays(throughput_queries, &stats);
4517 auto end_time = std::chrono::high_resolution_clock::now();
4519 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
4522 DOCTEST_CHECK(duration.count() < 2000);
4525 DOCTEST_CHECK(results.size() == throughput_queries.size());
4529 float rays_per_second = (float) throughput_queries.size() / (duration.count() / 1000.0f);
4530 DOCTEST_CHECK(rays_per_second > 1000.0f);
4533DOCTEST_TEST_CASE(
"CollisionDetection Performance Regression - Memory Usage Validation") {
4536 collision.disableMessages();
4539 for (
int i = 0; i < 2000; i++) {
4540 float x = i * 0.05f;
4546 collision.buildBVH();
4548 auto memory_stats = collision.getBVHMemoryUsage();
4551 DOCTEST_CHECK(memory_stats.soa_memory_bytes < 100 * 1024 * 1024);
4552 DOCTEST_CHECK(memory_stats.soa_memory_bytes > 0);
4556 std::vector<CollisionDetection::RayQuery> stream_queries;
4558 for (
int i = 0; i < 5000; i++) {
4562 stream_queries.push_back(query);
4565 stream.
addRays(stream_queries);
4569 DOCTEST_CHECK(stream_memory < 50 * 1024 * 1024);
4570 DOCTEST_CHECK(stream_memory > 0);
4575DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Ray-Patch Intersection") {
4578 collision.disableMessages();
4579 collision.disableGPUAcceleration();
4594 DOCTEST_CHECK(result.
hit ==
true);
4598 DOCTEST_CHECK(std::abs(result.
distance - 1.0f) < 1e-6f);
4606DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Ray-Patch Edge Cases") {
4609 collision.disableMessages();
4610 collision.disableGPUAcceleration();
4621 DOCTEST_CHECK(edge_result.
hit ==
true);
4632 DOCTEST_CHECK(corner_result.
hit ==
true);
4638DOCTEST_TEST_CASE(
"CollisionDetection Complex Geometry - Multi-Patch Accuracy") {
4641 collision.disableMessages();
4642 collision.disableGPUAcceleration();
4645 std::vector<uint> uuids;
4648 for (
int i = 0; i < 5; i++) {
4649 for (
int j = 0; j < 5; j++) {
4653 uuids.push_back(uuid);
4658 int correct_predictions = 0;
4659 int total_predictions = 0;
4661 for (
int i = 0; i < 10; i++) {
4662 for (
int j = 0; j < 10; j++) {
4672 bool should_hit =
false;
4673 for (
int ti = 0; ti < 5; ti++) {
4674 for (
int tj = 0; tj < 5; tj++) {
4675 float px = ti * 0.8f;
4676 float py = tj * 0.8f;
4679 const float EPSILON = 1e-6f;
4680 if ((x >= px - 0.25f - EPSILON) && (x <= px + 0.25f + EPSILON) && (y >= py - 0.25f - EPSILON) && (y <= py + 0.25f + EPSILON)) {
4689 if (result.
hit == should_hit) {
4690 correct_predictions++;
4692 total_predictions++;
4697 float accuracy = (float) correct_predictions / (
float) total_predictions;
4698 DOCTEST_CHECK(accuracy == 1.0f);
4703DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Ray-Voxel Intersection") {
4706 collision.disableMessages();
4707 collision.disableGPUAcceleration();
4718 DOCTEST_CHECK(result.
hit ==
true);
4722 DOCTEST_CHECK(std::abs(result.
distance - 1.0f) < 1e-6f);
4730DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Ray-Voxel Edge Cases") {
4733 collision.disableMessages();
4734 collision.disableGPUAcceleration();
4745 DOCTEST_CHECK(edge_result.
hit ==
true);
4747 DOCTEST_CHECK(std::abs(edge_result.
distance - 1.0f) < 1e-6f);
4758 DOCTEST_CHECK(corner_result.
hit ==
true);
4764DOCTEST_TEST_CASE(
"CollisionDetection Complex Geometry - Multi-Voxel Accuracy") {
4767 collision.disableMessages();
4768 collision.disableGPUAcceleration();
4771 std::vector<uint> uuids;
4774 for (
int i = 0; i < 3; i++) {
4775 for (
int j = 0; j < 3; j++) {
4776 for (
int k = 0; k < 3; k++) {
4781 uuids.push_back(uuid);
4787 int correct_predictions = 0;
4788 int total_predictions = 0;
4790 for (
int i = 0; i < 10; i++) {
4791 for (
int j = 0; j < 10; j++) {
4801 bool should_hit =
false;
4802 for (
int vi = 0; vi < 3; vi++) {
4803 for (
int vj = 0; vj < 3; vj++) {
4804 for (
int vk = 0; vk < 3; vk++) {
4805 float vx = vi * 1.5f;
4806 float vy = vj * 1.5f;
4807 float vz = vk * 1.5f;
4810 float voxel_x_min = vx - 0.5f;
4811 float voxel_x_max = vx + 0.5f;
4812 float voxel_y_min = vy - 0.5f;
4813 float voxel_y_max = vy + 0.5f;
4814 float voxel_z_min = vz - 0.5f;
4815 float voxel_z_max = vz + 0.5f;
4818 const float EPSILON = 1e-6f;
4819 if ((x >= voxel_x_min - EPSILON) && (x <= voxel_x_max + EPSILON) && (y >= voxel_y_min - EPSILON) && (y <= voxel_y_max + EPSILON)) {
4821 if (voxel_z_max >= -1.0f - EPSILON) {
4834 if (result.
hit == should_hit) {
4835 correct_predictions++;
4837 total_predictions++;
4842 float accuracy = (float) correct_predictions / (
float) total_predictions;
4843 DOCTEST_CHECK(accuracy == 1.0f);
4846DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - Basic getVoxelRayHitCounts Functionality") {
4849 collision.disableMessages();
4855 vec3 grid_center(0, 0, 0);
4856 vec3 grid_size(4, 4, 4);
4857 int3 grid_divisions(2, 2, 2);
4860 std::vector<vec3> ray_origins;
4861 std::vector<vec3> ray_directions;
4864 ray_origins.push_back(
make_vec3(0, 0, -3));
4865 ray_directions.push_back(
make_vec3(0, 0, 1));
4868 ray_origins.push_back(
make_vec3(-1.5, -1.5, -3));
4869 ray_directions.push_back(
make_vec3(0, 0, 1));
4872 ray_origins.push_back(
make_vec3(0, 0, -0.5));
4873 ray_directions.push_back(
make_vec3(0, 0, 1));
4876 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
4879 int hit_before, hit_after, hit_inside;
4880 collision.getVoxelRayHitCounts(
make_int3(1, 1, 0), hit_before, hit_after, hit_inside);
4883 DOCTEST_CHECK(hit_before >= 0);
4884 DOCTEST_CHECK(hit_after >= 0);
4885 DOCTEST_CHECK(hit_inside >= 0);
4888 collision.getVoxelRayHitCounts(
make_int3(0, 0, 0), hit_before, hit_after, hit_inside);
4891 DOCTEST_CHECK(hit_before >= 0);
4892 DOCTEST_CHECK(hit_after >= 0);
4893 DOCTEST_CHECK(hit_inside >= 0);
4896DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - getVoxelRayPathLengths Individual Lengths") {
4899 collision.disableMessages();
4902 vec3 grid_center(0, 0, 0);
4903 vec3 grid_size(2, 2, 2);
4904 int3 grid_divisions(1, 1, 1);
4907 std::vector<vec3> ray_origins;
4908 std::vector<vec3> ray_directions;
4911 ray_origins.push_back(
make_vec3(0, 0, -2));
4912 ray_directions.push_back(
make_vec3(0, 0, 1));
4915 ray_origins.push_back(
make_vec3(-2, -2, -2));
4916 ray_directions.push_back(normalize(
make_vec3(1, 1, 1)));
4919 ray_origins.push_back(
make_vec3(0, -2, -2));
4920 ray_directions.push_back(normalize(
make_vec3(0, 1, 1)));
4922 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
4925 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(
make_int3(0, 0, 0));
4928 DOCTEST_CHECK(path_lengths.size() >= 1);
4931 float max_diagonal = 2.0f * sqrt(3.0f);
4932 for (
float length: path_lengths) {
4933 DOCTEST_CHECK(length > 0.0f);
4934 DOCTEST_CHECK(length <= max_diagonal + 1e-6f);
4938 bool found_center_ray =
false;
4939 bool found_diagonal_ray =
false;
4941 for (
float length: path_lengths) {
4942 if (std::abs(length - 2.0f) < 0.1f) {
4943 found_center_ray =
true;
4945 if (std::abs(length - 2.0f * sqrt(3.0f)) < 0.1f) {
4946 found_diagonal_ray =
true;
4950 DOCTEST_CHECK(found_center_ray);
4953DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - Beer's Law Scenario with Geometry") {
4956 collision.disableMessages();
4962 vec3 grid_center(0, 0, 0);
4963 vec3 grid_size(6, 6, 6);
4964 int3 grid_divisions(3, 3, 3);
4967 std::vector<vec3> ray_origins;
4968 std::vector<vec3> ray_directions;
4970 int num_rays_per_axis = 10;
4971 for (
int i = 0; i < num_rays_per_axis; i++) {
4972 for (
int j = 0; j < num_rays_per_axis; j++) {
4973 float x = -2.5f + (5.0f * i) / (num_rays_per_axis - 1);
4974 float y = -2.5f + (5.0f * j) / (num_rays_per_axis - 1);
4976 ray_origins.push_back(
make_vec3(x, y, -4));
4977 ray_directions.push_back(
make_vec3(0, 0, 1));
4981 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
4984 int hit_before, hit_after, hit_inside;
4985 collision.getVoxelRayHitCounts(
make_int3(1, 1, 1), hit_before, hit_after, hit_inside);
4992 DOCTEST_CHECK(hit_before >= 0);
4993 DOCTEST_CHECK(hit_after >= 0);
4994 DOCTEST_CHECK(hit_inside >= 0);
4995 DOCTEST_CHECK((hit_before + hit_after + hit_inside) > 0);
4998 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(
make_int3(1, 1, 1));
4999 DOCTEST_CHECK(path_lengths.size() > 0);
5002 collision.getVoxelRayHitCounts(
make_int3(0, 0, 0), hit_before, hit_after, hit_inside);
5006 DOCTEST_CHECK(hit_before >= 0);
5007 DOCTEST_CHECK(hit_after >= 0);
5008 DOCTEST_CHECK(hit_inside >= 0);
5011DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - Edge Cases and Boundary Conditions") {
5014 collision.disableMessages();
5020 vec3 grid_center(0, 0, 0);
5021 vec3 grid_size(4, 4, 4);
5022 int3 grid_divisions(2, 2, 2);
5024 std::vector<vec3> ray_origins;
5025 std::vector<vec3> ray_directions;
5028 ray_origins.push_back(
make_vec3(0, 0, -0.5));
5029 ray_directions.push_back(
make_vec3(0, 0, 1));
5032 ray_origins.push_back(
make_vec3(-1.99, -1.99, -3));
5033 ray_directions.push_back(
make_vec3(0, 0, 1));
5036 ray_origins.push_back(
make_vec3(-2, 0, 0));
5037 ray_directions.push_back(
make_vec3(1, 0, 0));
5040 ray_origins.push_back(
make_vec3(-2, -2, -2));
5041 ray_directions.push_back(normalize(
make_vec3(1, 1, 1)));
5043 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
5046 for (
int i = 0; i < grid_divisions.
x; i++) {
5047 for (
int j = 0; j < grid_divisions.
y; j++) {
5048 for (
int k = 0; k < grid_divisions.
z; k++) {
5049 int hit_before, hit_after, hit_inside;
5053 collision.getVoxelRayHitCounts(voxel_idx, hit_before, hit_after, hit_inside);
5056 DOCTEST_CHECK(hit_before >= 0);
5057 DOCTEST_CHECK(hit_after >= 0);
5058 DOCTEST_CHECK(hit_inside >= 0);
5061 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(voxel_idx);
5062 for (
float length: path_lengths) {
5063 DOCTEST_CHECK(length > 0.0f);
5064 DOCTEST_CHECK(length < 100.0f);
5071DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - Error Handling and Invalid Inputs") {
5074 collision.disableMessages();
5080 bool caught_exception =
false;
5082 int hit_before, hit_after, hit_inside;
5083 collision.getVoxelRayHitCounts(
make_int3(-1, 0, 0), hit_before, hit_after, hit_inside);
5084 }
catch (
const std::exception &e) {
5085 caught_exception =
true;
5086 DOCTEST_CHECK(std::string(e.what()).find(
"Invalid voxel indices") != std::string::npos);
5088 DOCTEST_CHECK(caught_exception);
5091 caught_exception =
false;
5093 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(
make_int3(100, 100, 100));
5094 }
catch (
const std::exception &e) {
5095 caught_exception =
true;
5096 DOCTEST_CHECK(std::string(e.what()).find(
"Invalid voxel indices") != std::string::npos);
5098 DOCTEST_CHECK(caught_exception);
5101 int hit_before, hit_after, hit_inside;
5102 collision.getVoxelRayHitCounts(
make_int3(0, 0, 0), hit_before, hit_after, hit_inside);
5105 DOCTEST_CHECK(hit_before == 0);
5106 DOCTEST_CHECK(hit_after == 0);
5107 DOCTEST_CHECK(hit_inside == 0);
5109 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(
make_int3(0, 0, 0));
5110 DOCTEST_CHECK(path_lengths.empty());
5113DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - Beer's Law Integration Test") {
5116 collision.disableMessages();
5120 std::vector<uint> vegetation_uuids;
5123 for (
int i = 0; i < 3; i++) {
5124 for (
int j = 0; j < 3; j++) {
5125 if ((i + j) % 2 == 0) {
5126 float x = -2.0f + i * 2.0f;
5127 float y = -2.0f + j * 2.0f;
5128 float z = 1.0f + i * 0.5f;
5131 vegetation_uuids.push_back(patch_uuid);
5136 vec3 grid_center(0, 0, 0);
5137 vec3 grid_size(8, 8, 6);
5138 int3 grid_divisions(4, 4, 3);
5141 std::vector<vec3> ray_origins;
5142 std::vector<vec3> ray_directions;
5144 int rays_per_axis = 20;
5145 for (
int i = 0; i < rays_per_axis; i++) {
5146 for (
int j = 0; j < rays_per_axis; j++) {
5147 float x = -3.5f + (7.0f * i) / (rays_per_axis - 1);
5148 float y = -3.5f + (7.0f * j) / (rays_per_axis - 1);
5150 ray_origins.push_back(
make_vec3(x, y, -3));
5151 ray_directions.push_back(
make_vec3(0, 0, 1));
5155 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
5158 bool found_realistic_data =
false;
5160 for (
int i = 0; i < grid_divisions.
x; i++) {
5161 for (
int j = 0; j < grid_divisions.
y; j++) {
5162 for (
int k = 0; k < grid_divisions.
z; k++) {
5163 int hit_before, hit_after, hit_inside;
5164 collision.getVoxelRayHitCounts(
make_int3(i, j, k), hit_before, hit_after, hit_inside);
5166 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(
make_int3(i, j, k));
5168 if (!path_lengths.empty() && (hit_before + hit_after + hit_inside) > 0) {
5169 found_realistic_data =
true;
5172 int P_denom = path_lengths.size();
5173 int P_trans = P_denom - hit_inside;
5175 DOCTEST_CHECK(P_trans >= 0);
5176 DOCTEST_CHECK(P_trans <= P_denom);
5179 float transmission_probability =
static_cast<float>(P_trans) /
static_cast<float>(P_denom);
5180 DOCTEST_CHECK(transmission_probability >= 0.0f);
5181 DOCTEST_CHECK(transmission_probability <= 1.0f);
5184 if (hit_inside > 0) {
5185 DOCTEST_CHECK(transmission_probability < 1.0f);
5190 float total_path_length = 0.0f;
5191 for (
float length: path_lengths) {
5192 total_path_length += length;
5194 float r_bar = total_path_length / P_denom;
5196 DOCTEST_CHECK(r_bar > 0.0f);
5197 DOCTEST_CHECK(r_bar < 10.0f);
5201 if (P_trans < P_denom && P_trans > 0) {
5202 float ln_arg =
static_cast<float>(P_trans) /
static_cast<float>(P_denom);
5203 DOCTEST_CHECK(ln_arg > 0.0f);
5204 DOCTEST_CHECK(ln_arg <= 1.0f);
5211 DOCTEST_CHECK(found_realistic_data);
5214DOCTEST_TEST_CASE(
"CollisionDetection calculateVoxelPathLengths Enhanced Method") {
5223 std::vector<vec3> ray_directions;
5224 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.0f, 0.0f)));
5225 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.1f, 0.0f)));
5226 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.0f, 0.1f)));
5229 std::vector<vec3> voxel_centers;
5230 std::vector<vec3> voxel_sizes;
5232 voxel_centers.push_back(
make_vec3(2.0f, 0.0f, 0.0f));
5233 voxel_centers.push_back(
make_vec3(5.0f, 0.0f, 0.0f));
5234 voxel_centers.push_back(
make_vec3(2.0f, 3.0f, 0.0f));
5236 voxel_sizes.push_back(
make_vec3(1.0f, 1.0f, 1.0f));
5237 voxel_sizes.push_back(
make_vec3(1.0f, 1.0f, 1.0f));
5238 voxel_sizes.push_back(
make_vec3(1.0f, 1.0f, 1.0f));
5240 auto result = collision.calculateVoxelPathLengths(scan_origin, ray_directions, voxel_centers, voxel_sizes);
5243 DOCTEST_CHECK(result.size() == 3);
5246 DOCTEST_CHECK(result[0].size() > 0);
5249 DOCTEST_CHECK(result[1].size() > 0);
5253 DOCTEST_CHECK(result[2].size() >= 0);
5256 for (
size_t voxel_idx = 0; voxel_idx < 2; ++voxel_idx) {
5257 for (
const auto &hit: result[voxel_idx]) {
5258 DOCTEST_CHECK(hit.path_length > 0.0f);
5259 DOCTEST_CHECK(hit.path_length <= 2.0f);
5260 DOCTEST_CHECK(hit.hit ==
false);
5261 DOCTEST_CHECK(hit.distance == -1.0f);
5262 DOCTEST_CHECK(hit.primitive_UUID == 0);
5272 std::vector<vec3> ray_directions;
5273 for (
int i = 0; i < 1000; ++i) {
5274 float theta = i * 0.01f;
5275 ray_directions.push_back(normalize(
make_vec3(1.0f, sin(theta), cos(theta))));
5279 std::vector<vec3> voxel_centers;
5280 std::vector<vec3> voxel_sizes;
5281 for (
int x = 0; x < 10; ++x) {
5282 for (
int y = 0; y < 10; ++y) {
5283 voxel_centers.push_back(
make_vec3(x + 1.0f, y - 5.0f, 0.0f));
5284 voxel_sizes.push_back(
make_vec3(0.5f, 0.5f, 0.5f));
5289 auto start_time = std::chrono::high_resolution_clock::now();
5290 auto result = collision.calculateVoxelPathLengths(scan_origin, ray_directions, voxel_centers, voxel_sizes);
5291 auto end_time = std::chrono::high_resolution_clock::now();
5293 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
5296 DOCTEST_CHECK(duration.count() < 500);
5299 DOCTEST_CHECK(result.size() == 100);
5302 size_t total_intersections = 0;
5303 for (
size_t i = 0; i < 100; ++i) {
5304 total_intersections += result[i].size();
5305 for (
const auto &hit: result[i]) {
5306 DOCTEST_CHECK(hit.path_length > 0.0f);
5307 DOCTEST_CHECK(hit.path_length <= 1.0f);
5312 DOCTEST_CHECK(total_intersections > 0);
5320 std::vector<vec3> empty_rays;
5321 std::vector<vec3> voxel_centers = {
make_vec3(1.0f, 0.0f, 0.0f)};
5322 std::vector<vec3> voxel_sizes = {
make_vec3(1.0f, 1.0f, 1.0f)};
5324 auto result = collision.calculateVoxelPathLengths(scan_origin, empty_rays, voxel_centers, voxel_sizes);
5325 DOCTEST_CHECK(result.empty());
5328 std::vector<vec3> ray_directions = {normalize(
make_vec3(1.0f, 0.0f, 0.0f))};
5329 std::vector<vec3> empty_voxels;
5330 std::vector<vec3> empty_sizes;
5332 result = collision.calculateVoxelPathLengths(scan_origin, ray_directions, empty_voxels, empty_sizes);
5333 DOCTEST_CHECK(result.empty());
5336 std::vector<vec3> mismatched_sizes = {
make_vec3(1.0f, 1.0f, 1.0f),
make_vec3(2.0f, 2.0f, 2.0f)};
5339 bool threw_exception =
false;
5341 collision.calculateVoxelPathLengths(scan_origin, ray_directions, voxel_centers, mismatched_sizes);
5342 }
catch (
const std::exception &) {
5343 threw_exception =
true;
5345 DOCTEST_CHECK(threw_exception);
5351 vec3 ray_direction = normalize(
make_vec3(1.0f, 0.0f, 0.0f));
5355 auto result = collision.calculateVoxelPathLengths(scan_origin, {ray_direction}, {voxel_center}, {voxel_size});
5357 DOCTEST_CHECK(result.size() == 1);
5358 DOCTEST_CHECK(result[0].size() == 1);
5362 DOCTEST_CHECK(std::abs(path_length - 2.0f) < 1e-4f);
5368 std::vector<vec3> ray_directions;
5371 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.0f, 0.0f)));
5372 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.2f, 0.0f)));
5373 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.0f, 0.2f)));
5378 auto result = collision.calculateVoxelPathLengths(scan_origin, ray_directions, {voxel_center}, {voxel_size});
5381 DOCTEST_CHECK(result.size() == 1);
5382 DOCTEST_CHECK(result[0].size() == 3);
5385 std::vector<float> path_lengths;
5386 for (
const auto &hit: result[0]) {
5387 path_lengths.push_back(hit.path_length);
5391 for (
float path: path_lengths) {
5392 DOCTEST_CHECK(path > 0.5f);
5393 DOCTEST_CHECK(path < 2.0f);
5398 DOCTEST_CHECK(!(path_lengths[0] == path_lengths[1] && path_lengths[1] == path_lengths[2]));
5404 std::vector<vec3> ray_directions = {normalize(
make_vec3(1.0f, 0.0f, 0.0f)), normalize(
make_vec3(1.0f, 0.1f, 0.0f)), normalize(
make_vec3(1.0f, 0.0f, 0.1f))};
5406 std::vector<vec3> voxel_centers = {
make_vec3(2.0f, 0.0f, 0.0f),
make_vec3(5.0f, 0.0f, 0.0f)};
5408 std::vector<vec3> voxel_sizes = {
make_vec3(1.0f, 1.0f, 1.0f),
make_vec3(1.0f, 1.0f, 1.0f)};
5411 auto result = collision.calculateVoxelPathLengths(scan_origin, ray_directions, voxel_centers, voxel_sizes);
5414 for (
size_t c = 0; c < voxel_centers.size(); ++c) {
5415 std::vector<float> dr_agg;
5416 uint hit_after_agg = 0;
5419 for (
const auto &hit: result[c]) {
5420 dr_agg.push_back(hit.path_length);
5425 DOCTEST_CHECK(hit_after_agg == result[c].size());
5426 for (
float path_length: dr_agg) {
5427 DOCTEST_CHECK(path_length > 0.0f);
5428 DOCTEST_CHECK(path_length <= 2.0f);