#ifndef slic3r_Geometry_Voronoi_hpp_ #define slic3r_Geometry_Voronoi_hpp_ #include "../Line.hpp" #include "../Polyline.hpp" #ifdef _MSC_VER // Suppress warning C4146 in OpenVDB: unary minus operator applied to unsigned type, result still unsigned #pragma warning(push) #pragma warning(disable : 4146) #endif // _MSC_VER #include "boost/polygon/voronoi.hpp" #ifdef _MSC_VER #pragma warning(pop) #endif // _MSC_VER namespace Slic3r::Geometry { class VoronoiDiagram { public: using coord_type = double; using voronoi_diagram_type = boost::polygon::voronoi_diagram; using point_type = boost::polygon::point_data; using segment_type = boost::polygon::segment_data; using rect_type = boost::polygon::rectangle_data; using coordinate_type = voronoi_diagram_type::coordinate_type; using vertex_type = voronoi_diagram_type::vertex_type; using edge_type = voronoi_diagram_type::edge_type; using cell_type = voronoi_diagram_type::cell_type; using const_vertex_iterator = voronoi_diagram_type::const_vertex_iterator; using const_edge_iterator = voronoi_diagram_type::const_edge_iterator; using const_cell_iterator = voronoi_diagram_type::const_cell_iterator; using vertex_container_type = voronoi_diagram_type::vertex_container_type; using edge_container_type = voronoi_diagram_type::edge_container_type; using cell_container_type = voronoi_diagram_type::cell_container_type; enum class IssueType { NO_ISSUE_DETECTED, FINITE_EDGE_WITH_NON_FINITE_VERTEX, MISSING_VORONOI_VERTEX, NON_PLANAR_VORONOI_DIAGRAM, VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT, UNKNOWN // Repairs are disabled in the constructor. }; enum class State { REPAIR_NOT_NEEDED, // The original Voronoi diagram doesn't have any issue. REPAIR_SUCCESSFUL, // The original Voronoi diagram has some issues, but it was repaired. REPAIR_UNSUCCESSFUL, // The original Voronoi diagram has some issues, but it wasn't repaired. UNKNOWN // Repairs are disabled in the constructor. }; VoronoiDiagram() = default; virtual ~VoronoiDiagram() = default; IssueType get_issue_type() const { return m_issue_type; } State get_state() const { return m_state; } bool is_valid() const { return m_state != State::REPAIR_UNSUCCESSFUL; } void clear(); const vertex_container_type &vertices() const { return m_is_modified ? m_vertices : m_voronoi_diagram.vertices(); } const edge_container_type &edges() const { return m_is_modified ? m_edges : m_voronoi_diagram.edges(); } const cell_container_type &cells() const { return m_is_modified ? m_cells : m_voronoi_diagram.cells(); } std::size_t num_vertices() const { return m_is_modified ? m_vertices.size() : m_voronoi_diagram.num_vertices(); } std::size_t num_edges() const { return m_is_modified ? m_edges.size() : m_voronoi_diagram.num_edges(); } std::size_t num_cells() const { return m_is_modified ? m_cells.size() : m_voronoi_diagram.num_cells(); } template typename boost::polygon::enable_if< typename boost::polygon::gtl_if::value_type>::type>::type>::type, void>::type construct_voronoi(SegmentIterator segment_begin, SegmentIterator segment_end, bool try_to_repair_if_needed = true); template typename boost::polygon::enable_if< typename boost::polygon::gtl_if::value_type>::type>::type>::type, void>::type construct_voronoi(const PointIterator first, const PointIterator last) { boost::polygon::construct_voronoi(first, last, &m_voronoi_diagram); m_state = State::UNKNOWN; m_issue_type = IssueType::UNKNOWN; } template typename boost::polygon::enable_if< typename boost::polygon::gtl_and< typename boost::polygon::gtl_if::value_type>::type>::type>::type, typename boost::polygon::gtl_if::value_type>::type>::type>::type>::type, void>::type construct_voronoi(const PointIterator p_first, const PointIterator p_last, const SegmentIterator s_first, const SegmentIterator s_last) { boost::polygon::construct_voronoi(p_first, p_last, s_first, s_last, &m_voronoi_diagram); m_state = State::UNKNOWN; m_issue_type = IssueType::UNKNOWN; } // Try to detect cases when some Voronoi vertex is missing, when the Voronoi diagram // is not planar or some Voronoi edge is intersecting input segment. template static typename boost::polygon::enable_if< typename boost::polygon::gtl_if::value_type>::type>::type>::type, IssueType>::type detect_known_issues(const VoronoiDiagram &voronoi_diagram, SegmentIterator segment_begin, SegmentIterator segment_end); template typename boost::polygon::enable_if< typename boost::polygon::gtl_if::value_type>::type>::type>::type, VoronoiDiagram::IssueType>::type try_to_repair_degenerated_voronoi_diagram_by_rotation(SegmentIterator segment_begin, SegmentIterator segment_end, double fix_angle); template typename boost::polygon::enable_if< typename boost::polygon::gtl_if::value_type>::type>::type>::type, VoronoiDiagram::IssueType>::type try_to_repair_degenerated_voronoi_diagram(SegmentIterator segment_begin, SegmentIterator segment_end); private: struct Segment { Point from; Point to; Segment() = delete; explicit Segment(const Point &from, const Point &to) : from(from), to(to) {} }; void copy_to_local(voronoi_diagram_type &voronoi_diagram); // Detect issues related to Voronoi cells, or that can be detected by iterating over Voronoi cells. // The first type of issue that can be detected is a missing Voronoi vertex, especially when it is // missing at one of the endpoints of the input segment. // The second type of issue that can be detected is a Voronoi edge that intersects the input segment. template static typename boost::polygon::enable_if< typename boost::polygon::gtl_if::value_type>::type>::type>::type, IssueType>::type detect_known_voronoi_cell_issues(const VoronoiDiagram &voronoi_diagram, SegmentIterator segment_begin, SegmentIterator segment_end); static bool has_finite_edge_with_non_finite_vertex(const VoronoiDiagram &voronoi_diagram); voronoi_diagram_type m_voronoi_diagram; vertex_container_type m_vertices; edge_container_type m_edges; cell_container_type m_cells; bool m_is_modified = false; State m_state = State::UNKNOWN; IssueType m_issue_type = IssueType::UNKNOWN; public: using SegmentIt = std::vector::iterator; friend struct boost::polygon::segment_traits; }; } // namespace Slic3r::Geometry namespace boost::polygon { template<> struct geometry_concept { typedef segment_concept type; }; template<> struct segment_traits { using coordinate_type = coord_t; using point_type = Slic3r::Point; using segment_type = Slic3r::Geometry::VoronoiDiagram::Segment; static inline point_type get(const segment_type &segment, direction_1d dir) { return dir.to_int() ? segment.to : segment.from; } }; } // namespace boost::polygon #endif // slic3r_Geometry_Voronoi_hpp_