diff --git a/extern/glow-extras b/extern/glow-extras
index f53a709b1a5926ce12dd00d3514d7d730c859748..2c3cb630bfe74e262535123015a444cb0247278e 160000
--- a/extern/glow-extras
+++ b/extern/glow-extras
@@ -1 +1 @@
-Subproject commit f53a709b1a5926ce12dd00d3514d7d730c859748
+Subproject commit 2c3cb630bfe74e262535123015a444cb0247278e
diff --git a/extern/typed-geometry b/extern/typed-geometry
index 0dad6e4b42f81513e75fd264394ec45461cbccc1..a6c6371f1bac47f0f269032549adb1873d1f32e4 160000
--- a/extern/typed-geometry
+++ b/extern/typed-geometry
@@ -1 +1 @@
-Subproject commit 0dad6e4b42f81513e75fd264394ec45461cbccc1
+Subproject commit a6c6371f1bac47f0f269032549adb1873d1f32e4
diff --git a/tests/feature/intersections/intersection.cc b/tests/feature/intersections/intersection.cc
index 07e1e82cbf29b52e457ac28a68c9820e631629a3..8cdff59ab73199c391f78d7bb02f8507e8cba9d8 100644
--- a/tests/feature/intersections/intersection.cc
+++ b/tests/feature/intersections/intersection.cc
@@ -7,7 +7,7 @@ TG_FUZZ_TEST(TypedGeometry, IntersectionRay3Sphere3)
     auto box1 = tg::aabb1(tg::pos1(-1.0f), tg::pos1(1.0f));
     auto box3 = tg::aabb3(tg::pos3(-1.0f), tg::pos3(1.0f));
     // random sphere
