diff --git a/extern/typed-geometry b/extern/typed-geometry index 9f74e822f4b8e3d974de0fd615ca185dd0a34e75..693adea14919b8e60828ed92f8d0bc8fe6206735 160000 --- a/extern/typed-geometry +++ b/extern/typed-geometry @@ -1 +1 @@ -Subproject commit 9f74e822f4b8e3d974de0fd615ca185dd0a34e75 +Subproject commit 693adea14919b8e60828ed92f8d0bc8fe6206735 diff --git a/tests/feature/objects/aabb.cc b/tests/feature/objects/aabb.cc index 3f1e167787273e071dcaeca830aae2c59767d0b0..ab7152f560550f943407528936d459bd48b341a3 100644 --- a/tests/feature/objects/aabb.cc +++ b/tests/feature/objects/aabb.cc @@ -98,6 +98,12 @@ TG_FUZZ_TEST(TypedGeometry, ObjectAABB) test_obj(tg::capsule3(axis0, r)); // cylinder test_obj(tg::cylinder3(axis0, r)); + // ellipse + test_obj(tg::ellipse1(pos10, m1)); + test_obj(tg::ellipse2(pos20, m2)); + test_obj(tg::ellipse3(pos30, m3)); + // TODO: ellipse4 + test_obj(tg::ellipse2in3(pos30, m23)); // hemisphere test_obj(tg::hemisphere1(pos10, r, n1)); test_obj(tg::hemisphere2(pos20, r, n2)); diff --git a/tests/feature/objects/any_point.cc b/tests/feature/objects/any_point.cc new file mode 100644 index 0000000000000000000000000000000000000000..dbe7da1a92d38bd9d580a4036724061fef0fa221 --- /dev/null +++ b/tests/feature/objects/any_point.cc @@ -0,0 +1,150 @@ +#include <test.hh> + +TG_FUZZ_TEST(TypedGeometry, AnyPoint) +{ + const auto tolerance = 0.001f; + auto const test_obj = [tolerance](const auto& o) { + auto p = any_point(o); + CHECK(contains(o, p, tolerance)); + }; + + auto const test_obj_and_boundary = [&test_obj](const auto& o) { + test_obj(o); + test_obj(boundary_of(o)); + }; + + auto const test_obj_and_boundary_no_caps = [&test_obj](const auto& o) { + test_obj(o); + test_obj(boundary_of(o)); + test_obj(boundary_no_caps_of(o)); + }; + + const auto r = uniform(rng, 0.0f, 10.0f); + const auto h = uniform(rng, 0.0f, 10.0f); + const auto a = tg::uniform<tg::angle>(rng); + const auto n1 = tg::uniform<tg::dir1>(rng); + const auto n2 = tg::uniform<tg::dir2>(rng); + const auto n3 = tg::uniform<tg::dir3>(rng); + const auto n4 = tg::uniform<tg::dir4>(rng); + + const auto range1 = tg::aabb1(tg::pos1(-10), tg::pos1(10)); + const auto range2 = tg::aabb2(tg::pos2(-10), tg::pos2(10)); + const auto range3 = tg::aabb3(tg::pos3(-10), tg::pos3(10)); + const auto range4 = tg::aabb4(tg::pos4(-10), tg::pos4(10)); + + const auto pos10 = uniform(rng, range1); + const auto pos11 = uniform(rng, range1); + + const auto pos20 = uniform(rng, range2); + const auto pos21 = uniform(rng, range2); + const auto pos22 = uniform(rng, range2); + + const auto pos30 = uniform(rng, range3); + const auto pos31 = uniform(rng, range3); + const auto pos32 = uniform(rng, range3); + + const auto pos40 = uniform(rng, range4); + const auto pos41 = uniform(rng, range4); + + const auto axis0 = tg::segment3(pos30, pos31); + const auto disk0 = tg::sphere2in3(pos30, r, n3); + + const auto d1 = tg::uniform<tg::dir1>(rng); + auto m1 = tg::mat1(); + m1[0] = d1 * uniform(rng, 1.0f, 3.0f); + + const auto d20 = tg::uniform<tg::dir2>(rng); + const auto d21 = perpendicular(d20); + auto m2 = tg::mat2(); + m2[0] = d20 * uniform(rng, 1.0f, 3.0f); + m2[1] = d21 * uniform(rng, 1.0f, 3.0f); + + const auto d30 = tg::uniform<tg::dir3>(rng); + const auto d31 = any_normal(d30); + const auto d32 = normalize(cross(d30, d31)); + auto m3 = tg::mat3(); + m3[0] = d30 * uniform(rng, 1.0f, 3.0f); + m3[1] = d31 * uniform(rng, 1.0f, 3.0f); + m3[2] = d32 * uniform(rng, 1.0f, 3.0f); + + auto m23 = tg::mat2x3(); + m23[0] = d30 * uniform(rng, 1.0f, 3.0f); + m23[1] = d31 * uniform(rng, 1.0f, 3.0f); + + // aabb + test_obj_and_boundary(aabb_of(pos10, pos11)); + test_obj_and_boundary(aabb_of(pos20, pos21)); + test_obj_and_boundary(aabb_of(pos30, pos31)); + test_obj_and_boundary(aabb_of(pos40, pos41)); + // box + test_obj_and_boundary(tg::box1(pos10, m1)); + test_obj_and_boundary(tg::box2(pos20, m2)); + test_obj_and_boundary(tg::box3(pos30, m3)); + // TODO: box4 + test_obj_and_boundary(tg::box2in3(pos30, m23)); + // capsule + test_obj_and_boundary(tg::capsule3(axis0, r)); + // cylinder + test_obj_and_boundary_no_caps(tg::cylinder3(axis0, r)); + // ellipse + test_obj_and_boundary(tg::ellipse1(pos10, m1)); + test_obj_and_boundary(tg::ellipse2(pos20, m2)); + test_obj_and_boundary(tg::ellipse3(pos30, m3)); + // TODO: ellipse4 + test_obj_and_boundary(tg::ellipse2in3(pos30, m23)); + // halfspace + test_obj(tg::halfspace1(n1, h)); + test_obj(tg::halfspace2(n2, h)); + test_obj(tg::halfspace3(n3, h)); + test_obj(tg::halfspace4(n4, h)); + // hemisphere + test_obj_and_boundary_no_caps(tg::hemisphere1(pos10, r, n1)); + test_obj_and_boundary_no_caps(tg::hemisphere2(pos20, r, n2)); + test_obj_and_boundary_no_caps(tg::hemisphere3(pos30, r, n3)); + // test_obj_and_boundary_no_caps(tg::hemisphere4(pos40, r, n4)); + // inf_cone + test_obj_and_boundary(tg::inf_cone3(pos30, n3, a)); + // inf_cylinder + test_obj_and_boundary(tg::inf_cylinder3(tg::line3(pos30, n3), r)); + // line + test_obj(tg::line1(pos10, n1)); + test_obj(tg::line2(pos20, n2)); + test_obj(tg::line3(pos30, n3)); + test_obj(tg::line4(pos40, n4)); + // plane + test_obj(tg::plane1(n1, h)); + test_obj(tg::plane2(n2, h)); + test_obj(tg::plane3(n3, h)); + test_obj(tg::plane4(n4, h)); + // pyramid + test_obj_and_boundary_no_caps(tg::pyramid<tg::box2in3>(tg::box2in3(pos30, m23), h)); + test_obj_and_boundary_no_caps(tg::pyramid<tg::sphere2in3>(disk0, h)); // == cone + test_obj_and_boundary_no_caps(tg::pyramid<tg::triangle3>(tg::triangle3(pos30, pos31, pos32), h)); + test_obj(tg::pyramid_boundary_no_caps<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30)), h)); + // test_obj_and_boundary_no_caps(tg::pyramid<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30)), h)); + // TODO: quad + // test_obj(tg::quad2(pos20, pos21, pos22, pos23)); + // test_obj(tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30))); + // test_obj(tg::quad4(pos40, pos41, pos42, pos42 + (pos41 - pos40))); + // ray + test_obj(tg::ray1(pos10, n1)); + test_obj(tg::ray2(pos20, n2)); + test_obj(tg::ray3(pos30, n3)); + test_obj(tg::ray4(pos40, n4)); + // segment + test_obj(tg::segment1(pos10, pos11)); + test_obj(tg::segment2(pos20, pos21)); + test_obj(tg::segment3(pos30, pos31)); + test_obj(tg::segment4(pos40, pos41)); + // sphere + test_obj_and_boundary(tg::sphere1(pos10, r)); + test_obj_and_boundary(tg::sphere2(pos20, r)); + test_obj_and_boundary(tg::sphere3(pos30, r)); + test_obj_and_boundary(tg::sphere4(pos40, r)); + test_obj_and_boundary(tg::sphere<1, float, 2>(pos20, r, n2)); + test_obj_and_boundary(tg::sphere2in3(pos30, r, n3)); + // triangle + test_obj(tg::triangle2(pos20, pos21, pos22)); + test_obj(tg::triangle3(pos30, pos31, pos32)); + // test_obj(tg::triangle4(pos40, pos41, pos42)); +} diff --git a/tests/feature/objects/area.cc b/tests/feature/objects/area.cc index 188a8bfa099585d1dace187d2bff83c10a423c32..00b74dd1534a484b9848ad8fba1eff8a20fac2e4 100644 --- a/tests/feature/objects/area.cc +++ b/tests/feature/objects/area.cc @@ -3,6 +3,7 @@ TG_FUZZ_TEST(TypedGeometry, AreaRelations) { const auto r = uniform(rng, 0.0f, 10.0f); + const auto r2 = uniform(rng, 0.0f, 10.0f); const auto h = uniform(rng, 0.0f, 10.0f); const auto n2 = tg::uniform<tg::dir2>(rng); const auto n3 = tg::uniform<tg::dir3>(rng); @@ -15,6 +16,12 @@ TG_FUZZ_TEST(TypedGeometry, AreaRelations) const auto pos30 = uniform(rng, range3); const auto pos31 = uniform(rng, range3); + const auto d20 = tg::uniform<tg::dir2>(rng); + const auto d21 = perpendicular(d20); + const auto d30 = tg::uniform<tg::dir3>(rng); + const auto d31 = any_normal(d30); + const auto d32 = normalize(cross(d30, d31)); + const auto aabb = aabb_of(pos20, pos21); const auto aAabb = area_of(aabb); @@ -24,8 +31,12 @@ TG_FUZZ_TEST(TypedGeometry, AreaRelations) const auto aDisk = area_of(tg::disk2(pos20, r)); const auto aDiskIn3 = area_of(tg::disk3(pos30, r, n3)); const auto aHemisphere2 = area_of(tg::hemisphere2(pos20, r, n2)); + const auto aEllipse2 = area_of(tg::ellipse2(pos20, tg::mat2x2::from_cols(r * d20, r2 * d21))); + const auto aEllipse23 = area_of(tg::ellipse2in3(pos30, tg::mat2x3::from_cols(r * d30, r2 * d31))); CHECK(aDisk == approx(aDiskIn3)); // 2D and 3D disks have the same area CHECK(aDisk == approx(2.f * aHemisphere2)); // sphere == 2 * hemisphere (2D) + CHECK(aEllipse2 == approx(aDisk * r2 / r)); + CHECK(aEllipse23 == approx(aEllipse2)); const auto hemisphere3 = tg::hemisphere3(pos30, r, n3); const auto aSphere = area_of(tg::sphere3(pos30, r)); @@ -37,6 +48,9 @@ TG_FUZZ_TEST(TypedGeometry, AreaRelations) CHECK(aHemisphereCaps == approx(aDiskIn3)); // round base is the same as a disk2in3 CHECK(aHemisphereNoCaps == approx(2.f * aHemisphereCaps)); // ratio of areas for hemispheres + const auto aEllipsoid3 = area_of(tg::ellipse3(pos30, tg::mat3::from_cols(r * d30, r * d31, r * d32))); + CHECK(aEllipsoid3 == approx(aSphere)); // sphere == ellipsoid if all 3 axes have equal length + const auto cylinder = tg::cylinder3(pos30, pos31, r); const auto aCylinder = area_of(cylinder); const auto aCylinderMantle = area_of(boundary_no_caps_of(cylinder)); diff --git a/tests/feature/objects/centroid.cc b/tests/feature/objects/centroid.cc index 45f24d13e9f53fe014ed876bde22def12f3918ba..290c219921e0689e50f636fd2338180d517c7e8f 100644 --- a/tests/feature/objects/centroid.cc +++ b/tests/feature/objects/centroid.cc @@ -158,6 +158,12 @@ TG_FUZZ_TEST(TypedGeometry, CentroidByUniform) test_obj_and_boundary(tg::capsule3(axis0, r)); // cylinder test_obj_and_boundary_no_caps(tg::cylinder3(axis0, r)); + // ellipse + test_obj_and_boundary(tg::ellipse1(pos10, m1)); + test_obj_and_boundary(tg::ellipse2(pos20, m2)); + test_obj_and_boundary(tg::ellipse3(pos30, m3)); + // TODO: ellipse4 + test_obj_and_boundary(tg::ellipse2in3(pos30, m23)); // hemisphere // test_obj_and_boundary_no_caps(tg::hemisphere1(pos10, r, n1)); test_obj_and_boundary_no_caps(tg::hemisphere2(pos20, r, n2)); diff --git a/tests/feature/objects/faces.cc b/tests/feature/objects/faces.cc index a5ee2bde90e09360c24282d32666106edbb8ff0d..2d9e1fcfefe1b79600979bd609e6f141f495a784 100644 --- a/tests/feature/objects/faces.cc +++ b/tests/feature/objects/faces.cc @@ -47,9 +47,15 @@ TG_FUZZ_TEST_MAX_ITS(TypedGeometry, FacesOfPyramids, 1) static_assert(std::is_same_v<decltype(facesPyTri.base), tg::triangle3>); static_assert(facesPyTri.mantle.size() == 3); - /* + const auto pyBox = tg::pyramid<tg::box2in3>(tg::box2in3(pos30, m23), h); + const auto facesPyBox = faces_of(pyBox); + static_assert(std::is_same_v<decltype(facesPyBox.base), tg::box2in3>); + static_assert(facesPyBox.mantle.size() == 4); + + /* This structured binding should also work, but there are apparently problems with clang const auto pyBox = tg::pyramid<tg::box2in3>(tg::box2in3(pos30, m23), h); auto [baseFace, triangleFaces] = faces_of(pyBox); static_assert(std::is_same_v<decltype(baseFace), tg::box2in3>); - CHECK(triangleFaces.size() == 4);*/ + CHECK(triangleFaces.size() == 4); + */ } diff --git a/tests/feature/objects/perimeter.cc b/tests/feature/objects/perimeter.cc index 9ab8805018fafdbe23a6865adf184f6bee6bd220..0380eb27e9801aa35745bd02c0e933cd6b23dfa0 100644 --- a/tests/feature/objects/perimeter.cc +++ b/tests/feature/objects/perimeter.cc @@ -13,26 +13,38 @@ TG_FUZZ_TEST(TypedGeometry, Perimeter) const auto pos30 = uniform(rng, range3); - // === disk, disk2in3, circle, circle2in3, hemisphere2 === + const auto d20 = tg::uniform<tg::dir2>(rng); + const auto d21 = perpendicular(d20); + const auto d30 = tg::uniform<tg::dir3>(rng); + const auto d31 = any_normal(d30); + + // === disk, disk2in3, circle, circle2in3, hemisphere2, (ellipse) === // random radius auto r = uniform(rng, 0.0f, 10.0f); { - auto ball = tg::sphere<2,float>(pos20, r); - auto sphere = tg::sphere_boundary<2, float>(pos20, r); + auto disk = tg::sphere<2,float>(pos20, r); + auto circle = tg::sphere_boundary<2, float>(pos20, r); auto disk3 = tg::sphere2in3(pos30, r, n3); auto circle3 = tg::sphere_boundary<2, float, 3>(pos30, r, n3); - auto pb = perimeter_of(ball); - auto ps = perimeter_of(sphere); + auto ellipse = tg::ellipse2(pos20, tg::mat2::from_cols(r * d20, r * d21)); + auto ellipse3 = tg::ellipse2in3(pos30, tg::mat2x3::from_cols(r * d30, r * d31)); + + auto pd = perimeter_of(disk); + auto pc = perimeter_of(circle); auto pd3 = perimeter_of(disk3); auto pc3 = perimeter_of(circle3); + auto pe = perimeter_of(ellipse); + auto pe3 = perimeter_of(ellipse3); // must all be the same - CHECK(pb == approx(ps)); - CHECK(ps == approx(pd3)); + CHECK(pd == approx(pc)); + CHECK(pc == approx(pd3)); CHECK(pd3 == approx(pc3)); + CHECK(pc3 == approx(pe)); + CHECK(pe == approx(pe3)); auto hemisphere = tg::hemisphere2(pos20, r, n2); auto hemisphereBound = boundary_of(hemisphere); @@ -44,30 +56,37 @@ TG_FUZZ_TEST(TypedGeometry, Perimeter) CHECK(ph == approx(phb)); // must be the same CHECK(ph == approx(phc + 2.f * r)); // is no_caps + diameter - CHECK(pb == approx(2.f * phc)); // sphere == 2 * hemisphere + CHECK(pd == approx(2.f * phc)); // sphere == 2 * hemisphere } // known radius r = 1.0f / (2.0f * tg::pi<tg::f32>.radians()); { - auto ball = tg::sphere<2, float>(pos20, r); - auto sphere = tg::sphere_boundary<2, float>(pos20, r); + auto disk2 = tg::sphere<2, float>(pos20, r); + auto circle2 = tg::sphere_boundary<2, float>(pos20, r); auto disk3 = tg::sphere2in3(pos30, r, n3); auto circle3 = tg::sphere_boundary<2, float, 3>(pos30, r, n3); + auto ellipse = tg::ellipse2(pos20, tg::mat2::from_cols(r * d20, r * d21)); + auto ellipse23 = tg::ellipse2in3(pos30, tg::mat2x3::from_cols(r * d30, r * d31)); + auto hemisphere = tg::hemisphere_boundary_no_caps<2, float>(pos20, r, n2); - auto pb = perimeter_of(ball); - auto ps = perimeter_of(sphere); + auto pd = perimeter_of(disk2); + auto pc = perimeter_of(circle2); auto pd3 = perimeter_of(disk3); auto pc3 = perimeter_of(circle3); + auto pe = perimeter_of(ellipse); + auto pe23 = perimeter_of(ellipse23); auto phc = perimeter_of(hemisphere); - CHECK(pb == approx(1.0f)); - CHECK(ps == approx(1.0f)); + CHECK(pd == approx(1.0f)); + CHECK(pc == approx(1.0f)); CHECK(pd3 == approx(1.0f)); CHECK(pc3 == approx(1.0f)); + CHECK(pe == approx(1.0f)); + CHECK(pe23 == approx(1.0f)); CHECK(phc == approx(0.5f)); } diff --git a/tests/feature/objects/volume.cc b/tests/feature/objects/volume.cc index 947a069981b06bb0776b67d22d686f3e1a32d6ae..b7c76ad408e58c5431889a6161809dc830512b99 100644 --- a/tests/feature/objects/volume.cc +++ b/tests/feature/objects/volume.cc @@ -5,9 +5,14 @@ TG_FUZZ_TEST(TypedGeometry, Volume) { const auto range3 = tg::aabb3(tg::pos3(-10), tg::pos3(10)); const auto r = uniform(rng, 0.0f, 10.0f); + const auto r2 = uniform(rng, 0.0f, 10.0f); + const auto r3 = uniform(rng, 0.0f, 10.0f); const auto n = tg::uniform<tg::dir3>(rng); const auto pos30 = uniform(rng, range3); const auto pos31 = uniform(rng, range3); + const auto d30 = tg::uniform<tg::dir3>(rng); + const auto d31 = any_normal(d30); + const auto d32 = normalize(cross(d30, d31)); const auto ball = tg::sphere3(pos30, r); @@ -24,6 +29,11 @@ TG_FUZZ_TEST(TypedGeometry, Volume) // volume of ball is equal to volume of cylinder - 2 * volume of cone CHECK(vb == approx(vc - 2 * volume_of(cone))); + const auto ellipsoid = tg::ellipse3(pos30, tg::mat3::from_cols(r * d30, r2 * d31, r3 * d32)); + const auto ve = volume_of(ellipsoid); + // volume of ellipsoid is scaled compared to the sphere + CHECK(ve == approx(vb * r2 * r3 / (r * r))); + const auto hemisphere = tg::hemisphere3(pos30, r, n); const auto vh = volume_of(hemisphere); // volume of ball is two times volume of hemisphere diff --git a/tests/feature/random/uniform.cc b/tests/feature/random/uniform.cc index be8ebbe91e850746a5557d46b5b72c8ad5b133ab..26413c5adc2b57380450c9d95d179915e0f85d22 100644 --- a/tests/feature/random/uniform.cc +++ b/tests/feature/random/uniform.cc @@ -136,6 +136,12 @@ TG_FUZZ_TEST_MAX_ITS(TypedGeometry, UniformGeneralProperties, 100) test_obj_and_boundary(samples3, tg::capsule3(axis0, r)); // cylinder test_obj_and_boundary_no_caps(samples3, tg::cylinder3(axis0, r)); + // ellipse + test_obj(samples1, tg::ellipse1(pos10, m1)); // ellipse1_boundary consists of only 2 points and therefore collisions in the samples are unavoidable + test_obj_and_boundary(samples2, tg::ellipse2(pos20, m2)); + test_obj_and_boundary(samples3, tg::ellipse3(pos30, m3)); + // TODO: ellipse4 + test_obj_and_boundary(samples3, tg::ellipse2in3(pos30, m23)); // hemisphere // TODO: And boundary no caps //test_obj_and_boundary_no_caps(samples1, tg::hemisphere1(pos10, r, n1)); diff --git a/tests/impl-report.cc b/tests/impl-report.cc index 1b14768b0d789e5b9e292f8a06942f88c4e6811b..ee0a1fd17884b5e788bd27c85c1cfd65da467ad7 100644 --- a/tests/impl-report.cc +++ b/tests/impl-report.cc @@ -39,6 +39,9 @@ using try_less = decltype(std::map<T, int>().find(std::declval<T>())); template <class T> using try_hash = decltype(std::hash<T>()(std::declval<T const&>())); +template <class T> +using try_any_point = decltype(any_point(std::declval<T const&>())); + template <class T> using try_project_pos2 = decltype(project(tg::pos2(), std::declval<T const&>())); @@ -125,10 +128,16 @@ void test_single_object_type(std::string name) if constexpr (!can_apply<try_hash, ObjT>) std::cerr << "std::hash not specialized for tg::" << name << std::endl; + if constexpr (!can_apply<try_any_point, ObjT>) + std::cerr << "no any_point(tg::" << name << ")" << std::endl; + if constexpr (domainD == 2) { if constexpr (!can_apply<try_project_pos2, ObjT>) - std::cerr << "no project(tg::pos2, tg::" << name << ")" << std::endl; + { + if (!(name.size() >= 7 && std::string_view(name).substr(0, 7) == "ellipse")) // project(ellipse) is not planned + std::cerr << "no project(tg::pos2, tg::" << name << ")" << std::endl; + } if constexpr (!can_apply<try_contains_pos2, ObjT>) std::cerr << "no contains(tg::" << name << ", tg::pos2)" << std::endl; @@ -136,7 +145,10 @@ void test_single_object_type(std::string name) else if constexpr (domainD == 3) { if constexpr (!can_apply<try_project_pos3, ObjT>) - std::cerr << "no project(tg::pos3, tg::" << name << ")" << std::endl; + { + if (!(name.size() >= 7 && std::string_view(name).substr(0, 7) == "ellipse")) // project(ellipse) is not planned + std::cerr << "no project(tg::pos3, tg::" << name << ")" << std::endl; + } if constexpr (!can_apply<try_contains_pos3, ObjT>) std::cerr << "no contains(tg::" << name << ", tg::pos3)" << std::endl; @@ -278,6 +290,7 @@ TEST_CASE("implementation report") test_object_type_3_boundary_caps<tg::cylinder>("cylinder"); test_object_type_23_boundary<tg::box>("box"); + test_object_type_23_boundary<tg::ellipse>("ellipse"); test_object_type_23_boundary<tg::sphere>("sphere"); test_object_type_nested_boundary_caps<tg::pyramid, tg::sphere2in3>("pyramid", "sphere2in3");