Skip to content

src/core/bounds.h

Namespaces

Name
dakku
dakku namespace

Classes

Name
class dakku::BoundsBase
bounding box base
class dakku::Bounds2
2d bounding box
class dakku::Bounds3
3d bounding box
class dakku::Bounds2iIterator
2d integer bounds iterator, iterate all pixels inside

Source code

#ifndef DAKKU_CORE_BOUNDS_H_
#define DAKKU_CORE_BOUNDS_H_
#include <core/vector.h>

#include <iterator>
#include <utility>

namespace dakku {

template <ArithmeticType T, size_t S>
class BoundsBase {
 public:
  BoundsBase() = default;

  explicit BoundsBase(const Point<T, S> &p) : p_min(p), p_max(p) {}

  template <ArithmeticType U>
  explicit BoundsBase(const BoundsBase<U, S> &b)
      : p_min(Point<T, S>(b.p_min)), p_max(Point<T, S>(b.p_max)) {}

  BoundsBase(const Point<T, S> &p1, const Point<T, S> &p2)
      : p_min(min(p1, p2)), p_max(max(p1, p2)) {}

  BoundsBase(const sol::table &table)
      : p_min(table.get_or(1, table)), p_max(table.get_or(2, table)) {}

  [[nodiscard]] Vector<T, S> diagonal() const { return p_max - p_min; }

  [[nodiscard]] size_t max_extent() const {
    return this->diagonal().max_element_index();
  }

  bool operator==(const BoundsBase &rhs) const {
    return p_min == rhs.p_min && p_max == rhs.p_max;
  }

  bool operator!=(const BoundsBase &rhs) const {
    return p_min != rhs.p_min || p_max != rhs.p_max;
  }

  [[nodiscard]] Point<T, S> lerp(const Point<float, S> &t) const {
    Point<T, S> ret;
    for (size_t i = 0; i < S; ++i) ret[i] = std::lerp(p_min[i], p_max[i], t[i]);
    return ret;
  }

  [[nodiscard]] Vector<T, S> offset(const Point<T, S> &p) const {
    Vector<T, S> o = p - p_min;
    for (size_t i = 0; i < S; ++i)
      if (p_max[i] > p_min[i]) o[i] /= p_max[i] - p_min[i];
    return o;
  }

  friend bool overlaps(const BoundsBase &a, const BoundsBase &b) {
    for (size_t i = 0; i < S; ++i)
      if (!(a.p_max[i] >= b.p_min[i] && a.p_min[i] <= b.p_max[i])) return false;
    return true;
  }

  [[nodiscard]] bool overlaps(const BoundsBase &rhs) const {
    return overlaps(*this, rhs);
  }

  friend bool inside(const Point<T, S> &p, const BoundsBase &b) {
    for (size_t i = 0; i < S; ++i)
      if (!(p[i] >= b.p_min[i] && p[i] <= b.p_max[i])) return false;
    return true;
  }

  [[nodiscard]] bool inside(const Point<T, S> &p) const {
    return inside(p, *this);
  }

  friend bool inside_exclusive(const Point<T, S> &p, const BoundsBase &b) {
    for (size_t i = 0; i < S; ++i)
      if (!(p[i] >= b.p_min[i] && p[i] < b.p_max[i])) return false;
    return true;
  }

  [[nodiscard]] bool inside_exclusive(const Point<T, S> &p) const {
    return inside_exclusive(p, *this);
  }

  void bounding_sphere(Point<T, S> &center, float &rad) {
    center = (p_min + p_max) / 2;
    rad = inside(center, *this) ? distance(center, p_max) : 0;
  }

  friend BoundsBase operator|(const BoundsBase &a, const BoundsBase &b) {
    BoundsBase ret;
    ret.p_min = min(a.p_min, b.p_min);
    ret.p_max = max(a.p_max, b.p_max);
    return ret;
  }

  [[nodiscard]] BoundsBase union_bounds(const BoundsBase &rhs) const {
    return *this | rhs;
  }

  friend BoundsBase operator|(const BoundsBase &b, const Point<T, S> &p) {
    BoundsBase ret;
    ret.p_min = min(b.p_min, p);
    ret.p_max = max(b.p_max, p);
    return ret;
  }

  [[nodiscard]] BoundsBase union_bounds(const Point<T, S> &p) const {
    return *this | p;
  }

  friend BoundsBase operator&(const BoundsBase &a, const BoundsBase &b) {
    BoundsBase ret;
    // important: assign to pMin/pMax directly and don't run the BoundsBase()
    // constructor, since it takes min/max of the points passed to it.  In
    // turn, that breaks returning an invalid bound for the case where we
    // intersect non-overlapping bounds (as we'd like to happen).
    ret.p_min = max(a.p_min, b.p_min);
    ret.p_max = min(a.p_max, b.p_max);
    return ret;
  }

  [[nodiscard]] BoundsBase intersect(const BoundsBase &rhs) const {
    return *this & rhs;
  }

  friend decltype(auto) distance_squared(const Point<T, S> &p,
                                         const BoundsBase<T, S> &b) {
    return max(Vector<T, S>{}, max(b.p_min - p, p - b.p_max)).squared_norm();
  }

  friend decltype(auto) distance(const Point<T, S> &p,
                                 const BoundsBase<T, S> &b) {
    return std::sqrt(distance_squared(p, b));
  }

  [[nodiscard]] std::string to_string() const {
    return "[" + p_min.to_string() + ", " + p_max.to_string() + "]";
  }

  friend std::ostream &operator<<(std::ostream &os, const BoundsBase &b) {
    return os << b.to_string();
  }

  Point<T, S> p_min{std::numeric_limits<T>::max()};
  Point<T, S> p_max{std::numeric_limits<T>::lowest()};
};

template <ArithmeticType T>
class Bounds2 : public BoundsBase<T, 2> {
 public:
  using BoundsBase<T, 2>::BoundsBase;

  Bounds2(const BoundsBase<T, 2> &base) : BoundsBase<T, 2>(base) {}

  [[nodiscard]] decltype(auto) area() const {
    auto d = this->diagonal();
    return d.x() * d.y();
  }
};

using Bounds2f = Bounds2<float>;
using Bounds2i = Bounds2<int>;

template <ArithmeticType T>
class Bounds3 : public BoundsBase<T, 3> {
 public:
  using BoundsBase<T, 3>::BoundsBase;

  Bounds3(const BoundsBase<T, 3> &base) : BoundsBase<T, 3>(base) {}
};

using Bounds3f = Bounds3<float>;

class Bounds2iIterator : public std::forward_iterator_tag {
 public:
  explicit Bounds2iIterator(const Bounds2i &b, Point2i p)
      : b(b), p(std::move(p)) {}

  Bounds2iIterator operator++() {
    advance();
    return *this;
  }

  Bounds2iIterator operator++(int) {
    Bounds2iIterator ret = *this;
    advance();
    return ret;
  }

  bool operator==(const Bounds2iIterator &rhs) const {
    return p == rhs.p && (&b == &rhs.b);
  }

  bool operator!=(const Bounds2iIterator &rhs) const {
    return p != rhs.p || (&b != &rhs.b);
  }

  Point2i operator*() const { return p; }

 private:
  void advance() {
    ++p.x();
    if (p.x() == b.p_max.x()) {
      p.x() = b.p_min.x();
      ++p.y();
    }
  }

  const Bounds2i &b;
  Point2i p;
};

inline Bounds2iIterator begin(const Bounds2i &b) {
  return Bounds2iIterator{b, b.p_min};
}

inline Bounds2iIterator end(const Bounds2i &b) {
  // normally, the ending point is at the minimum x value and one past
  // the last valid y value.
  Point2i pEnd(b.p_min.x(), b.p_max.y());
  // however, if the bounds are degenerate, override the end point to
  // equal the start point so that any attempt to iterate over the bounds
  // exits out immediately.
  if (b.p_min.x() >= b.p_max.x() || b.p_min.y() >= b.p_max.y()) pEnd = b.p_min;
  return Bounds2iIterator{b, pEnd};
}

DAKKU_DECLARE_LUA_OBJECT(Bounds, DAKKU_EXPORT_CORE);
}  // namespace dakku
#endif

Updated on 2022-04-30 at 15:46:11 +0000