-    auto s = tg::sphere3(uniform(rng, box3) * 10.0f, tg::abs(uniform(rng, box1).x));
+    auto s = tg::sphere_boundary<3, float>(uniform(rng, box3) * 10.0f, tg::abs(uniform(rng, box1).x));
 
     {
         // ray from sphere origin to random direction
@@ -357,19 +357,19 @@ TG_FUZZ_TEST(Triangle, Intersection)
     auto ip4 = tg::intersection(ray, t4);
     auto ip5 = tg::intersection(ray, t5);
 
-    CHECK(ip0.has_value());
-    CHECK(ip1.has_value());
-    CHECK(ip2.has_value());
-    CHECK(ip3.has_value());
-    CHECK(ip4.has_value());
-    CHECK(ip5.has_value());
-
-    CHECK(ip0.value() == approx(p, 0.01f));
-    CHECK(ip1.value() == approx(p, 0.01f));
-    CHECK(ip2.value() == approx(p, 0.01f));
-    CHECK(ip3.value() == approx(p, 0.01f));
-    CHECK(ip4.value() == approx(p, 0.01f));
-    CHECK(ip5.value() == approx(p, 0.01f));
+    CHECK(ip0.any());
+    CHECK(ip1.any());
+    CHECK(ip2.any());
+    CHECK(ip3.any());
+    CHECK(ip4.any());
+    CHECK(ip5.any());
+
+    CHECK(ip0.first() == approx(p, 0.01f));
+    CHECK(ip1.first() == approx(p, 0.01f));
+    CHECK(ip2.first() == approx(p, 0.01f));
+    CHECK(ip3.first() == approx(p, 0.01f));
+    CHECK(ip4.first() == approx(p, 0.01f));
+    CHECK(ip5.first() == approx(p, 0.01f));
 
     auto a = uniform(rng, -2.f, 2.f);
     auto b = uniform(rng, -2.f, 2.f);
diff --git a/tests/feature/intersections/ray-intersect.cc b/tests/feature/intersections/ray-intersect.cc
index df4316f7e1065043825287b569987e98c21a3c80..9cab42e5a5d9e519c45c5c0994405634d2e8a51a 100644
--- a/tests/feature/intersections/ray-intersect.cc
+++ b/tests/feature/intersections/ray-intersect.cc
@@ -1,67 +1,223 @@
 #include <test.hh>
 
-TG_FUZZ_TEST(Ray, Intersect)
+TG_FUZZ_TEST(TypedGeometry, Intersections)
 {
-    auto bounds = tg::aabb3(-10, 10);
-
-    // ray - plane
-    {
-        auto const r = tg::ray3(uniform(rng, bounds), tg::uniform<tg::dir3>(rng));
-        auto const p = tg::plane(tg::uniform<tg::dir3>(rng), uniform(rng, bounds));
-
-        auto t = intersection_parameter(r, p);
-
+    auto const tolerance = 0.002f;
+    auto const test_obj = [tolerance](auto const& ray, auto const& obj) {
+        auto const ts = tg::intersection_parameter(ray, obj);
+        for (auto const& t : ts)
+        {
+            auto const ip = ray[t];
+            CHECK(contains(obj, ip, tolerance * tg::sqrt(t)));
+        }
+        auto const t = tg::closest_intersection_parameter(ray, obj);
         if (t.has_value())
         {
-            auto const ip = r[t.value()];
-
-            CHECK(distance(ip, p) == approx(0).epsilon(1e-2f));
+            CHECK(t.value() == ts.first());
+            CHECK(closest_intersection(ray, obj) == ray[t.value()]);
+            CHECK(intersects(ray, obj));
         }
-    }
-
-    // ray - tube
-    {
-        auto const r = tg::ray3(uniform(rng, bounds), tg::uniform<tg::dir3>(rng));
-        auto const t = tg::cylinder_boundary_no_caps<3, float>(uniform(rng, bounds), uniform(rng, bounds), uniform(rng, 0.5f, 10.0f));
-
-        auto is = intersection_parameter(r, t);
-        for (auto i : is)
+        else
+            CHECK(!intersects(ray, obj));
+
+        auto const tsLine = tg::intersection_parameter(inf_of(ray), obj);
+        auto iRay = 0;
+        for (auto iLine = 0; iLine < tsLine.size(); ++iLine)
+            if (tsLine[iLine] >= 0)
+                CHECK(tsLine[iLine] == ts[iRay++]);
+        CHECK(iRay == ts.size());
+    };
+
+    auto const test_solid_obj = [tolerance, &rng](auto const& ray, auto const& obj) {
+        auto const ts = tg::intersection_parameter(ray, obj);
+        if (ts.has_value())
         {
-            auto ip = r[i];
-
-            CHECK(distance(ip, t) == approx(0).epsilon(1e-2f));
+            auto const interval = ts.value();
+            auto const ip1 = ray[interval.start];
+            auto const ip2 = ray[uniform(rng, interval.start, interval.end)];
+            auto const ip3 = ray[interval.end];
+            CHECK(contains(obj, ip1, tolerance));
+            CHECK(contains(obj, ip2, tolerance));
+            CHECK(contains(obj, ip3, tolerance * tg::sqrt(interval.end)));
+
+            auto const t = tg::closest_intersection_parameter(ray, obj);
+            CHECK(t.has_value());
+            CHECK(t.value() == ts.value().start);
+            CHECK(closest_intersection(ray, obj) == ip1);
+            CHECK(intersects(ray, obj));
+
+            auto const tsLine = tg::intersection_parameter(inf_of(ray), obj);
+            CHECK(tsLine.has_value());
+            auto const interLine = tsLine.value();
+            CHECK(interLine.end == interval.end);
+            if (interLine.start < 0)
+                CHECK(interval.start == 0);
+            else
+                CHECK(interLine.start == interval.start);
         }
-    }
-
-    // ray - disk
-    {
-        auto const r = tg::ray3(uniform(rng, bounds), tg::uniform<tg::dir3>(rng));
-        auto const d = tg::sphere2in3(uniform(rng, bounds), uniform(rng, 0.5f, 10.0f), tg::uniform<tg::dir3>(rng));
-
-        auto ip = intersection(r, d);
-
-        if (ip.has_value())
-            CHECK(distance(ip.value(), d) == approx(0).epsilon(1e-2f));
-    }
-
-    // ray - cylinder
-    {
-        auto const r = tg::ray3(uniform(rng, bounds), tg::uniform<tg::dir3>(rng));
-        auto const c = tg::cylinder3(uniform(rng, bounds), uniform(rng, bounds), uniform(rng, 0.5f, 10.0f));
-        auto const t = tg::cylinder_boundary_no_caps<3, float>(c.axis, c.radius);
-
-        auto it = closest_intersection(r, t);
-        if (it.has_value())
+        else
+            CHECK(!intersects(ray, obj));
+    };
+
+    auto const test_obj_and_boundary = [&](auto const& ray, auto const& obj) {
+        auto const bounds = boundary_of(obj);
+        test_solid_obj(ray, obj);
+        test_obj(ray, bounds);
+
+        auto const iObj = tg::closest_intersection_parameter(ray, obj);
+        auto const iBounds = tg::closest_intersection_parameter(ray, bounds);
+        if (iBounds.has_value())
         {
-            CHECK(distance(it.value(), t) == approx(0).epsilon(1e-2f));
-            CHECK(distance(it.value(), c) == approx(0).epsilon(1e-2f));
+            CHECK(iObj.has_value());
+            CHECK(iBounds.value() >= iObj.value());
+            CHECK(contains(obj, ray[iBounds.value()], tolerance));
         }
-
-        auto ip = closest_intersection(r, c);
-
-        if (ip.has_value())
-            CHECK(distance(ip.value(), c) == approx(0).epsilon(1e-2f));
-    }
+    };
+
+    auto const test_obj_and_boundary_no_caps = [&](auto const& ray, auto const& obj) {
+        auto const bounds = boundary_of(obj);
+        auto const boundsNoCaps = boundary_no_caps_of(obj);
+        test_solid_obj(ray, obj);
+        test_obj(ray, bounds);
+        test_obj(ray, boundsNoCaps);
+
+        auto const iObj = tg::closest_intersection_parameter(ray, obj);
+        auto const iBounds = tg::closest_intersection_parameter(ray, bounds);
+        auto const iBoundsNoCaps = tg::closest_intersection_parameter(ray, boundsNoCaps);
+        if (iBoundsNoCaps.has_value())
+        {
+            CHECK(iBounds.has_value());
+            CHECK(iBoundsNoCaps.value() >= iBounds.value());
+            CHECK(contains(bounds, ray[iBoundsNoCaps.value()], tolerance));
+        }
+        if (iBounds.has_value())
+        {
+            CHECK(iObj.has_value());
+            CHECK(iBounds.value() >= iObj.value());
+            CHECK(contains(obj, ray[iBounds.value()], tolerance));
+        }
+    };
+
+    auto const r = uniform(rng, 0.0f, 10.0f);
+    auto const h = uniform(rng, 0.0f, 10.0f);
+    auto const a = uniform(rng, 5_deg, 180_deg); // sensible range for a convex inf_cone
+    auto const n1 = tg::uniform<tg::dir1>(rng);
+    auto const n2 = tg::uniform<tg::dir2>(rng);
+    auto const n3 = tg::uniform<tg::dir3>(rng);
+    auto const n4 = tg::uniform<tg::dir4>(rng);
+
+    auto const range1 = tg::aabb1(tg::pos1(-10), tg::pos1(10));
+    auto const range2 = tg::aabb2(tg::pos2(-10), tg::pos2(10));
+    auto const range3 = tg::aabb3(tg::pos3(-10), tg::pos3(10));
+    auto const range4 = tg::aabb4(tg::pos4(-10), tg::pos4(10));
+
+    auto const pos10 = uniform(rng, range1);
+    auto const pos11 = uniform(rng, range1);
+
+    auto const pos20 = uniform(rng, range2);
+    auto const pos21 = uniform(rng, range2);
+    auto const pos22 = uniform(rng, range2);
+
+    auto const pos30 = uniform(rng, range3);
+    auto const pos31 = uniform(rng, range3);
+    auto const pos32 = uniform(rng, range3);
+
+    auto const pos40 = uniform(rng, range4);
+    auto const pos41 = uniform(rng, range4);
+
+    auto const axis0 = tg::segment3(pos30, pos31);
+    auto const disk0 = tg::sphere2in3(pos30, r, n3);
+
+    auto const d1 = tg::uniform<tg::dir1>(rng);
+    auto m1 = tg::mat1();
+    m1[0] = d1 * uniform(rng, 1.0f, 3.0f);
+
+    auto const d20 = tg::uniform<tg::dir2>(rng);
+    auto const 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);
+
+    auto const d30 = tg::uniform<tg::dir3>(rng);
+    auto const d31 = any_normal(d30);
+    auto const 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);
+
+
+    auto const ray1 = tg::ray1(uniform(rng, range1), tg::uniform<tg::dir1>(rng));
+    auto const ray2 = tg::ray2(uniform(rng, range2), tg::uniform<tg::dir2>(rng));
+    auto const ray3 = tg::ray3(uniform(rng, range3), tg::uniform<tg::dir3>(rng));
+    auto const ray4 = tg::ray4(uniform(rng, range4), tg::uniform<tg::dir4>(rng));
+
+    // aabb
+    test_obj_and_boundary(ray1, aabb_of(pos10, pos11));
+    test_obj_and_boundary(ray2, aabb_of(pos20, pos21));
+    test_obj_and_boundary(ray3, aabb_of(pos30, pos31));
+    test_obj_and_boundary(ray4, aabb_of(pos40, pos41));
+    // box
+    test_obj_and_boundary(ray1, tg::box1(pos10, m1));
+    test_obj_and_boundary(ray2, tg::box2(pos20, m2));
+    test_obj_and_boundary(ray3, tg::box3(pos30, m3));
+    test_obj(ray3, tg::box2in3(pos30, m23));
+    // capsule
+    test_obj_and_boundary(ray3, tg::capsule3(axis0, r));
+    // cylinder
+    test_obj_and_boundary_no_caps(ray3, tg::cylinder<3, float>(axis0, r));
+    // ellipse
+    test_obj_and_boundary(ray1, tg::ellipse1(pos10, m1));
+    test_obj_and_boundary(ray2, tg::ellipse2(pos20, m2));
+    test_obj_and_boundary(ray3, tg::ellipse3(pos30, m3));
+    // TODO: ellipse4
+    test_obj(ray3, tg::ellipse2in3(pos30, m23));
+    // halfspace
+    test_solid_obj(ray1, tg::halfspace1(n1, h));
+    test_solid_obj(ray2, tg::halfspace2(n2, h));
+    test_solid_obj(ray3, tg::halfspace3(n3, h));
+    test_solid_obj(ray4, tg::halfspace4(n4, h));
+    // hemisphere
+    test_obj_and_boundary_no_caps(ray1, tg::hemisphere1(pos10, r, n1));
+    test_obj_and_boundary_no_caps(ray2, tg::hemisphere2(pos20, r, n2));
+    test_obj_and_boundary_no_caps(ray3, tg::hemisphere3(pos30, r, n3));
+    // test_obj_and_boundary_no_caps(ray4, tg::hemisphere4(pos40, r, n4));
+    // inf_cone
+    test_obj_and_boundary(ray2, tg::inf_cone2(pos20, n2, a));
+    test_obj_and_boundary(ray3, tg::inf_cone3(pos30, n3, a));
+    // inf_cylinder
+    test_obj_and_boundary(ray2, tg::inf_cylinder2(tg::line2(pos20, n2), r));
+    test_obj_and_boundary(ray3, tg::inf_cylinder3(tg::line3(pos30, n3), r));
+    // line
+    test_obj(ray2, tg::line2(pos20, n2));
+    // plane
+    test_obj(ray1, tg::plane1(n1, h));
+    test_obj(ray2, tg::plane2(n2, h));
+    test_obj(ray3, tg::plane3(n3, h));
+    test_obj(ray4, tg::plane4(n4, h));
+    // pyramid
+    test_obj_and_boundary_no_caps(ray3, tg::pyramid<tg::box2in3>(tg::box2in3(pos30, m23), h));
+    test_obj_and_boundary_no_caps(ray3, tg::pyramid<tg::sphere2in3>(disk0, h)); // == cone
+    test_obj_and_boundary_no_caps(ray3, tg::pyramid<tg::triangle3>(tg::triangle3(pos30, pos31, pos32), h));
+    test_obj(ray3, tg::pyramid_boundary_no_caps<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos30 - pos31)), h));
+    // test_obj_and_boundary_no_caps(ray3, tg::pyramid<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos30 - pos31)), h));
+    // ray
+    test_obj(ray2, tg::ray2(pos20, n2));
+    // segment
+    test_obj(ray2, tg::segment2(pos20, pos21));
+    // sphere
+    test_obj_and_boundary(ray1, tg::sphere1(pos10, r));
+    test_obj_and_boundary(ray2, tg::sphere2(pos20, r));
+    test_obj_and_boundary(ray3, tg::sphere3(pos30, r));
+    test_obj_and_boundary(ray4, tg::sphere4(pos40, r));
+    test_obj(ray3, tg::sphere2in3(pos30, r, n3));
+    // triangle
+    test_solid_obj(ray2, tg::triangle2(pos20, pos21, pos22));
+    test_obj(ray3, tg::triangle3(pos30, pos31, pos32));
 }
 
 TG_FUZZ_TEST(Intersect, LineLine2)
