#ifndef slic3r_Geometry_Circle_hpp_ #define slic3r_Geometry_Circle_hpp_ #include "../Point.hpp" #include namespace Slic3r { namespace Geometry { // https://en.wikipedia.org/wiki/Circumscribed_circle // Circumcenter coordinates, Cartesian coordinates template Vector circle_center(const Vector &a, const Vector &bsrc, const Vector &csrc, typename Vector::Scalar epsilon) { using Scalar = typename Vector::Scalar; Vector b = bsrc - a; Vector c = csrc - a; Scalar lb = b.squaredNorm(); Scalar lc = c.squaredNorm(); if (Scalar d = b.x() * c.y() - b.y() * c.x(); std::abs(d) < epsilon) { // The three points are collinear. Take the center of the two points // furthest away from each other. Scalar lbc = (csrc - bsrc).squaredNorm(); return Scalar(0.5) * ( lb > lc && lb > lbc ? a + bsrc : lc > lb && lc > lbc ? a + csrc : bsrc + csrc); } else { Vector v = lc * b - lb * c; return a + Vector(- v.y(), v.x()) / (2 * d); } } // 2D circle defined by its center and squared radius template struct CircleSq { using Scalar = typename Vector::Scalar; Vector center; Scalar radius2; CircleSq() {} CircleSq(const Vector ¢er, const Scalar radius2) : center(center), radius2(radius2) {} CircleSq(const Vector &a, const Vector &b) : center(Scalar(0.5) * (a + b)) { radius2 = (a - center).squaredNorm(); } CircleSq(const Vector &a, const Vector &b, const Vector &c, Scalar epsilon) { this->center = circle_center(a, b, c, epsilon); this->radius2 = (a - this->center).squaredNorm(); } bool invalid() const { return this->radius2 < 0; } bool valid() const { return ! this->invalid(); } bool contains(const Vector &p) const { return (p - this->center).squaredNorm() < this->radius2; } bool contains(const Vector &p, const Scalar epsilon2) const { return (p - this->center).squaredNorm() < this->radius2 + epsilon2; } CircleSq inflated(Scalar epsilon) const { assert(this->radius2 >= 0); Scalar r = sqrt(this->radius2) + epsilon; return { this->center, r * r }; } static CircleSq make_invalid() { return CircleSq { { 0, 0 }, -1 }; } }; // 2D circle defined by its center and radius template struct Circle { using Scalar = typename Vector::Scalar; Vector center; Scalar radius; Circle() {} Circle(const Vector ¢er, const Scalar radius) : center(center), radius(radius) {} Circle(const Vector &a, const Vector &b) : center(Scalar(0.5) * (a + b)) { radius = (a - center).norm(); } Circle(const Vector &a, const Vector &b, const Vector &c, const Scalar epsilon) { *this = CircleSq(a, b, c, epsilon); } // Conversion from CircleSq template explicit Circle(const CircleSq &c) : center(c.center), radius(c.radius2 <= 0 ? c.radius2 : sqrt(c.radius2)) {} template Circle operator=(const CircleSq& c) { this->center = c.center; this->radius = c.radius2 <= 0 ? c.radius2 : sqrt(c.radius2); } bool invalid() const { return this->radius < 0; } bool valid() const { return ! this->invalid(); } bool contains(const Vector &p) const { return (p - this->center).squaredNorm() <= this->radius * this->radius; } bool contains(const Vector &p, const Scalar epsilon) const { Scalar re = this->radius + epsilon; return (p - this->center).squaredNorm() < re * re; } Circle inflated(Scalar epsilon) const { assert(this->radius >= 0); return { this->center, this->radius + epsilon }; } static Circle make_invalid() { return Circle { { 0, 0 }, -1 }; } }; using Circlef = Circle; using Circled = Circle; using CircleSqf = CircleSq; using CircleSqd = CircleSq; /// Find the center of the circle corresponding to the vector of Points as an arc. Point circle_center_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20); inline Point circle_center_taubin_newton(const Points& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); } /// Find the center of the circle corresponding to the vector of Pointfs as an arc. Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_start, const Vec2ds::const_iterator& input_end, size_t cycles = 20); inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); } Circled circle_taubin_newton(const Vec2ds& input, size_t cycles = 20); // Find circle using RANSAC randomized algorithm. Circled circle_ransac(const Vec2ds &input, size_t iterations = 20, double *min_error = nullptr); // Randomized algorithm by Emo Welzl, working with squared radii for efficiency. The returned circle radius is inflated by epsilon. template CircleSq smallest_enclosing_circle2_welzl(const Points &points, const typename Vector::Scalar epsilon) { using Scalar = typename Vector::Scalar; CircleSq circle; if (! points.empty()) { const auto &p0 = points[0].template cast(); if (points.size() == 1) { circle.center = p0; circle.radius2 = epsilon * epsilon; } else { circle = CircleSq(p0, points[1].template cast()).inflated(epsilon); for (size_t i = 2; i < points.size(); ++ i) if (const Vector &p = points[i].template cast(); ! circle.contains(p)) { // p is the first point on the smallest circle enclosing points[0..i] circle = CircleSq(p0, p).inflated(epsilon); for (size_t j = 1; j < i; ++ j) if (const Vector &q = points[j].template cast(); ! circle.contains(q)) { // q is the second point on the smallest circle enclosing points[0..i] circle = CircleSq(p, q).inflated(epsilon); for (size_t k = 0; k < j; ++ k) if (const Vector &r = points[k].template cast(); ! circle.contains(r)) circle = CircleSq(p, q, r, epsilon).inflated(epsilon); } } } } return circle; } // Randomized algorithm by Emo Welzl. The returned circle radius is inflated by epsilon. template Circle smallest_enclosing_circle_welzl(const Points &points, const typename Vector::Scalar epsilon) { return Circle(smallest_enclosing_circle2_welzl(points, epsilon)); } // Randomized algorithm by Emo Welzl. The returned circle radius is inflated by SCALED_EPSILON. inline Circled smallest_enclosing_circle_welzl(const Points &points) { return smallest_enclosing_circle_welzl(points, SCALED_EPSILON); } // Ugly named variant, that accepts the squared line // Don't call me with a nearly zero length vector! // sympy: // factor(solve([a * x + b * y + c, x**2 + y**2 - r**2], [x, y])[0]) // factor(solve([a * x + b * y + c, x**2 + y**2 - r**2], [x, y])[1]) template int ray_circle_intersections_r2_lv2_c(T r2, T a, T b, T lv2, T c, std::pair, Eigen::Matrix> &out) { T x0 = - a * c; T y0 = - b * c; T d2 = r2 * lv2 - c * c; if (d2 < T(0)) return 0; T d = sqrt(d2); out.first.x() = (x0 + b * d) / lv2; out.first.y() = (y0 - a * d) / lv2; out.second.x() = (x0 - b * d) / lv2; out.second.y() = (y0 + a * d) / lv2; return d == T(0) ? 1 : 2; } template int ray_circle_intersections(T r, T a, T b, T c, std::pair, Eigen::Matrix> &out) { T lv2 = a * a + b * b; if (lv2 < T(SCALED_EPSILON * SCALED_EPSILON)) { //FIXME what is the correct epsilon? // What if the line touches the circle? return false; } return ray_circle_intersections_r2_lv2_c2(r * r, a, b, a * a + b * b, c, out); } } } // namespace Slic3r::Geometry #endif // slic3r_Geometry_Circle_hpp_