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");