diff --git a/tests/feature/objects/aabb.cc b/tests/feature/objects/aabb.cc
index ab7152f560550f943407528936d459bd48b341a3..5836bd6c97da079b804a55ade7071449e13d20a5 100644
--- a/tests/feature/objects/aabb.cc
+++ b/tests/feature/objects/aabb.cc
@@ -24,40 +24,40 @@ TG_FUZZ_TEST(TypedGeometry, AABB)
 
 TG_FUZZ_TEST(TypedGeometry, ObjectAABB)
 {;
-    auto test_obj = [&](auto obj) {
+    auto const test_obj = [&](auto const& obj) {
         auto bb = aabb_of(obj);
         auto p = uniform(rng, obj);
         CHECK(contains(bb, p));
     };
 
 
-    const auto r = uniform(rng, 0.0f, 10.0f);
-    const auto h = uniform(rng, 0.0f, 10.0f);
-    const auto n1 = tg::dir(uniform(rng, tg::sphere_boundary<1, float>::unit));
-    const auto n2 = tg::dir(uniform(rng, tg::sphere_boundary<2, float>::unit));
-    const auto n3 = tg::dir(uniform(rng, tg::sphere_boundary<3, float>::unit));
+    auto const r = uniform(rng, 0.0f, 10.0f);
+    auto const h = uniform(rng, 0.0f, 10.0f);
+    auto const n1 = tg::dir(uniform(rng, tg::sphere_boundary<1, float>::unit));
+    auto const n2 = tg::dir(uniform(rng, tg::sphere_boundary<2, float>::unit));
+    auto const n3 = tg::dir(uniform(rng, tg::sphere_boundary<3, float>::unit));
 
-    const auto range1 = tg::aabb1(-10,10);
-    const auto range2 = tg::aabb2(-10,10);
-    const auto range3 = tg::aabb3(-10,10);
-    const auto range4 = tg::aabb4(-10,10);
+    auto const range1 = tg::aabb1(-10,10);
+    auto const range2 = tg::aabb2(-10,10);
+    auto const range3 = tg::aabb3(-10,10);
+    auto const range4 = tg::aabb4(-10,10);
 
-    const auto pos10 = uniform(rng, range1);
-    const auto pos11 = uniform(rng, range1);
+    auto const pos10 = uniform(rng, range1);
+    auto const pos11 = uniform(rng, range1);
 
-    const auto pos20 = uniform(rng, range2);
-    const auto pos21 = uniform(rng, range2);
-    const auto pos22 = uniform(rng, range2);
+    auto const pos20 = uniform(rng, range2);
+    auto const pos21 = uniform(rng, range2);
+    auto const pos22 = uniform(rng, range2);
 
-    const auto pos30 = uniform(rng, range3);
-    const auto pos31 = uniform(rng, range3);
-    const auto pos32 = uniform(rng, range3);
+    auto const pos30 = uniform(rng, range3);
+    auto const pos31 = uniform(rng, range3);
+    auto const pos32 = uniform(rng, range3);
 
-    const auto pos40 = uniform(rng, range4);
-    const auto pos41 = uniform(rng, range4);
-    const auto pos42 = uniform(rng, range4);
+    auto const pos40 = uniform(rng, range4);
+    auto const pos41 = uniform(rng, range4);
+    auto const pos42 = uniform(rng, range4);
 
-    const auto axis0 = tg::segment3(pos30, pos31);
+    auto const axis0 = tg::segment3(pos30, pos31);
 
     auto d1 = tg::uniform<tg::dir1>(rng);
     auto m1 = tg::mat1();
@@ -111,7 +111,7 @@ TG_FUZZ_TEST(TypedGeometry, ObjectAABB)
     // test_obj(p4, tg::hemisphere4(pos40, r, n4));
     // pyramid
     test_obj(tg::pyramid<tg::box2in3>(tg::box2in3(pos30, m23), h));
-    //test_obj(tg::pyramid<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30)), h)); // TODO: uniform(quad) missing
+    //test_obj(tg::pyramid<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos30 - pos31)), h)); // TODO: uniform(quad) missing
     test_obj(tg::pyramid<tg::sphere2in3>(tg::sphere2in3(pos30, r, n3), h)); // == cone
     test_obj(tg::pyramid<tg::triangle3>(tg::triangle3(pos30, pos31, pos32), h));
     // TODO: test for quad require uniform(quad)
diff --git a/tests/feature/objects/any_point.cc b/tests/feature/objects/any_point.cc
index dbe7da1a92d38bd9d580a4036724061fef0fa221..ab9c7e387ff6650f27509d5e2b12ad5a88e06593 100644
--- a/tests/feature/objects/any_point.cc
+++ b/tests/feature/objects/any_point.cc
@@ -2,66 +2,66 @@
 
 TG_FUZZ_TEST(TypedGeometry, AnyPoint)
 {
-    const auto tolerance = 0.001f;
-    auto const test_obj = [tolerance](const auto& o) {
+    auto const tolerance = 0.001f;
+    auto const test_obj = [tolerance](auto const& o) {
         auto p = any_point(o);
         CHECK(contains(o, p, tolerance));
     };
 
-    auto const test_obj_and_boundary = [&test_obj](const auto& o) {
+    auto const test_obj_and_boundary = [&test_obj](auto const& o) {
         test_obj(o);
         test_obj(boundary_of(o));
     };
 
-    auto const test_obj_and_boundary_no_caps = [&test_obj](const auto& o) {
+    auto const test_obj_and_boundary_no_caps = [&test_obj](auto const& 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);
+    auto const r = uniform(rng, 0.0f, 10.0f);
+    auto const h = uniform(rng, 0.0f, 10.0f);
+    auto const a = tg::uniform<tg::angle>(rng);
+    auto const n1 = tg::uniform<tg::dir1>(rng);
+    auto const n2 = tg::uniform<tg::dir2>(rng);
+    auto const n3 = tg::uniform<tg::dir3>(rng);
+    auto const 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));
+    auto const range1 = tg::aabb1(tg::pos1(-10), tg::pos1(10));
+    auto const range2 = tg::aabb2(tg::pos2(-10), tg::pos2(10));
+    auto const range3 = tg::aabb3(tg::pos3(-10), tg::pos3(10));
+    auto const range4 = tg::aabb4(tg::pos4(-10), tg::pos4(10));
 
-    const auto pos10 = uniform(rng, range1);
-    const auto pos11 = uniform(rng, range1);
+    auto const pos10 = uniform(rng, range1);
+    auto const pos11 = uniform(rng, range1);
 
-    const auto pos20 = uniform(rng, range2);
-    const auto pos21 = uniform(rng, range2);
-    const auto pos22 = uniform(rng, range2);
+    auto const pos20 = uniform(rng, range2);
+    auto const pos21 = uniform(rng, range2);
+    auto const pos22 = uniform(rng, range2);
 
-    const auto pos30 = uniform(rng, range3);
-    const auto pos31 = uniform(rng, range3);
-    const auto pos32 = uniform(rng, range3);
+    auto const pos30 = uniform(rng, range3);
+    auto const pos31 = uniform(rng, range3);
+    auto const pos32 = uniform(rng, range3);
 
-    const auto pos40 = uniform(rng, range4);
-    const auto pos41 = uniform(rng, range4);
+    auto const pos40 = uniform(rng, range4);
+    auto const pos41 = uniform(rng, range4);
 
-    const auto axis0 = tg::segment3(pos30, pos31);
-    const auto disk0 = tg::sphere2in3(pos30, r, n3);
+    auto const axis0 = tg::segment3(pos30, pos31);
+    auto const disk0 = tg::sphere2in3(pos30, r, n3);
 
-    const auto d1 = tg::uniform<tg::dir1>(rng);
+    auto const 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 const d20 = tg::uniform<tg::dir2>(rng);
+    auto const 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 const d30 = tg::uniform<tg::dir3>(rng);
+    auto const d31 = any_normal(d30);
+    auto const 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);
@@ -103,8 +103,10 @@ TG_FUZZ_TEST(TypedGeometry, AnyPoint)
     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_cone2(pos20, n2, a));
     test_obj_and_boundary(tg::inf_cone3(pos30, n3, a));
     // inf_cylinder
+    test_obj_and_boundary(tg::inf_cylinder2(tg::line2(pos20, n2), r));
     test_obj_and_boundary(tg::inf_cylinder3(tg::line3(pos30, n3), r));
     // line
     test_obj(tg::line1(pos10, n1));
@@ -120,8 +122,8 @@ TG_FUZZ_TEST(TypedGeometry, AnyPoint)
     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));
