From 60a0ac46c249307aba5de06090e1063991c6ccde Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 24 Mar 2021 05:51:16 +0100 Subject: [PATCH] =?UTF-8?q?=EF=BB=BFAlign=20of=20SP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SLA/SupportIslands/LineUtils.cpp | 18 +- .../SLA/SupportIslands/LineUtils.hpp | 6 +- .../SLA/SupportIslands/PolygonUtils.cpp | 11 ++ .../SLA/SupportIslands/PolygonUtils.hpp | 8 + .../SLA/SupportIslands/SampleConfig.hpp | 6 +- .../SupportIslands/SampleConfigFactory.hpp | 6 + .../SLA/SupportIslands/SampleIslandUtils.cpp | 79 +++++++-- .../SLA/SupportIslands/SampleIslandUtils.hpp | 13 +- .../SLA/SupportIslands/SupportIslandPoint.hpp | 10 +- .../SLA/SupportIslands/VectorUtils.hpp | 128 +++++++++++++++ .../SLA/SupportIslands/VoronoiGraphUtils.cpp | 155 ++++++++++++++---- .../SLA/SupportIslands/VoronoiGraphUtils.hpp | 56 +++++-- tests/sla_print/sla_supptgen_tests.cpp | 36 +++- tests/sla_print/sla_voronoi_graph_tests.cpp | 1 + 14 files changed, 447 insertions(+), 86 deletions(-) create mode 100644 src/libslic3r/SLA/SupportIslands/VectorUtils.hpp diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp index b85dff1568..99081b4865 100644 --- a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp @@ -1,24 +1,18 @@ #include "LineUtils.hpp" #include #include +#include "VectorUtils.hpp" using namespace Slic3r::sla; // sort counter clock wise lines void LineUtils::sort_CCW(Lines &lines, const Point& center) { - using namespace Slic3r; - std::sort(lines.begin(), lines.end(), [&](const Line &l1, const Line &l2) { - Point p1 = l1.a - center; - Point p2 = l2.a - center; - if (p1.y() < 0) { - if (p2.y() > 0) return false; - return p1.x() < p2.x(); - } else { - if (p2.y() < 0) return true; - return p1.x() > p2.x(); - } - }); + std::function calc = [¢er](const Line &line) { + Point p = line.a - center; + return std::atan2(p.y(), p.x()); + }; + VectorUtils::sort_by(lines, calc); } bool LineUtils::is_parallel_y(const Line &line) { diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp index 9ad580d78f..0f7338158a 100644 --- a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp @@ -18,10 +18,10 @@ public: LineUtils() = delete; /// - /// Sort lines to be in counter clock wise order + /// Sort lines to be in counter clock wise order only by point Line::a and function std::atan2 /// - /// Lines to sort - /// Center for order + /// Lines to be sort + /// Center for CCW order static void sort_CCW(Lines &lines, const Point ¢er); /// diff --git a/src/libslic3r/SLA/SupportIslands/PolygonUtils.cpp b/src/libslic3r/SLA/SupportIslands/PolygonUtils.cpp index a8785d3663..91e1fc032c 100644 --- a/src/libslic3r/SLA/SupportIslands/PolygonUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/PolygonUtils.cpp @@ -1,4 +1,5 @@ #include "PolygonUtils.hpp" +#include "libslic3r/Geometry.hpp" using namespace Slic3r::sla; @@ -52,4 +53,14 @@ 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}}; +} + +bool PolygonUtils::is_ccw(const Polygon &polygon, const Point ¢er) { + const Point *prev = &polygon.points.back(); + for (const Point &point : polygon.points) { + Geometry::Orientation o = Geometry::orient(center, *prev, point); + if (o != Geometry::Orientation::ORIENTATION_CCW) return false; + prev = &point; + } + return true; } \ No newline at end of file diff --git a/src/libslic3r/SLA/SupportIslands/PolygonUtils.hpp b/src/libslic3r/SLA/SupportIslands/PolygonUtils.hpp index 19f18a871f..3f4495e177 100644 --- a/src/libslic3r/SLA/SupportIslands/PolygonUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/PolygonUtils.hpp @@ -63,6 +63,14 @@ public: /// height /// Rectangle static Polygon create_rect(double width, double height); + + /// + /// check if all pairs on polygon create with center ccw triangle + /// + /// input polygon to check + /// center point inside polygon + /// True when all points in polygon are CCW with center + static bool is_ccw(const Polygon &polygon, const Point ¢er); }; } // 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 de51181a92..5c99e802ba 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp @@ -35,9 +35,13 @@ struct SampleConfig // Term criteria for end of alignment // Minimal change in manhatn move of support position before termination - coord_t minimal_move = 1; + coord_t minimal_move = 1000; // in nanometers, devide from print resolution to quater pixel + // Maximal count of align iteration size_t count_iteration = 100; + + // Maximal distance over Voronoi diagram edges to find closest point during aligning Support point + double max_align_distance = 0; }; } // namespace Slic3r::sla #endif // slic3r_SLA_SuppotstIslands_SampleConfig_hpp_ diff --git a/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp b/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp index 2ab08e3844..64dce6e76d 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp @@ -65,6 +65,12 @@ public: result.max_width_for_zig_zag_supportr_line = max_width_for_zig_zag_supportr_line; assert(result.max_width_for_zig_zag_supportr_line < result.max_width_for_center_supportr_line); + // Align support points + // TODO: propagate print resolution + result.minimal_move = 1000; // [in nanometers], devide from print resolution to quater pixel + result.count_iteration = 100; // speed VS precission + result.max_align_distance = result.max_distance / 2; + return result; } }; diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp index 5727c8da16..8afe0614b6 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp @@ -6,21 +6,27 @@ #include "EvaluateNeighbor.hpp" #include "ParabolaUtils.hpp" #include "VoronoiGraphUtils.hpp" +#include "VectorUtils.hpp" #include #include #include // allign +// comment to enable assert() +// #define NDEBUG +#include + using namespace Slic3r::sla; + SupportIslandPoint SampleIslandUtils::create_point( const VoronoiGraph::Node::Neighbor *neighbor, double ratio, SupportIslandPoint::Type type) { VoronoiGraph::Position position(neighbor, ratio); - Slic3r::Point p = VoronoiGraphUtils::get_edge_point(neighbor->edge, ratio); + Slic3r::Point p = VoronoiGraphUtils::create_edge_point(position); return SupportIslandPoint(p, type, position); } @@ -86,7 +92,7 @@ SupportIslandPoints SampleIslandUtils::sample_side_branch( VoronoiGraph::Nodes reverse_path = side_path.nodes; std::reverse(reverse_path.begin(), reverse_path.end()); reverse_path.push_back(first_node); - return {create_point_on_path(reverse_path, cfg.side_distance)}; + return {create_point_on_path(reverse_path, cfg.side_distance, SupportIslandPoint::Type::center_line_end)}; } // count of segment between points on main path size_t segment_count = static_cast( @@ -122,6 +128,7 @@ SupportIslandPoints SampleIslandUtils::sample_side_branch( prev_node = node; } assert(fabs(distance - (sample_distance - cfg.side_distance)) < 1e-5); + result.back().type = SupportIslandPoint::Type::center_line_end; return result; } @@ -180,34 +187,54 @@ std::vector> create_circles_sets( Slic3r::Points SampleIslandUtils::to_points(const SupportIslandPoints &support_points) { - Slic3r::Points points; - points.reserve(support_points.size()); - std::transform(support_points.begin(), support_points.end(), - std::back_inserter(points), - [](const SupportIslandPoint &p) { - return p.point; }); - return points; + std::function transform_func = &SupportIslandPoint::point; + return VectorUtils::transform(support_points, transform_func); } void SampleIslandUtils::align_samples(SupportIslandPoints &samples, const ExPolygon & island, const SampleConfig & config) { + assert(samples.size() > 2); size_t count_iteration = config.count_iteration; // copy + coord_t max_move = 0; while (--count_iteration > 1) { - coord_t max_move = align_once(samples, island, config); + max_move = align_once(samples, island, config); if (max_move < config.minimal_move) break; } + //std::cout << "Align use " << config.count_iteration - count_iteration + // << " iteration and finish with precision " << max_move << "nano meters" << std::endl; } +bool is_points_in_distance(const Slic3r::Point & p, + const Slic3r::Points &points, + double max_distance) +{ + for (const auto &p2 : points) { + double d = (p - p2).cast().norm(); + if (d > max_distance) return false; + } + return true; +} + +//#define VISUALIZE_SAMPLE_ISLAND_UTILS_ALIGN_ONCE + coord_t SampleIslandUtils::align_once(SupportIslandPoints &samples, const ExPolygon & island, const SampleConfig & config) { + assert(samples.size() > 2); using VD = Slic3r::Geometry::VoronoiDiagram; VD vd; Slic3r::Points points = SampleIslandUtils::to_points(samples); +#ifdef VISUALIZE_SAMPLE_ISLAND_UTILS_ALIGN_ONCE + static int counter = 0; + BoundingBox bbox(island); + SVG svg(("align_"+std::to_string(counter++)+".svg").c_str(), bbox); + svg.draw(island, "lightblue"); +#endif // VISUALIZE_SAMPLE_ISLAND_UTILS_ALIGN_ONCE + // create voronoi diagram with points construct_voronoi(points.begin(), points.end(), &vd); coord_t max_move = 0; @@ -223,16 +250,35 @@ coord_t SampleIslandUtils::align_once(SupportIslandPoints &samples, 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 + assert(island_cell != nullptr); + assert(is_points_in_distance(center, island_cell->points, config.max_distance)); +#ifdef VISUALIZE_SAMPLE_ISLAND_UTILS_ALIGN_ONCE + svg.draw(polygon, "lightgray"); + svg.draw(*island_cell, "gray"); + svg.draw(sample.point, "black", config.head_radius); + svg.draw(Line(sample.point, center), "blue", config.head_radius / 5); +#endif // VISUALIZE_SAMPLE_ISLAND_UTILS_ALIGN_ONCE + coord_t act_move = align_support(sample, center, config.max_align_distance); if (max_move < act_move) max_move = act_move; } return max_move; } +coord_t SampleIslandUtils::align_support(SupportIslandPoint &support, + const Point & wanted, + double max_distance) +{ + //move only along VD + VoronoiGraph::Position position = + VoronoiGraphUtils::align(support.position, wanted, max_distance); + Point new_point = VoronoiGraphUtils::create_edge_point(position); + Point move = new_point - support.point; + support.position= position; + support.point = new_point; + return abs(move.x()) + abs(move.y()); +} + SupportIslandPoints SampleIslandUtils::sample_center_line( const VoronoiGraph::ExPath &path, const CenterLineConfiguration &cfg) { @@ -570,8 +616,9 @@ SupportIslandPoints SampleIslandUtils::sample_expath( 2 * config.minimal_distance_from_outline, config.max_distance, config.minimal_distance_from_outline); - - return sample_center_line(path, centerLineConfiguration); + SupportIslandPoints samples = sample_center_line(path, centerLineConfiguration); + samples.front().type = SupportIslandPoint::Type::center_line_end2; + return samples; } // line of zig zag points diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp index dac556a719..63adfcfd0b 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp @@ -135,7 +135,7 @@ public: /// keep same distances between support points /// call once align /// - /// In/Out support points to be alligned + /// In/Out support points to be alligned(min 3 points) /// Area for sampling, border for position of samples /// Sampling configuration /// Maximal distance between neighbor points + @@ -147,7 +147,7 @@ public: /// /// once align /// - /// In/Out support points to be alligned + /// In/Out support points to be alligned(min 3 points) /// Area for sampling, border for position of samples /// Sampling configuration /// Maximal distance between neighbor points + @@ -156,6 +156,15 @@ public: const ExPolygon & island, const SampleConfig & config); + /// + /// Align support point the closest to Wanted point + /// + /// In/Out support point, contain restriction for move + /// Wanted position point + /// Maximal search distance on VD for closest point + /// Distance move of original point + static coord_t align_support(SupportIslandPoint &support, const Point &wanted, double max_distance); + static void draw(SVG & svg, const SupportIslandPoints &supportIslandPoints, double size, diff --git a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp index e50db35979..821f8af2ce 100644 --- a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp +++ b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp @@ -16,9 +16,11 @@ struct SupportIslandPoint one_center_point, two_points, center_line, + center_line_end, // end of branch + center_line_end2, // start of main path(only one per VD) center_circle, - center_circle_end, - center_circle_end2, + center_circle_end, // circle finish by one point (one end per circle - need allign) + center_circle_end2, // circle finish by multi points (one end per circle - need allign) undefined }; @@ -52,7 +54,9 @@ struct SupportIslandPoint static const std::set cant_move({ Type::one_center_point, - Type::two_points + Type::two_points, + Type::center_line_end, + Type::center_line_end2 }); return cant_move.find(type) == cant_move.end(); diff --git a/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp b/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp new file mode 100644 index 0000000000..5b6319c0ca --- /dev/null +++ b/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp @@ -0,0 +1,128 @@ +#ifndef slic3r_SLA_SuppotstIslands_VectorUtils_hpp_ +#define slic3r_SLA_SuppotstIslands_VectorUtils_hpp_ + +#include +#include +#include + +namespace Slic3r::sla { + +/// +/// Class which contain collection of static function +/// for work with std vector +/// QUESTION: Is it only for SLA? +/// +class VectorUtils +{ +public: + VectorUtils() = delete; + + /// + /// For sorting a vector by calculated value + /// CACHE for calculated values + /// + /// vetor to be sorted + /// function to calculate sorting value + template + static void sort_by(std::vector &data, std::function &calc) + { + // initialize original index locations + std::vector idx(data.size()); + iota(idx.begin(), idx.end(), 0); + + // values used for sort + std::vector v; + v.reserve(data.size()); + for (const T1 &d : data) v.emplace_back(calc(d)); + + // sort indexes based on comparing values in v + // using std::stable_sort instead of std::sort + // to avoid unnecessary index re-orderings + // when v contains elements of equal values + std::stable_sort(idx.begin(), idx.end(), [&v](size_t i1, size_t i2) { + return v[i1] < v[i2]; + }); + + reorder_destructive(idx.begin(), idx.end(), data.begin()); + } + + /// + /// shortcut to use std::transform with alocation for result + /// + /// vetor to be transformed + /// lambda to transform data types + /// result vector + template + static std::vector transform(const std::vector &data, std::function &transform_func) + { + std::vector result; + result.reserve(data.size()); + std::transform(data.begin(), data.end(), std::back_inserter(result), transform_func); + return result; + } + + /// + /// Reorder vector by indexes given by iterators + /// + /// Start index + /// Last index + /// data to reorder. e.g. vector::begin() + template + static void reorder(order_iterator order_begin, + order_iterator order_end, + value_iterator v) + { + typedef typename std::iterator_traits::value_type value_t; + typedef typename std::iterator_traits::value_type index_t; + typedef typename std::iterator_traits::difference_type diff_t; + + diff_t remaining = order_end - 1 - order_begin; + for (index_t s = index_t(), d; remaining > 0; ++s) { + for (d = order_begin[s]; d > s; d = order_begin[d]) + ; + if (d == s) { + --remaining; + value_t temp = v[s]; + while (d = order_begin[d], d != s) { + swap(temp, v[d]); + --remaining; + } + v[s] = temp; + } + } + } + + /// + /// Same as function 'reorder' but destroy order vector for speed + /// + /// Start index + /// Last index + /// data to reorder. e.g. vector::begin() + template + static void reorder_destructive(order_iterator order_begin, + order_iterator order_end, + value_iterator v) + { + typedef typename std::iterator_traits::value_type value_t; + typedef typename std::iterator_traits::value_type index_t; + typedef typename std::iterator_traits::difference_type diff_t; + + diff_t remaining = order_end - 1 - order_begin; + for (index_t s = index_t(); remaining > 0; ++s) { + index_t d = order_begin[s]; + if (d == (diff_t) -1) continue; + --remaining; + value_t temp = v[s]; + for (index_t d2; d != s; d = d2) { + std::swap(temp, v[d]); + std::swap(order_begin[d], d2 = (diff_t) -1); + --remaining; + } + v[s] = temp; + } + } + +}; + +} // namespace Slic3r::sla +#endif // slic3r_SLA_SuppotstIslands_VectorUtils_hpp_ diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp index 56aa476d06..7ec0fd9129 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp @@ -1,6 +1,7 @@ #include "VoronoiGraphUtils.hpp" #include +#include #include #include "IStackFunction.hpp" #include "EvaluateNeighbor.hpp" @@ -11,7 +12,7 @@ #include -#define SLA_CELL_2_POLYGON_DEBUG +//#define SLA_CELL_2_POLYGON_DEBUG using namespace Slic3r::sla; @@ -31,6 +32,11 @@ Slic3r::Point VoronoiGraphUtils::to_point(const VD::vertex_type *vertex) return Point(to_coord(vertex->x()), to_coord(vertex->y())); } +Slic3r::Vec2d VoronoiGraphUtils::to_point_d(const VD::vertex_type *vertex) +{ + return Vec2d(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) @@ -120,27 +126,10 @@ std::optional VoronoiGraphUtils::to_line( 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; - //*/ - - // slower but more precisse version - 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); - 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); + Point ray_point = to_point(edge_vertex); + Line ray(ray_point, ray_point + direction); + return LineUtils::crop_half_ray(ray, p1, maximal_distance); } std::optional segment; if (use_both) { @@ -148,7 +137,7 @@ std::optional VoronoiGraphUtils::to_line( 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()); + Linef ray(ray_point, ray_point + direction.cast()); segment = LineUtils::crop_half_ray(ray, p1, maximal_distance); } if (!segment.has_value()) return {}; @@ -196,7 +185,6 @@ 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; - double x = cos(angle) * maximal_distance + center.x(); assert(x < std::numeric_limits::max()); assert(x > std::numeric_limits::min()); @@ -207,7 +195,23 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const Lines &lines, } points.push_back(p2); } - return Polygon(points); + Polygon polygon(points); + assert(polygon.is_valid()); + if (!polygon.contains(center)) { + SVG svg("bad_polygon.svg", {polygon.points}); + svg.draw(polygon, "orange"); + svg.draw(lines, "red"); + int counter = 0; + for (auto &line : lines) { + ++counter; + svg.draw_text(line.a, ("A"+std::to_string(counter)).c_str(), "lightgreen"); + svg.draw_text(line.b, ("B" + std::to_string(counter)).c_str(), "lightblue"); + } + svg.draw(center); + } + assert(polygon.contains(center)); + assert(PolygonUtils::is_ccw(polygon, center)); + return polygon; } Slic3r::Polygon VoronoiGraphUtils::to_polygon(const VD::cell_type & cell, @@ -229,9 +233,13 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const VD::cell_type & cell, edge = edge->next(); continue; } - Geometry::Orientation o = Geometry::orient(center, line->a, line->b); - assert(o != Geometry::Orientation::ORIENTATION_COLINEAR); - if (o == Geometry::Orientation::ORIENTATION_CW) + Geometry::Orientation orientation = Geometry::orient(center, line->a, line->b); + if (orientation == Geometry::Orientation::ORIENTATION_COLINEAR) + { // on circle over source point edge + edge = edge->next(); + continue; + } + if (orientation == Geometry::Orientation::ORIENTATION_CW) std::swap(line->a, line->b); lines.push_back(line.value()); edge = edge->next(); @@ -411,7 +419,7 @@ double VoronoiGraphUtils::calculate_max_width( return 2 * std::max(distance0, distance1); } -VoronoiGraph VoronoiGraphUtils::getSkeleton(const VD &vd, const Lines &lines) +VoronoiGraph VoronoiGraphUtils::create_skeleton(const VD &vd, const Lines &lines) { // vd should be annotated. // assert(Voronoi::debug::verify_inside_outside_annotations(vd)); @@ -781,8 +789,14 @@ const VoronoiGraph::Node *VoronoiGraphUtils::get_twin_node(const VoronoiGraph::N return nullptr; } -Slic3r::Point VoronoiGraphUtils::get_edge_point(const VD::edge_type *edge, - double ratio) +Slic3r::Point VoronoiGraphUtils::create_edge_point( + const VoronoiGraph::Position &position) +{ + return create_edge_point(position.neighbor->edge, position.ratio); +} + +Slic3r::Point VoronoiGraphUtils::create_edge_point(const VD::edge_type *edge, + double ratio) { const VD::vertex_type *v0 = edge->vertex0(); const VD::vertex_type *v1 = edge->vertex1(); @@ -807,6 +821,87 @@ Slic3r::Point VoronoiGraphUtils::get_edge_point(const VD::edge_type *edge, return Point(v0->x() + dir.x(), v0->y() + dir.y()); } +VoronoiGraph::Position VoronoiGraphUtils::align( + const VoronoiGraph::Position &position, const Point &to, double max_distance) +{ + // for each neighbor in max distance try align edge + struct NodeDistance + { + const VoronoiGraph::Node *node; + double distance; // distance to search for closest point + NodeDistance(const VoronoiGraph::Node *node, double distance) + : node(node), distance(distance) + {} + }; + std::queue process; + const VoronoiGraph::Node::Neighbor* neighbor = position.neighbor; + double from_distance = neighbor->edge_length * position.ratio; + if (from_distance < max_distance) { + const VoronoiGraph::Node *from_node = VoronoiGraphUtils::get_twin_node(neighbor); + process.emplace(from_node, from_distance); + } + double to_distance = neighbor->edge_length * (1 - position.ratio); + if (to_distance < max_distance) { + const VoronoiGraph::Node *to_node = neighbor->node; + process.emplace(to_node, to_distance); + } + if (process.empty()) { + const VoronoiGraph::Node *node = (position.ratio < 0.5) ? + VoronoiGraphUtils::get_twin_node(neighbor) : neighbor->node; + process.emplace(node, max_distance); + } + + double closest_distance = std::numeric_limits::max(); + VoronoiGraph::Position closest; + + std::set done; + while (!process.empty()) { + NodeDistance nd = process.front(); // copy + process.pop(); + if (done.find(nd.node) != done.end()) continue; + done.insert(nd.node); + for (const auto &neighbor : nd.node->neighbors) { + if (done.find(neighbor.node) != done.end()) continue; + double ratio; + double distance = get_distance(*neighbor.edge, to, ratio); + if (closest_distance > distance) { + closest_distance = distance; + closest = VoronoiGraph::Position(&neighbor, ratio); + } + double from_start = nd.distance + neighbor.edge_length; + if (from_start < max_distance) + process.emplace(neighbor.node, from_start); + } + } + return closest; +} + +double VoronoiGraphUtils::get_distance(const VD::edge_type &edge, + const Point & point, + double & edge_ratio) +{ + // TODO: find closest point on curve edge + //if (edge.is_linear()) { + + // get line foot point, inspired Geometry::foot_pt + Vec2d v0 = to_point_d(edge.vertex0()); + Vec2d v = point.cast() - v0; + Vec2d v1 = to_point_d(edge.vertex1()); + Vec2d edge_dir = v1 - v0; + double l2 = edge_dir.squaredNorm(); + edge_ratio = v.dot(edge_dir) / l2; + // IMPROVE: not neccesary to calculate point if (edge_ratio > 1 || edge_ratio < 0) + Point edge_point; + if (edge_ratio > 1.) edge_point = v1.cast(); + else if (edge_ratio < 0.) edge_point = v0.cast(); + else { // foot point + edge_point = (v0 + edge_dir * edge_ratio).cast(); + } + double distance = (point - edge_point).cast().norm(); + return distance; +} + + const VoronoiGraph::Node *VoronoiGraphUtils::getFirstContourNode( const VoronoiGraph &graph) { diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp index 607038e665..f130455abc 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp @@ -34,12 +34,19 @@ public: static coord_t to_coord(const VD::coordinate_type &coord); /// - /// Convert Point type to slicer point + /// Convert Vodonoi diagram vertex type to Slicer Point /// - /// Input point pointer - /// When it is possible to convert than convert otherwise empty optional + /// Input point pointer(double precission) + /// Convertedf point(int preccission) static Slic3r::Point to_point(const VD::vertex_type *vertex); + /// + /// Convert point type between voronoi and slicer format + /// + /// Input vertex + /// created vector + static Slic3r::Vec2d to_point_d(const VD::vertex_type* vertex); + /// /// check if coord is in limits for coord_t /// @@ -95,7 +102,7 @@ public: /// Radius around center point /// Merge points closer than minimal_distance /// Count checking points, create help points for result polygon - /// CCW polygon with center inside of polygon + /// Valid CCW polygon with center inside of polygon static Slic3r::Polygon to_polygon(const Lines &lines, const Point ¢er, double maximal_distance, @@ -179,16 +186,7 @@ public: /// (use function Slic3r::Voronoi::annotate_inside_outside) /// Source lines for voronoi diagram /// Extended voronoi graph by distances and length - static VoronoiGraph getSkeleton(const VD &vd, const Lines &lines); - - /// - /// For generating support point in distance from node - /// - /// Node lay on outline with only one neighbor - /// Distance from outline - /// - static Slic3r::Point get_offseted_point(const VoronoiGraph::Node &node, - double padding); + static VoronoiGraph create_skeleton(const VD &vd, const Lines &lines); /// /// find neighbor and return distance between nodes @@ -299,7 +297,35 @@ public: /// Create point on edge defined by neighbor /// in distance defined by edge length ratio /// - static Point get_edge_point(const VD::edge_type *edge, double ratio); + /// Containe neighbor and position ratio on neighbor + /// Point laying on neighbor edge + static Point create_edge_point(const VoronoiGraph::Position& position); + static Point create_edge_point(const VD::edge_type *edge, double ratio); + + /// + /// align "position" close to point "to" + /// + /// input position on VD + /// point to align + /// maximal distance on VD for search point + /// Position on VD + static VoronoiGraph::Position align(const VoronoiGraph::Position &position, + const Point & to, + double max_distance); + + /// + /// Calc position by ratio to closest point laying on edge + /// + /// edge to align + /// point to align + /// output: ratio between vertex0 and vertex1 closest to point, + /// when less than zero vertex0 is closest point on edge + /// when grater than one vertex1 is closest point on edge + /// distance edge to "to" point + /// only when result ratio is in range from 0 to 1 + static double get_distance(const VD::edge_type &edge, + const Point & point, + double & edge_ratio); static const VoronoiGraph::Node *getFirstContourNode( const VoronoiGraph &graph); diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index a108ef7bd6..1ba5209338 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -321,8 +321,8 @@ SupportIslandPoints test_island_sampling(const ExPolygon & island, const SampleConfig &config) { auto points = SupportPointGenerator::uniform_cover_island(island, config); - Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer + Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer bool is_ok = true; double max_distance = config.max_distance; std::vector point_distances(chck_points.size(), @@ -353,7 +353,7 @@ SupportIslandPoints test_island_sampling(const ExPolygon & island, for (const Point &pt : island.contour.points) bb.merge(pt); SVG svg("Error" + std::to_string(++counter) + ".svg", bb); svg.draw(island, "blue", 0.5f); - for (auto p : points) + for (auto& p : points) svg.draw(p.point, "lightgreen", config.head_radius); for (size_t index = 0; index < chck_points.size(); ++index) { const Point &chck_point = chck_points[index]; @@ -380,6 +380,10 @@ SampleConfig create_sample_config(double size) { cfg.max_length_for_two_support_points = 4*size; cfg.max_width_for_center_supportr_line = size; cfg.max_width_for_zig_zag_supportr_line = 2*size; + + cfg.minimal_move = std::max(1000., size/1000); + cfg.count_iteration = 100; + cfg.max_align_distance = 0; return cfg; } @@ -403,17 +407,41 @@ TEST_CASE("Sampling speed test on FrogLegs", "[VoronoiSkeleton]") for (int i = 0; i < 100; ++i) { VoronoiGraph::ExPath longest_path; - VoronoiGraph skeleton = VoronoiGraphUtils::getSkeleton(vd, lines); + VoronoiGraph skeleton = VoronoiGraphUtils::create_skeleton(vd, lines); auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, cfg, longest_path); } } +TEST_CASE("Speed align", "[VoronoiSkeleton]") +{ + SampleConfig cfg = create_sample_config(3e7); + cfg.max_align_distance = 1000; + cfg.count_iteration = 1000; + cfg.max_align_distance = 3e7; + + double size = 3e7; + auto island = create_square_with_4holes(5 * size, 5 * size / 2 - size / 3); + using VD = Slic3r::Geometry::VoronoiDiagram; + VD vd; + Lines lines = to_lines(island); + construct_voronoi(lines.begin(), lines.end(), &vd); + Slic3r::Voronoi::annotate_inside_outside(vd, lines); + VoronoiGraph::ExPath longest_path; + VoronoiGraph skeleton = VoronoiGraphUtils::create_skeleton(vd, lines); + auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, cfg, longest_path); + + for (int i = 0; i < 100; ++i) { auto sample_copy = samples; // copy + SampleIslandUtils::align_samples(sample_copy, island, cfg); + } +} + TEST_CASE("Small islands should be supported in center", "[SupGen][VoronoiSkeleton]") { double size = 3e7; SampleConfig cfg = create_sample_config(size); ExPolygons islands = createTestIslands(size); - for (auto &island : islands) { + for (ExPolygon &island : islands) { + size_t debug_index = &island - &islands.front(); auto points = test_island_sampling(island, cfg); //cgal_test(points, island); double angle = 3.14 / 3; // cca 60 degree diff --git a/tests/sla_print/sla_voronoi_graph_tests.cpp b/tests/sla_print/sla_voronoi_graph_tests.cpp index f44a6dbc50..ff7a4bc24d 100644 --- a/tests/sla_print/sla_voronoi_graph_tests.cpp +++ b/tests/sla_print/sla_voronoi_graph_tests.cpp @@ -47,6 +47,7 @@ void check(Slic3r::Points points, double max_distance) { TEST_CASE("Polygon from cell", "[Voronoi]") { + // for debug #define SLA_CELL_2_POLYGON_DEBUG in VoronoiGraphUtils double max_distance = 1e7; coord_t size = (int) (4e6); coord_t half_size = size/2;