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());
158 collision.disableMessages();
166 collision.enableGPUAcceleration();
167 gpu_enabled = collision.isGPUAccelerationEnabled();
172 collision.buildBVH();
175 DOCTEST_INFO(
"GPU acceleration capability test - actual hardware dependent");
177 DOCTEST_WARN(
"GPU acceleration is available and enabled on this system");
179 DOCTEST_WARN(
"GPU acceleration requested but not available - using CPU fallback");
183 DOCTEST_CHECK_NOTHROW(collision.disableGPUAcceleration());
184 DOCTEST_CHECK(collision.isGPUAccelerationEnabled() ==
false);
186 }
catch (std::exception &e) {
188 DOCTEST_WARN((std::string(
"GPU acceleration test failed (expected on non-NVIDIA systems): ") + e.what()).c_str());
191 DOCTEST_CHECK_NOTHROW(collision.disableGPUAcceleration());
192 DOCTEST_CHECK(collision.isGPUAccelerationEnabled() ==
false);
197DOCTEST_TEST_CASE(
"CollisionDetection BVH Construction") {
202 collision.disableMessages();
210 collision.buildBVH();
212 DOCTEST_CHECK(collision.isBVHValid() ==
true);
213 DOCTEST_CHECK(collision.getPrimitiveCount() == 3);
217DOCTEST_TEST_CASE(
"CollisionDetection Basic Collision Detection") {
222 collision.disableMessages();
223 collision.disableGPUAcceleration();
232 collision.buildBVH();
235 std::vector<uint> collisions1 = collision.findCollisions(UUID1);
238 bool found_UUID2 = std::find(collisions1.begin(), collisions1.end(), UUID2) != collisions1.end();
239 bool found_UUID3 = std::find(collisions1.begin(), collisions1.end(), UUID3) != collisions1.end();
241 DOCTEST_CHECK(found_UUID2 ==
true);
242 DOCTEST_CHECK(found_UUID3 ==
false);
246DOCTEST_TEST_CASE(
"CollisionDetection BVH Statistics") {
251 collision.disableMessages();
254 for (
int i = 0; i < 20; i++) {
258 collision.buildBVH();
260 size_t node_count, leaf_count, max_depth;
261 collision.getBVHStatistics(node_count, leaf_count, max_depth);
263 DOCTEST_CHECK(node_count > 0);
264 DOCTEST_CHECK(leaf_count > 0);
266 DOCTEST_CHECK(max_depth >= 0);
270DOCTEST_TEST_CASE(
"CollisionDetection Empty Geometry Handling") {
275 collision.disableMessages();
278 collision.buildBVH();
281 DOCTEST_CHECK(collision.getPrimitiveCount() == 0);
284 std::vector<uint> collisions = collision.findCollisions(std::vector<uint>{});
286 DOCTEST_CHECK(collisions.empty() ==
true);
290DOCTEST_TEST_CASE(
"CollisionDetection Invalid UUID Handling") {
295 collision.disableMessages();
300 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(999999), std::runtime_error);
304 collision.findCollisions(999999);
305 DOCTEST_FAIL(
"Expected exception was not thrown");
306 }
catch (
const std::runtime_error &e) {
307 std::string error_msg = e.what();
308 bool has_relevant_content = error_msg.find(
"UUID") != std::string::npos || error_msg.find(
"invalid") != std::string::npos;
309 DOCTEST_CHECK(has_relevant_content);
314DOCTEST_TEST_CASE(
"CollisionDetection GPU/CPU Mode Switching") {
319 collision.disableMessages();
324 collision.buildBVH();
327 std::vector<uint> gpu_results;
330 collision.enableGPUAcceleration();
331 gpu_results = collision.findCollisions(UUID1);
335 collision.disableGPUAcceleration();
336 std::vector<uint> cpu_results = collision.findCollisions(UUID1);
339 std::sort(gpu_results.begin(), gpu_results.end());
340 std::sort(cpu_results.begin(), cpu_results.end());
342 DOCTEST_CHECK(gpu_results == cpu_results);
346DOCTEST_TEST_CASE(
"CollisionDetection Null Context Error Handling") {
353 DOCTEST_FAIL(
"Expected exception was not thrown");
354 }
catch (
const std::runtime_error &e) {
355 std::string error_msg = e.what();
356 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;
357 DOCTEST_CHECK(has_relevant_content);
362DOCTEST_TEST_CASE(
"CollisionDetection Invalid UUIDs in BuildBVH") {
367 collision.disableMessages();
370 std::vector<uint> invalid_UUIDs = {999999, 888888};
372 DOCTEST_CHECK_THROWS_AS(collision.buildBVH(invalid_UUIDs), std::runtime_error);
376 collision.buildBVH(invalid_UUIDs);
377 DOCTEST_FAIL(
"Expected exception was not thrown");
378 }
catch (
const std::runtime_error &e) {
379 std::string error_msg = e.what();
380 bool has_relevant_content = error_msg.find(
"UUID") != std::string::npos || error_msg.find(
"invalid") != std::string::npos;
381 DOCTEST_CHECK(has_relevant_content);
386DOCTEST_TEST_CASE(
"CollisionDetection Primitive/Object Collision Detection") {
391 collision.disableMessages();
392 collision.disableGPUAcceleration();
401 collision.buildBVH();
404 std::vector<uint> primitive_UUIDs = {UUID1};
405 std::vector<uint> object_IDs = {objID};
407 DOCTEST_CHECK_NOTHROW(collision.findCollisions(primitive_UUIDs, object_IDs));
411DOCTEST_TEST_CASE(
"CollisionDetection Empty Input Handling") {
416 collision.disableMessages();
419 std::vector<uint> empty_primitives;
420 std::vector<uint> collisions1 = collision.findCollisions(empty_primitives);
423 std::vector<uint> empty_objects;
424 std::vector<uint> collisions2 = collision.findCollisions(empty_primitives, empty_objects);
426 DOCTEST_CHECK(collisions1.empty() ==
true);
427 DOCTEST_CHECK(collisions2.empty() ==
true);
431DOCTEST_TEST_CASE(
"CollisionDetection Invalid Object ID Error Handling") {
436 collision.disableMessages();
439 std::vector<uint> empty_primitives;
440 std::vector<uint> invalid_objects = {999999};
442 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(empty_primitives, invalid_objects), std::runtime_error);
446 collision.findCollisions(empty_primitives, invalid_objects);
447 DOCTEST_FAIL(
"Expected exception was not thrown");
448 }
catch (
const std::runtime_error &e) {
449 std::string error_msg = e.what();
450 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 ||
451 error_msg.find(
"invalid") != std::string::npos;
452 DOCTEST_CHECK(has_relevant_content);
457DOCTEST_TEST_CASE(
"CollisionDetection Manual BVH Rebuild") {
462 collision.disableMessages();
466 collision.buildBVH();
468 size_t initial_count = collision.getPrimitiveCount();
474 collision.rebuildBVH();
476 size_t final_count = collision.getPrimitiveCount();
478 DOCTEST_CHECK(final_count == 2);
482DOCTEST_TEST_CASE(
"CollisionDetection Message Control") {
487 collision.disableMessages();
490 DOCTEST_CHECK_NOTHROW(collision.disableMessages());
491 DOCTEST_CHECK_NOTHROW(collision.enableMessages());
492 collision.disableMessages();
496DOCTEST_TEST_CASE(
"CollisionDetection Large Geometry Handling") {
501 collision.disableMessages();
502 collision.disableGPUAcceleration();
505 for (
int i = 0; i < 50; i++) {
509 collision.buildBVH();
513 std::vector<uint> collisions = collision.findCollisions(UUID);
516 size_t node_count, leaf_count, max_depth;
517 collision.getBVHStatistics(node_count, leaf_count, max_depth);
519 DOCTEST_CHECK(collision.getPrimitiveCount() == 50);
520 DOCTEST_CHECK(node_count > 0);
522 DOCTEST_CHECK(max_depth >= 0);
526DOCTEST_TEST_CASE(
"CollisionDetection Single Primitive Edge Case") {
531 collision.disableMessages();
532 collision.disableGPUAcceleration();
537 collision.buildBVH();
540 std::vector<uint> collisions = collision.findCollisions(UUID1);
543 size_t node_count, leaf_count, max_depth;
544 collision.getBVHStatistics(node_count, leaf_count, max_depth);
546 DOCTEST_CHECK(collision.getPrimitiveCount() == 1);
547 DOCTEST_CHECK(collision.isBVHValid() ==
true);
551DOCTEST_TEST_CASE(
"CollisionDetection Overlapping AABB Primitives") {
556 collision.disableMessages();
557 collision.disableGPUAcceleration();
564 collision.buildBVH();
567 std::vector<uint> collisions = collision.findCollisions(UUID1);
569 bool found_collision = std::find(collisions.begin(), collisions.end(), UUID2) != collisions.end();
571 DOCTEST_CHECK(found_collision ==
true);
575DOCTEST_TEST_CASE(
"CollisionDetection BVH Validity Persistence") {
580 collision.disableMessages();
583 DOCTEST_CHECK(collision.isBVHValid() ==
false);
587 collision.buildBVH();
590 DOCTEST_CHECK(collision.isBVHValid() ==
true);
594DOCTEST_TEST_CASE(
"CollisionDetection Soft/Hard Detection Integration - BVH Sharing") {
598 collision.disableMessages();
599 collision.disableGPUAcceleration();
607 std::vector<uint> all_geometry = {obstacle, soft_prim1, soft_prim2};
608 collision.buildBVH(all_geometry);
611 DOCTEST_CHECK(collision.isBVHValid() ==
true);
612 size_t initial_node_count, initial_leaf_count, initial_max_depth;
613 collision.getBVHStatistics(initial_node_count, initial_leaf_count, initial_max_depth);
616 std::vector<uint> soft_collisions = collision.findCollisions({soft_prim1, soft_prim2});
617 bool soft_detection_completed =
true;
623 vec3 obstacle_direction;
626 bool hard_hit = collision.findNearestSolidObstacleInCone(test_origin, test_direction, 0.52f, 1.0f, {obstacle}, distance, obstacle_direction);
629 DOCTEST_CHECK(soft_detection_completed ==
true);
630 DOCTEST_CHECK(hard_hit ==
true);
631 DOCTEST_CHECK(distance < 1.0f);
634 size_t final_node_count, final_leaf_count, final_max_depth;
635 collision.getBVHStatistics(final_node_count, final_leaf_count, final_max_depth);
637 DOCTEST_CHECK(initial_node_count == final_node_count);
638 DOCTEST_CHECK(initial_leaf_count == final_leaf_count);
639 DOCTEST_CHECK(collision.isBVHValid() ==
true);
643DOCTEST_TEST_CASE(
"CollisionDetection Soft/Hard Detection Integration - Sequential Calls") {
647 collision.disableMessages();
648 collision.disableGPUAcceleration();
655 std::vector<uint> all_obstacles = {ground, wall};
656 std::vector<uint> plant_parts = {plant_stem};
658 collision.buildBVH(all_obstacles);
661 std::vector<uint> soft_collisions = collision.findCollisions(plant_parts, {}, all_obstacles, {});
670 vec3 obstacle_direction;
671 bool hard_hit = collision.findNearestSolidObstacleInCone(growth_tip, growth_direction, 0.35f, 2.0f,
672 all_obstacles, distance, obstacle_direction);
675 std::vector<uint> soft_collisions_2 = collision.findCollisions(plant_parts, {}, all_obstacles, {});
681 bool hard_hit_2 = collision.findNearestSolidObstacleInCone(growth_tip, growth_direction_2, 0.35f, 0.5f, all_obstacles, distance, obstacle_direction);
684 DOCTEST_CHECK(soft_collisions.size() == soft_collisions_2.size());
685 DOCTEST_CHECK(hard_hit ==
true);
686 DOCTEST_CHECK(collision.isBVHValid() ==
true);
690DOCTEST_TEST_CASE(
"CollisionDetection Soft/Hard Detection Integration - Different Geometry Sets") {
694 collision.disableMessages();
695 collision.disableGPUAcceleration();
707 std::vector<uint> all_geometry = {hard_obstacle_1, hard_obstacle_2, soft_object_1, soft_object_2, shared_object};
708 collision.buildBVH(all_geometry);
715 std::vector<uint> hard_only = {hard_obstacle_1, hard_obstacle_2, shared_object};
717 vec3 obstacle_direction;
719 bool hard_hit = collision.findNearestSolidObstacleInCone(test_origin, test_direction, 0.4f, 2.0f, hard_only, distance, obstacle_direction);
722 std::vector<uint> soft_only = {soft_object_1, soft_object_2, shared_object};
723 std::vector<uint> soft_collisions = collision.findCollisions(soft_only);
729 bool hard_hit_2 = collision.findNearestSolidObstacleInCone(test_origin, test_direction_2, 0.4f, 10.0f,
730 soft_only, distance, obstacle_direction);
733 DOCTEST_CHECK(hard_hit ==
true);
734 DOCTEST_CHECK(hard_hit_2 ==
true);
735 DOCTEST_CHECK(collision.isBVHValid() ==
true);
738 DOCTEST_CHECK(collision.getPrimitiveCount() == all_geometry.size());
742DOCTEST_TEST_CASE(
"CollisionDetection Soft/Hard Detection Integration - BVH Rebuild Behavior") {
746 collision.disableMessages();
747 collision.disableGPUAcceleration();
751 std::vector<uint> initial_geometry = {obstacle1};
753 collision.buildBVH(initial_geometry);
759 vec3 obstacle_direction;
761 bool hit_initial = collision.findNearestSolidObstacleInCone(test_origin, test_direction, 0.3f, 1.0f, initial_geometry, distance, obstacle_direction);
767 std::vector<uint> expanded_geometry = {obstacle1, obstacle2, obstacle3};
770 collision.buildBVH(expanded_geometry);
773 std::vector<uint> soft_collisions = collision.findCollisions(expanded_geometry);
775 bool hit_after_rebuild = collision.findNearestSolidObstacleInCone(test_origin, test_direction, 0.3f, 1.0f, expanded_geometry, distance, obstacle_direction);
781 bool hit_subset = collision.findNearestSolidObstacleInCone(test_origin, test_direction_2, 0.5f, 2.0f, {obstacle2}, distance, obstacle_direction);
784 DOCTEST_CHECK(hit_initial ==
true);
785 DOCTEST_CHECK(hit_after_rebuild ==
true);
786 DOCTEST_CHECK(hit_subset ==
true);
787 DOCTEST_CHECK(collision.isBVHValid() ==
true);
788 DOCTEST_CHECK(collision.getPrimitiveCount() == expanded_geometry.size());
792DOCTEST_TEST_CASE(
"CollisionDetection GPU Acceleration") {
797 collision.disableMessages();
800 for (
int i = 0; i < 5; i++) {
806 collision.disableGPUAcceleration();
807 collision.buildBVH();
809 std::vector<uint> cpu_results = collision.findCollisions(UUID);
811 std::vector<uint> gpu_results;
814 collision.enableGPUAcceleration();
815 collision.buildBVH();
816 gpu_results = collision.findCollisions(UUID);
820 std::sort(cpu_results.begin(), cpu_results.end());
821 std::sort(gpu_results.begin(), gpu_results.end());
824 DOCTEST_INFO(
"GPU/CPU result comparison - GPU may not be available on this system");
825 if (cpu_results.size() == gpu_results.size()) {
826 bool results_match =
true;
827 for (
size_t i = 0; i < cpu_results.size(); i++) {
828 if (cpu_results[i] != gpu_results[i]) {
829 results_match =
false;
834 if (!results_match) {
835 DOCTEST_WARN(
"GPU/CPU results differ - may be expected if no CUDA device");
838 }
catch (std::exception &e) {
840 DOCTEST_WARN((std::string(
"GPU test failed (may be expected): ") + e.what()).c_str());
845DOCTEST_TEST_CASE(
"CollisionDetection GPU/CPU Message Display") {
850 collision.disableMessages();
854 collision.buildBVH();
857 collision.enableMessages();
858 collision.disableGPUAcceleration();
861 std::string cpu_output;
862 std::vector<uint> cpu_results;
865 cpu_results = collision.findCollisions(UUID1);
875 std::string gpu_output;
876 std::vector<uint> gpu_results;
880 collision.enableGPUAcceleration();
881 gpu_results = collision.findCollisions(UUID1);
890 collision.disableMessages();
892 std::string silent_output;
893 std::vector<uint> silent_results;
896 silent_results = collision.findCollisions(UUID1);
901 DOCTEST_CHECK(silent_output.find(
"Using GPU acceleration") == std::string::npos);
902 DOCTEST_CHECK(silent_output.find(
"Using CPU traversal") == std::string::npos);
906DOCTEST_TEST_CASE(
"CollisionDetection Automatic BVH Building") {
911 collision.disableMessages();
912 collision.disableGPUAcceleration();
918 DOCTEST_CHECK(collision.isBVHValid() ==
false);
921 std::vector<uint> results = collision.findCollisions(UUID1);
924 DOCTEST_CHECK(collision.isBVHValid() ==
true);
925 DOCTEST_CHECK(collision.getPrimitiveCount() == 1);
931 DOCTEST_CHECK(collision.isBVHValid() ==
false);
934 results = collision.findCollisions(UUID1);
937 DOCTEST_CHECK(collision.getPrimitiveCount() == 2);
938 DOCTEST_CHECK(collision.isBVHValid() ==
true);
941 size_t count_before = collision.getPrimitiveCount();
942 results = collision.findCollisions(UUID1);
943 size_t count_after = collision.getPrimitiveCount();
946 DOCTEST_CHECK(count_before == count_after);
950DOCTEST_TEST_CASE(
"CollisionDetection Restricted Geometry - UUIDs Only") {
955 collision.disableMessages();
956 collision.disableGPUAcceleration();
965 std::vector<uint> all_results = collision.findCollisions(UUID1);
968 bool found_UUID2_all = std::find(all_results.begin(), all_results.end(), UUID2) != all_results.end();
969 bool found_UUID3_all = std::find(all_results.begin(), all_results.end(), UUID3) != all_results.end();
971 DOCTEST_CHECK(found_UUID2_all ==
true);
972 DOCTEST_CHECK(found_UUID3_all ==
true);
975 std::vector<uint> query_UUIDs = {UUID1};
976 std::vector<uint> query_objects = {};
977 std::vector<uint> target_UUIDs = {UUID2};
978 std::vector<uint> target_objects = {};
980 std::vector<uint> restricted_results = collision.findCollisions(query_UUIDs, query_objects, target_UUIDs, target_objects);
983 bool found_UUID2_restricted = std::find(restricted_results.begin(), restricted_results.end(), UUID2) != restricted_results.end();
984 bool found_UUID3_restricted = std::find(restricted_results.begin(), restricted_results.end(), UUID3) != restricted_results.end();
986 DOCTEST_CHECK(found_UUID2_restricted ==
true);
987 DOCTEST_CHECK(found_UUID3_restricted ==
false);
991DOCTEST_TEST_CASE(
"CollisionDetection Restricted Geometry - Object IDs") {
996 collision.disableMessages();
997 collision.disableGPUAcceleration();
1007 std::vector<uint> query_UUIDs = {UUID1};
1008 std::vector<uint> query_objects = {};
1009 std::vector<uint> target_UUIDs = {};
1010 std::vector<uint> target_objects = {objID1};
1012 DOCTEST_CHECK_NOTHROW(collision.findCollisions(query_UUIDs, query_objects, target_UUIDs, target_objects));
1015 std::vector<uint> mixed_target_UUIDs = {UUID1};
1016 std::vector<uint> mixed_target_objects = {objID1};
1018 DOCTEST_CHECK_NOTHROW(collision.findCollisions(query_UUIDs, query_objects, mixed_target_UUIDs, mixed_target_objects));
1022DOCTEST_TEST_CASE(
"CollisionDetection Restricted Geometry - Error Handling") {
1027 collision.disableMessages();
1033 std::vector<uint> invalid_query_UUIDs = {999999};
1034 std::vector<uint> query_objects = {};
1035 std::vector<uint> valid_target_UUIDs = {UUID1};
1036 std::vector<uint> target_objects = {};
1038 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(invalid_query_UUIDs, query_objects, valid_target_UUIDs, target_objects), std::runtime_error);
1041 std::vector<uint> valid_query_UUIDs = {UUID1};
1042 std::vector<uint> invalid_target_UUIDs = {999999};
1044 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(valid_query_UUIDs, query_objects, invalid_target_UUIDs, target_objects), std::runtime_error);
1047 std::vector<uint> invalid_query_objects = {999999};
1049 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(valid_query_UUIDs, invalid_query_objects, valid_target_UUIDs, target_objects), std::runtime_error);
1052 std::vector<uint> invalid_target_objects = {999999};
1054 DOCTEST_CHECK_THROWS_AS(collision.findCollisions(valid_query_UUIDs, query_objects, valid_target_UUIDs, invalid_target_objects), std::runtime_error);
1058 collision.findCollisions(invalid_query_UUIDs, query_objects, valid_target_UUIDs, target_objects);
1059 DOCTEST_FAIL(
"Expected exception was not thrown");
1060 }
catch (
const std::runtime_error &e) {
1061 std::string error_msg = e.what();
1062 bool has_relevant_content = error_msg.find(
"UUID") != std::string::npos || error_msg.find(
"invalid") != std::string::npos;
1063 DOCTEST_CHECK(has_relevant_content);
1067DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Basic Functionality") {
1072 collision.disableMessages();
1082 float half_angle =
M_PI / 4.0f;
1094DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Gap Detection") {
1099 collision.disableMessages();
1109 float half_angle =
M_PI / 3.0f;
1126 DOCTEST_CHECK(gap_result.
confidence >= 0.0f);
1127 DOCTEST_CHECK(gap_result.
confidence <= 1.0f);
1128 DOCTEST_CHECK(dense_result.
confidence >= 0.0f);
1129 DOCTEST_CHECK(dense_result.
confidence <= 1.0f);
1130 DOCTEST_CHECK(sparse_result.
confidence >= 0.0f);
1131 DOCTEST_CHECK(sparse_result.
confidence <= 1.0f);
1134 float gap_deviation = acosf(std::max(-1.0f, std::min(1.0f, gap_result.
direction * central_axis)));
1135 float dense_deviation = acosf(std::max(-1.0f, std::min(1.0f, dense_result.
direction * central_axis)));
1137 DOCTEST_CHECK(gap_deviation >= 0.0f);
1138 DOCTEST_CHECK(dense_deviation >= 0.0f);
1141DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Edge Cases") {
1146 collision.disableMessages();
1155 DOCTEST_CHECK(result.
direction * central_axis > 0.9f);
1161 DOCTEST_CHECK(result.
direction * central_axis > 0.9f);
1167 DOCTEST_CHECK(result.
direction * central_axis > 0.9f);
1183DOCTEST_TEST_CASE(
"CollisionDetection Finite Cone Height") {
1188 collision.disableMessages();
1198 float half_angle =
M_PI / 6.0f;
1217DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Accuracy - Single Gap Tests") {
1220 collision.disableMessages();
1224 float half_angle =
M_PI / 4.0f;
1236 DOCTEST_CHECK(angular_error < 0.1f);
1244 collision_reset.disableMessages();
1253 DOCTEST_CHECK(angular_error < 0.15f);
1261 collision_reset.disableMessages();
1269 DOCTEST_CHECK(angular_error < 0.2f);
1274DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Accuracy - Symmetric Twin Gaps") {
1277 collision.disableMessages();
1281 float half_angle =
M_PI / 3.0f;
1290 float deviation_from_center = fabsf(acosf(std::max(-1.0f, std::min(1.0f, result.
direction * central_axis))));
1291 DOCTEST_CHECK(deviation_from_center <
M_PI / 6.0f);
1299 collision_reset.disableMessages();
1311 DOCTEST_CHECK(angular_error < 0.3f);
1316DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Accuracy - Angular Precision") {
1319 collision.disableMessages();
1323 float half_angle =
M_PI / 4.0f;
1328 float expected_angle_from_center;
1329 std::string description;
1332 std::vector<TestCase> test_cases = {
1333 {
make_vec3(0, 0, 0), 0.0f,
"Center gap"},
1334 {
make_vec3(0.5f, 0, 0), 0.245f,
"15 degree gap"},
1335 {
make_vec3(-0.7f, 0, 0), -0.334f,
"Negative 19 degree gap"},
1336 {
make_vec3(0, 0.8f, 0), 0.381f,
"Vertical 22 degree gap"}
1339 for (
const auto &test_case: test_cases) {
1343 collision_fresh.disableMessages();
1351 vec3 gap_center_3d =
make_vec3(test_case.gap_position.x, test_case.gap_position.y, 2.0f);
1356 DOCTEST_CHECK_MESSAGE(angular_error < 0.35f, test_case.description.c_str());
1357 DOCTEST_CHECK_MESSAGE(result.
confidence > 0.1f, test_case.description.c_str());
1361DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Accuracy - Distance-Based Priority") {
1364 collision.disableMessages();
1368 float half_angle =
M_PI / 3.0f;
1385 DOCTEST_CHECK(angular_error_near < 0.5f);
1393 collision_reset.disableMessages();
1407 DOCTEST_CHECK(angular_error_near < 0.4f);
1415 collision_reset.disableMessages();
1437 DOCTEST_CHECK((error_near < 0.3f || error_far < 0.3f));
1441DOCTEST_TEST_CASE(
"CollisionDetection findOptimalConePath Accuracy - Edge Case Geometry") {
1444 collision.disableMessages();
1451 float half_angle =
M_PI / 6.0f;
1464 DOCTEST_CHECK(angular_error < 0.5f);
1471 collision_reset.disableMessages();
1473 float half_angle =
M_PI / 4.0f;
1475 float edge_angle = half_angle * 0.8f;
1476 float gap_x = 2.0f * tan(edge_angle);
1488 DOCTEST_CHECK(angular_error < 0.4f);
1495 collision_reset.disableMessages();
1497 float half_angle =
M_PI / 3.0f;
1515 DOCTEST_CHECK(angular_error < 0.6f);
1522 collision_reset.disableMessages();
1524 float half_angle =
M_PI / 2.5f;
1540 float deviation_from_center = acosf(std::max(-1.0f, std::min(1.0f, result.
direction * central_axis)));
1541 DOCTEST_CHECK(deviation_from_center <= half_angle + 0.01f);
1545DOCTEST_TEST_CASE(
"CollisionDetection Scale Test - 1000 Primitives") {
1550 collision.disableMessages();
1556 DOCTEST_CHECK_NOTHROW(collision.buildBVH());
1558 DOCTEST_CHECK(collision.isBVHValid() ==
true);
1559 DOCTEST_CHECK(collision.getPrimitiveCount() == 1000);
1562DOCTEST_TEST_CASE(
"CollisionDetection Scale Test - 10000 Primitives") {
1567 collision.disableMessages();
1573 DOCTEST_CHECK_NOTHROW(collision.buildBVH());
1575 DOCTEST_CHECK(collision.isBVHValid() ==
true);
1576 DOCTEST_CHECK(collision.getPrimitiveCount() == 10000);
1581DOCTEST_TEST_CASE(
"CollisionDetection CPU vs GPU Consistency - Small Scale") {
1592 cpu_collision.disableMessages();
1593 cpu_collision.disableGPUAcceleration();
1594 cpu_collision.buildBVH();
1598 gpu_collision.disableMessages();
1599 gpu_collision.enableGPUAcceleration();
1600 gpu_collision.buildBVH();
1603 for (
uint uuid: overlapping) {
1604 auto cpu_results = cpu_collision.findCollisions(uuid);
1605 auto gpu_results = gpu_collision.findCollisions(uuid);
1608 std::sort(cpu_results.begin(), cpu_results.end());
1609 std::sort(gpu_results.begin(), gpu_results.end());
1611 DOCTEST_CHECK(cpu_results == gpu_results);
1615DOCTEST_TEST_CASE(
"CollisionDetection CPU vs GPU Consistency - Large Scale") {
1620 collision.disableMessages();
1628 collision.disableGPUAcceleration();
1629 collision.buildBVH();
1630 auto cpu_results = collision.findCollisions(cluster1[0]);
1633 std::vector<uint> gpu_results;
1636 collision.enableGPUAcceleration();
1637 collision.buildBVH();
1638 gpu_results = collision.findCollisions(cluster1[0]);
1642 std::sort(cpu_results.begin(), cpu_results.end());
1643 std::sort(gpu_results.begin(), gpu_results.end());
1646 DOCTEST_CHECK(cpu_results == gpu_results);
1651DOCTEST_TEST_CASE(
"CollisionDetection Negative Test - Well Separated Primitives") {
1656 collision.disableMessages();
1663 collision.buildBVH();
1666 collision.disableGPUAcceleration();
1667 auto cpu_collisions = collision.findCollisions(triangle1);
1670 std::vector<uint> gpu_collisions;
1673 collision.enableGPUAcceleration();
1674 gpu_collisions = collision.findCollisions(triangle1);
1678 DOCTEST_CHECK(cpu_collisions.size() == 0);
1679 DOCTEST_CHECK(gpu_collisions.size() == 0);
1682DOCTEST_TEST_CASE(
"CollisionDetection Negative Test - Patch vs Distant Model") {
1687 collision.disableMessages();
1695 collision.buildBVH();
1698 collision.disableGPUAcceleration();
1699 auto cpu_collisions = collision.findCollisions(patch);
1701 std::vector<uint> gpu_collisions;
1704 collision.enableGPUAcceleration();
1705 gpu_collisions = collision.findCollisions(patch);
1709 DOCTEST_CHECK(cpu_collisions.size() == 0);
1710 DOCTEST_CHECK(gpu_collisions.size() == 0);
1711 DOCTEST_CHECK(cpu_collisions == gpu_collisions);
1716DOCTEST_TEST_CASE(
"CollisionDetection Edge Case - Boundary Touching") {
1721 collision.disableMessages();
1727 collision.buildBVH();
1730 collision.disableGPUAcceleration();
1731 auto cpu_results = collision.findCollisions(triangle1);
1733 std::vector<uint> gpu_results;
1736 collision.enableGPUAcceleration();
1737 gpu_results = collision.findCollisions(triangle1);
1740 std::sort(cpu_results.begin(), cpu_results.end());
1741 std::sort(gpu_results.begin(), gpu_results.end());
1743 DOCTEST_CHECK(cpu_results == gpu_results);
1746DOCTEST_TEST_CASE(
"CollisionDetection Edge Case - Very Small Overlaps") {
1751 collision.disableMessages();
1757 collision.buildBVH();
1760 collision.disableGPUAcceleration();
1761 auto cpu_results = collision.findCollisions(triangle1);
1763 std::vector<uint> gpu_results;
1766 collision.enableGPUAcceleration();
1767 gpu_results = collision.findCollisions(triangle1);
1770 std::sort(cpu_results.begin(), cpu_results.end());
1771 std::sort(gpu_results.begin(), gpu_results.end());
1773 DOCTEST_CHECK(cpu_results == gpu_results);
1778DOCTEST_TEST_CASE(
"CollisionDetection Real Geometry - PLY File Loading") {
1783 collision.disableMessages();
1787 std::vector<uint> complex_model;
1790 for (
int i = 0; i < 1000; i++) {
1791 float scale = 0.1f + (i % 10) * 0.05f;
1792 float angle = (i * 0.1f);
1793 float x = cos(angle) * (i * 0.01f);
1794 float y = sin(angle) * (i * 0.01f);
1795 float z = (i % 100) * 0.001f;
1798 complex_model.push_back(uuid);
1805 collision.buildBVH();
1808 collision.disableGPUAcceleration();
1809 auto cpu_intersecting = collision.findCollisions(patch_intersecting);
1810 auto cpu_non_intersecting = collision.findCollisions(patch_non_intersecting);
1812 std::vector<uint> gpu_intersecting, gpu_non_intersecting;
1815 collision.enableGPUAcceleration();
1816 gpu_intersecting = collision.findCollisions(patch_intersecting);
1817 gpu_non_intersecting = collision.findCollisions(patch_non_intersecting);
1821 std::sort(cpu_intersecting.begin(), cpu_intersecting.end());
1822 std::sort(gpu_intersecting.begin(), gpu_intersecting.end());
1823 std::sort(cpu_non_intersecting.begin(), cpu_non_intersecting.end());
1824 std::sort(gpu_non_intersecting.begin(), gpu_non_intersecting.end());
1826 DOCTEST_CHECK(cpu_intersecting == gpu_intersecting);
1827 DOCTEST_CHECK(cpu_non_intersecting == gpu_non_intersecting);
1828 DOCTEST_CHECK(cpu_non_intersecting.size() == 0);
1833DOCTEST_TEST_CASE(
"CollisionDetection Performance - BVH Construction Time") {
1838 collision.disableMessages();
1845 auto start = std::chrono::high_resolution_clock::now();
1846 collision.buildBVH();
1847 auto end = std::chrono::high_resolution_clock::now();
1849 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
1852 DOCTEST_CHECK(duration.count() < 5000);
1853 DOCTEST_CHECK(collision.isBVHValid() ==
true);
1858DOCTEST_TEST_CASE(
"CollisionDetection Memory Stress - Progressive Loading") {
1863 collision.disableMessages();
1866 std::vector<uint> all_uuids;
1868 for (
int batch = 0; batch < 10; batch++) {
1871 all_uuids.insert(all_uuids.end(), batch_uuids.begin(), batch_uuids.end());
1874 DOCTEST_CHECK_NOTHROW(collision.buildBVH());
1875 DOCTEST_CHECK(collision.isBVHValid() ==
true);
1876 DOCTEST_CHECK(collision.getPrimitiveCount() == all_uuids.size());
1882DOCTEST_TEST_CASE(
"CollisionDetection findNearestPrimitiveDistance - Basic Functionality") {
1885 collision.disableMessages();
1895 std::vector<uint> candidate_UUIDs = {triangle1, triangle2, triangle3};
1897 vec3 obstacle_direction;
1899 bool result = collision.findNearestPrimitiveDistance(origin, direction, candidate_UUIDs, distance, obstacle_direction);
1900 DOCTEST_CHECK(result ==
true);
1901 DOCTEST_CHECK(distance >= 4.0f);
1902 DOCTEST_CHECK(distance <= 6.0f);
1906 bool result_miss = collision.findNearestPrimitiveDistance(origin, direction_miss, candidate_UUIDs, distance, obstacle_direction);
1907 DOCTEST_CHECK(result_miss ==
false);
1910 std::vector<uint> subset_UUIDs = {triangle2, triangle3};
1911 bool result_subset = collision.findNearestPrimitiveDistance(origin, direction, subset_UUIDs, distance, obstacle_direction);
1912 DOCTEST_CHECK(result_subset ==
true);
1913 DOCTEST_CHECK(distance >= 9.0f);
1914 DOCTEST_CHECK(distance <= 11.0f);
1917DOCTEST_TEST_CASE(
"CollisionDetection findNearestPrimitiveDistance - Edge Cases") {
1920 collision.disableMessages();
1929 std::vector<uint> empty_UUIDs;
1930 vec3 obstacle_direction_unused;
1931 bool result_empty = collision.findNearestPrimitiveDistance(origin, direction, empty_UUIDs, distance, obstacle_direction_unused);
1932 DOCTEST_CHECK(result_empty ==
false);
1936 std::vector<uint> valid_UUIDs = {triangle1};
1937 bool result_non_norm = collision.findNearestPrimitiveDistance(origin, non_normalized_dir, valid_UUIDs, distance, obstacle_direction_unused);
1938 DOCTEST_CHECK(result_non_norm ==
false);
1941 std::vector<uint> invalid_UUIDs = {999999};
1942 bool result_invalid = collision.findNearestPrimitiveDistance(origin, direction, invalid_UUIDs, distance, obstacle_direction_unused);
1943 DOCTEST_CHECK(result_invalid ==
false);
1946 std::vector<uint> mixed_UUIDs = {triangle1, 999999};
1947 bool result_mixed = collision.findNearestPrimitiveDistance(origin, direction, mixed_UUIDs, distance, obstacle_direction_unused);
1948 DOCTEST_CHECK(result_mixed ==
true);
1949 DOCTEST_CHECK(distance >= 4.0f);
1950 DOCTEST_CHECK(distance <= 6.0f);
1953DOCTEST_TEST_CASE(
"CollisionDetection findNearestPrimitiveDistance - Complex Scenarios") {
1956 collision.disableMessages();
1965 vec3 obstacle_direction_unused;
1967 bool result = collision.findNearestPrimitiveDistance(origin, direction, cluster, distance, obstacle_direction_unused);
1969 DOCTEST_CHECK(result ==
false);
1975 bool result_near = collision.findNearestPrimitiveDistance(origin_near, direction_out, cluster, distance, obstacle_direction_unused);
1976 DOCTEST_CHECK(result_near ==
false);
1981 bool result_perpendicular = collision.findNearestPrimitiveDistance(origin_above, direction_down, cluster, distance, obstacle_direction_unused);
1982 DOCTEST_CHECK(result_perpendicular ==
true);
1983 DOCTEST_CHECK(distance >= 0.9f);
1984 DOCTEST_CHECK(distance <= 1.1f);
1987DOCTEST_TEST_CASE(
"CollisionDetection findNearestPrimitiveDistance - Directional Testing") {
1990 collision.disableMessages();
1998 std::vector<uint> all_triangles = {triangle_x, triangle_y, triangle_z, triangle_neg_x};
2003 struct DirectionTest {
2010 std::vector<DirectionTest> tests = {
2014 {
make_vec3(-1, 0, 0), 4.0f, 6.0f,
true},
2015 {
make_vec3(0.707f, 0.707f, 0), 6.0f, 8.0f,
false},
2018 vec3 obstacle_direction_unused;
2019 for (
const auto &test: tests) {
2020 bool result = collision.findNearestPrimitiveDistance(origin, test.direction, all_triangles, distance, obstacle_direction_unused);
2021 DOCTEST_CHECK(result == test.should_hit);
2022 if (result && test.should_hit) {
2023 DOCTEST_CHECK(distance >= test.expected_min);
2024 DOCTEST_CHECK(distance <= test.expected_max);
2029DOCTEST_TEST_CASE(
"CollisionDetection - findNearestPrimitiveDistance front/back face detection") {
2032 collision.disableMessages();
2037 uint horizontal_patch = context.
addPatch(patch_center, patch_size);
2039 std::vector<uint> candidates = {horizontal_patch};
2041 vec3 obstacle_direction;
2047 bool found_below = collision.findNearestPrimitiveDistance(origin_below, direction_up, candidates, distance, obstacle_direction);
2048 DOCTEST_CHECK(found_below ==
true);
2049 DOCTEST_CHECK(distance >= 0.49f);
2050 DOCTEST_CHECK(distance <= 0.51f);
2052 DOCTEST_CHECK(obstacle_direction.
z > 0.9f);
2053 DOCTEST_CHECK(std::abs(obstacle_direction.
x) < 0.1f);
2054 DOCTEST_CHECK(std::abs(obstacle_direction.
y) < 0.1f);
2060 bool found_above = collision.findNearestPrimitiveDistance(origin_above, direction_down, candidates, distance, obstacle_direction);
2061 DOCTEST_CHECK(found_above ==
true);
2062 DOCTEST_CHECK(distance >= 0.49f);
2063 DOCTEST_CHECK(distance <= 0.51f);
2065 DOCTEST_CHECK(obstacle_direction.
z < -0.9f);
2066 DOCTEST_CHECK(std::abs(obstacle_direction.
x) < 0.1f);
2067 DOCTEST_CHECK(std::abs(obstacle_direction.
y) < 0.1f);
2073 bool found_away = collision.findNearestPrimitiveDistance(origin_below2, direction_away, candidates, distance, obstacle_direction);
2074 DOCTEST_CHECK(found_away ==
false);
2079DOCTEST_TEST_CASE(
"CollisionDetection Cone-Based Obstacle Detection - Basic Functionality") {
2082 collision.disableMessages();
2086 std::vector<uint> obstacles = {obstacle_uuid};
2087 collision.buildBVH(obstacles);
2092 float half_angle =
deg2rad(30.0f);
2093 float height = 1.0f;
2096 vec3 obstacle_direction;
2098 bool found = collision.findNearestSolidObstacleInCone(apex, axis, half_angle, height, obstacles, distance, obstacle_direction);
2100 DOCTEST_CHECK(found ==
true);
2101 DOCTEST_CHECK(distance >= 0.49f);
2102 DOCTEST_CHECK(distance <= 0.51f);
2103 DOCTEST_CHECK(obstacle_direction.
z > 0.9f);
2107 bool found_far = collision.findNearestSolidObstacleInCone(apex_far, axis, half_angle, height, obstacles, distance, obstacle_direction);
2108 DOCTEST_CHECK(found_far ==
false);
2111 float narrow_angle =
deg2rad(5.0f);
2115 bool found_narrow = collision.findNearestSolidObstacleInCone(apex, axis_offset, narrow_angle, height, obstacles, distance, obstacle_direction);
2116 DOCTEST_CHECK(found_narrow ==
false);
2119DOCTEST_TEST_CASE(
"CollisionDetection Cone-Based vs Legacy Method Comparison") {
2122 collision.disableMessages();
2126 std::vector<uint> obstacles = {obstacle_uuid};
2127 collision.buildBVH(obstacles);
2134 float legacy_distance;
2135 vec3 legacy_obstacle_direction;
2136 bool legacy_found = collision.findNearestPrimitiveDistance(origin, direction, obstacles, legacy_distance, legacy_obstacle_direction);
2139 float cone_distance;
2140 vec3 cone_obstacle_direction;
2141 float half_angle =
deg2rad(30.0f);
2142 float height = 2.0f;
2143 bool cone_found = collision.findNearestSolidObstacleInCone(origin, direction, half_angle, height, obstacles, cone_distance, cone_obstacle_direction);
2146 DOCTEST_CHECK(legacy_found ==
true);
2147 DOCTEST_CHECK(cone_found ==
true);
2150 DOCTEST_CHECK(std::abs(cone_distance - 0.8f) < 0.04f);
2151 DOCTEST_CHECK(std::abs(legacy_distance - 0.8f) < 0.04f);
2154 DOCTEST_CHECK(legacy_obstacle_direction.
magnitude() > 0.9f);
2155 DOCTEST_CHECK(cone_obstacle_direction.
magnitude() > 0.9f);
2158DOCTEST_TEST_CASE(
"CollisionDetection Cone-Based Triangle vs Patch Intersection") {
2161 collision.disableMessages();
2169 std::vector<uint> triangle_obstacles = {triangle_uuid};
2170 std::vector<uint> patch_obstacles = {patch_uuid};
2172 collision.buildBVH({triangle_uuid, patch_uuid});
2176 float half_angle =
deg2rad(30.0f);
2177 float height = 1.0f;
2179 vec3 obstacle_direction;
2182 bool triangle_found = collision.findNearestSolidObstacleInCone(apex, axis, half_angle, height, triangle_obstacles, distance, obstacle_direction);
2183 DOCTEST_CHECK(triangle_found ==
true);
2184 DOCTEST_CHECK(distance > 0.4f);
2185 DOCTEST_CHECK(distance < 0.6f);
2189 bool patch_found = collision.findNearestSolidObstacleInCone(apex_patch, axis, half_angle, height, patch_obstacles, distance, obstacle_direction);
2190 DOCTEST_CHECK(patch_found ==
true);
2191 DOCTEST_CHECK(distance > 0.4f);
2192 DOCTEST_CHECK(distance < 0.6f);
2195DOCTEST_TEST_CASE(
"CollisionDetection Cone-Based Parameter Validation") {
2198 collision.disableMessages();
2201 std::vector<uint> obstacles = {obstacle_uuid};
2202 collision.buildBVH(obstacles);
2207 vec3 obstacle_direction;
2210 bool result1 = collision.findNearestSolidObstacleInCone(apex, axis, -0.1f, 1.0f, obstacles, distance, obstacle_direction);
2211 DOCTEST_CHECK(result1 ==
false);
2213 bool result2 = collision.findNearestSolidObstacleInCone(apex, axis,
M_PI, 1.0f, obstacles, distance, obstacle_direction);
2214 DOCTEST_CHECK(result2 ==
false);
2216 bool result3 = collision.findNearestSolidObstacleInCone(apex, axis,
deg2rad(30.0f), -1.0f, obstacles, distance, obstacle_direction);
2217 DOCTEST_CHECK(result3 ==
false);
2220 std::vector<uint> empty_obstacles;
2221 bool result4 = collision.findNearestSolidObstacleInCone(apex, axis,
deg2rad(30.0f), 1.0f, empty_obstacles, distance, obstacle_direction);
2222 DOCTEST_CHECK(result4 ==
false);
2225 bool result5 = collision.findNearestSolidObstacleInCone(apex, axis,
deg2rad(30.0f), 1.0f, obstacles, distance, obstacle_direction);
2226 DOCTEST_CHECK(result5 ==
true);
2230DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Basic Functionality") {
2233 collision.disableMessages();
2236 vec3 grid_center(0, 0, 0);
2237 vec3 grid_size(10, 10, 10);
2238 int3 grid_divisions(2, 2, 2);
2241 std::vector<vec3> ray_origins;
2242 std::vector<vec3> ray_directions;
2245 ray_origins.push_back(
make_vec3(-10, 0, 0));
2246 ray_directions.push_back(
make_vec3(1, 0, 0));
2249 ray_origins.push_back(
make_vec3(-10, -10, -10));
2250 ray_directions.push_back(normalize(
make_vec3(1, 1, 1)));
2253 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2256 int P_denom, P_trans;
2257 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2258 DOCTEST_CHECK(P_denom >= 0);
2259 DOCTEST_CHECK(P_trans >= 0);
2260 DOCTEST_CHECK(P_trans <= P_denom);
2263 float r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2264 DOCTEST_CHECK(r_bar >= 0.0f);
2267 collision.clearVoxelData();
2270DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Edge Cases") {
2273 collision.disableMessages();
2276 vec3 grid_center(0, 0, 0);
2277 vec3 grid_size(5, 5, 5);
2278 int3 grid_divisions(1, 1, 1);
2280 std::vector<vec3> empty_origins;
2281 std::vector<vec3> empty_directions;
2284 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, empty_origins, empty_directions);
2287 int P_denom, P_trans;
2293 collision.getVoxelTransmissionProbability(
make_int3(-1, 0, 0), P_denom, P_trans);
2294 }
catch (
const std::exception &e) {
2299 collision.getVoxelTransmissionProbability(
make_int3(1, 0, 0), P_denom, P_trans);
2300 }
catch (
const std::exception &e) {
2306 DOCTEST_CHECK(
true);
2309DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Data Consistency") {
2312 collision.disableMessages();
2314 vec3 grid_center(0, 0, 0);
2315 vec3 grid_size(6, 6, 6);
2316 int3 grid_divisions(3, 3, 3);
2319 std::vector<vec3> ray_origins;
2320 std::vector<vec3> ray_directions;
2323 for (
int i = -1; i <= 1; i++) {
2324 for (
int j = -1; j <= 1; j++) {
2325 ray_origins.push_back(
make_vec3(i * 1.5f, j * 1.5f, -10));
2326 ray_directions.push_back(
make_vec3(0, 0, 1));
2331 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2334 bool found_data =
false;
2335 for (
int i = 0; i < grid_divisions.x; i++) {
2336 for (
int j = 0; j < grid_divisions.y; j++) {
2337 for (
int k = 0; k < grid_divisions.z; k++) {
2338 int P_denom, P_trans;
2339 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2344 DOCTEST_CHECK(P_trans <= P_denom);
2347 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2348 DOCTEST_CHECK(r_bar > 0.0f);
2354 DOCTEST_CHECK(found_data);
2357DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Manual Data Setting") {
2360 collision.disableMessages();
2362 vec3 grid_center(0, 0, 0);
2363 vec3 grid_size(4, 4, 4);
2364 int3 grid_divisions(2, 2, 2);
2367 std::vector<vec3> init_origins;
2368 std::vector<vec3> init_directions;
2369 init_origins.push_back(
make_vec3(0, 0, -10));
2370 init_directions.push_back(
make_vec3(0, 0, 1));
2371 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, init_origins, init_directions);
2374 int3 test_voxel(0, 0, 0);
2375 collision.setVoxelTransmissionProbability(100, 75, test_voxel);
2376 collision.setVoxelRbar(2.5f, test_voxel);
2379 int P_denom, P_trans;
2380 collision.getVoxelTransmissionProbability(test_voxel, P_denom, P_trans);
2381 DOCTEST_CHECK(P_denom == 100);
2382 DOCTEST_CHECK(P_trans == 75);
2384 float r_bar = collision.getVoxelRbar(test_voxel);
2385 DOCTEST_CHECK(std::abs(r_bar - 2.5f) < 1e-6f);
2388 int3 test_voxel2(1, 1, 1);
2389 collision.setVoxelTransmissionProbability(200, 150, test_voxel2);
2390 collision.setVoxelRbar(3.7f, test_voxel2);
2392 collision.getVoxelTransmissionProbability(test_voxel2, P_denom, P_trans);
2393 DOCTEST_CHECK(P_denom == 200);
2394 DOCTEST_CHECK(P_trans == 150);
2396 r_bar = collision.getVoxelRbar(test_voxel2);
2397 DOCTEST_CHECK(std::abs(r_bar - 3.7f) < 1e-6f);
2400 collision.getVoxelTransmissionProbability(test_voxel, P_denom, P_trans);
2401 DOCTEST_CHECK(P_denom == 100);
2402 DOCTEST_CHECK(P_trans == 75);
2405DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Different Grid Sizes") {
2408 collision.disableMessages();
2411 std::vector<int3> test_grids = {
2418 for (
const auto &grid_div: test_grids) {
2419 vec3 grid_center(0, 0, 0);
2420 vec3 grid_size(10, 10, 10);
2422 std::vector<vec3> ray_origins;
2423 std::vector<vec3> ray_directions;
2426 ray_origins.push_back(
make_vec3(0, 0, -10));
2427 ray_directions.push_back(
make_vec3(0, 0, 1));
2429 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_div, ray_origins, ray_directions);
2432 bool found_valid_voxel =
false;
2433 for (
int i = 0; i < grid_div.x; i++) {
2434 for (
int j = 0; j < grid_div.y; j++) {
2435 for (
int k = 0; k < grid_div.z; k++) {
2436 int P_denom, P_trans;
2437 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2438 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2441 DOCTEST_CHECK(P_denom >= 0);
2442 DOCTEST_CHECK(P_trans >= 0);
2443 DOCTEST_CHECK(r_bar >= 0.0f);
2444 found_valid_voxel =
true;
2448 DOCTEST_CHECK(found_valid_voxel);
2450 collision.clearVoxelData();
2454DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Ray Direction Variations") {
2457 collision.disableMessages();
2459 vec3 grid_center(0, 0, 0);
2460 vec3 grid_size(8, 8, 8);
2461 int3 grid_divisions(2, 2, 2);
2464 std::vector<vec3> test_directions = {
2473 for (
const auto &direction: test_directions) {
2474 std::vector<vec3> ray_origins;
2475 std::vector<vec3> ray_directions;
2478 vec3 start_point = grid_center - direction * 10.0f;
2479 ray_origins.push_back(start_point);
2480 ray_directions.push_back(direction);
2482 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2485 bool found_intersections =
false;
2486 for (
int i = 0; i < grid_divisions.x; i++) {
2487 for (
int j = 0; j < grid_divisions.y; j++) {
2488 for (
int k = 0; k < grid_divisions.z; k++) {
2489 int P_denom, P_trans;
2490 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2492 found_intersections =
true;
2493 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2494 DOCTEST_CHECK(r_bar > 0.0f);
2500 DOCTEST_CHECK(found_intersections);
2501 collision.clearVoxelData();
2505DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - GPU/CPU Consistency") {
2508 collision.disableMessages();
2510 vec3 grid_center(0, 0, 0);
2511 vec3 grid_size(6, 6, 6);
2512 int3 grid_divisions(3, 3, 3);
2515 std::vector<vec3> ray_origins;
2516 std::vector<vec3> ray_directions;
2518 for (
int i = 0; i < 5; i++) {
2519 ray_origins.push_back(
make_vec3(i - 2.0f, 0, -10));
2520 ray_directions.push_back(
make_vec3(0, 0, 1));
2524 collision.disableGPUAcceleration();
2525 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2528 std::vector<std::vector<std::vector<std::pair<int, float>>>> cpu_results(grid_divisions.x);
2529 for (
int i = 0; i < grid_divisions.x; i++) {
2530 cpu_results[i].resize(grid_divisions.y);
2531 for (
int j = 0; j < grid_divisions.y; j++) {
2532 cpu_results[i][j].resize(grid_divisions.z);
2533 for (
int k = 0; k < grid_divisions.z; k++) {
2534 int P_denom, P_trans;
2535 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2536 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2537 cpu_results[i][j][k] = std::make_pair(P_denom, r_bar);
2545 collision.enableGPUAcceleration();
2546 collision.clearVoxelData();
2547 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2551 for (
int i = 0; i < grid_divisions.x; i++) {
2552 for (
int j = 0; j < grid_divisions.y; j++) {
2553 for (
int k = 0; k < grid_divisions.z; k++) {
2554 int P_denom_gpu, P_trans_gpu;
2555 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom_gpu, P_trans_gpu);
2556 float r_bar_gpu = collision.getVoxelRbar(
make_int3(i, j, k));
2558 int P_denom_cpu = cpu_results[i][j][k].first;
2559 float r_bar_cpu = cpu_results[i][j][k].second;
2563 DOCTEST_CHECK(abs(P_denom_gpu - P_denom_cpu) <= 1);
2564 if (r_bar_cpu > 0 && r_bar_gpu > 0) {
2565 DOCTEST_CHECK(std::abs(r_bar_gpu - r_bar_cpu) < 1e-4f);
2572DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Parameter Validation") {
2575 collision.disableMessages();
2578 vec3 grid_center(0, 0, 0);
2579 vec3 negative_size(-5, 5, 5);
2580 int3 grid_divisions(2, 2, 2);
2581 std::vector<vec3> ray_origins = {
make_vec3(0, 0, -10)};
2582 std::vector<vec3> ray_directions = {
make_vec3(0, 0, 1)};
2586 collision.calculateVoxelRayPathLengths(grid_center, negative_size, grid_divisions, ray_origins, ray_directions);
2588 int P_denom, P_trans;
2589 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2590 DOCTEST_CHECK(P_denom >= 0);
2591 DOCTEST_CHECK(P_trans >= 0);
2592 }
catch (
const std::exception &e) {
2594 DOCTEST_CHECK(
true);
2598 int3 zero_divisions(0, 2, 2);
2600 collision.calculateVoxelRayPathLengths(grid_center,
make_vec3(5, 5, 5), zero_divisions, ray_origins, ray_directions);
2602 int P_denom_zero, P_trans_zero;
2604 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom_zero, P_trans_zero);
2606 DOCTEST_CHECK(P_denom_zero >= 0);
2607 }
catch (
const std::exception &inner_e) {
2609 DOCTEST_CHECK(
true);
2611 }
catch (
const std::exception &e) {
2612 DOCTEST_CHECK(
true);
2618 collision.calculateVoxelRayPathLengths(grid_center,
make_vec3(5, 5, 5),
make_int3(2, 2, 2), ray_origins, mismatched_directions);
2619 DOCTEST_CHECK(
false);
2620 }
catch (
const std::exception &e) {
2621 DOCTEST_CHECK(
true);
2625 vec3 valid_grid_size(4, 4, 4);
2626 int3 valid_divisions(2, 2, 2);
2627 collision.calculateVoxelRayPathLengths(grid_center, valid_grid_size, valid_divisions, ray_origins, ray_directions);
2630 collision.setVoxelTransmissionProbability(10, 15,
make_int3(0, 0, 0));
2631 int test_P_denom, test_P_trans;
2632 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), test_P_denom, test_P_trans);
2633 DOCTEST_CHECK(test_P_denom == 10);
2634 DOCTEST_CHECK(test_P_trans == 15);
2637 collision.setVoxelTransmissionProbability(-5, -3,
make_int3(0, 0, 0));
2638 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), test_P_denom, test_P_trans);
2639 DOCTEST_CHECK(test_P_denom == -5);
2640 DOCTEST_CHECK(test_P_trans == -3);
2643DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Mathematical Validation") {
2646 collision.disableMessages();
2649 vec3 grid_center(0, 0, 0);
2650 vec3 grid_size(2, 2, 2);
2651 int3 grid_divisions(1, 1, 1);
2654 std::vector<vec3> ray_origins = {
make_vec3(0, 0, -5)};
2655 std::vector<vec3> ray_directions = {
make_vec3(0, 0, 1)};
2657 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2659 float r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2662 DOCTEST_CHECK(std::abs(r_bar - 2.0f) < 0.1f);
2665 collision.clearVoxelData();
2666 std::vector<vec3> diagonal_origins = {
make_vec3(-2, -2, -2)};
2667 std::vector<vec3> diagonal_directions = {normalize(
make_vec3(1, 1, 1))};
2669 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, diagonal_origins, diagonal_directions);
2671 float diagonal_r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2674 float expected_diagonal = std::sqrt(3.0f) * 2.0f;
2675 DOCTEST_CHECK(std::abs(diagonal_r_bar - expected_diagonal) < 0.2f);
2678 collision.clearVoxelData();
2679 std::vector<vec3> multi_origins;
2680 std::vector<vec3> multi_directions;
2683 for (
int i = 0; i < 4; i++) {
2684 multi_origins.push_back(
make_vec3(0.5f * i - 0.75f, 0, -5));
2685 multi_directions.push_back(
make_vec3(0, 0, 1));
2688 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, multi_origins, multi_directions);
2690 int P_denom, P_trans;
2691 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2692 float multi_r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2695 DOCTEST_CHECK(P_denom == 4);
2697 DOCTEST_CHECK(P_trans == P_denom);
2699 DOCTEST_CHECK(std::abs(multi_r_bar - 2.0f) < 0.1f);
2702DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Numerical Precision") {
2705 collision.disableMessages();
2708 vec3 tiny_center(0, 0, 0);
2709 vec3 tiny_size(0.001f, 0.001f, 0.001f);
2710 int3 tiny_divisions(1, 1, 1);
2712 std::vector<vec3> tiny_origins = {
make_vec3(0, 0, -0.01f)};
2713 std::vector<vec3> tiny_directions = {
make_vec3(0, 0, 1)};
2715 collision.calculateVoxelRayPathLengths(tiny_center, tiny_size, tiny_divisions, tiny_origins, tiny_directions);
2716 float tiny_r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2719 DOCTEST_CHECK(tiny_r_bar > 0.0f);
2720 DOCTEST_CHECK(tiny_r_bar < 0.1f);
2723 vec3 large_center(0, 0, 0);
2724 vec3 large_size(1000.0f, 1000.0f, 1000.0f);
2725 int3 large_divisions(2, 2, 2);
2727 std::vector<vec3> large_origins = {
make_vec3(0, 0, -2000)};
2728 std::vector<vec3> large_directions = {
make_vec3(0, 0, 1)};
2730 collision.calculateVoxelRayPathLengths(large_center, large_size, large_divisions, large_origins, large_directions);
2731 float large_r_bar = collision.getVoxelRbar(
make_int3(1, 1, 1));
2734 DOCTEST_CHECK(large_r_bar > 0.0f);
2735 DOCTEST_CHECK(large_r_bar < 2000.0f);
2738 collision.clearVoxelData();
2739 vec3 precision_center(0, 0, 0);
2740 vec3 precision_size(10, 10, 10);
2741 int3 precision_divisions(5, 5, 5);
2743 std::vector<vec3> precision_origins;
2744 std::vector<vec3> precision_directions;
2745 for (
int i = 0; i < 10; i++) {
2746 precision_origins.push_back(
make_vec3(i - 5.0f, 0, -20));
2747 precision_directions.push_back(
make_vec3(0, 0, 1));
2751 collision.disableGPUAcceleration();
2752 collision.calculateVoxelRayPathLengths(precision_center, precision_size, precision_divisions, precision_origins, precision_directions);
2755 std::vector<float> cpu_rbars;
2756 for (
int i = 0; i < precision_divisions.x; i++) {
2757 for (
int j = 0; j < precision_divisions.y; j++) {
2758 for (
int k = 0; k < precision_divisions.z; k++) {
2759 cpu_rbars.push_back(collision.getVoxelRbar(
make_int3(i, j, k)));
2767 collision.enableGPUAcceleration();
2768 collision.clearVoxelData();
2769 collision.calculateVoxelRayPathLengths(precision_center, precision_size, precision_divisions, precision_origins, precision_directions);
2774 for (
int i = 0; i < precision_divisions.x; i++) {
2775 for (
int j = 0; j < precision_divisions.y; j++) {
2776 for (
int k = 0; k < precision_divisions.z; k++) {
2777 float gpu_rbar = collision.getVoxelRbar(
make_int3(i, j, k));
2778 float cpu_rbar = cpu_rbars[idx++];
2780 if (cpu_rbar > 0 && gpu_rbar > 0) {
2781 float relative_error = std::abs(gpu_rbar - cpu_rbar) / std::max(cpu_rbar, gpu_rbar);
2782 DOCTEST_CHECK(relative_error < 1e-5f);
2789DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Error Recovery and State Management") {
2792 collision.disableMessages();
2796 int P_denom, P_trans;
2797 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2799 DOCTEST_CHECK(P_denom == 0);
2800 DOCTEST_CHECK(P_trans == 0);
2801 }
catch (
const std::exception &e) {
2803 DOCTEST_CHECK(
true);
2807 float r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2809 DOCTEST_CHECK(r_bar == 0.0f);
2810 }
catch (
const std::exception &e) {
2812 DOCTEST_CHECK(
true);
2816 vec3 grid_center(0, 0, 0);
2817 vec3 grid_size(4, 4, 4);
2818 int3 grid_divisions(2, 2, 2);
2819 std::vector<vec3> ray_origins = {
make_vec3(0, 0, -10)};
2820 std::vector<vec3> ray_directions = {
make_vec3(0, 0, 1)};
2823 for (
int cycle = 0; cycle < 3; cycle++) {
2824 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2827 int P_denom, P_trans;
2828 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2829 DOCTEST_CHECK(P_denom >= 0);
2831 float r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2832 DOCTEST_CHECK(r_bar >= 0);
2835 collision.clearVoxelData();
2841 collision.setVoxelTransmissionProbability(10, 5,
make_int3(-1, 0, 0));
2842 DOCTEST_CHECK(
false);
2843 }
catch (
const std::exception &e) {
2845 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2847 int P_denom, P_trans;
2848 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2849 DOCTEST_CHECK(P_denom >= 0);
2853DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Memory and Performance Stress") {
2856 collision.disableMessages();
2859 vec3 stress_center(0, 0, 0);
2860 vec3 stress_size(50, 50, 50);
2861 int3 stress_divisions(10, 10, 10);
2864 std::vector<vec3> stress_origins;
2865 std::vector<vec3> stress_directions;
2866 for (
int i = 0; i < 100; i++) {
2867 for (
int j = 0; j < 10; j++) {
2868 stress_origins.push_back(
make_vec3(i - 50.0f, j - 5.0f, -100));
2869 stress_directions.push_back(
make_vec3(0, 0, 1));
2874 auto start_time = std::chrono::high_resolution_clock::now();
2875 collision.calculateVoxelRayPathLengths(stress_center, stress_size, stress_divisions, stress_origins, stress_directions);
2876 auto end_time = std::chrono::high_resolution_clock::now();
2877 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
2880 bool found_data =
false;
2881 for (
int i = 0; i < stress_divisions.x && !found_data; i++) {
2882 for (
int j = 0; j < stress_divisions.y && !found_data; j++) {
2883 for (
int k = 0; k < stress_divisions.z && !found_data; k++) {
2884 int P_denom, P_trans;
2885 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2888 DOCTEST_CHECK(P_trans <= P_denom);
2889 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2890 DOCTEST_CHECK(r_bar > 0.0f);
2895 DOCTEST_CHECK(found_data);
2898 collision.clearVoxelData();
2901 int P_denom, P_trans;
2902 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2903 DOCTEST_CHECK(P_denom == 0);
2904 DOCTEST_CHECK(P_trans == 0);
2906 float r_bar = collision.getVoxelRbar(
make_int3(0, 0, 0));
2907 DOCTEST_CHECK(r_bar == 0.0f);
2910 for (
int cycle = 0; cycle < 5; cycle++) {
2911 vec3 cycle_size(8 + cycle * 2, 8 + cycle * 2, 8 + cycle * 2);
2912 int3 cycle_divisions(2 + cycle, 2 + cycle, 2 + cycle);
2914 std::vector<vec3> cycle_origins = {
make_vec3(0, 0, -20)};
2915 std::vector<vec3> cycle_directions = {
make_vec3(0, 0, 1)};
2917 collision.calculateVoxelRayPathLengths(stress_center, cycle_size, cycle_divisions, cycle_origins, cycle_directions);
2920 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
2921 DOCTEST_CHECK(P_denom >= 0);
2923 collision.clearVoxelData();
2927DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Integration with BVH") {
2930 collision.disableMessages();
2936 collision.buildBVH(sphere_UUIDs);
2939 vec3 grid_center(0, 0, 0);
2940 vec3 grid_size(10, 10, 10);
2941 int3 grid_divisions(5, 5, 5);
2943 std::vector<vec3> ray_origins;
2944 std::vector<vec3> ray_directions;
2945 for (
int i = 0; i < 8; i++) {
2946 ray_origins.push_back(
make_vec3(i - 4.0f, 0, -15));
2947 ray_directions.push_back(
make_vec3(0, 0, 1));
2951 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2954 bool found_voxel_data =
false;
2955 for (
int i = 0; i < grid_divisions.x; i++) {
2956 for (
int j = 0; j < grid_divisions.y; j++) {
2957 for (
int k = 0; k < grid_divisions.z; k++) {
2958 int P_denom, P_trans;
2959 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
2961 found_voxel_data =
true;
2962 DOCTEST_CHECK(P_trans >= 0);
2963 DOCTEST_CHECK(P_trans <= P_denom);
2965 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
2966 DOCTEST_CHECK(r_bar >= 0.0f);
2971 DOCTEST_CHECK(found_voxel_data);
2974 std::vector<uint> collision_results = collision.findCollisions(sphere_UUIDs[0]);
2975 DOCTEST_CHECK(collision_results.size() >= 0);
2978 collision.clearVoxelData();
2981 std::vector<uint> triangle_UUIDs;
2985 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
2988 std::vector<uint> new_collisions = collision.findCollisions(triangle_UUIDs);
2989 DOCTEST_CHECK(new_collisions.size() >= 0);
2992 int final_P_denom, final_P_trans;
2993 collision.getVoxelTransmissionProbability(
make_int3(2, 2, 2), final_P_denom, final_P_trans);
2994 DOCTEST_CHECK(final_P_denom >= 0);
2995 DOCTEST_CHECK(final_P_trans >= 0);
2998DOCTEST_TEST_CASE(
"CollisionDetection Voxel Ray Path Length - Edge Case Ray Geometries") {
3001 collision.disableMessages();
3003 vec3 grid_center(0, 0, 0);
3004 vec3 grid_size(4, 4, 4);
3005 int3 grid_divisions(2, 2, 2);
3008 std::vector<vec3> grazing_origins = {
make_vec3(-3, 2.0f, 0)};
3009 std::vector<vec3> grazing_directions = {
make_vec3(1, 0, 0)};
3011 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, grazing_origins, grazing_directions);
3014 bool found_grazing_intersection =
false;
3015 for (
int i = 0; i < grid_divisions.x; i++) {
3016 for (
int j = 0; j < grid_divisions.y; j++) {
3017 for (
int k = 0; k < grid_divisions.z; k++) {
3018 int P_denom, P_trans;
3019 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
3021 found_grazing_intersection =
true;
3022 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
3023 DOCTEST_CHECK(r_bar >= 0.0f);
3030 collision.clearVoxelData();
3031 std::vector<vec3> corner_origins = {
make_vec3(-3, -3, -3)};
3032 std::vector<vec3> corner_directions = {normalize(
make_vec3(1, 1, 1))};
3034 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, corner_origins, corner_directions);
3036 bool found_corner_intersection =
false;
3037 for (
int i = 0; i < grid_divisions.x; i++) {
3038 for (
int j = 0; j < grid_divisions.y; j++) {
3039 for (
int k = 0; k < grid_divisions.z; k++) {
3040 int P_denom, P_trans;
3041 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
3043 found_corner_intersection =
true;
3044 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
3045 DOCTEST_CHECK(r_bar >= 0.0f);
3052 collision.clearVoxelData();
3053 std::vector<vec3> near_zero_origins = {
make_vec3(0, 0, -5)};
3054 std::vector<vec3> near_zero_directions = {normalize(
make_vec3(1e-6f, 1e-6f, 1.0f))};
3056 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, near_zero_origins, near_zero_directions);
3059 bool found_near_zero_intersection =
false;
3060 for (
int i = 0; i < grid_divisions.x; i++) {
3061 for (
int j = 0; j < grid_divisions.y; j++) {
3062 for (
int k = 0; k < grid_divisions.z; k++) {
3063 int P_denom, P_trans;
3064 collision.getVoxelTransmissionProbability(
make_int3(i, j, k), P_denom, P_trans);
3066 found_near_zero_intersection =
true;
3067 float r_bar = collision.getVoxelRbar(
make_int3(i, j, k));
3068 DOCTEST_CHECK(r_bar >= 0.0f);
3069 DOCTEST_CHECK(std::isfinite(r_bar));
3078DOCTEST_TEST_CASE(
"CollisionDetection Generic Ray Casting - Basic Functionality") {
3081 collision.disableMessages();
3093 CollisionDetection::CollisionDetection::HitResult result = collision.castRay(ray_origin, ray_direction);
3095 DOCTEST_CHECK(result.hit ==
true);
3096 DOCTEST_CHECK(result.primitive_UUID == triangle_uuid);
3097 DOCTEST_CHECK(result.distance > 0.9f);
3098 DOCTEST_CHECK(result.distance < 1.1f);
3101 DOCTEST_CHECK(result.intersection_point.x > 0.4f);
3102 DOCTEST_CHECK(result.intersection_point.x < 0.6f);
3103 DOCTEST_CHECK(std::abs(result.intersection_point.y) < 1e-5f);
3104 DOCTEST_CHECK(result.intersection_point.z > 0.4f);
3105 DOCTEST_CHECK(result.intersection_point.z < 0.6f);
3112 DOCTEST_CHECK(miss_result.
hit ==
false);
3113 DOCTEST_CHECK(miss_result.
distance < 0);
3118 float max_distance = 1.5f;
3121 DOCTEST_CHECK(limited_result.
hit ==
false);
3124DOCTEST_TEST_CASE(
"CollisionDetection Generic Ray Casting - CollisionDetection::RayQuery Structure") {
3127 collision.disableMessages();
3139 DOCTEST_CHECK(result1.
hit ==
true);
3145 DOCTEST_CHECK(result2.hit ==
true);
3146 DOCTEST_CHECK(result2.primitive_UUID == triangle2);
3152 DOCTEST_CHECK(result3.hit ==
true);
3153 DOCTEST_CHECK(result3.primitive_UUID == triangle1);
3159 DOCTEST_CHECK(result4.hit ==
false);
3162DOCTEST_TEST_CASE(
"CollisionDetection Batch Ray Casting") {
3165 collision.disableMessages();
3172 std::vector<CollisionDetection::RayQuery> queries;
3180 std::vector<CollisionDetection::HitResult> results = collision.castRays(queries, &stats);
3183 DOCTEST_CHECK(results.size() == 4);
3189 DOCTEST_CHECK(results[0].hit ==
true);
3190 DOCTEST_CHECK(results[1].hit ==
false);
3191 DOCTEST_CHECK(results[2].hit ==
true);
3192 DOCTEST_CHECK(results[3].hit ==
true);
3195 for (
const auto &result: results) {
3197 DOCTEST_CHECK(result.primitive_UUID == triangle);
3198 DOCTEST_CHECK(result.distance > 0);
3203DOCTEST_TEST_CASE(
"CollisionDetection Grid Ray Intersection") {
3206 collision.disableMessages();
3217 std::vector<CollisionDetection::RayQuery> rays;
3222 auto grid_results = collision.performGridRayIntersection(grid_center, grid_size, grid_divisions, rays);
3224 DOCTEST_CHECK(grid_results.size() == 2);
3225 DOCTEST_CHECK(grid_results[0].size() == 2);
3226 DOCTEST_CHECK(grid_results[0][0].size() == 2);
3230 for (
int i = 0; i < 2; i++) {
3231 for (
int j = 0; j < 2; j++) {
3232 for (
int k = 0; k < 2; k++) {
3233 total_hits += grid_results[i][j][k].size();
3237 DOCTEST_CHECK(total_hits >= 1);
3240DOCTEST_TEST_CASE(
"CollisionDetection Ray Path Lengths Detailed") {
3243 collision.disableMessages();
3253 std::vector<vec3> ray_origins = {
3259 std::vector<CollisionDetection::HitResult> hit_results;
3260 collision.calculateRayPathLengthsDetailed(grid_center, grid_size, grid_divisions, ray_origins, ray_directions, hit_results);
3262 DOCTEST_CHECK(hit_results.size() == 3);
3265 DOCTEST_CHECK(hit_results[0].hit ==
true);
3266 DOCTEST_CHECK(hit_results[1].hit ==
true);
3267 DOCTEST_CHECK(hit_results[2].hit ==
false);
3270 DOCTEST_CHECK(hit_results[0].distance > 1.5f);
3271 DOCTEST_CHECK(hit_results[0].distance < 2.5f);
3272 DOCTEST_CHECK(hit_results[1].distance > 1.5f);
3273 DOCTEST_CHECK(hit_results[1].distance < 2.5f);
3276 int P_denom, P_trans;
3277 collision.getVoxelTransmissionProbability(
make_int3(0, 0, 0), P_denom, P_trans);
3278 DOCTEST_CHECK(P_denom >= 0);
3281DOCTEST_TEST_CASE(
"CollisionDetection Ray Casting - Normal Calculation") {
3284 collision.disableMessages();
3295 CollisionDetection::CollisionDetection::HitResult result = collision.castRay(ray_origin, ray_direction);
3297 DOCTEST_CHECK(result.hit ==
true);
3301 float dot_product = result.normal.x * expected_normal.
x + result.normal.y * expected_normal.
y + result.normal.z * expected_normal.
z;
3302 DOCTEST_CHECK(std::abs(dot_product) > 0.9f);
3312 DOCTEST_CHECK(patch_result.
hit ==
true);
3320DOCTEST_TEST_CASE(
"CollisionDetection Ray Casting - Edge Cases and Error Handling") {
3323 collision.disableMessages();
3331 DOCTEST_CHECK(zero_result.
hit ==
false);
3340 DOCTEST_CHECK(inf_result.
hit ==
true);
3344 DOCTEST_CHECK(neg_result.
hit ==
true);
3347 std::vector<uint> empty_targets;
3349 DOCTEST_CHECK(empty_result.
hit ==
true);
3352 std::vector<uint> invalid_targets = {99999, triangle, 88888};
3354 DOCTEST_CHECK(invalid_result.
hit ==
true);
3358DOCTEST_TEST_CASE(
"CollisionDetection Ray Casting - Performance and Scalability") {
3361 collision.disableMessages();
3364 std::vector<uint> triangles;
3365 for (
int i = 0; i < 100; i++) {
3368 triangles.push_back(triangle);
3372 std::vector<CollisionDetection::RayQuery> many_rays;
3373 for (
int i = 0; i < 200; i++) {
3374 float x = (i % 100) * 0.1f + 0.025f;
3378 auto start_time = std::chrono::high_resolution_clock::now();
3381 std::vector<CollisionDetection::HitResult> results = collision.castRays(many_rays, &stats);
3383 auto end_time = std::chrono::high_resolution_clock::now();
3384 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
3386 DOCTEST_CHECK(results.size() == 200);
3391 DOCTEST_CHECK(duration.count() < 1000);
3398DOCTEST_TEST_CASE(
"CollisionDetection Ray Casting - Integration with Existing BVH") {
3401 collision.disableMessages();
3408 collision.buildBVH();
3411 DOCTEST_CHECK(result1.
hit ==
true);
3415 DOCTEST_CHECK(result2.
hit ==
true);
3419 collision.enableAutomaticBVHRebuilds();
3426 DOCTEST_CHECK(result3.
hit ==
true);
3430DOCTEST_TEST_CASE(
"CollisionDetection Ray Casting - Compatibility with Other Plugin Methods") {
3433 collision.disableMessages();
3439 auto collisions = collision.findCollisions(triangles[0]);
3440 DOCTEST_CHECK(collisions.size() > 0);
3443 DOCTEST_CHECK(triangles.size() == 5);
3446 auto cone_result = collision.findOptimalConePath(
make_vec3(0, -2, 0),
make_vec3(0, 1, 0),
M_PI / 6, 3.0f);
3450 DOCTEST_CHECK(collisions.size() > 0);
3451 DOCTEST_CHECK(
true);
3458DOCTEST_TEST_CASE(
"CollisionDetection - BVH Optimization Mode Management") {
3461 collision.disableMessages();
3475 collision.buildBVH();
3482 auto memory_stats = collision.getBVHMemoryUsage();
3483 DOCTEST_CHECK(memory_stats.soa_memory_bytes > 0);
3486 DOCTEST_CHECK(memory_stats.quantized_memory_bytes == 0);
3487 DOCTEST_CHECK(memory_stats.quantized_reduction_percent == 0.0f);
3490DOCTEST_TEST_CASE(
"CollisionDetection - Optimized Ray Casting Correctness") {
3493 collision.disableMessages();
3500 std::vector<CollisionDetection::RayQuery> rays;
3506 std::vector<CollisionDetection::HitResult> legacy_results, soa_results;
3510 legacy_results = collision.castRays(rays);
3514 soa_results = collision.castRaysOptimized(rays);
3517 DOCTEST_REQUIRE(legacy_results.size() == 3);
3518 DOCTEST_REQUIRE(soa_results.size() == 3);
3520 for (
size_t i = 0; i < legacy_results.size(); i++) {
3522 DOCTEST_CHECK(legacy_results[i].hit == soa_results[i].hit);
3524 if (legacy_results[i].hit) {
3526 DOCTEST_CHECK(legacy_results[i].primitive_UUID == soa_results[i].primitive_UUID);
3529 DOCTEST_CHECK(std::abs(legacy_results[i].distance - soa_results[i].distance) < 0.001f);
3534 DOCTEST_CHECK(legacy_results[0].hit ==
true);
3535 DOCTEST_CHECK(legacy_results[1].hit ==
true);
3536 DOCTEST_CHECK(legacy_results[2].hit ==
false);
3539DOCTEST_TEST_CASE(
"CollisionDetection - Ray Streaming Interface") {
3542 collision.disableMessages();
3546 for (
int i = 0; i < 5; i++) {
3553 std::vector<CollisionDetection::RayQuery> batch;
3556 for (
int i = 0; i < 50; i++) {
3557 float x = (i % 5) * 2.0f + 0.5f;
3563 DOCTEST_CHECK(stream.
packets.size() > 0);
3567 bool success = collision.processRayStream(stream, &stats);
3568 DOCTEST_CHECK(success ==
true);
3573 DOCTEST_CHECK(results.size() == 50);
3576 size_t hit_count = 0;
3577 for (
const auto &result: results) {
3581 DOCTEST_CHECK(hit_count > 40);
3584DOCTEST_TEST_CASE(
"CollisionDetection - BVH Layout Conversion Methods") {
3587 collision.disableMessages();
3596 collision.buildBVH();
3599 std::vector<CollisionDetection::RayQuery> test_rays = {
3606 auto legacy_results = collision.castRays(test_rays);
3607 auto soa_results = collision.castRaysOptimized(test_rays);
3608 auto memory_stats = collision.getBVHMemoryUsage();
3611 DOCTEST_REQUIRE(legacy_results.size() == 3);
3612 DOCTEST_REQUIRE(soa_results.size() == 3);
3615 size_t legacy_hits = 0, soa_hits = 0;
3616 for (
size_t i = 0; i < 3; i++) {
3617 if (legacy_results[i].hit)
3619 if (soa_results[i].hit)
3624 DOCTEST_CHECK(legacy_hits == soa_hits);
3627 DOCTEST_CHECK(memory_stats.soa_memory_bytes > 0);
3628 DOCTEST_CHECK(memory_stats.quantized_memory_bytes == 0);
3629 DOCTEST_CHECK(memory_stats.quantized_reduction_percent == 0.0f);
3632DOCTEST_TEST_CASE(
"CollisionDetection - RayPacket Edge Cases and Functionality") {
3635 collision.disableMessages();
3639 DOCTEST_CHECK(empty_packet.
ray_count == 0);
3644 DOCTEST_CHECK_NOTHROW(empty_packet.
clear());
3651 std::vector<CollisionDetection::RayQuery> test_queries;
3652 for (
int i = 0; i < 150; i++) {
3655 test_queries.push_back(query);
3656 capacity_packet.
addRay(query);
3659 DOCTEST_CHECK(capacity_packet.
ray_count == 150);
3660 DOCTEST_CHECK(capacity_packet.
origins.size() == 150);
3661 DOCTEST_CHECK(capacity_packet.
directions.size() == 150);
3662 DOCTEST_CHECK(capacity_packet.
results.size() == 150);
3665 auto converted_queries = capacity_packet.
toRayQueries();
3666 DOCTEST_REQUIRE(converted_queries.size() == 150);
3668 for (
size_t i = 0; i < 150; i++) {
3669 DOCTEST_CHECK(converted_queries[i].origin.
magnitude() == test_queries[i].origin.magnitude());
3670 DOCTEST_CHECK(converted_queries[i].direction.magnitude() == test_queries[i].direction.magnitude());
3671 DOCTEST_CHECK(converted_queries[i].max_distance == test_queries[i].max_distance);
3675 size_t expected_memory = (150 * 2) *
sizeof(
helios::vec3) +
3676 150 *
sizeof(float) +
3679 DOCTEST_CHECK(actual_memory >= expected_memory);
3682 capacity_packet.
clear();
3683 DOCTEST_CHECK(capacity_packet.
ray_count == 0);
3684 DOCTEST_CHECK(capacity_packet.
origins.empty());
3685 DOCTEST_CHECK(capacity_packet.
directions.empty());
3686 DOCTEST_CHECK(capacity_packet.
results.empty());
3690DOCTEST_TEST_CASE(
"CollisionDetection - RayStream Batch Management") {
3693 collision.disableMessages();
3697 for (
int i = 0; i < 3; i++) {
3704 std::vector<CollisionDetection::RayQuery> large_batch;
3708 for (
size_t i = 0; i < total_rays; i++) {
3709 float x = (i % 3) * 3.0f + 0.5f;
3713 large_stream.
addRays(large_batch);
3714 DOCTEST_CHECK(large_stream.
total_rays == total_rays);
3715 DOCTEST_CHECK(large_stream.
packets.size() == 3);
3719 DOCTEST_CHECK(stream_memory_before > 0);
3722 bool large_success = collision.processRayStream(large_stream, &large_stats);
3723 DOCTEST_CHECK(large_success ==
true);
3728 DOCTEST_CHECK(all_results.size() == total_rays);
3731 size_t hit_count = 0;
3732 for (
const auto &result: all_results) {
3736 float hit_rate = float(hit_count) / float(total_rays);
3737 DOCTEST_CHECK(hit_rate > 0.8f);
3742 DOCTEST_CHECK(empty_stream.
packets.empty());
3746 bool empty_success = collision.processRayStream(empty_stream, &empty_stats);
3747 DOCTEST_CHECK(empty_success ==
true);
3751 large_stream.
clear();
3753 DOCTEST_CHECK(large_stream.
packets.empty());
3758DOCTEST_TEST_CASE(
"CollisionDetection - SoA Precision Validation") {
3761 collision.disableMessages();
3770 collision.buildBVH();
3772 std::vector<CollisionDetection::RayQuery> precision_test_rays = {
3779 CollisionDetection::RayQuery(
make_vec3(20, -1, 0),
make_vec3(0, 1, 0)),
CollisionDetection::RayQuery(
make_vec3(-5, -1, 0),
make_vec3(0, 1, 0))};
3781 auto legacy_results = collision.castRays(precision_test_rays);
3782 auto soa_results = collision.castRaysOptimized(precision_test_rays);
3784 DOCTEST_REQUIRE(legacy_results.size() == precision_test_rays.size());
3785 DOCTEST_REQUIRE(soa_results.size() == precision_test_rays.size());
3788 for (
size_t i = 0; i < precision_test_rays.size(); i++) {
3789 DOCTEST_CHECK(legacy_results[i].hit == soa_results[i].hit);
3791 if (legacy_results[i].hit && soa_results[i].hit) {
3792 DOCTEST_CHECK(legacy_results[i].primitive_UUID == soa_results[i].primitive_UUID);
3793 DOCTEST_CHECK(std::abs(legacy_results[i].distance - soa_results[i].distance) < 0.001f);
3798 auto memory_stats = collision.getBVHMemoryUsage();
3799 DOCTEST_CHECK(memory_stats.soa_memory_bytes > 0);
3800 DOCTEST_CHECK(memory_stats.quantized_memory_bytes == 0);
3801 DOCTEST_CHECK(memory_stats.quantized_reduction_percent == 0.0f);
3804DOCTEST_TEST_CASE(
"CollisionDetection - Error Handling and Edge Cases") {
3807 collision.disableMessages();
3815 auto initial_mode = collision.getBVHOptimizationMode();
3816 DOCTEST_CHECK_NOTHROW(collision.setBVHOptimizationMode(initial_mode));
3817 DOCTEST_CHECK(collision.getBVHOptimizationMode() == initial_mode);
3820 auto empty_memory_stats = collision.getBVHMemoryUsage();
3821 DOCTEST_CHECK(empty_memory_stats.soa_memory_bytes == 0);
3822 DOCTEST_CHECK(empty_memory_stats.quantized_memory_bytes == 0);
3827 DOCTEST_CHECK_NOTHROW(collision.castRays(empty_test_rays));
3828 DOCTEST_CHECK_NOTHROW(collision.castRaysOptimized(empty_test_rays));
3830 auto empty_results = collision.castRaysOptimized(empty_test_rays);
3831 DOCTEST_CHECK(empty_results.size() == 1);
3832 DOCTEST_CHECK(empty_results[0].hit ==
false);
3837 DOCTEST_CHECK_NOTHROW(collision.processRayStream(empty_stream, &empty_stats));
3841 collision.buildBVH();
3845 auto recovery_results = collision.castRaysOptimized(recovery_rays);
3846 DOCTEST_CHECK(recovery_results.size() == 1);
3847 DOCTEST_CHECK(recovery_results[0].hit ==
true);
3850DOCTEST_TEST_CASE(
"CollisionDetection - Memory and Statistics Validation") {
3853 collision.disableMessages();
3856 for (
int i = 0; i < 8; i++) {
3858 float y = (i % 2) * 2.0f;
3865 collision.buildBVH();
3866 auto legacy_memory = collision.getBVHMemoryUsage();
3869 auto memory_stats = collision.getBVHMemoryUsage();
3872 DOCTEST_CHECK(memory_stats.soa_memory_bytes > 0);
3873 DOCTEST_CHECK(memory_stats.quantized_memory_bytes == 0);
3874 DOCTEST_CHECK(memory_stats.quantized_reduction_percent == 0.0f);
3877 std::vector<CollisionDetection::RayQuery> stat_test_rays;
3878 for (
int i = 0; i < 20; i++) {
3879 float x = (i % 4) * 2.0f + 0.5f;
3880 float y = (i / 4) * 2.0f + 0.5f;
3885 auto stat_results = collision.castRaysOptimized(stat_test_rays, &stats);
3889 DOCTEST_CHECK(stat_results.size() == 20);
3899 stats_stream.
addRays(stat_test_rays);
3902 bool stream_success = collision.processRayStream(stats_stream, &stream_stats);
3903 DOCTEST_CHECK(stream_success ==
true);
3915DOCTEST_TEST_CASE(
"CollisionDetection Voxel Primitive Intersection - Basic Ray-AABB Tests") {
3918 collision.disableMessages();
3919 collision.disableGPUAcceleration();
3924 collision.buildBVH();
3933 auto results = collision.castRays({ray});
3934 DOCTEST_CHECK(results.size() == 1);
3935 DOCTEST_CHECK(results[0].hit ==
true);
3936 DOCTEST_CHECK(results[0].distance > 3.9f);
3937 DOCTEST_CHECK(results[0].distance < 4.1f);
3938 DOCTEST_CHECK(results[0].primitive_UUID == voxel_uuid);
3948 auto results = collision.castRays({ray});
3949 DOCTEST_CHECK(results.size() == 1);
3950 DOCTEST_CHECK(results[0].hit ==
false);
3960 auto results = collision.castRays({ray});
3961 DOCTEST_CHECK(results.size() == 1);
3962 DOCTEST_CHECK(results[0].hit ==
true);
3963 DOCTEST_CHECK(results[0].distance > 0.9f);
3964 DOCTEST_CHECK(results[0].distance < 1.1f);
3968DOCTEST_TEST_CASE(
"CollisionDetection Voxel Primitive Intersection - Multiple Voxels") {
3971 collision.disableMessages();
3972 collision.disableGPUAcceleration();
3979 collision.buildBVH();
3988 auto results = collision.castRays({ray});
3989 DOCTEST_CHECK(results.size() == 1);
3990 DOCTEST_CHECK(results[0].hit ==
true);
3991 DOCTEST_CHECK(results[0].primitive_UUID == voxel1);
4001 auto results = collision.castRays({ray});
4002 DOCTEST_CHECK(results.size() == 1);
4003 DOCTEST_CHECK(results[0].hit ==
true);
4004 DOCTEST_CHECK(results[0].primitive_UUID == voxel1);
4005 DOCTEST_CHECK(results[0].distance > 2.9f);
4006 DOCTEST_CHECK(results[0].distance < 3.1f);
4010DOCTEST_TEST_CASE(
"CollisionDetection Voxel Primitive Intersection - GPU vs CPU Consistency") {
4013 collision.disableMessages();
4016 std::vector<uint> voxel_uuids;
4022 std::vector<CollisionDetection::RayQuery> test_rays;
4025 for (
int i = 0; i < 5; i++) {
4026 for (
int j = 0; j < 3; j++) {
4031 test_rays.push_back(ray);
4035 collision.buildBVH();
4038 collision.disableGPUAcceleration();
4039 auto cpu_results = collision.castRays(test_rays);
4042 std::vector<CollisionDetection::HitResult> gpu_results;
4045 collision.enableGPUAcceleration();
4046 gpu_results = collision.castRays(test_rays);
4050 DOCTEST_CHECK(cpu_results.size() == gpu_results.size());
4051 DOCTEST_CHECK(cpu_results.size() == test_rays.size());
4053 for (
size_t i = 0; i < cpu_results.size(); i++) {
4054 DOCTEST_CHECK(cpu_results[i].hit == gpu_results[i].hit);
4056 if (cpu_results[i].hit && gpu_results[i].hit) {
4058 DOCTEST_CHECK(std::abs(cpu_results[i].distance - gpu_results[i].distance) < 0.01f);
4059 DOCTEST_CHECK(cpu_results[i].primitive_UUID == gpu_results[i].primitive_UUID);
4066DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Ray-Triangle Intersection Algorithms") {
4069 collision.disableMessages();
4070 collision.disableGPUAcceleration();
4079 vec3 ray_origin =
make_vec3(1.0f / 3.0f, 1.0f / 3.0f, -1.0f);
4084 DOCTEST_CHECK(result.
hit ==
true);
4088 DOCTEST_CHECK(std::abs(result.
distance - 1.0f) < 1e-6f);
4096 vec3 expected_normal = normalize(
cross(v1 - v0, v2 - v0));
4097 float normal_dot = result.
normal.
x * expected_normal.
x + result.
normal.
y * expected_normal.
y + result.
normal.
z * expected_normal.
z;
4098 DOCTEST_CHECK(std::abs(normal_dot - 1.0f) < 1e-6f);
4101DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Edge Case Intersections") {
4104 collision.disableMessages();
4105 collision.disableGPUAcceleration();
4119 DOCTEST_CHECK(result.
hit ==
true);
4131 if (vertex_result.
hit) {
4138DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Barycentric Coordinate Validation") {
4141 collision.disableMessages();
4142 collision.disableGPUAcceleration();
4145 float sqrt3 = std::sqrt(3.0f);
4152 vec3 centroid = (v0 + v1 + v2) * (1.0f / 3.0f);
4158 DOCTEST_CHECK(result.
hit ==
true);
4163 vec3 midpoint_v0_v1 = (v0 + v1) * 0.5f;
4164 vec3 midpoint_ray_origin =
make_vec3(midpoint_v0_v1.
x, midpoint_v0_v1.
y, -1);
4168 DOCTEST_CHECK(midpoint_result.
hit ==
true);
4175DOCTEST_TEST_CASE(
"CollisionDetection GPU-Specific - Direct castRaysGPU Testing") {
4178 collision.disableMessages();
4181 std::vector<uint> uuids;
4187 std::vector<CollisionDetection::RayQuery> queries;
4188 for (
int i = 0; i < 100; i++) {
4193 queries.push_back(query);
4198 collision.enableGPUAcceleration();
4199#ifdef HELIOS_CUDA_AVAILABLE
4200 if (collision.isGPUAccelerationEnabled()) {
4202 std::vector<CollisionDetection::HitResult> gpu_results = collision.castRaysGPU(queries, gpu_stats);
4204 DOCTEST_CHECK(gpu_results.size() == queries.size());
4208 collision.disableGPUAcceleration();
4210 std::vector<CollisionDetection::HitResult> cpu_results = collision.castRays(queries, &cpu_stats);
4213 DOCTEST_CHECK(cpu_results.size() == gpu_results.size());
4215 int consistent_hits = 0;
4216 for (
size_t i = 0; i < cpu_results.size(); i++) {
4217 if (cpu_results[i].hit == gpu_results[i].hit) {
4219 if (cpu_results[i].hit) {
4221 DOCTEST_CHECK(std::abs(cpu_results[i].distance - gpu_results[i].distance) < 0.001f);
4222 DOCTEST_CHECK(cpu_results[i].primitive_UUID == gpu_results[i].primitive_UUID);
4228 DOCTEST_CHECK(consistent_hits >= (
int) (0.95f * queries.size()));
4231 DOCTEST_WARN(
"GPU acceleration not available - skipping direct GPU tests");
4234 }
catch (std::exception &e) {
4235 DOCTEST_WARN((std::string(
"GPU test failed (expected on non-NVIDIA systems): ") + e.what()).c_str());
4239DOCTEST_TEST_CASE(
"CollisionDetection GPU-Specific - Error Handling and Edge Cases") {
4242 collision.disableMessages();
4245 collision.enableGPUAcceleration();
4246#ifdef HELIOS_CUDA_AVAILABLE
4247 if (collision.isGPUAccelerationEnabled()) {
4250 std::vector<CollisionDetection::RayQuery> empty_queries;
4252 std::vector<CollisionDetection::HitResult> results = collision.castRaysGPU(empty_queries, stats);
4253 DOCTEST_CHECK(results.empty());
4257 std::vector<CollisionDetection::RayQuery> large_batch;
4258 for (
int i = 0; i < 10000; i++) {
4263 large_batch.push_back(query);
4267 std::vector<CollisionDetection::HitResult> large_results = collision.castRaysGPU(large_batch, large_stats);
4268 DOCTEST_CHECK(large_results.size() == large_batch.size());
4272 std::vector<CollisionDetection::RayQuery> degenerate_queries;
4276 degenerate_queries.push_back(degenerate);
4279 std::vector<CollisionDetection::HitResult> degenerate_results = collision.castRaysGPU(degenerate_queries, degenerate_stats);
4280 DOCTEST_CHECK(degenerate_results.size() == 1);
4281 DOCTEST_CHECK(degenerate_results[0].hit ==
false);
4284 DOCTEST_WARN(
"GPU acceleration not available - skipping GPU error handling tests");
4287 }
catch (std::exception &e) {
4288 DOCTEST_WARN((std::string(
"GPU error handling test failed: ") + e.what()).c_str());
4294DOCTEST_TEST_CASE(
"CollisionDetection Floating-Point Precision - Extreme Values") {
4297 collision.disableMessages();
4298 collision.disableGPUAcceleration();
4301 float epsilon = 1e-6f;
4305 uint small_triangle = context.
addTriangle(v0_small, v1_small, v2_small);
4307 vec3 ray_origin =
make_vec3(epsilon / 3.0f, epsilon / 3.0f, -epsilon);
4312 DOCTEST_CHECK(std::isfinite(small_result.
distance));
4318 float large_scale = 1e6f;
4322 uint large_triangle = context.
addTriangle(v0_large, v1_large, v2_large);
4328 DOCTEST_CHECK(std::isfinite(large_result.
distance));
4329 if (large_result.
hit) {
4336DOCTEST_TEST_CASE(
"CollisionDetection Floating-Point Precision - Near-Parallel Rays") {
4339 collision.disableMessages();
4340 collision.disableGPUAcceleration();
4349 float tiny_angle = 1e-6f;
4351 vec3 near_parallel_direction = normalize(
make_vec3(0, tiny_angle, 1));
4356 DOCTEST_CHECK(std::isfinite(near_parallel_result.
distance));
4357 if (near_parallel_result.
hit) {
4361 DOCTEST_CHECK(near_parallel_result.
distance > 0);
4365DOCTEST_TEST_CASE(
"CollisionDetection Floating-Point Precision - Boundary Conditions") {
4368 collision.disableMessages();
4369 collision.disableGPUAcceleration();
4378 float boundary_offset = 1e-8f;
4380 std::vector<vec3> boundary_origins = {
4382 make_vec3(1 + boundary_offset, 0.5f, -1),
4384 make_vec3(0.5f + boundary_offset, 0.5f + boundary_offset, -1)
4387 for (
const auto &origin: boundary_origins) {
4392 DOCTEST_CHECK(std::isfinite(result.
distance));
4403DOCTEST_TEST_CASE(
"CollisionDetection Complex Geometry - Multi-Primitive Accuracy") {
4406 collision.disableMessages();
4407 collision.disableGPUAcceleration();
4410 std::vector<uint> uuids;
4413 for (
int i = 0; i < 5; i++) {
4414 for (
int j = 0; j < 5; j++) {
4418 uuids.push_back(uuid);
4423 int correct_predictions = 0;
4424 int total_predictions = 0;
4426 for (
int i = 0; i < 10; i++) {
4427 for (
int j = 0; j < 10; j++) {
4437 bool should_hit =
false;
4438 for (
int ti = 0; ti < 5; ti++) {
4439 for (
int tj = 0; tj < 5; tj++) {
4440 float tx = ti * 0.8f;
4441 float ty = tj * 0.8f;
4454 float dot00 = v0.
x * v0.
x + v0.
y * v0.
y + v0.
z * v0.
z;
4455 float dot01 = v0.
x * v1.
x + v0.
y * v1.
y + v0.
z * v1.
z;
4456 float dot02 = v0.
x * v2.
x + v0.
y * v2.
y + v0.
z * v2.
z;
4457 float dot11 = v1.
x * v1.
x + v1.
y * v1.
y + v1.
z * v1.
z;
4458 float dot12 = v1.
x * v2.
x + v1.
y * v2.
y + v1.
z * v2.
z;
4460 float inv_denom = 1.0f / (dot00 * dot11 - dot01 * dot01);
4461 float u = (dot11 * dot02 - dot01 * dot12) * inv_denom;
4462 float v = (dot00 * dot12 - dot01 * dot02) * inv_denom;
4465 const float EPSILON = 1e-6f;
4466 if ((u >= -EPSILON) && (v >= -EPSILON) && (u + v <= 1 + EPSILON)) {
4475 if (result.
hit == should_hit) {
4476 correct_predictions++;
4478 total_predictions++;
4483 float accuracy = (float) correct_predictions / (
float) total_predictions;
4484 DOCTEST_CHECK(accuracy == 1.0f);
4487DOCTEST_TEST_CASE(
"CollisionDetection Complex Geometry - Stress Test Validation") {
4490 collision.disableMessages();
4491 collision.disableGPUAcceleration();
4494 std::vector<uint> stress_uuids;
4495 for (
int i = 0; i < 1000; i++) {
4496 float x = (rand() % 100) * 0.01f;
4497 float y = (rand() % 100) * 0.01f;
4498 float z = (rand() % 10) * 0.01f;
4499 float size = 0.1f + (rand() % 10) * 0.01f;
4502 stress_uuids.push_back(uuid);
4506 std::vector<CollisionDetection::RayQuery> stress_queries;
4507 for (
int i = 0; i < 500; i++) {
4509 query.
origin =
make_vec3((rand() % 200) * 0.01f - 1.0f, (rand() % 200) * 0.01f - 1.0f, -1.0f);
4512 stress_queries.push_back(query);
4516 std::vector<CollisionDetection::HitResult> stress_results = collision.castRays(stress_queries, &stats);
4518 DOCTEST_CHECK(stress_results.size() == stress_queries.size());
4522 int valid_results = 0;
4523 for (
const auto &result: stress_results) {
4529 DOCTEST_CHECK(valid_results == (
int) stress_results.size());
4534DOCTEST_TEST_CASE(
"CollisionDetection Performance Regression - BVH Construction Timing") {
4537 collision.disableMessages();
4540 std::vector<uint> large_geometry;
4541 for (
int i = 0; i < 5000; i++) {
4544 large_geometry.push_back(uuid);
4548 auto start_time = std::chrono::high_resolution_clock::now();
4549 collision.buildBVH();
4550 auto end_time = std::chrono::high_resolution_clock::now();
4552 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
4555 DOCTEST_CHECK(duration.count() < 5000);
4558 DOCTEST_CHECK(collision.isBVHValid() ==
true);
4559 DOCTEST_CHECK(collision.getPrimitiveCount() == large_geometry.size());
4561 size_t node_count, leaf_count, max_depth;
4562 collision.getBVHStatistics(node_count, leaf_count, max_depth);
4565 DOCTEST_CHECK(node_count > 0);
4566 DOCTEST_CHECK(leaf_count > 0);
4567 DOCTEST_CHECK(max_depth > 0);
4568 DOCTEST_CHECK(max_depth < 50);
4571DOCTEST_TEST_CASE(
"CollisionDetection Performance Regression - Ray Casting Throughput") {
4574 collision.disableMessages();
4575 collision.disableGPUAcceleration();
4578 for (
int i = 0; i < 1000; i++) {
4579 float x = (i % 50) * 0.2f;
4580 float y = (i / 50) * 0.2f;
4585 std::vector<CollisionDetection::RayQuery> throughput_queries;
4586 for (
int i = 0; i < 10000; i++) {
4590 throughput_queries.push_back(query);
4594 auto start_time = std::chrono::high_resolution_clock::now();
4596 std::vector<CollisionDetection::HitResult> results = collision.castRays(throughput_queries, &stats);
4597 auto end_time = std::chrono::high_resolution_clock::now();
4599 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
4602 DOCTEST_CHECK(duration.count() < 2000);
4605 DOCTEST_CHECK(results.size() == throughput_queries.size());
4609 float rays_per_second = (float) throughput_queries.size() / (duration.count() / 1000.0f);
4610 DOCTEST_CHECK(rays_per_second > 1000.0f);
4613DOCTEST_TEST_CASE(
"CollisionDetection Performance Regression - Memory Usage Validation") {
4616 collision.disableMessages();
4619 for (
int i = 0; i < 2000; i++) {
4620 float x = i * 0.05f;
4626 collision.buildBVH();
4628 auto memory_stats = collision.getBVHMemoryUsage();
4631 DOCTEST_CHECK(memory_stats.soa_memory_bytes < 100 * 1024 * 1024);
4632 DOCTEST_CHECK(memory_stats.soa_memory_bytes > 0);
4636 std::vector<CollisionDetection::RayQuery> stream_queries;
4638 for (
int i = 0; i < 5000; i++) {
4642 stream_queries.push_back(query);
4645 stream.
addRays(stream_queries);
4649 DOCTEST_CHECK(stream_memory < 50 * 1024 * 1024);
4650 DOCTEST_CHECK(stream_memory > 0);
4655DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Ray-Patch Intersection") {
4658 collision.disableMessages();
4659 collision.disableGPUAcceleration();
4674 DOCTEST_CHECK(result.
hit ==
true);
4678 DOCTEST_CHECK(std::abs(result.
distance - 1.0f) < 1e-6f);
4686DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Ray-Patch Edge Cases") {
4689 collision.disableMessages();
4690 collision.disableGPUAcceleration();
4701 DOCTEST_CHECK(edge_result.
hit ==
true);
4712 DOCTEST_CHECK(corner_result.
hit ==
true);
4718DOCTEST_TEST_CASE(
"CollisionDetection Complex Geometry - Multi-Patch Accuracy") {
4721 collision.disableMessages();
4722 collision.disableGPUAcceleration();
4725 std::vector<uint> uuids;
4728 for (
int i = 0; i < 5; i++) {
4729 for (
int j = 0; j < 5; j++) {
4733 uuids.push_back(uuid);
4738 int correct_predictions = 0;
4739 int total_predictions = 0;
4741 for (
int i = 0; i < 10; i++) {
4742 for (
int j = 0; j < 10; j++) {
4752 bool should_hit =
false;
4753 for (
int ti = 0; ti < 5; ti++) {
4754 for (
int tj = 0; tj < 5; tj++) {
4755 float px = ti * 0.8f;
4756 float py = tj * 0.8f;
4759 const float EPSILON = 1e-6f;
4760 if ((x >= px - 0.25f - EPSILON) && (x <= px + 0.25f + EPSILON) && (y >= py - 0.25f - EPSILON) && (y <= py + 0.25f + EPSILON)) {
4769 if (result.
hit == should_hit) {
4770 correct_predictions++;
4772 total_predictions++;
4777 float accuracy = (float) correct_predictions / (
float) total_predictions;
4778 DOCTEST_CHECK(accuracy == 1.0f);
4783DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Ray-Voxel Intersection") {
4786 collision.disableMessages();
4787 collision.disableGPUAcceleration();
4798 DOCTEST_CHECK(result.
hit ==
true);
4802 DOCTEST_CHECK(std::abs(result.
distance - 1.0f) < 1e-6f);
4810DOCTEST_TEST_CASE(
"CollisionDetection Mathematical Accuracy - Ray-Voxel Edge Cases") {
4813 collision.disableMessages();
4814 collision.disableGPUAcceleration();
4825 DOCTEST_CHECK(edge_result.
hit ==
true);
4827 DOCTEST_CHECK(std::abs(edge_result.
distance - 1.0f) < 1e-6f);
4838 DOCTEST_CHECK(corner_result.
hit ==
true);
4844DOCTEST_TEST_CASE(
"CollisionDetection Complex Geometry - Multi-Voxel Accuracy") {
4847 collision.disableMessages();
4848 collision.disableGPUAcceleration();
4851 std::vector<uint> uuids;
4854 for (
int i = 0; i < 3; i++) {
4855 for (
int j = 0; j < 3; j++) {
4856 for (
int k = 0; k < 3; k++) {
4861 uuids.push_back(uuid);
4867 int correct_predictions = 0;
4868 int total_predictions = 0;
4870 for (
int i = 0; i < 10; i++) {
4871 for (
int j = 0; j < 10; j++) {
4881 bool should_hit =
false;
4882 for (
int vi = 0; vi < 3; vi++) {
4883 for (
int vj = 0; vj < 3; vj++) {
4884 for (
int vk = 0; vk < 3; vk++) {
4885 float vx = vi * 1.5f;
4886 float vy = vj * 1.5f;
4887 float vz = vk * 1.5f;
4890 float voxel_x_min = vx - 0.5f;
4891 float voxel_x_max = vx + 0.5f;
4892 float voxel_y_min = vy - 0.5f;
4893 float voxel_y_max = vy + 0.5f;
4894 float voxel_z_min = vz - 0.5f;
4895 float voxel_z_max = vz + 0.5f;
4898 const float EPSILON = 1e-6f;
4899 if ((x >= voxel_x_min - EPSILON) && (x <= voxel_x_max + EPSILON) && (y >= voxel_y_min - EPSILON) && (y <= voxel_y_max + EPSILON)) {
4901 if (voxel_z_max >= -1.0f - EPSILON) {
4914 if (result.
hit == should_hit) {
4915 correct_predictions++;
4917 total_predictions++;
4922 float accuracy = (float) correct_predictions / (
float) total_predictions;
4923 DOCTEST_CHECK(accuracy == 1.0f);
4926DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - Basic getVoxelRayHitCounts Functionality") {
4929 collision.disableMessages();
4935 vec3 grid_center(0, 0, 0);
4936 vec3 grid_size(4, 4, 4);
4937 int3 grid_divisions(2, 2, 2);
4940 std::vector<vec3> ray_origins;
4941 std::vector<vec3> ray_directions;
4944 ray_origins.push_back(
make_vec3(0, 0, -3));
4945 ray_directions.push_back(
make_vec3(0, 0, 1));
4948 ray_origins.push_back(
make_vec3(-1.5, -1.5, -3));
4949 ray_directions.push_back(
make_vec3(0, 0, 1));
4952 ray_origins.push_back(
make_vec3(0, 0, -0.5));
4953 ray_directions.push_back(
make_vec3(0, 0, 1));
4956 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
4959 int hit_before, hit_after, hit_inside;
4960 collision.getVoxelRayHitCounts(
make_int3(1, 1, 0), hit_before, hit_after, hit_inside);
4963 DOCTEST_CHECK(hit_before >= 0);
4964 DOCTEST_CHECK(hit_after >= 0);
4965 DOCTEST_CHECK(hit_inside >= 0);
4968 collision.getVoxelRayHitCounts(
make_int3(0, 0, 0), hit_before, hit_after, hit_inside);
4971 DOCTEST_CHECK(hit_before >= 0);
4972 DOCTEST_CHECK(hit_after >= 0);
4973 DOCTEST_CHECK(hit_inside >= 0);
4976DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - getVoxelRayPathLengths Individual Lengths") {
4979 collision.disableMessages();
4982 vec3 grid_center(0, 0, 0);
4983 vec3 grid_size(2, 2, 2);
4984 int3 grid_divisions(1, 1, 1);
4987 std::vector<vec3> ray_origins;
4988 std::vector<vec3> ray_directions;
4991 ray_origins.push_back(
make_vec3(0, 0, -2));
4992 ray_directions.push_back(
make_vec3(0, 0, 1));
4995 ray_origins.push_back(
make_vec3(-2, -2, -2));
4996 ray_directions.push_back(normalize(
make_vec3(1, 1, 1)));
4999 ray_origins.push_back(
make_vec3(0, -2, -2));
5000 ray_directions.push_back(normalize(
make_vec3(0, 1, 1)));
5002 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
5005 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(
make_int3(0, 0, 0));
5008 DOCTEST_CHECK(path_lengths.size() >= 1);
5011 float max_diagonal = 2.0f * sqrt(3.0f);
5012 for (
float length: path_lengths) {
5013 DOCTEST_CHECK(length > 0.0f);
5014 DOCTEST_CHECK(length <= max_diagonal + 1e-6f);
5018 bool found_center_ray =
false;
5019 bool found_diagonal_ray =
false;
5021 for (
float length: path_lengths) {
5022 if (std::abs(length - 2.0f) < 0.1f) {
5023 found_center_ray =
true;
5025 if (std::abs(length - 2.0f * sqrt(3.0f)) < 0.1f) {
5026 found_diagonal_ray =
true;
5030 DOCTEST_CHECK(found_center_ray);
5033DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - Beer's Law Scenario with Geometry") {
5036 collision.disableMessages();
5042 vec3 grid_center(0, 0, 0);
5043 vec3 grid_size(6, 6, 6);
5044 int3 grid_divisions(3, 3, 3);
5047 std::vector<vec3> ray_origins;
5048 std::vector<vec3> ray_directions;
5050 int num_rays_per_axis = 10;
5051 for (
int i = 0; i < num_rays_per_axis; i++) {
5052 for (
int j = 0; j < num_rays_per_axis; j++) {
5053 float x = -2.5f + (5.0f * i) / (num_rays_per_axis - 1);
5054 float y = -2.5f + (5.0f * j) / (num_rays_per_axis - 1);
5056 ray_origins.push_back(
make_vec3(x, y, -4));
5057 ray_directions.push_back(
make_vec3(0, 0, 1));
5061 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
5064 int hit_before, hit_after, hit_inside;
5065 collision.getVoxelRayHitCounts(
make_int3(1, 1, 1), hit_before, hit_after, hit_inside);
5072 DOCTEST_CHECK(hit_before >= 0);
5073 DOCTEST_CHECK(hit_after >= 0);
5074 DOCTEST_CHECK(hit_inside >= 0);
5075 DOCTEST_CHECK((hit_before + hit_after + hit_inside) > 0);
5078 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(
make_int3(1, 1, 1));
5079 DOCTEST_CHECK(path_lengths.size() > 0);
5082 collision.getVoxelRayHitCounts(
make_int3(0, 0, 0), hit_before, hit_after, hit_inside);
5086 DOCTEST_CHECK(hit_before >= 0);
5087 DOCTEST_CHECK(hit_after >= 0);
5088 DOCTEST_CHECK(hit_inside >= 0);
5091DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - Edge Cases and Boundary Conditions") {
5094 collision.disableMessages();
5100 vec3 grid_center(0, 0, 0);
5101 vec3 grid_size(4, 4, 4);
5102 int3 grid_divisions(2, 2, 2);
5104 std::vector<vec3> ray_origins;
5105 std::vector<vec3> ray_directions;
5108 ray_origins.push_back(
make_vec3(0, 0, -0.5));
5109 ray_directions.push_back(
make_vec3(0, 0, 1));
5112 ray_origins.push_back(
make_vec3(-1.99, -1.99, -3));
5113 ray_directions.push_back(
make_vec3(0, 0, 1));
5116 ray_origins.push_back(
make_vec3(-2, 0, 0));
5117 ray_directions.push_back(
make_vec3(1, 0, 0));
5120 ray_origins.push_back(
make_vec3(-2, -2, -2));
5121 ray_directions.push_back(normalize(
make_vec3(1, 1, 1)));
5123 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
5126 for (
int i = 0; i < grid_divisions.
x; i++) {
5127 for (
int j = 0; j < grid_divisions.
y; j++) {
5128 for (
int k = 0; k < grid_divisions.
z; k++) {
5129 int hit_before, hit_after, hit_inside;
5133 collision.getVoxelRayHitCounts(voxel_idx, hit_before, hit_after, hit_inside);
5136 DOCTEST_CHECK(hit_before >= 0);
5137 DOCTEST_CHECK(hit_after >= 0);
5138 DOCTEST_CHECK(hit_inside >= 0);
5141 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(voxel_idx);
5142 for (
float length: path_lengths) {
5143 DOCTEST_CHECK(length > 0.0f);
5144 DOCTEST_CHECK(length < 100.0f);
5151DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - Error Handling and Invalid Inputs") {
5154 collision.disableMessages();
5157 bool caught_negative_exception =
false;
5158 bool caught_large_exception =
false;
5159 std::string negative_error_msg;
5160 std::string large_error_msg;
5167 int hit_before, hit_after, hit_inside;
5168 collision.getVoxelRayHitCounts(
make_int3(-1, 0, 0), hit_before, hit_after, hit_inside);
5169 }
catch (
const std::exception &e) {
5170 caught_negative_exception =
true;
5171 negative_error_msg = e.what();
5176 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(
make_int3(100, 100, 100));
5177 }
catch (
const std::exception &e) {
5178 caught_large_exception =
true;
5179 large_error_msg = e.what();
5184 DOCTEST_CHECK(caught_negative_exception);
5185 DOCTEST_CHECK(negative_error_msg.find(
"Invalid voxel indices") != std::string::npos);
5186 DOCTEST_CHECK(caught_large_exception);
5187 DOCTEST_CHECK(large_error_msg.find(
"Invalid voxel indices") != std::string::npos);
5190 int hit_before, hit_after, hit_inside;
5191 collision.getVoxelRayHitCounts(
make_int3(0, 0, 0), hit_before, hit_after, hit_inside);
5194 DOCTEST_CHECK(hit_before == 0);
5195 DOCTEST_CHECK(hit_after == 0);
5196 DOCTEST_CHECK(hit_inside == 0);
5198 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(
make_int3(0, 0, 0));
5199 DOCTEST_CHECK(path_lengths.empty());
5202DOCTEST_TEST_CASE(
"CollisionDetection Ray Classification - Beer's Law Integration Test") {
5205 collision.disableMessages();
5209 std::vector<uint> vegetation_uuids;
5212 for (
int i = 0; i < 3; i++) {
5213 for (
int j = 0; j < 3; j++) {
5214 if ((i + j) % 2 == 0) {
5215 float x = -2.0f + i * 2.0f;
5216 float y = -2.0f + j * 2.0f;
5217 float z = 1.0f + i * 0.5f;
5220 vegetation_uuids.push_back(patch_uuid);
5225 vec3 grid_center(0, 0, 0);
5226 vec3 grid_size(8, 8, 6);
5227 int3 grid_divisions(4, 4, 3);
5230 std::vector<vec3> ray_origins;
5231 std::vector<vec3> ray_directions;
5233 int rays_per_axis = 20;
5234 for (
int i = 0; i < rays_per_axis; i++) {
5235 for (
int j = 0; j < rays_per_axis; j++) {
5236 float x = -3.5f + (7.0f * i) / (rays_per_axis - 1);
5237 float y = -3.5f + (7.0f * j) / (rays_per_axis - 1);
5239 ray_origins.push_back(
make_vec3(x, y, -3));
5240 ray_directions.push_back(
make_vec3(0, 0, 1));
5244 collision.calculateVoxelRayPathLengths(grid_center, grid_size, grid_divisions, ray_origins, ray_directions);
5247 bool found_realistic_data =
false;
5249 for (
int i = 0; i < grid_divisions.
x; i++) {
5250 for (
int j = 0; j < grid_divisions.
y; j++) {
5251 for (
int k = 0; k < grid_divisions.
z; k++) {
5252 int hit_before, hit_after, hit_inside;
5253 collision.getVoxelRayHitCounts(
make_int3(i, j, k), hit_before, hit_after, hit_inside);
5255 std::vector<float> path_lengths = collision.getVoxelRayPathLengths(
make_int3(i, j, k));
5257 if (!path_lengths.empty() && (hit_before + hit_after + hit_inside) > 0) {
5258 found_realistic_data =
true;
5261 int P_denom = path_lengths.size();
5262 int P_trans = P_denom - hit_inside;
5264 DOCTEST_CHECK(P_trans >= 0);
5265 DOCTEST_CHECK(P_trans <= P_denom);
5268 float transmission_probability =
static_cast<float>(P_trans) /
static_cast<float>(P_denom);
5269 DOCTEST_CHECK(transmission_probability >= 0.0f);
5270 DOCTEST_CHECK(transmission_probability <= 1.0f);
5273 if (hit_inside > 0) {
5274 DOCTEST_CHECK(transmission_probability < 1.0f);
5279 float total_path_length = 0.0f;
5280 for (
float length: path_lengths) {
5281 total_path_length += length;
5283 float r_bar = total_path_length / P_denom;
5285 DOCTEST_CHECK(r_bar > 0.0f);
5286 DOCTEST_CHECK(r_bar < 10.0f);
5290 if (P_trans < P_denom && P_trans > 0) {
5291 float ln_arg =
static_cast<float>(P_trans) /
static_cast<float>(P_denom);
5292 DOCTEST_CHECK(ln_arg > 0.0f);
5293 DOCTEST_CHECK(ln_arg <= 1.0f);
5300 DOCTEST_CHECK(found_realistic_data);
5303DOCTEST_TEST_CASE(
"CollisionDetection calculateVoxelPathLengths Enhanced Method") {
5306 collision.disableMessages();
5313 std::vector<vec3> ray_directions;
5314 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.0f, 0.0f)));
5315 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.1f, 0.0f)));
5316 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.0f, 0.1f)));
5319 std::vector<vec3> voxel_centers;
5320 std::vector<vec3> voxel_sizes;
5322 voxel_centers.push_back(
make_vec3(2.0f, 0.0f, 0.0f));
5323 voxel_centers.push_back(
make_vec3(5.0f, 0.0f, 0.0f));
5324 voxel_centers.push_back(
make_vec3(2.0f, 3.0f, 0.0f));
5326 voxel_sizes.push_back(
make_vec3(1.0f, 1.0f, 1.0f));
5327 voxel_sizes.push_back(
make_vec3(1.0f, 1.0f, 1.0f));
5328 voxel_sizes.push_back(
make_vec3(1.0f, 1.0f, 1.0f));
5330 auto result = collision.calculateVoxelPathLengths(scan_origin, ray_directions, voxel_centers, voxel_sizes);
5333 DOCTEST_CHECK(result.size() == 3);
5336 DOCTEST_CHECK(result[0].size() > 0);
5339 DOCTEST_CHECK(result[1].size() > 0);
5343 DOCTEST_CHECK(result[2].size() >= 0);
5346 for (
size_t voxel_idx = 0; voxel_idx < 2; ++voxel_idx) {
5347 for (
const auto &hit: result[voxel_idx]) {
5348 DOCTEST_CHECK(hit.path_length > 0.0f);
5349 DOCTEST_CHECK(hit.path_length <= 2.0f);
5350 DOCTEST_CHECK(hit.hit ==
false);
5351 DOCTEST_CHECK(hit.distance == -1.0f);
5352 DOCTEST_CHECK(hit.primitive_UUID == 0);
5362 std::vector<vec3> ray_directions;
5363 for (
int i = 0; i < 1000; ++i) {
5364 float theta = i * 0.01f;
5365 ray_directions.push_back(normalize(
make_vec3(1.0f, sin(theta), cos(theta))));
5369 std::vector<vec3> voxel_centers;
5370 std::vector<vec3> voxel_sizes;
5371 for (
int x = 0; x < 10; ++x) {
5372 for (
int y = 0; y < 10; ++y) {
5373 voxel_centers.push_back(
make_vec3(x + 1.0f, y - 5.0f, 0.0f));
5374 voxel_sizes.push_back(
make_vec3(0.5f, 0.5f, 0.5f));
5379 auto start_time = std::chrono::high_resolution_clock::now();
5380 auto result = collision.calculateVoxelPathLengths(scan_origin, ray_directions, voxel_centers, voxel_sizes);
5381 auto end_time = std::chrono::high_resolution_clock::now();
5383 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
5386 DOCTEST_CHECK(duration.count() < 500);
5389 DOCTEST_CHECK(result.size() == 100);
5392 size_t total_intersections = 0;
5393 for (
size_t i = 0; i < 100; ++i) {
5394 total_intersections += result[i].size();
5395 for (
const auto &hit: result[i]) {
5396 DOCTEST_CHECK(hit.path_length > 0.0f);
5397 DOCTEST_CHECK(hit.path_length <= 1.0f);
5402 DOCTEST_CHECK(total_intersections > 0);
5410 std::vector<vec3> empty_rays;
5411 std::vector<vec3> voxel_centers = {
make_vec3(1.0f, 0.0f, 0.0f)};
5412 std::vector<vec3> voxel_sizes = {
make_vec3(1.0f, 1.0f, 1.0f)};
5414 auto result = collision.calculateVoxelPathLengths(scan_origin, empty_rays, voxel_centers, voxel_sizes);
5415 DOCTEST_CHECK(result.empty());
5418 std::vector<vec3> ray_directions = {normalize(
make_vec3(1.0f, 0.0f, 0.0f))};
5419 std::vector<vec3> empty_voxels;
5420 std::vector<vec3> empty_sizes;
5422 result = collision.calculateVoxelPathLengths(scan_origin, ray_directions, empty_voxels, empty_sizes);
5423 DOCTEST_CHECK(result.empty());
5426 std::vector<vec3> mismatched_sizes = {
make_vec3(1.0f, 1.0f, 1.0f),
make_vec3(2.0f, 2.0f, 2.0f)};
5428 bool threw_exception =
false;
5432 collision.calculateVoxelPathLengths(scan_origin, ray_directions, voxel_centers, mismatched_sizes);
5433 }
catch (
const std::exception &) {
5434 threw_exception =
true;
5438 DOCTEST_CHECK(threw_exception);
5444 vec3 ray_direction = normalize(
make_vec3(1.0f, 0.0f, 0.0f));
5448 auto result = collision.calculateVoxelPathLengths(scan_origin, {ray_direction}, {voxel_center}, {voxel_size});
5450 DOCTEST_CHECK(result.size() == 1);
5451 DOCTEST_CHECK(result[0].size() == 1);
5455 DOCTEST_CHECK(std::abs(path_length - 2.0f) < 1e-4f);
5461 std::vector<vec3> ray_directions;
5464 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.0f, 0.0f)));
5465 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.2f, 0.0f)));
5466 ray_directions.push_back(normalize(
make_vec3(1.0f, 0.0f, 0.2f)));
5471 auto result = collision.calculateVoxelPathLengths(scan_origin, ray_directions, {voxel_center}, {voxel_size});
5474 DOCTEST_CHECK(result.size() == 1);
5475 DOCTEST_CHECK(result[0].size() == 3);
5478 std::vector<float> path_lengths;
5479 for (
const auto &hit: result[0]) {
5480 path_lengths.push_back(hit.path_length);
5484 for (
float path: path_lengths) {
5485 DOCTEST_CHECK(path > 0.5f);
5486 DOCTEST_CHECK(path < 2.0f);
5491 DOCTEST_CHECK(!(path_lengths[0] == path_lengths[1] && path_lengths[1] == path_lengths[2]));
5497 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))};
5499 std::vector<vec3> voxel_centers = {
make_vec3(2.0f, 0.0f, 0.0f),
make_vec3(5.0f, 0.0f, 0.0f)};
5501 std::vector<vec3> voxel_sizes = {
make_vec3(1.0f, 1.0f, 1.0f),
make_vec3(1.0f, 1.0f, 1.0f)};
5504 auto result = collision.calculateVoxelPathLengths(scan_origin, ray_directions, voxel_centers, voxel_sizes);
5507 for (
size_t c = 0; c < voxel_centers.size(); ++c) {
5508 std::vector<float> dr_agg;
5509 uint hit_after_agg = 0;
5512 for (
const auto &hit: result[c]) {
5513 dr_agg.push_back(hit.path_length);
5518 DOCTEST_CHECK(hit_after_agg == result[c].size());
5519 for (
float path_length: dr_agg) {
5520 DOCTEST_CHECK(path_length > 0.0f);
5521 DOCTEST_CHECK(path_length <= 2.0f);
5527DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - Patch with no slices") {
5534 uint prim_UUID = context.
addPatch(patch_center, patch_size);
5540 collisiondetection.disableMessages();
5542 std::vector<uint> voxel_UUIDs = collisiondetection.slicePrimitivesUsingGrid(std::vector<uint>{prim_UUID}, grid_center, grid_size,
make_int3(2, 2, 2));
5544 DOCTEST_CHECK(voxel_UUIDs.size() >= 1);
5547DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - Patch with one slice") {
5554 uint prim_UUID = context.
addPatch(patch_center, patch_size);
5560 collisiondetection.disableMessages();
5562 std::vector<uint> voxel_UUIDs = collisiondetection.slicePrimitivesUsingGrid(std::vector<uint>{prim_UUID}, grid_center, grid_size,
make_int3(2, 1, 1));
5564 DOCTEST_CHECK(voxel_UUIDs.size() >= 1);
5567DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - Patch with 2 slices") {
5574 uint prim_UUID = context.
addPatch(patch_center, patch_size);
5580 collisiondetection.disableMessages();
5582 std::vector<uint> voxel_UUIDs = collisiondetection.slicePrimitivesUsingGrid(std::vector<uint>{prim_UUID}, grid_center, grid_size,
make_int3(3, 1, 1));
5584 DOCTEST_CHECK(voxel_UUIDs.size() >= 1);
5587DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - Patch with 3 slices and no vertices inside voxel") {
5594 uint prim_UUID = context.
addPatch(patch_center, patch_size);
5600 collisiondetection.disableMessages();
5602 std::vector<uint> voxel_UUIDs = collisiondetection.slicePrimitivesUsingGrid(std::vector<uint>{prim_UUID}, grid_center, grid_size,
make_int3(4, 1, 1));
5604 DOCTEST_CHECK(voxel_UUIDs.size() >= 1);
5607DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - Areas after slicing (non-textured)") {
5614 uint prim_UUID = context.
addPatch(patch_center, patch_size);
5622 collisiondetection.disableMessages();
5624 std::vector<uint> voxel_UUIDs = collisiondetection.slicePrimitivesUsingGrid(std::vector<uint>{prim_UUID}, grid_center, grid_size,
make_int3(2, 1, 2));
5627 for (
uint UUID: voxel_UUIDs) {
5631 DOCTEST_CHECK(fabs(area_tot - area_patch) / area_patch < 0.05f);
5634DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - Textured areas after slicing") {
5649 collisiondetection.disableMessages();
5651 std::vector<uint> voxel_UUIDs = collisiondetection.slicePrimitivesUsingGrid(std::vector<uint>{prim_UUID}, grid_center, grid_size,
make_int3(2, 1, 2));
5654 for (
uint UUID: voxel_UUIDs) {
5658 DOCTEST_CHECK(fabs(area_tot - area_patch) / area_patch < 0.05f);
5661DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - Cropping non-textured primitives") {
5667 uint prim_UUID = tri1_UUID;
5673 collisiondetection.disableMessages();
5676 std::vector<uint> voxel_UUIDs = collisiondetection.slicePrimitivesUsingGrid(std::vector<uint>{prim_UUID}, grid_center, grid_size,
make_int3(1, 1, 1));
5678 DOCTEST_CHECK(voxel_UUIDs.size() >= 1);
5681DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - Cropping textured primitives") {
5694 collisiondetection.disableMessages();
5697 std::vector<uint> voxel_UUIDs = collisiondetection.slicePrimitivesUsingGrid(std::vector<uint>{prim_UUID}, grid_center, grid_size,
make_int3(1, 1, 1));
5699 DOCTEST_CHECK(voxel_UUIDs.size() >= 1);
5702DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - Basic functionality test") {
5709 uint prim_UUID = context.
addPatch(patch_center, patch_size);
5712 collisiondetection.disableMessages();
5717 std::vector<uint> sliced_UUIDs = collisiondetection.slicePrimitivesUsingGrid(std::vector<uint>{prim_UUID}, grid_center, grid_size,
make_int3(2, 1, 2));
5720 DOCTEST_CHECK(sliced_UUIDs.size() >= 1);
5727DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - calculatePrimitiveVoxelIntersection Basic") {
5730 collisiondetection.disableMessages();
5735 uint voxel_uuid = context.
addVoxel(voxel_center, voxel_size);
5740 uint patch_uuid = context.
addPatch(patch_center, patch_size);
5744 uint outside_uuid = context.
addPatch(outside_center, patch_size);
5747 collisiondetection.calculatePrimitiveVoxelIntersection();
5752 std::vector<uint> inside_prims;
5755 DOCTEST_CHECK(inside_prims.size() == 1);
5756 DOCTEST_CHECK(inside_prims[0] == patch_uuid);
5759DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - calculatePrimitiveVoxelIntersection Axis-Aligned") {
5762 collisiondetection.disableMessages();
5777 collisiondetection.calculatePrimitiveVoxelIntersection();
5782 std::vector<uint> inside_prims;
5785 DOCTEST_CHECK(inside_prims.size() == 3);
5788DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - calculatePrimitiveVoxelIntersection Multiple Voxels") {
5791 collisiondetection.disableMessages();
5804 collisiondetection.calculatePrimitiveVoxelIntersection();
5807 std::vector<uint> inside1;
5809 DOCTEST_CHECK(inside1.size() == 1);
5810 DOCTEST_CHECK(inside1[0] == patch1);
5813 std::vector<uint> inside2;
5815 DOCTEST_CHECK(inside2.size() == 1);
5816 DOCTEST_CHECK(inside2[0] == patch2);
5819 std::vector<uint> inside3;
5821 DOCTEST_CHECK(inside3.size() == 2);
5824DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - calculatePrimitiveVoxelIntersection Empty Inputs") {
5827 collisiondetection.disableMessages();
5831 collisiondetection.calculatePrimitiveVoxelIntersection();
5837 collisiondetection2.disableMessages();
5839 collisiondetection2.calculatePrimitiveVoxelIntersection();
5844DOCTEST_TEST_CASE(
"CollisionDetection VoxelIntersection Integration - calculatePrimitiveVoxelIntersection Specific UUIDs") {
5847 collisiondetection.disableMessages();
5856 std::vector<uint> test_uuids = {voxel_uuid, patch1, patch3};
5857 collisiondetection.calculatePrimitiveVoxelIntersection(test_uuids);
5860 std::vector<uint> inside_prims;
5863 DOCTEST_CHECK(inside_prims.size() == 1);
5864 DOCTEST_CHECK(inside_prims[0] == patch1);
5867DOCTEST_TEST_CASE(
"CollisionDetection GPU/CPU Ray Casting Parity") {
5874 cd.disableMessages();
5879 float wall_size = 2.0f;
5880 float spacing = wall_size / grid_size;
5882 for (
int iy = 0; iy < grid_size; iy++) {
5883 for (
int iz = 0; iz < grid_size; iz++) {
5884 float y = -wall_size/2.0f + iy * spacing;
5885 float z = -wall_size/2.0f + iz * spacing;
5889 vec3 v1(0, y + spacing, z);
5890 vec3 v2(0, y + spacing, z + spacing);
5891 vec3 v3(0, y, z + spacing);
5899 std::cout <<
"Test geometry: " << triangle_count <<
" triangles" << std::endl;
5905 std::vector<CollisionDetection::RayQuery> ray_queries;
5906 int rays_per_dim = 1050;
5908 for (
int iy = 0; iy < rays_per_dim; iy++) {
5909 for (
int iz = 0; iz < rays_per_dim; iz++) {
5910 float y = -wall_size/2.0f + (iy + 0.5f) * wall_size / rays_per_dim;
5911 float z = -wall_size/2.0f + (iz + 0.5f) * wall_size / rays_per_dim;
5912 vec3 origin(-2.0f, y, z);
5913 vec3 direction(1.0f, 0.0f, 0.0f);
5914 ray_queries.emplace_back(origin, direction, 10.0f);
5918 std::cout <<
"Casting " << ray_queries.size() <<
" rays through wall (testing GPU path)..." << std::endl;
5921 cd.disableGPUAcceleration();
5922 std::vector<CollisionDetection::HitResult> cpu_results = cd.castRays(ray_queries);
5924 size_t cpu_hits = 0;
5925 for (
const auto& result : cpu_results) {
5926 if (result.
hit) cpu_hits++;
5930 cd.enableGPUAcceleration();
5931 std::vector<CollisionDetection::HitResult> gpu_results = cd.castRays(ray_queries);
5933 size_t gpu_hits = 0;
5934 for (
const auto& result : gpu_results) {
5935 if (result.
hit) gpu_hits++;
5938 std::cout <<
"CPU hits: " << cpu_hits <<
" (" << (100.0*cpu_hits/ray_queries.size()) <<
"%)" << std::endl;
5939 std::cout <<
"GPU hits: " << gpu_hits <<
" (" << (100.0*gpu_hits/ray_queries.size()) <<
"%)" << std::endl;
5942 float cpu_hit_rate =
static_cast<float>(cpu_hits) / ray_queries.size();
5943 DOCTEST_CHECK(cpu_hit_rate > 0.90f);
5948 float hit_ratio =
static_cast<float>(gpu_hits) /
static_cast<float>(cpu_hits);
5949 DOCTEST_CHECK(hit_ratio > 0.95f);
5950 DOCTEST_CHECK(hit_ratio < 1.05f);