+    test_obj(tg::pyramid_boundary_no_caps<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos30 - pos31)), h));
+    // test_obj_and_boundary_no_caps(tg::pyramid<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos30 - pos31)), h));
     // TODO: quad
     // test_obj(tg::quad2(pos20, pos21, pos22, pos23));
     // test_obj(tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30)));
diff --git a/tests/feature/objects/centroid.cc b/tests/feature/objects/centroid.cc
index 290c219921e0689e50f636fd2338180d517c7e8f..09360733ab0b46a81b4beddd133ab8ed5db5b071 100644
--- a/tests/feature/objects/centroid.cc
+++ b/tests/feature/objects/centroid.cc
@@ -16,9 +16,9 @@ TG_FUZZ_TEST(TypedGeometry, Centroid)
     }
     {
         auto t = tg::triangle2(uniform(rng, range2), uniform(rng, range2), uniform(rng, range2));
-        const auto center = centroid_of(t);
+        auto const center = centroid_of(t);
         CHECK(contains(t, center));
-        const auto transl = uniform_vec(rng, range2);
+        auto const transl = uniform_vec(rng, range2);
 
         t.pos0.x += transl.x;
         t.pos0.y += transl.y;
@@ -27,7 +27,7 @@ TG_FUZZ_TEST(TypedGeometry, Centroid)
         t.pos2.x += transl.x;
         t.pos2.y += transl.y;
 
-        const auto newCenter = center + transl;
+        auto const newCenter = center + transl;
 
         CHECK(contains(t, newCenter));
         CHECK(centroid_of(t) == approx(newCenter));
@@ -56,29 +56,29 @@ TG_FUZZ_TEST(TypedGeometry, Centroid)
 
     {
         // tetrahedron
-        const auto pyTri = tg::pyramid<tg::triangle3>(tg::triangle3(uniform(rng, range3), uniform(rng, range3), uniform(rng, range3)), uniform(rng, 0.1f, 10.f));
+        auto const pyTri = tg::pyramid<tg::triangle3>(tg::triangle3(uniform(rng, range3), uniform(rng, range3), uniform(rng, range3)), uniform(rng, 0.1f, 10.f));
         auto center = apex_of(pyTri);
-        for (const auto& vertex : vertices_of(pyTri.base))
+        for (auto const& vertex : vertices_of(pyTri.base))
             center += vertex;
         center /= 4.f;
-        const auto centroid = centroid_of(pyTri);
+        auto const centroid = centroid_of(pyTri);
         CHECK(centroid == approx(center));
     }
 }
 
 TG_FUZZ_TEST(TypedGeometry, CentroidByUniform)
 {
-    const auto tolerance = 0.25f;
-    const auto numSamples = 100000;
+    auto const tolerance = 0.25f;
+    auto const numSamples = 100000;
 
     auto const test_obj = [&rng, numSamples, tolerance](auto const& o) {
         auto center = uniform(rng, o);
         for (auto i = 1; i < numSamples; ++i)
             center += uniform(rng, o);
         center /= numSamples;
-        const auto centroid = centroid_of(o);
-        const auto relError = tg::distance_sqr(center, centroid) / tg::distance_sqr_to_origin(centroid);
-        const auto approxEqual = centroid == approx(center, tolerance) || relError <= tg::pow2(tolerance);
+        auto const centroid = centroid_of(o);
+        auto const relError = tg::distance_sqr(center, centroid) / tg::distance_sqr_to_origin(centroid);
+        auto const approxEqual = centroid == approx(center, tolerance) || relError <= tg::pow2(tolerance);
         CHECK(approxEqual);
     };
 
@@ -93,47 +93,47 @@ TG_FUZZ_TEST(TypedGeometry, CentroidByUniform)
         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 n2 = tg::uniform<tg::dir2>(rng);
-    const auto n3 = tg::uniform<tg::dir3>(rng);
+    auto const r = uniform(rng, 0.0f, 10.0f);
+    auto const h = uniform(rng, 0.0f, 10.0f);
+    auto const n2 = tg::uniform<tg::dir2>(rng);
+    auto const n3 = tg::uniform<tg::dir3>(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));
+    auto const range1 = tg::aabb1(tg::pos1(-10), tg::pos1(10));
+    auto const range2 = tg::aabb2(tg::pos2(-10), tg::pos2(10));
+    auto const range3 = tg::aabb3(tg::pos3(-10), tg::pos3(10));
+    auto const range4 = tg::aabb4(tg::pos4(-10), tg::pos4(10));
 
