From f9bc03fb6e0f321d9512463918af2ac9aa28bba8 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 4 Oct 2024 13:16:07 +0200 Subject: [PATCH] Use CGAL voronoi to calculate aligne of support point on the island --- src/libslic3r/CMakeLists.txt | 1 + .../SLA/SupportIslands/SampleIslandUtils.cpp | 35 ++- .../SLA/SupportIslands/VoronoiDiagramCGAL.cpp | 231 ++++++++++++++++++ .../SLA/SupportIslands/VoronoiDiagramCGAL.hpp | 26 ++ 4 files changed, 280 insertions(+), 13 deletions(-) create mode 100644 src/libslic3r/SLA/SupportIslands/VoronoiDiagramCGAL.cpp create mode 100644 src/libslic3r/SLA/SupportIslands/VoronoiDiagramCGAL.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e9ea1343f1..a19cc5d058 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -573,6 +573,7 @@ add_library(libslic3r_cgal STATIC MeshBoolean.hpp MeshBoolean.cpp TryCatchSignal.hpp TryCatchSignal.cpp Triangulation.hpp Triangulation.cpp + SLA/SupportIslands/VoronoiDiagramCGAL.hpp SLA/SupportIslands/VoronoiDiagramCGAL.cpp ) target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(libslic3r_cgal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..) diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp index cd3fbafaf2..c174cebb10 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp @@ -17,6 +17,8 @@ #include "libslic3r/SLA/SupportPointGenerator.hpp" +#include "VoronoiDiagramCGAL.hpp" // aligning of points + // comment definition of NDEBUG to enable assert() //#define NDEBUG @@ -25,7 +27,7 @@ //#define SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG //#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGNED_TO_SVG -//#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH "C:/data/temp/Align_once_<>.svg" +//#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH "C:/data/temp/align_once/iter_<>.svg" //#define SLA_SAMPLE_ISLAND_UTILS_DEBUG_CELL_DISTANCE_PATH "C:/data/temp/island_cell.svg" #include @@ -532,7 +534,7 @@ coord_t SampleIslandUtils::align_once( // IMPROVE2: add filter for create cell polygon only for moveable samples Slic3r::Points points = SampleIslandUtils::to_points(samples); - Polygons cell_polygons = //* + Polygons cell_polygons = /* create_voronoi_cells_boost /*/ create_voronoi_cells_cgal @@ -578,20 +580,27 @@ coord_t SampleIslandUtils::align_once( // IMPROVE: add intersection polygon with expolygon Polygons intersections = Slic3r::intersection(island, ExPolygon(cell_polygon)); const Polygon *island_cell = nullptr; - for (const Polygon &intersection : intersections) { - if (intersection.contains(sample->point)) { - island_cell = &intersection; - break; + if (intersections.size() == 1) { + island_cell = &intersections.front(); + // intersection island and cell made by suppot point + // must generate polygon containing initial source for voronoi cell + // otherwise it is invalid voronoi diagram + assert(island_cell->contains(sample->point)); + } else { + for (const Polygon &intersection : intersections) { + if (intersection.contains(sample->point)) { + island_cell = &intersection; + break; + } } + // intersection island and cell made by suppot point + // must generate polygon containing initial source for voronoi cell + // otherwise it is invalid voronoi diagram + assert(island_cell != nullptr); + if (island_cell == nullptr) + continue; } - // intersection island and cell made by suppot point - // must generate polygon containing initial source for voronoi cell - // otherwise it is invalid voronoi diagram - assert(island_cell != nullptr); - if (island_cell == nullptr) - continue; - // new aligned position for sample Point island_cell_center = island_cell->centroid(); diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiDiagramCGAL.cpp b/src/libslic3r/SLA/SupportIslands/VoronoiDiagramCGAL.cpp new file mode 100644 index 0000000000..1b7a092fd2 --- /dev/null +++ b/src/libslic3r/SLA/SupportIslands/VoronoiDiagramCGAL.cpp @@ -0,0 +1,231 @@ +#include "VoronoiDiagramCGAL.hpp" + +#include + +// includes for defining the Voronoi diagram adaptor +#include +#include +#include +#include +#include + +#include "libslic3r/Geometry.hpp" +#include "libslic3r/Line.hpp" +#include "libslic3r/SLA/SupportIslands/LineUtils.hpp" +#include "libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp" + +using namespace Slic3r; +using namespace Slic3r::sla; + +// typedefs for defining the adaptor +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using DT = CGAL::Delaunay_triangulation_2; +using AT = CGAL::Delaunay_triangulation_adaptation_traits_2
; +using AP = CGAL::Delaunay_triangulation_caching_degeneracy_removal_policy_2
; +using VD = CGAL::Voronoi_diagram_2; + +// typedef for the result type of the point location +using Site_2 = AT::Site_2; +using Point_2 = AT::Point_2; +using Locate_result = VD::Locate_result; +using Vertex_handle = VD::Vertex_handle; +using Face_handle = VD::Face_handle; +using Halfedge_handle = VD::Halfedge_handle; +using Ccb_halfedge_circulator = VD::Ccb_halfedge_circulator; + +namespace{ +Vec2d to_point_d(const Site_2 &s) { return {s.x(), s.y()}; } +Slic3r::Point to_point(const Site_2 &s) { + // conversion from double to coor_t + return Slic3r::Point(s.x(), s.y()); +} + +/// +/// Create line segment lay between given points with distance limited by maximal_distance +/// +/// +/// +/// limits for result segment +/// line perpendicular to line between point1 and point2 +Slic3r::Line create_line_between_points( + const Point &point1, const Point &point2, double maximal_distance +) { + Point middle = (point1 + point2) / 2; + Point diff = point1 - point2; // direction from point2 to point1 + coord_t manhatn_distance = abs(diff.x()) + abs(diff.y()); + // it is not neccesary to know exact distance + // One need to know minimal distance between points. + // worst case is diagonal: sqrt(2*(0.5 * manhatn_distance)^2) = + double min_distance = manhatn_distance * .7; // 1 / sqrt(2) + double scale = maximal_distance / min_distance; + Point side_dir(-diff.y() * scale, diff.x() * scale); + return Line(middle - side_dir, middle + side_dir); +} + +/// +/// Crop ray to line which is no too far away(compare to maximal_distance) from v1(or v2) +/// +/// ray start +/// +/// +/// Limits for line +/// +std::optional crop_ray(const Point_2 &p, const Point_2 &v1, const Point_2 &v2, double maximal_distance) { + assert(maximal_distance > 0); + Vec2d ray_point = to_point_d(p); + Vec2d ray_dir(v1.y() - v2.y(), v2.x() - v1.x()); + + // bounds are similar as for line between points + Vec2d middle = (to_point_d(v1) + to_point_d(v2)) / 2; + double abs_x = abs(ray_dir.x()); + double abs_y = abs(ray_dir.y()); + double manhatn_dist = abs_x + abs_y; // maximal distance + + // alligned points should not be too close + assert(manhatn_dist <= 1e-5); + + double min_distance = manhatn_dist * .7; + assert(min_distance > 0); + double scale = maximal_distance / min_distance; + + double middle_t = (abs_x > abs_y) ? + // use x coor + (middle.x() - ray_point.x()) / ray_dir.x() : + // use y coor + (middle.y() - ray_point.y()) / ray_dir.y(); + + // minimal distance from ray point to middle point + double min_middle_dist = middle_t * min_distance; + if (min_middle_dist < -2 * maximal_distance) + // ray start out of area of interest + return {}; + + if (min_middle_dist > 2 * maximal_distance) + // move ray start close to middle + ray_point = middle - ray_dir * scale; + + return Line(ray_point.cast(), (middle + ray_dir * scale).cast()); +} + +std::optional to_line( + const Halfedge_handle &edge, + double maximal_distance +) { + assert(edge->is_valid()); + if (!edge->is_valid()) + return {}; + + auto crop_ray = [&maximal_distance](const Point_2 &p, const Point_2 &v1, const Point_2 &v2) + -> std::optional { + Vec2d ray_point = to_point_d(p); + Vec2d dir(v1.y() - v2.y(), v2.x() - v1.x()); + Linef ray(ray_point, ray_point + dir); + auto seg_opt = LineUtils::crop_half_ray(ray, to_point(v1), maximal_distance); + if (!seg_opt.has_value()) + return {}; + return Line(seg_opt->a.cast(), seg_opt->b.cast()); + }; + + if (edge->has_source()) { + // source point of edge + if (edge->has_target()) { // Line segment + assert(edge->is_segment()); + return Line( + to_point(edge->source()->point()), + to_point(edge->target()->point())); + } else { // ray from source + assert(edge->is_ray()); + return crop_ray( + edge->source()->point(), + edge->up()->point(), + edge->down()->point()); + } + } else if (edge->has_target()) { // ray from target + assert(edge->is_ray()); + return crop_ray( + edge->target()->point(), + edge->down()->point(), + edge->up()->point()); + } + // infinite line between points + assert(edge->is_bisector()); + return create_line_between_points( + to_point(edge->up()->point()), + to_point(edge->down()->point()), + maximal_distance + ); +} + +} + +Polygons Slic3r::sla::create_voronoi_cells_cgal(const Points &points, coord_t max_distance) { + assert(points.size() > 1); + + // Different way to fill points into VD + // delaunary triangulation + std::vector dt_points; + dt_points.reserve(points.size()); + for (const Point &p : points) + dt_points.emplace_back(p.x(), p.y()); + DT dt(dt_points.begin(), dt_points.end()); + assert(dt.is_valid()); + VD vd(dt); + assert(vd.is_valid()); + + // voronoi diagram seems that points face order is same as inserted points + //VD vd; + //for (const Point& p: points) { + // Site_2 t(p.x(), p.y()); + // vd.insert(t); + //} + + Polygons cells(points.size()); + size_t fit_index = 0; + // Loop over the faces of the Voronoi diagram in order of given points + for (VD::Face_iterator fit = vd.faces_begin(); fit != vd.faces_end(); ++fit, ++fit_index) { + // get source point index + // TODO: do it without search !!! + Point_2 source_point = fit->dual()->point(); + Slic3r::Point source_pt(source_point.x(), source_point.y()); + auto it = std::find(points.begin(), points.end(), source_pt); + assert(it != points.end()); + if (it == points.end()) + continue; + size_t index = it - points.begin(); + assert(source_pt.x() == points[index].x()); + assert(source_pt.y() == points[index].y()); + + // origin of voronoi face + const Point& origin = points[index]; + Lines lines; + // collect croped lines of field + Ccb_halfedge_circulator ec_start = fit->ccb(); + Ccb_halfedge_circulator ec = ec_start; + do { + assert(ec->is_valid()); + std::optional line_opt = to_line(ec, max_distance); + if (!line_opt.has_value()) + continue; + Line &line = *line_opt; + Geometry::Orientation orientation = Geometry::orient(origin, line.a, line.b); + // Can be rich on circle over source point edge + if (orientation == Geometry::Orientation::ORIENTATION_COLINEAR) continue; + if (orientation == Geometry::Orientation::ORIENTATION_CW) std::swap(line.a, line.b); + lines.push_back(std::move(line)); + } while (++ec != ec_start); + assert(!lines.empty()); + if (lines.size() > 1) + LineUtils::sort_CCW(lines, origin); + + // preccission to decide when not connect neighbor points + double min_distance = max_distance / 1000.; + size_t count_point = 6; // count added points + // cell for current processed face + cells[index] = + VoronoiGraphUtils::to_polygon(lines, origin, max_distance, min_distance, count_point); + } + + // Point_2 p; + // Locate_result lr = vd.locate(p); // Could locate face of VD - potentionaly could iterate input points + return cells; +} \ No newline at end of file diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiDiagramCGAL.hpp b/src/libslic3r/SLA/SupportIslands/VoronoiDiagramCGAL.hpp new file mode 100644 index 0000000000..02781e5810 --- /dev/null +++ b/src/libslic3r/SLA/SupportIslands/VoronoiDiagramCGAL.hpp @@ -0,0 +1,26 @@ +///|/ Copyright (c) Prusa Research 2024 Filip Sykala @Jony01 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_SLA_SuppotstIslands_VoronoiDiagramCGAL_hpp_ +#define slic3r_SLA_SuppotstIslands_VoronoiDiagramCGAL_hpp_ + +#include +#include + +namespace Slic3r::sla { + +/// +/// +/// IMPROVE1: use accessor to point coordinate instead of points +/// IMPROVE2: add filter for create cell polygon only for moveable samples +/// +/// Input points for voronoi diagram +/// Limit for polygon made by point +/// NOTE: prerequisities input points are in max_distance only outer have infinite cell +/// which are croped to max_distance +/// Polygon cell for input point +Polygons create_voronoi_cells_cgal(const Points &points, coord_t max_distance); + +} +#endif // slic3r_SLA_SuppotstIslands_VoronoiDiagramCGAL_hpp_