/* Copyright (C) 2017 Eric Wasylishen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See file, 'COPYING', for details. */ #pragma once #include #include #include /**! * touching a side/edge/corner is considered touching */ template class aabb { public: using value_type = qvec; class intersection_t { public: bool valid; aabb bbox; constexpr intersection_t() : valid(false), bbox(value_type{}, value_type{}) { } constexpr intersection_t(const aabb &i) : valid(true), bbox(i) { } constexpr bool operator==(const intersection_t &other) const { return valid == other.valid && bbox == other.bbox; } constexpr operator bool() const { return valid; } }; private: template friend class aabb; std::array m_corners; constexpr void fix() { for (size_t i = 0; i < N; i++) { if (m_corners[1][i] < m_corners[0][i]) { m_corners[1][i] = m_corners[0][i]; } } } public: constexpr aabb() : m_corners({value_type{ std::numeric_limits::max() }, value_type{ std::numeric_limits::lowest() }}) { } constexpr aabb(const value_type &mins, const value_type &maxs) : m_corners({ mins, maxs }) { fix(); } constexpr aabb(const value_type &points) : aabb(points, points) { } template constexpr aabb(const aabb &other) : aabb(other.m_corners[0], other.m_corners[1]) { } template, int> = 0> constexpr aabb(Iter start, Iter end) : aabb() { for (auto it = start; it != end; it++) { *this += *it; } } constexpr bool operator==(const aabb &other) const { return m_corners == other.m_corners; } constexpr const value_type &mins() const { return m_corners[0]; } constexpr const value_type &maxs() const { return m_corners[1]; } constexpr aabb translate(const value_type &vec) const { return {mins() + vec, maxs() + vec}; } template constexpr bool disjoint(const aabb &other, const F &epsilon = 0) const { for (size_t i = 0; i < N; i++) { if (maxs()[i] < (other.mins()[i] - epsilon) || mins()[i] > (other.maxs()[i] + epsilon)) { return true; } } return false; } template constexpr bool disjoint_or_touching(const aabb &other, const F &epsilon = 0) const { for (size_t i = 0; i < N; i++) { if (maxs()[i] <= (other.mins()[i] - epsilon) || mins()[i] >= (other.maxs()[i] + epsilon)) { return true; } } return false; } constexpr bool contains(const aabb &other) const { for (size_t i = 0; i < N; i++) { if (other.mins()[i] < mins()[i] || other.maxs()[i] > maxs()[i]) { return false; } } return true; } constexpr bool containsPoint(const value_type &p) const { for (size_t i = 0; i < N; i++) { if (!(p[i] >= mins()[i] && p[i] <= maxs()[i])) { return false; } } return true; } constexpr aabb expand(const value_type &pt) const { auto corners = m_corners; for (size_t i = 0; i < N; i++) { corners[0][i] = min(corners[0][i], pt[i]); corners[1][i] = max(corners[1][i], pt[i]); } return {corners[0], corners[1]}; } constexpr aabb operator+(const value_type &pt) const { return expand(pt); } constexpr aabb operator+(const aabb &other) const { return unionWith(other); } constexpr aabb unionWith(const aabb &other) const { return expand(other.mins()).expand(other.maxs()); } // in-place expansions constexpr aabb &expand_in_place(const value_type &pt) { for (size_t i = 0; i < N; i++) { m_corners[0][i] = min(m_corners[0][i], pt[i]); m_corners[1][i] = max(m_corners[1][i], pt[i]); } return *this; } constexpr aabb &operator+=(const value_type &pt) { return expand_in_place(pt); } constexpr aabb &operator+=(const aabb &other) { return unionWith_in_place(other); } constexpr aabb &unionWith_in_place(const aabb &other) { for (size_t i = 0; i < N; i++) { m_corners[0][i] = min({ m_corners[0][i], other.mins()[i], other.maxs()[i] }); m_corners[1][i] = max({ m_corners[1][i], other.mins()[i], other.maxs()[i] }); } return *this; } constexpr intersection_t intersectWith(const aabb &other) const { auto corners = m_corners; for (size_t i = 0; i < N; i++) { corners[0][i] = max(corners[0][i], other.mins()[i]); corners[1][i] = min(corners[1][i], other.maxs()[i]); if (corners[0][i] > corners[1][i]) { // empty intersection return {}; } } return {{corners[0], corners[1]}}; } constexpr value_type size() const { return maxs() - mins(); } constexpr bool valid() const { value_type our_size = size(); if (our_size[0] < static_cast(0) || our_size[1] < static_cast(0) || our_size[2] < static_cast(0)) { return false; } return true; } constexpr aabb grow(const value_type &size) const { return {mins() - size, maxs() + size}; } constexpr value_type &operator[](const size_t &index) { return m_corners[index]; } constexpr const value_type &operator[](const size_t &index) const { return m_corners[index]; } constexpr value_type centroid() const { return (mins() + maxs()) * 0.5; } constexpr V volume() const { auto s = size(); return s[0] * s[1] * s[2]; } constexpr auto begin() { return m_corners.begin(); } constexpr auto end() { return m_corners.end(); } constexpr auto begin() const { return m_corners.begin(); } constexpr auto end() const { return m_corners.end(); } // stream support auto stream_data() { return std::tie(m_corners); } }; // Fmt support template struct fmt::formatter> : formatter> { template auto format(const aabb &b, FormatContext &ctx) -> decltype(ctx.out()) { fmt::format_to(ctx.out(), "{{mins: "); fmt::formatter>::format(b.mins(), ctx); fmt::format_to(ctx.out(), ", maxs: "); fmt::formatter>::format(b.maxs(), ctx); fmt::format_to(ctx.out(), "}}"); return ctx.out(); } }; using aabb3d = aabb; using aabb2d = aabb; using aabb3f = aabb; using aabb2f = aabb;