-    const auto pos10 = uniform(rng, range1);
-    const auto pos11 = uniform(rng, range1);
+    auto const pos10 = uniform(rng, range1);
+    auto const pos11 = uniform(rng, range1);
 
-    const auto pos20 = uniform(rng, range2);
-    const auto pos21 = uniform(rng, range2);
-    const auto pos22 = uniform(rng, range2);
+    auto const pos20 = uniform(rng, range2);
+    auto const pos21 = uniform(rng, range2);
+    auto const pos22 = uniform(rng, range2);
 
-    const auto pos30 = uniform(rng, range3);
-    const auto pos31 = uniform(rng, range3);
-    const auto pos32 = uniform(rng, range3);
+    auto const pos30 = uniform(rng, range3);
+    auto const pos31 = uniform(rng, range3);
+    auto const pos32 = uniform(rng, range3);
 
-    const auto pos40 = uniform(rng, range4);
-    const auto pos41 = uniform(rng, range4);
-    const auto pos42 = uniform(rng, range4);
+    auto const pos40 = uniform(rng, range4);
+    auto const pos41 = uniform(rng, range4);
+    auto const pos42 = uniform(rng, range4);
 
-    const auto axis0 = tg::segment3(pos30, pos31);
-    const auto disk0 = tg::sphere2in3(pos30, r, n3);
+    auto const axis0 = tg::segment3(pos30, pos31);
+    auto const disk0 = tg::sphere2in3(pos30, r, n3);
 
-    const auto d1 = tg::uniform<tg::dir1>(rng);
+    auto const 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 const d20 = tg::uniform<tg::dir2>(rng);
+    auto const 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 const d30 = tg::uniform<tg::dir3>(rng);
+    auto const d31 = any_normal(d30);
+    auto const 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);
@@ -173,8 +173,8 @@ TG_FUZZ_TEST(TypedGeometry, CentroidByUniform)
     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_and_boundary_no_caps(tg::pyramid<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30)), h)); // TODO: area(quad) missing
