Add util, geom, cg, pcg & gfx libs
This commit is contained in:
parent
51ae10fdc4
commit
549f2ada41
100 changed files with 11151 additions and 0 deletions
13
CMakeLists.txt
Normal file
13
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
project(psemek)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} PSEMEK_BUILD_TYPE)
|
||||
|
||||
if(PSEMEK_BUILD_TYPE STREQUAL "DEBUG")
|
||||
add_definitions("-DPSEMEK_DEBUG=1")
|
||||
endif()
|
||||
|
||||
add_subdirectory(libs)
|
||||
19
cmake/modules/FindGMP.cmake
Normal file
19
cmake/modules/FindGMP.cmake
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
if(GMP_FOUND)
|
||||
set(GMP_FIND_QUIETLY TRUE)
|
||||
endif()
|
||||
|
||||
find_path(GMP_INCLUDE_DIRS NAMES "gmp.h" PATHS "${GMP_ROOT}/include")
|
||||
find_library(GMP_LIBRARIES NAMES "gmp" PATHS "${GMP_ROOT}/lib")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_INCLUDE_DIRS GMP_LIBRARIES)
|
||||
|
||||
if(GMP_FOUND AND NOT TARGET GMP)
|
||||
add_library(GMP SHARED IMPORTED)
|
||||
set_target_properties(GMP PROPERTIES
|
||||
IMPORTED_LOCATION "${GMP_LIBRARIES}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE_DIRS}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(GMP_INCLUDE_DIRS GMP_LIBRARIES)
|
||||
19
cmake/modules/FindSDL2.cmake
Normal file
19
cmake/modules/FindSDL2.cmake
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
if(SDL2_FOUND)
|
||||
set(SDL2_FIND_QUIETLY TRUE)
|
||||
endif()
|
||||
|
||||
find_path(SDL2_INCLUDE_DIRS NAMES "SDL2/SDL.h" PATHS "${SDL2_ROOT}/include")
|
||||
find_library(SDL2_LIBRARIES NAMES "SDL2" PATHS "${SDL2_ROOT}/lib")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SDL2 DEFAULT_MSG SDL2_INCLUDE_DIRS SDL2_LIBRARIES)
|
||||
|
||||
if(SDL2_FOUND AND NOT TARGET sdl2)
|
||||
add_library(sdl2 SHARED IMPORTED)
|
||||
set_target_properties(sdl2 PROPERTIES
|
||||
IMPORTED_LOCATION "${SDL2_LIBRARIES}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIRS}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(SDL2_INCLUDE_DIRS SDL2_LIBRARIES)
|
||||
5
libs/CMakeLists.txt
Normal file
5
libs/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
add_subdirectory(util)
|
||||
add_subdirectory(geom)
|
||||
add_subdirectory(cg)
|
||||
add_subdirectory(pcg)
|
||||
add_subdirectory(gfx)
|
||||
6
libs/cg/CMakeLists.txt
Normal file
6
libs/cg/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
file(GLOB_RECURSE PSEMEK_CG_HEADERS "include/*.hpp")
|
||||
|
||||
add_library(cg STATIC ${PSEMEK_CG_HEADERS})
|
||||
target_include_directories(cg PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(cg PUBLIC geom)
|
||||
set_target_properties(cg PROPERTIES LINKER_LANGUAGE CXX)
|
||||
19
libs/cg/include/psemek/cg/bbox.hpp
Normal file
19
libs/cg/include/psemek/cg/bbox.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/box.hpp>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
template <typename Iterator>
|
||||
auto bbox (Iterator begin, Iterator end)
|
||||
{
|
||||
using point_type = std::decay_t<decltype(*begin)>;
|
||||
|
||||
box<typename point_type::scalar_type, point_type::dimension> result;
|
||||
for (; begin != end; ++begin)
|
||||
result |= *begin;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
174
libs/cg/include/psemek/cg/clip.hpp
Normal file
174
libs/cg/include/psemek/cg/clip.hpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/cg/dcel.hpp>
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
// TODO: this was written for Voronoi and is known to fail in some cases
|
||||
template <typename T, typename Edge, typename Face, typename Index>
|
||||
void clip (dcel<point<T, 2>, Edge, Face, Index> & dcel, cg::vector<T, 3> const & eq)
|
||||
{
|
||||
auto outer_face = dcel.face(0);
|
||||
|
||||
auto inside = [eq](auto const & p){
|
||||
return p[0] * eq[0] + p[1] * eq[1] + eq[2] >= 0.f;
|
||||
};
|
||||
|
||||
auto cross = [&inside](auto edge){
|
||||
return !inside(edge.origin().data()) && inside(edge.next().origin().data());
|
||||
};
|
||||
|
||||
auto split_edge = [&dcel, eq](auto edge)
|
||||
{
|
||||
auto q0 = edge.origin().data();
|
||||
auto q1 = edge.next().origin().data();
|
||||
|
||||
float t = - (eq[0] * q0[0] + eq[1] * q0[1] + eq[2]) / (eq[0] * (q1[0] - q0[0]) + eq[1] * (q1[1] - q0[1]));
|
||||
auto q = q0 + t * (q1 - q0);
|
||||
|
||||
auto e = dcel.push_edge();
|
||||
auto twin = dcel.push_edge();
|
||||
auto p = dcel.push_point(q);
|
||||
|
||||
auto ctwin = edge.twin();
|
||||
|
||||
p.edge(e);
|
||||
|
||||
e.origin(p);
|
||||
e.next(edge.next());
|
||||
e.twin(ctwin);
|
||||
e.face(edge.face());
|
||||
|
||||
twin.origin(p);
|
||||
twin.next(ctwin.next());
|
||||
twin.twin(edge);
|
||||
twin.face(ctwin.face());
|
||||
|
||||
edge.next(e);
|
||||
edge.twin(twin);
|
||||
|
||||
ctwin.next(twin);
|
||||
ctwin.twin(e);
|
||||
};
|
||||
|
||||
auto prev_edge = [](auto edge)
|
||||
{
|
||||
auto e = edge;
|
||||
while (true)
|
||||
{
|
||||
e = e.twin();
|
||||
if (e.next() == edge) return e;
|
||||
e = e.next();
|
||||
}
|
||||
};
|
||||
|
||||
auto merge_edge = [&](auto edge)
|
||||
{
|
||||
auto dcel_copy = dcel;
|
||||
|
||||
auto next = edge.next();
|
||||
auto twin = edge.twin();
|
||||
auto ntwin = next.twin();
|
||||
|
||||
assert(ntwin.next() == twin);
|
||||
assert(next.origin() == twin.origin());
|
||||
|
||||
edge.next(next.next());
|
||||
edge.twin(ntwin);
|
||||
|
||||
ntwin.next(twin.next());
|
||||
ntwin.twin(edge);
|
||||
|
||||
edge.face().edge(edge);
|
||||
ntwin.face().edge(ntwin);
|
||||
|
||||
auto face = edge.face();
|
||||
auto p = next.origin();
|
||||
if (next > twin)
|
||||
{
|
||||
dcel.remove_edge(next);
|
||||
dcel.remove_edge(twin);
|
||||
}
|
||||
else
|
||||
{
|
||||
dcel.remove_edge(twin);
|
||||
dcel.remove_edge(next);
|
||||
}
|
||||
dcel.remove_point(p);
|
||||
|
||||
return face.edge();
|
||||
};
|
||||
|
||||
auto current_edge = outer_face.edge();
|
||||
|
||||
bool no_intersection = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (cross(current_edge)) {
|
||||
no_intersection = false;
|
||||
break;
|
||||
}
|
||||
|
||||
current_edge = current_edge.next();
|
||||
if (current_edge == outer_face.edge())
|
||||
break;
|
||||
}
|
||||
|
||||
if (no_intersection)
|
||||
return;
|
||||
|
||||
split_edge(current_edge);
|
||||
|
||||
while (true)
|
||||
{
|
||||
bool end = false;
|
||||
while (true)
|
||||
{
|
||||
auto prev = current_edge.twin().next().twin();
|
||||
|
||||
if (prev.face() != outer_face) break;
|
||||
|
||||
if (cross(prev.twin()))
|
||||
{
|
||||
end = true;
|
||||
split_edge(prev.twin());
|
||||
current_edge = merge_edge(prev.next());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
current_edge = merge_edge(prev);
|
||||
}
|
||||
|
||||
if (end) break;
|
||||
|
||||
for (bool finished = false; !finished;)
|
||||
{
|
||||
auto n = current_edge.twin().next();
|
||||
|
||||
if (cross(n))
|
||||
{
|
||||
finished = true;
|
||||
split_edge(n);
|
||||
}
|
||||
|
||||
auto prev = prev_edge(current_edge);
|
||||
|
||||
prev.next(n);
|
||||
n.face().edge(n.next());
|
||||
n.face(outer_face);
|
||||
current_edge.origin(n.next().origin());
|
||||
current_edge.twin().next(n.next());
|
||||
n.next(current_edge);
|
||||
n.origin().edge(n);
|
||||
}
|
||||
|
||||
current_edge = prev_edge(current_edge);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
53
libs/cg/include/psemek/cg/convex_hull_2d/andrew.hpp
Normal file
53
libs/cg/include/psemek/cg/convex_hull_2d/andrew.hpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/orientation.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
template <typename InIterator, typename OutIterator>
|
||||
OutIterator andrew_convex_hull (InIterator begin, InIterator end, OutIterator out)
|
||||
{
|
||||
// need to store iterators to sort them
|
||||
std::vector<InIterator> its(end - begin);
|
||||
std::iota(its.begin(), its.end(), begin);
|
||||
|
||||
// sort lexicographically
|
||||
std::sort(its.begin(), its.end(), [](auto i1, auto i2){ return *i1 < *i2; });
|
||||
|
||||
std::vector<InIterator> hull;
|
||||
hull.push_back(its.front());
|
||||
|
||||
// find lower half of the hull
|
||||
for (auto it = its.begin() + 1; it != its.end(); ++it)
|
||||
{
|
||||
while (hull.size() >= 2 && orientation(**(hull.end() - 2), **(hull.end() - 1), **it) != sign_t::positive)
|
||||
hull.pop_back();
|
||||
|
||||
hull.push_back(*it);
|
||||
}
|
||||
|
||||
// find upper half of the hull
|
||||
for (auto it = its.rbegin() + 1; it != its.rend(); ++it)
|
||||
{
|
||||
if (*it == *(hull.end() - 2))
|
||||
continue;
|
||||
|
||||
while (hull.size() >= 2 && orientation(**(hull.end() - 2), **(hull.end() - 1), **it) != sign_t::positive)
|
||||
hull.pop_back();
|
||||
|
||||
hull.push_back(*it);
|
||||
}
|
||||
|
||||
// the last upper part point is the first lower part point - remove it
|
||||
hull.pop_back();
|
||||
|
||||
// copy result to output
|
||||
return std::copy(hull.begin(), hull.end(), out);
|
||||
}
|
||||
|
||||
}
|
||||
54
libs/cg/include/psemek/cg/convex_hull_2d/graham.hpp
Normal file
54
libs/cg/include/psemek/cg/convex_hull_2d/graham.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/orientation.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
template <typename InIterator, typename OutIterator>
|
||||
OutIterator graham_convex_hull (InIterator begin, InIterator end, OutIterator out)
|
||||
{
|
||||
// need to store iterators to sort them
|
||||
std::vector<InIterator> its(end - begin);
|
||||
std::iota(its.begin(), its.end(), begin);
|
||||
|
||||
// find leftmost point & move its iterator to beginning
|
||||
{
|
||||
auto leftmost = std::min_element(its.begin(), its.end(), [](auto i1, auto i2){ return *i1 < *i2; });
|
||||
std::iter_swap(leftmost, its.begin());
|
||||
}
|
||||
|
||||
// sort with respect to angle to leftmost point
|
||||
std::sort(its.begin() + 1, its.end(), [&](auto i1, auto i2){
|
||||
auto o = orientation(*its.front(), *i1, *i2);
|
||||
|
||||
// carefully deal with parallel points
|
||||
if (o == sign_t::positive)
|
||||
return true;
|
||||
else if (o == sign_t::negative)
|
||||
return false;
|
||||
|
||||
return *i1 < *i2;
|
||||
});
|
||||
|
||||
// perform gift wrapping
|
||||
|
||||
auto hull_end = its.begin() + 1;
|
||||
|
||||
for (auto it = its.begin() + 1; it != its.end(); ++it)
|
||||
{
|
||||
while (hull_end - its.begin() >= 2 && orientation(**(hull_end - 2), **(hull_end - 1), **it) != sign_t::positive)
|
||||
--hull_end;
|
||||
|
||||
*hull_end++ = *it;
|
||||
}
|
||||
|
||||
// copy result to output
|
||||
return std::copy(its.begin(), hull_end, out);
|
||||
}
|
||||
|
||||
}
|
||||
51
libs/cg/include/psemek/cg/convex_hull_2d/jarvis.hpp
Normal file
51
libs/cg/include/psemek/cg/convex_hull_2d/jarvis.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/orientation.hpp>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
template <typename InIterator, typename OutIterator>
|
||||
OutIterator jarvis_convex_hull (InIterator begin, InIterator end, OutIterator out)
|
||||
{
|
||||
auto first_hull_point = std::min_element(begin, end);
|
||||
auto last_hull_point = first_hull_point;
|
||||
|
||||
*out++ = last_hull_point;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto it = begin;
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
if (it == last_hull_point) continue;
|
||||
|
||||
bool is_hull_edge = true;
|
||||
for (auto jt = begin; jt != end; ++jt)
|
||||
{
|
||||
if (jt == it) continue;
|
||||
if (jt == last_hull_point) continue;
|
||||
|
||||
if (orientation(*last_hull_point, *it, *jt) != cg::sign_t::positive)
|
||||
{
|
||||
is_hull_edge = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_hull_edge)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it == first_hull_point) break;
|
||||
|
||||
*out++ = it;
|
||||
last_hull_point = it;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
67
libs/cg/include/psemek/cg/convex_hull_2d/quickhull.hpp
Normal file
67
libs/cg/include/psemek/cg/convex_hull_2d/quickhull.hpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/orientation.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename InIterator, typename ItIterator, typename OutIterator>
|
||||
OutIterator quick_hull_recursive_helper (InIterator p1, InIterator p2, ItIterator begin, ItIterator end, OutIterator out)
|
||||
{
|
||||
if (begin == end)
|
||||
return *out++ = p1;
|
||||
|
||||
// find point in [begin, end) furthest from segment (p1,p2)
|
||||
auto mid = *std::max_element(begin, end, [&](auto it1, auto it2){
|
||||
// TODO: design a robust predicate
|
||||
return orientation(*it2, (*it2) + ((*p2) - (*p1)), *it1) == sign_t::positive;
|
||||
});
|
||||
|
||||
auto end1 = std::partition(begin, end, [&](auto it){ return orientation(*p1, *it, *mid) == sign_t::positive; });
|
||||
auto end2 = std::partition(end1, end, [&](auto it){ return orientation(*mid, *it, *p2) == sign_t::positive; });
|
||||
|
||||
out = quick_hull_recursive_helper(p1, mid, begin, end1, out);
|
||||
out = quick_hull_recursive_helper(mid, p2, end1, end2, out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename InIterator, typename OutIterator>
|
||||
OutIterator quick_hull (InIterator begin, InIterator end, OutIterator out)
|
||||
{
|
||||
// need to store iterators to move sets of points around
|
||||
std::vector<InIterator> its(end - begin);
|
||||
std::iota(its.begin(), its.end(), begin);
|
||||
|
||||
// find leftmost point and move it to the beginning
|
||||
{
|
||||
auto p = std::min_element(its.begin(), its.end(), [](auto it1, auto it2){ return *it1 < *it2; });
|
||||
std::iter_swap(its.begin(), p);
|
||||
}
|
||||
|
||||
// find the next hull point in clockwise order and move it to second place
|
||||
{
|
||||
auto p = std::find_if(its.begin() + 1, its.end(), [&](auto it){
|
||||
return std::all_of(its.begin() + 1, its.end(), [&](auto jt){
|
||||
return it == jt || orientation(*its.front(), *it, *jt) == sign_t::negative;
|
||||
});
|
||||
});
|
||||
std::iter_swap(its.begin() + 1, p);
|
||||
}
|
||||
|
||||
// everything is set up, do the recursion
|
||||
out = detail::quick_hull_recursive_helper(its[0], its[1], its.begin() + 2, its.end(), out);
|
||||
|
||||
return *out++ = *(its.begin() + 1);
|
||||
}
|
||||
|
||||
}
|
||||
477
libs/cg/include/psemek/cg/dcel.hpp
Normal file
477
libs/cg/include/psemek/cg/dcel.hpp
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/cg/utility.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename Point, typename Index>
|
||||
struct point_rec : util::ebo_helper<Point>
|
||||
{
|
||||
Index edge;
|
||||
};
|
||||
|
||||
template <typename Edge, typename Index>
|
||||
struct edge_rec : util::ebo_helper<Edge>
|
||||
{
|
||||
Index origin;
|
||||
Index next;
|
||||
Index twin;
|
||||
Index face;
|
||||
};
|
||||
|
||||
template <typename Face, typename Index>
|
||||
struct face_rec : util::ebo_helper<Face>
|
||||
{
|
||||
Index edge;
|
||||
};
|
||||
|
||||
template <typename DCEL, typename Index, typename Tag>
|
||||
struct handle_base
|
||||
{
|
||||
handle_base ( )
|
||||
: owner_(nullptr)
|
||||
, i_(static_cast<Index>(-1))
|
||||
{ }
|
||||
|
||||
handle_base (DCEL * owner, Index i)
|
||||
: owner_(owner)
|
||||
, i_(i)
|
||||
{ }
|
||||
|
||||
DCEL & owner ( ) const { return *owner_; }
|
||||
|
||||
Index index ( ) const { return i_; }
|
||||
|
||||
explicit operator bool ( ) const { return i_ != static_cast<Index>(-1); }
|
||||
|
||||
friend bool operator == (handle_base const & h1, handle_base const & h2)
|
||||
{
|
||||
return h1.i_ == h2.i_;
|
||||
}
|
||||
|
||||
friend bool operator != (handle_base const & h1, handle_base const & h2)
|
||||
{
|
||||
return !(h1 == h2);
|
||||
}
|
||||
|
||||
friend bool operator < (handle_base const & h1, handle_base const & h2)
|
||||
{
|
||||
return h1.i_ < h2.i_;
|
||||
}
|
||||
|
||||
friend bool operator > (handle_base const & h1, handle_base const & h2)
|
||||
{
|
||||
return h2 < h1;
|
||||
}
|
||||
|
||||
friend bool operator <= (handle_base const & h1, handle_base const & h2)
|
||||
{
|
||||
return !(h2 < h1);
|
||||
}
|
||||
|
||||
friend bool operator >= (handle_base const & h1, handle_base const & h2)
|
||||
{
|
||||
return !(h1 < h2);
|
||||
}
|
||||
|
||||
protected:
|
||||
DCEL * owner_;
|
||||
Index i_;
|
||||
};
|
||||
|
||||
struct point_tag { };
|
||||
struct edge_tag { };
|
||||
struct face_tag { };
|
||||
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge = util::empty, typename Face = util::empty, typename Index = std::size_t>
|
||||
struct dcel
|
||||
{
|
||||
static constexpr Index null = static_cast<Index>(-1);
|
||||
|
||||
using point_rec = detail::point_rec<Point, Index>;
|
||||
using edge_rec = detail::edge_rec<Edge, Index>;
|
||||
using face_rec = detail::face_rec<Face, Index>;
|
||||
|
||||
std::vector<point_rec> points;
|
||||
std::vector<edge_rec> edges;
|
||||
std::vector<face_rec> faces;
|
||||
|
||||
// TODO: const handles
|
||||
|
||||
// TODO: iteration over handles
|
||||
|
||||
struct point_handle;
|
||||
struct edge_handle;
|
||||
struct face_handle;
|
||||
|
||||
struct point_handle : detail::handle_base<dcel, Index, detail::point_tag>
|
||||
{
|
||||
using detail::handle_base<dcel, Index, detail::point_tag>::handle_base;
|
||||
|
||||
Point & data ( ) const;
|
||||
|
||||
edge_handle edge ( ) const;
|
||||
|
||||
void edge (edge_handle h);
|
||||
|
||||
protected:
|
||||
point_rec & get ( ) const
|
||||
{
|
||||
assert(this->owner_ != nullptr);
|
||||
assert(this->i_ < this->owner_->points.size());
|
||||
return this->owner_->points[this->i_];
|
||||
}
|
||||
};
|
||||
|
||||
struct edge_handle : detail::handle_base<dcel, Index, detail::edge_tag>
|
||||
{
|
||||
using detail::handle_base<dcel, Index, detail::edge_tag>::handle_base;
|
||||
|
||||
Edge & data ( ) const;
|
||||
|
||||
point_handle origin ( ) const;
|
||||
edge_handle next ( ) const;
|
||||
edge_handle twin ( ) const;
|
||||
face_handle face ( ) const;
|
||||
|
||||
void origin (point_handle h);
|
||||
void next (edge_handle h);
|
||||
void twin (edge_handle h);
|
||||
void face (face_handle h);
|
||||
|
||||
protected:
|
||||
edge_rec & get ( ) const
|
||||
{
|
||||
assert(this->owner_ != nullptr);
|
||||
assert(this->i_ < this->owner_->edges.size());
|
||||
return this->owner_->edges[this->i_];
|
||||
}
|
||||
};
|
||||
|
||||
struct face_handle : detail::handle_base<dcel, Index, detail::face_tag>
|
||||
{
|
||||
using detail::handle_base<dcel, Index, detail::face_tag>::handle_base;
|
||||
|
||||
Face & data ( ) const;
|
||||
|
||||
edge_handle edge ( ) const;
|
||||
|
||||
void edge (edge_handle h);
|
||||
|
||||
protected:
|
||||
face_rec & get ( ) const
|
||||
{
|
||||
assert(this->owner_ != nullptr);
|
||||
assert(this->i_ < this->owner_->faces.size());
|
||||
return this->owner_->faces[this->i_];
|
||||
}
|
||||
};
|
||||
|
||||
point_handle point (Index i)
|
||||
{
|
||||
return {this, i};
|
||||
}
|
||||
|
||||
edge_handle edge (Index i)
|
||||
{
|
||||
return {this, i};
|
||||
}
|
||||
|
||||
face_handle face (Index i)
|
||||
{
|
||||
return {this, i};
|
||||
}
|
||||
|
||||
point_handle push_point (Point data = {})
|
||||
{
|
||||
auto i = static_cast<Index>(points.size());
|
||||
points.push_back({{std::move(data)}, null});
|
||||
return point(i);
|
||||
}
|
||||
|
||||
edge_handle push_edge (Edge data = {})
|
||||
{
|
||||
auto i = static_cast<Index>(edges.size());
|
||||
edges.push_back({{std::move(data)}, null, null, null, null});
|
||||
return edge(i);
|
||||
}
|
||||
|
||||
face_handle push_face (Face data = {})
|
||||
{
|
||||
auto i = static_cast<Index>(faces.size());
|
||||
faces.push_back({{std::move(data)}, null});
|
||||
return face(i);
|
||||
}
|
||||
|
||||
point_handle insert_point (Index i, Point data = {})
|
||||
{
|
||||
points.insert(points.begin() + i, {{std::move(data)}, null});
|
||||
for (auto & e : edges)
|
||||
{
|
||||
if (e.origin >= i)
|
||||
++e.origin;
|
||||
}
|
||||
return point(i);
|
||||
}
|
||||
|
||||
edge_handle insert_edge (Index i, Edge data = {})
|
||||
{
|
||||
edges.insert(edges.begin() + i, {{std::move(data)}, null, null, null, null});
|
||||
for (auto & p : points)
|
||||
{
|
||||
if (p.edge >= i)
|
||||
++p.edge;
|
||||
}
|
||||
for (auto & e : edges)
|
||||
{
|
||||
if (e.next >= i)
|
||||
++e.next;
|
||||
if (e.twin >= i)
|
||||
++e.twin;
|
||||
}
|
||||
for (auto & f : faces)
|
||||
{
|
||||
if (f.edge >= i)
|
||||
++f.edge;
|
||||
}
|
||||
return edge(i);
|
||||
}
|
||||
|
||||
face_handle insert_face (Index i, Face data = {})
|
||||
{
|
||||
faces.insert(faces.begin() + i, {{std::move(data)}, null});
|
||||
for (auto & e : edges)
|
||||
{
|
||||
if (e.face >= i)
|
||||
++e.face;
|
||||
}
|
||||
return face(i);
|
||||
}
|
||||
|
||||
void remove_point (point_handle h)
|
||||
{
|
||||
points.erase(points.begin() + h.index());
|
||||
for (auto & e : edges)
|
||||
{
|
||||
if (e.origin > h.index())
|
||||
--e.origin;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_edge (edge_handle h)
|
||||
{
|
||||
edges.erase(edges.begin() + h.index());
|
||||
for (auto & p : points)
|
||||
{
|
||||
if (p.edge > h.index())
|
||||
--p.edge;
|
||||
}
|
||||
for (auto & e : edges)
|
||||
{
|
||||
if (e.next > h.index())
|
||||
--e.next;
|
||||
if (e.twin > h.index())
|
||||
--e.twin;
|
||||
}
|
||||
for (auto & f : faces)
|
||||
{
|
||||
if (f.edge > h.index())
|
||||
--f.edge;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_face (face_handle h)
|
||||
{
|
||||
faces.erase(faces.begin() + h.index());
|
||||
for (auto & e : edges)
|
||||
{
|
||||
if (e.face > h.index())
|
||||
--e.face;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
Point & dcel<Point, Edge, Face, Index>::point_handle::data() const
|
||||
{
|
||||
return get().data();
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
typename dcel<Point, Edge, Face, Index>::edge_handle dcel<Point, Edge, Face, Index>::point_handle::edge() const
|
||||
{
|
||||
return edge_handle{this->owner_, get().edge};
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
void dcel<Point, Edge, Face, Index>::point_handle::edge(edge_handle h)
|
||||
{
|
||||
get().edge = h.index();
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
Edge & dcel<Point, Edge, Face, Index>::edge_handle::data() const
|
||||
{
|
||||
return get().data();
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
typename dcel<Point, Edge, Face, Index>::point_handle dcel<Point, Edge, Face, Index>::edge_handle::origin() const
|
||||
{
|
||||
return point_handle{this->owner_, get().origin};
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
typename dcel<Point, Edge, Face, Index>::edge_handle dcel<Point, Edge, Face, Index>::edge_handle::next() const
|
||||
{
|
||||
return edge_handle{this->owner_, get().next};
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
typename dcel<Point, Edge, Face, Index>::edge_handle dcel<Point, Edge, Face, Index>::edge_handle::twin() const
|
||||
{
|
||||
return edge_handle{this->owner_, get().twin};
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
typename dcel<Point, Edge, Face, Index>::face_handle dcel<Point, Edge, Face, Index>::edge_handle::face() const
|
||||
{
|
||||
return face_handle{this->owner_, get().face};
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
void dcel<Point, Edge, Face, Index>::edge_handle::origin(point_handle h)
|
||||
{
|
||||
get().origin = h.index();
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
void dcel<Point, Edge, Face, Index>::edge_handle::next(edge_handle h)
|
||||
{
|
||||
get().next = h.index();
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
void dcel<Point, Edge, Face, Index>::edge_handle::twin(edge_handle h)
|
||||
{
|
||||
get().twin = h.index();
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
void dcel<Point, Edge, Face, Index>::edge_handle::face(face_handle h)
|
||||
{
|
||||
get().face = h.index();
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
Face & dcel<Point, Edge, Face, Index>::face_handle::data() const
|
||||
{
|
||||
return get().data();
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
typename dcel<Point, Edge, Face, Index>::edge_handle dcel<Point, Edge, Face, Index>::face_handle::edge() const
|
||||
{
|
||||
return edge_handle{this->owner_, get().edge};
|
||||
}
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
void dcel<Point, Edge, Face, Index>::face_handle::edge(edge_handle h)
|
||||
{
|
||||
get().edge = h.index();
|
||||
}
|
||||
|
||||
template <
|
||||
typename Point2, typename Edge2 = util::empty, typename Face2 = util::empty, typename Index2 = std::size_t,
|
||||
typename Point, typename Edge, typename Face, typename Index,
|
||||
typename PointFn, typename EdgeFn, typename FaceFn>
|
||||
dcel<Point2, Edge2, Face2, Index2> map(dcel<Point, Edge, Face, Index> const & d, PointFn && point_fn, EdgeFn && edge_fn, FaceFn && face_fn)
|
||||
{
|
||||
using result_type = dcel<Point2, Edge2, Face2, Index2>;
|
||||
result_type result;
|
||||
|
||||
result.points.reserve(d.points.size());
|
||||
result.edges.reserve(d.edges.size());
|
||||
result.faces.reserve(d.faces.size());
|
||||
|
||||
for (auto const & p : d.points)
|
||||
{
|
||||
typename result_type::point_rec rec;
|
||||
rec.edge = static_cast<Index2>(p.edge);
|
||||
rec.data() = point_fn(p.data());
|
||||
result.points.push_back(std::move(rec));
|
||||
}
|
||||
|
||||
for (auto const & e : d.edges)
|
||||
{
|
||||
typename result_type::edge_rec rec;
|
||||
rec.origin = static_cast<Index2>(e.origin);
|
||||
rec.next = static_cast<Index2>(e.next);
|
||||
rec.twin = static_cast<Index2>(e.twin);
|
||||
rec.face = static_cast<Index2>(e.face);
|
||||
rec.data() = edge_fn(e.data());
|
||||
result.edges.push_back(std::move(rec));
|
||||
}
|
||||
|
||||
for (auto const & f : d.faces)
|
||||
{
|
||||
typename result_type::face_rec rec;
|
||||
rec.edge = static_cast<Index2>(f.edge);
|
||||
rec.data() = face_fn(f.data());
|
||||
result.faces.push_back(std::move(rec));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Index = std::size_t, typename Iterator>
|
||||
auto polygon_dcel (Iterator begin, Iterator end)
|
||||
{
|
||||
using point_type = std::decay_t<decltype(*begin)>;
|
||||
|
||||
dcel<point_type, util::empty, util::empty, Index> result;
|
||||
|
||||
auto outer_face = result.push_face();
|
||||
auto inner_face = result.push_face();
|
||||
|
||||
outer_face.edge(result.edge(0));
|
||||
inner_face.edge(result.edge(1));
|
||||
|
||||
for (; begin != end; ++begin)
|
||||
{
|
||||
result.push_point(*begin);
|
||||
result.push_edge();
|
||||
result.push_edge();
|
||||
}
|
||||
|
||||
std::size_t const N = result.points.size();
|
||||
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
result.points[i].edge = 2 * i;
|
||||
|
||||
result.edges[2 * i].face = 0;
|
||||
result.edges[2 * i].next = (2 * i + 2) % (2 * N);
|
||||
result.edges[2 * i].twin = 2 * i + 1;
|
||||
result.edges[2 * i].origin = i;
|
||||
|
||||
result.edges[2 * i + 1].face = 1;
|
||||
result.edges[2 * i + 1].next = (2 * i + 2 * N - 1) % (2 * N);
|
||||
result.edges[2 * i + 1].twin = 2 * i;
|
||||
result.edges[2 * i + 1].origin = (i + 1) % N;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
50
libs/cg/include/psemek/cg/dual.hpp
Normal file
50
libs/cg/include/psemek/cg/dual.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/cg/dcel.hpp>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
template <typename Point, typename Edge, typename Face, typename Index>
|
||||
dcel<Face, Edge, Point, Index> dual (dcel<Point, Edge, Face, Index> const & d)
|
||||
{
|
||||
dcel<Face, Edge, Point, Index> result;
|
||||
|
||||
result.edges.reserve(d.edges.size());
|
||||
for (auto const & e : d.edges)
|
||||
{
|
||||
result.push_edge(e.data());
|
||||
auto & n = result.edges.back();
|
||||
n.twin = e.twin;
|
||||
n.face = e.origin;
|
||||
n.origin = d.edges[e.twin].face;
|
||||
}
|
||||
|
||||
for (Index e = 0; e < result.edges.size(); ++e)
|
||||
{
|
||||
auto next = [&](auto i){ return d.edges[d.edges[i].twin].next; };
|
||||
|
||||
Index i = e, n = next(i);
|
||||
for (; n != e; i = n, n = next(n));
|
||||
|
||||
result.edges[e].next = i;
|
||||
}
|
||||
|
||||
result.points.reserve(d.faces.size());
|
||||
for (auto const & f : d.faces)
|
||||
{
|
||||
result.push_point(f.data());
|
||||
result.points.back().edge = d.edges[f.edge].twin;
|
||||
}
|
||||
|
||||
result.faces.reserve(d.points.size());
|
||||
for (auto const & p : d.points)
|
||||
{
|
||||
result.push_face(p.data());
|
||||
result.faces.back().edge = p.edge;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
147
libs/cg/include/psemek/cg/segment_intersection.hpp
Normal file
147
libs/cg/include/psemek/cg/segment_intersection.hpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/intersection.hpp>
|
||||
#include <psemek/cg/utility.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <variant>
|
||||
#include <numeric>
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
template <typename InIterator, typename OutIterator>
|
||||
OutIterator segment_intersection (InIterator begin, InIterator end, OutIterator out)
|
||||
{
|
||||
using segment = std::decay_t<decltype(*begin)>;
|
||||
using point = typename segment::point_type;
|
||||
using scalar = typename point::scalar_type;
|
||||
|
||||
enum event_type
|
||||
{
|
||||
BEGIN,
|
||||
END,
|
||||
CROSS,
|
||||
};
|
||||
|
||||
// TODO: robust event comparison predicate
|
||||
|
||||
struct event
|
||||
{
|
||||
InIterator it1, it2;
|
||||
point p;
|
||||
event_type type;
|
||||
};
|
||||
|
||||
struct event_comparator
|
||||
{
|
||||
bool operator() (event const & e1, event const & e2) const
|
||||
{
|
||||
return e1.p > e2.p;
|
||||
}
|
||||
};
|
||||
|
||||
std::priority_queue<event, std::vector<event>, event_comparator> queue;
|
||||
|
||||
for (auto it = begin; it != end; ++it)
|
||||
{
|
||||
event e_begin{it, end, (*it)[0], BEGIN};
|
||||
event e_end {it, end, (*it)[1], END };
|
||||
|
||||
if (e_begin.p > e_end.p) std::swap(e_begin.p, e_end.p);
|
||||
queue.push(e_begin);
|
||||
queue.push(e_end);
|
||||
}
|
||||
|
||||
auto push_cross_event = [&queue](InIterator it, InIterator jt){
|
||||
auto ion = intersection(*it, *jt);
|
||||
if (auto p = std::get_if<point>(&ion))
|
||||
{
|
||||
queue.push({it, jt, *p, CROSS});
|
||||
}
|
||||
};
|
||||
|
||||
scalar sweep_line = -std::numeric_limits<scalar>::infinity();
|
||||
|
||||
struct status_comparator
|
||||
{
|
||||
scalar & sweep_line;
|
||||
|
||||
bool operator() (InIterator it1, InIterator it2) const
|
||||
{
|
||||
scalar const eps = 1e-4;
|
||||
scalar const y1 = (*it1)[0][1] + ((*it1)[1][1] -(*it1)[0][1]) * (sweep_line + eps - (*it1)[0][0]) / ((*it1)[1][0] - (*it1)[0][0]);
|
||||
scalar const y2 = (*it2)[0][1] + ((*it2)[1][1] -(*it2)[0][1]) * (sweep_line + eps - (*it2)[0][0]) / ((*it2)[1][0] - (*it2)[0][0]);
|
||||
|
||||
return y1 < y2;
|
||||
}
|
||||
};
|
||||
|
||||
std::set<InIterator, status_comparator> status{status_comparator{sweep_line}};
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
event e = queue.top();
|
||||
queue.pop();
|
||||
|
||||
switch (e.type)
|
||||
{
|
||||
case BEGIN: {
|
||||
auto it = status.insert(e.it1).first;
|
||||
if (it != status.begin()) push_cross_event(*std::prev(it), *it);
|
||||
if (std::next(it) != status.end()) push_cross_event(*it, *std::next(it));
|
||||
sweep_line = e.p[0];
|
||||
break;
|
||||
}
|
||||
case END: {
|
||||
auto it = status.find(e.it1);
|
||||
if (it == status.end())
|
||||
{
|
||||
int fuck = 42;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (it != status.begin() && std::next(it) != status.end()) push_cross_event(*std::prev(it), *std::next(it));
|
||||
status.erase(it);
|
||||
}
|
||||
sweep_line = e.p[0];
|
||||
break;
|
||||
}
|
||||
case CROSS: {
|
||||
*out++ = {e.it1, e.it2};
|
||||
|
||||
status.erase(e.it1);
|
||||
status.erase(e.it2);
|
||||
sweep_line = e.p[0];
|
||||
auto it1 = status.insert(e.it1).first;
|
||||
auto it2 = status.insert(e.it2).first;
|
||||
if (it1 == status.end())
|
||||
{
|
||||
int fuck_really = 42;
|
||||
}
|
||||
if (it2 == status.end())
|
||||
{
|
||||
int fuck_really = 42;
|
||||
}
|
||||
|
||||
if (std::next(it2) != it1)
|
||||
{
|
||||
bool check = std::next(it1) == it2;
|
||||
|
||||
int fuck = 42;
|
||||
}
|
||||
if (it2 != status.begin()) push_cross_event(*std::prev(it2), *it2);
|
||||
if (std::next(it1) != status.end()) push_cross_event(*it1, *std::next(it1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
103
libs/cg/include/psemek/cg/triangulation/delaunay.hpp
Normal file
103
libs/cg/include/psemek/cg/triangulation/delaunay.hpp
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/incircle.hpp>
|
||||
#include <psemek/cg/triangulation/triangulation.hpp>
|
||||
|
||||
#include <queue>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
template <typename Index = std::size_t, typename InputIterator>
|
||||
auto delaunay (InputIterator begin, InputIterator end)
|
||||
{
|
||||
std::vector<Index> edge_queue;
|
||||
|
||||
auto callback = [&](auto & dcel, auto p)
|
||||
{
|
||||
auto outer_face = dcel.face(0);
|
||||
|
||||
// grab interior edges outgoing from p
|
||||
for (auto e = p.edge();;)
|
||||
{
|
||||
auto t = e.twin();
|
||||
|
||||
if (e.face() != outer_face)
|
||||
{
|
||||
if (t.face() != outer_face)
|
||||
{
|
||||
edge_queue.push_back(e.index());
|
||||
}
|
||||
|
||||
auto n = e.next();
|
||||
if (n.twin().face() != outer_face)
|
||||
{
|
||||
edge_queue.push_back(n.index());
|
||||
}
|
||||
}
|
||||
|
||||
e = t.next();
|
||||
|
||||
if (e == p.edge()) break;
|
||||
}
|
||||
|
||||
while (!edge_queue.empty())
|
||||
{
|
||||
auto e = dcel.edge(edge_queue.back());
|
||||
edge_queue.pop_back();
|
||||
|
||||
auto next = e.next();
|
||||
auto prev = next.next();
|
||||
auto twin = e.twin();
|
||||
auto tnext = twin.next();
|
||||
auto tprev = tnext.next();
|
||||
|
||||
auto p0 = prev.origin();
|
||||
auto p1 = e.origin();
|
||||
auto p2 = next.origin();
|
||||
auto p3 = e.twin().next().next().origin();
|
||||
|
||||
// decide if a flip is needed
|
||||
if (in_circle(*p0.data(), *p1.data(), *p2.data(), *p3.data()) != sign_t::positive) continue;
|
||||
|
||||
auto f0 = e.face();
|
||||
auto f1 = twin.face();
|
||||
|
||||
e.origin(p0);
|
||||
e.next(tprev);
|
||||
|
||||
next.next(e);
|
||||
|
||||
prev.face(f1);
|
||||
prev.next(tnext);
|
||||
|
||||
twin.origin(p3);
|
||||
twin.next(prev);
|
||||
|
||||
tnext.next(twin);
|
||||
|
||||
tprev.face(f0);
|
||||
tprev.next(next);
|
||||
|
||||
p1.edge(tnext);
|
||||
p2.edge(next);
|
||||
|
||||
f0.edge(e);
|
||||
f1.edge(twin);
|
||||
|
||||
auto push = [&](auto e)
|
||||
{
|
||||
if (e.twin().face() != outer_face)
|
||||
edge_queue.push_back(e.index());
|
||||
};
|
||||
|
||||
push(next);
|
||||
push(prev);
|
||||
push(tnext);
|
||||
push(tprev);
|
||||
}
|
||||
};
|
||||
return detail::triangulate(begin, end, callback);
|
||||
}
|
||||
|
||||
}
|
||||
262
libs/cg/include/psemek/cg/triangulation/triangulation.hpp
Normal file
262
libs/cg/include/psemek/cg/triangulation/triangulation.hpp
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/orientation.hpp>
|
||||
#include <psemek/cg/dcel.hpp>
|
||||
#include <psemek/cg/utility.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename Index = std::size_t, typename Iterator, typename Callback>
|
||||
auto triangulate (Iterator begin, Iterator end, Callback && callback)
|
||||
{
|
||||
using point_type = std::decay_t<decltype(*begin)>;
|
||||
static_assert(point_type::dimension == 2);
|
||||
|
||||
using dcel_type = dcel<Iterator, util::empty, util::empty, Index>;
|
||||
|
||||
dcel_type result;
|
||||
|
||||
std::vector<Iterator> sweepline_events;
|
||||
sweepline_events.resize(std::distance(begin, end));
|
||||
std::iota(sweepline_events.begin(), sweepline_events.end(), begin);
|
||||
std::sort(sweepline_events.begin(), sweepline_events.end(), [](auto it, auto jt){ return *it < *jt; });
|
||||
|
||||
auto it = sweepline_events.begin();
|
||||
|
||||
auto outer_face = result.push_face();
|
||||
|
||||
// handle degenerate cases
|
||||
if (sweepline_events.empty()) return result;
|
||||
|
||||
if (sweepline_events.size() == 1)
|
||||
{
|
||||
result.push_point(*it++);
|
||||
return result;
|
||||
}
|
||||
|
||||
{
|
||||
auto const N = sweepline_events.size();
|
||||
result.points.reserve(N);
|
||||
result.edges.reserve(3*(N-1));
|
||||
result.faces.reserve(2*(N-1));
|
||||
}
|
||||
|
||||
typename dcel_type::edge_handle hull_start;
|
||||
|
||||
{
|
||||
auto p0 = result.push_point(*it++);
|
||||
auto p1 = result.push_point(*it++);
|
||||
|
||||
auto e01 = result.push_edge();
|
||||
auto e10 = result.push_edge();
|
||||
|
||||
p0.edge(e01);
|
||||
p1.edge(e10);
|
||||
|
||||
e01.origin(p0);
|
||||
e01.next(e10);
|
||||
e01.face(outer_face);
|
||||
e01.twin(e10);
|
||||
e10.origin(p1);
|
||||
e10.next(e01);
|
||||
e10.face(outer_face);
|
||||
e10.twin(e01);
|
||||
outer_face.edge(e01);
|
||||
|
||||
hull_start = e01;
|
||||
}
|
||||
|
||||
for (; it != sweepline_events.end(); ++it)
|
||||
{
|
||||
// walk along the hull clockwise
|
||||
|
||||
auto cur_hull_edge = hull_start;
|
||||
auto next_hull_edge = cur_hull_edge.next();
|
||||
|
||||
auto hp0 = cur_hull_edge.origin();
|
||||
auto hp1 = next_hull_edge.origin();
|
||||
|
||||
auto move_hull_edge = [&]{
|
||||
cur_hull_edge = next_hull_edge;
|
||||
next_hull_edge = next_hull_edge.next();
|
||||
hp0 = hp1;
|
||||
hp1 = next_hull_edge.origin();
|
||||
};
|
||||
|
||||
bool degenerate = false;
|
||||
|
||||
// find first hull edge visible from new point
|
||||
while (orientation(**it, *hp0.data(), *hp1.data()) != sign_t::positive)
|
||||
{
|
||||
move_hull_edge();
|
||||
if (cur_hull_edge == hull_start)
|
||||
{
|
||||
degenerate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (degenerate)
|
||||
{
|
||||
// the whole hull is just a sequence of points on a line
|
||||
// find the closest & connect via a single edge
|
||||
|
||||
auto q = result.point(0);
|
||||
|
||||
bool vertical = false;
|
||||
|
||||
// find rightmost point
|
||||
for (Index i = 1; i < result.points.size(); ++i)
|
||||
{
|
||||
auto const x = (*result.points[i].data())[0];
|
||||
auto const qx = (*q.data())[0];
|
||||
|
||||
if (x == qx)
|
||||
{
|
||||
vertical = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (x > qx)
|
||||
{
|
||||
q = result.point(i);
|
||||
}
|
||||
}
|
||||
|
||||
// all points lie on a vertical line - find topmost
|
||||
if (vertical)
|
||||
{
|
||||
q = result.point(0);
|
||||
|
||||
for (Index i = 1; i < result.points.size(); ++i)
|
||||
{
|
||||
auto const y = (*result.points[i].data())[1];
|
||||
auto const qy = (*q.data())[1];
|
||||
|
||||
if (y > qy)
|
||||
{
|
||||
q = result.point(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto p = result.push_point(*it);
|
||||
|
||||
auto qnext = q.edge();
|
||||
auto qprev = qnext.twin();
|
||||
|
||||
auto pnext = result.push_edge();
|
||||
auto pprev = result.push_edge();
|
||||
|
||||
qprev.next(pprev);
|
||||
|
||||
pprev.next(pnext);
|
||||
pprev.origin(q);
|
||||
pprev.twin(pnext);
|
||||
pprev.face(outer_face);
|
||||
|
||||
pnext.next(qnext);
|
||||
pnext.origin(p);
|
||||
pnext.twin(pprev);
|
||||
pnext.face(outer_face);
|
||||
|
||||
p.edge(pnext);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto p = result.push_point(*it);
|
||||
|
||||
auto mid_edge = result.push_edge();
|
||||
mid_edge.origin(hp0);
|
||||
mid_edge.face(outer_face);
|
||||
|
||||
auto first_mid_edge = mid_edge;
|
||||
|
||||
{
|
||||
// fix prev_hull_edge.next
|
||||
auto prev_hull_edge = cur_hull_edge.twin();
|
||||
|
||||
while (prev_hull_edge.face() != outer_face)
|
||||
{
|
||||
prev_hull_edge = prev_hull_edge.next().twin();
|
||||
}
|
||||
prev_hull_edge.next(first_mid_edge);
|
||||
}
|
||||
|
||||
// until current edge is visible
|
||||
while (orientation(**it, *hp0.data(), *hp1.data()) == sign_t::positive)
|
||||
{
|
||||
// fill new triangle
|
||||
|
||||
auto ep0 = result.push_edge();
|
||||
auto e1p = result.push_edge();
|
||||
|
||||
auto f = result.push_face();
|
||||
|
||||
if (!p.edge())
|
||||
{
|
||||
p.edge(ep0);
|
||||
}
|
||||
|
||||
mid_edge.twin(ep0);
|
||||
|
||||
ep0.origin(p);
|
||||
ep0.next(cur_hull_edge);
|
||||
ep0.twin(mid_edge);
|
||||
ep0.face(f);
|
||||
|
||||
e1p.origin(hp1);
|
||||
e1p.next(ep0);
|
||||
e1p.face(f);
|
||||
|
||||
cur_hull_edge.next(e1p);
|
||||
cur_hull_edge.face(f);
|
||||
|
||||
f.edge(ep0);
|
||||
|
||||
mid_edge = e1p;
|
||||
|
||||
move_hull_edge();
|
||||
}
|
||||
|
||||
auto last_mid_edge = result.push_edge();
|
||||
last_mid_edge.origin(p);
|
||||
last_mid_edge.face(outer_face);
|
||||
last_mid_edge.twin(mid_edge);
|
||||
last_mid_edge.next(cur_hull_edge);
|
||||
|
||||
mid_edge.twin(last_mid_edge);
|
||||
|
||||
first_mid_edge.next(last_mid_edge);
|
||||
|
||||
// update first hull edge
|
||||
|
||||
if (hull_start.face() != outer_face)
|
||||
{
|
||||
hull_start = hull_start.next().next().twin();
|
||||
outer_face.edge(hull_start);
|
||||
}
|
||||
|
||||
callback(result, p);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename Index = std::size_t, typename InputIterator>
|
||||
auto triangulate (InputIterator begin, InputIterator end)
|
||||
{
|
||||
return detail::triangulate(begin, end, util::nop);
|
||||
}
|
||||
|
||||
}
|
||||
68
libs/cg/include/psemek/cg/utility.hpp
Normal file
68
libs/cg/include/psemek/cg/utility.hpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace cg::util
|
||||
{
|
||||
|
||||
struct empty{};
|
||||
|
||||
template <typename ... Fs>
|
||||
struct overload_impl
|
||||
: Fs ...
|
||||
{
|
||||
using Fs::operator() ...;
|
||||
};
|
||||
|
||||
template <typename ... Fs>
|
||||
auto overload (Fs && ... fs)
|
||||
{
|
||||
return overload_impl<Fs...>{ std::forward<Fs>(fs) ... };
|
||||
}
|
||||
|
||||
template <typename Variant, typename ... Fs>
|
||||
decltype(auto) match (Variant && v, Fs && ... fs)
|
||||
{
|
||||
return std::visit(overload(std::forward<Fs>(fs)...), std::forward<Variant>(v));
|
||||
}
|
||||
|
||||
constexpr auto id = [](auto const & x){ return x; };
|
||||
|
||||
constexpr auto nop = [](auto const & ...){};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename T, bool CanInherit>
|
||||
struct ebo_helper_impl : T
|
||||
{
|
||||
T & data() { return *this; }
|
||||
T const & data() const { return *this; }
|
||||
|
||||
template <typename ... Args>
|
||||
ebo_helper_impl (Args && ... args)
|
||||
: T(std::forward<Args>(args)...)
|
||||
{ }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ebo_helper_impl<T, false>
|
||||
{
|
||||
T value;
|
||||
|
||||
T & data() { return value; }
|
||||
T const & data() const { return value; }
|
||||
|
||||
template <typename ... Args>
|
||||
ebo_helper_impl (Args && ... args)
|
||||
: value(std::forward<Args>(args)...)
|
||||
{ }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using ebo_helper = detail::ebo_helper_impl<T, std::is_class_v<T> && !(std::is_final_v<T>)>;
|
||||
|
||||
}
|
||||
194
libs/cg/include/psemek/cg/voronoi.hpp
Normal file
194
libs/cg/include/psemek/cg/voronoi.hpp
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/cg/triangulation/delaunay.hpp>
|
||||
#include <psemek/cg/dual.hpp>
|
||||
#include <psemek/geom/gauss.hpp>
|
||||
#include <psemek/cg/bbox.hpp>
|
||||
|
||||
namespace cg
|
||||
{
|
||||
|
||||
// Point #0 of the resulting dcel corresponds to the outer face,
|
||||
// i.e. the point at infinity, and has unspecified coordinates.
|
||||
template <typename Index = std::size_t, typename Iterator>
|
||||
auto voronoi (Iterator begin, Iterator end)
|
||||
{
|
||||
using point_type = std::decay_t<decltype(*begin)>;
|
||||
|
||||
auto del = dual(delaunay<Index>(begin, end));
|
||||
|
||||
dcel<point_type, util::empty, Iterator, Index> result;
|
||||
|
||||
result.points.reserve(del.points.size());
|
||||
for (std::size_t i = 0; i < del.points.size(); ++i)
|
||||
{
|
||||
auto p = del.point(i);
|
||||
|
||||
point_type q[3];
|
||||
|
||||
auto e = p.edge();
|
||||
q[0] = *e.face().data();
|
||||
e = e.twin().next();
|
||||
q[1] = *e.face().data();
|
||||
e = e.twin().next();
|
||||
q[2] = *e.face().data();
|
||||
|
||||
using T = typename point_type::scalar_type;
|
||||
|
||||
matrix<T, 3, 3> m;
|
||||
vector<T, 3> b;
|
||||
|
||||
for (std::size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
m[i][0] = q[i][0];
|
||||
m[i][1] = q[i][1];
|
||||
m[i][2] = T{1};
|
||||
|
||||
b[i] = q[i][0] * q[i][0] + q[i][1] * q[i][1];
|
||||
}
|
||||
|
||||
gauss(m, b);
|
||||
|
||||
auto newp = result.push_point(point_type{b[0] / 2, b[1] / 2});
|
||||
newp.edge(result.edge(p.edge().index()));
|
||||
}
|
||||
|
||||
result.edges = std::move(del.edges);
|
||||
result.faces = std::move(del.faces);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Replaces the #0 infinite point of a dcel with a proper outer face
|
||||
template <typename Point, typename Face, typename Edge, typename Index>
|
||||
void remove_infinite_point (dcel<Point, Face, Edge, Index> & dcel)
|
||||
{
|
||||
// insert outer face at position #0
|
||||
auto outer_face = dcel.insert_face(0);
|
||||
|
||||
std::vector<std::size_t> delete_edges;
|
||||
std::vector<std::size_t> delete_faces;
|
||||
|
||||
auto infinite_point = dcel.point(0);
|
||||
|
||||
// iterate edges of infinite vertex #0 and fix hull
|
||||
for (auto e = infinite_point.edge();;)
|
||||
{
|
||||
for (auto n = e.next(); n.twin().origin() != infinite_point; n = n.next())
|
||||
{
|
||||
n.face(outer_face);
|
||||
n.origin().edge(n);
|
||||
if (!outer_face.edge())
|
||||
outer_face.edge(n);
|
||||
}
|
||||
|
||||
auto n = e;
|
||||
for (; n.next() != e.twin(); n = n.next().twin());
|
||||
|
||||
if (n.origin() != infinite_point)
|
||||
{
|
||||
auto t = e.next();
|
||||
for (; t.twin().origin() == infinite_point; t = t.twin().next());
|
||||
|
||||
n.next(t);
|
||||
}
|
||||
|
||||
delete_faces.push_back(e.face().index());
|
||||
delete_edges.push_back(e.index());
|
||||
e = e.twin();
|
||||
delete_edges.push_back(e.index());
|
||||
e = e.next();
|
||||
|
||||
if (e == infinite_point.edge()) break;
|
||||
}
|
||||
|
||||
// remove edges incident to infinite vertex #0
|
||||
std::sort(delete_edges.begin(), delete_edges.end(), std::greater<>{});
|
||||
for (auto e : delete_edges)
|
||||
dcel.remove_edge(dcel.edge(e));
|
||||
|
||||
// remove vertex #0
|
||||
dcel.remove_point(infinite_point);
|
||||
|
||||
std::sort(delete_faces.begin(), delete_faces.end(), std::greater<>{});
|
||||
for (auto f : delete_faces)
|
||||
dcel.remove_face(dcel.face(f));
|
||||
}
|
||||
|
||||
// iteratively removes degenerate edges
|
||||
template <typename Point, typename Face, typename Edge, typename Index>
|
||||
void remove_degenerate_edges (dcel<Point, Face, Edge, Index> & dcel)
|
||||
{
|
||||
std::vector<std::size_t> delete_points;
|
||||
std::vector<std::size_t> delete_edges;
|
||||
|
||||
for (std::size_t i = 0; i < dcel.points.size(); ++i)
|
||||
{
|
||||
auto p = dcel.point(i);
|
||||
while (p.edge().twin().next() == p.edge())
|
||||
{
|
||||
delete_points.push_back(p.index());
|
||||
delete_edges.push_back(p.edge().index());
|
||||
delete_edges.push_back(p.edge().twin().index());
|
||||
|
||||
p.edge().next().origin().edge(p.edge().next());
|
||||
p.edge().face().edge(p.edge().next());
|
||||
|
||||
auto n = p.edge();
|
||||
while (true)
|
||||
{
|
||||
auto m = n.next().twin();
|
||||
if (m == p.edge()) break;
|
||||
n = m;
|
||||
}
|
||||
|
||||
n.next(p.edge().next());
|
||||
|
||||
p = p.edge().next().origin();
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(delete_points.begin(), delete_points.end(), std::greater<>{});
|
||||
for (auto p : delete_points)
|
||||
dcel.remove_point(dcel.point(p));
|
||||
|
||||
std::sort(delete_edges.begin(), delete_edges.end(), std::greater<>{});
|
||||
for (auto e : delete_edges)
|
||||
dcel.remove_edge(dcel.edge(e));
|
||||
}
|
||||
|
||||
// Outputs 4 points such that the finite faces of the voronoi diagram of (anything
|
||||
// inside the convex hull of input + the output points) covers the convex hull of input
|
||||
template <typename InputIterator, typename OutputIterator>
|
||||
OutputIterator bounded_voronoi_extra_points (InputIterator begin, InputIterator end, OutputIterator out)
|
||||
{
|
||||
using point_type = std::decay_t<decltype(*begin)>;
|
||||
|
||||
static_assert(point_type::dimension == 2);
|
||||
|
||||
auto box = bbox(begin, end);
|
||||
|
||||
point_type const center {(box[0].min + box[0].max) / 2, (box[1].min + box[1].max) / 2};
|
||||
|
||||
auto const R = std::sqrt(box[0].length() * box[0].length() + box[1].length() * box[1].length());
|
||||
|
||||
*out++ = point_type{center[0] + R, center[1]};
|
||||
*out++ = point_type{center[0], center[1] + R};
|
||||
*out++ = point_type{center[0] - R, center[1]};
|
||||
*out++ = point_type{center[0], center[1] - R};
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Compute a clipped voronoi tessellation of (begin, end - 4)
|
||||
// Last 4 points will be used for temporary data
|
||||
template <typename InputIterator>
|
||||
auto bounded_voronoi (InputIterator begin, InputIterator end)
|
||||
{
|
||||
bounded_voronoi_extra_points(begin, end - 4, end - 4);
|
||||
auto dcel = cg::voronoi(begin, end);
|
||||
cg::remove_infinite_point(dcel);
|
||||
return dcel;
|
||||
}
|
||||
|
||||
}
|
||||
9
libs/geom/CMakeLists.txt
Normal file
9
libs/geom/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
find_package(Boost REQUIRED)
|
||||
find_package(GMP REQUIRED)
|
||||
|
||||
file(GLOB_RECURSE PSEMEK_GEOM_HEADERS "include/*.hpp")
|
||||
file(GLOB_RECURSE PSEMEK_GEOM_SOURCES "source/*.cpp")
|
||||
|
||||
add_library(geom STATIC ${PSEMEK_GEOM_HEADERS} ${PSEMEK_GEOM_SOURCES})
|
||||
target_include_directories(geom PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(geom PUBLIC Boost::boost GMP)
|
||||
41
libs/geom/include/psemek/geom/bezier.hpp
Normal file
41
libs/geom/include/psemek/geom/bezier.hpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename Point>
|
||||
struct bezier
|
||||
{
|
||||
bezier (std::vector<Point> points)
|
||||
: points_(std::move(points))
|
||||
{
|
||||
if (points_.empty())
|
||||
throw std::runtime_error("Points array should be non-empty");
|
||||
temp_.resize(points_.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto operator() (T const & t) const
|
||||
{
|
||||
// In-place de Casteljau's algorithm
|
||||
temp_ = points_;
|
||||
|
||||
for (std::size_t k = 0; k + 1 < points_.size(); ++k)
|
||||
{
|
||||
for (std::size_t i = 0; i + k + 1 < points_.size(); ++i)
|
||||
temp_[i] = lerp(temp_[i], temp_[i + 1], t);
|
||||
}
|
||||
return temp_.front();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Point> const points_;
|
||||
std::vector<Point> mutable temp_;
|
||||
};
|
||||
|
||||
}
|
||||
208
libs/geom/include/psemek/geom/box.hpp
Normal file
208
libs/geom/include/psemek/geom/box.hpp
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/interval.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct box
|
||||
{
|
||||
interval<T> axes[N];
|
||||
|
||||
using point_type = point<T, N>;
|
||||
using vector_type = vector<T, N>;
|
||||
|
||||
interval<T> & operator [] (std::size_t i)
|
||||
{
|
||||
return axes[i];
|
||||
}
|
||||
|
||||
interval<T> const & operator [] (std::size_t i) const
|
||||
{
|
||||
return axes[i];
|
||||
}
|
||||
|
||||
// singleton box
|
||||
static box singleton (point_type const & p)
|
||||
{
|
||||
box b;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
b.axes[i] = interval<T>::singleton(p[i]);
|
||||
return b;
|
||||
}
|
||||
|
||||
bool empty ( ) const
|
||||
{
|
||||
for (auto const & i : axes)
|
||||
if (i.empty())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
T size ( ) const
|
||||
{
|
||||
T result = T{1};
|
||||
for (auto const & i : axes)
|
||||
result *= i.size();
|
||||
return result;
|
||||
}
|
||||
|
||||
box & operator += (vector_type const & delta);
|
||||
box & operator -= (vector_type const & delta);
|
||||
|
||||
box & operator &= (point_type const & p);
|
||||
box & operator |= (point_type const & p);
|
||||
|
||||
box & operator &= (box const & b);
|
||||
box & operator |= (box const & b);
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> operator + (box<T, N> const & b, vector<T, N> const & delta)
|
||||
{
|
||||
box<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
result[i] = b[i] + delta[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> operator + (vector<T, N> const & delta, box<T, N> const & b)
|
||||
{
|
||||
box<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
result[i] = delta[i] + b[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> operator - (box<T, N> const & b, vector<T, N> const & delta)
|
||||
{
|
||||
box<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
result[i] = b[i] - delta[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> operator & (box<T, N> const & b, point<T, N> const & p)
|
||||
{
|
||||
box<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
result[i] = b[i] & p[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> operator & (point<T, N> const & p, box<T, N> const & b)
|
||||
{
|
||||
box<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
result[i] = p[i] & b[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> operator | (box<T, N> const & b, point<T, N> const & p)
|
||||
{
|
||||
box<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
result[i] = b[i] | p[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> operator | (point<T, N> const & p, box<T, N> const & b)
|
||||
{
|
||||
box<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
result[i] = p[i] | b[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> operator & (box<T, N> const & b1, box<T, N> const & b2)
|
||||
{
|
||||
box<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
result[i] = b1[i] & b2[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> operator | (box<T, N> const & b1, box<T, N> const & b2)
|
||||
{
|
||||
box<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
result[i] = b1[i] | b2[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> & box<T, N>::operator += (vector<T, N> const & delta)
|
||||
{
|
||||
return *this = *this + delta;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> & box<T, N>::operator -= (vector<T, N> const & delta)
|
||||
{
|
||||
return *this = *this - delta;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> & box<T, N>::operator &= (point<T, N> const & p)
|
||||
{
|
||||
return *this = *this & p;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> & box<T, N>::operator |= (point<T, N> const & p)
|
||||
{
|
||||
return *this = *this | p;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> & box<T, N>::operator &= (box<T, N> const & b)
|
||||
{
|
||||
return *this = *this & b;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
box<T, N> & box<T, N>::operator |= (box<T, N> const & b)
|
||||
{
|
||||
return *this = *this | b;
|
||||
}
|
||||
|
||||
template <typename Stream, typename T, std::size_t N>
|
||||
Stream & operator << (Stream & os, box<T, N> const & b)
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
if (i != 0) os << 'x';
|
||||
os << b[i];
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
59
libs/geom/include/psemek/geom/camera.hpp
Normal file
59
libs/geom/include/psemek/geom/camera.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
struct camera
|
||||
{
|
||||
virtual matrix<float, 4, 4> projection() const = 0;
|
||||
virtual matrix<float, 4, 4> view() const = 0;
|
||||
|
||||
virtual matrix<float, 4, 4> transform() const;
|
||||
virtual point<float, 3> position() const;
|
||||
|
||||
// View direction is -axis_z(), or equivalently direction(0,0)
|
||||
// NB: these vectors are not necessarily normalized
|
||||
virtual vector<float, 3> axis_x() const;
|
||||
virtual vector<float, 3> axis_y() const;
|
||||
virtual vector<float, 3> axis_z() const;
|
||||
|
||||
// x, y are in clip-space [-1, 1]
|
||||
// NB: this vector is not necessarily normalized
|
||||
virtual vector<float, 3> direction(float x, float y) const;
|
||||
|
||||
// view frustum is the intersection of half-spaces defined by dot(clip_plane, x) >= 0
|
||||
virtual std::array<vector<float, 4>, 6> clip_planes() const;
|
||||
|
||||
virtual ~camera() = default;
|
||||
};
|
||||
|
||||
struct perspective_camera
|
||||
: camera
|
||||
{
|
||||
float fov_x;
|
||||
float fov_y;
|
||||
|
||||
float near_clip;
|
||||
float far_clip;
|
||||
|
||||
matrix<float, 4, 4> projection() const override;
|
||||
};
|
||||
|
||||
struct spherical_camera
|
||||
: perspective_camera
|
||||
{
|
||||
// assumes up is +Y
|
||||
//
|
||||
float azimuthal_angle;
|
||||
float elevation_angle;
|
||||
float distance;
|
||||
point<float, 3> target;
|
||||
|
||||
matrix<float, 4, 4> view() const override;
|
||||
};
|
||||
|
||||
}
|
||||
37
libs/geom/include/psemek/geom/contains.hpp
Normal file
37
libs/geom/include/psemek/geom/contains.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/interval.hpp>
|
||||
#include <psemek/geom/box.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/simplex.hpp>
|
||||
#include <psemek/geom/orientation.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
bool contains (interval<T> const & i, T const & x)
|
||||
{
|
||||
return (i.min <= x) && (x <= i.max);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool contains (box<T, N> const & b, point<T, N> const & p)
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
if (!contains(b[i], p[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool contains (triangle<point<T, 2>> const & t, point<T, 2> const & p)
|
||||
{
|
||||
return true
|
||||
&& orientation(t[0], t[1], p) != sign_t::negative
|
||||
&& orientation(t[1], t[2], p) != sign_t::negative
|
||||
&& orientation(t[2], t[0], p) != sign_t::negative
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
238
libs/geom/include/psemek/geom/gauss.hpp
Normal file
238
libs/geom/include/psemek/geom/gauss.hpp
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// A helper to treat vectors & matrices uniformly
|
||||
|
||||
template <typename X>
|
||||
struct gauss_helper;
|
||||
|
||||
template <typename X>
|
||||
gauss_helper (X &) -> gauss_helper<X>;
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct gauss_helper<vector<T, N>>
|
||||
{
|
||||
vector<T, N> & v;
|
||||
|
||||
static constexpr std::size_t columns ( ) { return 1; }
|
||||
T & at (std::size_t row, std::size_t) { return v[row]; }
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N, std::size_t M>
|
||||
struct gauss_helper<matrix<T, N, M>>
|
||||
{
|
||||
matrix<T, N, M> & m;
|
||||
|
||||
static constexpr std::size_t columns ( ) { return M; }
|
||||
T & at (std::size_t row, std::size_t col) { return m[row][col]; }
|
||||
};
|
||||
|
||||
template <typename F, typename ... Args>
|
||||
void for_each (F && f, Args & ... args)
|
||||
{
|
||||
(f(args), ...);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
T det (matrix<T, N, N> m)
|
||||
{
|
||||
using std::abs;
|
||||
|
||||
std::size_t sign = 0;
|
||||
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
// find maximal modulus along i-th column
|
||||
auto M = abs(m[i][i]);
|
||||
std::size_t k = i;
|
||||
for (std::size_t j = i + 1; j < N; ++j)
|
||||
{
|
||||
auto const n = abs(m[j][i]);
|
||||
if (n > M)
|
||||
{
|
||||
M = n;
|
||||
k = j;
|
||||
}
|
||||
}
|
||||
|
||||
// avoid dividing by zero
|
||||
if (M == T{})
|
||||
return T{};
|
||||
|
||||
// swap rows i & k
|
||||
if (i != k)
|
||||
{
|
||||
sign += 1;
|
||||
|
||||
for (std::size_t j = i; j < N; ++j)
|
||||
std::swap(m[i][j], m[k][j]);
|
||||
}
|
||||
|
||||
// zero out the column under i,i
|
||||
for (std::size_t k = i + 1; k < N; ++k)
|
||||
{
|
||||
auto r = m[k][i] / m[i][i];
|
||||
for (std::size_t j = i; j < N; ++j)
|
||||
{
|
||||
m[k][j] -= m[i][j] * r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// determinant is product of diagonal entries + sign
|
||||
T res = ((sign % 2) == 0) ? T{1} : -T{1};
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
res *= m[i][i];
|
||||
return res;
|
||||
}
|
||||
|
||||
// each element of the rhs can be a vector<N> or a matrix<N, K>
|
||||
// the set of equations (m * x = rhs)... is solved simultaneously column-wise
|
||||
// returns false is the matrix is degenerate
|
||||
template <typename T, std::size_t N, typename ... RHS>
|
||||
bool gauss (matrix<T, N, N> m, RHS & ... rhs)
|
||||
{
|
||||
using std::abs;
|
||||
|
||||
// forward elimination
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
// find maximal modulus along i-th column
|
||||
auto M = abs(m[i][i]);
|
||||
std::size_t k = i;
|
||||
for (std::size_t j = i + 1; j < N; ++j)
|
||||
{
|
||||
auto const n = abs(m[j][i]);
|
||||
if (n > M)
|
||||
{
|
||||
M = n;
|
||||
k = j;
|
||||
}
|
||||
}
|
||||
|
||||
// avoid dividing by zero
|
||||
if (M == T{})
|
||||
return false;
|
||||
|
||||
// swap rows i & k
|
||||
if (i != k)
|
||||
{
|
||||
for (std::size_t j = i; j < N; ++j)
|
||||
std::swap(m[i][j], m[k][j]);
|
||||
|
||||
detail::for_each([i, k](auto & rhs){
|
||||
detail::gauss_helper h{rhs};
|
||||
for (std::size_t c = 0; c < h.columns(); ++c)
|
||||
{
|
||||
std::swap(h.at(i, c), h.at(k, c));
|
||||
}
|
||||
}, rhs...);
|
||||
}
|
||||
|
||||
// make i,i equal 1
|
||||
{
|
||||
auto r = m[i][i];
|
||||
for (std::size_t j = i; j < N; ++j)
|
||||
{
|
||||
m[i][j] /= r;
|
||||
}
|
||||
|
||||
detail::for_each([i, r](auto & rhs){
|
||||
detail::gauss_helper h{rhs};
|
||||
for (std::size_t c = 0; c < h.columns(); ++c)
|
||||
{
|
||||
h.at(i, c) /= r;
|
||||
}
|
||||
}, rhs...);
|
||||
}
|
||||
|
||||
// zero out the column under i,i
|
||||
for (std::size_t k = i + 1; k < N; ++k)
|
||||
{
|
||||
auto r = m[k][i];
|
||||
for (std::size_t j = i; j < N; ++j)
|
||||
{
|
||||
m[k][j] -= m[i][j] * r;
|
||||
}
|
||||
|
||||
detail::for_each([i, k, r](auto & rhs){
|
||||
detail::gauss_helper h{rhs};
|
||||
for (std::size_t c = 0; c < h.columns(); ++c)
|
||||
{
|
||||
h.at(k, c) -= h.at(i, c) * r;
|
||||
}
|
||||
}, rhs...);
|
||||
}
|
||||
}
|
||||
|
||||
// backward elimination
|
||||
for (std::size_t i = N; i --> 0;)
|
||||
{
|
||||
for (std::size_t j = 0; j < i; ++j)
|
||||
{
|
||||
auto r = m[j][i];
|
||||
m[j][i] = T{};
|
||||
|
||||
detail::for_each([i, j, r](auto & rhs){
|
||||
detail::gauss_helper h{rhs};
|
||||
for (std::size_t c = 0; c < h.columns(); ++c)
|
||||
{
|
||||
h.at(j, c) -= r * h.at(i, c);
|
||||
}
|
||||
}, rhs...);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
std::optional<matrix<T, N, N>> inverse (matrix<T, N, N> m)
|
||||
{
|
||||
matrix<T, N, N> r = matrix<T, N, N>::identity();
|
||||
if (!gauss(m, r))
|
||||
return std::nullopt;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Least-squares solution of m*x=b with full-rank m
|
||||
template <typename T, std::size_t N, std::size_t M>
|
||||
std::optional<vector<T, M>> least_squares (matrix<T, N, M> const & m, vector<T, N> const & b)
|
||||
{
|
||||
if constexpr (N == M)
|
||||
{
|
||||
auto rhs = b;
|
||||
if (gauss(m, rhs))
|
||||
return rhs;
|
||||
return {};
|
||||
}
|
||||
else if constexpr (N < M)
|
||||
{
|
||||
auto const mt = transpose(m);
|
||||
auto rhs = b;
|
||||
if (gauss(m * mt, rhs))
|
||||
return mt * rhs;
|
||||
return {};
|
||||
}
|
||||
else // if constexpr (N > M)
|
||||
{
|
||||
auto const mt = transpose(m);
|
||||
auto rhs = mt * b;
|
||||
if (gauss(mt * m, rhs))
|
||||
return rhs;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
21
libs/geom/include/psemek/geom/gram_schmidt.hpp
Normal file
21
libs/geom/include/psemek/geom/gram_schmidt.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
void gram_schmidt()
|
||||
{}
|
||||
|
||||
template <typename V1, typename ... Vs>
|
||||
void gram_schmidt(V1 & v1, Vs & ... vs)
|
||||
{
|
||||
v1 = normalized(v1);
|
||||
|
||||
((vs -= dot(v1, vs) * v1), ...);
|
||||
|
||||
gram_schmidt(vs...);
|
||||
}
|
||||
|
||||
}
|
||||
45
libs/geom/include/psemek/geom/homogeneous.hpp
Normal file
45
libs/geom/include/psemek/geom/homogeneous.hpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D+1> homogeneous(vector<T, D> const & v)
|
||||
{
|
||||
vector<T, D+1> result;
|
||||
for (std::size_t i = 0; i < D; ++i)
|
||||
result[i] = v[i];
|
||||
result[D] = T(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D+1> homogeneous(point<T, D> const & v)
|
||||
{
|
||||
vector<T, D+1> result;
|
||||
for (std::size_t i = 0; i < D; ++i)
|
||||
result[i] = v[i];
|
||||
result[D] = T(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
matrix<T, D+1, D+1> homogeneous(matrix<T, D, D> const & m)
|
||||
{
|
||||
matrix<T, D+1, D+1> result = matrix<T, D+1, D+1>::zero();
|
||||
for (std::size_t i = 0; i < D; ++i)
|
||||
{
|
||||
for (std::size_t j = 0; j < D; ++j)
|
||||
{
|
||||
result[i][j] = m[i][j];
|
||||
}
|
||||
}
|
||||
result[D][D] = T(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
70
libs/geom/include/psemek/geom/incircle.hpp
Normal file
70
libs/geom/include/psemek/geom/incircle.hpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/sign.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/orientation.hpp>
|
||||
|
||||
#include <boost/multiprecision/gmp.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<!std::is_floating_point_v<T>, sign_t>
|
||||
in_circle (point<T, 2> const & p0, point<T, 2> const & p1, point<T, 2> const & p2, point<T, 2> const & p3)
|
||||
{
|
||||
auto proj = [](point<T, 2> const & p){
|
||||
auto const x = p[0];
|
||||
auto const y = p[1];
|
||||
return point<T, 3>{ x, y, x*x + y*y };
|
||||
};
|
||||
|
||||
return orientation(proj(p0), proj(p1), proj(p2), proj(p3));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<T>, sign_t>
|
||||
in_circle (point<T, 2> const & p0, point<T, 2> const & p1, point<T, 2> const & p2, point<T, 2> const & p3)
|
||||
{
|
||||
constexpr T error = std::numeric_limits<T>::epsilon() * T(29) / T(2);
|
||||
|
||||
T const m01 = (p0[0] - p3[0]) * (p1[1] - p3[1]);
|
||||
T const m02 = (p0[0] - p3[0]) * (p2[1] - p3[1]);
|
||||
T const m10 = (p1[0] - p3[0]) * (p0[1] - p3[1]);
|
||||
T const m12 = (p1[0] - p3[0]) * (p2[1] - p3[1]);
|
||||
T const m20 = (p2[0] - p3[0]) * (p0[1] - p3[1]);
|
||||
T const m21 = (p2[0] - p3[0]) * (p1[1] - p3[1]);
|
||||
|
||||
T const d = T(0)
|
||||
+ m01 * p2[0] * p2[0] + m01 * p2[1] * p2[1] - m01 * p3[0] * p3[0] - m01 * p3[1] * p3[1]
|
||||
- m02 * p1[0] * p1[0] - m02 * p1[1] * p1[1] + m02 * p3[0] * p3[0] + m02 * p3[1] * p3[1]
|
||||
- m10 * p2[0] * p2[0] - m10 * p2[1] * p2[1] + m10 * p3[0] * p3[0] + m10 * p3[1] * p3[1]
|
||||
+ m12 * p0[0] * p0[0] + m12 * p0[1] * p0[1] - m12 * p3[0] * p3[0] - m12 * p3[1] * p3[1]
|
||||
+ m20 * p1[0] * p1[0] + m20 * p1[1] * p1[1] - m20 * p3[0] * p3[0] - m20 * p3[1] * p3[1]
|
||||
- m21 * p0[0] * p0[0] - m21 * p0[1] * p0[1] + m21 * p3[0] * p3[0] + m21 * p3[1] * p3[1]
|
||||
;
|
||||
|
||||
T const t = T(0)
|
||||
+ std::abs(m01 * p2[0] * p2[0]) + std::abs(m01 * p2[1] * p2[1]) + std::abs(m01 * p3[0] * p3[0]) + std::abs(m01 * p3[1] * p3[1])
|
||||
+ std::abs(m02 * p1[0] * p1[0]) + std::abs(m02 * p1[1] * p1[1]) + std::abs(m02 * p3[0] * p3[0]) + std::abs(m02 * p3[1] * p3[1])
|
||||
+ std::abs(m10 * p2[0] * p2[0]) + std::abs(m10 * p2[1] * p2[1]) + std::abs(m10 * p3[0] * p3[0]) + std::abs(m10 * p3[1] * p3[1])
|
||||
+ std::abs(m12 * p0[0] * p0[0]) + std::abs(m12 * p0[1] * p0[1]) + std::abs(m12 * p3[0] * p3[0]) + std::abs(m12 * p3[1] * p3[1])
|
||||
+ std::abs(m20 * p1[0] * p1[0]) + std::abs(m20 * p1[1] * p1[1]) + std::abs(m20 * p3[0] * p3[0]) + std::abs(m20 * p3[1] * p3[1])
|
||||
+ std::abs(m21 * p0[0] * p0[0]) + std::abs(m21 * p0[1] * p0[1]) + std::abs(m21 * p3[0] * p3[0]) + std::abs(m21 * p3[1] * p3[1])
|
||||
;
|
||||
|
||||
if (d > t * error)
|
||||
return sign_t::positive;
|
||||
else if (d < - t * error)
|
||||
return sign_t::negative;
|
||||
else
|
||||
{
|
||||
using exact_type = boost::multiprecision::mpq_rational;
|
||||
|
||||
return in_circle(cast<exact_type>(p0), cast<exact_type>(p1), cast<exact_type>(p2), cast<exact_type>(p3));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
112
libs/geom/include/psemek/geom/intersection.hpp
Normal file
112
libs/geom/include/psemek/geom/intersection.hpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/simplex.hpp>
|
||||
#include <psemek/geom/orientation.hpp>
|
||||
#include <psemek/geom/contains.hpp>
|
||||
|
||||
#include <variant>
|
||||
#include <type_traits>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
// denotes empty intersection
|
||||
struct empty { };
|
||||
|
||||
template <typename Stream>
|
||||
Stream & operator << (Stream & os, empty)
|
||||
{
|
||||
return os << "empty";
|
||||
}
|
||||
|
||||
template <typename Point, typename = std::enable_if_t<Point::dimension == 2>>
|
||||
bool intersect (segment<Point> const & s0, segment<Point> const & s1)
|
||||
{
|
||||
auto const o00 = orientation(s0[0], s0[1], s1[0]);
|
||||
auto const o01 = orientation(s0[0], s0[1], s1[1]);
|
||||
auto const o10 = orientation(s1[0], s1[1], s0[0]);
|
||||
auto const o11 = orientation(s1[0], s1[1], s0[1]);
|
||||
|
||||
return ((o00 != o01) || (o00 == sign_t::zero)) && ((o10 != o11) || (o10 == sign_t::zero));
|
||||
}
|
||||
|
||||
// TODO: robust implementation
|
||||
template <typename Point, typename = std::enable_if_t<Point::dimension == 2>>
|
||||
std::variant<empty, Point, segment<Point>> intersection (segment<Point> s0, segment<Point> s1)
|
||||
{
|
||||
auto const a0 = -det(s1[0] - s0[0], s1[1] - s1[0]);
|
||||
auto const a1 = det(s0[1] - s0[0], s1[0] - s0[0]);
|
||||
auto const b = -det(s0[1] - s0[0], s1[1] - s1[0]);
|
||||
|
||||
if (b != 0)
|
||||
{
|
||||
// general case
|
||||
|
||||
auto const t0 = a0 / b;
|
||||
auto const t1 = a1 / b;
|
||||
|
||||
if (t0 < 0 || t0 > 1 || t1 < 0 || t1 > 1)
|
||||
return empty{};
|
||||
|
||||
return s0[0] + t0 * (s0[1] - s0[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// collinear segments
|
||||
|
||||
if (a0 != 0)
|
||||
{
|
||||
// segments do not lie on the same line: no intersection
|
||||
return empty{};
|
||||
}
|
||||
|
||||
// if segments are not Y-axis aligned, safe to use X-coordinates to sort them (k = 0)
|
||||
// otherwise use Y-coordinates (k = 1)
|
||||
std::size_t const k = (s0[0][0] != s0[1][0]) ? 0 : 1;
|
||||
|
||||
if (s0[0][k] > s0[1][k])
|
||||
std::swap(s0[0], s0[1]);
|
||||
|
||||
if (s1[0][k] > s1[1][k])
|
||||
std::swap(s1[0], s1[1]);
|
||||
|
||||
auto const r0 = std::max(s0[0][k], s1[0][k]);
|
||||
auto const r1 = std::min(s0[1][k], s1[1][k]);
|
||||
|
||||
if (r0 > r1)
|
||||
return empty{};
|
||||
|
||||
bool const s0_is_first = s0[0][k] < s1[0][k];
|
||||
|
||||
if (r0 == r1)
|
||||
{
|
||||
Point p;
|
||||
p[k] = r0;
|
||||
p[1 - k] = s0_is_first ? s0[1][1 - k] : s1[1][1 - k];
|
||||
return p;
|
||||
}
|
||||
else if (s0_is_first)
|
||||
return segment{ s1[0], s0[1] };
|
||||
else
|
||||
return segment{ s0[0], s1[1] };
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool intersect(triangle<point<T, 2>> const & t0, triangle<point<T, 2>> const & t1)
|
||||
{
|
||||
if (contains(t0, t1[0]) || contains(t0, t1[1]) || contains(t0, t1[2])) return true;
|
||||
|
||||
if (contains(t1, t0[0]) || contains(t1, t0[1]) || contains(t1, t0[2])) return true;
|
||||
|
||||
if (intersect(segment{t0[0], t0[1]}, segment{t1[0], t1[1]})) return true;
|
||||
if (intersect(segment{t0[0], t0[1]}, segment{t1[1], t1[2]})) return true;
|
||||
if (intersect(segment{t0[1], t0[2]}, segment{t1[0], t1[1]})) return true;
|
||||
if (intersect(segment{t0[1], t0[2]}, segment{t1[1], t1[2]})) return true;
|
||||
if (intersect(segment{t0[2], t0[0]}, segment{t1[0], t1[1]})) return true;
|
||||
if (intersect(segment{t0[2], t0[0]}, segment{t1[1], t1[2]})) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
222
libs/geom/include/psemek/geom/interval.hpp
Normal file
222
libs/geom/include/psemek/geom/interval.hpp
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <cmath>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
// Can be specialized in client code
|
||||
template <typename T>
|
||||
struct limits
|
||||
{
|
||||
static constexpr T min ( )
|
||||
{
|
||||
if constexpr (std::is_floating_point_v<T>)
|
||||
{
|
||||
return -std::numeric_limits<T>::infinity();
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::numeric_limits<T>::min();
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr T max ( )
|
||||
{
|
||||
if constexpr (std::is_floating_point_v<T>)
|
||||
{
|
||||
return std::numeric_limits<T>::infinity();
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::numeric_limits<T>::max();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct interval_iterator
|
||||
{
|
||||
T value;
|
||||
|
||||
T operator* () const { return value; }
|
||||
|
||||
interval_iterator & operator++ ()
|
||||
{
|
||||
++value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
interval_iterator operator++ (int)
|
||||
{
|
||||
auto copy = *this;
|
||||
++value;
|
||||
return copy;
|
||||
}
|
||||
|
||||
friend bool operator == (interval_iterator const & i1, interval_iterator const & i2)
|
||||
{
|
||||
return i1.value == i2.value;
|
||||
}
|
||||
|
||||
friend bool operator != (interval_iterator const & i1, interval_iterator const & i2)
|
||||
{
|
||||
return !(i1 == i2);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct interval
|
||||
{
|
||||
T min = limits<T>::max();
|
||||
T max = limits<T>::min();
|
||||
|
||||
static interval singleton (T const & value)
|
||||
{
|
||||
return {value, value};
|
||||
}
|
||||
|
||||
bool empty ( ) const
|
||||
{
|
||||
return min > max;
|
||||
}
|
||||
|
||||
T length ( ) const
|
||||
{
|
||||
return empty() ? T{} : max - min;
|
||||
}
|
||||
|
||||
using iterator = interval_iterator<T>;
|
||||
using const_iterator = iterator;
|
||||
|
||||
iterator begin() const { return {min}; }
|
||||
iterator end() const { return {max}; }
|
||||
|
||||
interval & operator += (T const & delta);
|
||||
interval & operator -= (T const & delta);
|
||||
|
||||
interval & operator &= (T const & a);
|
||||
interval & operator |= (T const & a);
|
||||
|
||||
interval & operator &= (interval const & i);
|
||||
interval & operator |= (interval const & i);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
interval<T> operator + (interval<T> const & i, T const & delta)
|
||||
{
|
||||
return {i.min + delta, i.max + delta};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> operator + (T const & delta, interval<T> const & i)
|
||||
{
|
||||
return {delta + i.min, delta + i.max};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> operator - (interval<T> const & i, T const & delta)
|
||||
{
|
||||
return {i.min - delta, i.max - delta};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> operator & (interval<T> const & i, T const & a)
|
||||
{
|
||||
using std::min;
|
||||
using std::max;
|
||||
return {max(i.min, a), min(i.max, a)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> operator & (T const & a, interval<T> const & i)
|
||||
{
|
||||
using std::min;
|
||||
using std::max;
|
||||
return {max(a, i.min), min(a, i.max)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> operator | (interval<T> const & i, T const & a)
|
||||
{
|
||||
using std::min;
|
||||
using std::max;
|
||||
return {min(i.min, a), max(i.max, a)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> operator | (T const & a, interval<T> const & i)
|
||||
{
|
||||
using std::min;
|
||||
using std::max;
|
||||
return {min(a, i.min), max(a, i.max)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> operator & (interval<T> const & i1, interval<T> const & i2)
|
||||
{
|
||||
using std::min;
|
||||
using std::max;
|
||||
return {max(i1.min, i2.min), min(i1.max, i2.max)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> operator | (interval<T> const & i1, interval<T> const & i2)
|
||||
{
|
||||
using std::min;
|
||||
using std::max;
|
||||
return {min(i1.min, i2.min), max(i1.max, i2.max)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> & interval<T>::operator += (T const & delta)
|
||||
{
|
||||
return *this = *this + delta;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> & interval<T>::operator -= (T const & delta)
|
||||
{
|
||||
return *this = *this - delta;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> & interval<T>::operator &= (T const & a)
|
||||
{
|
||||
return *this = *this & a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> & interval<T>::operator |= (T const & a)
|
||||
{
|
||||
return *this = *this | a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> & interval<T>::operator &= (interval<T> const & i)
|
||||
{
|
||||
return *this = *this & i;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
interval<T> & interval<T>::operator |= (interval<T> const & i)
|
||||
{
|
||||
return *this = *this | i;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T clamp (T x, interval<T> const & i)
|
||||
{
|
||||
return std::max(i.min, std::min(i.max, x));
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
Stream & operator << (Stream & os, interval<T> const & i)
|
||||
{
|
||||
return os << '[' << i.min << " .. " << i.max << ']';
|
||||
}
|
||||
|
||||
}
|
||||
238
libs/geom/include/psemek/geom/matrix.hpp
Normal file
238
libs/geom/include/psemek/geom/matrix.hpp
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
struct matrix
|
||||
{
|
||||
static constexpr std::size_t rows = R;
|
||||
static constexpr std::size_t columns = C;
|
||||
|
||||
using scalar_type = T;
|
||||
|
||||
T coords[R * C];
|
||||
|
||||
T * operator [] (std::size_t i)
|
||||
{
|
||||
return coords + C * i;
|
||||
}
|
||||
|
||||
T const * operator [] (std::size_t i) const
|
||||
{
|
||||
return coords + C * i;
|
||||
}
|
||||
|
||||
matrix & operator *= (T const & s);
|
||||
matrix & operator /= (T const & s);
|
||||
|
||||
matrix & operator += (matrix const & v);
|
||||
matrix & operator -= (matrix const & v);
|
||||
|
||||
static matrix zero ( );
|
||||
static matrix identity ( );
|
||||
static matrix scalar (T const & s);
|
||||
};
|
||||
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> matrix<T, R, C>::zero ( )
|
||||
{
|
||||
matrix<T, R, C> m;
|
||||
for (std::size_t i = 0; i < R * C; ++i)
|
||||
m.coords[i] = 0;
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> matrix<T, R, C>::identity ( )
|
||||
{
|
||||
return scalar(T{1});
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> matrix<T, R, C>::scalar (T const & s)
|
||||
{
|
||||
matrix<T, R, C> m = zero();
|
||||
for (std::size_t i = 0; i < std::min(R, C); ++i)
|
||||
for (std::size_t j = 0; j < C; ++j)
|
||||
m[i][i] = s;
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
bool operator == (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
|
||||
{
|
||||
return std::equal(m1.coords, m1.coords + R * C, m2.coords);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
bool operator != (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
|
||||
{
|
||||
return !(m1 == m2);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
bool operator < (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
|
||||
{
|
||||
return std::lexicographical_compare(m1.coords, m1.coords + R * C, m2.coords, m2.coords + R * C);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
bool operator > (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
|
||||
{
|
||||
return m2 < m1;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
bool operator <= (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
|
||||
{
|
||||
return !(m2 < m1);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
bool operator >= (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
|
||||
{
|
||||
return !(m1 < m2);
|
||||
}
|
||||
|
||||
template <typename T1, typename T, std::size_t R, std::size_t C>
|
||||
matrix<T1, R, C> cast (matrix<T, R, C> const & m)
|
||||
{
|
||||
matrix<T1, R, C> r;
|
||||
for (std::size_t i = 0; i < R * C; ++i)
|
||||
r.coords[i] = static_cast<T1>(m.coords[i]);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> operator * (matrix<T, R, C> const & m, T const & s)
|
||||
{
|
||||
matrix<T, R, C> r;
|
||||
for (std::size_t i = 0; i < R * C; ++i)
|
||||
r.coords[i] = m.coords[i] * s;
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> operator * (T const & s, matrix<T, R, C> const & m)
|
||||
{
|
||||
matrix<T, R, C> r;
|
||||
for (std::size_t i = 0; i < R * C; ++i)
|
||||
r.coords[i] = s * m.coords[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> operator / (matrix<T, R, C> const & m, T const & s)
|
||||
{
|
||||
matrix<T, R, C> r;
|
||||
for (std::size_t i = 0; i < R * C; ++i)
|
||||
r.coords[i] = m.coords[i] / s;
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> & matrix<T, R, C>::operator *= (T const & s)
|
||||
{
|
||||
*this = *this * s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> & matrix<T, R, C>::operator /= (T const & s)
|
||||
{
|
||||
*this = *this / s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> operator + (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
|
||||
{
|
||||
matrix<T, R, C> r;
|
||||
for (std::size_t i = 0; i < R * C; ++i)
|
||||
r.coords[i] = m1.coords[i] + m2.coords[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> operator - (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
|
||||
{
|
||||
matrix<T, R, C> r;
|
||||
for (std::size_t i = 0; i < R * C; ++i)
|
||||
r.coords[i] = m1.coords[i] - m2.coords[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> & matrix<T, R, C>::operator += (matrix<T, R, C> const & m)
|
||||
{
|
||||
*this = *this + m;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, R, C> & matrix<T, R, C>::operator -= (matrix<T, R, C> const & m)
|
||||
{
|
||||
*this = *this - m;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
vector<T, R> operator * (matrix<T, R, C> const & m, vector<T, C> const & v)
|
||||
{
|
||||
vector<T, R> r;
|
||||
for (std::size_t i = 0; i < R; ++i)
|
||||
{
|
||||
r[i] = T{};
|
||||
for (std::size_t j = 0; j < C; ++j)
|
||||
r[i] += m[i][j] * v[j];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
vector<T, C> operator * (vector<T, R> const & v, matrix<T, R, C> const & m)
|
||||
{
|
||||
vector<T, C> r;
|
||||
for (std::size_t j = 0; j < C; ++j)
|
||||
{
|
||||
r[j] = T{};
|
||||
for (std::size_t i = 0; i < R; ++i)
|
||||
r[i] += v[j] * m[i][j];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t K, std::size_t C>
|
||||
matrix<T, R, C> operator * (matrix<T, R, K> const & m1, matrix<T, K, C> const & m2)
|
||||
{
|
||||
matrix<T, R, C> r;
|
||||
for (std::size_t i = 0; i < R; ++i)
|
||||
{
|
||||
for (std::size_t j = 0; j < C; ++j)
|
||||
{
|
||||
r[i][j] = T{};
|
||||
|
||||
for (std::size_t k = 0; k < K; ++k)
|
||||
r[i][j] += m1[i][k] * m2[k][j];
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t R, std::size_t C>
|
||||
matrix<T, C, R> transpose (matrix<T, R, C> const & m)
|
||||
{
|
||||
matrix<T, C, R> r;
|
||||
for (std::size_t i = 0; i < R; ++i)
|
||||
for (std::size_t j = 0; j < C; ++j)
|
||||
r[j][i] = m[i][j];
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
||||
67
libs/geom/include/psemek/geom/orientation.hpp
Normal file
67
libs/geom/include/psemek/geom/orientation.hpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/sign.hpp>
|
||||
|
||||
#include <boost/multiprecision/gmp.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
// TODO: generic implementation
|
||||
template <typename T>
|
||||
std::enable_if_t<!std::is_floating_point_v<T>, sign_t>
|
||||
orientation (point<T, 2> const & p0, point<T, 2> const & p1, point<T, 2> const & p2)
|
||||
{
|
||||
T const d = det(p1 - p0, p2 - p0);
|
||||
|
||||
if (d > T{})
|
||||
return sign_t::positive;
|
||||
else if (d < T{})
|
||||
return sign_t::negative;
|
||||
else
|
||||
return sign_t::zero;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<T>, sign_t>
|
||||
orientation (point<T, 2> const & p0, point<T, 2> const & p1, point<T, 2> const & p2)
|
||||
{
|
||||
constexpr T error = std::numeric_limits<T>::epsilon() * T(5) / T(2);
|
||||
|
||||
T const d = (p1[0] - p0[0]) * (p2[1] - p0[1])
|
||||
- (p1[1] - p0[1]) * (p2[0] - p0[0]);
|
||||
|
||||
T const t = std::abs((p1[0] - p0[0]) * (p2[1] - p0[1]))
|
||||
+ std::abs((p1[1] - p0[1]) * (p2[0] - p0[0]));
|
||||
|
||||
if (d > t * error)
|
||||
return sign_t::positive;
|
||||
else if (d < - t * error)
|
||||
return sign_t::negative;
|
||||
else
|
||||
{
|
||||
using exact_type = boost::multiprecision::mpq_rational;
|
||||
|
||||
return orientation(cast<exact_type>(p0), cast<exact_type>(p1), cast<exact_type>(p2));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
sign_t orientation (point<T, 3> const & p0, point<T, 3> const & p1, point<T, 3> const & p2, point<T, 3> const & p3)
|
||||
{
|
||||
T const d = det(p0 - p3, p1 - p3, p2 - p3);
|
||||
|
||||
if (d > T{})
|
||||
return sign_t::positive;
|
||||
else if (d < T{})
|
||||
return sign_t::negative;
|
||||
else
|
||||
return sign_t::zero;
|
||||
}
|
||||
|
||||
}
|
||||
92
libs/geom/include/psemek/geom/orthographic.hpp
Normal file
92
libs/geom/include/psemek/geom/orthographic.hpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/box.hpp>
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
#include <psemek/geom/homogeneous.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
struct orthographic
|
||||
{
|
||||
using vector_type = vector<T, D>;
|
||||
using point_type = point<T, D>;
|
||||
using homogeneous_matrix_type = matrix<T, D + 1, D + 1>;
|
||||
using box_type = box<T, D>;
|
||||
|
||||
orthographic();
|
||||
orthographic(box_type r);
|
||||
|
||||
box_type box() const;
|
||||
box_type box(box_type r);
|
||||
|
||||
vector_type operator() (vector_type v) const;
|
||||
point_type operator() (point_type p) const;
|
||||
|
||||
homogeneous_matrix_type homogeneous_matrix() const;
|
||||
|
||||
orthographic inverse() const;
|
||||
|
||||
private:
|
||||
box_type r_;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
orthographic<T, D>::orthographic()
|
||||
{
|
||||
for (std::size_t i = 0; i < D; ++i)
|
||||
{
|
||||
r_[i] = {T(-1), T(1)};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
orthographic<T, D>::orthographic(box_type r)
|
||||
: r_(r)
|
||||
{ }
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
box<T, D> orthographic<T, D>::box() const
|
||||
{
|
||||
return r_;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
box<T, D> orthographic<T, D>::box(box_type r)
|
||||
{
|
||||
std::swap(r, r_);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D> orthographic<T, D>::operator() (vector<T, D> v) const
|
||||
{
|
||||
return homogeneous_matrix() * homogeneous(v);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
point<T, D> orthographic<T, D>::operator() (point<T, D> p) const
|
||||
{
|
||||
return homogeneous_matrix() * homogeneous(p);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
matrix<T, D + 1, D + 1> orthographic<T, D>::homogeneous_matrix() const
|
||||
{
|
||||
auto m = matrix<T, D + 1, D + 1>::zero();
|
||||
|
||||
for (std::size_t d = 0; d < D; ++d)
|
||||
m[d][d] = T(2) / (r_[d].max - r_[d].min);
|
||||
|
||||
for (std::size_t d = 0; d < D; ++d)
|
||||
m[d][D] = - (r_[d].max + r_[d].min) / (r_[d].max - r_[d].min);
|
||||
|
||||
m[D][D] = T(1);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
}
|
||||
59
libs/geom/include/psemek/geom/permutation.hpp
Normal file
59
libs/geom/include/psemek/geom/permutation.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
struct swap
|
||||
{
|
||||
using vector_type = vector<T, D>;
|
||||
using point_type = point<T, D>;
|
||||
using matrix_type = matrix<T, D, D>;
|
||||
|
||||
swap(std::size_t i, std::size_t j);
|
||||
|
||||
vector_type operator() (vector_type v) const;
|
||||
|
||||
matrix_type matrix() const;
|
||||
|
||||
swap inverse() const;
|
||||
|
||||
private:
|
||||
std::size_t i_, j_;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
swap<T, D>::swap (std::size_t i, std::size_t j)
|
||||
: i_(i)
|
||||
, j_(j)
|
||||
{ }
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D> swap<T, D>::operator() (vector_type v) const
|
||||
{
|
||||
std::swap(v[i_], v[j_]);
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
matrix<T, D, D> swap<T, D>::matrix() const
|
||||
{
|
||||
matrix_type m = matrix_type::identity();
|
||||
m[i_][i_] = 0;
|
||||
m[j_][j_] = 0;
|
||||
m[i_][j_] = 1;
|
||||
m[j_][i_] = 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
swap<T, D> swap<T, D>::inverse() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
61
libs/geom/include/psemek/geom/perspective.hpp
Normal file
61
libs/geom/include/psemek/geom/perspective.hpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
struct perspective;
|
||||
|
||||
template <typename T>
|
||||
struct perspective<T, 3>
|
||||
{
|
||||
using scalar_type = T;
|
||||
using homogeneous_matrix_type = matrix<T, 4, 4>;
|
||||
|
||||
// fov are in radians
|
||||
perspective (scalar_type fov_x, scalar_type fov_y, scalar_type near, scalar_type far);
|
||||
|
||||
perspective (scalar_type left, scalar_type right, scalar_type bottom, scalar_type top, scalar_type near, scalar_type far);
|
||||
|
||||
homogeneous_matrix_type homogeneous_matrix() const;
|
||||
|
||||
private:
|
||||
homogeneous_matrix_type homogeneous_matrix_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
perspective<T, 3>::perspective(scalar_type fov_x, scalar_type fov_y, scalar_type near, scalar_type far)
|
||||
: homogeneous_matrix_(homogeneous_matrix_type::zero())
|
||||
{
|
||||
scalar_type dx = 1 / std::tan(fov_x / 2);
|
||||
scalar_type dy = 1 / std::tan(fov_y / 2);
|
||||
homogeneous_matrix_[0][0] = dx;
|
||||
homogeneous_matrix_[1][1] = dy;
|
||||
homogeneous_matrix_[2][2] = (near + far) / (near - far);
|
||||
homogeneous_matrix_[2][3] = 2 * near * far / (near - far);
|
||||
homogeneous_matrix_[3][2] = -1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
perspective<T, 3>::perspective(scalar_type left, scalar_type right, scalar_type bottom, scalar_type top, scalar_type near, scalar_type far)
|
||||
: homogeneous_matrix_(homogeneous_matrix_type::zero())
|
||||
{
|
||||
homogeneous_matrix_[0][0] = 2 * near / (right - left);
|
||||
homogeneous_matrix_[1][1] = 2 * near / (top - bottom);
|
||||
homogeneous_matrix_[0][2] = (right + left) / (right - left);
|
||||
homogeneous_matrix_[1][2] = (top + bottom) / (top - bottom);
|
||||
homogeneous_matrix_[2][2] = - (far + near) / (far - near);
|
||||
homogeneous_matrix_[2][3] = - 2 * far * near / (far - near);
|
||||
homogeneous_matrix_[3][2] = -1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
matrix<T, 4, 4> perspective<T, 3>::homogeneous_matrix() const
|
||||
{
|
||||
return homogeneous_matrix_;
|
||||
}
|
||||
|
||||
}
|
||||
173
libs/geom/include/psemek/geom/point.hpp
Normal file
173
libs/geom/include/psemek/geom/point.hpp
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct point
|
||||
{
|
||||
static constexpr std::size_t dimension = N;
|
||||
|
||||
using scalar_type = T;
|
||||
|
||||
T coords[N];
|
||||
|
||||
point ( ) = default;
|
||||
point (point const &) = default;
|
||||
point (point &) = default;
|
||||
point (point &&) = default;
|
||||
point & operator = (point const &) = default;
|
||||
point & operator = (point &) = default;
|
||||
point & operator = (point &&) = default;
|
||||
|
||||
template <typename ... Args>
|
||||
point (Args && ... args)
|
||||
: coords{ std::forward<Args>(args)... }
|
||||
{
|
||||
static_assert(sizeof...(Args) == N);
|
||||
}
|
||||
|
||||
T & operator[] (std::size_t i)
|
||||
{
|
||||
return coords[i];
|
||||
}
|
||||
|
||||
T const & operator[] (std::size_t i) const
|
||||
{
|
||||
return coords[i];
|
||||
}
|
||||
|
||||
point & operator += (vector<T, N> const & v);
|
||||
point & operator -= (vector<T, N> const & v);
|
||||
};
|
||||
|
||||
template <typename ... Args>
|
||||
point (Args && ...) -> point<std::common_type_t<Args...>, sizeof...(Args)>;
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator == (point<T, N> const & p1, point<T, N> const & p2)
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
if (p1[i] != p2[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator != (point<T, N> const & p1, point<T, N> const & p2)
|
||||
{
|
||||
return !(p1 == p2);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator < (point<T, N> const & p1, point<T, N> const & p2)
|
||||
{
|
||||
return std::lexicographical_compare(p1.coords, p1.coords + N, p2.coords, p2.coords + N);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator > (point<T, N> const & p1, point<T, N> const & p2)
|
||||
{
|
||||
return p2 < p1;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator <= (point<T, N> const & p1, point<T, N> const & p2)
|
||||
{
|
||||
return !(p2 < p1);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator >= (point<T, N> const & p1, point<T, N> const & p2)
|
||||
{
|
||||
return !(p1 < p2);
|
||||
}
|
||||
|
||||
template <typename T1, typename T, std::size_t N>
|
||||
point<T1, N> cast (point<T, N> const & p)
|
||||
{
|
||||
point<T1, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = T1(p[i]);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
point<T, N> operator + (point<T, N> const & p, vector<T, N> const & v)
|
||||
{
|
||||
point<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = p[i] + v[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
point<T, N> operator + (vector<T, N> const & v, point<T, N> const & p)
|
||||
{
|
||||
point<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = v[i] + p[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
point<T, N> operator - (point<T, N> const & p, vector<T, N> const & v)
|
||||
{
|
||||
point<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = p[i] - v[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator - (point<T, N> const & p1, point<T, N> const & p2)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = p1[i] - p2[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
point<T, N> & point<T, N>::operator += (vector<T, N> const & v)
|
||||
{
|
||||
return (*this) = (*this) + v;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
point<T, N> & point<T, N>::operator -= (vector<T, N> const & v)
|
||||
{
|
||||
return (*this) = (*this) - v;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
point<T, N> lerp (point<T, N> const & p0, point<T, N> const & p1, T const & t)
|
||||
{
|
||||
return p0 + t * (p1 - p0);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
T distance_sqr (point<T, N> const & p1, point<T, N> const & p2)
|
||||
{
|
||||
return length_sqr(p2 - p1);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
T distance (point<T, N> const & p1, point<T, N> const & p2)
|
||||
{
|
||||
return length(p2 - p1);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T, std::size_t N>
|
||||
Stream & operator << (Stream & os, point<T, N> const & p)
|
||||
{
|
||||
os << '(' << p[0];
|
||||
for (std::size_t i = 1; i < N; ++i)
|
||||
os << ", " << p[i];
|
||||
os << ')';
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
184
libs/geom/include/psemek/geom/rotation.hpp
Normal file
184
libs/geom/include/psemek/geom/rotation.hpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
#include <psemek/geom/homogeneous.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
// Rotation in oriented plane (i,j)
|
||||
template <typename T, std::size_t D>
|
||||
struct plane_rotation
|
||||
{
|
||||
using scalar_type = T;
|
||||
using vector_type = geom::vector<T, D>;
|
||||
using point_type = geom::point<T, D>;
|
||||
using matrix_type = geom::matrix<T, D, D>;
|
||||
|
||||
plane_rotation(std::size_t i, std::size_t j, T angle = T(0));
|
||||
|
||||
scalar_type angle() const;
|
||||
scalar_type angle(scalar_type a);
|
||||
|
||||
vector_type operator() (vector_type v) const;
|
||||
|
||||
matrix_type matrix() const;
|
||||
|
||||
plane_rotation inverse() const;
|
||||
|
||||
private:
|
||||
std::size_t const i_;
|
||||
std::size_t const j_;
|
||||
T angle_;
|
||||
};
|
||||
|
||||
// 3D-rotation around an axis
|
||||
template <typename T>
|
||||
struct axis_rotation
|
||||
{
|
||||
using scalar_type = T;
|
||||
using vector_type = geom::vector<T, 3>;
|
||||
using point_type = geom::point<T, 3>;
|
||||
using matrix_type = geom::matrix<T, 3, 3>;
|
||||
|
||||
axis_rotation();
|
||||
axis_rotation(vector_type axis, scalar_type angle);
|
||||
|
||||
vector_type axis() const;
|
||||
vector_type axis(vector_type a);
|
||||
|
||||
scalar_type angle() const;
|
||||
scalar_type angle(scalar_type a);
|
||||
|
||||
vector_type operator() (vector_type v) const;
|
||||
|
||||
matrix_type matrix() const;
|
||||
|
||||
axis_rotation inverse() const;
|
||||
|
||||
private:
|
||||
vector_type axis_;
|
||||
scalar_type angle_;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
plane_rotation<T, D>::plane_rotation(std::size_t i, std::size_t j, T angle)
|
||||
: i_(i), j_(j), angle_(angle)
|
||||
{ }
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
T plane_rotation<T, D>::angle() const
|
||||
{
|
||||
return angle_;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
T plane_rotation<T, D>::angle(T a)
|
||||
{
|
||||
T t = angle_;
|
||||
angle_ = a;
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D> plane_rotation<T, D>::operator() (vector_type v) const
|
||||
{
|
||||
T vi = v[i_] * std::cos(angle_) - v[j_] * std::sin(angle_);
|
||||
T vj = v[i_] * std::sin(angle_) + v[j_] * std::cos(angle_);
|
||||
v[i_] = vi;
|
||||
v[j_] = vj;
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
matrix<T, D, D> plane_rotation<T, D>::matrix() const
|
||||
{
|
||||
matrix_type m = matrix_type::identity();
|
||||
m[i_][i_] = std::cos(angle_);
|
||||
m[i_][j_] = -std::sin(angle_);
|
||||
m[j_][i_] = std::sin(angle_);
|
||||
m[j_][j_] = std::cos(angle_);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
plane_rotation<T, D> plane_rotation<T, D>::inverse() const
|
||||
{
|
||||
return {i_, j_, -angle_};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
axis_rotation<T>::axis_rotation()
|
||||
: axis_rotation(vector_type{T(0), T(0), T(1)}, T(0))
|
||||
{ }
|
||||
|
||||
template <typename T>
|
||||
axis_rotation<T>::axis_rotation(vector_type axis, scalar_type angle)
|
||||
: axis_(axis)
|
||||
, angle_(angle)
|
||||
{ }
|
||||
|
||||
template <typename T>
|
||||
vector<T, 3> axis_rotation<T>::axis() const
|
||||
{
|
||||
return axis_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
vector<T, 3> axis_rotation<T>::axis(vector_type a)
|
||||
{
|
||||
auto t = axis_;
|
||||
axis_ = a;
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T axis_rotation<T>::angle ( ) const
|
||||
{
|
||||
return angle_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T axis_rotation<T>::angle(scalar_type a)
|
||||
{
|
||||
auto t = angle_;
|
||||
angle_ = a;
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
vector<T, 3> axis_rotation<T>::operator() (vector_type v) const
|
||||
{
|
||||
return matrix() * v;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
matrix<T, 3, 3> axis_rotation<T>::matrix() const
|
||||
{
|
||||
matrix_type m = matrix_type::identity();
|
||||
T c = std::cos(angle_);
|
||||
T s = std::sin(angle_);
|
||||
T x = axis_[0];
|
||||
T y = axis_[1];
|
||||
T z = axis_[2];
|
||||
m[0][0] = c + x * x * (1 - c);
|
||||
m[0][1] = x * y * (1 - c) - z * s;
|
||||
m[0][2] = x * z * (1 - c) + y * s;
|
||||
m[1][0] = y * x * (1 - c) + z * s;
|
||||
m[1][1] = c + y * y * (1 - c);
|
||||
m[1][2] = y * z * (1 - c) - x * s;
|
||||
m[2][0] = z * x * (1 - c) - y * s;
|
||||
m[2][1] = z * y * (1 - c) + x * s;
|
||||
m[2][2] = c + z * z * (1 - c);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
axis_rotation<T> axis_rotation<T>::inverse() const
|
||||
{
|
||||
return {axis_, -angle_};
|
||||
}
|
||||
|
||||
}
|
||||
93
libs/geom/include/psemek/geom/scale.hpp
Normal file
93
libs/geom/include/psemek/geom/scale.hpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
struct scale
|
||||
{
|
||||
using vector_type = vector<T, D>;
|
||||
using point_type = point<T, D>;
|
||||
using matrix_type = matrix<T, D, D>;
|
||||
|
||||
scale();
|
||||
scale(T v);
|
||||
scale(vector_type v);
|
||||
|
||||
vector_type vector() const;
|
||||
vector_type vector(vector_type v);
|
||||
|
||||
vector_type operator() (vector_type v) const;
|
||||
|
||||
matrix_type matrix() const;
|
||||
|
||||
scale inverse() const;
|
||||
|
||||
private:
|
||||
vector_type v_;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
scale<T, D>::scale()
|
||||
{
|
||||
for (std::size_t i = 0; i < D; ++i)
|
||||
v_[i] = T(1);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
scale<T, D>::scale(T v)
|
||||
{
|
||||
for (std::size_t i = 0; i < D; ++i)
|
||||
v_[i] = v;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
scale<T, D>::scale(vector_type v)
|
||||
: v_(v)
|
||||
{ }
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D> scale<T, D>::vector() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D> scale<T, D>::vector(vector_type v)
|
||||
{
|
||||
auto t = v_;
|
||||
v_ = v;
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D> scale<T, D>::operator() (vector_type v) const
|
||||
{
|
||||
for (std::size_t i = 0; i < D; ++i)
|
||||
v[i] *= v_[i];
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
matrix<T, D, D> scale<T, D>::matrix() const
|
||||
{
|
||||
matrix_type m = matrix_type::identity();
|
||||
for (std::size_t i = 0; i < D; ++i)
|
||||
m[i][i] = v_[i];
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
scale<T, D> scale<T, D>::inverse() const
|
||||
{
|
||||
vector_type v;
|
||||
for (std::size_t i = 0; i < D; ++i)
|
||||
v[i] = T(1) / v_[i];
|
||||
return {v};
|
||||
}
|
||||
|
||||
}
|
||||
26
libs/geom/include/psemek/geom/sign.hpp
Normal file
26
libs/geom/include/psemek/geom/sign.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
enum class sign_t : int
|
||||
{
|
||||
positive = 1,
|
||||
zero = 0,
|
||||
negative = -1,
|
||||
};
|
||||
|
||||
template <typename OStream>
|
||||
OStream & operator << (OStream & o, sign_t s)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case sign_t::positive: return o << "positive";
|
||||
case sign_t::zero: return o << "zero";
|
||||
case sign_t::negative: return o << "negative";
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
}
|
||||
56
libs/geom/include/psemek/geom/simplex.hpp
Normal file
56
libs/geom/include/psemek/geom/simplex.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename Point, std::size_t K>
|
||||
struct simplex
|
||||
{
|
||||
using point_type = Point;
|
||||
|
||||
point_type points[K + 1];
|
||||
|
||||
point_type & operator[] (std::size_t i)
|
||||
{
|
||||
return points[i];
|
||||
}
|
||||
|
||||
point_type const & operator[] (std::size_t i) const
|
||||
{
|
||||
return points[i];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ... Args>
|
||||
simplex (Args ...) -> simplex<std::common_type_t<Args...>, sizeof...(Args) - 1>;
|
||||
|
||||
template <typename Point>
|
||||
struct segment
|
||||
: simplex<Point, 1>
|
||||
{ };
|
||||
|
||||
template <typename Point>
|
||||
segment (Point, Point) -> segment<Point>;
|
||||
|
||||
template <typename Point>
|
||||
struct triangle
|
||||
: simplex<Point, 2>
|
||||
{ };
|
||||
|
||||
template <typename Point>
|
||||
triangle (Point, Point, Point) -> triangle<Point>;
|
||||
|
||||
template <typename Stream, typename Point, std::size_t K>
|
||||
Stream & operator << (Stream & os, simplex<Point, K> const & s)
|
||||
{
|
||||
os << '(' << s[0];
|
||||
for (std::size_t i = 1; i <= K; ++i)
|
||||
os << ", " << s[i];
|
||||
return os << ')';
|
||||
}
|
||||
|
||||
}
|
||||
85
libs/geom/include/psemek/geom/translation.hpp
Normal file
85
libs/geom/include/psemek/geom/translation.hpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
struct translation
|
||||
{
|
||||
using vector_type = geom::vector<T, D>;
|
||||
using point_type = geom::point<T, D>;
|
||||
using homogeneous_matrix_type = geom::matrix<T, D + 1, D + 1>;
|
||||
|
||||
translation();
|
||||
translation(vector_type v);
|
||||
|
||||
vector_type vector() const;
|
||||
vector_type vector(vector_type v);
|
||||
|
||||
vector_type operator() (vector_type v) const;
|
||||
point_type operator() (point_type p) const;
|
||||
|
||||
homogeneous_matrix_type homogeneous_matrix() const;
|
||||
|
||||
translation inverse() const;
|
||||
|
||||
private:
|
||||
vector_type v_;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
translation<T, D>::translation()
|
||||
: translation(vector_type::zero())
|
||||
{ }
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
translation<T, D>::translation(vector_type v)
|
||||
: v_(v)
|
||||
{ }
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D> translation<T, D>::vector() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D> translation<T, D>::vector(vector_type v)
|
||||
{
|
||||
auto t = v_;
|
||||
v_ = v;
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
vector<T, D> translation<T, D>::operator() (vector_type v) const
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
point<T, D> translation<T, D>::operator() (point_type p) const
|
||||
{
|
||||
return p + v_;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
matrix<T, D + 1, D + 1> translation<T, D>::homogeneous_matrix() const
|
||||
{
|
||||
homogeneous_matrix_type m = homogeneous_matrix_type::identity();
|
||||
for (std::size_t i = 0; i < D; ++i)
|
||||
m[i][D] = v_[i];
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t D>
|
||||
translation<T, D> translation<T, D>::inverse() const
|
||||
{
|
||||
return {-v_};
|
||||
}
|
||||
|
||||
}
|
||||
268
libs/geom/include/psemek/geom/vector.hpp
Normal file
268
libs/geom/include/psemek/geom/vector.hpp
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <cmath>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct vector
|
||||
{
|
||||
static constexpr std::size_t dimension = N;
|
||||
|
||||
using scalar_type = T;
|
||||
|
||||
T coords[N];
|
||||
|
||||
vector ( ) = default;
|
||||
vector (vector const &) = default;
|
||||
vector (vector &) = default;
|
||||
vector (vector &&) = default;
|
||||
vector & operator = (vector const &) = default;
|
||||
vector & operator = (vector &) = default;
|
||||
vector & operator = (vector &&) = default;
|
||||
|
||||
template <typename ... Args>
|
||||
vector (Args && ... args)
|
||||
: coords{ std::forward<Args>(args)... }
|
||||
{
|
||||
static_assert(sizeof...(Args) == N);
|
||||
}
|
||||
|
||||
T & operator[] (std::size_t i)
|
||||
{
|
||||
return coords[i];
|
||||
}
|
||||
|
||||
T const & operator[] (std::size_t i) const
|
||||
{
|
||||
return coords[i];
|
||||
}
|
||||
|
||||
vector & operator *= (T const & s);
|
||||
vector & operator /= (T const & s);
|
||||
|
||||
vector & operator += (vector const & v);
|
||||
vector & operator -= (vector const & v);
|
||||
|
||||
static vector zero();
|
||||
};
|
||||
|
||||
template <typename ... Args>
|
||||
vector (Args && ...) -> vector<std::common_type_t<Args...>, sizeof...(Args)>;
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator == (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
return std::equal(v1.coords, v1.coords + N, v2.coords);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator != (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
return !(v1 == v2);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator < (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
return std::lexicographical_compare(v1.coords, v1.coords + N, v2.coords, v2.coords + N);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator > (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
return v2 < v1;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator <= (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
return !(v2 < v1);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
bool operator >= (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
return !(v1 < v2);
|
||||
}
|
||||
|
||||
template <typename T1, typename T, std::size_t N>
|
||||
vector<T1, N> cast (vector<T, N> const & v)
|
||||
{
|
||||
vector<T1, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = static_cast<T1>(v[i]);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator * (vector<T, N> const & v, T const & s)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = v[i] * s;
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator * (T const & s, vector<T, N> const & v)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = s * v[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator / (vector<T, N> const & v, T const & s)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = v[i] / s;
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator - (vector<T, N> const & v)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = -v[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator + (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = v1[i] + v2[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator - (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = v1[i] - v2[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> & vector<T, N>::operator *= (T const & s)
|
||||
{
|
||||
return (*this) = (*this) * s;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> & vector<T, N>::operator /= (T const & s)
|
||||
{
|
||||
return (*this) = (*this) / s;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> & vector<T, N>::operator += (vector<T, N> const & v)
|
||||
{
|
||||
return (*this) = (*this) + v;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> & vector<T, N>::operator -= (vector<T, N> const & v)
|
||||
{
|
||||
return (*this) = (*this) - v;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> vector<T, N>::zero()
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = T(0);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
T dot (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
T r{};
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r += v1[i] * v2[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
T length_sqr (vector<T, N> const & v)
|
||||
{
|
||||
return dot(v, v);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
T length (vector<T, N> const & v)
|
||||
{
|
||||
return std::sqrt(length_sqr(v));
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> normalized (vector<T, N> const & v)
|
||||
{
|
||||
return v / length(v);
|
||||
}
|
||||
|
||||
// TODO: generic implementation
|
||||
template <typename T>
|
||||
T det (vector<T, 2> const & v0, vector<T, 2> const & v1)
|
||||
{
|
||||
return v0[0] * v1[1] - v0[1] * v1[0];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T det (vector<T, 3> const & v0, vector<T, 3> const & v1, vector<T, 3> const & v2)
|
||||
{
|
||||
return
|
||||
+ v0[0] * v1[1] * v2[2]
|
||||
- v0[0] * v1[2] * v2[1]
|
||||
- v0[1] * v1[0] * v2[2]
|
||||
+ v0[1] * v1[2] * v2[0]
|
||||
+ v0[2] * v1[0] * v2[1]
|
||||
- v0[2] * v1[1] * v2[0]
|
||||
;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
vector<T, 3> cross (vector<T, 3> const & v0, vector<T, 3> const & v1)
|
||||
{
|
||||
return vector<T, 3>{
|
||||
v0[1] * v1[2] - v0[2] * v1[1],
|
||||
v0[2] * v1[0] - v0[0] * v1[2],
|
||||
v0[0] * v1[1] - v0[1] * v1[0],
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T lerp (T const & x0, T const & x1, T const & t)
|
||||
{
|
||||
return x0 * (T(1) - t) + x1 * t;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> lerp (vector<T, N> const & v0, vector<T, N> const & v1, T const & t)
|
||||
{
|
||||
return v0 * (T(1) - t) + v1 * t;
|
||||
}
|
||||
|
||||
template <typename Stream, typename T, std::size_t N>
|
||||
Stream & operator << (Stream & os, vector<T, N> const & v)
|
||||
{
|
||||
os << '(' << v[0];
|
||||
for (std::size_t i = 1; i < N; ++i)
|
||||
os << ", " << v[i];
|
||||
os << ')';
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
86
libs/geom/source/camera.cpp
Normal file
86
libs/geom/source/camera.cpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#include <psemek/geom/camera.hpp>
|
||||
#include <psemek/geom/gauss.hpp>
|
||||
#include <psemek/geom/perspective.hpp>
|
||||
#include <psemek/geom/translation.hpp>
|
||||
#include <psemek/geom/rotation.hpp>
|
||||
#include <psemek/geom/homogeneous.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace psemek::geom
|
||||
{
|
||||
|
||||
matrix<float, 4, 4> camera::transform() const
|
||||
{
|
||||
return projection() * view();
|
||||
}
|
||||
|
||||
point<float, 3> camera::position() const
|
||||
{
|
||||
vector<float, 4> b{ 0.f, 0.f, 0.f, 1.f };
|
||||
gauss(view(), b);
|
||||
return { b[0], b[1], b[2] };
|
||||
}
|
||||
|
||||
vector<float, 3> camera::axis_x() const
|
||||
{
|
||||
vector<float, 4> b{ 1.f, 0.f, 0.f, 0.f };
|
||||
gauss(view(), b);
|
||||
return { b[0], b[1], b[2] };
|
||||
}
|
||||
|
||||
vector<float, 3> camera::axis_y() const
|
||||
{
|
||||
vector<float, 4> b{ 0.f, 1.f, 0.f, 0.f };
|
||||
gauss(view(), b);
|
||||
return { b[0], b[1], b[2] };
|
||||
}
|
||||
|
||||
vector<float, 3> camera::axis_z() const
|
||||
{
|
||||
vector<float, 4> b{ 0.f, 0.f, 1.f, 0.f };
|
||||
gauss(view(), b);
|
||||
return { b[0], b[1], b[2] };
|
||||
}
|
||||
|
||||
vector<float, 3> camera::direction(float x, float y) const
|
||||
{
|
||||
vector<float, 4> b{ x, y, -1.f, 1.f};
|
||||
gauss(transform(), b);
|
||||
|
||||
point<float, 3> p{ b[0] / b[3], b[1] / b[3], b[2] / b[3] };
|
||||
return p - position();
|
||||
}
|
||||
|
||||
std::array<vector<float, 4>, 6> camera::clip_planes() const
|
||||
{
|
||||
auto const m = transpose(transform());
|
||||
|
||||
std::array<vector<float, 4>, 6> p;
|
||||
|
||||
p[0] = m * vector{1.f, 0.f, 0.f, 1.f};
|
||||
p[1] = m * vector{-1.f, 0.f, 0.f, 1.f};
|
||||
p[2] = m * vector{0.f, 1.f, 0.f, 1.f};
|
||||
p[3] = m * vector{0.f, -1.f, 0.f, 1.f};
|
||||
p[4] = m * vector{0.f, 0.f, 1.f, 1.f};
|
||||
p[5] = m * vector{0.f, 0.f, -1.f, 1.f};
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
matrix<float, 4, 4> perspective_camera::projection() const
|
||||
{
|
||||
return perspective<float, 3>(fov_x, fov_y, near_clip, far_clip).homogeneous_matrix();
|
||||
}
|
||||
|
||||
matrix<float, 4, 4> spherical_camera::view() const
|
||||
{
|
||||
return
|
||||
translation<float, 3>({0.f, 0.f, -distance}).homogeneous_matrix()
|
||||
* homogeneous(plane_rotation<float, 3>(1, 2, elevation_angle).matrix())
|
||||
* homogeneous(plane_rotation<float, 3>(2, 0, azimuthal_angle).matrix())
|
||||
* translation<float, 3>({ -target[0], -target[1], -target[2] }).homogeneous_matrix()
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
9
libs/gfx/CMakeLists.txt
Normal file
9
libs/gfx/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
set(OpenGL_GL_PREFERENCE GLVND)
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
file(GLOB_RECURSE PSEMEK_GFX_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
|
||||
file(GLOB_RECURSE PSEMEK_GFX_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")
|
||||
|
||||
add_library(gfx ${PSEMEK_GFX_HEADERS} ${PSEMEK_GFX_SOURCES})
|
||||
target_include_directories(gfx PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(gfx PUBLIC util geom OpenGL::GL)
|
||||
47
libs/gfx/include/psemek/gfx/buffer.hpp
Normal file
47
libs/gfx/include/psemek/gfx/buffer.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
struct buffer
|
||||
{
|
||||
buffer();
|
||||
buffer(buffer const &) = delete;
|
||||
buffer(buffer &&);
|
||||
|
||||
buffer & operator = (buffer const &) = delete;
|
||||
buffer & operator = (buffer &&);
|
||||
|
||||
~buffer();
|
||||
|
||||
static buffer null();
|
||||
|
||||
GLuint id() const { return id_; }
|
||||
|
||||
void bind() const;
|
||||
|
||||
void load(void const * data, std::size_t size, GLenum usage = gl::STREAM_DRAW);
|
||||
|
||||
template <typename T>
|
||||
void load(T const * data, std::size_t size, GLenum usage = gl::STREAM_DRAW)
|
||||
{
|
||||
load(static_cast<void const *>(data), size * sizeof(T), usage);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void load(std::vector<T> const & data, GLenum usage = gl::STREAM_DRAW)
|
||||
{
|
||||
load(data.data(), data.size(), usage);
|
||||
}
|
||||
|
||||
private:
|
||||
GLuint id_;
|
||||
|
||||
explicit buffer(GLuint id);
|
||||
};
|
||||
|
||||
}
|
||||
41
libs/gfx/include/psemek/gfx/framebuffer.hpp
Normal file
41
libs/gfx/include/psemek/gfx/framebuffer.hpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
#include <psemek/gfx/texture.hpp>
|
||||
#include <psemek/gfx/renderbuffer.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
struct framebuffer
|
||||
{
|
||||
framebuffer();
|
||||
framebuffer(framebuffer const &) = delete;
|
||||
framebuffer(framebuffer &&);
|
||||
|
||||
framebuffer & operator = (framebuffer const &) = delete;
|
||||
framebuffer & operator = (framebuffer &&);
|
||||
|
||||
~framebuffer();
|
||||
|
||||
static framebuffer null();
|
||||
|
||||
GLuint id() const { return id_; }
|
||||
|
||||
void bind() const;
|
||||
|
||||
void color(texture_2d const & tex, int attachment = 0);
|
||||
void color(renderbuffer const & rb, int attachment = 0);
|
||||
void depth(renderbuffer const & rb);
|
||||
|
||||
GLenum status() const;
|
||||
bool complete() const;
|
||||
void assert_complete() const;
|
||||
|
||||
private:
|
||||
GLuint id_;
|
||||
|
||||
explicit framebuffer(GLuint id);
|
||||
};
|
||||
|
||||
}
|
||||
10
libs/gfx/include/psemek/gfx/fullscreen.hpp
Normal file
10
libs/gfx/include/psemek/gfx/fullscreen.hpp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/gfx/mesh.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
indexed_mesh const & fullscreen_quad();
|
||||
|
||||
}
|
||||
1742
libs/gfx/include/psemek/gfx/gl.hpp
Normal file
1742
libs/gfx/include/psemek/gfx/gl.hpp
Normal file
File diff suppressed because it is too large
Load diff
384
libs/gfx/include/psemek/gfx/mesh.hpp
Normal file
384
libs/gfx/include/psemek/gfx/mesh.hpp
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <initializer_list>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct normalized
|
||||
{};
|
||||
|
||||
template <typename Attrib>
|
||||
struct attrib_traits;
|
||||
|
||||
template <>
|
||||
struct attrib_traits<std::uint8_t>
|
||||
{
|
||||
using attrib_type = std::uint8_t;
|
||||
|
||||
static constexpr GLint size = 1;
|
||||
static constexpr GLenum type = gl::UNSIGNED_BYTE;
|
||||
static constexpr GLboolean normalized = gl::FALSE_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct attrib_traits<std::int8_t>
|
||||
{
|
||||
using attrib_type = std::int8_t;
|
||||
|
||||
static constexpr GLint size = 1;
|
||||
static constexpr GLenum type = gl::BYTE;
|
||||
static constexpr GLboolean normalized = gl::FALSE_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct attrib_traits<std::uint16_t>
|
||||
{
|
||||
using attrib_type = std::uint16_t;
|
||||
|
||||
static constexpr GLint size = 1;
|
||||
static constexpr GLenum type = gl::UNSIGNED_SHORT;
|
||||
static constexpr GLboolean normalized = gl::FALSE_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct attrib_traits<std::int16_t>
|
||||
{
|
||||
using attrib_type = std::int16_t;
|
||||
|
||||
static constexpr GLint size = 1;
|
||||
static constexpr GLenum type = gl::SHORT;
|
||||
static constexpr GLboolean normalized = gl::FALSE_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct attrib_traits<std::uint32_t>
|
||||
{
|
||||
using attrib_type = std::uint32_t;
|
||||
|
||||
static constexpr GLint size = 1;
|
||||
static constexpr GLenum type = gl::UNSIGNED_INT;
|
||||
static constexpr GLboolean normalized = gl::FALSE_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct attrib_traits<std::int32_t>
|
||||
{
|
||||
using attrib_type = std::int32_t;
|
||||
|
||||
static constexpr GLint size = 1;
|
||||
static constexpr GLenum type = gl::INT;
|
||||
static constexpr GLboolean normalized = gl::FALSE_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct attrib_traits<float>
|
||||
{
|
||||
using attrib_type = float;
|
||||
|
||||
static constexpr GLint size = 1;
|
||||
static constexpr GLenum type = gl::FLOAT;
|
||||
static constexpr GLboolean normalized = gl::FALSE_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct attrib_traits<double>
|
||||
{
|
||||
using attrib_type = double;
|
||||
|
||||
static constexpr GLint size = 1;
|
||||
static constexpr GLenum type = gl::DOUBLE;
|
||||
static constexpr GLboolean normalized = gl::FALSE_;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct attrib_traits<geom::vector<T, N>>
|
||||
{
|
||||
using attrib_type = geom::vector<T, N>;
|
||||
|
||||
static constexpr GLint size = N;
|
||||
static constexpr GLenum type = attrib_traits<T>::type;
|
||||
static constexpr GLboolean normalized = gl::FALSE_;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct attrib_traits<geom::point<T, N>>
|
||||
{
|
||||
using attrib_type = geom::point<T, N>;
|
||||
|
||||
static constexpr GLint size = N;
|
||||
static constexpr GLenum type = attrib_traits<T>::type;
|
||||
static constexpr GLboolean normalized = gl::FALSE_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct attrib_traits<normalized<T>>
|
||||
{
|
||||
using attrib_type = T;
|
||||
|
||||
static constexpr GLint size = attrib_traits<T>::size;
|
||||
static constexpr GLenum type = attrib_traits<T>::type;
|
||||
static constexpr GLboolean normalized = gl::TRUE_;
|
||||
};
|
||||
|
||||
template <typename ... Attribs>
|
||||
struct vertex
|
||||
{
|
||||
static constexpr std::size_t size = (0 + ... + sizeof(typename attrib_traits<Attribs>::attrib_type));
|
||||
|
||||
using attribs = std::tuple<Attribs...>;
|
||||
using attrib_types = std::tuple<typename attrib_traits<Attribs>::attrib_type...>;
|
||||
|
||||
template <std::size_t I>
|
||||
static constexpr std::size_t offset()
|
||||
{
|
||||
return offset_impl(std::make_index_sequence<I>{});
|
||||
}
|
||||
|
||||
template <std::size_t I>
|
||||
auto & get()
|
||||
{
|
||||
using T = std::tuple_element_t<I, attrib_types>;
|
||||
return *reinterpret_cast<T *>(reinterpret_cast<char *>(&storage) + offset<I>());
|
||||
}
|
||||
|
||||
template <std::size_t I>
|
||||
auto const & get() const
|
||||
{
|
||||
using T = std::tuple_element_t<I, attrib_types>;
|
||||
return *reinterpret_cast<T const *>(reinterpret_cast<char const *>(&storage) + offset<I>());
|
||||
}
|
||||
|
||||
vertex() = default;
|
||||
|
||||
vertex(vertex const & other)
|
||||
{
|
||||
copy_impl(std::make_index_sequence<sizeof...(Attribs)>{}, other);
|
||||
}
|
||||
|
||||
vertex (typename attrib_traits<Attribs>::attrib_type const & ... args)
|
||||
{
|
||||
construct_impl(std::make_index_sequence<sizeof...(Attribs)>{}, args...);
|
||||
}
|
||||
|
||||
vertex & operator = (vertex const & other)
|
||||
{
|
||||
if (this != &other) copy_impl(std::make_index_sequence<sizeof...(Attribs)>{}, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::aligned_storage_t<size, std::max(std::initializer_list<std::size_t>{alignof(Attribs)...})> storage;
|
||||
|
||||
private:
|
||||
|
||||
template <std::size_t ... Is>
|
||||
static constexpr std::size_t offset_impl(std::index_sequence<Is...>)
|
||||
{
|
||||
return (0 + ... + sizeof(std::tuple_element_t<Is, attrib_types>));
|
||||
}
|
||||
|
||||
template <std::size_t ... Is>
|
||||
void construct_impl(std::index_sequence<Is...>, typename attrib_traits<Attribs>::attrib_type const & ... args)
|
||||
{
|
||||
((get<Is>() = args), ...);
|
||||
}
|
||||
|
||||
template <std::size_t ... Is>
|
||||
void copy_impl(std::index_sequence<Is...>, vertex const & other)
|
||||
{
|
||||
((get<Is>() = other.get<Is>()), ...);
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <std::size_t ... Is>
|
||||
void enable_attribs(std::index_sequence<Is...>)
|
||||
{
|
||||
int x[] { ((void) gl::EnableVertexAttribArray(Is), 0) ... };
|
||||
(void) x;
|
||||
}
|
||||
|
||||
template <typename Vertex, std::size_t I>
|
||||
void attrib_pointer()
|
||||
{
|
||||
using traits = attrib_traits<std::tuple_element_t<I, typename Vertex::attribs>>;
|
||||
gl::VertexAttribPointer(I, traits::size, traits::type, traits::normalized, sizeof(Vertex), reinterpret_cast<char const *>(Vertex::template offset<I>()));
|
||||
}
|
||||
|
||||
template <typename Vertex, std::size_t ... Is>
|
||||
void attrib_pointers(std::index_sequence<Is...>)
|
||||
{
|
||||
int x[] { ((void) attrib_pointer<Vertex, Is>(), 0) ... };
|
||||
(void) x;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
struct mesh_setup;
|
||||
|
||||
template <typename ... Attribs>
|
||||
struct mesh_setup<vertex<Attribs...>>
|
||||
{
|
||||
static void setup()
|
||||
{
|
||||
using indices = std::make_index_sequence<sizeof...(Attribs)>;
|
||||
|
||||
enable_attribs(indices{});
|
||||
attrib_pointers<vertex<Attribs...>>(indices{});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct gl_type;
|
||||
|
||||
template <>
|
||||
struct gl_type<std::uint8_t>
|
||||
{
|
||||
static constexpr GLenum value = gl::UNSIGNED_BYTE;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct gl_type<std::uint16_t>
|
||||
{
|
||||
static constexpr GLenum value = gl::UNSIGNED_SHORT;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct gl_type<std::uint32_t>
|
||||
{
|
||||
static constexpr GLenum value = gl::UNSIGNED_INT;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static constexpr GLenum gl_type_v = gl_type<T>::value;
|
||||
|
||||
}
|
||||
|
||||
struct mesh
|
||||
{
|
||||
static mesh null();
|
||||
|
||||
mesh();
|
||||
mesh(mesh &&);
|
||||
mesh(mesh const &) = delete;
|
||||
|
||||
mesh & operator =(mesh &&);
|
||||
mesh & operator =(mesh const &) = delete;
|
||||
|
||||
~mesh();
|
||||
|
||||
template <typename Vertex>
|
||||
void setup()
|
||||
{
|
||||
static_assert(sizeof(Vertex) == Vertex::size);
|
||||
|
||||
gl::BindVertexArray(array_);
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, buffer_);
|
||||
|
||||
detail::mesh_setup<Vertex>::setup();
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
void load(std::vector<Vertex> const & vertices, GLenum usage = gl::STREAM_DRAW)
|
||||
{
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, buffer_);
|
||||
gl::BufferData(gl::ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), usage);
|
||||
|
||||
count_ = vertices.size();
|
||||
}
|
||||
|
||||
void draw(GLenum mode) const
|
||||
{
|
||||
if (count_ == 0) return;
|
||||
|
||||
gl::BindVertexArray(array_);
|
||||
gl::DrawArrays(mode, 0, count_);
|
||||
}
|
||||
|
||||
GLsizei count() const
|
||||
{
|
||||
return count_;
|
||||
}
|
||||
|
||||
private:
|
||||
GLuint array_;
|
||||
GLuint buffer_;
|
||||
|
||||
GLsizei count_ = 0;
|
||||
|
||||
mesh(int);
|
||||
};
|
||||
|
||||
struct indexed_mesh
|
||||
{
|
||||
static indexed_mesh null();
|
||||
|
||||
indexed_mesh();
|
||||
indexed_mesh(indexed_mesh &&);
|
||||
indexed_mesh(indexed_mesh const &) = delete;
|
||||
|
||||
indexed_mesh & operator =(indexed_mesh &&);
|
||||
indexed_mesh & operator =(indexed_mesh const &) = delete;
|
||||
|
||||
~indexed_mesh();
|
||||
|
||||
template <typename Vertex>
|
||||
void setup()
|
||||
{
|
||||
gl::BindVertexArray(array_);
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, buffer_);
|
||||
|
||||
detail::mesh_setup<Vertex>::setup();
|
||||
|
||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer_);
|
||||
}
|
||||
|
||||
template <typename Vertex, typename Index>
|
||||
void load(std::vector<Vertex> const & vertices, std::vector<Index> const & indices, GLenum usage = gl::STREAM_DRAW)
|
||||
{
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, buffer_);
|
||||
gl::BufferData(gl::ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), usage);
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, index_buffer_);
|
||||
gl::BufferData(gl::ARRAY_BUFFER, indices.size() * sizeof(Index), indices.data(), usage);
|
||||
|
||||
count_ = indices.size();
|
||||
index_type_ = detail::gl_type_v<Index>;
|
||||
}
|
||||
|
||||
void draw(GLenum mode) const
|
||||
{
|
||||
if (count_ == 0) return;
|
||||
|
||||
gl::BindVertexArray(array_);
|
||||
gl::DrawElements(mode, count_, index_type_, nullptr);
|
||||
}
|
||||
|
||||
GLsizei count() const
|
||||
{
|
||||
return count_;
|
||||
}
|
||||
|
||||
private:
|
||||
GLuint array_;
|
||||
GLuint buffer_;
|
||||
GLuint index_buffer_;
|
||||
|
||||
GLsizei count_ = 0;
|
||||
GLenum index_type_ = 0;
|
||||
|
||||
indexed_mesh(int);
|
||||
};
|
||||
|
||||
}
|
||||
94
libs/gfx/include/psemek/gfx/program.hpp
Normal file
94
libs/gfx/include/psemek/gfx/program.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/point.hpp>
|
||||
#include <psemek/geom/matrix.hpp>
|
||||
#include <psemek/geom/interval.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string_view>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
struct program
|
||||
{
|
||||
program(std::string_view vertex_source, std::string_view fragment_source);
|
||||
program(std::string_view vertex_source, std::string_view geometry_source, std::string_view fragment_source);
|
||||
|
||||
GLuint id() const;
|
||||
|
||||
void bind() const;
|
||||
|
||||
GLint location(char const * name) const;
|
||||
|
||||
struct uniform_proxy
|
||||
{
|
||||
uniform_proxy(GLint location)
|
||||
: location_(location)
|
||||
{}
|
||||
|
||||
GLint location() const { return location_; }
|
||||
|
||||
void operator = (int i);
|
||||
void operator = (unsigned int i);
|
||||
void operator = (float f);
|
||||
|
||||
void operator = (geom::vector<int, 1> const & v);
|
||||
void operator = (geom::vector<int, 2> const & v);
|
||||
void operator = (geom::vector<int, 3> const & v);
|
||||
void operator = (geom::vector<int, 4> const & v);
|
||||
|
||||
void operator = (geom::vector<unsigned int, 1> const & v);
|
||||
void operator = (geom::vector<unsigned int, 2> const & v);
|
||||
void operator = (geom::vector<unsigned int, 3> const & v);
|
||||
void operator = (geom::vector<unsigned int, 4> const & v);
|
||||
|
||||
void operator = (geom::vector<float, 1> const & v);
|
||||
void operator = (geom::vector<float, 2> const & v);
|
||||
void operator = (geom::vector<float, 3> const & v);
|
||||
void operator = (geom::vector<float, 4> const & v);
|
||||
|
||||
void operator = (geom::point<int, 1> const & v);
|
||||
void operator = (geom::point<int, 2> const & v);
|
||||
void operator = (geom::point<int, 3> const & v);
|
||||
void operator = (geom::point<int, 4> const & v);
|
||||
|
||||
void operator = (geom::point<unsigned int, 1> const & v);
|
||||
void operator = (geom::point<unsigned int, 2> const & v);
|
||||
void operator = (geom::point<unsigned int, 3> const & v);
|
||||
void operator = (geom::point<unsigned int, 4> const & v);
|
||||
|
||||
void operator = (geom::point<float, 1> const & v);
|
||||
void operator = (geom::point<float, 2> const & v);
|
||||
void operator = (geom::point<float, 3> const & v);
|
||||
void operator = (geom::point<float, 4> const & v);
|
||||
|
||||
void operator = (geom::matrix<float, 2, 2> const & m);
|
||||
void operator = (geom::matrix<float, 2, 3> const & m);
|
||||
void operator = (geom::matrix<float, 2, 4> const & m);
|
||||
void operator = (geom::matrix<float, 3, 2> const & m);
|
||||
void operator = (geom::matrix<float, 3, 3> const & m);
|
||||
void operator = (geom::matrix<float, 3, 4> const & m);
|
||||
void operator = (geom::matrix<float, 4, 2> const & m);
|
||||
void operator = (geom::matrix<float, 4, 3> const & m);
|
||||
void operator = (geom::matrix<float, 4, 4> const & m);
|
||||
|
||||
void operator = (geom::interval<int> const & i);
|
||||
void operator = (geom::interval<unsigned int> const & i);
|
||||
void operator = (geom::interval<float> const & i);
|
||||
|
||||
private:
|
||||
GLint location_;
|
||||
};
|
||||
|
||||
uniform_proxy operator[] (char const * name) const;
|
||||
|
||||
private:
|
||||
GLuint program_;
|
||||
mutable std::unordered_map<std::string, GLint> uniforms_;
|
||||
};
|
||||
|
||||
}
|
||||
36
libs/gfx/include/psemek/gfx/render_to_texture.hpp
Normal file
36
libs/gfx/include/psemek/gfx/render_to_texture.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/gfx/texture.hpp>
|
||||
#include <psemek/gfx/framebuffer.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
struct render_to_texture
|
||||
{
|
||||
render_to_texture()
|
||||
{
|
||||
texture_.bind();
|
||||
texture_.unbind();
|
||||
framebuffer_.color(texture_);
|
||||
}
|
||||
|
||||
struct framebuffer & framebuffer() { return framebuffer_; }
|
||||
texture_2d & texture() { return texture_; }
|
||||
|
||||
void bind() const
|
||||
{
|
||||
framebuffer_.bind();
|
||||
}
|
||||
|
||||
void unbind() const
|
||||
{
|
||||
framebuffer_.unbind();
|
||||
}
|
||||
|
||||
private:
|
||||
texture_2d texture_;
|
||||
struct framebuffer framebuffer_;
|
||||
};
|
||||
|
||||
}
|
||||
33
libs/gfx/include/psemek/gfx/renderbuffer.hpp
Normal file
33
libs/gfx/include/psemek/gfx/renderbuffer.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
struct renderbuffer
|
||||
{
|
||||
renderbuffer();
|
||||
renderbuffer(renderbuffer const &) = delete;
|
||||
renderbuffer(renderbuffer &&);
|
||||
|
||||
renderbuffer & operator = (renderbuffer const &) = delete;
|
||||
renderbuffer & operator = (renderbuffer &&);
|
||||
|
||||
~renderbuffer();
|
||||
|
||||
static renderbuffer null();
|
||||
|
||||
GLuint id() const { return id_; }
|
||||
|
||||
void bind() const;
|
||||
|
||||
void storage(GLenum internal_format, GLsizei width, GLsizei height);
|
||||
|
||||
private:
|
||||
GLuint id_;
|
||||
|
||||
explicit renderbuffer(GLuint id);
|
||||
};
|
||||
|
||||
}
|
||||
115
libs/gfx/include/psemek/gfx/texture.hpp
Normal file
115
libs/gfx/include/psemek/gfx/texture.hpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
#include <psemek/util/pixmap.hpp>
|
||||
#include <psemek/geom/vector.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
template <typename Pixel>
|
||||
struct pixel_traits;
|
||||
|
||||
template <>
|
||||
struct pixel_traits<std::uint8_t>
|
||||
{
|
||||
static constexpr GLenum internal_format = gl::R8;
|
||||
static constexpr GLenum format = gl::RED;
|
||||
static constexpr GLenum type = gl::UNSIGNED_BYTE;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct pixel_traits<std::array<std::uint8_t, 3>>
|
||||
{
|
||||
static constexpr GLenum internal_format = gl::RGB8;
|
||||
static constexpr GLenum format = gl::RGB;
|
||||
static constexpr GLenum type = gl::UNSIGNED_BYTE;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct pixel_traits<std::array<std::uint8_t, 4>>
|
||||
{
|
||||
static constexpr GLenum internal_format = gl::RGBA8;
|
||||
static constexpr GLenum format = gl::RGBA;
|
||||
static constexpr GLenum type = gl::UNSIGNED_BYTE;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct pixel_traits<float>
|
||||
{
|
||||
static constexpr GLenum internal_format = gl::R32F;
|
||||
static constexpr GLenum format = gl::RED;
|
||||
static constexpr GLenum type = gl::FLOAT;
|
||||
};
|
||||
|
||||
struct texture_2d
|
||||
{
|
||||
texture_2d();
|
||||
texture_2d(texture_2d const &) = delete;
|
||||
texture_2d(texture_2d &&);
|
||||
|
||||
texture_2d & operator = (texture_2d const &) = delete;
|
||||
texture_2d & operator = (texture_2d &&);
|
||||
|
||||
~texture_2d();
|
||||
|
||||
static texture_2d null();
|
||||
|
||||
GLuint id() const { return id_; }
|
||||
|
||||
void bind() const;
|
||||
|
||||
explicit operator bool () const { return id_ != 0; }
|
||||
|
||||
int width() const { return width_; }
|
||||
int height() const { return height_; }
|
||||
|
||||
geom::vector<int, 2> size() const { return {width_, height_}; }
|
||||
|
||||
void load(GLint internal_format, std::size_t width, std::size_t height, GLenum format, GLenum type, const void * data);
|
||||
|
||||
template <typename Pixel>
|
||||
void load(std::size_t width, std::size_t height, Pixel const * data = nullptr)
|
||||
{
|
||||
using traits = pixel_traits<Pixel>;
|
||||
load(traits::internal_format, width, height, traits::format, traits::type, data);
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
void load(util::basic_pixmap<Pixel> const & p)
|
||||
{
|
||||
load(p.width(), p.height(), p.data());
|
||||
}
|
||||
|
||||
void pixels(GLenum format, GLenum type, void * data) const;
|
||||
|
||||
template <typename Pixmap>
|
||||
Pixmap pixels() const
|
||||
{
|
||||
using traits = pixel_traits<typename Pixmap::pixel_type>;
|
||||
Pixmap p(width_, height_);
|
||||
pixels(traits::format, traits::type, p.data());
|
||||
return p;
|
||||
}
|
||||
|
||||
static texture_2d from_data(GLint internal_format, std::size_t width, std::size_t height, GLenum format, GLenum type, const void * data);
|
||||
|
||||
template <typename Pixmap>
|
||||
static texture_2d from_pixmap(Pixmap const & p)
|
||||
{
|
||||
texture_2d t;
|
||||
t.load(p);
|
||||
return t;
|
||||
}
|
||||
|
||||
void generate_mipmap();
|
||||
|
||||
private:
|
||||
GLuint id_;
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
|
||||
texture_2d(GLuint id);
|
||||
};
|
||||
|
||||
}
|
||||
52
libs/gfx/source/gfx/buffer.cpp
Normal file
52
libs/gfx/source/gfx/buffer.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include <psemek/gfx/buffer.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
buffer::buffer()
|
||||
{
|
||||
gl::GenBuffers(1, &id_);
|
||||
}
|
||||
|
||||
buffer::buffer(buffer && other)
|
||||
: id_(other.id_)
|
||||
{
|
||||
other.id_ = 0;
|
||||
}
|
||||
|
||||
buffer & buffer::operator = (buffer && other)
|
||||
{
|
||||
if (this == &other) return *this;
|
||||
|
||||
gl::DeleteBuffers(1, &id_);
|
||||
id_ = other.id_;
|
||||
other.id_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
buffer::~buffer()
|
||||
{
|
||||
gl::DeleteBuffers(1, &id_);
|
||||
}
|
||||
|
||||
buffer buffer::null()
|
||||
{
|
||||
return buffer(0);
|
||||
}
|
||||
|
||||
void buffer::bind() const
|
||||
{
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, id_);
|
||||
}
|
||||
|
||||
void buffer::load(void const * data, std::size_t size, GLenum usage)
|
||||
{
|
||||
bind();
|
||||
gl::BufferData(gl::ARRAY_BUFFER, size, data, usage);
|
||||
}
|
||||
|
||||
buffer::buffer(GLuint id)
|
||||
: id_(id)
|
||||
{}
|
||||
|
||||
}
|
||||
95
libs/gfx/source/gfx/framebuffer.cpp
Normal file
95
libs/gfx/source/gfx/framebuffer.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#include <psemek/gfx/framebuffer.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
static std::string framebuffer_status_string(GLenum status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case gl::FRAMEBUFFER_UNDEFINED: return "GL_FRAMEBUFFER_UNDEFINED";
|
||||
case gl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
|
||||
case gl::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
|
||||
case gl::FRAMEBUFFER_UNSUPPORTED: return "GL_FRAMEBUFFER_UNSUPPORTED";
|
||||
case gl::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
|
||||
}
|
||||
|
||||
return "(unknown)";
|
||||
}
|
||||
|
||||
framebuffer::framebuffer()
|
||||
{
|
||||
gl::GenFramebuffers(1, &id_);
|
||||
}
|
||||
|
||||
framebuffer::framebuffer(GLuint id)
|
||||
: id_(id)
|
||||
{}
|
||||
|
||||
framebuffer::framebuffer(framebuffer && other)
|
||||
: id_(other.id_)
|
||||
{
|
||||
other.id_ = 0;
|
||||
}
|
||||
|
||||
framebuffer & framebuffer::operator = (framebuffer && other)
|
||||
{
|
||||
if (this == &other) return *this;
|
||||
|
||||
gl::DeleteFramebuffers(1, &id_);
|
||||
id_ = other.id_;
|
||||
other.id_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
framebuffer::~framebuffer()
|
||||
{
|
||||
gl::DeleteFramebuffers(1, &id_);
|
||||
}
|
||||
|
||||
framebuffer framebuffer::null()
|
||||
{
|
||||
return framebuffer{0};
|
||||
}
|
||||
|
||||
void framebuffer::bind() const
|
||||
{
|
||||
gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, id_);
|
||||
}
|
||||
|
||||
void framebuffer::color(texture_2d const & tex, int attachment)
|
||||
{
|
||||
bind();
|
||||
gl::FramebufferTexture2D(gl::DRAW_FRAMEBUFFER, gl::COLOR_ATTACHMENT0 + attachment, gl::TEXTURE_2D, tex.id(), 0);
|
||||
}
|
||||
|
||||
void framebuffer::color(renderbuffer const & rb, int attachment)
|
||||
{
|
||||
bind();
|
||||
gl::FramebufferRenderbuffer(gl::DRAW_FRAMEBUFFER, gl::COLOR_ATTACHMENT0 + attachment, gl::RENDERBUFFER, rb.id());
|
||||
}
|
||||
|
||||
void framebuffer::depth(renderbuffer const & rb)
|
||||
{
|
||||
bind();
|
||||
gl::FramebufferRenderbuffer(gl::DRAW_FRAMEBUFFER, gl::DEPTH_STENCIL_ATTACHMENT, gl::RENDERBUFFER, rb.id());
|
||||
}
|
||||
|
||||
GLenum framebuffer::status() const
|
||||
{
|
||||
bind();
|
||||
return gl::CheckFramebufferStatus(gl::DRAW_FRAMEBUFFER);
|
||||
}
|
||||
|
||||
bool framebuffer::complete() const
|
||||
{
|
||||
return status() == gl::FRAMEBUFFER_COMPLETE;
|
||||
}
|
||||
|
||||
void framebuffer::assert_complete() const
|
||||
{
|
||||
if (auto s = status(); s != gl::FRAMEBUFFER_COMPLETE)
|
||||
throw std::runtime_error("Framebuffer incomplete: " + framebuffer_status_string(s));
|
||||
}
|
||||
|
||||
}
|
||||
39
libs/gfx/source/gfx/fullscreen.cpp
Normal file
39
libs/gfx/source/gfx/fullscreen.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#include <psemek/gfx/fullscreen.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
static indexed_mesh create_fullscreen_quad()
|
||||
{
|
||||
indexed_mesh m;
|
||||
|
||||
using vertex = gfx::vertex<geom::point<float, 2>>;
|
||||
|
||||
std::vector<vertex> vertices(4);
|
||||
std::vector<std::uint8_t> indices(6);
|
||||
|
||||
vertices[0].get<0>() = {-1.f, -1.f};
|
||||
vertices[1].get<0>() = { 1.f, -1.f};
|
||||
vertices[2].get<0>() = {-1.f, 1.f};
|
||||
vertices[3].get<0>() = { 1.f, 1.f};
|
||||
|
||||
indices[0] = 0;
|
||||
indices[1] = 1;
|
||||
indices[2] = 2;
|
||||
indices[3] = 2;
|
||||
indices[4] = 1;
|
||||
indices[5] = 3;
|
||||
|
||||
m.setup<vertex>();
|
||||
m.load(vertices, indices, gl::STATIC_DRAW);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
indexed_mesh const & fullscreen_quad()
|
||||
{
|
||||
static indexed_mesh m = create_fullscreen_quad();
|
||||
return m;
|
||||
}
|
||||
|
||||
}
|
||||
1617
libs/gfx/source/gfx/gl.cpp
Normal file
1617
libs/gfx/source/gfx/gl.cpp
Normal file
File diff suppressed because it is too large
Load diff
129
libs/gfx/source/gfx/mesh.cpp
Normal file
129
libs/gfx/source/gfx/mesh.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#include <psemek/gfx/mesh.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
mesh mesh::null()
|
||||
{
|
||||
return mesh(0);
|
||||
}
|
||||
|
||||
mesh::mesh()
|
||||
{
|
||||
gl::GenVertexArrays(1, &array_);
|
||||
gl::GenBuffers(1, &buffer_);
|
||||
}
|
||||
|
||||
mesh::mesh(mesh && other)
|
||||
{
|
||||
array_ = other.array_;
|
||||
buffer_ = other.buffer_;
|
||||
count_ = other.count_;
|
||||
|
||||
other.array_ = 0;
|
||||
other.buffer_ = 0;
|
||||
other.count_ = 0;
|
||||
}
|
||||
|
||||
mesh & mesh::operator =(mesh && other)
|
||||
{
|
||||
if (this == &other) return *this;
|
||||
|
||||
if (array_)
|
||||
{
|
||||
gl::DeleteVertexArrays(1, &array_);
|
||||
gl::DeleteBuffers(1, &buffer_);
|
||||
}
|
||||
|
||||
array_ = other.array_;
|
||||
buffer_ = other.buffer_;
|
||||
count_ = other.count_;
|
||||
|
||||
other.array_ = 0;
|
||||
other.buffer_ = 0;
|
||||
other.count_ = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
mesh::~mesh()
|
||||
{
|
||||
gl::DeleteVertexArrays(1, &array_);
|
||||
gl::DeleteBuffers(1, &buffer_);
|
||||
}
|
||||
|
||||
mesh::mesh(int)
|
||||
{
|
||||
array_ = 0;
|
||||
buffer_ = 0;
|
||||
}
|
||||
|
||||
indexed_mesh indexed_mesh::null()
|
||||
{
|
||||
return indexed_mesh(0);
|
||||
}
|
||||
|
||||
indexed_mesh::indexed_mesh()
|
||||
{
|
||||
gl::GenVertexArrays(1, &array_);
|
||||
gl::GenBuffers(1, &buffer_);
|
||||
gl::GenBuffers(1, &index_buffer_);
|
||||
}
|
||||
|
||||
indexed_mesh::indexed_mesh(indexed_mesh && other)
|
||||
{
|
||||
array_ = other.array_;
|
||||
buffer_ = other.buffer_;
|
||||
index_buffer_ = other.index_buffer_;
|
||||
count_ = other.count_;
|
||||
index_type_ = other.index_type_;
|
||||
|
||||
other.array_ = 0;
|
||||
other.buffer_ = 0;
|
||||
other.index_buffer_ = 0;
|
||||
other.count_ = 0;
|
||||
other.index_type_ = 0;
|
||||
}
|
||||
|
||||
indexed_mesh & indexed_mesh::operator =(indexed_mesh && other)
|
||||
{
|
||||
if (this == &other) return *this;
|
||||
|
||||
if (array_)
|
||||
{
|
||||
gl::DeleteVertexArrays(1, &array_);
|
||||
gl::DeleteBuffers(1, &buffer_);
|
||||
gl::DeleteBuffers(1, &index_buffer_);
|
||||
}
|
||||
|
||||
array_ = other.array_;
|
||||
buffer_ = other.buffer_;
|
||||
index_buffer_ = other.index_buffer_;
|
||||
count_ = other.count_;
|
||||
index_type_ = other.index_type_;
|
||||
|
||||
other.array_ = 0;
|
||||
other.buffer_ = 0;
|
||||
other.index_buffer_ = 0;
|
||||
other.count_ = 0;
|
||||
other.index_type_ = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
indexed_mesh::~indexed_mesh()
|
||||
{
|
||||
gl::DeleteVertexArrays(1, &array_);
|
||||
gl::DeleteBuffers(1, &buffer_);
|
||||
gl::DeleteBuffers(1, &index_buffer_);
|
||||
}
|
||||
|
||||
indexed_mesh::indexed_mesh(int)
|
||||
{
|
||||
array_ = 0;
|
||||
buffer_ = 0;
|
||||
index_buffer_ = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
300
libs/gfx/source/gfx/program.cpp
Normal file
300
libs/gfx/source/gfx/program.cpp
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
#include <psemek/gfx/program.hpp>
|
||||
|
||||
#include <psemek/util/to_string.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
void program::uniform_proxy::operator = (int i)
|
||||
{
|
||||
gl::Uniform1i(location_, i);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (unsigned int u)
|
||||
{
|
||||
gl::Uniform1ui(location_, u);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (float f)
|
||||
{
|
||||
gl::Uniform1f(location_, f);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<int, 1> const & v)
|
||||
{
|
||||
gl::Uniform1i(location_, v[0]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<int, 2> const & v)
|
||||
{
|
||||
gl::Uniform2i(location_, v[0], v[1]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<int, 3> const & v)
|
||||
{
|
||||
gl::Uniform3i(location_, v[0], v[1], v[2]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<int, 4> const & v)
|
||||
{
|
||||
gl::Uniform4i(location_, v[0], v[1], v[2], v[3]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<unsigned int, 1> const & v)
|
||||
{
|
||||
gl::Uniform1ui(location_, v[0]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<unsigned int, 2> const & v)
|
||||
{
|
||||
gl::Uniform2ui(location_, v[0], v[1]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<unsigned int, 3> const & v)
|
||||
{
|
||||
gl::Uniform3ui(location_, v[0], v[1], v[2]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<unsigned int, 4> const & v)
|
||||
{
|
||||
gl::Uniform4ui(location_, v[0], v[1], v[2], v[3]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<float, 1> const & v)
|
||||
{
|
||||
gl::Uniform1f(location_, v[0]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<float, 2> const & v)
|
||||
{
|
||||
gl::Uniform2f(location_, v[0], v[1]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<float, 3> const & v)
|
||||
{
|
||||
gl::Uniform3f(location_, v[0], v[1], v[2]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::vector<float, 4> const & v)
|
||||
{
|
||||
gl::Uniform4f(location_, v[0], v[1], v[2], v[3]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<int, 1> const & v)
|
||||
{
|
||||
gl::Uniform1i(location_, v[0]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<int, 2> const & v)
|
||||
{
|
||||
gl::Uniform2i(location_, v[0], v[1]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<int, 3> const & v)
|
||||
{
|
||||
gl::Uniform3i(location_, v[0], v[1], v[2]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<int, 4> const & v)
|
||||
{
|
||||
gl::Uniform4i(location_, v[0], v[1], v[2], v[3]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<unsigned int, 1> const & v)
|
||||
{
|
||||
gl::Uniform1ui(location_, v[0]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<unsigned int, 2> const & v)
|
||||
{
|
||||
gl::Uniform2ui(location_, v[0], v[1]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<unsigned int, 3> const & v)
|
||||
{
|
||||
gl::Uniform3ui(location_, v[0], v[1], v[2]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<unsigned int, 4> const & v)
|
||||
{
|
||||
gl::Uniform4ui(location_, v[0], v[1], v[2], v[3]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<float, 1> const & v)
|
||||
{
|
||||
gl::Uniform1f(location_, v[0]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<float, 2> const & v)
|
||||
{
|
||||
gl::Uniform2f(location_, v[0], v[1]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<float, 3> const & v)
|
||||
{
|
||||
gl::Uniform3f(location_, v[0], v[1], v[2]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::point<float, 4> const & v)
|
||||
{
|
||||
gl::Uniform4f(location_, v[0], v[1], v[2], v[3]);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::matrix<float, 2, 2> const & m)
|
||||
{
|
||||
gl::UniformMatrix2fv(location_, 1, gl::TRUE_, m.coords);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::matrix<float, 2, 3> const & m)
|
||||
{
|
||||
gl::UniformMatrix2x3fv(location_, 1, gl::TRUE_, m.coords);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::matrix<float, 2, 4> const & m)
|
||||
{
|
||||
gl::UniformMatrix2x4fv(location_, 1, gl::TRUE_, m.coords);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::matrix<float, 3, 2> const & m)
|
||||
{
|
||||
gl::UniformMatrix3x2fv(location_, 1, gl::TRUE_, m.coords);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::matrix<float, 3, 3> const & m)
|
||||
{
|
||||
gl::UniformMatrix3fv(location_, 1, gl::TRUE_, m.coords);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::matrix<float, 3, 4> const & m)
|
||||
{
|
||||
gl::UniformMatrix3x4fv(location_, 1, gl::TRUE_, m.coords);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::matrix<float, 4, 2> const & m)
|
||||
{
|
||||
gl::UniformMatrix4x2fv(location_, 1, gl::TRUE_, m.coords);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::matrix<float, 4, 3> const & m)
|
||||
{
|
||||
gl::UniformMatrix4x3fv(location_, 1, gl::TRUE_, m.coords);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::matrix<float, 4, 4> const & m)
|
||||
{
|
||||
gl::UniformMatrix4fv(location_, 1, gl::TRUE_, m.coords);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::interval<int> const & i)
|
||||
{
|
||||
gl::Uniform2i(location_, i.min, i.max);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::interval<unsigned int> const & i)
|
||||
{
|
||||
gl::Uniform2ui(location_, i.min, i.max);
|
||||
}
|
||||
|
||||
void program::uniform_proxy::operator = (geom::interval<float> const & i)
|
||||
{
|
||||
gl::Uniform2f(location_, i.min, i.max);
|
||||
}
|
||||
|
||||
static void load_shader(GLuint shader, std::string_view source)
|
||||
{
|
||||
char const * vert_sources[1] { source.data() };
|
||||
GLint vert_sources_len[1] { static_cast<GLint>(source.size()) };
|
||||
gl::ShaderSource(shader, 1, vert_sources, vert_sources_len);
|
||||
gl::CompileShader(shader);
|
||||
|
||||
GLint status;
|
||||
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &status);
|
||||
if (status != gl::TRUE_)
|
||||
{
|
||||
GLint log_len;
|
||||
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &log_len);
|
||||
std::unique_ptr<char[]> log(new char [log_len]);
|
||||
gl::GetShaderInfoLog(shader, log_len, nullptr, log.get());
|
||||
throw std::runtime_error(util::to_string("Shader compilation failed: ", log.get()));
|
||||
}
|
||||
}
|
||||
|
||||
static void load_program(GLuint program, std::vector<GLuint> const & shaders)
|
||||
{
|
||||
for (auto s : shaders)
|
||||
gl::AttachShader(program, s);
|
||||
gl::LinkProgram(program);
|
||||
|
||||
GLint status;
|
||||
gl::GetProgramiv(program, gl::LINK_STATUS, &status);
|
||||
if (status != gl::TRUE_)
|
||||
{
|
||||
GLint log_len;
|
||||
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &log_len);
|
||||
std::unique_ptr<char[]> log(new char [log_len]);
|
||||
gl::GetProgramInfoLog(program, log_len, nullptr, log.get());
|
||||
throw std::runtime_error(util::to_string("Program link failed: ", log.get()));
|
||||
}
|
||||
|
||||
for (auto s : shaders)
|
||||
gl::DetachShader(program, s);
|
||||
}
|
||||
|
||||
static GLuint create_program(std::vector<std::pair<GLenum, std::string_view>> const & sources)
|
||||
{
|
||||
std::vector<GLuint> shaders;
|
||||
for (auto const & p : sources)
|
||||
{
|
||||
GLuint sh = gl::CreateShader(p.first);
|
||||
load_shader(sh, p.second);
|
||||
}
|
||||
|
||||
GLuint program = gl::CreateProgram();
|
||||
load_program(program, shaders);
|
||||
|
||||
for (auto s : shaders)
|
||||
gl::DeleteShader(s);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
program::program(std::string_view vertex_source, std::string_view fragment_source)
|
||||
{
|
||||
program_ = create_program({{gl::VERTEX_SHADER, vertex_source}, {gl::FRAGMENT_SHADER, fragment_source}});
|
||||
}
|
||||
|
||||
program::program(std::string_view vertex_source, std::string_view geometry_source, std::string_view fragment_source)
|
||||
{
|
||||
program_ = create_program({{gl::VERTEX_SHADER, vertex_source}, {gl::GEOMETRY_SHADER, geometry_source}, {gl::FRAGMENT_SHADER, fragment_source}});
|
||||
}
|
||||
|
||||
GLuint program::id() const
|
||||
{
|
||||
return program_;
|
||||
}
|
||||
|
||||
void program::bind() const
|
||||
{
|
||||
gl::UseProgram(program_);
|
||||
}
|
||||
|
||||
GLint program::location(char const * name) const
|
||||
{
|
||||
auto it = uniforms_.find(name);
|
||||
if (it == uniforms_.end())
|
||||
{
|
||||
auto l = gl::GetUniformLocation(program_, name);
|
||||
uniforms_[name] = l;
|
||||
return l;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
program::uniform_proxy program::operator[] (char const * name) const
|
||||
{
|
||||
return {location(name)};
|
||||
}
|
||||
|
||||
}
|
||||
52
libs/gfx/source/gfx/renderbuffer.cpp
Normal file
52
libs/gfx/source/gfx/renderbuffer.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include <psemek/gfx/renderbuffer.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
renderbuffer::renderbuffer()
|
||||
{
|
||||
gl::GenRenderbuffers(1, &id_);
|
||||
}
|
||||
|
||||
renderbuffer::renderbuffer(renderbuffer && other)
|
||||
{
|
||||
id_ = other.id_;
|
||||
other.id_ = 0;
|
||||
}
|
||||
|
||||
renderbuffer & renderbuffer::operator = (renderbuffer && other)
|
||||
{
|
||||
if (this == &other) return *this;
|
||||
|
||||
gl::DeleteRenderbuffers(1, &id_);
|
||||
id_ = other.id_;
|
||||
other.id_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
renderbuffer::~renderbuffer()
|
||||
{
|
||||
gl::DeleteRenderbuffers(1, &id_);
|
||||
}
|
||||
|
||||
renderbuffer renderbuffer::null()
|
||||
{
|
||||
return renderbuffer(0);
|
||||
}
|
||||
|
||||
void renderbuffer::bind() const
|
||||
{
|
||||
gl::BindRenderbuffer(gl::RENDERBUFFER, id_);
|
||||
}
|
||||
|
||||
void renderbuffer::storage(GLenum internal_format, GLsizei width, GLsizei height)
|
||||
{
|
||||
bind();
|
||||
gl::RenderbufferStorage(gl::RENDERBUFFER, internal_format, width, height);
|
||||
}
|
||||
|
||||
renderbuffer::renderbuffer(GLuint id)
|
||||
: id_(id)
|
||||
{}
|
||||
|
||||
}
|
||||
84
libs/gfx/source/gfx/texture.cpp
Normal file
84
libs/gfx/source/gfx/texture.cpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#include <psemek/gfx/texture.hpp>
|
||||
|
||||
namespace psemek::gfx
|
||||
{
|
||||
|
||||
texture_2d::texture_2d()
|
||||
{
|
||||
gl::GenTextures(1, &id_);
|
||||
}
|
||||
|
||||
texture_2d::texture_2d(GLuint id)
|
||||
: id_(id)
|
||||
{}
|
||||
|
||||
texture_2d texture_2d::null()
|
||||
{
|
||||
return texture_2d(0);
|
||||
}
|
||||
|
||||
void texture_2d::bind() const
|
||||
{
|
||||
gl::BindTexture(gl::TEXTURE_2D, id_);
|
||||
}
|
||||
|
||||
texture_2d::texture_2d(texture_2d && other)
|
||||
: id_(other.id_)
|
||||
, width_(other.width_)
|
||||
, height_(other.height_)
|
||||
{
|
||||
other.id_ = 0;
|
||||
other.width_ = 0;
|
||||
other.height_ = 0;
|
||||
}
|
||||
|
||||
texture_2d & texture_2d::operator = (texture_2d && other)
|
||||
{
|
||||
if (this == &other) return *this;
|
||||
|
||||
gl::DeleteTextures(1, &id_);
|
||||
id_ = other.id_;
|
||||
width_ = other.width_;
|
||||
height_ = other.height_;
|
||||
other.id_ = 0;
|
||||
other.width_ = 0;
|
||||
other.height_ = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
texture_2d::~texture_2d()
|
||||
{
|
||||
gl::DeleteTextures(1, &id_);
|
||||
}
|
||||
|
||||
void texture_2d::load(GLint internal_format, std::size_t width, std::size_t height, GLenum format, GLenum type, const void * data)
|
||||
{
|
||||
bind();
|
||||
gl::TexImage2D(gl::TEXTURE_2D, 0, internal_format, width, height, 0, format, type, data);
|
||||
gl::GenerateMipmap(gl::TEXTURE_2D);
|
||||
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
}
|
||||
|
||||
void texture_2d::pixels(GLenum format, GLenum type, void * data) const
|
||||
{
|
||||
bind();
|
||||
gl::GetTexImage(gl::TEXTURE_2D, 0, format, type, data);
|
||||
}
|
||||
|
||||
texture_2d texture_2d::from_data(GLint internal_format, std::size_t width, std::size_t height, GLenum format, GLenum type, const void * data)
|
||||
{
|
||||
texture_2d tex;
|
||||
tex.load(internal_format, width, height, format, type, data);
|
||||
return tex;
|
||||
}
|
||||
|
||||
void texture_2d::generate_mipmap()
|
||||
{
|
||||
bind();
|
||||
gl::GenerateMipmap(gl::TEXTURE_2D);
|
||||
}
|
||||
|
||||
}
|
||||
6
libs/pcg/CMakeLists.txt
Normal file
6
libs/pcg/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
file(GLOB_RECURSE PSEMEK_PCG_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
|
||||
file(GLOB_RECURSE PSEMEK_PCG_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")
|
||||
|
||||
add_library(pcg ${PSEMEK_PCG_HEADERS} ${PSEMEK_PCG_SOURCES})
|
||||
target_include_directories(pcg PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(pcg PUBLIC util geom)
|
||||
35
libs/pcg/include/psemek/pcg/perlin.hpp
Normal file
35
libs/pcg/include/psemek/pcg/perlin.hpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/pixmap.hpp>
|
||||
#include <psemek/geom/vector.hpp>
|
||||
|
||||
namespace psemek::pcg
|
||||
{
|
||||
|
||||
struct perlin
|
||||
{
|
||||
perlin() = default;
|
||||
perlin(util::basic_pixmap<geom::vector<float, 2>> grad_map);
|
||||
perlin(perlin &&) = default;
|
||||
|
||||
perlin & operator= (perlin &&) = default;
|
||||
|
||||
std::size_t width() const
|
||||
{
|
||||
return grad_map_.width() - 1;
|
||||
}
|
||||
|
||||
std::size_t height() const
|
||||
{
|
||||
return grad_map_.height() - 1;
|
||||
}
|
||||
|
||||
// x \in [0.0 .. 1.0]
|
||||
// y \in [0.0 .. 1.0]
|
||||
float operator() (float x, float y) const;
|
||||
|
||||
private:
|
||||
util::basic_pixmap<geom::vector<float, 2>> grad_map_;
|
||||
};
|
||||
|
||||
}
|
||||
50
libs/pcg/source/perlin.cpp
Normal file
50
libs/pcg/source/perlin.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#include <psemek/pcg/perlin.hpp>
|
||||
|
||||
#include <psemek/geom/interval.hpp>
|
||||
|
||||
#include <psemek/util/assert.hpp>
|
||||
|
||||
namespace psemek::pcg
|
||||
{
|
||||
|
||||
perlin::perlin(util::basic_pixmap<geom::vector<float, 2>> grad_map)
|
||||
: grad_map_(std::move(grad_map))
|
||||
{}
|
||||
|
||||
static float step(float x0, float x1, float t)
|
||||
{
|
||||
return x0 * (1.f - t) + x1 * t;
|
||||
}
|
||||
|
||||
static float smoothstep(float x0, float x1, float t)
|
||||
{
|
||||
float const s = t * t * (3.f - 2.f * t);
|
||||
return step(x0, x1, s);
|
||||
}
|
||||
|
||||
float perlin::operator() (float x, float y) const
|
||||
{
|
||||
assert(x >= 0.f);
|
||||
assert(y >= 0.f);
|
||||
|
||||
assert(x <= 1.f);
|
||||
assert(y <= 1.f);
|
||||
|
||||
x *= width();
|
||||
y *= height();
|
||||
|
||||
int const ix = geom::clamp<int>(std::floor(x), {0, static_cast<int>(width()) - 1});
|
||||
int const iy = geom::clamp<int>(std::floor(y), {0, static_cast<int>(height()) - 1});
|
||||
|
||||
float const tx = x - ix;
|
||||
float const ty = y - iy;
|
||||
|
||||
float const d00 = tx * grad_map_(ix, iy)[0] + ty * grad_map_(ix, iy)[1];
|
||||
float const d10 = (tx-1.f) * grad_map_(ix+1, iy)[0] + ty * grad_map_(ix+1, iy)[1];
|
||||
float const d01 = tx * grad_map_(ix, iy+1)[0] + (ty-1.f) * grad_map_(ix, iy+1)[1];
|
||||
float const d11 = (tx-1.f) * grad_map_(ix+1, iy+1)[0] + (ty-1.f) * grad_map_(ix+1, iy+1)[1];
|
||||
|
||||
return smoothstep(smoothstep(d00, d10, tx), smoothstep(d01, d11, tx), ty);
|
||||
}
|
||||
|
||||
}
|
||||
8
libs/util/CMakeLists.txt
Normal file
8
libs/util/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
find_package(Threads)
|
||||
|
||||
file(GLOB_RECURSE PSEMEK_UTIL_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
|
||||
file(GLOB_RECURSE PSEMEK_UTIL_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")
|
||||
|
||||
add_library(util ${PSEMEK_UTIL_HEADERS} ${PSEMEK_UTIL_SOURCES})
|
||||
target_include_directories(util PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(util PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||
18
libs/util/include/psemek/util/assert.hpp
Normal file
18
libs/util/include/psemek/util/assert.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#undef assert
|
||||
|
||||
#ifdef PSEMEK_DEBUG
|
||||
#define assert(x) ((void)(!(x) && ::psemek::util::assert_handler(#x, __FILE__, __LINE__)))
|
||||
#else
|
||||
#define assert(x) ((void)sizeof(x))
|
||||
#endif
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
[[noreturn]] bool assert_handler (char const * expression, char const * file, int line);
|
||||
|
||||
}
|
||||
21
libs/util/include/psemek/util/at_scope_exit.hpp
Normal file
21
libs/util/include/psemek/util/at_scope_exit.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename F>
|
||||
struct at_scope_exit
|
||||
{
|
||||
F f;
|
||||
|
||||
at_scope_exit(F f)
|
||||
: f(f)
|
||||
{}
|
||||
|
||||
~at_scope_exit()
|
||||
{
|
||||
f();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
5
libs/util/include/psemek/util/autoname.hpp
Normal file
5
libs/util/include/psemek/util/autoname.hpp
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#define _UTIL_CAT2(x,y) x##y
|
||||
#define _UTIL_CAT(x,y) _UTIL_CAT2(x,y)
|
||||
#define autoname _UTIL_CAT(autonamed_,__COUNTER__)
|
||||
121
libs/util/include/psemek/util/blob.hpp
Normal file
121
libs/util/include/psemek/util/blob.hpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
struct blob
|
||||
{
|
||||
blob() = default;
|
||||
blob(blob const & other);
|
||||
blob(blob && other);
|
||||
|
||||
blob(std::size_t size);
|
||||
blob(std::size_t size, std::unique_ptr<char[]> data);
|
||||
blob(std::size_t size, char * data);
|
||||
|
||||
blob & operator = (blob const & other);
|
||||
blob & operator = (blob && other);
|
||||
|
||||
~ blob() = default;
|
||||
|
||||
char * data() { return data_.get(); }
|
||||
char const * data() const { return data_.get(); }
|
||||
|
||||
std::size_t size() const { return size_; }
|
||||
|
||||
void reset();
|
||||
std::unique_ptr<char[]> release();
|
||||
|
||||
using iterator = char *;
|
||||
using const_iterator = char const *;
|
||||
|
||||
char * begin() { return data(); }
|
||||
char const * begin() const { return data(); }
|
||||
|
||||
char * end() { return data() + size(); }
|
||||
char const * end() const { return data() + size(); }
|
||||
|
||||
char & operator[] (std::size_t i) { return data()[i]; }
|
||||
char const & operator[] (std::size_t i) const { return data()[i]; }
|
||||
|
||||
std::string string() const;
|
||||
std::string_view string_view() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<char[]> data_;
|
||||
std::size_t size_ = 0;
|
||||
};
|
||||
|
||||
inline blob::blob(blob const & other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
inline blob::blob(blob && other)
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
inline blob::blob(std::size_t size)
|
||||
: data_{new char [size]}
|
||||
, size_{size}
|
||||
{}
|
||||
|
||||
inline blob::blob(std::size_t size, std::unique_ptr<char[]> data)
|
||||
: data_{std::move(data)}
|
||||
, size_{size}
|
||||
{}
|
||||
|
||||
inline blob::blob(std::size_t size, char * data)
|
||||
: data_{data}
|
||||
, size_{size}
|
||||
{}
|
||||
|
||||
inline blob & blob::operator=(blob const & other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
data_.reset(new char [other.size()]);
|
||||
std::copy(other.begin(), other.end(), begin());
|
||||
size_ = other.size();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline blob & blob::operator=(blob && other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
data_ = std::move(other.data_);
|
||||
size_ = other.size();
|
||||
other.size_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void blob::reset()
|
||||
{
|
||||
data_.reset();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
inline std::unique_ptr<char[]> blob::release()
|
||||
{
|
||||
size_ = 0;
|
||||
return std::move(data_);
|
||||
}
|
||||
|
||||
inline std::string blob::string() const
|
||||
{
|
||||
return std::string(data_.get(), data_.get() + size_);
|
||||
}
|
||||
|
||||
inline std::string_view blob::string_view() const
|
||||
{
|
||||
return std::string_view(data_.get(), size_);
|
||||
}
|
||||
}
|
||||
49
libs/util/include/psemek/util/clock.hpp
Normal file
49
libs/util/include/psemek/util/clock.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename Duration = std::chrono::duration<double>, typename Clock = std::chrono::system_clock>
|
||||
struct clock
|
||||
{
|
||||
typedef Duration duration_type;
|
||||
typedef typename duration_type::rep rep_type;
|
||||
|
||||
typedef Clock clock_type;
|
||||
typedef typename clock_type::time_point time_point_type;
|
||||
|
||||
clock ( )
|
||||
{
|
||||
restart();
|
||||
}
|
||||
|
||||
time_point_type now ( ) const
|
||||
{
|
||||
return clock_type::now();
|
||||
}
|
||||
|
||||
duration_type restart ( )
|
||||
{
|
||||
auto t = now();
|
||||
auto delta = t - start_;
|
||||
start_ = t;
|
||||
return std::chrono::duration_cast<duration_type>(delta);
|
||||
}
|
||||
|
||||
duration_type duration ( ) const
|
||||
{
|
||||
return std::chrono::duration_cast<duration_type>(now() - start_);
|
||||
}
|
||||
|
||||
rep_type count ( ) const
|
||||
{
|
||||
return duration().count();
|
||||
}
|
||||
|
||||
private:
|
||||
time_point_type start_;
|
||||
};
|
||||
|
||||
}
|
||||
77
libs/util/include/psemek/util/flat_list.hpp
Normal file
77
libs/util/include/psemek/util/flat_list.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename T, typename IndexType = std::size_t>
|
||||
struct flat_list
|
||||
{
|
||||
flat_list ( );
|
||||
explicit flat_list (std::size_t count);
|
||||
flat_list (std::size_t count, T const & value);
|
||||
flat_list (flat_list const &);
|
||||
flat_list (flat_list &&);
|
||||
flat_list (std::initializer_list<T> init);
|
||||
|
||||
~ flat_list ( );
|
||||
|
||||
std::size_t size ( ) const;
|
||||
|
||||
private:
|
||||
using item = std::aligned_storage_t<std::max(sizeof(T), sizeof(IndexType))>;
|
||||
|
||||
static constexpr std::size_t nil = static_cast<std::size_t>(-1);
|
||||
|
||||
// Invariant: data_.size() >= size_
|
||||
std::vector<item> data_;
|
||||
std::size_t size_ = 0;
|
||||
std::size_t first_free_ = nil;
|
||||
};
|
||||
|
||||
template <typename T, typename I>
|
||||
flat_list<T, I>::flat_list ( ) = default;
|
||||
|
||||
template <typename T, typename I>
|
||||
flat_list<T, I>::flat_list (std::size_t count)
|
||||
: data_(count)
|
||||
, size_(count)
|
||||
{ }
|
||||
|
||||
template <typename T, typename I>
|
||||
flat_list<T, I>::flat_list (std::size_t count, T const & value)
|
||||
: data_(count, value)
|
||||
, size_(count)
|
||||
{ }
|
||||
|
||||
template <typename T, typename I>
|
||||
flat_list<T, I>::flat_list (flat_list const & other) = default;
|
||||
|
||||
template <typename T, typename I>
|
||||
flat_list<T, I>::flat_list (flat_list && other)
|
||||
: data_(std::move(other.data_))
|
||||
, size_(other.size_)
|
||||
, first_free_(other.first_free_)
|
||||
{
|
||||
other.size_ = 0;
|
||||
other.first_free_ = nil;
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
flat_list<T, I>::flat_list (std::initializer_list<T> init)
|
||||
: data_(std::move(init))
|
||||
, size_(data_.size())
|
||||
{ }
|
||||
|
||||
template <typename T, typename I>
|
||||
flat_list<T, I>::~flat_list ( ) = default;
|
||||
|
||||
template <typename T, typename I>
|
||||
std::size_t flat_list<T, I>::size ( ) const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
}
|
||||
38
libs/util/include/psemek/util/fmap.hpp
Normal file
38
libs/util/include/psemek/util/fmap.hpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/range.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename F>
|
||||
struct fmap
|
||||
{
|
||||
F f;
|
||||
|
||||
template <typename T>
|
||||
auto operator() (std::optional<T> && x)
|
||||
{
|
||||
using R = decltype(f(*x));
|
||||
|
||||
if (x)
|
||||
return std::optional<R>(f(*x));
|
||||
else
|
||||
return std::optional<R>();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
auto fmap (F f)
|
||||
{
|
||||
return detail::fmap<F>{std::move(f)};
|
||||
}
|
||||
|
||||
}
|
||||
24
libs/util/include/psemek/util/functional.hpp
Normal file
24
libs/util/include/psemek/util/functional.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
constexpr auto nop = [](auto const & ...){};
|
||||
|
||||
constexpr auto id = [](auto && x) -> decltype(auto) { return x; };
|
||||
|
||||
template <typename T>
|
||||
auto constant (T const & x)
|
||||
{
|
||||
return [x](auto const & ...){ return x; };
|
||||
}
|
||||
|
||||
template <typename F1, typename F2>
|
||||
auto bind_and (F1 && f1, F2 && f2)
|
||||
{
|
||||
return [=](auto const &... args){
|
||||
return f1(args...) && f2(args...);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
65
libs/util/include/psemek/util/lazy_range.hpp
Normal file
65
libs/util/include/psemek/util/lazy_range.hpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename Gen>
|
||||
struct lazy_range
|
||||
{
|
||||
Gen generator;
|
||||
|
||||
lazy_range (Gen gen)
|
||||
: generator(std::move(gen))
|
||||
{ }
|
||||
|
||||
using value_type = decltype(generator());
|
||||
|
||||
struct iterator
|
||||
{
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
lazy_range & range;
|
||||
|
||||
value_type value;
|
||||
|
||||
value_type const & operator * ()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
iterator & operator ++ ()
|
||||
{
|
||||
value = range.generator();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin ( )
|
||||
{
|
||||
return { *this, generator() };
|
||||
}
|
||||
|
||||
struct sentinel
|
||||
{
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
};
|
||||
|
||||
sentinel end ( )
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
friend bool operator == (iterator const &, sentinel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
friend bool operator != (iterator const &, sentinel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
84
libs/util/include/psemek/util/movable_function.hpp
Normal file
84
libs/util/include/psemek/util/movable_function.hpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename Signature>
|
||||
struct movable_function_node_base;
|
||||
|
||||
template <typename R, typename ... Args>
|
||||
struct movable_function_node_base<R(Args...)>
|
||||
{
|
||||
virtual R call (Args ...) = 0;
|
||||
|
||||
virtual ~ movable_function_node_base ( ) = default;
|
||||
};
|
||||
|
||||
template <typename Signature, typename F>
|
||||
struct movable_function_node;
|
||||
|
||||
template <typename R, typename ... Args, typename F>
|
||||
struct movable_function_node<R(Args...), F>
|
||||
: movable_function_node_base<R(Args...)>
|
||||
{
|
||||
F f;
|
||||
|
||||
movable_function_node (F && f)
|
||||
: f(std::move(f))
|
||||
{ }
|
||||
|
||||
R call (Args ... args) override
|
||||
{
|
||||
return f(args...);
|
||||
}
|
||||
};
|
||||
|
||||
// Implemented in cpp to prevent dependency on <functional>
|
||||
[[noreturn]] void bad_function_call ( );
|
||||
|
||||
}
|
||||
|
||||
template <typename Signature>
|
||||
struct movable_function;
|
||||
|
||||
template <typename R, typename ... Args>
|
||||
struct movable_function<R(Args...)>
|
||||
{
|
||||
using signature = R(Args...);
|
||||
|
||||
movable_function ( ) = default;
|
||||
|
||||
template <typename F>
|
||||
movable_function (F f)
|
||||
: p { std::make_unique<detail::movable_function_node<signature, std::remove_cv_t<F>>>(std::move(f)) }
|
||||
{ }
|
||||
|
||||
movable_function (movable_function &&) = default;
|
||||
movable_function & operator = (movable_function &&) = default;
|
||||
|
||||
movable_function (movable_function const &) = delete;
|
||||
movable_function & operator = (movable_function const &) = delete;
|
||||
|
||||
explicit operator bool ( ) const
|
||||
{
|
||||
return static_cast<bool>(p);
|
||||
}
|
||||
|
||||
R operator() (Args ... args) const
|
||||
{
|
||||
if (!p)
|
||||
detail::bad_function_call();
|
||||
|
||||
return p->call(args...);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<detail::movable_function_node_base<signature>> p;
|
||||
};
|
||||
|
||||
}
|
||||
62
libs/util/include/psemek/util/moving_average.hpp
Normal file
62
libs/util/include/psemek/util/moving_average.hpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct moving_average
|
||||
{
|
||||
moving_average (std::size_t max)
|
||||
: data_(max, T())
|
||||
, begin_(0)
|
||||
, size_(0)
|
||||
, sum_(T())
|
||||
{ }
|
||||
|
||||
void clear ( )
|
||||
{
|
||||
begin_ = 0;
|
||||
size_ = 0;
|
||||
sum_ = T(0);
|
||||
}
|
||||
|
||||
void push (T x)
|
||||
{
|
||||
if (size_ >= data_.size())
|
||||
{
|
||||
sum_ -= data_[begin_];
|
||||
}
|
||||
else
|
||||
size_++;
|
||||
|
||||
data_[begin_++] = x;
|
||||
sum_ += x;
|
||||
|
||||
if (begin_ == data_.size())
|
||||
begin_ = 0;
|
||||
}
|
||||
|
||||
T sum ( ) const
|
||||
{
|
||||
return sum_;
|
||||
}
|
||||
|
||||
std::size_t count ( ) const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
T average ( ) const
|
||||
{
|
||||
return sum() / count();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<T> data_;
|
||||
std::size_t begin_, size_;
|
||||
T sum_;
|
||||
};
|
||||
|
||||
}
|
||||
29
libs/util/include/psemek/util/multidimensional_array.hpp
Normal file
29
libs/util/include/psemek/util/multidimensional_array.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename Type, std::size_t ... Sizes>
|
||||
struct multidimentional_array_impl;
|
||||
|
||||
template <typename Type, std::size_t FirstSize, std::size_t ... Sizes>
|
||||
struct multidimentional_array_impl<Type, FirstSize, Sizes...>
|
||||
{
|
||||
typedef std::array<typename multidimentional_array_impl<Type, Sizes...>::type, FirstSize> type;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct multidimentional_array_impl<Type>
|
||||
{
|
||||
typedef Type type;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Type, std::size_t ... Sizes>
|
||||
using multidimentional_array = typename detail::multidimentional_array_impl<Type, Sizes...>::type;
|
||||
|
||||
}
|
||||
16
libs/util/include/psemek/util/noncopyable.hpp
Normal file
16
libs/util/include/psemek/util/noncopyable.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
struct noncopyable
|
||||
{
|
||||
noncopyable (noncopyable const &) = delete;
|
||||
noncopyable & operator = (noncopyable const &) = delete;
|
||||
|
||||
noncopyable ( ) = default;
|
||||
noncopyable (noncopyable &&) = default;
|
||||
noncopyable & operator = (noncopyable &&) = default;
|
||||
};
|
||||
|
||||
}
|
||||
8
libs/util/include/psemek/util/not_implemented.hpp
Normal file
8
libs/util/include/psemek/util/not_implemented.hpp
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
void not_implemented ( );
|
||||
|
||||
}
|
||||
30
libs/util/include/psemek/util/overload.hpp
Normal file
30
libs/util/include/psemek/util/overload.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename ... Fs>
|
||||
struct overload_impl
|
||||
: Fs...
|
||||
{
|
||||
overload_impl (Fs ... fs)
|
||||
: Fs(fs)...
|
||||
{ }
|
||||
|
||||
using Fs::operator()...;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename ... Fs>
|
||||
auto overload (Fs ... fs)
|
||||
{
|
||||
return detail::overload_impl<Fs...>{std::move(fs)...};
|
||||
}
|
||||
|
||||
}
|
||||
68
libs/util/include/psemek/util/pimpl.hpp
Normal file
68
libs/util/include/psemek/util/pimpl.hpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
|
||||
namespace psemek::util::pimpl
|
||||
{
|
||||
|
||||
template <typename Parent>
|
||||
struct impl;
|
||||
|
||||
template <typename Parent, std::size_t Size>
|
||||
struct in_place
|
||||
{
|
||||
protected:
|
||||
|
||||
using impl_type = ::util::pimpl::impl<Parent>;
|
||||
|
||||
impl_type * pimpl ( ) { return reinterpret_cast<impl_type *>(std::addressof(storage_)); }
|
||||
impl_type & impl ( ) { return *pimpl(); }
|
||||
|
||||
impl_type const * pimpl ( ) const { return reinterpret_cast<impl_type const *>(std::addressof(storage_)); }
|
||||
impl_type const & impl ( ) const { return *pimpl(); }
|
||||
|
||||
template <typename ... Args>
|
||||
in_place (Args && ... args)
|
||||
{
|
||||
static_assert(sizeof(impl_type) <= Size, "impl storage size too small");
|
||||
new (pimpl()) impl_type (std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
~ in_place ( )
|
||||
{
|
||||
impl().~impl_type();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
typename std::aligned_storage<Size>::type storage_;
|
||||
};
|
||||
|
||||
template <typename Parent>
|
||||
struct dynamic
|
||||
{
|
||||
protected:
|
||||
|
||||
using impl_type = ::util::pimpl::impl<Parent>;
|
||||
|
||||
impl_type * pimpl ( ) { return pointer_.get(); }
|
||||
impl_type & impl ( ) { return *pimpl(); }
|
||||
|
||||
impl_type const * pimpl ( ) const { return pointer_.get(); }
|
||||
impl_type const & impl ( ) const { return *pimpl(); }
|
||||
|
||||
template <typename ... Args>
|
||||
dynamic (Args && ... args)
|
||||
{
|
||||
pointer_.reset(new impl_type(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
~ dynamic ( ) = default;
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<impl_type> pointer_;
|
||||
};
|
||||
|
||||
}
|
||||
201
libs/util/include/psemek/util/pixmap.hpp
Normal file
201
libs/util/include/psemek/util/pixmap.hpp
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename Pixel>
|
||||
struct basic_pixmap
|
||||
{
|
||||
using pixel_type = Pixel;
|
||||
|
||||
basic_pixmap() = default;
|
||||
basic_pixmap(std::size_t width, std::size_t height);
|
||||
basic_pixmap(std::size_t width, std::size_t height, Pixel value);
|
||||
basic_pixmap(std::size_t width, std::size_t height, std::unique_ptr<Pixel[]> data);
|
||||
basic_pixmap(basic_pixmap &&);
|
||||
|
||||
basic_pixmap(basic_pixmap const &) = delete;
|
||||
|
||||
basic_pixmap & operator = (basic_pixmap &&);
|
||||
basic_pixmap & operator = (basic_pixmap const &) = delete;
|
||||
|
||||
std::size_t width() const { return width_; }
|
||||
std::size_t height() const { return height_; }
|
||||
|
||||
void resize(std::size_t width, std::size_t height);
|
||||
void resize(std::size_t width, std::size_t height, Pixel value);
|
||||
|
||||
Pixel * operator[] (std::size_t row);
|
||||
Pixel const * operator[] (std::size_t row) const;
|
||||
|
||||
Pixel * data() { return data_.get(); }
|
||||
Pixel const * data() const { return data_.get(); }
|
||||
|
||||
Pixel & operator()(std::size_t column, std::size_t row);
|
||||
Pixel const & operator()(std::size_t column, std::size_t row) const;
|
||||
|
||||
std::unique_ptr<Pixel[]> release();
|
||||
void reset(std::size_t width, std::size_t height, std::unique_ptr<Pixel[]> data);
|
||||
|
||||
bool empty() const;
|
||||
|
||||
void clear();
|
||||
|
||||
void fill(Pixel value);
|
||||
|
||||
private:
|
||||
std::size_t width_ = 0;
|
||||
std::size_t height_ = 0;
|
||||
std::unique_ptr<Pixel[]> data_;
|
||||
};
|
||||
|
||||
using pixmap_monochrome = basic_pixmap<std::uint8_t>;
|
||||
using pixmap_rgb = basic_pixmap<std::array<std::uint8_t, 3>>;
|
||||
using pixmap_rgba = basic_pixmap<std::array<std::uint8_t, 4>>;
|
||||
using pixmap_float = basic_pixmap<float>;
|
||||
|
||||
pixmap_monochrome read_pgm(std::istream & is);
|
||||
pixmap_rgb read_ppm(std::istream & is);
|
||||
|
||||
void write_pgm(pixmap_monochrome const & p, std::ostream & os);
|
||||
void write_ppm(pixmap_rgb const & p, std::ostream & os);
|
||||
|
||||
template <typename Pixel>
|
||||
basic_pixmap<Pixel>::basic_pixmap(std::size_t width, std::size_t height)
|
||||
: width_(width)
|
||||
, height_(height)
|
||||
{
|
||||
data_.reset(new Pixel [width_ * height_]);
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
basic_pixmap<Pixel>::basic_pixmap(std::size_t width, std::size_t height, Pixel value)
|
||||
: width_(width)
|
||||
, height_(height)
|
||||
{
|
||||
data_.reset(new Pixel [width_ * height_]);
|
||||
fill(value);
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
basic_pixmap<Pixel>::basic_pixmap(std::size_t width, std::size_t height, std::unique_ptr<Pixel[]> data)
|
||||
: width_(width)
|
||||
, height_(height)
|
||||
, data_(std::move(data))
|
||||
{}
|
||||
|
||||
template <typename Pixel>
|
||||
basic_pixmap<Pixel>::basic_pixmap(basic_pixmap && other)
|
||||
: width_(other.width_)
|
||||
, height_(other.height_)
|
||||
, data_(other.release())
|
||||
{}
|
||||
|
||||
template <typename Pixel>
|
||||
basic_pixmap<Pixel> & basic_pixmap<Pixel>::operator = (basic_pixmap && other)
|
||||
{
|
||||
if (this == &other) return *this;
|
||||
|
||||
width_ = other.width_;
|
||||
height_ = other.height_;
|
||||
data_ = other.release();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
void basic_pixmap<Pixel>::resize(std::size_t width, std::size_t height)
|
||||
{
|
||||
data_.reset(new Pixel[width * height]);
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
void basic_pixmap<Pixel>::resize(std::size_t width, std::size_t height, Pixel value)
|
||||
{
|
||||
data_.reset(new Pixel[width * height]);
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
fill(value);
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
Pixel * basic_pixmap<Pixel>::operator[] (std::size_t row)
|
||||
{
|
||||
return data() + row * width();
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
Pixel const * basic_pixmap<Pixel>::operator[] (std::size_t row) const
|
||||
{
|
||||
return data() + row * width();
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
Pixel & basic_pixmap<Pixel>::operator()(std::size_t column, std::size_t row)
|
||||
{
|
||||
return (*this)[row][column];
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
Pixel const & basic_pixmap<Pixel>::operator()(std::size_t column, std::size_t row) const
|
||||
{
|
||||
return (*this)[row][column];
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
std::unique_ptr<Pixel[]> basic_pixmap<Pixel>::release()
|
||||
{
|
||||
auto data = std::move(data_);
|
||||
width_ = 0;
|
||||
height_ = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
void basic_pixmap<Pixel>::reset(std::size_t width, std::size_t height, std::unique_ptr<Pixel[]> data)
|
||||
{
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
data_ = std::move(data);
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
bool basic_pixmap<Pixel>::empty() const
|
||||
{
|
||||
return width() == 0 || height() == 0;
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
void basic_pixmap<Pixel>::clear()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
void basic_pixmap<Pixel>::fill(Pixel value)
|
||||
{
|
||||
std::fill(data_.get(), data_.get() + width_ * height_, value);
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
void mirror_x(basic_pixmap<Pixel> & p)
|
||||
{
|
||||
for (std::size_t y = 0; y < p.height(); ++y)
|
||||
for (std::size_t x = 0; x < p.width() / 2; ++x)
|
||||
std::swap(p(x, y), p(p.width() - x - 1, y));
|
||||
}
|
||||
|
||||
template <typename Pixel>
|
||||
void mirror_y(basic_pixmap<Pixel> & p)
|
||||
{
|
||||
for (std::size_t y = 0; y < p.height() / 2; ++y)
|
||||
for (std::size_t x = 0; x < p.width(); ++x)
|
||||
std::swap(p(x, y), p(x, p.height() - y - 1));
|
||||
}
|
||||
|
||||
}
|
||||
44
libs/util/include/psemek/util/pretty_print.hpp
Normal file
44
libs/util/include/psemek/util/pretty_print.hpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename Duration, typename UpTo>
|
||||
struct pretty_print_time_wrapper
|
||||
{
|
||||
Duration d;
|
||||
UpTo up_to;
|
||||
};
|
||||
|
||||
void pretty_print_time (std::ostream & o, std::int64_t d, std::int64_t up_to);
|
||||
|
||||
template <typename Duration, typename UpTo>
|
||||
std::ostream & operator << (std::ostream & o, pretty_print_time_wrapper<Duration, UpTo> t)
|
||||
{
|
||||
std::int64_t const d = std::chrono::duration_cast<std::chrono::nanoseconds>(t.d).count();
|
||||
std::int64_t const up_to = std::chrono::duration_cast<std::chrono::nanoseconds>(t.up_to).count();
|
||||
pretty_print_time(o, d, up_to);
|
||||
return o;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename Rep, typename Period, typename UpTo>
|
||||
auto pretty (std::chrono::duration<Rep, Period> d, UpTo up_to)
|
||||
{
|
||||
return detail::pretty_print_time_wrapper<std::chrono::duration<Rep, Period>, UpTo>{d, up_to};
|
||||
}
|
||||
|
||||
template <typename Rep, typename Period>
|
||||
auto pretty (std::chrono::duration<Rep, Period> d)
|
||||
{
|
||||
return pretty(d, std::chrono::seconds{1});
|
||||
}
|
||||
|
||||
}
|
||||
27
libs/util/include/psemek/util/profiler.hpp
Normal file
27
libs/util/include/psemek/util/profiler.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/clock.hpp>
|
||||
#include <psemek/util/pretty_print.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
struct profiler
|
||||
{
|
||||
profiler (std::string name)
|
||||
: name_(std::move(name))
|
||||
{ }
|
||||
|
||||
~ profiler ( )
|
||||
{
|
||||
std::cout << name_ << ": " << util::pretty(clock_.duration(), std::chrono::microseconds{1}) << "\n";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string const name_;
|
||||
util::clock<> clock_;
|
||||
};
|
||||
|
||||
}
|
||||
80
libs/util/include/psemek/util/range.hpp
Normal file
80
libs/util/include/psemek/util/range.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename Container>
|
||||
auto begin_helper (Container & x)
|
||||
{
|
||||
using std::begin;
|
||||
return begin(x);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
auto end_helper (Container & x)
|
||||
{
|
||||
using std::end;
|
||||
return end(x);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
auto begin (Container & x)
|
||||
{
|
||||
return detail::begin_helper(x);
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
auto end (Container & x)
|
||||
{
|
||||
return detail::end_helper(x);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct range_traits
|
||||
{
|
||||
using iterator = decltype(begin(std::declval<T>()));
|
||||
using iterator_traits = std::iterator_traits<iterator>;
|
||||
|
||||
using iterator_category = typename iterator_traits::iterator_category;
|
||||
using value_type = typename iterator_traits::value_type ;
|
||||
using difference = typename iterator_traits::difference ;
|
||||
using pointer = typename iterator_traits::pointer ;
|
||||
using reference = typename iterator_traits::reference ;
|
||||
};
|
||||
|
||||
template <typename Iterator>
|
||||
struct range
|
||||
{
|
||||
Iterator it_begin;
|
||||
Iterator it_end;
|
||||
|
||||
Iterator begin() const
|
||||
{
|
||||
return it_begin;
|
||||
}
|
||||
|
||||
Iterator end() const
|
||||
{
|
||||
return it_end;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Range>
|
||||
auto reversed (Range const & r)
|
||||
{
|
||||
auto it1 = begin(r);
|
||||
auto it2 = end(r);
|
||||
|
||||
using ReverseIterator = std::reverse_iterator<decltype(it1)>;
|
||||
return range<ReverseIterator>{ std::make_reverse_iterator(it2), std::make_reverse_iterator(it1) };
|
||||
}
|
||||
|
||||
}
|
||||
29
libs/util/include/psemek/util/recursive.hpp
Normal file
29
libs/util/include/psemek/util/recursive.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename F>
|
||||
struct recursive_impl
|
||||
{
|
||||
F f;
|
||||
|
||||
template <typename ... Args>
|
||||
auto operator() (Args && ... args) -> decltype(auto)
|
||||
{
|
||||
return f(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
auto recursive (F f)
|
||||
{
|
||||
return detail::recursive_impl<F>{std::move(f)};
|
||||
}
|
||||
|
||||
}
|
||||
129
libs/util/include/psemek/util/shared_blob.hpp
Normal file
129
libs/util/include/psemek/util/shared_blob.hpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/blob.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
struct shared_blob
|
||||
{
|
||||
shared_blob() = default;
|
||||
shared_blob(shared_blob const & other);
|
||||
shared_blob(shared_blob && other);
|
||||
shared_blob(blob && other);
|
||||
|
||||
shared_blob(std::size_t size);
|
||||
shared_blob(std::size_t size, std::shared_ptr<char[]> data);
|
||||
shared_blob(std::size_t size, char * data);
|
||||
|
||||
shared_blob & operator = (shared_blob const & other);
|
||||
shared_blob & operator = (shared_blob && other);
|
||||
shared_blob & operator = (blob && other);
|
||||
|
||||
~ shared_blob() = default;
|
||||
|
||||
char * data() { return data_.get(); }
|
||||
char const * data() const { return data_.get(); }
|
||||
|
||||
std::size_t size() const { return size_; }
|
||||
|
||||
void reset();
|
||||
|
||||
using iterator = char *;
|
||||
using const_iterator = char const *;
|
||||
|
||||
char * begin() { return data(); }
|
||||
char const * begin() const { return data(); }
|
||||
|
||||
char * end() { return data() + size(); }
|
||||
char const * end() const { return data() + size(); }
|
||||
|
||||
char & operator[] (std::size_t i) { return data()[i]; }
|
||||
char const & operator[] (std::size_t i) const { return data()[i]; }
|
||||
|
||||
std::string string() const;
|
||||
std::string_view string_view() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<char[]> data_;
|
||||
std::size_t size_ = 0;
|
||||
};
|
||||
|
||||
inline shared_blob::shared_blob(shared_blob const & other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
inline shared_blob::shared_blob(shared_blob && other)
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
inline shared_blob::shared_blob(blob && other)
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
inline shared_blob::shared_blob(std::size_t size)
|
||||
: data_{new char [size]}
|
||||
, size_{size}
|
||||
{}
|
||||
|
||||
inline shared_blob::shared_blob(std::size_t size, std::shared_ptr<char[]> data)
|
||||
: data_{std::move(data)}
|
||||
, size_{size}
|
||||
{}
|
||||
|
||||
inline shared_blob::shared_blob(std::size_t size, char * data)
|
||||
: data_{data}
|
||||
, size_{size}
|
||||
{}
|
||||
|
||||
inline shared_blob & shared_blob::operator=(shared_blob const & other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
data_ = other.data_;
|
||||
size_ = other.size();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline shared_blob & shared_blob::operator=(shared_blob && other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
data_ = std::move(other.data_);
|
||||
size_ = other.size();
|
||||
other.size_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline shared_blob & shared_blob::operator=(blob && other)
|
||||
{
|
||||
size_ = other.size();
|
||||
data_ = other.release();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void shared_blob::reset()
|
||||
{
|
||||
data_.reset();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
inline std::string shared_blob::string() const
|
||||
{
|
||||
return std::string(data_.get(), data_.get() + size_);
|
||||
}
|
||||
|
||||
inline std::string_view shared_blob::string_view() const
|
||||
{
|
||||
return std::string_view(data_.get(), size_);
|
||||
}
|
||||
}
|
||||
147
libs/util/include/psemek/util/synchronyzed_queue.hpp
Normal file
147
libs/util/include/psemek/util/synchronyzed_queue.hpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct synchronized_queue
|
||||
{
|
||||
synchronized_queue (std::size_t max_size = std::numeric_limits<std::size_t>::max()) noexcept
|
||||
: max_size_(max_size)
|
||||
{ }
|
||||
|
||||
std::size_t max_size ( ) const noexcept
|
||||
{
|
||||
return max_size_;
|
||||
}
|
||||
|
||||
void push (T const & x);
|
||||
void push (T && x);
|
||||
T pop ( );
|
||||
|
||||
bool try_push (T const & x);
|
||||
template <typename Rep, typename Period>
|
||||
bool try_push (T const & x, std::chrono::duration<Rep, Period> const & timeout);
|
||||
|
||||
std::optional<T> try_pop ( );
|
||||
template <typename Rep, typename Period>
|
||||
std::optional<T> try_pop (std::chrono::duration<Rep, Period> const & timeout);
|
||||
|
||||
void clear ( );
|
||||
|
||||
// Wait for the queue to become empty
|
||||
// e.g. when no new items are going to be pushed
|
||||
void wait ( );
|
||||
|
||||
private:
|
||||
std::mutex mutex;
|
||||
std::condition_variable push_cv, pop_cv;
|
||||
std::deque<T> queue;
|
||||
std::size_t const max_size_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void synchronized_queue<T>::push (T const & x)
|
||||
{
|
||||
std::unique_lock lock { mutex };
|
||||
push_cv.wait(lock, [this]{ return queue.size() < max_size(); });
|
||||
queue.push_back(x);
|
||||
pop_cv.notify_one();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void synchronized_queue<T>::push (T && x)
|
||||
{
|
||||
std::unique_lock lock { mutex };
|
||||
push_cv.wait(lock, [this]{ return queue.size() < max_size(); });
|
||||
queue.push_back(std::move(x));
|
||||
pop_cv.notify_one();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T synchronized_queue<T>::pop ( )
|
||||
{
|
||||
std::unique_lock lock { mutex };
|
||||
pop_cv.wait(lock, [this]{ return !queue.empty(); });
|
||||
T x = std::move(queue.front());
|
||||
queue.pop_front();
|
||||
push_cv.notify_one();
|
||||
return x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool synchronized_queue<T>::try_push (T const & x)
|
||||
{
|
||||
std::lock_guard lock { mutex };
|
||||
if (queue.size() >= max_size())
|
||||
return false;
|
||||
|
||||
queue.push_back(x);
|
||||
pop_cv.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename Rep, typename Period>
|
||||
bool synchronized_queue<T>::try_push (T const & x, std::chrono::duration<Rep, Period> const & timeout)
|
||||
{
|
||||
std::unique_lock lock { mutex };
|
||||
if (push_cv.wait_for(lock, timeout, [this]{ return queue.size() < max_size(); }))
|
||||
{
|
||||
queue.push_back(std::move(x));
|
||||
pop_cv.notify_one();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<T> synchronized_queue<T>::try_pop ( )
|
||||
{
|
||||
std::lock_guard lock { mutex };
|
||||
if (queue.empty())
|
||||
return std::nullopt;
|
||||
T x = std::move(queue.front());
|
||||
queue.pop_front();
|
||||
push_cv.notify_one();
|
||||
return { std::move(x) };
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename Rep, typename Period>
|
||||
std::optional<T> synchronized_queue<T>::try_pop (std::chrono::duration<Rep, Period> const & timeout)
|
||||
{
|
||||
std::unique_lock lock { mutex };
|
||||
if (pop_cv.wait_for(lock, timeout, [this]{ return !queue.empty(); }))
|
||||
{
|
||||
T x = std::move(queue.front());
|
||||
queue.pop_front();
|
||||
push_cv.notify_one();
|
||||
return { std::move(x) };
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void synchronized_queue<T>::clear ( )
|
||||
{
|
||||
std::lock_guard lock { mutex };
|
||||
queue.clear();
|
||||
push_cv.notify_all();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void synchronized_queue<T>::wait ( )
|
||||
{
|
||||
std::unique_lock lock { mutex };
|
||||
push_cv.wait(lock, [this]{ return queue.empty(); });
|
||||
}
|
||||
|
||||
}
|
||||
25
libs/util/include/psemek/util/thread.hpp
Normal file
25
libs/util/include/psemek/util/thread.hpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
struct thread
|
||||
: std::thread
|
||||
{
|
||||
template <typename ... Args>
|
||||
thread (Args && ... args)
|
||||
: std::thread(std::forward<Args>(args)...)
|
||||
{ }
|
||||
|
||||
thread (thread &&) = default;
|
||||
|
||||
~ thread ( )
|
||||
{
|
||||
if (joinable())
|
||||
join();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
57
libs/util/include/psemek/util/threadpool.hpp
Normal file
57
libs/util/include/psemek/util/threadpool.hpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/thread.hpp>
|
||||
#include <psemek/util/synchronyzed_queue.hpp>
|
||||
#include <psemek/util/movable_function.hpp>
|
||||
|
||||
#include <future>
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
struct threadpool
|
||||
{
|
||||
threadpool ( )
|
||||
: threadpool(std::max(1u, std::thread::hardware_concurrency()))
|
||||
{ }
|
||||
|
||||
threadpool (std::size_t thread_count)
|
||||
{
|
||||
start(thread_count);
|
||||
}
|
||||
|
||||
~ threadpool ( )
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
auto dispatch (F && f)
|
||||
{
|
||||
using R = decltype(f());
|
||||
|
||||
std::packaged_task<R()> task { std::forward<F>(f) };
|
||||
|
||||
auto result = task.get_future();
|
||||
|
||||
tasks_queue.push(std::move(task));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void start (std::size_t thread_count);
|
||||
|
||||
void stop ( );
|
||||
|
||||
void wait ( )
|
||||
{
|
||||
tasks_queue.wait();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<util::thread> threads;
|
||||
util::synchronized_queue<movable_function<void()>> tasks_queue;
|
||||
};
|
||||
|
||||
}
|
||||
31
libs/util/include/psemek/util/timer.hpp
Normal file
31
libs/util/include/psemek/util/timer.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/clock.hpp>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename Duration = std::chrono::duration<double>, typename Clock = std::chrono::system_clock>
|
||||
struct timer
|
||||
: clock<Duration, Clock>
|
||||
{
|
||||
timer (Duration duration)
|
||||
: duration_(duration)
|
||||
{ }
|
||||
|
||||
explicit operator bool ( )
|
||||
{
|
||||
if (this->duration() >= duration_)
|
||||
{
|
||||
this->restart();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
Duration duration_;
|
||||
};
|
||||
|
||||
}
|
||||
59
libs/util/include/psemek/util/to_string.hpp
Normal file
59
libs/util/include/psemek/util/to_string.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename Char, typename Traits = std::char_traits<Char>, typename ... Args>
|
||||
std::basic_string<Char, Traits> to_string (Args const & ... args)
|
||||
{
|
||||
std::basic_ostringstream<Char, Traits> oss;
|
||||
|
||||
((oss << args), ...);
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename ... Args>
|
||||
std::string to_string (Args const & ... args)
|
||||
{
|
||||
return detail::to_string<char>(args...);
|
||||
}
|
||||
|
||||
template <typename ... Args>
|
||||
std::wstring to_wstring (Args const & ... args)
|
||||
{
|
||||
return detail::to_string<wchar_t>(args...);
|
||||
}
|
||||
|
||||
template <typename ... Args>
|
||||
std::u32string to_u32string (Args const & ... args)
|
||||
{
|
||||
return detail::to_string<char32_t>(args...);
|
||||
}
|
||||
|
||||
template <typename T, typename Char, typename Traits>
|
||||
T from_string (std::basic_string<Char, Traits> const & s)
|
||||
{
|
||||
std::basic_istringstream<Char, Traits> iss(s);
|
||||
T x;
|
||||
iss >> x;
|
||||
if (!iss)
|
||||
throw std::invalid_argument("failed to parse from string");
|
||||
return x;
|
||||
}
|
||||
|
||||
template <typename T, typename Char>
|
||||
T from_string (Char const * s)
|
||||
{
|
||||
return from_string<T, Char, std::char_traits<Char>>(s);
|
||||
}
|
||||
|
||||
}
|
||||
11
libs/util/include/psemek/util/unicode.hpp
Normal file
11
libs/util/include/psemek/util/unicode.hpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
std::string to_utf8 (std::u32string const & str);
|
||||
std::u32string from_utf8 (std::string const & str);
|
||||
|
||||
}
|
||||
14
libs/util/source/assert.cpp
Normal file
14
libs/util/source/assert.cpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#include <psemek/util/assert.hpp>
|
||||
#include <psemek/util/to_string.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
[[noreturn]] bool assert_handler (char const * expression, char const * file, int line)
|
||||
{
|
||||
throw std::runtime_error(to_string(file, ":", line, " Assertion failed: ", expression));
|
||||
}
|
||||
|
||||
}
|
||||
18
libs/util/source/movable_function.cpp
Normal file
18
libs/util/source/movable_function.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#include <psemek/util/movable_function.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
void bad_function_call ( )
|
||||
{
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
13
libs/util/source/not_implemented.cpp
Normal file
13
libs/util/source/not_implemented.cpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#include <psemek/util/not_implemented.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
void not_implemented ( )
|
||||
{
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
}
|
||||
110
libs/util/source/pixmap.cpp
Normal file
110
libs/util/source/pixmap.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#include <psemek/util/pixmap.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
pixmap_monochrome read_pgm(std::istream & is)
|
||||
{
|
||||
auto fail = [](std::string str)
|
||||
{
|
||||
throw std::runtime_error("Error loading PGM image: " + str);
|
||||
};
|
||||
|
||||
std::string line;
|
||||
std::getline(is, line);
|
||||
|
||||
bool binary = true;
|
||||
|
||||
if (line == "P2")
|
||||
{
|
||||
binary = false;
|
||||
}
|
||||
else if (line == "P5")
|
||||
{
|
||||
binary = true;
|
||||
}
|
||||
else
|
||||
fail("unknown format " + line);
|
||||
|
||||
std::size_t width, height;
|
||||
std::size_t max;
|
||||
pixmap_monochrome pixmap;
|
||||
|
||||
is >> width >> height >> max;
|
||||
|
||||
if (max != 255)
|
||||
fail("max value " + std::to_string(max) + " is not supported");
|
||||
|
||||
pixmap.resize(width, height);
|
||||
|
||||
if (binary)
|
||||
is.read(reinterpret_cast<char *>(pixmap.data()), width * height * sizeof(pixmap.data()[0]));
|
||||
else
|
||||
fail("P2 format is not supported");
|
||||
|
||||
if (!is)
|
||||
fail("stream error");
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
pixmap_rgb load_ppm(std::istream & is)
|
||||
{
|
||||
auto fail = [](std::string str)
|
||||
{
|
||||
throw std::runtime_error("Error loading PPM image: " + str);
|
||||
};
|
||||
|
||||
std::string line;
|
||||
std::getline(is, line);
|
||||
|
||||
bool binary = true;
|
||||
|
||||
if (line == "P3")
|
||||
{
|
||||
binary = false;
|
||||
}
|
||||
else if (line == "P6")
|
||||
{
|
||||
binary = true;
|
||||
}
|
||||
else
|
||||
fail("unknown format " + line);
|
||||
|
||||
std::size_t width, height;
|
||||
std::size_t max;
|
||||
pixmap_rgb pixmap;
|
||||
|
||||
is >> width >> height >> max;
|
||||
|
||||
if (max != 255)
|
||||
fail("max value " + std::to_string(max) + " is not supported");
|
||||
|
||||
pixmap.resize(width, height);
|
||||
|
||||
if (binary)
|
||||
is.read(reinterpret_cast<char *>(pixmap.data()), width * height * sizeof(pixmap.data()[0]));
|
||||
else
|
||||
fail("P3 format is not supported");
|
||||
|
||||
if (!is)
|
||||
fail("stream error");
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
void write_pgm(pixmap_monochrome const & p, std::ostream & os)
|
||||
{
|
||||
os << "P5\n" << p.width() << " " << p.height() << "\n255\n";
|
||||
os.write(reinterpret_cast<char const *>(p.data()), p.width() * p.height() * sizeof(p.data()[0]));
|
||||
}
|
||||
|
||||
void write_ppm(pixmap_rgb const & p, std::ostream & os)
|
||||
{
|
||||
os << "P6\n" << p.width() << " " << p.height() << "\n255\n";
|
||||
os.write(reinterpret_cast<char const *>(p.data()), p.width() * p.height() * sizeof(p.data()[0]));
|
||||
}
|
||||
|
||||
}
|
||||
66
libs/util/source/pretty_print_time.cpp
Normal file
66
libs/util/source/pretty_print_time.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#include <psemek/util/pretty_print.hpp>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
void pretty_print_time (std::ostream & o, std::int64_t d, std::int64_t up_to)
|
||||
{
|
||||
static constexpr const std::int64_t durations[8] = {
|
||||
604800 * 1000000000ull,
|
||||
86400 * 1000000000ull,
|
||||
3600 * 1000000000ull,
|
||||
60 * 1000000000ull,
|
||||
1000000000ull,
|
||||
1000000ull,
|
||||
1000ull,
|
||||
1ull
|
||||
};
|
||||
|
||||
static const std::string_view suffixes[8] = {
|
||||
"w",
|
||||
"d",
|
||||
"h",
|
||||
"m",
|
||||
"s",
|
||||
"ms",
|
||||
"us",
|
||||
"ns"
|
||||
};
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (std::size_t i = 0; i < 8; ++i)
|
||||
{
|
||||
if (up_to > durations[i]) break;
|
||||
|
||||
std::int64_t value = d / durations[i];
|
||||
d %= durations[i];
|
||||
|
||||
if (value == 0) continue;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
o << ' ';
|
||||
|
||||
o << value << suffixes[i];
|
||||
}
|
||||
|
||||
// [ first == true ] means no output was generated
|
||||
// Find first matching suffix and output zero
|
||||
if (first) for (std::size_t i = 0; i < 8; ++i)
|
||||
{
|
||||
if (up_to >= durations[i])
|
||||
{
|
||||
o << 0 << suffixes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
35
libs/util/source/threadpool.cpp
Normal file
35
libs/util/source/threadpool.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#include <psemek/util/threadpool.hpp>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
void threadpool::start (std::size_t thread_count)
|
||||
{
|
||||
for (std::size_t th = 0; th < thread_count; ++th)
|
||||
{
|
||||
threads.emplace_back([this]
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
auto task = tasks_queue.pop();
|
||||
|
||||
if (!task)
|
||||
break;
|
||||
|
||||
task();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void threadpool::stop ( )
|
||||
{
|
||||
tasks_queue.clear();
|
||||
for (auto const & thread: threads)
|
||||
{
|
||||
tasks_queue.push({});
|
||||
}
|
||||
threads.clear();
|
||||
}
|
||||
|
||||
}
|
||||
21
libs/util/source/unicode.cpp
Normal file
21
libs/util/source/unicode.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#include <psemek/util/unicode.hpp>
|
||||
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
using converter = std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t>;
|
||||
|
||||
std::string to_utf8 (std::u32string const & str)
|
||||
{
|
||||
return converter().to_bytes(str);
|
||||
}
|
||||
|
||||
std::u32string from_utf8 (std::string const & str)
|
||||
{
|
||||
return converter().from_bytes(str);
|
||||
}
|
||||
|
||||
}
|
||||
10
todo.md
Normal file
10
todo.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
* Use the same code style everywhere (mostly about spaces after function names)
|
||||
* Make sure program & shaders are deleted properly if program creation fails
|
||||
* Remove gfx::vertex, setup mesh using attributes directly
|
||||
* Design affine transforms in geom & use them instead of matrices when appropriate
|
||||
* Create an 'app' module that simplifies application creation
|
||||
* Implement pixmap font rendering
|
||||
* Create a simple generic primive painter
|
||||
* Design resources system
|
||||
* Design logging system
|
||||
* Design ui system
|
||||
Loading…
Add table
Reference in a new issue