Commit ab66aca6 authored by Jonathan Kunstwald's avatar Jonathan Kunstwald
Browse files

Merge develop, fix usage of tg::abs, increase quat is_normalized default eps

parents 452c82b6 41d86538
......@@ -9,12 +9,14 @@
#include <typed-geometry/types/objects/box.hh>
#include <typed-geometry/types/objects/ellipse.hh>
#include <typed-geometry/types/objects/frustum.hh>
#include <typed-geometry/types/objects/halfspace.hh>
#include <typed-geometry/types/objects/line.hh>
#include <typed-geometry/types/objects/plane.hh>
#include <typed-geometry/detail/operators/ops_pos.hh>
#include <typed-geometry/functions/vector/dot.hh>
#include <typed-geometry/functions/vector/length.hh>
// Header for all constructors that depend on functions
......
......@@ -6,11 +6,11 @@ namespace tg
{
namespace literals
{
constexpr angle32 operator""_deg(u64 v) { return angle32::from_degree(f32(v)); }
constexpr angle32 operator""_deg(unsigned long long v) { return angle32::from_degree(f32(v)); }
constexpr angle64 operator""_deg(long double v) { return angle64::from_degree(f64(v)); }
constexpr angle32 operator""_degf(long double v) { return angle32::from_degree(f32(v)); }
constexpr angle32 operator""_rad(u64 v) { return angle32::from_radians(f32(v)); }
constexpr angle32 operator""_rad(unsigned long long v) { return angle32::from_radians(f32(v)); }
constexpr angle64 operator""_rad(long double v) { return angle64::from_radians(f64(v)); }
constexpr angle32 operator""_radf(long double v) { return angle32::from_radians(f32(v)); }
} // namespace literals
......
......@@ -2,6 +2,7 @@
#include <typed-geometry/types/comp.hh>
#include <typed-geometry/types/objects/box.hh>
#include <typed-geometry/types/objects/line.hh>
#include <typed-geometry/types/objects/ray.hh>
#include <typed-geometry/types/objects/segment.hh>
......
......@@ -16,13 +16,18 @@
#include <typed-geometry/functions/objects/distance.hh>
#include <typed-geometry/functions/objects/edges.hh>
#include <typed-geometry/functions/objects/faces.hh>
#include <typed-geometry/functions/objects/frustum.hh>
#include <typed-geometry/functions/objects/intersection.hh>
#include <typed-geometry/functions/objects/normal.hh>
#include <typed-geometry/functions/objects/perimeter.hh>
#include <typed-geometry/functions/objects/plane.hh>
#include <typed-geometry/functions/objects/project.hh>
#include <typed-geometry/functions/objects/rasterize.hh>
#include <typed-geometry/functions/objects/segmentize.hh>
#include <typed-geometry/functions/objects/size.hh>
#include <typed-geometry/functions/objects/tangent.hh>
#include <typed-geometry/functions/objects/triangle.hh>
#include <typed-geometry/functions/objects/triangulate.hh>
#include <typed-geometry/functions/objects/triangulation.hh>
#include <typed-geometry/functions/objects/vertices.hh>
#include <typed-geometry/functions/objects/volume.hh>
#pragma once
#include <typed-geometry/detail/operators.hh>
#include <typed-geometry/functions/vector/angle.hh>
#include <typed-geometry/functions/vector/cross.hh>
#include <typed-geometry/functions/vector/distance.hh>
......
......@@ -10,6 +10,8 @@
*
* - min_element
* - max_element
* - max_index
* - min_index
* - average (same as arithmetic_mean)
* - mean (same as arithmetic_mean)
* - arithmetic_mean
......@@ -39,11 +41,11 @@ template <class T = void, class RangeT, class TransformF, class ReduceF>
using U = std::decay_t<decltype(f(t(R(*it)), t(R(*it))))>;
auto const e = tg::end(values);
U r = t(R(*it));
it++;
++it;
while (it != e)
{
r = f(r, t(R(*it)));
it++;
++it;
}
return r;
}
......@@ -59,7 +61,7 @@ template <class RangeT, class KeyT>
auto min_e = it;
auto min_v = key(*it);
it++;
++it;
while (it != e)
{
auto v = key(*it);
......@@ -68,7 +70,7 @@ template <class RangeT, class KeyT>
min_v = v;
min_e = it;
}
it++;
++it;
}
return *min_e;
......@@ -84,7 +86,7 @@ template <class RangeT, class KeyT>
auto max_e = it;
auto max_v = key(*it);
it++;
++it;
while (it != e)
{
auto v = key(*it);
......@@ -93,7 +95,7 @@ template <class RangeT, class KeyT>
max_v = v;
max_e = it;
}
it++;
++it;
}
return *max_e;
......@@ -111,6 +113,63 @@ template <class RangeT, class TransformT = identity_fun>
return detail::fold_right(values, transform, [](auto&& a, auto&& b) { return max(a, b); });
}
/// returns the index of the max element
template <class RangeT, class TransformT = identity_fun>
[[nodiscard]] constexpr size_t max_index(RangeT const& values, TransformT&& transform = {})
{
TG_CONTRACT(tg::begin(values) != tg::end(values) && "values must not be empty");
size_t curr_idx = 0;
auto it = tg::begin(values);
auto const end = tg::end(values);
auto max_v = transform(*it);
size_t max_idx = curr_idx;
++it;
++curr_idx;
while (it != end)
{
auto v = transform(*it);
if (v > max_v)
{
max_v = v;
max_idx = curr_idx;
}
++it;
++curr_idx;
}
return max_idx;
}
/// returns the index of the min element
template <class RangeT, class TransformT = identity_fun>
[[nodiscard]] constexpr size_t min_index(RangeT const& values, TransformT&& transform = {})
{
TG_CONTRACT(tg::begin(values) != tg::end(values) && "values must not be empty");
size_t curr_idx = 0;
auto it = tg::begin(values);
auto const end = tg::end(values);
auto min_v = transform(*it);
size_t min_idx = curr_idx;
++it;
++curr_idx;
while (it != end)
{
auto v = transform(*it);
if (v < min_v)
{
min_v = v;
min_idx = curr_idx;
}
++it;
++curr_idx;
}
return min_idx;
}
template <class T = void, class RangeT = void, class TransformT = identity_fun>
[[nodiscard]] constexpr auto sum(RangeT const& values, TransformT&& transform = {})
{
......
......@@ -12,7 +12,7 @@
namespace tg
{
template <class ScalarT>
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_opengl(pos<3, ScalarT> const& eye, dir<3, ScalarT> const& fwd, vec<3, ScalarT> const& ref_up)
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_opengl(pos<3, ScalarT> const& eye, dir<3, ScalarT> const& fwd, vec_or_dir<3, ScalarT> const& ref_up)
{
auto const right = normalize(cross(vec(fwd), ref_up));
auto const up = vec(cross(right, fwd));
......@@ -40,7 +40,7 @@ template <class ScalarT>
return m;
}
template <class ScalarT>
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_directx(pos<3, ScalarT> const& eye, dir<3, ScalarT> const& fwd, vec<3, ScalarT> const& ref_up)
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_directx(pos<3, ScalarT> const& eye, dir<3, ScalarT> const& fwd, vec_or_dir<3, ScalarT> const& ref_up)
{
auto const right = normalize(cross(ref_up, vec(fwd)));
auto const up = vec(cross(fwd, right));
......@@ -68,22 +68,22 @@ template <class ScalarT>
return m;
}
template <class ScalarT>
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_opengl(pos<3, ScalarT> const& eye, pos<3, ScalarT> const& target, vec<3, ScalarT> const& ref_up)
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_opengl(pos<3, ScalarT> const& eye, pos<3, ScalarT> const& target, vec_or_dir<3, ScalarT> const& ref_up)
{
return look_at_opengl(eye, normalize(target - eye), ref_up);
}
template <class ScalarT>
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_opengl(pos<3, ScalarT> const& eye, vec<3, ScalarT> const& fwd, vec<3, ScalarT> const& ref_up)
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_opengl(pos<3, ScalarT> const& eye, vec<3, ScalarT> const& fwd, vec_or_dir<3, ScalarT> const& ref_up)
{
return look_at_opengl(eye, normalize(fwd), ref_up);
}
template <class ScalarT>
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_directx(pos<3, ScalarT> const& eye, pos<3, ScalarT> const& target, vec<3, ScalarT> const& ref_up)
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_directx(pos<3, ScalarT> const& eye, pos<3, ScalarT> const& target, vec_or_dir<3, ScalarT> const& ref_up)
{
return look_at_directx(eye, normalize(target - eye), ref_up);
}
template <class ScalarT>
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_directx(pos<3, ScalarT> const& eye, vec<3, ScalarT> const& fwd, vec<3, ScalarT> const& ref_up)
[[nodiscard]] constexpr mat<4, 4, ScalarT> look_at_directx(pos<3, ScalarT> const& eye, vec<3, ScalarT> const& fwd, vec_or_dir<3, ScalarT> const& ref_up)
{
return look_at_directx(eye, normalize(fwd), ref_up);
}
......@@ -91,21 +91,21 @@ template <class ScalarT>
template <class ScalarT>
[[deprecated("use explicit _opengl or _directx version")]] [[nodiscard]] constexpr mat<4, 4, ScalarT> look_at(pos<3, ScalarT> const& eye,
dir<3, ScalarT> const& fwd,
vec<3, ScalarT> const& ref_up)
vec_or_dir<3, ScalarT> const& ref_up)
{
return look_at_opengl(eye, fwd, ref_up);
}
template <class ScalarT>
[[deprecated("use explicit _opengl or _directx version")]] [[nodiscard]] constexpr mat<4, 4, ScalarT> look_at(pos<3, ScalarT> const& eye,
pos<3, ScalarT> const& target,
vec<3, ScalarT> const& ref_up)
vec_or_dir<3, ScalarT> const& ref_up)
{
return look_at_opengl(eye, normalize(target - eye), ref_up);
}
template <class ScalarT>
[[deprecated("use explicit _opengl or _directx version")]] [[nodiscard]] constexpr mat<4, 4, ScalarT> look_at(pos<3, ScalarT> const& eye,
vec<3, ScalarT> const& fwd,
vec<3, ScalarT> const& ref_up)
vec_or_dir<3, ScalarT> const& ref_up)
{
return look_at_opengl(eye, normalize(fwd), ref_up);
}
......
......@@ -25,7 +25,7 @@ template <int D, class ScalarT>
template <int D, class ScalarT, class TraitsT>
[[nodiscard]] constexpr aabb<D, ScalarT> aabb_of(aabb<D, ScalarT, TraitsT> const& b)
{
return b;
return aabb<D, ScalarT>(b);
}
template <int D, class ScalarT, class TraitsT>
......
......@@ -5,9 +5,11 @@
#include <typed-geometry/types/objects/capsule.hh>
#include <typed-geometry/types/objects/cylinder.hh>
#include <typed-geometry/types/objects/ellipse.hh>
#include <typed-geometry/types/objects/halfspace.hh>
#include <typed-geometry/types/objects/hemisphere.hh>
#include <typed-geometry/types/objects/inf_cone.hh>
#include <typed-geometry/types/objects/inf_cylinder.hh>
#include <typed-geometry/types/objects/plane.hh>
#include <typed-geometry/types/objects/pyramid.hh>
#include <typed-geometry/types/objects/sphere.hh>
......@@ -75,6 +77,11 @@ template <class ScalarT, class TraitsT>
{
return {v.center, v.radius, v.normal};
}
template <int D, class ScalarT>
[[nodiscard]] constexpr plane<D, ScalarT> boundary_of(halfspace<D, ScalarT> const& v)
{
return {v.normal, v.dis};
}
// === no caps versions ===
......
......@@ -2,13 +2,9 @@
#include <typed-geometry/detail/utility.hh>
#include <typed-geometry/types/objects/line.hh>
#include <typed-geometry/types/objects/plane.hh>
#include <typed-geometry/types/objects/segment.hh>
#include <typed-geometry/types/pos.hh>
#include <typed-geometry/types/quadric.hh>
#include <typed-geometry/functions/basic/mix.hh>
#include "coordinates.hh"
#include "project.hh"
......@@ -45,7 +41,7 @@ template <class ScalarT>
auto d0d1 = dot(l0.dir, l1.dir);
auto b0 = dot(l1.pos - l0.pos, l0.dir);
auto b1 = dot(l1.pos - l0.pos, l1.dir);
auto [t0, t1] = inverse(tg::mat<2, 2, ScalarT>::from_cols({1, d0d1}, {-d0d1, -1})) * tg::vec2(b0, b1);
auto [t0, t1] = inverse(mat<2, 2, ScalarT>::from_cols({ScalarT(1), d0d1}, {-d0d1, ScalarT(-1)})) * vec<2, ScalarT>(b0, b1);
return {t0, t1};
}
......@@ -56,6 +52,17 @@ template <class ScalarT>
return {l0[t0], l1[t1]};
}
template <class ScalarT>
[[nodiscard]] constexpr pair<ScalarT, ScalarT> closest_points_parameters(segment<3, ScalarT> const& s, line<3, ScalarT> const& l)
{
auto ls = inf_of(s);
auto len = length(s);
auto [ts, tl] = closest_points_parameters(ls, l);
auto tClamped = clamp(ts, ScalarT(0), len);
return {tClamped / len, coordinates(l, ls[tClamped])};
}
// =========== Other Implementations ===========
......
......@@ -8,6 +8,7 @@
#include <typed-geometry/types/objects/cone.hh>
#include <typed-geometry/types/objects/cylinder.hh>
#include <typed-geometry/types/objects/ellipse.hh>
#include <typed-geometry/types/objects/frustum.hh>
#include <typed-geometry/types/objects/halfspace.hh>
#include <typed-geometry/types/objects/hemisphere.hh>
#include <typed-geometry/types/objects/inf_cone.hh>
......@@ -46,6 +47,20 @@ template <int D, class ScalarT>
return b == o;
}
// default implementation for contains(objA, objB) that works when objA is solid and vertices_of(objB) is defined
template <class A, class B>
[[nodiscard]] constexpr auto contains(A const& a, B const& b, dont_deduce<typename B::scalar_t> eps = static_cast<typename B::scalar_t>(0))
-> enable_if<std::is_same_v<typename object_traits<A>::tag_t, default_object_tag>, decltype((void)vertices_of(b), false)>
{
for (auto const& vertex : vertices_of(b))
if (!contains(a, vertex, eps))
return false;
return true;
}
// object specific implementations for contains(obj, pos)
template <class ScalarT>
[[nodiscard]] constexpr bool contains(aabb<1, ScalarT> const& b, ScalarT const& o, dont_deduce<ScalarT> eps = ScalarT(0))
{
......@@ -120,8 +135,7 @@ template <int D, class ScalarT>
if (ri > bi + eps)
return false; // False if outside of the aabb in any dimension
if (!onSomeBoundary && (ri >= bi - eps))
onSomeBoundary = true;
onSomeBoundary = onSomeBoundary || (ri >= bi - eps);
}
return onSomeBoundary; // True, if at on the boundary in at least one dimension
}
......@@ -462,4 +476,14 @@ template <int D, class ScalarT>
return angle_between(dir<D, ScalarT>(apexOuterToP), c.opening_dir) <= ScalarT(0.5) * c.opening_angle
&& angle_between(dir<D, ScalarT>(apexInnerToP), c.opening_dir) >= ScalarT(0.5) * c.opening_angle;
}
template <class ScalarT>
[[nodiscard]] constexpr bool contains(frustum<3, ScalarT> const& f, pos<3, ScalarT> const& p, dont_deduce<ScalarT> eps = ScalarT(0))
{
for (auto const& pl : f.planes)
if (signed_distance(p, pl) > eps)
return false;
return true;
}
} // namespace tg
......@@ -91,45 +91,72 @@ template <class ScalarT>
}
template <class ScalarT>
[[nodiscard]] constexpr fractional_result<ScalarT> distance(segment<2, ScalarT> const& s0, segment<2, ScalarT> const& s1)
[[nodiscard]] constexpr fractional_result<ScalarT> distance_sqr(segment<2, ScalarT> const& s0, segment<2, ScalarT> const& s1)
{
auto l0 = line<2, ScalarT>::from_points(s0.pos0, s0.pos1);
auto l1 = line<2, ScalarT>::from_points(s1.pos0, s1.pos1);
auto sl0 = distance(s0.pos0, s0.pos1);
auto sl1 = distance(s1.pos0, s1.pos1);
auto l0 = inf_of(s0);
auto l1 = inf_of(s1);
auto len0 = length(s0);
auto len1 = length(s1);
auto [t0, t1] = intersection_parameters(l0, l1);
if (ScalarT(0) <= t0 && t0 <= ScalarT(sl0) && //
ScalarT(0) <= t1 && t1 <= ScalarT(sl1))
if (ScalarT(0) <= t0 && t0 <= len0 && //
ScalarT(0) <= t1 && t1 <= len1)
return ScalarT(0); // intersects
auto p0 = t0 < ScalarT(0) ? s0.pos0 : s0.pos1;
auto p1 = t1 < ScalarT(0) ? s1.pos0 : s1.pos1;
auto p0 = t0 * ScalarT(2) < len0 ? s0.pos0 : s0.pos1;
auto p1 = t1 * ScalarT(2) < len1 ? s1.pos0 : s1.pos1;
return min(distance(p0, s1), distance(p1, s0));
return min(distance_sqr(p0, s1), distance_sqr(p1, s0));
}
template <class ScalarT>
[[nodiscard]] constexpr fractional_result<ScalarT> distance(segment<3, ScalarT> const& s0, segment<3, ScalarT> const& s1)
[[nodiscard]] constexpr fractional_result<ScalarT> distance_sqr(segment<3, ScalarT> const& s0, segment<3, ScalarT> const& s1)
{
auto l0 = line<3, ScalarT>::from_points(s0.pos0, s0.pos1);
auto l1 = line<3, ScalarT>::from_points(s1.pos0, s1.pos1);
auto sl0 = distance(s0.pos0, s0.pos1);
auto sl1 = distance(s1.pos0, s1.pos1);
auto l0 = inf_of(s0);
auto l1 = inf_of(s1);
auto len0 = length(s0);
auto len1 = length(s1);
auto [t0, t1] = closest_points_parameters(l0, l1);
if (ScalarT(0) <= t0 && t0 <= ScalarT(sl0) && //
ScalarT(0) <= t1 && t1 <= ScalarT(sl1))
return distance(l0[t0], l1[t1]); // closest points is inside segments
if (ScalarT(0) <= t0 && t0 <= len0 && //
ScalarT(0) <= t1 && t1 <= len1)
return distance_sqr(l0[t0], l1[t1]); // closest points is inside segments
auto p0 = t0 * ScalarT(2) < len0 ? s0.pos0 : s0.pos1;
auto p1 = t1 * ScalarT(2) < len1 ? s1.pos0 : s1.pos1;
return min(distance_sqr(p0, s1), distance_sqr(p1, s0));
}
template <class ScalarT>
[[nodiscard]] constexpr fractional_result<ScalarT> distance_sqr(segment<2, ScalarT> const& s, line<2, ScalarT> const& l)
{
auto ls = inf_of(s);
auto len = length(s);
auto [ts, tl] = intersection_parameters(ls, l);
if (ScalarT(0) <= ts && ts <= len)
return ScalarT(0); // intersects
auto p0 = t0 < ScalarT(0) ? s0.pos0 : s0.pos1;
auto p1 = t1 < ScalarT(0) ? s1.pos0 : s1.pos1;
auto p = ts * ScalarT(2) < len ? s.pos0 : s.pos1;
return distance_sqr(p, l);
}
template <class ScalarT>
[[nodiscard]] constexpr fractional_result<ScalarT> distance_sqr(segment<3, ScalarT> const& s, line<3, ScalarT> const& l)
{
auto ls = inf_of(s);
auto len = length(s);
return min(distance(p0, s1), distance(p1, s0));
auto [ts, tl] = closest_points_parameters(ls, l);
auto tClamped = clamp(ts, ScalarT(0), len);
return distance_sqr(ls[tClamped], l);
}
template <int D, class ScalarT>
[[nodiscard]] constexpr fractional_result<ScalarT> distance_sqr(line<D, ScalarT> const& l, segment<D, ScalarT> const& s)
{
return distance_sqr(s, l);
}
// TODO: use GJK or something?
......
......@@ -17,7 +17,7 @@ template <int D, class ScalarT>
}
template <int D, class ScalarT>
[[nodiscard]] constexpr array<segment<2, ScalarT>, 4> edges_of(quad<D, ScalarT> const& q)
[[nodiscard]] constexpr array<segment<D, ScalarT>, 4> edges_of(quad<D, ScalarT> const& q)
{
return {{{q.pos00, q.pos10}, {q.pos10, q.pos11}, {q.pos11, q.pos01}, {q.pos01, q.pos00}}};
}
......@@ -60,14 +60,14 @@ template <class ScalarT, class TraitsT>
}
template <class ScalarT, int DomainD, class TraitsT>
[[nodiscard]] constexpr array<segment<2, ScalarT>, 4> edges_of(box<2, ScalarT, DomainD, TraitsT> const& b)
[[nodiscard]] constexpr array<segment<DomainD, ScalarT>, 4> edges_of(box<2, ScalarT, DomainD, TraitsT> const& b)
{
const auto vs = vertices_of(b);
return {{{vs[0], vs[1]}, {vs[1], vs[2]}, {vs[2], vs[3]}, {vs[3], vs[0]}}};
}
template <class ScalarT, int DomainD, class TraitsT>
[[nodiscard]] constexpr array<segment<3, ScalarT>, 12> edges_of(box<3, ScalarT, DomainD, TraitsT> const& b)
[[nodiscard]] constexpr array<segment<DomainD, ScalarT>, 12> edges_of(box<3, ScalarT, DomainD, TraitsT> const& b)
{
const auto vs = vertices_of(b);
return {{
......@@ -79,12 +79,10 @@ template <class ScalarT, int DomainD, class TraitsT>
}};
}
template <class BaseT, class TraitsT>
template <class BaseT, class TraitsT, typename = std::enable_if_t<!std::is_same_v<BaseT, sphere<2, typename BaseT::scalar_t, 3>>>>
[[nodiscard]] constexpr auto edges_of(pyramid<BaseT, TraitsT> const& py)
{
using ScalarT = typename BaseT::scalar_t;
static_assert(!std::is_same_v<BaseT, sphere<2, ScalarT, 3>>, "not possible for cones");
const auto apex = apex_of(py);
const auto edgesBase = edges_of(py.base);
auto res = array<segment<3, ScalarT>, edgesBase.size() * 2>();
......
......@@ -62,12 +62,11 @@ template <class ScalarT, class TraitsT>
return {{faceX0, faceX1, faceY0, faceY1, faceZ0, faceZ1}};
}
template <class BaseT>
template <class BaseT, typename = std::enable_if_t<!std::is_same_v<BaseT, sphere<2, typename BaseT::scalar_t, 3>>>>
[[nodiscard]] constexpr auto faces_of(pyramid<BaseT, boundary_no_caps_tag> const& py)
{
using ScalarT = typename BaseT::scalar_t;
static_assert(is_floating_point<ScalarT>, "cannot be guaranteed for integers");
static_assert(!std::is_same_v<BaseT, sphere<2, ScalarT, 3>>, "not possible for cones");
const auto apex = apex_of(py);
const auto verts = vertices_of(py.base);
......@@ -77,7 +76,7 @@ template <class BaseT>
return triangles;
}
template <class BaseT, class TraitsT>
template <class BaseT, class TraitsT, typename = std::enable_if_t<!std::is_same_v<BaseT, sphere<2, typename BaseT::scalar_t, 3>>>>
[[nodiscard]] constexpr auto faces_of(pyramid<BaseT, TraitsT> const& py)
{
auto mantle = faces_of(boundary_no_caps_of(py));
......
#pragma once
#include <typed-geometry/feature/matrix.hh>
#include <typed-geometry/feature/vector.hh>
#include <typed-geometry/types/objects/aabb.hh>
#include <typed-geometry/types/objects/frustum.hh>
namespace tg
{
template <class ScalarT, class TraitsT>
constexpr frustum<3, ScalarT, TraitsT> frustum<3, ScalarT, TraitsT>::from_view_proj(mat<4, 4, ScalarT> const& m, aabb<3, ScalarT> const& ndc)
{
// TODO: see if insight from http://www8.cs.umu.se/kurser/5DV051/HT12/lab/plane_extraction.pdf can be used
using frustum_t = frustum<3, ScalarT, TraitsT>;
using comp_t = tg::comp<3, ScalarT>;
using plane_t = tg::plane<3, ScalarT>;