-    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 + (pos30 - pos31)), h)); // TODO: area(quad) missing
+    test_obj(tg::pyramid_boundary_no_caps<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos30 - pos31)), h));
     // TODO: quad
     // segment
     test_obj(tg::segment1(pos10, pos11));
diff --git a/tests/feature/objects/project.cc b/tests/feature/objects/project.cc
index e496d308afeca40933f100210788f87ee90cf804..c157b192eec81ba382b0cc6298a8a71ae35231bf 100644
--- a/tests/feature/objects/project.cc
+++ b/tests/feature/objects/project.cc
@@ -4,7 +4,7 @@
 
 TG_FUZZ_TEST_MAX_ITS_MAX_CYCLES(TypedGeometry, Project, 25, 100'000'000'000)
 {
-    auto const test_obj = [&rng](auto p, auto o) {
+    auto const test_obj = [&rng](auto const& p, auto const& o) {
         auto proj = project(p, o);
 
         // Projected point lies in the object
@@ -29,63 +29,63 @@ TG_FUZZ_TEST_MAX_ITS_MAX_CYCLES(TypedGeometry, Project, 25, 100'000'000'000)
         CHECK(dist == approx(0.0f));
     };
 
-    auto const test_obj_and_boundary = [&test_obj](auto p, auto o) {
+    auto const test_obj_and_boundary = [&test_obj](auto const& p, auto const& o) {
         test_obj(p, o);
         test_obj(p, boundary_of(o));
     };
 
-    auto const test_obj_and_boundary_no_caps = [&test_obj](auto p, auto o) {
+    auto const test_obj_and_boundary_no_caps = [&test_obj](auto const& p, auto const& o) {
         test_obj(p, o);
         test_obj(p, boundary_of(o));
         test_obj(p, 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 n2 = tg::uniform<tg::dir2>(rng);
-    const auto n3 = tg::uniform<tg::dir3>(rng);
+    auto const r = uniform(rng, 0.0f, 10.0f);
+    auto const h = uniform(rng, 0.0f, 10.0f);
+    auto const n2 = tg::uniform<tg::dir2>(rng);
+    auto const n3 = tg::uniform<tg::dir3>(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));
+    auto const range1 = tg::aabb1(tg::pos1(-10), tg::pos1(10));
+    auto const range2 = tg::aabb2(tg::pos2(-10), tg::pos2(10));
+    auto const range3 = tg::aabb3(tg::pos3(-10), tg::pos3(10));
+    auto const range4 = tg::aabb4(tg::pos4(-10), tg::pos4(10));
 
-    const auto p1 = uniform(rng, range1);
-    const auto p2 = uniform(rng, range2);
-    const auto p3 = uniform(rng, range3);
-    const auto p4 = uniform(rng, range4);
+    auto const p1 = uniform(rng, range1);
+    auto const p2 = uniform(rng, range2);
+    auto const p3 = uniform(rng, range3);
+    auto const p4 = uniform(rng, range4);
 
-    const auto pos10 = uniform(rng, range1);
-    const auto pos11 = uniform(rng, range1);
+    auto const pos10 = uniform(rng, range1);
+    auto const pos11 = uniform(rng, range1);
 
-    const auto pos20 = uniform(rng, range2);
-    const auto pos21 = uniform(rng, range2);
-    const auto pos22 = uniform(rng, range2);
+    auto const pos20 = uniform(rng, range2);
+    auto const pos21 = uniform(rng, range2);
+    auto const pos22 = uniform(rng, range2);
 
-    const auto pos30 = uniform(rng, range3);
-    const auto pos31 = uniform(rng, range3);
-    const auto pos32 = uniform(rng, range3);
+    auto const pos30 = uniform(rng, range3);
+    auto const pos31 = uniform(rng, range3);
+    auto const pos32 = uniform(rng, range3);
 
-    const auto pos40 = uniform(rng, range4);
-    const auto pos41 = uniform(rng, range4);
+    auto const pos40 = uniform(rng, range4);
+    auto const pos41 = uniform(rng, range4);
 
-    const auto axis0 = tg::segment3(pos30, pos31);
-    const auto disk0 = tg::sphere2in3(pos30, r, n3);
+    auto const axis0 = tg::segment3(pos30, pos31);
+    auto const disk0 = tg::sphere2in3(pos30, r, n3);
 
-    const auto d1 = tg::uniform<tg::dir1>(rng);
+    auto const 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 const d20 = tg::uniform<tg::dir2>(rng);
+    auto const 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 const d30 = tg::uniform<tg::dir3>(rng);
+    auto const d31 = any_normal(d30);
+    auto const 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);
@@ -121,8 +121,8 @@ TG_FUZZ_TEST_MAX_ITS_MAX_CYCLES(TypedGeometry, Project, 25, 100'000'000'000)
     test_obj_and_boundary_no_caps(p3, tg::pyramid<tg::box2in3>(tg::box2in3(pos30, m23), h));
     test_obj_and_boundary_no_caps(p3, tg::pyramid<tg::sphere2in3>(disk0, h)); // == cone
     test_obj_and_boundary_no_caps(p3, tg::pyramid<tg::triangle3>(tg::triangle3(pos30, pos31, pos32), h));
-    // test_obj_and_boundary_no_caps(p3, tg::pyramid<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30)), h)); // TODO: project(quad) missing
-    test_obj(p3, tg::pyramid_boundary_no_caps<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30)), h));
+    // test_obj_and_boundary_no_caps(p3, tg::pyramid<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos30 - pos31)), h)); // TODO: project(quad) missing
+    test_obj(p3, tg::pyramid_boundary_no_caps<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos30 - pos31)), h));
     // TODO: quad
     // segment
     test_obj(p1, tg::segment1(pos10, pos11));
diff --git a/tests/feature/random/uniform.cc b/tests/feature/random/uniform.cc
index 26413c5adc2b57380450c9d95d179915e0f85d22..7bcf2431578c9399d570e7515c366a62b3a27867 100644
--- a/tests/feature/random/uniform.cc
+++ b/tests/feature/random/uniform.cc
@@ -39,10 +39,10 @@ TG_FUZZ_TEST(TypedGeometry, Uniform)
 
 TG_FUZZ_TEST_MAX_ITS(TypedGeometry, UniformGeneralProperties, 100)
 {
-    const auto tolerance = 0.01f;
-    const tg::u64 sampleSize = 32;
+    auto const tolerance = 0.01f;
+    tg::u64 const sampleSize = 32;
 
-    auto const test_obj = [&rng, tolerance](auto samples, auto o) {
+    auto const test_obj = [&rng, tolerance](auto samples, auto const& o) {
         for (tg::u64 i = 0; i < samples.size(); ++i)
         {
             auto p = uniform(rng, o);
@@ -54,12 +54,12 @@ TG_FUZZ_TEST_MAX_ITS(TypedGeometry, UniformGeneralProperties, 100)
         }
     };
 
-    auto const test_obj_and_boundary = [&test_obj](auto p, auto o) {
+    auto const test_obj_and_boundary = [&test_obj](auto const& p, auto const& o) {
         test_obj(p, o);
         test_obj(p, boundary_of(o));
     };
 
-    auto const test_obj_and_boundary_no_caps = [&test_obj](auto p, auto o) {
+    auto const test_obj_and_boundary_no_caps = [&test_obj](auto const& p, auto const& o) {
         test_obj(p, o);
         test_obj(p, boundary_of(o));
         test_obj(p, boundary_no_caps_of(o));
@@ -71,46 +71,46 @@ TG_FUZZ_TEST_MAX_ITS(TypedGeometry, UniformGeneralProperties, 100)
     auto samples3 = tg::array<tg::pos3, sampleSize>();
     auto samples4 = tg::array<tg::pos4, sampleSize>();
 
-    const auto r = 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);
+    auto const r = uniform(rng, 0.0f, 10.0f);
+    auto const h = uniform(rng, 0.0f, 10.0f);
+    auto const n2 = tg::uniform<tg::dir2>(rng);
+    auto const n3 = tg::uniform<tg::dir3>(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));
+    auto const range1 = tg::aabb1(tg::pos1(-10), tg::pos1(10));
+    auto const range2 = tg::aabb2(tg::pos2(-10), tg::pos2(10));
+    auto const range3 = tg::aabb3(tg::pos3(-10), tg::pos3(10));
+    auto const range4 = tg::aabb4(tg::pos4(-10), tg::pos4(10));
 
-    const auto pos10 = uniform(rng, range1);
-    const auto pos11 = uniform(rng, range1);
+    auto const pos10 = uniform(rng, range1);
+    auto const pos11 = uniform(rng, range1);
 
-    const auto pos20 = uniform(rng, range2);
-    const auto pos21 = uniform(rng, range2);
-    const auto pos22 = uniform(rng, range2);
+    auto const pos20 = uniform(rng, range2);
+    auto const pos21 = uniform(rng, range2);
+    auto const pos22 = uniform(rng, range2);
 
-    const auto pos30 = uniform(rng, range3);
-    const auto pos31 = uniform(rng, range3);
-    const auto pos32 = uniform(rng, range3);
+    auto const pos30 = uniform(rng, range3);
+    auto const pos31 = uniform(rng, range3);
+    auto const pos32 = uniform(rng, range3);
 
-    const auto pos40 = uniform(rng, range4);
-    const auto pos41 = uniform(rng, range4);
+    auto const pos40 = uniform(rng, range4);
+    auto const pos41 = uniform(rng, range4);
 
-    const auto axis0 = tg::segment3(pos30, pos31);
-    const auto disk0 = tg::sphere2in3(pos30, r, n3);
+    auto const axis0 = tg::segment3(pos30, pos31);
+    auto const disk0 = tg::sphere2in3(pos30, r, n3);
 
-    const auto d1 = tg::uniform<tg::dir1>(rng);
+    auto const 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 const d20 = tg::uniform<tg::dir2>(rng);
+    auto const 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 const d30 = tg::uniform<tg::dir3>(rng);
+    auto const d31 = any_normal(d30);
+    auto const 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);
@@ -152,8 +152,8 @@ TG_FUZZ_TEST_MAX_ITS(TypedGeometry, UniformGeneralProperties, 100)
     test_obj_and_boundary_no_caps(samples3, tg::pyramid<tg::box2in3>(tg::box2in3(pos30, m23), h));
     test_obj_and_boundary_no_caps(samples3, tg::pyramid<tg::sphere2in3>(disk0, h)); // == cone
     test_obj_and_boundary_no_caps(samples3, tg::pyramid<tg::triangle3>(tg::triangle3(pos30, pos31, pos32), h));
-    // test_obj_and_boundary_no_caps(samples3, tg::pyramid<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30)), h)); // TODO: uniform(quad) missing
-    test_obj(samples3, tg::pyramid_boundary_no_caps<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30)), h));
+    // test_obj_and_boundary_no_caps(samples3, tg::pyramid<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos30 - pos31)), h)); // TODO: uniform(quad) missing
+    test_obj(samples3, tg::pyramid_boundary_no_caps<tg::quad3>(tg::quad3(pos30, pos31, pos32, pos32 + (pos30 - pos31)), h));
     // TODO: quad
     // segment
     test_obj(samples1, tg::segment1(pos10, pos11));
