diff --git a/extern/typed-geometry b/extern/typed-geometry index a6c6371f1bac47f0f269032549adb1873d1f32e4..3f36234671dc3fbead5b0398912809e4c803df2c 160000 --- a/extern/typed-geometry +++ b/extern/typed-geometry @@ -1 +1 @@ -Subproject commit a6c6371f1bac47f0f269032549adb1873d1f32e4 +Subproject commit 3f36234671dc3fbead5b0398912809e4c803df2c diff --git a/samples/04-object-properties/main.cc b/samples/04-object-properties/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..5119f84477f2b18e8c9504428a8645f91f616a7c --- /dev/null +++ b/samples/04-object-properties/main.cc @@ -0,0 +1,291 @@ +#include <typed-geometry/tg.hh> + +#include <glow-extras/glfw/GlfwContext.hh> +#include <glow-extras/viewer/view.hh> + +template <class Rng, class Obj> +void test_obj(Rng& rng, Obj const& obj) +{ + auto const domainD = tg::object_traits<Obj>::domain_dimension; + auto const objectD = tg::object_traits<Obj>::object_dimension; + auto const solidD = [&] { + if constexpr (tg::object_traits<Obj>::is_boundary) + return objectD + 1; + return objectD; + }(); + using TraitsT = typename tg::object_traits<Obj>::tag_t; + + tg::aabb<domainD, float> aabbBigger; + auto nameString = tg::to_string(""); + + auto g = gv::grid(); + + if constexpr (tg::object_traits<Obj>::is_finite) + { + float size; + if constexpr (objectD == 3) + size = 1000.f + volume_of(obj); + else if constexpr (objectD == 2) + size = 500.f + area_of(obj); + else if constexpr (tg::object_traits<Obj>::is_boundary) + size = 100.f + perimeter_of(obj); + else + size = 100.f + length(obj); + + auto uniformPts = std::vector<tg::pos3>(); + for (auto i = 0; i < size; ++i) + uniformPts.emplace_back(uniform(rng, obj)); + + auto const aabb = aabb_of(obj); + aabbBigger = tg::aabb(aabb.min - 0.25f, aabb.max + 0.25f); + tg::aabb3 aabb3; + if constexpr (domainD == 3) + aabb3 = aabb; + else + aabb3 = tg::aabb3(tg::pos3(aabb.min), tg::pos3(aabb.max)); + + auto v = gv::view(); + + // uniform + gv::view(uniformPts, tg::to_string(obj)); + + // any_point + gv::view(gv::points(tg::pos3(any_point(obj))).point_size_px(12.f), tg::color4::red); + + // centroid + auto const centroid = centroid_of(obj); + if (any_point(obj) == centroid) + gv::view(gv::points(tg::pos3(centroid)).point_size_px(13.f), tg::color4::yellow); + else + gv::view(gv::points(tg::pos3(centroid)).point_size_px(12.f), tg::color4::green); + + // aabb_of + gv::view(gv::lines(aabb3)); + + // normal_of + if constexpr (domainD == 3 && solidD == 2) + gv::view(tg::segment3(centroid, centroid + normal_of(obj)), tg::color4::green); + } + else // infinite objects + { + auto const p = any_point(obj); + aabbBigger = tg::aabb<domainD, float>(p - 10.f, p + 10.f); + nameString = " for " + tg::to_string(obj); + } + + // common visualization for finite and infinite objects + { + auto pointsInside = std::vector<tg::pos3>(); + auto pointsOutside = std::vector<tg::pos3>(); + auto const numPts = tg::pow(20.f, domainD); + auto const tolerance = objectD < domainD ? 0.1f : 0.f; + for (auto i = 0; i < numPts; ++i) + { + auto const p = uniform(rng, aabbBigger); + if (contains(obj, p, tolerance)) + pointsInside.emplace_back(p); + else + pointsOutside.emplace_back(p); + } + + // contains + auto v = gv::view(); + view(pointsInside, gv::maybe_empty, tg::color4::green, "contains (+-" + tg::to_string(tolerance) + ")" + nameString); + gv::view(gv::points(pointsOutside).point_size_px(5.f)); + } + + if constexpr (!std::is_same_v<Obj, tg::ellipse<solidD, float, domainD, TraitsT>>) + { + auto points = std::vector<tg::pos3>(); + auto lines = std::vector<tg::segment3>(); + auto const numPts = tg::pow(20.f, domainD); + for (auto i = 0; i < numPts; ++i) + { + auto const p = uniform(rng, aabbBigger); + auto const pProj = tg::pos3(project(p, obj)); + points.emplace_back(pProj); + if (!contains(obj, p)) + lines.emplace_back(tg::pos3(p), pProj); + } + + // project + auto v = gv::view(); + gv::view(points, "project"); + gv::view(lines); + } + + if constexpr (objectD >= domainD - 1) + { + auto linesGray = std::vector<tg::segment3>(); + auto linesGreen = std::vector<tg::segment3>(); + auto linesRed = std::vector<tg::segment3>(); + auto pointsGray = std::vector<tg::pos3>(); + auto pointsGreen = std::vector<tg::pos3>(); + auto pointsRed = std::vector<tg::pos3>(); + auto const numPts = tg::pow(10.f, domainD); + for (auto i = 0; i < numPts; ++i) + { + auto const p = uniform(rng, aabbBigger); + auto const d = tg::uniform<tg::dir<domainD, float>>(rng); + auto const r = tg::ray(p, d); + if (intersects(r, obj)) + { + if constexpr (std::is_same_v<decltype(tg::intersection_parameter(r, obj)), tg::optional<tg::hit_interval<float>>>) + { + auto const ts = tg::intersection_parameter(r, obj).value(); + if (ts.start > 0.f) + { + pointsGray.emplace_back(p); + linesGray.emplace_back(tg::pos3(p), tg::pos3(r[ts.start])); + } + pointsGreen.emplace_back(r[ts.start]); + if (ts.end == tg::max<float>()) + linesGreen.emplace_back(tg::pos3(r[ts.start]), tg::pos3(r[ts.start] + d)); + else + { + linesGreen.emplace_back(tg::pos3(r[ts.start]), tg::pos3(r[ts.end])); + pointsGreen.emplace_back(r[ts.end]); + linesGray.emplace_back(tg::pos3(r[ts.end]), tg::pos3(r[ts.end] + d)); + } + } + else + { + auto const ts = tg::intersection_parameter(r, obj); + pointsGray.emplace_back(p); + for (auto const& t : ts) + pointsGreen.emplace_back(r[t]); + linesGray.emplace_back(tg::pos3(p), tg::pos3(r[ts.last()] + d)); + } + } + else + { + pointsRed.emplace_back(p); + linesRed.emplace_back(tg::pos3(p), tg::pos3(p + d)); + } + } + + // ray intersection + auto v = gv::view(); + gv::view(linesGray, "ray intersection", tg::aabb3(tg::pos3(aabbBigger.min), tg::pos3(aabbBigger.max))); + view(linesGreen, gv::maybe_empty, tg::color4::green); + gv::view(linesRed, tg::color4::red); + gv::view(pointsGray); + gv::view(pointsGreen, tg::color4::green); + gv::view(pointsRed, tg::color4::red); + } +} + +int main() +{ + auto const test_obj_and_boundary = [](auto& rng, auto const& o) { + test_obj(rng, o); + test_obj(rng, boundary_of(o)); + }; + + auto const test_obj_and_boundary_no_caps = [](auto& rng, auto const& o) { + test_obj(rng, o); + test_obj(rng, boundary_of(o)); + test_obj(rng, boundary_no_caps_of(o)); + }; + + glow::glfw::GlfwContext ctx; + tg::rng rng; + + 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 n2 = tg::uniform<tg::dir2>(rng); + auto const n3 = tg::uniform<tg::dir3>(rng); + + auto const range2 = tg::aabb2(tg::pos2(-10), tg::pos2(10)); + auto const range3 = tg::aabb3(tg::pos3(-10), tg::pos3(10)); + + 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 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); + + // aabb + test_obj_and_boundary(rng, aabb_of(pos20, pos21)); + test_obj_and_boundary(rng, aabb_of(pos30, pos31)); + // box + test_obj_and_boundary(rng, tg::box2(pos20, m2)); + test_obj_and_boundary(rng, tg::box3(pos30, m3)); + test_obj_and_boundary(rng, tg::box2in3(pos30, m23)); + // capsule + test_obj_and_boundary(rng, tg::capsule3(axis0, r)); + // cylinder + test_obj_and_boundary_no_caps(rng, tg::cylinder3(axis0, r)); + // ellipse + test_obj_and_boundary(rng, tg::ellipse2(pos20, m2)); + test_obj_and_boundary(rng, tg::ellipse3(pos30, m3)); + test_obj_and_boundary(rng, tg::ellipse2in3(pos30, m23)); + // halfspace + test_obj(rng, tg::halfspace2(n2, h)); + test_obj(rng, tg::halfspace3(n3, h)); + // hemisphere + test_obj_and_boundary_no_caps(rng, tg::hemisphere2(pos20, r, n2)); + test_obj_and_boundary_no_caps(rng, tg::hemisphere3(pos30, r, n3)); + // inf_cone + test_obj_and_boundary(rng, tg::inf_cone2(pos20, n2, a)); + test_obj_and_boundary(rng, tg::inf_cone3(pos30, n3, a)); + // inf_cylinder + test_obj_and_boundary(rng, tg::inf_cylinder2(tg::line2(pos20, n2), r)); + test_obj_and_boundary(rng, tg::inf_cylinder3(tg::line3(pos30, n3), r)); + // line + test_obj(rng, tg::line2(pos20, n2)); + test_obj(rng, tg::line3(pos30, n3)); + // plane + test_obj(rng, tg::plane2(n2, h)); + test_obj(rng, tg::plane3(n3, h)); + // pyramid + test_obj_and_boundary_no_caps(rng, tg::pyramid<tg::box2in3>(tg::box2in3(pos30, m23), h)); + test_obj_and_boundary_no_caps(rng, tg::pyramid<tg::sphere2in3>(disk0, h)); // == cone + test_obj_and_boundary_no_caps(rng, tg::pyramid<tg::triangle3>(tg::triangle3(pos30, pos31, pos32), h)); + test_obj(rng, 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(rng, tg::quad2(pos20, pos21, pos22, pos23)); + // test_obj(rng, tg::quad3(pos30, pos31, pos32, pos32 + (pos31 - pos30))); + // ray + test_obj(rng, tg::ray2(pos20, n2)); + test_obj(rng, tg::ray3(pos30, n3)); + // segment + test_obj(rng, tg::segment2(pos20, pos21)); + test_obj(rng, tg::segment3(pos30, pos31)); + // sphere + test_obj_and_boundary(rng, tg::sphere2(pos20, r)); + test_obj_and_boundary(rng, tg::sphere3(pos30, r)); + test_obj_and_boundary(rng, tg::sphere2in3(pos30, r, n3)); + // triangle + test_obj(rng, tg::triangle2(pos20, pos21, pos22)); + test_obj(rng, tg::triangle3(pos30, pos31, pos32)); +}