From 1a6444d010aff7e8c595c767ed24818ff284b119 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 5 Nov 2020 18:11:08 +0300 Subject: [PATCH] Add convex body utilities to cg --- examples/srtm.cpp | 631 +----------------- libs/cg/include/psemek/cg/body/body.hpp | 210 ++++++ libs/cg/include/psemek/cg/body/box.hpp | 119 ++++ libs/cg/include/psemek/cg/body/frustum.hpp | 72 ++ .../cg/include/psemek/cg/body/icosahedron.hpp | 78 +++ libs/cg/include/psemek/cg/body/prism.hpp | 87 +++ libs/cg/include/psemek/cg/convex/inside.hpp | 24 + .../include/psemek/cg/convex/separation.hpp | 141 ++++ 8 files changed, 747 insertions(+), 615 deletions(-) create mode 100644 libs/cg/include/psemek/cg/body/body.hpp create mode 100644 libs/cg/include/psemek/cg/body/box.hpp create mode 100644 libs/cg/include/psemek/cg/body/frustum.hpp create mode 100644 libs/cg/include/psemek/cg/body/icosahedron.hpp create mode 100644 libs/cg/include/psemek/cg/body/prism.hpp create mode 100644 libs/cg/include/psemek/cg/convex/inside.hpp create mode 100644 libs/cg/include/psemek/cg/convex/separation.hpp diff --git a/examples/srtm.cpp b/examples/srtm.cpp index ce077291..55a626db 100644 --- a/examples/srtm.cpp +++ b/examples/srtm.cpp @@ -14,576 +14,18 @@ #include #include +#include +#include +#include +#include +#include +#include + #include #include #include #include -namespace psemek::cg -{ - - template - struct box; - - template - box(geom::box) -> box; - - template - struct box - { - box() = default; - box(geom::box const & b); - - std::array, 8> vertices; - }; - - template - box::box(geom::box const & b) - { - for (std::size_t z = 0; z < 2; ++z) - { - for (std::size_t y = 0; y < 2; ++y) - { - for (std::size_t x = 0; x < 2; ++x) - { - std::size_t i = z * 4 + y * 2 + x; - - vertices[i][0] = (x == 0) ? b[0].min : b[0].max; - vertices[i][1] = (y == 0) ? b[1].min : b[1].max; - vertices[i][2] = (z == 0) ? b[2].min : b[2].max; - } - } - } - } - - template - auto const & vertices(box const & b) - { - return b.vertices; - } - - namespace detail - { - - inline auto const & cubiod_edges() - { - static const std::array, 12> result = - {{ - { 0b000, 0b001 }, - { 0b010, 0b011 }, - { 0b100, 0b101 }, - { 0b110, 0b111 }, - - { 0b000, 0b010 }, - { 0b001, 0b011 }, - { 0b100, 0b110 }, - { 0b101, 0b111 }, - - { 0b000, 0b100 }, - { 0b001, 0b101 }, - { 0b010, 0b110 }, - { 0b011, 0b111 }, - }}; - - return result; - } - - inline auto const & cubiod_faces() - { - static const std::array, 6> result = - {{ - {{ 0b000, 0b100, 0b110, 0b010 }}, - {{ 0b001, 0b011, 0b111, 0b101 }}, - {{ 0b000, 0b001, 0b101, 0b100 }}, - {{ 0b010, 0b110, 0b111, 0b011 }}, - {{ 0b000, 0b010, 0b011, 0b001 }}, - {{ 0b100, 0b101, 0b111, 0b110 }}, - }}; - - return result; - } - } - - template - auto const & edges(box const &) - { - return detail::cubiod_edges(); - } - - template - auto const & faces(box const &) - { - return detail::cubiod_faces(); - } - - template - auto const & face_normals(box const &) - { - static const std::array, 6> result = - {{ - {-1, 0, 0}, - { 1, 0, 0}, - { 0, -1, 0}, - { 0, 1, 0}, - { 0, 0, -1}, - { 0, 0, 1}, - }}; - - return result; - } - - template - auto const & edge_directions(box const &) - { - static const std::array, 3> result = - {{ - { 1, 0, 0}, - { 0, 1, 0}, - { 0, 0, 1}, - }}; - - return result; - } - - template - struct frustum; - - template - frustum(geom::matrix) -> frustum; - - template - struct frustum - { - frustum(geom::matrix const & m); - - std::array, 8> vertices; - std::array, 6> face_normals; - }; - - template - frustum::frustum(geom::matrix const & m) - { - bool flip = (geom::det(m) < 0); - - for (std::size_t z = 0; z < 2; ++z) - { - for (std::size_t y = 0; y < 2; ++y) - { - for (std::size_t x = 0; x < 2; ++x) - { - std::size_t i = z * 4 + y * 2 + (flip ? 1 - x : x); - - geom::vector p; - p[0] = (x == 0) ? -1 : 1; - p[1] = (y == 0) ? -1 : 1; - p[2] = (z == 0) ? -1 : 1; - p[3] = 1; - - geom::gauss(m, p); - - vertices[i] = geom::as_point(p); - } - } - } - } - - template - auto const & vertices(frustum const & f) - { - return f.vertices; - } - - template - auto const & edges(frustum const &) - { - return detail::cubiod_edges(); - } - - template - auto const & faces(frustum const &) - { - return detail::cubiod_faces(); - } - - template - struct triangular_prism - { - triangular_prism() = default; - triangular_prism(geom::triangle> const & t, geom::vector const & d); - - std::array, 6> vertices; - std::array, 4> edge_directions; - }; - - template - triangular_prism::triangular_prism(geom::triangle> const & t, geom::vector const & d) - { - for (std::size_t i = 0; i < 3; ++i) - { - vertices[i] = t[i]; - vertices[i + 3] = t[i] + d; - } - - if (geom::dot(geom::normal(vertices[0], vertices[1], vertices[2]), d) < 0) - { - std::swap(vertices[1], vertices[2]); - std::swap(vertices[4], vertices[5]); - } - - for (std::size_t i = 0; i < 3; ++i) - { - edge_directions[i] = geom::normalized(vertices[(i + 1) % 3] - vertices[i]); - } - edge_directions[3] = geom::normalized(d); - } - - template - auto const & vertices(triangular_prism const & p) - { - return p.vertices; - } - - template - auto const & edges(triangular_prism const &) - { - static const std::array, 9> result = - {{ - { 0, 1 }, - { 1, 2 }, - { 2, 0 }, - - { 3, 4 }, - { 4, 5 }, - { 5, 3 }, - - { 0, 3 }, - { 1, 4 }, - { 2, 5 }, - }}; - - return result; - } - - template - auto const & faces(triangular_prism const &) - { - static std::array, 5> result = - {{ - { 0, 2, 1 }, - { 3, 4, 5 }, - { 0, 1, 4, 3 }, - { 1, 2, 5, 4 }, - { 2, 0, 3, 5 }, - }}; - return result; - } - - template - auto const & edge_directions(triangular_prism const & p) - { - return p.edge_directions; - } - - namespace detail - { - - template - struct has_static_size - : std::false_type - {}; - - template - struct has_static_size> - : std::true_type - {}; - - template - constexpr bool has_static_size_v = has_static_size::value; - - template - struct static_size; - - template - struct static_size> - { - static constexpr std::size_t value = N; - }; - - template - constexpr std::size_t static_size_v = static_size::value; - - } - - template - auto triangles(Body const & b) - { - auto const & fs = faces(b); - - using faces_type = std::remove_cvref_t; - using face_type = std::remove_cvref_t; - using index_type = std::remove_cvref_t; - - auto impl = [&fs](auto out) - { - for (auto const & f : fs) - { - auto it0 = std::begin(f); - - for (auto it = std::next(it0), jt = std::next(it); jt < std::end(f); it = jt++) - { - *out++ = {*it0, *it, *jt}; - } - } - }; - - if constexpr (detail::has_static_size_v && detail::has_static_size_v) - { - constexpr std::size_t faces_count = detail::static_size_v; - constexpr std::size_t face_size = detail::static_size_v; - std::array, faces_count * (face_size - 2)> result; - impl(result.begin()); - return result; - } - else - { - std::vector> result; - if constexpr (detail::has_static_size_v) - { - constexpr std::size_t face_size = detail::static_size_v; - result.reserve(fs.size() * (face_size - 2)); - } - else - { - result.reserve(fs.size()); - } - impl(std::back_inserter(result)); - return result; - } - } - - template - auto edge_directions(Body const & b) - { - auto const & vs = vertices(b); - auto const & es = edges(b); - - using edges_type = std::remove_cvref_t; - using vector_type = std::remove_cvref_t; - - auto impl = [&es, &vs](auto out) - { - for (auto const & e : es) - { - *out++ = geom::normalized(vs[e[1]] - vs[e[0]]); - } - }; - - if constexpr (detail::has_static_size_v) - { - constexpr std::size_t edges_count = detail::static_size_v; - std::array result; - impl(result.begin()); - return result; - } - else - { - std::vector result; - result.reserve(es.size()); - impl(std::back_inserter(result)); - return result; - } - } - - template - auto faces(Body const & b) - { - return triangles(b); - } - - template - auto face_normals(Body const & b) - { - auto const & vs = vertices(b); - auto const & fs = faces(b); - - using faces_type = std::remove_cvref_t; - using vector_type = std::remove_cvref_t; - - auto impl = [&fs, &vs](auto out) - { - for (auto const & f : fs) - { - *out++ = geom::normal(vs[f[0]], vs[f[1]], vs[f[2]]); - } - }; - - if constexpr (detail::has_static_size_v) - { - constexpr std::size_t faces_count = detail::static_size_v; - std::array result; - impl(result.begin()); - return result; - } - else - { - std::vector result; - result.reserve(fs.size()); - impl(std::back_inserter(result)); - return result; - } - } - - template - bool inside(P const & p, Body const & body) - { - auto const & vs = vertices(body); - auto const & fs = faces(body); - - for (auto const & f : fs) - { - if (geom::orientation(vs[f[0]], vs[f[1]], vs[f[2]], p) == geom::sign_t::negative) - return false; - } - return true; - } - - // returns pair(normalized vector from 1 to 2, signed distance) - // distance <= 0 means intersection - template - auto separation(Body1 const & b1, Body2 const & b2) - { - auto const & vs1 = vertices(b1); - auto const & vs2 = vertices(b2); - - auto const & fs1 = faces(b1); - auto const & fs2 = faces(b2); - - auto const & eds1 = edge_directions(b1); - auto const & eds2 = edge_directions(b2); - - using vector_type = std::remove_cvref_t; - using scalar_type = std::remove_cvref_t; - - vector_type res_n = vector_type::zero(); - auto res_d = -std::numeric_limits::infinity(); - - auto process_faces = [](auto const & vs1, auto const & fs1, auto const & vs2) - { - vector_type res_n = vector_type::zero(); - scalar_type res_d = -std::numeric_limits::infinity(); - - for (auto const & f : fs1) - { - auto const face_n = geom::normal(vs1[f[0]], vs1[f[1]], vs1[f[2]]); - scalar_type face_d = std::numeric_limits::infinity(); - - auto const face_p = vs1[f[0]]; - - for (auto const & v : vs2) - { - auto const d = geom::dot(face_n, v - face_p); - face_d = std::min(d, face_d); - } - - if (face_d == std::numeric_limits::infinity()) - { - throw 42; - } - - if (face_d > res_d) - { - res_d = face_d; - res_n = face_n; - } - } - - return std::make_pair(res_n, res_d); - }; - - auto process_edges = [](auto const & vs1, auto const & eds1, auto const & vs2, auto const & eds2) - { - vector_type res_n = vector_type::zero(); - scalar_type res_d = -std::numeric_limits::infinity(); - - for (auto const & ed1 : eds1) - { - for (auto const & ed2 : eds2) - { - auto edge_n = geom::cross(ed1, ed2); - auto l = geom::length(edge_n); - if (l == 0) continue; - edge_n /= l; - - geom::interval i1, i2; - - for (auto const & v : vs1) - { - i1 |= geom::dot(geom::homogeneous(v), geom::homogeneous(edge_n)); - } - - for (auto const & v : vs2) - { - i2 |= geom::dot(geom::homogeneous(v), geom::homogeneous(edge_n)); - } - - auto edge_d12 = i2.min - i1.max; - auto edge_d21 = i1.min - i2.max; - - scalar_type edge_d; - - if (edge_d12 > edge_d21) - { - edge_d = edge_d12; - } - else - { - edge_d = edge_d21; - edge_n = -edge_n; - } - - if (edge_d > res_d) - { - res_d = edge_d; - res_n = edge_n; - } - } - } - - return std::make_pair(res_n, res_d); - }; - - { - auto res12 = process_faces(vs1, fs1, vs2); - if (res12.second > res_d) - { - res_d = res12.second; - res_n = res12.first; - } - } - - { - auto res21 = process_faces(vs2, fs2, vs1); - if (res21.second > res_d) - { - res_d = res21.second; - res_n = -res21.first; - } - } - - { - auto rese = process_edges(vs1, eds1, vs2, eds2); - if (rese.second > res_d) - { - res_d = rese.second; - res_n = rese.first; - } - } - - return std::make_pair(res_n, res_d); - } - -} - using namespace psemek; template @@ -617,53 +59,6 @@ private: T speed_; }; -static const float icosa_a = 0.850650808; // phi / sqrt(1 + phi^2) -static const float icosa_b = 0.525731112; // 1 / sqrt(1 + phi^2) - -static const float icosa_side = 1.05146222; // 2 / sqrt(phi * sqrt(5)) - -static const geom::vector icosa_vertices[12] = -{ - {-icosa_a, -icosa_b, 0.0}, // 0 - {-icosa_a, icosa_b, 0.0}, // 1 - { icosa_a, -icosa_b, 0.0}, // 2 - { icosa_a, icosa_b, 0.0}, // 3 - - {0.0, -icosa_a, -icosa_b}, // 4 - {0.0, -icosa_a, icosa_b}, // 5 - {0.0, icosa_a, -icosa_b}, // 6 - {0.0, icosa_a, icosa_b}, // 7 - - {-icosa_b, 0.0, -icosa_a}, // 8 - { icosa_b, 0.0, -icosa_a}, // 9 - {-icosa_b, 0.0, icosa_a}, // 10 - { icosa_b, 0.0, icosa_a}, // 11 -}; - -static const std::size_t icosa_faces[20][3] = -{ - {0, 1, 8,}, - {0, 10, 1,}, - {2, 9, 3,}, - {2, 3, 11,}, - {4, 5, 0,}, - {4, 2, 5,}, - {6, 1, 7,}, - {6, 7, 3,}, - {8, 9, 4,}, - {8, 6, 9,}, - {10, 5, 11,}, - {10, 11, 7,}, - {0, 8, 4,}, - {0, 5, 10,}, - {1, 6, 8,}, - {1, 10, 7,}, - {2, 4, 9,}, - {2, 11, 5,}, - {3, 9, 6,}, - {3, 7, 11}, -}; - static char const tile_vs[] = R"(#version 330 @@ -935,6 +330,12 @@ std::ostream & operator << (std::ostream & os, std::vector const & v) void srtm_app::present() { + cg::icosahedron icosahedron{geom::point::zero(), 1.f}; + + auto const & icosa_vertices = cg::vertices(icosahedron); + auto const & icosa_faces = cg::faces(icosahedron); + auto const icosa_side = geom::distance(icosa_vertices[icosa_faces[0][0]], icosa_vertices[icosa_faces[0][1]]); + std::vector info; gl::ClearColor(0.9f, 0.9f, 0.9f, 0.f); @@ -1113,9 +514,9 @@ void srtm_app::present() for (int f = 0; f < 20; ++f) { geom::vector v[3]; - v[0] = icosa_vertices[icosa_faces[f][0]]; - v[1] = icosa_vertices[icosa_faces[f][1]]; - v[2] = icosa_vertices[icosa_faces[f][2]]; + v[0] = icosa_vertices[icosa_faces[f][0]] - geom::point::zero(); + v[1] = icosa_vertices[icosa_faces[f][1]] - geom::point::zero(); + v[2] = icosa_vertices[icosa_faces[f][2]] - geom::point::zero(); id.push_back(f); visit(v); id.pop_back(); diff --git a/libs/cg/include/psemek/cg/body/body.hpp b/libs/cg/include/psemek/cg/body/body.hpp new file mode 100644 index 00000000..2dba99ea --- /dev/null +++ b/libs/cg/include/psemek/cg/body/body.hpp @@ -0,0 +1,210 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace psemek::cg +{ + + /* The basic interface of a 3D body is + * vertices(body) -> [point] + * edges(body) -> [(index, index)] + * faces(body) -> [[index]] + * triangles(body) -> [(index, index, index)] + * face_normals(body) -> [vector] + * edge_directions(body) -> [vector] + * + * Note that faces(body).size() == face_normals(body).size + * However, it is possible that edges(body).size() != edge_directions(body).size, + * if there are many collinear edges. + * + * Minimal set of required functions: + * vertices + * edges + * faces/triangles + */ + + namespace detail + { + + template + struct has_static_size + : std::false_type + {}; + + template + struct has_static_size> + : std::true_type + {}; + + template + constexpr bool has_static_size_v = has_static_size::value; + + template + struct static_size; + + template + struct static_size> + { + static constexpr std::size_t value = N; + }; + + template + constexpr std::size_t static_size_v = static_size::value; + + } + + template + auto faces(Body const & b) + { + return triangles(b); + } + + template + auto edges(Body const & b) + { + auto const & fs = faces(b); + + using index_type = std::remove_cvref_t; + + std::vector> result; + for (auto const & f : fs) + { + for (auto it = std::begin(f), jt = std::prev(std::end(f)); ++it, ++jt; it != std::end(f)) + { + geom::segment s{*jt, *it}; + if (s[0] > s[1]) std::swap(s[0], s[1]); + result.push_back(s); + } + } + + auto compare = [](auto const & s1, auto const & s2) + { + return std::tie(s1[0], s1[1]) < std::tie(s2[0], s2[1]); + }; + + std::sort(result.begin(), result.end(), compare); + result.erase(std::unique(result.begin(), result.end()), result.end()); + return result; + } + + template + auto triangles(Body const & b) + { + auto const & fs = faces(b); + + using faces_type = std::remove_cvref_t; + using face_type = std::remove_cvref_t; + using index_type = std::remove_cvref_t; + + auto impl = [&fs](auto out) + { + for (auto const & f : fs) + { + auto it0 = std::begin(f); + + for (auto it = std::next(it0), jt = std::next(it); jt < std::end(f); it = jt++) + { + *out++ = {*it0, *it, *jt}; + } + } + }; + + if constexpr (detail::has_static_size_v && detail::has_static_size_v) + { + constexpr std::size_t faces_count = detail::static_size_v; + constexpr std::size_t face_size = detail::static_size_v; + std::array, faces_count * (face_size - 2)> result; + impl(result.begin()); + return result; + } + else + { + std::vector> result; + if constexpr (detail::has_static_size_v) + { + constexpr std::size_t face_size = detail::static_size_v; + result.reserve(fs.size() * (face_size - 2)); + } + else + { + result.reserve(fs.size()); + } + impl(std::back_inserter(result)); + return result; + } + } + + template + auto edge_directions(Body const & b) + { + auto const & vs = vertices(b); + auto const & es = edges(b); + + using edges_type = std::remove_cvref_t; + using vector_type = std::remove_cvref_t; + + auto impl = [&es, &vs](auto out) + { + for (auto const & e : es) + { + *out++ = geom::normalized(vs[e[1]] - vs[e[0]]); + } + }; + + if constexpr (detail::has_static_size_v) + { + constexpr std::size_t edges_count = detail::static_size_v; + std::array result; + impl(result.begin()); + return result; + } + else + { + std::vector result; + result.reserve(es.size()); + impl(std::back_inserter(result)); + return result; + } + } + + template + auto face_normals(Body const & b) + { + auto const & vs = vertices(b); + auto const & fs = faces(b); + + using faces_type = std::remove_cvref_t; + using vector_type = std::remove_cvref_t; + + auto impl = [&fs, &vs](auto out) + { + for (auto const & f : fs) + { + *out++ = geom::normal(vs[f[0]], vs[f[1]], vs[f[2]]); + } + }; + + if constexpr (detail::has_static_size_v) + { + constexpr std::size_t faces_count = detail::static_size_v; + std::array result; + impl(result.begin()); + return result; + } + else + { + std::vector result; + result.reserve(fs.size()); + impl(std::back_inserter(result)); + return result; + } + } + +} diff --git a/libs/cg/include/psemek/cg/body/box.hpp b/libs/cg/include/psemek/cg/body/box.hpp new file mode 100644 index 00000000..200235fb --- /dev/null +++ b/libs/cg/include/psemek/cg/body/box.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include + +#include + +namespace psemek::cg +{ + + template + struct box; + + template + box(geom::box) -> box; + + template + struct box + { + box() = default; + box(geom::box const & b); + + std::array, 8> vertices; + }; + + template + box::box(geom::box const & b) + { + for (std::size_t z = 0; z < 2; ++z) + { + for (std::size_t y = 0; y < 2; ++y) + { + for (std::size_t x = 0; x < 2; ++x) + { + std::size_t i = z * 4 + y * 2 + x; + + vertices[i][0] = (x == 0) ? b[0].min : b[0].max; + vertices[i][1] = (y == 0) ? b[1].min : b[1].max; + vertices[i][2] = (z == 0) ? b[2].min : b[2].max; + } + } + } + } + + template + auto const & vertices(box const & b) + { + return b.vertices; + } + + template + auto const & edges(box const &) + { + static const std::array, 12> result = + {{ + { 0b000, 0b001 }, + { 0b010, 0b011 }, + { 0b100, 0b101 }, + { 0b110, 0b111 }, + + { 0b000, 0b010 }, + { 0b001, 0b011 }, + { 0b100, 0b110 }, + { 0b101, 0b111 }, + + { 0b000, 0b100 }, + { 0b001, 0b101 }, + { 0b010, 0b110 }, + { 0b011, 0b111 }, + }}; + + return result; + } + + template + auto const & faces(box const &) + { + static const std::array, 6> result = + {{ + {{ 0b000, 0b100, 0b110, 0b010 }}, + {{ 0b001, 0b011, 0b111, 0b101 }}, + {{ 0b000, 0b001, 0b101, 0b100 }}, + {{ 0b010, 0b110, 0b111, 0b011 }}, + {{ 0b000, 0b010, 0b011, 0b001 }}, + {{ 0b100, 0b101, 0b111, 0b110 }}, + }}; + + return result; + } + + template + auto const & face_normals(box const &) + { + static const std::array, 6> result = + {{ + {-1, 0, 0}, + { 1, 0, 0}, + { 0, -1, 0}, + { 0, 1, 0}, + { 0, 0, -1}, + { 0, 0, 1}, + }}; + + return result; + } + + template + auto const & edge_directions(box const &) + { + static const std::array, 3> result = + {{ + { 1, 0, 0}, + { 0, 1, 0}, + { 0, 0, 1}, + }}; + + return result; + } + +} diff --git a/libs/cg/include/psemek/cg/body/frustum.hpp b/libs/cg/include/psemek/cg/body/frustum.hpp new file mode 100644 index 00000000..e503c75a --- /dev/null +++ b/libs/cg/include/psemek/cg/body/frustum.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace psemek::cg +{ + + template + struct frustum; + + template + frustum(geom::matrix) -> frustum; + + template + struct frustum + { + frustum(geom::matrix const & m); + + std::array, 8> vertices; + }; + + template + frustum::frustum(geom::matrix const & m) + { + bool flip = (geom::det(m) < 0); + + for (std::size_t z = 0; z < 2; ++z) + { + for (std::size_t y = 0; y < 2; ++y) + { + for (std::size_t x = 0; x < 2; ++x) + { + std::size_t i = z * 4 + y * 2 + (flip ? 1 - x : x); + + geom::vector p; + p[0] = (x == 0) ? -1 : 1; + p[1] = (y == 0) ? -1 : 1; + p[2] = (z == 0) ? -1 : 1; + p[3] = 1; + + geom::gauss(m, p); + + vertices[i] = geom::as_point(p); + } + } + } + } + + template + auto const & vertices(frustum const & f) + { + return f.vertices; + } + + template + auto const & edges(frustum const &) + { + return edges(box{}); + } + + template + auto const & faces(frustum const &) + { + return faces(box{}); + } + +} diff --git a/libs/cg/include/psemek/cg/body/icosahedron.hpp b/libs/cg/include/psemek/cg/body/icosahedron.hpp new file mode 100644 index 00000000..00b7d2e1 --- /dev/null +++ b/libs/cg/include/psemek/cg/body/icosahedron.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include + +namespace psemek::cg +{ + + template + struct icosahedron + { + icosahedron() = default; + icosahedron(geom::point const & origin, T radius); + + std::array, 12> vertices; + }; + + template + icosahedron::icosahedron(geom::point const & origin, T radius) + { + static const T icosa_a = radius * 0.850650808; // radius * phi / sqrt(1 + phi^2) + static const T icosa_b = radius * 0.525731112; // radius / sqrt(1 + phi^2) + + vertices = + {{ + origin + geom::vector{-icosa_a, -icosa_b, 0}, // 0 + origin + geom::vector{-icosa_a, icosa_b, 0}, // 1 + origin + geom::vector{ icosa_a, -icosa_b, 0}, // 2 + origin + geom::vector{ icosa_a, icosa_b, 0}, // 3 + + origin + geom::vector{0, -icosa_a, -icosa_b}, // 4 + origin + geom::vector{0, -icosa_a, icosa_b}, // 5 + origin + geom::vector{0, icosa_a, -icosa_b}, // 6 + origin + geom::vector{0, icosa_a, icosa_b}, // 7 + + origin + geom::vector{-icosa_b, 0, -icosa_a}, // 8 + origin + geom::vector{ icosa_b, 0, -icosa_a}, // 9 + origin + geom::vector{-icosa_b, 0, icosa_a}, // 10 + origin + geom::vector{ icosa_b, 0, icosa_a}, // 11 + }}; + } + + template + auto const & vertices(icosahedron const & i) + { + return i.vertices; + } + + template + auto const & faces(icosahedron const &) + { + static const std::array, 20> result = + {{ + {0, 1, 8,}, + {0, 10, 1,}, + {2, 9, 3,}, + {2, 3, 11,}, + {4, 5, 0,}, + {4, 2, 5,}, + {6, 1, 7,}, + {6, 7, 3,}, + {8, 9, 4,}, + {8, 6, 9,}, + {10, 5, 11,}, + {10, 11, 7,}, + {0, 8, 4,}, + {0, 5, 10,}, + {1, 6, 8,}, + {1, 10, 7,}, + {2, 4, 9,}, + {2, 11, 5,}, + {3, 9, 6,}, + {3, 7, 11}, + }}; + + return result; + } + +} diff --git a/libs/cg/include/psemek/cg/body/prism.hpp b/libs/cg/include/psemek/cg/body/prism.hpp new file mode 100644 index 00000000..0c317aa0 --- /dev/null +++ b/libs/cg/include/psemek/cg/body/prism.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include + +namespace psemek::cg +{ + + template + struct triangular_prism + { + triangular_prism() = default; + triangular_prism(geom::triangle> const & t, geom::vector const & d); + + std::array, 6> vertices; + std::array, 4> edge_directions; + }; + + template + triangular_prism::triangular_prism(geom::triangle> const & t, geom::vector const & d) + { + for (std::size_t i = 0; i < 3; ++i) + { + vertices[i] = t[i]; + vertices[i + 3] = t[i] + d; + } + + if (geom::dot(geom::normal(vertices[0], vertices[1], vertices[2]), d) < 0) + { + std::swap(vertices[1], vertices[2]); + std::swap(vertices[4], vertices[5]); + } + + for (std::size_t i = 0; i < 3; ++i) + { + edge_directions[i] = geom::normalized(vertices[(i + 1) % 3] - vertices[i]); + } + edge_directions[3] = geom::normalized(d); + } + + template + auto const & vertices(triangular_prism const & p) + { + return p.vertices; + } + + template + auto const & edges(triangular_prism const &) + { + static const std::array, 9> result = + {{ + { 0, 1 }, + { 1, 2 }, + { 2, 0 }, + + { 3, 4 }, + { 4, 5 }, + { 5, 3 }, + + { 0, 3 }, + { 1, 4 }, + { 2, 5 }, + }}; + + return result; + } + + template + auto const & faces(triangular_prism const &) + { + static std::array, 5> result = + {{ + { 0, 2, 1 }, + { 3, 4, 5 }, + { 0, 1, 4, 3 }, + { 1, 2, 5, 4 }, + { 2, 0, 3, 5 }, + }}; + return result; + } + + template + auto const & edge_directions(triangular_prism const & p) + { + return p.edge_directions; + } + +} diff --git a/libs/cg/include/psemek/cg/convex/inside.hpp b/libs/cg/include/psemek/cg/convex/inside.hpp new file mode 100644 index 00000000..f593356c --- /dev/null +++ b/libs/cg/include/psemek/cg/convex/inside.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include + +namespace psemek::cg +{ + + template + bool inside(geom::point const & p, Body const & body) + { + auto const & vs = vertices(body); + auto const & fs = faces(body); + + for (auto const & f : fs) + { + if (geom::orientation(vs[f[0]], vs[f[1]], vs[f[2]], p) == geom::sign_t::negative) + return false; + } + return true; + } + +} diff --git a/libs/cg/include/psemek/cg/convex/separation.hpp b/libs/cg/include/psemek/cg/convex/separation.hpp new file mode 100644 index 00000000..20530013 --- /dev/null +++ b/libs/cg/include/psemek/cg/convex/separation.hpp @@ -0,0 +1,141 @@ +#pragma once + +#include + +#include +#include + +namespace psemek::cg +{ + + // returns pair(normalized vector from 1 to 2, signed distance) + // distance <= 0 means intersection + template + auto separation(Body1 const & b1, Body2 const & b2) + { + auto const & vs1 = vertices(b1); + auto const & vs2 = vertices(b2); + + auto const & fs1 = faces(b1); + auto const & fs2 = faces(b2); + + auto const & eds1 = edge_directions(b1); + auto const & eds2 = edge_directions(b2); + + using vector_type = std::remove_cvref_t; + using scalar_type = std::remove_cvref_t; + + vector_type res_n = vector_type::zero(); + auto res_d = -std::numeric_limits::infinity(); + + auto process_faces = [](auto const & vs1, auto const & fs1, auto const & vs2) + { + vector_type res_n = vector_type::zero(); + scalar_type res_d = -std::numeric_limits::infinity(); + + for (auto const & f : fs1) + { + auto const face_n = geom::normal(vs1[f[0]], vs1[f[1]], vs1[f[2]]); + scalar_type face_d = std::numeric_limits::infinity(); + + auto const face_p = vs1[f[0]]; + + for (auto const & v : vs2) + { + auto const d = geom::dot(face_n, v - face_p); + face_d = std::min(d, face_d); + } + + if (face_d > res_d) + { + res_d = face_d; + res_n = face_n; + } + } + + return std::make_pair(res_n, res_d); + }; + + auto process_edges = [](auto const & vs1, auto const & eds1, auto const & vs2, auto const & eds2) + { + vector_type res_n = vector_type::zero(); + scalar_type res_d = -std::numeric_limits::infinity(); + + for (auto const & ed1 : eds1) + { + for (auto const & ed2 : eds2) + { + auto edge_n = geom::cross(ed1, ed2); + auto l = geom::length(edge_n); + if (l == 0) continue; + edge_n /= l; + + geom::interval i1, i2; + + for (auto const & v : vs1) + { + i1 |= geom::dot(geom::homogeneous(v), geom::homogeneous(edge_n)); + } + + for (auto const & v : vs2) + { + i2 |= geom::dot(geom::homogeneous(v), geom::homogeneous(edge_n)); + } + + auto edge_d12 = i2.min - i1.max; + auto edge_d21 = i1.min - i2.max; + + scalar_type edge_d; + + if (edge_d12 > edge_d21) + { + edge_d = edge_d12; + } + else + { + edge_d = edge_d21; + edge_n = -edge_n; + } + + if (edge_d > res_d) + { + res_d = edge_d; + res_n = edge_n; + } + } + } + + return std::make_pair(res_n, res_d); + }; + + { + auto res12 = process_faces(vs1, fs1, vs2); + if (res12.second > res_d) + { + res_d = res12.second; + res_n = res12.first; + } + } + + { + auto res21 = process_faces(vs2, fs2, vs1); + if (res21.second > res_d) + { + res_d = res21.second; + res_n = -res21.first; + } + } + + { + auto rese = process_edges(vs1, eds1, vs2, eds2); + if (rese.second > res_d) + { + res_d = rese.second; + res_n = rese.first; + } + } + + return std::make_pair(res_n, res_d); + } + +}