diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 4b1fc1ce86..8c69d55126 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -1,9 +1,9 @@ -///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Lukáš Hejl @hejllukas +///|/ Copyright (c) Prusa Research 2016 - 2023 Vojt�ch Bubn�k @bubnikv, Enrico Turri @enricoturri1966, Tom� M�sz�ros @tamasmeszaros, Luk� Mat�na @lukasmatena, Filip Sykala @Jony01, Luk� Hejl @hejllukas ///|/ Copyright (c) 2017 Eyal Soha @eyal0 ///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel ///|/ ///|/ ported from lib/Slic3r/Geometry.pm: -///|/ Copyright (c) Prusa Research 2017 - 2022 Vojtěch Bubník @bubnikv +///|/ Copyright (c) Prusa Research 2017 - 2022 Vojt�ch Bubn�k @bubnikv ///|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel ///|/ Copyright (c) 2013 Jose Luis Perez Diez ///|/ Copyright (c) 2013 Anders Sundman @@ -309,6 +309,39 @@ bool liang_barsky_line_clipping( return liang_barsky_line_clipping(x0clip, x1clip, bbox); } +// Ugly named variant, that accepts the squared line +// Don't call me with a nearly zero length vector! +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 / lv2; + T y0 = - b * c / lv2; + T d = r2 - c * c / lv2; + if (d < T(0)) + return 0; + T mult = sqrt(d / lv2); + out.first.x() = x0 + b * mult; + out.first.y() = y0 - a * mult; + out.second.x() = x0 - b * mult; + out.second.y() = y0 + a * mult; + return mult == 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_c(r * r, a, b, a * a + b * b, c, out); +} + +Pointf3s convex_hull(Pointf3s points); +Polygon convex_hull(Points points); +Polygon convex_hull(const Polygons &polygons); + bool directions_parallel(double angle1, double angle2, double max_diff = 0); bool directions_perpendicular(double angle1, double angle2, double max_diff = 0); template bool contains(const std::vector &vector, const Point &point); diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp index 31afc8c1a9..b85dff1568 100644 --- a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp @@ -1,4 +1,6 @@ #include "LineUtils.hpp" +#include +#include using namespace Slic3r::sla; @@ -18,3 +20,212 @@ void LineUtils::sort_CCW(Lines &lines, const Point& center) } }); } + +bool LineUtils::is_parallel_y(const Line &line) { + coord_t x_change = line.a.x() - line.b.x(); + return (x_change == 0); +} +bool LineUtils::is_parallel_y(const Linef &line) +{ + double x_change = line.a.x() - line.b.x(); + return (fabs(x_change) < std::numeric_limits::epsilon()); +} + +std::optional LineUtils::crop_ray(const Line & ray, + const Point ¢er, + double radius) +{ + if (is_parallel_y(ray)) { + coord_t x = ray.a.x(); + coord_t diff = x - center.x(); + coord_t abs_diff = abs(diff); + if (abs_diff > radius) return {}; + // create cross points + double move_y = sqrt(radius * radius - (double) x * x); + coord_t y = std::round(move_y); + Point first(x, y); + Point second(x,-y); + return Line(first + center, second + center); + } else { + Line moved_line(ray.a - center, ray.b - center); + double a, b, c; + std::tie(a, b, c) = get_param(moved_line); + std::pair points; + int count = Slic3r::Geometry::ray_circle_intersections( + radius, a, b, c, points); + if (count != 2) return {}; + return Line(points.first.cast() + center, + points.second.cast() + center); + } +} +std::optional LineUtils::crop_ray(const Linef &ray, + const Point ¢er, + double radius) +{ + Vec2d center_d = center.cast(); + if (is_parallel_y(ray)) { + double x = ray.a.x(); + double diff = x - center.x(); + double abs_diff = fabs(diff); + if (abs_diff > radius) return {}; + // create cross points + double y = sqrt(radius * radius - x * x); + Vec2d first(x, y); + Vec2d second(x, -y); + return Linef(first + center_d, + second + center_d); + } else { + Linef moved_line(ray.a - center_d, ray.b - center_d); + double a, b, c; + std::tie(a, b, c) = get_param(moved_line); + std::pair points; + int count = Slic3r::Geometry::ray_circle_intersections(radius, a, b, + c, points); + if (count != 2) return {}; + return Linef(points.first + center_d, points.second + center_d); + } +} + +std::optional LineUtils::crop_half_ray(const Line & half_ray, + const Point ¢er, + double radius) +{ + auto segment = crop_ray(half_ray, center, radius); + if (!segment.has_value()) return {}; + Point dir = half_ray.b - half_ray.a; + using fnc = std::function; + fnc use_point_x = [&half_ray, &dir](const Point &p) -> bool { + return (p.x() > half_ray.a.x()) == (dir.x() > 0); + }; + fnc use_point_y = [&half_ray, &dir](const Point &p) -> bool { + return (p.y() > half_ray.a.y()) == (dir.y() > 0); + }; + bool use_x = abs(dir.x()) > abs(dir.y()); + fnc use_point = (use_x) ? use_point_x : use_point_y; + bool use_a = use_point(segment->a); + bool use_b = use_point(segment->b); + if (!use_a && !use_b) return {}; + if (use_a && use_b) return segment; + return Line(half_ray.a, (use_a)?segment->a : segment->b); +} + +std::optional LineUtils::crop_half_ray(const Linef & half_ray, + const Point ¢er, + double radius) +{ + auto segment = crop_ray(half_ray, center, radius); + if (!segment.has_value()) return {}; + Vec2d dir = half_ray.b - half_ray.a; + using fnc = std::function; + fnc use_point_x = [&half_ray, &dir](const Vec2d &p) -> bool { + return (p.x() > half_ray.a.x()) == (dir.x() > 0); + }; + fnc use_point_y = [&half_ray, &dir](const Vec2d &p) -> bool { + return (p.y() > half_ray.a.y()) == (dir.y() > 0); + }; + bool use_x = fabs(dir.x()) > fabs(dir.y()); + fnc use_point = (use_x) ? use_point_x : use_point_y; + bool use_a = use_point(segment->a); + bool use_b = use_point(segment->b); + if (!use_a && !use_b) return {}; + if (use_a && use_b) return segment; + return Linef(half_ray.a, (use_a) ? segment->a : segment->b); +} + +std::optional LineUtils::crop_line(const Line & line, + const Point ¢er, + double radius) +{ + auto segment = crop_ray(line, center, radius); + if (!segment.has_value()) return {}; + + Point dir = line.b - line.a; + using fnc = std::function; + fnc use_point_x = [&line, &dir](const Point &p) -> bool { + return (dir.x() > 0) ? (p.x() > line.a.x()) && (p.x() < line.b.x()) : + (p.x() < line.a.x()) && (p.x() > line.b.x()); + }; + fnc use_point_y = [&line, &dir](const Point &p) -> bool { + return (dir.y() > 0) ? (p.y() > line.a.y()) && (p.y() < line.b.y()) : + (p.y() < line.a.y()) && (p.y() > line.b.y()); + }; + bool use_x = abs(dir.x()) > abs(dir.y()); + fnc use_point = (use_x) ? use_point_x : use_point_y; + bool use_a = use_point(segment->a); + bool use_b = use_point(segment->b); + if (!use_a && !use_b) return {}; + if (use_a && use_b) return segment; + bool same_dir = (use_x) ? + ((dir.x() > 0) == (segment->b.x() - segment->a.x()) > 0) : + ((dir.y() > 0) == (segment->b.y() - segment->a.y()) > 0) ; + if (use_a) { + if (same_dir) + return Line(segment->a, line.b); + else + return Line(line.a, segment->a); + } else { // use b + if (same_dir) + return Line(line.a, segment->b); + else + return Line(segment->b, line.b); + } +} + +std::optional LineUtils::crop_line(const Linef & line, + const Point ¢er, + double radius) +{ + auto segment = crop_ray(line, center, radius); + if (!segment.has_value()) return {}; + + Vec2d dir = line.b - line.a; + using fnc = std::function; + fnc use_point_x = [&line, &dir](const Vec2d &p) -> bool { + return (dir.x() > 0) ? (p.x() > line.a.x()) && (p.x() < line.b.x()) : + (p.x() < line.a.x()) && (p.x() > line.b.x()); + }; + fnc use_point_y = [&line, &dir](const Vec2d &p) -> bool { + return (dir.y() > 0) ? (p.y() > line.a.y()) && (p.y() < line.b.y()) : + (p.y() < line.a.y()) && (p.y() > line.b.y()); + }; + bool use_x = abs(dir.x()) > abs(dir.y()); + fnc use_point = (use_x) ? use_point_x : use_point_y; + bool use_a = use_point(segment->a); + bool use_b = use_point(segment->b); + if (!use_a && !use_b) return {}; + if (use_a && use_b) return segment; + bool same_dir = (use_x) ? ((dir.x() > 0) == + (segment->b.x() - segment->a.x()) > 0) : + ((dir.y() > 0) == + (segment->b.y() - segment->a.y()) > 0); + if (use_a) { + if (same_dir) + return Linef(segment->a, line.b); + else + return Linef(line.a, segment->a); + } else { // use b + if (same_dir) + return Linef(line.a, segment->b); + else + return Linef(segment->b, line.b); + } +} + + +std::tuple LineUtils::get_param(const Line &line) { + Vector normal = line.normal(); + double a = normal.x(); + double b = normal.y(); + double c = -a * line.a.x() - b * line.a.y(); + return {a, b, c}; +} + +std::tuple LineUtils::get_param(const Linef &line) +{ + Vec2d direction = line.b - line.a; + Vec2d normal(-direction.y(), direction.x()); + double a = normal.x(); + double b = normal.y(); + double c = -a * line.a.x() - b * line.a.y(); + return {a, b, c}; +} \ No newline at end of file diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp index c904159356..9ad580d78f 100644 --- a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_SLA_SuppotstIslands_LineUtils_hpp_ #define slic3r_SLA_SuppotstIslands_LineUtils_hpp_ +#include +#include #include namespace Slic3r::sla { @@ -21,6 +23,64 @@ public: /// Lines to sort /// Center for order static void sort_CCW(Lines &lines, const Point ¢er); + + /// + /// Create line segment intersection of line and circle + /// + /// Input line. + /// Circle center. + /// Circle radius. + /// Chord -> line segment inside circle + static std::optional crop_line(const Line & line, + const Point ¢er, + double radius); + static std::optional crop_line(const Linef & line, + const Point ¢er, + double radius); + /// + /// Create line segment intersection of ray and circle, when exist + /// + /// Input ray. + /// Circle center. + /// Circle radius. + /// Chord -> line segment inside circle + static std::optional crop_ray(const Line & ray, + const Point ¢er, + double radius); + static std::optional crop_ray(const Linef & ray, + const Point ¢er, + double radius); + /// + /// Create line segment intersection of half ray(start point and direction) and circle, when exist + /// + /// Use Line::a as start point and Line::b as direction but no limit + /// Circle center. + /// Circle radius. + /// Chord -> line segment inside circle + static std::optional crop_half_ray(const Line & half_ray, + const Point ¢er, + double radius); + static std::optional crop_half_ray(const Linef & half_ray, + const Point ¢er, + double radius); + + /// + /// check if line is parallel to Y + /// + /// Input line + /// True when parallel otherwise FALSE + static bool is_parallel_y(const Line &line); + static bool is_parallel_y(const Linef &line); + + /// + /// Create parametric coeficient + /// ax + by + c = 0 + /// Can't be parallel to Y axis - check by function is_parallel_y + /// + /// Input line - cant be parallel with y axis + /// a, b, c + static std::tuple get_param(const Line &line); + static std::tuple get_param(const Linef &line); }; } // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportIslands/PolygonUtils.cpp b/src/libslic3r/SLA/SupportIslands/PolygonUtils.cpp new file mode 100644 index 0000000000..a8785d3663 --- /dev/null +++ b/src/libslic3r/SLA/SupportIslands/PolygonUtils.cpp @@ -0,0 +1,55 @@ +#include "PolygonUtils.hpp" + +using namespace Slic3r::sla; + +Slic3r::Polygon PolygonUtils::create_regular(size_t count_points, + double radius, + const Point ¢er) +{ + assert(radius >= 1.); + assert(count_points >= 3); + auto is_in_limits = [](double value) { + return (value < std::numeric_limits::max()) && + (value > std::numeric_limits::min()); + }; + Points points; + points.reserve(count_points); + double increase_angle = 2 * M_PI / count_points; + for (int i = 0; i < count_points; ++i) { + double angle = i * increase_angle; + double x = cos(angle) * radius + center.x(); + assert(is_in_limits(x)); + double y = sin(angle) * radius + center.y(); + assert(is_in_limits(y)); + points.emplace_back(x, y); + } + return Polygon(points); +} + +Slic3r::Polygon PolygonUtils::create_equilateral_triangle(double edge_size) +{ + return {{.0, .0}, + {edge_size, .0}, + {edge_size / 2., sqrt(edge_size * edge_size - edge_size * edge_size / 4)}}; +} + +Slic3r::Polygon PolygonUtils::create_isosceles_triangle(double side, double height) +{ + return {{-side / 2, 0.}, {side / 2, 0.}, {.0, height}}; +} + +Slic3r::Polygon PolygonUtils::create_square(double size) +{ + double size_2 = size / 2; + return {{-size_2, size_2}, + {-size_2, -size_2}, + {size_2, -size_2}, + {size_2, size_2}}; +} + +Slic3r::Polygon PolygonUtils::create_rect(double width, double height) +{ + double x_2 = width / 2; + double y_2 = height / 2; + return {{-x_2, y_2}, {-x_2, -y_2}, {x_2, -y_2}, {x_2, y_2}}; +} \ No newline at end of file diff --git a/src/libslic3r/SLA/SupportIslands/PolygonUtils.hpp b/src/libslic3r/SLA/SupportIslands/PolygonUtils.hpp new file mode 100644 index 0000000000..19f18a871f --- /dev/null +++ b/src/libslic3r/SLA/SupportIslands/PolygonUtils.hpp @@ -0,0 +1,68 @@ +#ifndef slic3r_SLA_SuppotstIslands_PolygonUtils_hpp_ +#define slic3r_SLA_SuppotstIslands_PolygonUtils_hpp_ + +#include + +namespace Slic3r::sla { +/// +/// Class which contain collection of static function +/// for work with Polygon. +/// +class PolygonUtils +{ +public: + PolygonUtils() = delete; + + /// + /// Create regular polygon with N points + /// + /// Count points of regular polygon + /// Radius around center + /// Center point + /// Regular Polygon with CCW points + static Polygon create_regular(size_t count_points, double radius = 10., const Point& center = Point(0,0)); + + /// + /// Create circle with N points + /// alias for create regular + /// + /// Radius of circle + /// Count points of circle + /// Center point + /// Regular Polygon with CCW points + static Polygon create_circle(double radius, size_t count_points = 10, const Point& center = Point(0,0)){ + return create_regular(count_points, radius, center); + } + + /// + /// Create triangle with same length for all sides + /// + /// triangel edge size + /// Equilateral triangle + static Polygon create_equilateral_triangle(double edge_size); + + /// + /// Create triangle with two side with same size + /// + /// Size of unique side + /// triangle height + /// Isosceles Triangle + static Polygon create_isosceles_triangle(double side, double height); + + /// + /// Create squar with center in [0,0] + /// + /// + /// Square + static Polygon create_square(double size); + + /// + /// Create rect with center in [0,0] + /// + /// width + /// height + /// Rectangle + static Polygon create_rect(double width, double height); +}; +} // namespace Slic3r::sla +#endif // slic3r_SLA_SuppotstIslands_PolygonUtils_hpp_ diff --git a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp index 75c65d47fe..de51181a92 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp @@ -33,6 +33,11 @@ struct SampleConfig // Maximal width of line island supported by zig zag double max_width_for_zig_zag_supportr_line = 1.; + // Term criteria for end of alignment + // Minimal change in manhatn move of support position before termination + coord_t minimal_move = 1; + // Maximal count of align iteration + size_t count_iteration = 100; }; } // namespace Slic3r::sla #endif // slic3r_SLA_SuppotstIslands_SampleConfig_hpp_ diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp index bec03040c7..5727c8da16 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp @@ -10,6 +10,8 @@ #include #include +#include // allign + using namespace Slic3r::sla; SupportIslandPoint SampleIslandUtils::create_point( @@ -187,18 +189,48 @@ Slic3r::Points SampleIslandUtils::to_points(const SupportIslandPoints &support_p return points; } -void SampleIslandUtils::align_samples(SupportIslandPoints &samples, double max_distance) +void SampleIslandUtils::align_samples(SupportIslandPoints &samples, + const ExPolygon & island, + const SampleConfig & config) +{ + size_t count_iteration = config.count_iteration; // copy + while (--count_iteration > 1) { + coord_t max_move = align_once(samples, island, config); + if (max_move < config.minimal_move) break; + } +} + +coord_t SampleIslandUtils::align_once(SupportIslandPoints &samples, + const ExPolygon & island, + const SampleConfig & config) { using VD = Slic3r::Geometry::VoronoiDiagram; - VD vd; + VD vd; Slic3r::Points points = SampleIslandUtils::to_points(samples); - construct_voronoi(points.begin(), points.end(), &vd); - for (const VD::cell_type &cell : vd.cells()) { - SupportIslandPoint& sample = samples[cell.source_index()]; - Polygon polygon = VoronoiGraphUtils::to_polygon(cell, points, max_distance); - } // create voronoi diagram with points + construct_voronoi(points.begin(), points.end(), &vd); + coord_t max_move = 0; + for (const VD::cell_type &cell : vd.cells()) { + SupportIslandPoint &sample = samples[cell.source_index()]; + if (!sample.can_move()) continue; + Polygon polygon = VoronoiGraphUtils::to_polygon(cell, points, config.max_distance); + Polygons intersections = Slic3r::intersection(island, ExPolygon(polygon)); + const Polygon *island_cell = nullptr; + for (const Polygon &intersection : intersections) { + if (intersection.contains(sample.point)) { + island_cell = &intersection; + break; + } + } + assert(island_cell != nullptr); + Point center = island_cell->centroid(); + Point move = center - sample.point; + + coord_t act_move = move.x() + move.y(); // manhatn distance + if (max_move < act_move) max_move = act_move; + } + return max_move; } SupportIslandPoints SampleIslandUtils::sample_center_line( @@ -214,8 +246,6 @@ SupportIslandPoints SampleIslandUtils::sample_center_line( if (path.circles.empty()) return result; sample_center_circles(path, cfg, result); - - align_samples(result, cfg.max_sample_distance); return result; } diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp index da59c215b4..dac556a719 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp @@ -133,10 +133,28 @@ public: /// /// keep same distances between support points + /// call once align /// - /// In/Out vector of support points - /// Maximal distance between neighbor points - static void align_samples(SupportIslandPoints &samples, double max_distance); + /// In/Out support points to be alligned + /// Area for sampling, border for position of samples + /// Sampling configuration + /// Maximal distance between neighbor points + + /// Term criteria for align: Minimal sample move and Maximal count of iteration + static void align_samples(SupportIslandPoints &samples, + const ExPolygon & island, + const SampleConfig &config); + + /// + /// once align + /// + /// In/Out support points to be alligned + /// Area for sampling, border for position of samples + /// Sampling configuration + /// Maximal distance between neighbor points + + /// Term criteria for align: Minimal sample move and Maximal count of iteration + static coord_t align_once(SupportIslandPoints &samples, + const ExPolygon & island, + const SampleConfig & config); static void draw(SVG & svg, const SupportIslandPoints &supportIslandPoints, diff --git a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp index 9789b4c46f..e50db35979 100644 --- a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp +++ b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_SLA_SuppotstIslands_SupportIslandPoint_hpp_ #define slic3r_SLA_SuppotstIslands_SupportIslandPoint_hpp_ +#include #include #include "VoronoiGraph.hpp" @@ -34,6 +35,29 @@ struct SupportIslandPoint VoronoiGraph::Position position) : point(std::move(point)), type(type), position(position) {} + + bool can_move() const{ + // use shorter list + /* + + static const std::set can_move({ + Type::center_line, + Type::center_circle, + Type::center_circle_end, + Type::center_circle_end2 + }); + return can_move.find(type) != can_move.end(); + + /*/ + + static const std::set cant_move({ + Type::one_center_point, + Type::two_points + }); + return cant_move.find(type) == cant_move.end(); + + //*/ + } }; using SupportIslandPoints = std::vector; diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp index a1b2522844..d29040593f 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp @@ -7,22 +7,53 @@ #include "ParabolaUtils.hpp" #include "LineUtils.hpp" #include "PointUtils.hpp" +#include "PolygonUtils.hpp" #include +#define SLA_CELL_2_POLYGON_DEBUG + using namespace Slic3r::sla; -Slic3r::Line VoronoiGraphUtils::to_line(const VD::edge_type &edge) { - assert(edge.is_linear()); - assert(edge.is_finite()); - return Line(Point(edge.vertex0()->x(), edge.vertex0()->y()), - Point(edge.vertex1()->x(), edge.vertex1()->y())); +coord_t VoronoiGraphUtils::to_coord(const VD::coordinate_type &coord) +{ + static const VD::coordinate_type min_val = + static_cast(std::numeric_limits::min()); + static const VD::coordinate_type max_val = + static_cast(std::numeric_limits::max()); + if (coord > max_val) return std::numeric_limits::max(); + if (coord < min_val) return std::numeric_limits::min(); + return static_cast(std::round(coord)); +} + +Slic3r::Point VoronoiGraphUtils::to_point(const VD::vertex_type *vertex) +{ + return Point(to_coord(vertex->x()), to_coord(vertex->y())); +} + +bool VoronoiGraphUtils::is_coord_in_limits(const VD::coordinate_type &coord, + const coord_t & source, + double max_distance) +{ + VD::coordinate_type min_val = source - max_distance; + VD::coordinate_type max_val = source + max_distance; + if (coord > max_val) return false; + if (coord < min_val) return false; + return true; +} + +bool VoronoiGraphUtils::is_point_in_limits(const VD::vertex_type *vertex, + const Point & source, + double max_distance) +{ + if (vertex == nullptr) return false; + return is_coord_in_limits(vertex->x(), source.x(), max_distance) && + is_coord_in_limits(vertex->y(), source.y(), max_distance); } // create line segment between (in the middle) points. With size depend on their distance -Slic3r::Line VoronoiGraphUtils::to_line(Point point1, - Point point2, - double maximal_distance) +Slic3r::Line VoronoiGraphUtils::create_line_between_source_points( + const Point &point1, const Point &point2, double maximal_distance) { Point middle = (point1 + point2) / 2; Point diff = point1 - point2; @@ -44,57 +75,84 @@ bool is_oposit_direction(const Slic3r::Point &p1, const Slic3r::Point &p2) { return (p1.y() > 0) != (p2.y() > 0); } -Slic3r::Line VoronoiGraphUtils::to_line(const VD::edge_type & edge, - const Points &points, - double maximal_distance) +std::optional VoronoiGraphUtils::to_line( + const VD::edge_type &edge, const Points &points, double maximal_distance) { assert(edge.is_linear()); assert(edge.is_primary()); + const Point &p1 = retrieve_point(points, *edge.cell()); + const Point &p2 = retrieve_point(points, *edge.twin()->cell()); + const VD::vertex_type *v0 = edge.vertex0(); + const VD::vertex_type *v1 = edge.vertex1(); - if (edge.is_finite()) return to_line(edge); + bool use_v1 = false; // v0 == nullptr or out of limits + bool use_double_precision = false; + bool use_both = false; + if (edge.is_finite()) { + bool is_v0_in_limit = is_point_in_limits(v0, p1, maximal_distance); + bool is_v1_in_limit = is_point_in_limits(v1, p1, maximal_distance); + if (!is_v0_in_limit) { + use_v1 = true; + if (!is_v1_in_limit) { + use_double_precision = true; + use_both = true; + } + } else if (is_v1_in_limit) { + // normal full edge line segment + return Line(to_point(v0), to_point(v1)); + } + } else if (v0 == nullptr) { + if (v1 == nullptr) + {// both vertex are nullptr, create edge between points + return create_line_between_source_points(p1, p2, maximal_distance); + } + if (!is_point_in_limits(v1, p1, maximal_distance)) + use_double_precision = true; + use_v1 = true; + } else if (!is_point_in_limits(v0, p1, maximal_distance)) { + use_double_precision = true; + if (v1 != nullptr) + use_v1 = true; // v1 is in + } - const VD::cell_type &cell1 = *edge.cell(); - const VD::cell_type &cell2 = *edge.twin()->cell(); - assert(cell1.contains_point()); - assert(cell2.contains_point()); - Point p1 = retrieve_point(points, cell1); - Point p2 = retrieve_point(points, cell2); - if (edge.vertex0() == nullptr && edge.vertex1() == nullptr) - return to_line(p1, p2, maximal_distance); - - bool is_v0_null = edge.vertex0() == nullptr; - if (is_v0_null) std::swap(p1, p2); - Point direction(p1.y() - p2.y(), p2.x() - p1.x()); - - auto get_koef = [&](const Point &v)->double { - /*/ - // faster but less preciss version + Point direction = (use_v1) ? + Point(p2.y() - p1.y(), p1.x() - p2.x()) : + Point(p1.y() - p2.y(), p2.x() - p1.x()); + const VD::vertex_type* edge_vertex = (use_v1) ? v1 : v0; + // koeficient for crop line + double koef = 1.; + if (!use_double_precision) { + // only half edges + Point vertex = to_point(edge_vertex); + /*// faster but less preciss version double abs_max_dir = (std::max)(fabs(direction.x()), - fabs(direction.y())); - return 2 * maximal_distance / abs_max_dir; - + fabs(direction.y())); return 2 * maximal_distance / abs_max_dir; //*/ // slower but more precisse version - double dir_size = direction.cast().operatorNorm(); - Point middle = (p1 + p2) / 2; - Point to_middle = middle - v; - double to_middle_size = to_middle.cast().operatorNorm(); + double dir_size = direction.cast().operatorNorm(); + Point middle = (p1 + p2) / 2; + Point to_middle = middle - vertex; + double to_middle_size = to_middle.cast().operatorNorm(); double from_middle_size = sqrt(maximal_distance * maximal_distance - - to_middle_size * to_middle_size); - if (is_oposit_direction(direction, to_middle)) to_middle_size *= -1; - return (from_middle_size + to_middle_size)/dir_size; - }; - - if (is_v0_null) { - Point v1(edge.vertex1()->x(), edge.vertex1()->y()); - Point a = v1 + direction * get_koef(v1); - return Line(a, v1); - } else { - Point v0(edge.vertex0()->x(), edge.vertex0()->y()); - Point b = v0 + direction * get_koef(v0); - return Line(v0, b); + to_middle_size * to_middle_size); + bool is_opposit = is_oposit_direction(direction, to_middle); + if (is_opposit) to_middle_size *= -1; + koef = (from_middle_size + to_middle_size) / dir_size; + Point line_point = vertex + direction * koef; + return Line(vertex, line_point); } + std::optional segment; + if (use_both) { + Linef edge_segment(Vec2d(v0->x(), v0->y()), Vec2d(v1->x(), v1->y())); + segment = LineUtils::crop_line(edge_segment, p1, maximal_distance); + } else { + Vec2d ray_point(edge_vertex->x(), edge_vertex->y()); + Linef ray = Linef(ray_point, ray_point + direction.cast()); + segment = LineUtils::crop_half_ray(ray, p1, maximal_distance); + } + if (!segment.has_value()) return {}; + return Line(segment->a.cast(), segment->b.cast()); } Slic3r::Polygon VoronoiGraphUtils::to_polygon(const Lines &lines, @@ -103,12 +161,14 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const Lines &lines, double minimal_distance, size_t count_points) { - assert(lines.size() >= 1); assert(minimal_distance > 0.); assert(maximal_distance > minimal_distance); assert(count_points >= 3); + if (lines.empty()) + return PolygonUtils::create_regular(count_points, maximal_distance, center); + Points points; - points.reserve(lines.size()); + points.reserve(std::max(lines.size(), count_points)); const Line *prev_line = &lines.back(); double max_angle = 2 * M_PI / count_points; for (const Line &line : lines) { @@ -136,13 +196,16 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const Lines &lines, points.push_back(p1); for (size_t i = 1; i < count_segment; i++) { double angle = a1 + i*increase_angle; - Point direction( - static_cast(cos(angle) * maximal_distance), - static_cast(sin(angle) * maximal_distance)); - points.push_back(center + direction); + + double x = cos(angle) * maximal_distance + center.x(); + assert(x < std::numeric_limits::max()); + assert(x > std::numeric_limits::min()); + double y = sin(angle) * maximal_distance + center.y(); + assert(y < std::numeric_limits::max()); + assert(y > std::numeric_limits::min()); + points.emplace_back(x,y); } points.push_back(p2); - } return Polygon(points); } @@ -152,16 +215,18 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const VD::cell_type & cell, double maximal_distance) { const VD::edge_type *edge = cell.incident_edge(); - Lines lines; Point center = points[cell.source_index()]; // Convenient way to iterate edges around Voronoi cell. do { assert(edge->is_linear()); if (edge->is_primary()) { - Line line = to_line(*edge, points, maximal_distance); - if (!PointUtils::is_ccw(line.a, line.b, center)) std::swap(line.a, line.b); - lines.push_back(line); + std::optional line = to_line(*edge, points, maximal_distance); + if (line.has_value()) { + if (!PointUtils::is_ccw(line->a, line->b, center)) + std::swap(line->a, line->b); + lines.push_back(line.value()); + } } edge = edge->next(); } while (edge != cell.incident_edge()); @@ -170,7 +235,7 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const VD::cell_type & cell, double min_distance = maximal_distance / 1000.; size_t count_point = 6; // count added points Slic3r::Polygon polygon = to_polygon(lines, center, maximal_distance, min_distance, count_point); -#ifdef SLA_SUPPORTPOINTGEN_DEBUG +#ifdef SLA_CELL_2_POLYGON_DEBUG { std::cout << "cell " << cell.source_index() << " has " << lines.size() << "edges" << std::endl; BoundingBox bbox(center - Point(maximal_distance, maximal_distance), @@ -188,7 +253,7 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const VD::cell_type & cell, } svg.draw(center, "red", maximal_distance / 100); } -#endif /* SLA_SUPPORTPOINTGEN_DEBUG */ +#endif /* SLA_CELL_2_POLYGON_DEBUG */ return polygon; } @@ -208,7 +273,7 @@ VoronoiGraph::Node *VoronoiGraphUtils::getNode(VoronoiGraph & graph, // const VD::cell_type * cell2 = edge.twin()->cell(); const Line &line = lines[cell->source_index()]; // const Line & line1 = lines[cell2->source_index()]; - Point point(vertex->x(), vertex->y()); + Point point = to_point(vertex); double distance = line.distance_to(point); auto &[iterator, @@ -229,9 +294,10 @@ Slic3r::Point VoronoiGraphUtils::retrieve_point(const Lines & lines, lines[cell.source_index()].b; } -Slic3r::Point VoronoiGraphUtils::retrieve_point(const Points & points, - const VD::cell_type &cell) +const Slic3r::Point &VoronoiGraphUtils::retrieve_point( + const Points &points, const VD::cell_type &cell) { + assert(cell.contains_point()); assert(cell.source_category() == boost::polygon::SOURCE_CATEGORY_SINGLE_POINT); return points[cell.source_index()]; } @@ -273,8 +339,8 @@ double VoronoiGraphUtils::calculate_length_of_parabola( const VD::edge_type & edge, const Lines & lines) { - Point v0{edge.vertex0()->x(), edge.vertex0()->y()}; - Point v1{edge.vertex1()->x(), edge.vertex1()->y()}; + Point v0 = to_point(edge.vertex0()); + Point v1 = to_point(edge.vertex1()); ParabolaSegment parabola(get_parabola(edge, lines), v0, v1); return ParabolaUtils::length(parabola); } @@ -296,8 +362,8 @@ double VoronoiGraphUtils::calculate_length( double VoronoiGraphUtils::calculate_max_width( const VD::edge_type &edge, const Lines &lines) { - Point v0{edge.vertex0()->x(), edge.vertex0()->y()}; - Point v1{edge.vertex1()->x(), edge.vertex1()->y()}; + Point v0 = to_point(edge.vertex0()); + Point v1 = to_point(edge.vertex1()); if (edge.is_linear()) { // edge line could be initialized by 2 points @@ -822,10 +888,8 @@ void VoronoiGraphUtils::draw(SVG & svg, svg.draw(Point(key->x(), key->y()), "lightgray", width); for (const auto &n : value.neighbors) { if (n.edge->vertex0() > n.edge->vertex1()) continue; - auto v0 = *n.edge->vertex0(); - Point from(v0.x(), v0.y()); - auto v1 = *n.edge->vertex1(); - Point to(v1.x(), v1.y()); + Point from = to_point(n.edge->vertex0()); + Point to = to_point(n.edge->vertex1()); svg.draw(Line(from, to), "gray", width); Point center = from + to; @@ -850,8 +914,9 @@ void VoronoiGraphUtils::draw(SVG & svg, prev_node = node; continue; } - Point from(prev_node->vertex->x(), prev_node->vertex->y()); - Point to(node->vertex->x(), node->vertex->y()); + + Point from = to_point(prev_node->vertex); + Point to = to_point(node->vertex); svg.draw(Line(from, to), color, width); svg.draw_text(from, std::to_string(index - 1).c_str(), color); @@ -872,8 +937,7 @@ void VoronoiGraphUtils::draw(SVG & svg, draw(svg, circle.nodes, width, circlePathColor, true); Point center(0, 0); for (auto p : circle.nodes) { - center.x() += p->vertex->x(); - center.y() += p->vertex->y(); + center += to_point(p->vertex); } center.x() /= circle.nodes.size(); center.y() /= circle.nodes.size(); diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp index f7fb5818a8..607038e665 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp @@ -27,24 +27,51 @@ public: VoronoiGraphUtils() = delete; /// - /// Convert line edge segment to slicer line - /// only for line edge - /// only for finite line + /// Convert coordinate type between voronoi and slicer format /// - /// line edge - /// line - static Slic3r::Line to_line(const VD::edge_type &edge); + /// Coordinate + /// When it is possible than cast it otherwise empty optional + static coord_t to_coord(const VD::coordinate_type &coord); + + /// + /// Convert Point type to slicer point + /// + /// Input point pointer + /// When it is possible to convert than convert otherwise empty optional + static Slic3r::Point to_point(const VD::vertex_type *vertex); + + /// + /// check if coord is in limits for coord_t + /// + /// input value + /// VD source point coordinate + /// Maximal distance from source + /// True when coord is in +- max_distance from source otherwise FALSE. + static bool is_coord_in_limits(const VD::coordinate_type &coord, + const coord_t & source, + double max_distance); + + /// + /// Check x and y values of vertex + /// + /// input vertex + /// VD source point + /// Maximal distance from source + /// True when both coord are in limits given by source and max distance otherwise FALSE + static bool is_point_in_limits(const VD::vertex_type *vertex, + const Point & source, + double max_distance); /// /// Private function to help convert edge without vertex to line /// - /// VD Source point - /// VD Source point + /// VD source point + /// VD source point /// Maximal distance from source point /// Line segment between lines - static Slic3r::Line to_line(Point point1, - Point point2, - double maximal_distance); + static Slic3r::Line create_line_between_source_points( + const Point &point1, const Point &point2, double maximal_distance); + /// /// Convert edge to line /// only for line edge @@ -54,10 +81,10 @@ public: /// /// Source point for voronoi diagram /// Maximal distance from source point - /// Croped line - static Slic3r::Line to_line(const VD::edge_type & edge, - const Points &points, - double maximal_distance); + /// Croped line, when all line segment is out of max distance return empty optional + static std::optional to_line(const VD::edge_type &edge, + const Points & points, + double maximal_distance); /// /// close polygon defined by lines /// close points will convert to their center @@ -112,7 +139,7 @@ public: /// Source of Voronoi diagram /// Cell inside of Voronoi diagram /// Point from source points. - static Point retrieve_point(const Points &points, const VD::cell_type &cell); + static const Point& retrieve_point(const Points &points, const VD::cell_type &cell); static Slic3r::Point get_parabola_point(const VD::edge_type ¶bola, const Slic3r::Lines &lines); static Slic3r::Line get_parabola_line(const VD::edge_type ¶bola, const Lines &lines); diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 6f8be6ca17..a108ef7bd6 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "sla_test_utils.hpp" @@ -138,59 +139,18 @@ TEST_CASE("Two parallel plates should be supported", "[SupGen][Hollowed]") REQUIRE(!pts.empty()); } -// all triangle side are same length -Slic3r::Polygon equilateral_triangle(double size) -{ - return {{.0, .0}, - {size, .0}, - {size / 2., sqrt(size * size - size * size / 4)}}; -} - -// two side of triangle are same size -Slic3r::Polygon isosceles_triangle(double side, double height) -{ - return {{-side / 2, 0.}, {side / 2, 0.}, {.0, height}}; -} - -Slic3r::Polygon square(double size) -{ - double size_2 = size / 2; - return {{-size_2, size_2}, - {-size_2, -size_2}, - {size_2, -size_2}, - {size_2, size_2}}; -} - -Slic3r::Polygon rect(double x, double y){ - double x_2 = x / 2; - double y_2 = y / 2; - return {{-x_2, y_2}, {-x_2, -y_2}, {x_2, -y_2}, {x_2, y_2}}; -} - -Slic3r::Polygon circle(double radius, size_t count_line_segments) { - // CCW: couter clock wise, CW: clock wise - Points circle; - circle.reserve(count_line_segments); - for (size_t i = 0; i < count_line_segments; ++i) { - double alpha = (2 * M_PI * i) / count_line_segments; - double sina = sin(alpha); - double cosa = cos(alpha); - circle.emplace_back(-radius * sina, radius * cosa); - } - return Slic3r::Polygon(circle); -} Slic3r::Polygon create_cross_roads(double size, double width) { - auto r1 = rect( 5.3 * size, width); + auto r1 = PolygonUtils::create_rect(5.3 * size, width); r1.rotate(3.14/4); r1.translate(2 * size, width / 2); - auto r2 = rect(6.1*size, 3/4.*width); + auto r2 = PolygonUtils::create_rect(6.1 * size, 3 / 4. * width); r2.rotate(-3.14 / 5); r2.translate(3 * size, width / 2); - auto r3 = rect(7.9*size, 4/5.*width); + auto r3 = PolygonUtils::create_rect(7.9 * size, 4 / 5. * width); r3.translate(2*size, width/2); - auto r4 = rect(5 / 6. * width, 5.7 * size); + auto r4 = PolygonUtils::create_rect(5 / 6. * width, 5.7 * size); r4.translate(-size,3*size); Polygons rr = union_(Polygons({r1, r2, r3, r4})); return rr.front(); @@ -198,7 +158,8 @@ Slic3r::Polygon create_cross_roads(double size, double width) ExPolygon create_trinagle_with_hole(double size) { - return ExPolygon(equilateral_triangle(size), {{size / 4, size / 4}, + return ExPolygon(PolygonUtils::create_equilateral_triangle(size), + {{size / 4, size / 4}, {size / 2, size / 2}, {size / 2, size / 4}}); } @@ -206,14 +167,14 @@ ExPolygon create_trinagle_with_hole(double size) ExPolygon create_square_with_hole(double size, double hole_size) { assert(sqrt(hole_size *hole_size / 2) < size); - auto hole = square(hole_size); + auto hole = PolygonUtils::create_square(hole_size); hole.rotate(M_PI / 4.); // 45 hole.reverse(); - return ExPolygon(square(size), hole); + return ExPolygon(PolygonUtils::create_square(size), hole); } ExPolygon create_square_with_4holes(double size, double hole_size) { - auto hole = square(hole_size); + auto hole = PolygonUtils::create_square(hole_size); hole.reverse(); double size_4 = size / 4; auto h1 = hole; @@ -224,7 +185,7 @@ ExPolygon create_square_with_4holes(double size, double hole_size) { h3.translate(size_4, -size_4); auto h4 = hole; h4.translate(-size_4, -size_4); - ExPolygon result(square(size)); + ExPolygon result(PolygonUtils::create_square(size)); result.holes = Polygons({h1, h2, h3, h4}); return result; } @@ -233,14 +194,17 @@ ExPolygon create_square_with_4holes(double size, double hole_size) { ExPolygon create_disc(double radius, double width, size_t count_line_segments) { double width_2 = width / 2; - auto hole = circle(radius-width_2, count_line_segments); + auto hole = PolygonUtils::create_circle(radius - width_2, + count_line_segments); hole.reverse(); - return ExPolygon(circle(radius + width_2, count_line_segments), hole); + return ExPolygon(PolygonUtils::create_circle(radius + width_2, + count_line_segments), + hole); } Slic3r::Polygon create_V_shape(double height, double line_width, double angle = M_PI/4) { double angle_2 = angle / 2; - auto left_side = rect(line_width, height); + auto left_side = PolygonUtils::create_rect(line_width, height); auto right_side = left_side; right_side.rotate(-angle_2); double small_move = cos(angle_2) * line_width / 2; @@ -248,7 +212,7 @@ Slic3r::Polygon create_V_shape(double height, double line_width, double angle = right_side.translate(side_move,0); left_side.rotate(angle_2); left_side.translate(-side_move, 0); - auto bottom = rect(4 * small_move, line_width); + auto bottom = PolygonUtils::create_rect(4 * small_move, line_width); bottom.translate(0., -cos(angle_2) * height / 2 + line_width/2); Polygons polygons = union_(Polygons({left_side, right_side, bottom})); return polygons.front(); @@ -267,22 +231,22 @@ ExPolygons createTestIslands(double size) {size / 7, size}}); ExPolygons result = { // one support point - ExPolygon(equilateral_triangle(size)), - ExPolygon(square(size)), - ExPolygon(rect(size / 2, size)), - ExPolygon(isosceles_triangle(size / 2, 3 * size / 2)), // small sharp triangle - ExPolygon(circle(size/2, 10)), + ExPolygon(PolygonUtils::create_equilateral_triangle(size)), + ExPolygon(PolygonUtils::create_square(size)), + ExPolygon(PolygonUtils::create_rect(size / 2, size)), + ExPolygon(PolygonUtils::create_isosceles_triangle(size / 2, 3 * size / 2)), // small sharp triangle + ExPolygon(PolygonUtils::create_circle(size / 2, 10)), create_square_with_4holes(size, size / 4), create_disc(size/4, size / 4, 10), ExPolygon(create_V_shape(2*size/3, size / 4)), // two support points - ExPolygon(isosceles_triangle(size / 2, 3 * size)), // small sharp triangle - ExPolygon(rect(size / 2, 3 * size)), + ExPolygon(PolygonUtils::create_isosceles_triangle(size / 2, 3 * size)), // small sharp triangle + ExPolygon(PolygonUtils::create_rect(size / 2, 3 * size)), ExPolygon(create_V_shape(1.5*size, size/3)), // tiny line support points - ExPolygon(rect(size / 2, 10 * size)), // long line + ExPolygon(PolygonUtils::create_rect(size / 2, 10 * size)), // long line ExPolygon(create_V_shape(size*4, size / 3)), ExPolygon(create_cross_roads(size, size / 3)), create_disc(3*size, size / 4, 30), @@ -290,8 +254,8 @@ ExPolygons createTestIslands(double size) // still problem // three support points - ExPolygon(equilateral_triangle(3 * size)), - ExPolygon(circle(size, 20)), + ExPolygon(PolygonUtils::create_equilateral_triangle(3 * size)), + ExPolygon(PolygonUtils::create_circle(size, 20)), mountains, create_trinagle_with_hole(size), @@ -444,23 +408,6 @@ TEST_CASE("Sampling speed test on FrogLegs", "[VoronoiSkeleton]") } } -/* -#include -#include -void cgal_test(const SupportIslandPoints &points, const ExPolygon &island) { - using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; - using Delaunay = CGAL::Delaunay_triangulation_2; - std::vector k_points; - k_points.reserve(points.size()); - std::transform(points.begin(), points.end(), std::back_inserter(k_points), - [](const SupportIslandPoint &p) { - return Kernel::Point_2(p.point.x(), p.point.y()); - }); - Delaunay dt; - dt.insert(k_points.begin(), k_points.end()); - std::cout << dt.number_of_vertices() << std::endl; -}*/ - TEST_CASE("Small islands should be supported in center", "[SupGen][VoronoiSkeleton]") { double size = 3e7; diff --git a/tests/sla_print/sla_voronoi_graph_tests.cpp b/tests/sla_print/sla_voronoi_graph_tests.cpp index 2d6692de8a..f44a6dbc50 100644 --- a/tests/sla_print/sla_voronoi_graph_tests.cpp +++ b/tests/sla_print/sla_voronoi_graph_tests.cpp @@ -1,18 +1,47 @@ #include "sla_test_utils.hpp" - #include +#include using namespace Slic3r; using namespace Slic3r::sla; +TEST_CASE("Convert coordinate datatype", "[Voronoi]") +{ + using VD = Slic3r::Geometry::VoronoiDiagram; + VD::coordinate_type coord = 101197493902.64694; + coord_t coord2 = VoronoiGraphUtils::to_coord(coord); + CHECK(coord2 > 100); + + coord = -101197493902.64694; + coord2 = VoronoiGraphUtils::to_coord(coord); + CHECK(coord2 < -100); + + coord = 12345.1; + coord2 = VoronoiGraphUtils::to_coord(coord); + CHECK(coord2 == 12345); + + coord = -12345.1; + coord2 = VoronoiGraphUtils::to_coord(coord); + CHECK(coord2 == -12345); + + coord = 12345.9; + coord2 = VoronoiGraphUtils::to_coord(coord); + CHECK(coord2 == 12346); + + coord = -12345.9; + coord2 = VoronoiGraphUtils::to_coord(coord); + CHECK(coord2 == -12346); +} + void check(Slic3r::Points points, double max_distance) { using VD = Slic3r::Geometry::VoronoiDiagram; VD vd; - construct_voronoi(points.begin(), points.end(), &vd); - double max_area = 3.15 * 4 * max_distance*max_distance; // circle + construct_voronoi(points.begin(), points.end(), &vd); + double max_area = M_PI * max_distance*max_distance; // circle = Pi * r^2 for (const VD::cell_type &cell : vd.cells()) { Slic3r::Polygon polygon = VoronoiGraphUtils::to_polygon(cell, points, max_distance); CHECK(polygon.area() < max_area); + CHECK(polygon.contains(points[cell.source_index()])); } } @@ -21,7 +50,7 @@ TEST_CASE("Polygon from cell", "[Voronoi]") double max_distance = 1e7; coord_t size = (int) (4e6); coord_t half_size = size/2; - + Slic3r::Points two_cols({Point(0, 0), Point(size, 0)}); check(two_cols, max_distance); @@ -40,7 +69,22 @@ TEST_CASE("Polygon from cell", "[Voronoi]") Slic3r::Points middle_point2({Point(half_size, half_size), Point(-size, -size), Point(-size, size), Point(size, -size), Point(size, size)}); - check(middle_point2, max_distance); + check(middle_point2, max_distance); + + Slic3r::Points diagonal_points({{-123473762, 71287970}, + {-61731535, 35684428}, + {0, 0}, + {61731535, -35684428}, + {123473762, -71287970}}); + double diagonal_max_distance = 5e7; + check(diagonal_points, diagonal_max_distance); + + int scale = 10; + Slic3r::Points diagonal_points2; + std::transform(diagonal_points.begin(), diagonal_points.end(), + std::back_inserter(diagonal_points2), + [&](const Slic3r::Point &p) { return p/scale; }); + check(diagonal_points2, diagonal_max_distance / scale); }