Add convex body utilities to cg
This commit is contained in:
parent
26440b0faf
commit
1a6444d010
8 changed files with 747 additions and 615 deletions
|
|
@ -14,576 +14,18 @@
|
||||||
#include <psemek/geom/intersection.hpp>
|
#include <psemek/geom/intersection.hpp>
|
||||||
#include <psemek/geom/distance.hpp>
|
#include <psemek/geom/distance.hpp>
|
||||||
|
|
||||||
|
#include <psemek/cg/body/icosahedron.hpp>
|
||||||
|
#include <psemek/cg/body/box.hpp>
|
||||||
|
#include <psemek/cg/body/frustum.hpp>
|
||||||
|
#include <psemek/cg/body/prism.hpp>
|
||||||
|
#include <psemek/cg/convex/inside.hpp>
|
||||||
|
#include <psemek/cg/convex/separation.hpp>
|
||||||
|
|
||||||
#include <psemek/util/clock.hpp>
|
#include <psemek/util/clock.hpp>
|
||||||
#include <psemek/util/to_string.hpp>
|
#include <psemek/util/to_string.hpp>
|
||||||
#include <psemek/util/moving_average.hpp>
|
#include <psemek/util/moving_average.hpp>
|
||||||
#include <psemek/util/recursive.hpp>
|
#include <psemek/util/recursive.hpp>
|
||||||
|
|
||||||
namespace psemek::cg
|
|
||||||
{
|
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
struct box;
|
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
box(geom::box<T, N>) -> box<T, N>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct box<T, 3>
|
|
||||||
{
|
|
||||||
box() = default;
|
|
||||||
box(geom::box<T, 3> const & b);
|
|
||||||
|
|
||||||
std::array<geom::point<T, 3>, 8> vertices;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
box<T, 3>::box(geom::box<T, 3> 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 <typename T>
|
|
||||||
auto const & vertices(box<T, 3> const & b)
|
|
||||||
{
|
|
||||||
return b.vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
|
|
||||||
inline auto const & cubiod_edges()
|
|
||||||
{
|
|
||||||
static const std::array<geom::segment<std::uint8_t>, 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<std::array<std::uint8_t, 4>, 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 <typename T>
|
|
||||||
auto const & edges(box<T, 3> const &)
|
|
||||||
{
|
|
||||||
return detail::cubiod_edges();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto const & faces(box<T, 3> const &)
|
|
||||||
{
|
|
||||||
return detail::cubiod_faces();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto const & face_normals(box<T, 3> const &)
|
|
||||||
{
|
|
||||||
static const std::array<geom::vector<T, 3>, 6> result =
|
|
||||||
{{
|
|
||||||
{-1, 0, 0},
|
|
||||||
{ 1, 0, 0},
|
|
||||||
{ 0, -1, 0},
|
|
||||||
{ 0, 1, 0},
|
|
||||||
{ 0, 0, -1},
|
|
||||||
{ 0, 0, 1},
|
|
||||||
}};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto const & edge_directions(box<T, 3> const &)
|
|
||||||
{
|
|
||||||
static const std::array<geom::vector<T, 3>, 3> result =
|
|
||||||
{{
|
|
||||||
{ 1, 0, 0},
|
|
||||||
{ 0, 1, 0},
|
|
||||||
{ 0, 0, 1},
|
|
||||||
}};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
struct frustum;
|
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
frustum(geom::matrix<T, N, N>) -> frustum<T, N-1>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct frustum<T, 3>
|
|
||||||
{
|
|
||||||
frustum(geom::matrix<T, 4, 4> const & m);
|
|
||||||
|
|
||||||
std::array<geom::point<T, 3>, 8> vertices;
|
|
||||||
std::array<geom::vector<T, 3>, 6> face_normals;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
frustum<T, 3>::frustum(geom::matrix<T, 4, 4> 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<T, 4> 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 <typename T>
|
|
||||||
auto const & vertices(frustum<T, 3> const & f)
|
|
||||||
{
|
|
||||||
return f.vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto const & edges(frustum<T, 3> const &)
|
|
||||||
{
|
|
||||||
return detail::cubiod_edges();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto const & faces(frustum<T, 3> const &)
|
|
||||||
{
|
|
||||||
return detail::cubiod_faces();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct triangular_prism
|
|
||||||
{
|
|
||||||
triangular_prism() = default;
|
|
||||||
triangular_prism(geom::triangle<geom::point<T, 3>> const & t, geom::vector<T, 3> const & d);
|
|
||||||
|
|
||||||
std::array<geom::point<T, 3>, 6> vertices;
|
|
||||||
std::array<geom::vector<T, 3>, 4> edge_directions;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
triangular_prism<T>::triangular_prism(geom::triangle<geom::point<T, 3>> const & t, geom::vector<T, 3> 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 <typename T>
|
|
||||||
auto const & vertices(triangular_prism<T> const & p)
|
|
||||||
{
|
|
||||||
return p.vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto const & edges(triangular_prism<T> const &)
|
|
||||||
{
|
|
||||||
static const std::array<geom::segment<std::uint8_t>, 9> result =
|
|
||||||
{{
|
|
||||||
{ 0, 1 },
|
|
||||||
{ 1, 2 },
|
|
||||||
{ 2, 0 },
|
|
||||||
|
|
||||||
{ 3, 4 },
|
|
||||||
{ 4, 5 },
|
|
||||||
{ 5, 3 },
|
|
||||||
|
|
||||||
{ 0, 3 },
|
|
||||||
{ 1, 4 },
|
|
||||||
{ 2, 5 },
|
|
||||||
}};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto const & faces(triangular_prism<T> const &)
|
|
||||||
{
|
|
||||||
static std::array<std::vector<std::uint8_t>, 5> result =
|
|
||||||
{{
|
|
||||||
{ 0, 2, 1 },
|
|
||||||
{ 3, 4, 5 },
|
|
||||||
{ 0, 1, 4, 3 },
|
|
||||||
{ 1, 2, 5, 4 },
|
|
||||||
{ 2, 0, 3, 5 },
|
|
||||||
}};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto const & edge_directions(triangular_prism<T> const & p)
|
|
||||||
{
|
|
||||||
return p.edge_directions;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
|
|
||||||
template <typename Container>
|
|
||||||
struct has_static_size
|
|
||||||
: std::false_type
|
|
||||||
{};
|
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
struct has_static_size<std::array<T, N>>
|
|
||||||
: std::true_type
|
|
||||||
{};
|
|
||||||
|
|
||||||
template <typename Container>
|
|
||||||
constexpr bool has_static_size_v = has_static_size<Container>::value;
|
|
||||||
|
|
||||||
template <typename Container>
|
|
||||||
struct static_size;
|
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
struct static_size<std::array<T, N>>
|
|
||||||
{
|
|
||||||
static constexpr std::size_t value = N;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Container>
|
|
||||||
constexpr std::size_t static_size_v = static_size<Container>::value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Body>
|
|
||||||
auto triangles(Body const & b)
|
|
||||||
{
|
|
||||||
auto const & fs = faces(b);
|
|
||||||
|
|
||||||
using faces_type = std::remove_cvref_t<decltype(fs)>;
|
|
||||||
using face_type = std::remove_cvref_t<decltype(*std::begin(fs))>;
|
|
||||||
using index_type = std::remove_cvref_t<decltype((*std::begin(fs))[0])>;
|
|
||||||
|
|
||||||
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<faces_type> && detail::has_static_size_v<face_type>)
|
|
||||||
{
|
|
||||||
constexpr std::size_t faces_count = detail::static_size_v<faces_type>;
|
|
||||||
constexpr std::size_t face_size = detail::static_size_v<face_type>;
|
|
||||||
std::array<geom::triangle<index_type>, faces_count * (face_size - 2)> result;
|
|
||||||
impl(result.begin());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::vector<geom::triangle<index_type>> result;
|
|
||||||
if constexpr (detail::has_static_size_v<face_type>)
|
|
||||||
{
|
|
||||||
constexpr std::size_t face_size = detail::static_size_v<face_type>;
|
|
||||||
result.reserve(fs.size() * (face_size - 2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.reserve(fs.size());
|
|
||||||
}
|
|
||||||
impl(std::back_inserter(result));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Body>
|
|
||||||
auto edge_directions(Body const & b)
|
|
||||||
{
|
|
||||||
auto const & vs = vertices(b);
|
|
||||||
auto const & es = edges(b);
|
|
||||||
|
|
||||||
using edges_type = std::remove_cvref_t<decltype(es)>;
|
|
||||||
using vector_type = std::remove_cvref_t<decltype(vs[0] - vs[0])>;
|
|
||||||
|
|
||||||
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<edges_type>)
|
|
||||||
{
|
|
||||||
constexpr std::size_t edges_count = detail::static_size_v<edges_type>;
|
|
||||||
std::array<vector_type, edges_count> result;
|
|
||||||
impl(result.begin());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::vector<vector_type> result;
|
|
||||||
result.reserve(es.size());
|
|
||||||
impl(std::back_inserter(result));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Body>
|
|
||||||
auto faces(Body const & b)
|
|
||||||
{
|
|
||||||
return triangles(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Body>
|
|
||||||
auto face_normals(Body const & b)
|
|
||||||
{
|
|
||||||
auto const & vs = vertices(b);
|
|
||||||
auto const & fs = faces(b);
|
|
||||||
|
|
||||||
using faces_type = std::remove_cvref_t<decltype(fs)>;
|
|
||||||
using vector_type = std::remove_cvref_t<decltype(vs[0] - vs[0])>;
|
|
||||||
|
|
||||||
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<faces_type>)
|
|
||||||
{
|
|
||||||
constexpr std::size_t faces_count = detail::static_size_v<faces_type>;
|
|
||||||
std::array<vector_type, faces_count> result;
|
|
||||||
impl(result.begin());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::vector<vector_type> result;
|
|
||||||
result.reserve(fs.size());
|
|
||||||
impl(std::back_inserter(result));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename P, typename Body>
|
|
||||||
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 <typename Body1, typename Body2>
|
|
||||||
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<decltype(vs1[0] - vs1[0])>;
|
|
||||||
using scalar_type = std::remove_cvref_t<decltype(vs1[0][0])>;
|
|
||||||
|
|
||||||
vector_type res_n = vector_type::zero();
|
|
||||||
auto res_d = -std::numeric_limits<scalar_type>::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<scalar_type>::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<scalar_type>::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<scalar_type>::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<scalar_type>::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<scalar_type> 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;
|
using namespace psemek;
|
||||||
|
|
||||||
template <typename Value, typename T>
|
template <typename Value, typename T>
|
||||||
|
|
@ -617,53 +59,6 @@ private:
|
||||||
T speed_;
|
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<float, 3> 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[] =
|
static char const tile_vs[] =
|
||||||
R"(#version 330
|
R"(#version 330
|
||||||
|
|
||||||
|
|
@ -935,6 +330,12 @@ std::ostream & operator << (std::ostream & os, std::vector<T> const & v)
|
||||||
|
|
||||||
void srtm_app::present()
|
void srtm_app::present()
|
||||||
{
|
{
|
||||||
|
cg::icosahedron<float> icosahedron{geom::point<float, 3>::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<std::string> info;
|
std::vector<std::string> info;
|
||||||
|
|
||||||
gl::ClearColor(0.9f, 0.9f, 0.9f, 0.f);
|
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)
|
for (int f = 0; f < 20; ++f)
|
||||||
{
|
{
|
||||||
geom::vector<float, 3> v[3];
|
geom::vector<float, 3> v[3];
|
||||||
v[0] = icosa_vertices[icosa_faces[f][0]];
|
v[0] = icosa_vertices[icosa_faces[f][0]] - geom::point<float, 3>::zero();
|
||||||
v[1] = icosa_vertices[icosa_faces[f][1]];
|
v[1] = icosa_vertices[icosa_faces[f][1]] - geom::point<float, 3>::zero();
|
||||||
v[2] = icosa_vertices[icosa_faces[f][2]];
|
v[2] = icosa_vertices[icosa_faces[f][2]] - geom::point<float, 3>::zero();
|
||||||
id.push_back(f);
|
id.push_back(f);
|
||||||
visit(v);
|
visit(v);
|
||||||
id.pop_back();
|
id.pop_back();
|
||||||
|
|
|
||||||
210
libs/cg/include/psemek/cg/body/body.hpp
Normal file
210
libs/cg/include/psemek/cg/body/body.hpp
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/geom/simplex.hpp>
|
||||||
|
#include <psemek/geom/vector.hpp>
|
||||||
|
#include <psemek/geom/point.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <tuple>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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 <typename Container>
|
||||||
|
struct has_static_size
|
||||||
|
: std::false_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct has_static_size<std::array<T, N>>
|
||||||
|
: std::true_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
constexpr bool has_static_size_v = has_static_size<Container>::value;
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
struct static_size;
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct static_size<std::array<T, N>>
|
||||||
|
{
|
||||||
|
static constexpr std::size_t value = N;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
constexpr std::size_t static_size_v = static_size<Container>::value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Body>
|
||||||
|
auto faces(Body const & b)
|
||||||
|
{
|
||||||
|
return triangles(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Body>
|
||||||
|
auto edges(Body const & b)
|
||||||
|
{
|
||||||
|
auto const & fs = faces(b);
|
||||||
|
|
||||||
|
using index_type = std::remove_cvref_t<decltype((*std::begin(fs))[0])>;
|
||||||
|
|
||||||
|
std::vector<geom::segment<index_type>> 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<index_type> 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 <typename Body>
|
||||||
|
auto triangles(Body const & b)
|
||||||
|
{
|
||||||
|
auto const & fs = faces(b);
|
||||||
|
|
||||||
|
using faces_type = std::remove_cvref_t<decltype(fs)>;
|
||||||
|
using face_type = std::remove_cvref_t<decltype(*std::begin(fs))>;
|
||||||
|
using index_type = std::remove_cvref_t<decltype((*std::begin(fs))[0])>;
|
||||||
|
|
||||||
|
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<faces_type> && detail::has_static_size_v<face_type>)
|
||||||
|
{
|
||||||
|
constexpr std::size_t faces_count = detail::static_size_v<faces_type>;
|
||||||
|
constexpr std::size_t face_size = detail::static_size_v<face_type>;
|
||||||
|
std::array<geom::triangle<index_type>, faces_count * (face_size - 2)> result;
|
||||||
|
impl(result.begin());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<geom::triangle<index_type>> result;
|
||||||
|
if constexpr (detail::has_static_size_v<face_type>)
|
||||||
|
{
|
||||||
|
constexpr std::size_t face_size = detail::static_size_v<face_type>;
|
||||||
|
result.reserve(fs.size() * (face_size - 2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.reserve(fs.size());
|
||||||
|
}
|
||||||
|
impl(std::back_inserter(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Body>
|
||||||
|
auto edge_directions(Body const & b)
|
||||||
|
{
|
||||||
|
auto const & vs = vertices(b);
|
||||||
|
auto const & es = edges(b);
|
||||||
|
|
||||||
|
using edges_type = std::remove_cvref_t<decltype(es)>;
|
||||||
|
using vector_type = std::remove_cvref_t<decltype(vs[0] - vs[0])>;
|
||||||
|
|
||||||
|
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<edges_type>)
|
||||||
|
{
|
||||||
|
constexpr std::size_t edges_count = detail::static_size_v<edges_type>;
|
||||||
|
std::array<vector_type, edges_count> result;
|
||||||
|
impl(result.begin());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<vector_type> result;
|
||||||
|
result.reserve(es.size());
|
||||||
|
impl(std::back_inserter(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Body>
|
||||||
|
auto face_normals(Body const & b)
|
||||||
|
{
|
||||||
|
auto const & vs = vertices(b);
|
||||||
|
auto const & fs = faces(b);
|
||||||
|
|
||||||
|
using faces_type = std::remove_cvref_t<decltype(fs)>;
|
||||||
|
using vector_type = std::remove_cvref_t<decltype(vs[0] - vs[0])>;
|
||||||
|
|
||||||
|
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<faces_type>)
|
||||||
|
{
|
||||||
|
constexpr std::size_t faces_count = detail::static_size_v<faces_type>;
|
||||||
|
std::array<vector_type, faces_count> result;
|
||||||
|
impl(result.begin());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<vector_type> result;
|
||||||
|
result.reserve(fs.size());
|
||||||
|
impl(std::back_inserter(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
119
libs/cg/include/psemek/cg/body/box.hpp
Normal file
119
libs/cg/include/psemek/cg/body/box.hpp
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/cg/body/body.hpp>
|
||||||
|
|
||||||
|
#include <psemek/geom/box.hpp>
|
||||||
|
|
||||||
|
namespace psemek::cg
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct box;
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
box(geom::box<T, N>) -> box<T, N>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct box<T, 3>
|
||||||
|
{
|
||||||
|
box() = default;
|
||||||
|
box(geom::box<T, 3> const & b);
|
||||||
|
|
||||||
|
std::array<geom::point<T, 3>, 8> vertices;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
box<T, 3>::box(geom::box<T, 3> 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 <typename T>
|
||||||
|
auto const & vertices(box<T, 3> const & b)
|
||||||
|
{
|
||||||
|
return b.vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto const & edges(box<T, 3> const &)
|
||||||
|
{
|
||||||
|
static const std::array<geom::segment<std::uint8_t>, 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 <typename T>
|
||||||
|
auto const & faces(box<T, 3> const &)
|
||||||
|
{
|
||||||
|
static const std::array<std::array<std::uint8_t, 4>, 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 <typename T>
|
||||||
|
auto const & face_normals(box<T, 3> const &)
|
||||||
|
{
|
||||||
|
static const std::array<geom::vector<T, 3>, 6> result =
|
||||||
|
{{
|
||||||
|
{-1, 0, 0},
|
||||||
|
{ 1, 0, 0},
|
||||||
|
{ 0, -1, 0},
|
||||||
|
{ 0, 1, 0},
|
||||||
|
{ 0, 0, -1},
|
||||||
|
{ 0, 0, 1},
|
||||||
|
}};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto const & edge_directions(box<T, 3> const &)
|
||||||
|
{
|
||||||
|
static const std::array<geom::vector<T, 3>, 3> result =
|
||||||
|
{{
|
||||||
|
{ 1, 0, 0},
|
||||||
|
{ 0, 1, 0},
|
||||||
|
{ 0, 0, 1},
|
||||||
|
}};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
72
libs/cg/include/psemek/cg/body/frustum.hpp
Normal file
72
libs/cg/include/psemek/cg/body/frustum.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/cg/body/body.hpp>
|
||||||
|
#include <psemek/cg/body/box.hpp>
|
||||||
|
|
||||||
|
#include <psemek/geom/matrix.hpp>
|
||||||
|
#include <psemek/geom/gauss.hpp>
|
||||||
|
#include <psemek/geom/homogeneous.hpp>
|
||||||
|
|
||||||
|
namespace psemek::cg
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct frustum;
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
frustum(geom::matrix<T, N, N>) -> frustum<T, N-1>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct frustum<T, 3>
|
||||||
|
{
|
||||||
|
frustum(geom::matrix<T, 4, 4> const & m);
|
||||||
|
|
||||||
|
std::array<geom::point<T, 3>, 8> vertices;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
frustum<T, 3>::frustum(geom::matrix<T, 4, 4> 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<T, 4> 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 <typename T>
|
||||||
|
auto const & vertices(frustum<T, 3> const & f)
|
||||||
|
{
|
||||||
|
return f.vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto const & edges(frustum<T, 3> const &)
|
||||||
|
{
|
||||||
|
return edges(box<T, 3>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto const & faces(frustum<T, 3> const &)
|
||||||
|
{
|
||||||
|
return faces(box<T, 3>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
78
libs/cg/include/psemek/cg/body/icosahedron.hpp
Normal file
78
libs/cg/include/psemek/cg/body/icosahedron.hpp
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/cg/body/body.hpp>
|
||||||
|
|
||||||
|
namespace psemek::cg
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct icosahedron
|
||||||
|
{
|
||||||
|
icosahedron() = default;
|
||||||
|
icosahedron(geom::point<T, 3> const & origin, T radius);
|
||||||
|
|
||||||
|
std::array<geom::point<T, 3>, 12> vertices;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
icosahedron<T>::icosahedron(geom::point<T, 3> 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<T, 3>{-icosa_a, -icosa_b, 0}, // 0
|
||||||
|
origin + geom::vector<T, 3>{-icosa_a, icosa_b, 0}, // 1
|
||||||
|
origin + geom::vector<T, 3>{ icosa_a, -icosa_b, 0}, // 2
|
||||||
|
origin + geom::vector<T, 3>{ icosa_a, icosa_b, 0}, // 3
|
||||||
|
|
||||||
|
origin + geom::vector<T, 3>{0, -icosa_a, -icosa_b}, // 4
|
||||||
|
origin + geom::vector<T, 3>{0, -icosa_a, icosa_b}, // 5
|
||||||
|
origin + geom::vector<T, 3>{0, icosa_a, -icosa_b}, // 6
|
||||||
|
origin + geom::vector<T, 3>{0, icosa_a, icosa_b}, // 7
|
||||||
|
|
||||||
|
origin + geom::vector<T, 3>{-icosa_b, 0, -icosa_a}, // 8
|
||||||
|
origin + geom::vector<T, 3>{ icosa_b, 0, -icosa_a}, // 9
|
||||||
|
origin + geom::vector<T, 3>{-icosa_b, 0, icosa_a}, // 10
|
||||||
|
origin + geom::vector<T, 3>{ icosa_b, 0, icosa_a}, // 11
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto const & vertices(icosahedron<T> const & i)
|
||||||
|
{
|
||||||
|
return i.vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto const & faces(icosahedron<T> const &)
|
||||||
|
{
|
||||||
|
static const std::array<geom::triangle<std::uint8_t>, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
87
libs/cg/include/psemek/cg/body/prism.hpp
Normal file
87
libs/cg/include/psemek/cg/body/prism.hpp
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/cg/body/body.hpp>
|
||||||
|
|
||||||
|
namespace psemek::cg
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct triangular_prism
|
||||||
|
{
|
||||||
|
triangular_prism() = default;
|
||||||
|
triangular_prism(geom::triangle<geom::point<T, 3>> const & t, geom::vector<T, 3> const & d);
|
||||||
|
|
||||||
|
std::array<geom::point<T, 3>, 6> vertices;
|
||||||
|
std::array<geom::vector<T, 3>, 4> edge_directions;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
triangular_prism<T>::triangular_prism(geom::triangle<geom::point<T, 3>> const & t, geom::vector<T, 3> 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 <typename T>
|
||||||
|
auto const & vertices(triangular_prism<T> const & p)
|
||||||
|
{
|
||||||
|
return p.vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto const & edges(triangular_prism<T> const &)
|
||||||
|
{
|
||||||
|
static const std::array<geom::segment<std::uint8_t>, 9> result =
|
||||||
|
{{
|
||||||
|
{ 0, 1 },
|
||||||
|
{ 1, 2 },
|
||||||
|
{ 2, 0 },
|
||||||
|
|
||||||
|
{ 3, 4 },
|
||||||
|
{ 4, 5 },
|
||||||
|
{ 5, 3 },
|
||||||
|
|
||||||
|
{ 0, 3 },
|
||||||
|
{ 1, 4 },
|
||||||
|
{ 2, 5 },
|
||||||
|
}};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto const & faces(triangular_prism<T> const &)
|
||||||
|
{
|
||||||
|
static std::array<std::vector<std::uint8_t>, 5> result =
|
||||||
|
{{
|
||||||
|
{ 0, 2, 1 },
|
||||||
|
{ 3, 4, 5 },
|
||||||
|
{ 0, 1, 4, 3 },
|
||||||
|
{ 1, 2, 5, 4 },
|
||||||
|
{ 2, 0, 3, 5 },
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto const & edge_directions(triangular_prism<T> const & p)
|
||||||
|
{
|
||||||
|
return p.edge_directions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
24
libs/cg/include/psemek/cg/convex/inside.hpp
Normal file
24
libs/cg/include/psemek/cg/convex/inside.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/cg/body/body.hpp>
|
||||||
|
|
||||||
|
#include <psemek/geom/orientation.hpp>
|
||||||
|
|
||||||
|
namespace psemek::cg
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T, std::size_t N, typename Body>
|
||||||
|
bool inside(geom::point<T, N> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
141
libs/cg/include/psemek/cg/convex/separation.hpp
Normal file
141
libs/cg/include/psemek/cg/convex/separation.hpp
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/cg/body/body.hpp>
|
||||||
|
|
||||||
|
#include <psemek/geom/interval.hpp>
|
||||||
|
#include <psemek/geom/homogeneous.hpp>
|
||||||
|
|
||||||
|
namespace psemek::cg
|
||||||
|
{
|
||||||
|
|
||||||
|
// returns pair(normalized vector from 1 to 2, signed distance)
|
||||||
|
// distance <= 0 means intersection
|
||||||
|
template <typename Body1, typename Body2>
|
||||||
|
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<decltype(vs1[0] - vs1[0])>;
|
||||||
|
using scalar_type = std::remove_cvref_t<decltype(vs1[0][0])>;
|
||||||
|
|
||||||
|
vector_type res_n = vector_type::zero();
|
||||||
|
auto res_d = -std::numeric_limits<scalar_type>::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<scalar_type>::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<scalar_type>::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<scalar_type>::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<scalar_type> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue