GenericCircle.h 3.78 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
#pragma once

#include <QObject>
#include <QPointF>

#include <cmath>

#include "boost/units/systems/angle/degrees.hpp"
#include "boost/units/systems/si.hpp"

template <int k, class Point> auto get(const Point &p);

namespace bu = boost::units;

namespace qty {
using Length = bu::quantity<bu::si::length>;
using Angle = bu::quantity<bu::si::plane_angle>;
using Degree = bu::quantity<bu::degree::plane_angle>;
using Radian = bu::quantity<bu::si::plane_angle>;
} // namespace qty

template <class NumberType, class Point> class GenericCircle {
public:
  GenericCircle();
  GenericCircle(NumberType rad, Point ori);

  // Property setters
  void setRadius(NumberType rad);
  void setOrigin(const Point &ori);

  // Property getters
  NumberType radius() const;
  const Point &origin() const;
  Point &origin();

private:
  NumberType _r;
  Point _origin;
};

// Free functions.
template <class Circle, class Container>
void approximate(Circle &circ, long n, qty::Angle alpha1, qty::Angle alpha2,
                 Container &c);
template <class Circle, class Container>
void approximate(Circle &circ, long n, Container &c);

// Impl.
template <class NumberType, class Point>
GenericCircle<NumberType, Point>::GenericCircle() : _r(0), _origin(0, 0) {}

template <class NumberType, class Point>
GenericCircle<NumberType, Point>::GenericCircle(NumberType rad, Point ori)
    : _r(rad), _origin(ori) {}

/*!
 * Returns a polygon with \a n corners which approximates the
 * circle.
 *
 * \sa Point
 */

template <class NumberType, class Point>
void GenericCircle<NumberType, Point>::setRadius(NumberType rad) {
  if (rad >= 0) {
    this->_r = rad;
  }
}

template <class NumberType, class Point>
void GenericCircle<NumberType, Point>::setOrigin(const Point &ori) {
  this->_origin = ori;
}

template <class NumberType, class Point>
NumberType GenericCircle<NumberType, Point>::radius() const {
  return this->_r;
}

template <class NumberType, class Point>
const Point &GenericCircle<NumberType, Point>::origin() const {
  return this->_origin;
}

template <class NumberType, class Point>
Point &GenericCircle<NumberType, Point>::origin() {
  return this->_origin;
}

template <class Circle, class Container>
void approximate(Circle &circ, long n, qty::Angle alpha1, qty::Angle alpha2,
                 Container &c) {

  auto clipp = [](double angle) {
    while (angle < 0) {
      angle += 2 * M_PI;
    }
    while (angle > 2 * M_PI) {
      angle -= 2 * M_PI;
    }
    return angle;
  };

  double a1 = clipp(alpha1.value());
  double a2 = clipp(alpha2.value());
  double angleDisc = 0;
  if (n > 0) {
    angleDisc = (a2 - a1) / (n - 1);
  } else {
    angleDisc = (a2 - a1) / (n + 1);
    n = n * (-1);
  }

  double a = a1;
  using Point =
      std::remove_cv_t<std::remove_reference_t<decltype(circ.origin())>>;
  auto x0 = get<0>(circ.origin());
  auto y0 = get<1>(circ.origin());
  auto r = circ.radius();
  using NumberType = std::remove_cv_t<std::remove_reference_t<decltype(x0)>>;
  while (n--) {
    auto x = NumberType(x0 + r * std::cos(a));
    auto y = NumberType(y0 + r * std::sin(a));
    c.push_back(Point(x, y));
    a += angleDisc;
  }
}

template <class Circle, class Container>
void approximate(Circle &circ, long n, Container &c) {
  using namespace bu;

  double angleDisc = 0;
  if (n > 0) {
    angleDisc = 2 * M_PI / (n - 1);
  } else {
    angleDisc = 2 * M_PI / (n + 1);
    n = n * (-1);
  }

  double a = 0;
  using Point =
      std::remove_cv_t<std::remove_reference_t<decltype(circ.origin())>>;
  auto x0 = get<0>(circ.origin());
  auto y0 = get<1>(circ.origin());
  auto r = circ.radius();
  using NumberType = std::remove_cv_t<std::remove_reference_t<decltype(x0)>>;
  while (n--) {
    auto x = NumberType(x0 + r * std::cos(a));
    auto y = NumberType(y0 + r * std::sin(a));
    c.push_back(Point(x, y));
    a += angleDisc;
  }
}