diff --git a/tests/impl-report.cc b/tests/impl-report.cc
index ee0a1fd17884b5e788bd27c85c1cfd65da467ad7..06ef44a1f020c0a09e46ca6b5528e7aa635c3779 100644
--- a/tests/impl-report.cc
+++ b/tests/impl-report.cc
@@ -11,21 +11,6 @@
 
 namespace
 {
-// see https://stackoverflow.com/questions/44395169/why-is-sfinae-on-if-constexpr-not-allowed
-namespace detail
-{
-template <template <class...> class, class, class...>
-struct can_apply : std::false_type
-{
-};
-template <template <class...> class Z, class... Ts>
-struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type
-{
-};
-[[maybe_unused]] tg::rng rng;
-}
-template <template <class...> class Z, class... Ts>
-constexpr bool can_apply = detail::can_apply<Z, void, Ts...>::value;
 
 template <class T>
 using try_print = decltype(std::declval<std::ostream&>() << std::declval<T>());
@@ -55,7 +40,7 @@ template <class T>
 using try_contains_pos3 = decltype(contains(std::declval<T const&>(), tg::pos3()));
 
 template <class T>
-using try_uniform = decltype(uniform(detail::rng, std::declval<T const&>()));
+using try_uniform = decltype(uniform(std::declval<tg::rng>(), std::declval<T const&>()));
 
 template <class T>
 using try_aabb_of = decltype(aabb_of(std::declval<T const&>()));
@@ -96,7 +81,7 @@ void test_single_object_type(std::string name)
     static auto constexpr domainD = tg::object_traits<ObjT>::domain_dimension;
     static auto constexpr objectD = tg::object_traits<ObjT>::object_dimension;
     static auto constexpr solidD = [] {
-        if constexpr (can_apply<try_solid_of, ObjT>)
+        if constexpr (tg::can_apply<try_solid_of, ObjT>)
             return tg::object_traits<try_solid_of<ObjT>>::object_dimension;
         return objectD;
     }(); // ternary constexpr of solid domain with fallback to object domain
@@ -104,7 +89,7 @@ void test_single_object_type(std::string name)
     if constexpr (!std::is_default_constructible_v<ObjT>)
         std::cerr << "cannot default construct tg::" << name << std::endl;
 
-    if constexpr (can_apply<try_print, ObjT>)
+    if constexpr (tg::can_apply<try_print, ObjT>)
     {
         std::stringstream ss;
         ss << ObjT{};
@@ -118,39 +103,39 @@ void test_single_object_type(std::string name)
     else
         std::cerr << "cannot print tg::" << name << std::endl;
 
-    if constexpr (!can_apply<try_equal, ObjT>)
+    if constexpr (!tg::can_apply<try_equal, ObjT>)
         std::cerr << "equality not implemented for tg::" << name << std::endl;
 
     // TODO: somehow doesnt work?
-    if constexpr (!can_apply<try_less, ObjT>)
+    if constexpr (!tg::can_apply<try_less, ObjT>)
         std::cerr << "std::less not specialized for tg::" << name << std::endl;
 
-    if constexpr (!can_apply<try_hash, ObjT>)
+    if constexpr (!tg::can_apply<try_hash, ObjT>)
         std::cerr << "std::hash not specialized for tg::" << name << std::endl;
 
-    if constexpr (!can_apply<try_any_point, ObjT>)
+    if constexpr (!tg::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>)
+        if constexpr (!tg::can_apply<try_project_pos2, ObjT>)
         {
             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>)
+        if constexpr (!tg::can_apply<try_contains_pos2, ObjT>)
             std::cerr << "no contains(tg::" << name << ", tg::pos2)" << std::endl;
     }
     else if constexpr (domainD == 3)
     {
-        if constexpr (!can_apply<try_project_pos3, ObjT>)
+        if constexpr (!tg::can_apply<try_project_pos3, ObjT>)
         {
             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>)
+        if constexpr (!tg::can_apply<try_contains_pos3, ObjT>)
             std::cerr << "no contains(tg::" << name << ", tg::pos3)" << std::endl;
     }
     else
@@ -159,37 +144,37 @@ void test_single_object_type(std::string name)
     // operations for finite objects
     if constexpr (tg::object_traits<ObjT>::is_finite)
     {
-        if constexpr (!can_apply<try_uniform, ObjT>)
+        if constexpr (!tg::can_apply<try_uniform, ObjT>)
             std::cerr << "no uniform(tg::rng, tg::" << name << ")" << std::endl;
 
-        if constexpr (!can_apply<try_aabb_of, ObjT>)
+        if constexpr (!tg::can_apply<try_aabb_of, ObjT>)
             std::cerr << "no aabb_of(tg::" << name << ")" << std::endl;
 
-        if constexpr (!can_apply<try_centroid_of, ObjT>)
+        if constexpr (!tg::can_apply<try_centroid_of, ObjT>)
             std::cerr << "no centroid_of(tg::" << name << ")" << std::endl;
 
         if constexpr (objectD == 1)
         {
-            if constexpr (solidD == 1 && !can_apply<try_length_of, ObjT>)
+            if constexpr (solidD == 1 && !tg::can_apply<try_length_of, ObjT>)
                 std::cerr << "no length(tg::" << name << ")" << std::endl;
 
-            if constexpr (solidD != 1 && !can_apply<try_perimeter_of, ObjT>)
+            if constexpr (solidD != 1 && !tg::can_apply<try_perimeter_of, ObjT>)
                 std::cerr << "no perimeter_of(tg::" << name << ")" << std::endl;
         }
         else if constexpr (objectD == 2)
         {
-            if constexpr (!can_apply<try_area_of, ObjT>)
+            if constexpr (!tg::can_apply<try_area_of, ObjT>)
                 std::cerr << "no area_of(tg::" << name << ")" << std::endl;
 
-            if constexpr (solidD == 2 && !can_apply<try_perimeter_of, ObjT>)
+            if constexpr (solidD == 2 && !tg::can_apply<try_perimeter_of, ObjT>)
                 std::cerr << "no perimeter_of(tg::" << name << ")" << std::endl;
         }
         else if constexpr (objectD == 3)
         {
-            if constexpr (!can_apply<try_volume_of, ObjT>)
+            if constexpr (!tg::can_apply<try_volume_of, ObjT>)
                 std::cerr << "no volume_of(tg::" << name << ")" << std::endl;
 
-            if constexpr (solidD == 3 && !can_apply<try_area_of, ObjT>)
+            if constexpr (solidD == 3 && !tg::can_apply<try_area_of, ObjT>)
                 std::cerr << "no area_of(tg::" << name << ")" << std::endl;
         }
         else
@@ -199,14 +184,14 @@ void test_single_object_type(std::string name)
     // ray intersections
     if constexpr (objectD >= domainD - 1)
     {
-        if constexpr (domainD == 2 && !can_apply<try_closest_ray2_intersect_of, ObjT>)
+        if constexpr (domainD == 2 && !tg::can_apply<try_closest_ray2_intersect_of, ObjT>)
             std::cerr << "no closest_intersection_parameter(tg::ray2, tg::" << name << ")" << std::endl;
-        if constexpr (domainD == 3 && !can_apply<try_closest_ray3_intersect_of, ObjT>)
+        if constexpr (domainD == 3 && !tg::can_apply<try_closest_ray3_intersect_of, ObjT>)
             std::cerr << "no closest_intersection_parameter(tg::ray3, tg::" << name << ")" << std::endl;
 
-        if constexpr (domainD == 2 && !can_apply<try_intersects_aabb2_of, ObjT>)
+        if constexpr (domainD == 2 && !tg::can_apply<try_intersects_aabb2_of, ObjT>)
             std::cerr << "no intersects(tg::" << name << ", tg::aabb2)" << std::endl;
-        if constexpr (domainD == 3 && !can_apply<try_intersects_aabb3_of, ObjT>)
+        if constexpr (domainD == 3 && !tg::can_apply<try_intersects_aabb3_of, ObjT>)
             std::cerr << "no intersects(tg::" << name << ", tg::aabb3)" << std::endl;
 
         // TODO: more