Prusa Slicer 2.6.0
Loading...
Searching...
No Matches
Slic3r::Voronoi Namespace Reference

Namespaces

namespace  debug
 
namespace  detail
 
namespace  Internal
 

Typedefs

using VD = Slic3r::Geometry::VoronoiDiagram
 

Enumerations

enum class  VertexCategory : unsigned char { OnContour , Inside , Outside , Unknown }
 
enum class  EdgeCategory : unsigned char { PointsToContour , PointsInside , PointsOutside , Unknown }
 
enum class  CellCategory : unsigned char { Boundary , Inside , Outside , Unknown }
 

Functions

void reset_inside_outside_annotations (VD &vd)
 
void annotate_inside_outside (VD &vd, const Lines &lines)
 
std::vector< double > signed_vertex_distances (const VD &vd, const Lines &lines)
 
std::vector< Vec2dedge_offset_contour_intersections (const VD &vd, const Lines &lines, const std::vector< double > &vertex_distances, double offset_distance)
 
Polygons offset (const Geometry::VoronoiDiagram &vd, const Lines &lines, const std::vector< double > &signed_vertex_distances, double offset_distance, double discretization_error)
 
Polygons offset (const VD &vd, const Lines &lines, double offset_distance, double discretization_error)
 
std::vector< Vec2dskeleton_edges_rough (const VD &vd, const Lines &lines, const double threshold_alpha)
 
const Pointcontour_point (const VD::cell_type &cell, const Line &line)
 
Pointcontour_point (const VD::cell_type &cell, Line &line)
 
const Pointcontour_point (const VD::cell_type &cell, const Lines &lines)
 
Pointcontour_point (const VD::cell_type &cell, Lines &lines)
 
Vec2d vertex_point (const VD::vertex_type &v)
 
Vec2d vertex_point (const VD::vertex_type *v)
 
VertexCategory vertex_category (const VD::vertex_type &v)
 
VertexCategory vertex_category (const VD::vertex_type *v)
 
void set_vertex_category (VD::vertex_type &v, VertexCategory c)
 
void set_vertex_category (VD::vertex_type *v, VertexCategory c)
 
EdgeCategory edge_category (const VD::edge_type &e)
 
EdgeCategory edge_category (const VD::edge_type *e)
 
void set_edge_category (VD::edge_type &e, EdgeCategory c)
 
void set_edge_category (VD::edge_type *e, EdgeCategory c)
 
CellCategory cell_category (const VD::cell_type &v)
 
CellCategory cell_category (const VD::cell_type *v)
 
void set_cell_category (const VD::cell_type &v, CellCategory c)
 
void set_cell_category (const VD::cell_type *v, CellCategory c)
 
static bool edge_offset_no_intersection (const Vec2d &intersection_point)
 
static bool edge_offset_has_intersection (const Vec2d &intersection_point)
 

Typedef Documentation

◆ VD

Enumeration Type Documentation

◆ CellCategory

enum class Slic3r::Voronoi::CellCategory : unsigned char
strong
Enumerator
Boundary 
Inside 
Outside 
Unknown 
61{
62 // This Voronoi cell is split by an input segment to two halves, one is inside, the other is outside.
64 // This Voronoi cell is completely inside.
65 Inside,
66 // This Voronoi cell is completely outside.
67 Outside,
68 // Not known yet.
70};

◆ EdgeCategory

enum class Slic3r::Voronoi::EdgeCategory : unsigned char
strong
Enumerator
PointsToContour 
PointsInside 
PointsOutside 
Unknown 
48{
49 // This half-edge points onto the contour, this VD::edge_type::vertex1().color() is OnContour.
51 // This half-edge points inside, this VD::edge_type::vertex1().color() is Inside.
53 // This half-edge points outside, this VD::edge_type::vertex1().color() is Outside.
55 // Not known yet.
57};

◆ VertexCategory

enum class Slic3r::Voronoi::VertexCategory : unsigned char
strong
Enumerator
OnContour 
Inside 
Outside 
Unknown 
31{
32 // Voronoi vertex is on the input contour.
33 // VD::vertex_type stores coordinates in double, though the coordinates shall match exactly
34 // with the coordinates of the input contour when converted to int32_t.
36 // Vertex is inside the CCW input contour, holes are respected.
37 Inside,
38 // Vertex is outside the CCW input contour, holes are respected.
39 Outside,
40 // Not known yet.
41 Unknown,
42};

Function Documentation

◆ annotate_inside_outside()

void Slic3r::Voronoi::annotate_inside_outside ( VD vd,
const Lines lines 
)
651{
652 assert(debug::verify_twin_halfedges_successive(vd, lines));
653
655
656#ifdef VORONOI_DEBUG_OUT
657 BoundingBox bbox;
658 {
659 bbox.merge(get_extents(lines));
660 bbox.min -= (0.01 * bbox.size().cast<double>()).cast<coord_t>();
661 bbox.max += (0.01 * bbox.size().cast<double>()).cast<coord_t>();
662 }
663 static int irun = 0;
664 ++ irun;
665 dump_voronoi_to_svg(debug_out_path("voronoi-offset-initial-%d.svg", irun).c_str(), vd, Points(), lines);
666#endif // VORONOI_DEBUG_OUT
667
668 // Set a VertexCategory, verify validity of the operation.
669 auto annotate_vertex = [](const VD::vertex_type *vertex, VertexCategory new_vertex_category) {
670#ifndef NDEBUG
671 VertexCategory vc = vertex_category(vertex);
672 assert(vc == VertexCategory::Unknown || vc == new_vertex_category);
673 assert(new_vertex_category == VertexCategory::Inside ||
674 new_vertex_category == VertexCategory::Outside ||
675 new_vertex_category == VertexCategory::OnContour);
676#endif // NDEBUG
677 set_vertex_category(const_cast<VD::vertex_type*>(vertex), new_vertex_category);
678 };
679
680 // Set an EdgeCategory, verify validity of the operation.
681 auto annotate_edge = [](const VD::edge_type *edge, EdgeCategory new_edge_category) {
682#ifndef NDEBUG
683 EdgeCategory ec = edge_category(edge);
684 assert(ec == EdgeCategory::Unknown || ec == new_edge_category);
685 switch (new_edge_category) {
686 case EdgeCategory::PointsInside:
687 assert(edge->vertex0() != nullptr);
688 assert(edge->vertex1() != nullptr);
689 break;
690 case EdgeCategory::PointsOutside:
691 // assert(edge->vertex0() != nullptr);
692 break;
693 case EdgeCategory::PointsToContour:
694 assert(edge->vertex1() != nullptr);
695 break;
696 default:
697 assert(false);
698 }
699#endif // NDEBUG
700 set_edge_category(const_cast<VD::edge_type*>(edge), new_edge_category);
701 };
702
703 // Set a CellCategory, verify validity of the operation.
704 // Handle marking of boundary cells (first time the cell is marked as outside, the other time as inside).
705 // Returns true if the current cell category was modified.
706 auto annotate_cell = [](const VD::cell_type *cell, CellCategory new_cell_category) -> bool {
707 CellCategory cc = cell_category(cell);
708 assert(cc == CellCategory::Inside || cc == CellCategory::Outside || cc == CellCategory::Boundary || cc == CellCategory::Unknown);
709 assert(new_cell_category == CellCategory::Inside || new_cell_category == CellCategory::Outside || new_cell_category == CellCategory::Boundary);
710 switch (cc) {
711 case CellCategory::Unknown:
712 // Old category unknown, just write the new category.
713 break;
714 case CellCategory::Outside:
715 if (new_cell_category == CellCategory::Inside)
716 new_cell_category = CellCategory::Boundary;
717 break;
718 case CellCategory::Inside:
719 if (new_cell_category == CellCategory::Outside)
720 new_cell_category = CellCategory::Boundary;
721 break;
722 case CellCategory::Boundary:
723 return false;
724 }
725 if (cc != new_cell_category) {
726 set_cell_category(const_cast<VD::cell_type*>(cell), new_cell_category);
727 return true;
728 }
729 return false;
730 };
731
732 // The next loop is supposed to annotate the "on input contour" vertices, but due to
733 // merging very close Voronoi vertices together by boost::polygon Voronoi generator
734 // the condition may not always be met. It should be safe to mark all Voronoi very close
735 // to the input contour as on contour.
736 for (const VD::edge_type &edge : vd.edges()) {
737 const VD::vertex_type *v = edge.vertex0();
738 if (v != nullptr) {
739 bool on_contour = detail::on_site(lines, *edge.cell(), vertex_point(v));
740#ifndef NDEBUG
741 bool on_contour2 = detail::on_site(lines, *edge.twin()->cell(), vertex_point(v));
742 assert(on_contour == on_contour2);
743#endif // NDEBUG
744 if (on_contour)
745 annotate_vertex(v, VertexCategory::OnContour);
746 }
747 }
748
749 // One side of a secondary edge is always on the source contour. Mark these vertices as OnContour.
750 // See the comment at the loop before, the condition may not always be met.
751 for (const VD::edge_type &edge : vd.edges()) {
752 if (edge.is_secondary() && edge.vertex0() != nullptr) {
753 assert(edge.is_linear());
754 assert(edge.cell()->contains_point() != edge.twin()->cell()->contains_point());
755 // The point associated with the point site shall be equal with one vertex of this Voronoi edge.
756 const Point &pt_on_contour = edge.cell()->contains_point() ? contour_point(*edge.cell(), lines) : contour_point(*edge.twin()->cell(), lines);
757 auto on_contour = [&pt_on_contour](const VD::vertex_type *v) { return detail::vertex_equal_to_point(v, pt_on_contour); };
758 if (edge.vertex1() == nullptr) {
759 assert(on_contour(edge.vertex0()));
760 annotate_vertex(edge.vertex0(), VertexCategory::OnContour);
761 } else {
762 // Only one of the two vertices may lie on input contour.
763 const VD::vertex_type *v0 = edge.vertex0();
764 const VD::vertex_type *v1 = edge.vertex1();
765#ifndef NDEBUG
766 VertexCategory v0_category = vertex_category(v0);
767 VertexCategory v1_category = vertex_category(v1);
768 assert(v0_category != VertexCategory::OnContour || v1_category != VertexCategory::OnContour);
769 assert(! (on_contour(v0) && on_contour(v1)));
770#endif // NDEBUG
771 if (on_contour(v0))
772 annotate_vertex(v0, VertexCategory::OnContour);
773 else {
774 assert(on_contour(v1));
775 annotate_vertex(v1, VertexCategory::OnContour);
776 }
777 }
778 }
779 }
780
781 assert(debug::verify_vertices_on_contour(vd, lines));
782
783 for (const VD::edge_type &edge : vd.edges())
784 if (edge.vertex1() == nullptr) {
785 // Infinite Voronoi edge separating two Point sites or a Point site and a Segment site.
786 // Infinite edge is always outside and it references at least one valid vertex.
787 assert(edge.is_infinite());
788 assert(edge.is_linear());
789 assert(edge.vertex0() != nullptr);
790 const VD::cell_type *cell = edge.cell();
791 const VD::cell_type *cell2 = edge.twin()->cell();
792 // A Point-Segment secondary Voronoi edge touches the input contour, a Point-Point Voronoi
793 // edge does not.
794 assert(edge.is_secondary() ? (cell->contains_segment() != cell2->contains_segment()) :
795 (cell->contains_point() == cell2->contains_point()));
796 annotate_edge(&edge, EdgeCategory::PointsOutside);
797 // Opposite edge of an infinite edge is certainly not active.
798 annotate_edge(edge.twin(), edge.is_secondary() ? EdgeCategory::PointsToContour : EdgeCategory::PointsOutside);
799 annotate_vertex(edge.vertex0(), edge.is_secondary() ? VertexCategory::OnContour : VertexCategory::Outside);
800 // edge.vertex1() is null, it is implicitely outside.
801 if (cell->contains_segment())
802 std::swap(cell, cell2);
803 // State of a cell containing a boundary point is certainly outside.
804 assert(cell->contains_point());
805 annotate_cell(cell, CellCategory::Outside);
806 assert(edge.is_secondary() == cell2->contains_segment());
807 annotate_cell(cell2, cell2->contains_point() ? CellCategory::Outside : CellCategory::Boundary);
808 } else if (edge.vertex0() != nullptr) {
809 assert(edge.is_finite());
810 const VD::cell_type *cell = edge.cell();
811 const Line *line = cell->contains_segment() ? &lines[cell->source_index()] : nullptr;
812 if (line == nullptr) {
813 cell = edge.twin()->cell();
814 line = cell->contains_segment() ? &lines[cell->source_index()] : nullptr;
815 }
816 // Only one of the two vertices may lie on input contour.
817 assert(! edge.is_linear() || vertex_category(edge.vertex0()) != VertexCategory::OnContour || vertex_category(edge.vertex1()) != VertexCategory::OnContour);
818 // Now classify the Voronoi vertices and edges as inside outside, if at least one Voronoi
819 // site is a Segment site.
820 // Inside / outside classification of Point - Point Voronoi edges will be done later
821 // by a propagation (seed fill).
822 if (line) {
823 const VD::vertex_type *v1 = edge.vertex1();
824 const VD::cell_type *cell2 = (cell == edge.cell()) ? edge.twin()->cell() : edge.cell();
825 assert(v1 != nullptr);
826 VertexCategory v0_category = vertex_category(edge.vertex0());
827 VertexCategory v1_category = vertex_category(edge.vertex1());
828 bool on_contour = v0_category == VertexCategory::OnContour || v1_category == VertexCategory::OnContour;
829#ifndef NDEBUG
830 if (! on_contour && cell == edge.cell() && edge.twin()->cell()->contains_segment()) {
831 // Constrained bisector of two segments. Vojtech is not quite sure whether the Voronoi generator is robust enough
832 // to always connect at least one secondary edge to an input contour point. Catch it here.
833 assert(edge.is_linear());
834 // OnContour state of this edge is not known yet.
835 const Point *pt_on_contour = nullptr;
836 // If the two segments share a point, then one end of the current Voronoi edge shares this point as well.
837 // A bisector may not necessarily connect to the source contour. Find pt_on_contour if it exists.
838 const Line &line2 = lines[cell2->source_index()];
839 if (line->a == line2.b)
840 pt_on_contour = &line->a;
841 else if (line->b == line2.a)
842 pt_on_contour = &line->b;
843 if (pt_on_contour) {
844 const VD::vertex_type *v0 = edge.vertex0();
845 auto on_contour = [&pt_on_contour](const VD::vertex_type *v) {
846 return std::abs(v->x() - pt_on_contour->x()) < 0.5001 &&
847 std::abs(v->y() - pt_on_contour->y()) < 0.5001;
848 };
849 assert(! on_contour(v0) && ! on_contour(v1));
850 }
851 }
852#endif // NDEBUG
853 if (on_contour && v1_category == VertexCategory::OnContour) {
854 // Skip secondary edge pointing to a contour point.
855 annotate_edge(&edge, EdgeCategory::PointsToContour);
856 } else {
857 // v0 is certainly not on the input polygons.
858 // Is v1 inside or outside the input polygons?
859 // The Voronoi vertex coordinate is in doubles, calculate orientation in doubles.
860 Vec2d l0(line->a.cast<double>());
861 Vec2d lv((line->b - line->a).cast<double>());
862 double side = cross2(Vec2d(v1->x(), v1->y()) - l0, lv);
863 // No Voronoi edge could connect two vertices of input polygons.
864 assert(side != 0.);
865 auto vc = side > 0. ? VertexCategory::Outside : VertexCategory::Inside;
866 annotate_vertex(v1, vc);
867 auto ec = vc == VertexCategory::Outside ? EdgeCategory::PointsOutside : EdgeCategory::PointsInside;
868 annotate_edge(&edge, ec);
869 // Annotate the twin edge and its vertex. As the Voronoi edge may never cross the input
870 // contour, the twin edge and its vertex will share the property of edge.
871 annotate_vertex(edge.vertex0(), on_contour ? VertexCategory::OnContour : vc);
872 annotate_edge(edge.twin(), on_contour ? EdgeCategory::PointsToContour : ec);
873 assert(cell->contains_segment());
874 annotate_cell(cell, on_contour ? CellCategory::Boundary :
876 annotate_cell(cell2, (on_contour && cell2->contains_segment()) ? CellCategory::Boundary :
878 }
879 }
880 }
881
882 assert(debug::verify_vertices_on_contour(vd, lines));
883
884 // Now most Voronoi vertices, edges and cells are annotated, with the exception of some
885 // edges separating two Point sites, their cells and vertices.
886 // Perform one round of expansion marking Voronoi edges and cells next to boundary cells.
887 std::vector<const VD::cell_type*> cell_queue;
888 for (const VD::edge_type &edge : vd.edges()) {
889 assert((edge_category(edge) == EdgeCategory::Unknown) == (edge_category(edge.twin()) == EdgeCategory::Unknown));
890 if (edge_category(edge) == EdgeCategory::Unknown) {
891 assert(edge.is_finite());
892 const VD::cell_type &cell = *edge.cell();
893 const VD::cell_type &cell2 = *edge.twin()->cell();
894 assert(cell.contains_point() && cell2.contains_point());
895 CellCategory cc = cell_category(cell);
896 CellCategory cc2 = cell_category(cell2);
897 assert(cc != CellCategory::Boundary && cc2 != CellCategory::Boundary);
898 CellCategory cc_new = cc;
899 if (cc_new == CellCategory::Unknown)
900 cc_new = cc2;
901 else
902 assert(cc2 == CellCategory::Unknown || cc == cc2);
903 if (cc_new == CellCategory::Unknown) {
904 VertexCategory vc = vertex_category(edge.vertex0());
905 assert(vc != VertexCategory::OnContour);
906 if (vc != VertexCategory::Unknown)
907 cc_new = (vc == VertexCategory::Outside) ? CellCategory::Outside : CellCategory::Inside;
908 }
909 if (cc_new != CellCategory::Unknown) {
910 VertexCategory vc = (cc_new == CellCategory::Outside) ? VertexCategory::Outside : VertexCategory::Inside;
911 annotate_vertex(edge.vertex0(), vc);
912 annotate_vertex(edge.vertex1(), vc);
913 auto ec_new = (cc_new == CellCategory::Outside) ? EdgeCategory::PointsOutside : EdgeCategory::PointsInside;
914 annotate_edge(&edge, ec_new);
915 annotate_edge(edge.twin(), ec_new);
916 if (cc != cc_new) {
917 annotate_cell(&cell, cc_new);
918 cell_queue.emplace_back(&cell);
919 }
920 if (cc2 != cc_new) {
921 annotate_cell(&cell2, cc_new);
922 cell_queue.emplace_back(&cell2);
923 }
924 }
925 }
926 }
927
928 assert(debug::verify_vertices_on_contour(vd, lines));
929
930 // Do a final seed fill over Voronoi cells and unmarked Voronoi edges.
931 while (! cell_queue.empty()) {
932 const VD::cell_type *cell = cell_queue.back();
933 const CellCategory cc = cell_category(cell);
934 assert(cc == CellCategory::Outside || cc == CellCategory::Inside);
935 cell_queue.pop_back();
936 const VD::edge_type *first_edge = cell->incident_edge();
937 const VD::edge_type *edge = first_edge;
938 const auto ec_new = (cc == CellCategory::Outside) ? EdgeCategory::PointsOutside : EdgeCategory::PointsInside;
939 do {
940 EdgeCategory ec = edge_category(edge);
941 if (ec == EdgeCategory::Unknown) {
942 assert(edge->cell()->contains_point() && edge->twin()->cell()->contains_point());
943 annotate_edge(edge, ec_new);
944 annotate_edge(edge->twin(), ec_new);
945 const VD::cell_type *cell2 = edge->twin()->cell();
946 CellCategory cc2 = cell_category(cell2);
947 assert(cc2 == CellCategory::Unknown || cc2 == cc);
948 if (cc2 != cc) {
949 annotate_cell(cell2, cc);
950 cell_queue.emplace_back(cell2);
951 }
952 } else {
953 assert(edge->vertex0() == nullptr || vertex_category(edge->vertex0()) != VertexCategory::Unknown);
954 assert(edge->vertex1() == nullptr || vertex_category(edge->vertex1()) != VertexCategory::Unknown);
955 assert(edge_category(edge->twin()) != EdgeCategory::Unknown);
956 assert(cell_category(edge->cell()) != CellCategory::Unknown);
957 assert(cell_category(edge->twin()->cell()) != CellCategory::Unknown);
958 }
959 edge = edge->next();
960 } while (edge != first_edge);
961 }
962
963 assert(debug::verify_vertices_on_contour(vd, lines));
964 assert(debug::verify_inside_outside_annotations(vd));
965}
PointType max
Definition BoundingBox.hpp:17
void merge(const PointType &point)
Definition BoundingBox.cpp:62
PointType size() const
Definition BoundingBox.cpp:144
PointType min
Definition BoundingBox.hpp:16
Definition BoundingBox.hpp:181
if(!(yy_init))
Definition lexer.c:1190
int32_t coord_t
Definition libslic3r.h:39
void set_edge_category(VD::edge_type &e, EdgeCategory c)
Definition VoronoiOffset.hpp:85
EdgeCategory
Definition VoronoiOffset.hpp:48
void set_cell_category(const VD::cell_type &v, CellCategory c)
Definition VoronoiOffset.hpp:94
CellCategory
Definition VoronoiOffset.hpp:61
CellCategory cell_category(const VD::cell_type &v)
Definition VoronoiOffset.hpp:90
VertexCategory vertex_category(const VD::vertex_type &v)
Definition VoronoiOffset.hpp:72
VertexCategory
Definition VoronoiOffset.hpp:31
void reset_inside_outside_annotations(VD &vd)
Definition VoronoiOffset.cpp:640
EdgeCategory edge_category(const VD::edge_type &e)
Definition VoronoiOffset.hpp:81
Vec2d vertex_point(const VD::vertex_type &v)
Definition VoronoiOffset.hpp:26
const Point & contour_point(const VD::cell_type &cell, const Line &line)
Definition VoronoiOffset.hpp:16
std::string debug_out_path(const char *name,...)
Definition utils.cpp:218
Derived::Scalar cross2(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:93
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:51
BoundingBox get_extents(const ExPolygon &expolygon)
Definition ExPolygon.cpp:352
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:58
IGL_INLINE void edges(const Eigen::MatrixBase< DerivedF > &F, Eigen::PlainObjectBase< DerivedE > &E)
Definition edges.cpp:13
Kernel::Point_2 Point
Definition point_areas.cpp:20

References Slic3r::Line::a, Slic3r::Line::b, Boundary, cell_category(), contour_point(), Slic3r::cross2(), Slic3r::debug_out_path(), Slic3r::dump_voronoi_to_svg(), edge_category(), Slic3r::get_extents(), Inside, Slic3r::BoundingBoxBase< PointType, APointsType >::max, Slic3r::BoundingBoxBase< PointType, APointsType >::merge(), Slic3r::BoundingBoxBase< PointType, APointsType >::min, Slic3r::Voronoi::detail::on_site(), OnContour, Outside, PointsInside, PointsOutside, PointsToContour, reset_inside_outside_annotations(), set_cell_category(), set_edge_category(), set_vertex_category(), Slic3r::BoundingBoxBase< PointType, APointsType >::size(), Unknown, Slic3r::Voronoi::debug::verify_inside_outside_annotations(), Slic3r::Voronoi::debug::verify_twin_halfedges_successive(), Slic3r::Voronoi::debug::verify_vertices_on_contour(), vertex_category(), Slic3r::Voronoi::detail::vertex_equal_to_point(), and vertex_point().

Referenced by Slic3r::Geometry::MedialAxis::build(), and offset().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cell_category() [1/2]

CellCategory Slic3r::Voronoi::cell_category ( const VD::cell_type &  v)
inline
91 { return static_cast<CellCategory>(v.color()); }

Referenced by annotate_inside_outside(), and Slic3r::Voronoi::debug::verify_inside_outside_annotations().

+ Here is the caller graph for this function:

◆ cell_category() [2/2]

CellCategory Slic3r::Voronoi::cell_category ( const VD::cell_type *  v)
inline
93 { return static_cast<CellCategory>(v->color()); }

◆ contour_point() [1/4]

const Point & Slic3r::Voronoi::contour_point ( const VD::cell_type &  cell,
const Line line 
)
inline
17 { return ((cell.source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) ? line.a : line.b); }
Point a
Definition Line.hpp:197

References Slic3r::Line::a, and Slic3r::Line::b.

Referenced by annotate_inside_outside(), contour_point(), contour_point(), edge_offset_contour_intersections(), Slic3r::Voronoi::detail::on_site(), signed_vertex_distances(), and skeleton_edges_rough().

+ Here is the caller graph for this function:

◆ contour_point() [2/4]

const Point & Slic3r::Voronoi::contour_point ( const VD::cell_type &  cell,
const Lines lines 
)
inline
22 { return contour_point(cell, lines[cell.source_index()]); }

References contour_point().

+ Here is the call graph for this function:

◆ contour_point() [3/4]

Point & Slic3r::Voronoi::contour_point ( const VD::cell_type &  cell,
Line line 
)
inline
19 { return ((cell.source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) ? line.a : line.b); }

References Slic3r::Line::a, and Slic3r::Line::b.

◆ contour_point() [4/4]

Point & Slic3r::Voronoi::contour_point ( const VD::cell_type &  cell,
Lines lines 
)
inline
24 { return contour_point(cell, lines[cell.source_index()]); }

References contour_point().

+ Here is the call graph for this function:

◆ edge_category() [1/2]

EdgeCategory Slic3r::Voronoi::edge_category ( const VD::edge_type &  e)
inline
82 { return static_cast<EdgeCategory>(e.color()); }

Referenced by annotate_inside_outside(), offset(), skeleton_edges_rough(), and Slic3r::Voronoi::debug::verify_inside_outside_annotations().

+ Here is the caller graph for this function:

◆ edge_category() [2/2]

EdgeCategory Slic3r::Voronoi::edge_category ( const VD::edge_type *  e)
inline
84 { return static_cast<EdgeCategory>(e->color()); }

◆ edge_offset_contour_intersections()

std::vector< Vec2d > Slic3r::Voronoi::edge_offset_contour_intersections ( const VD vd,
const Lines lines,
const std::vector< double > &  vertex_distances,
double  offset_distance 
)
1015{
1016 // vd shall be annotated.
1017 assert(debug::verify_inside_outside_annotations(vd));
1018
1019 bool outside = offset_distance > 0;
1020 if (! outside)
1021 offset_distance = - offset_distance;
1022 assert(offset_distance > 0.);
1023
1024 const VD::vertex_type *first_vertex = &vd.vertices().front();
1025 const VD::edge_type *first_edge = &vd.edges().front();
1026 static constexpr double nan = std::numeric_limits<double>::quiet_NaN();
1027 // By default none edge has an intersection with the offset curve.
1028 std::vector<Vec2d> out(vd.num_edges(), Vec2d(nan, 0.));
1029
1030 for (const VD::edge_type &edge : vd.edges()) {
1031 size_t edge_idx = &edge - first_edge;
1032 if (edge_offset_has_intersection(out[edge_idx]) || out[edge_idx].y() != 0.)
1033 // This edge was already classified.
1034 continue;
1035
1036 const VD::vertex_type *v0 = edge.vertex0();
1037 const VD::vertex_type *v1 = edge.vertex1();
1038 if (v0 == nullptr) {
1039 assert(vertex_category(v1) == VertexCategory::OnContour || vertex_category(v1) == VertexCategory::Outside);
1040 continue;
1041 }
1042
1043 double d0 = (v0 == nullptr) ? std::numeric_limits<double>::max() : vertex_distances[v0 - first_vertex];
1044 double d1 = (v1 == nullptr) ? std::numeric_limits<double>::max() : vertex_distances[v1 - first_vertex];
1045 assert(d0 * d1 >= 0.);
1046 if (! outside) {
1047 d0 = - d0;
1048 d1 = - d1;
1049 }
1050#ifndef NDEBUG
1051 {
1052 double err = std::abs(detail::dist_to_site(lines, *edge.cell(), vertex_point(v0)) - std::abs(d0));
1053 double err2 = std::abs(detail::dist_to_site(lines, *edge.twin()->cell(), vertex_point(v0)) - std::abs(d0));
1054 assert(err < SCALED_EPSILON);
1055 assert(err2 < SCALED_EPSILON);
1056 if (v1 != nullptr) {
1057 double err3 = std::abs(detail::dist_to_site(lines, *edge.cell(), vertex_point(v1)) - std::abs(d1));
1058 double err4 = std::abs(detail::dist_to_site(lines, *edge.twin()->cell(), vertex_point(v1)) - std::abs(d1));
1059 assert(err3 < SCALED_EPSILON);
1060 assert(err4 < SCALED_EPSILON);
1061 }
1062 }
1063#endif // NDEBUG
1064 double dmin, dmax;
1065 if (d0 < d1)
1066 dmin = d0, dmax = d1;
1067 else
1068 dmax = d0, dmin = d1;
1069 // Offset distance may be lower than dmin, but never higher than dmax.
1070 // Don't intersect an edge at dmax
1071 // 1) To avoid zero edge length, zero area offset contours.
1072 // 2) To ensure that the offset contours that cross a Voronoi vertex are traced consistently
1073 // at one side of the offset curve only.
1074 if (offset_distance >= dmax)
1075 continue;
1076
1077 // Edge candidate, intersection points were not calculated yet.
1078 assert(v0 != nullptr);
1079 const VD::cell_type *cell = edge.cell();
1080 const VD::cell_type *cell2 = edge.twin()->cell();
1081 const Line &line0 = lines[cell->source_index()];
1082 const Line &line1 = lines[cell2->source_index()];
1083 size_t edge_idx2 = edge.twin() - first_edge;
1084 if (v1 == nullptr) {
1085 assert(edge.is_infinite());
1086 assert(edge.is_linear());
1087 // Unconstrained edges have always montonous distance.
1088 assert(d0 != d1);
1089 if (offset_distance > dmin) {
1090 // There is certainly an intersection with the offset curve.
1091 if (cell->contains_point() && cell2->contains_point()) {
1092 assert(! edge.is_secondary());
1093 const Point &pt0 = contour_point(*cell, line0);
1094 const Point &pt1 = contour_point(*cell2, line1);
1095 // pt is inside the circle (pt0, offset_distance), (pt + dir) is certainly outside the same circle.
1096 Vec2d dir = Vec2d(double(pt0.y() - pt1.y()), double(pt1.x() - pt0.x())) * (2. * offset_distance);
1097 Vec2d pt(v0->x(), v0->y());
1098 double t = detail::first_circle_segment_intersection_parameter(Vec2d(pt0.x(), pt0.y()), offset_distance, pt, dir);
1099 assert(t > 0.);
1100 out[edge_idx] = pt + t * dir;
1101 } else {
1102 // Infinite edges could not be created by two segment sites.
1103 assert(cell->contains_point() != cell2->contains_point());
1104 // Linear edge goes through the endpoint of a segment.
1105 assert(edge.is_secondary());
1106 const Point &ipt = cell->contains_segment() ? contour_point(*cell2, line1) : contour_point(*cell, line0);
1107 #ifndef NDEBUG
1108 if (cell->contains_segment()) {
1109 const Point &pt1 = contour_point(*cell2, line1);
1110 assert(pt1 == line0.a || pt1 == line0.b);
1111 } else {
1112 const Point &pt0 = contour_point(*cell, line0);
1113 assert(pt0 == line1.a || pt0 == line1.b);
1114 }
1115 assert((vertex_point(v0) - ipt.cast<double>()).norm() < SCALED_EPSILON);
1116 #endif /* NDEBUG */
1117 // Infinite edge starts at an input contour, therefore there is always an intersection with an offset curve.
1118 const Line &line = cell->contains_segment() ? line0 : line1;
1119 assert(line.a == ipt || line.b == ipt);
1120 out[edge_idx] = ipt.cast<double>() + offset_distance * Vec2d(line.b.y() - line.a.y(), line.a.x() - line.b.x()).normalized();
1121 }
1122 } else if (offset_distance == dmin)
1123 out[edge_idx] = vertex_point(v0);
1124 // The other edge of an unconstrained edge starting with null vertex shall never be intersected. Mark it as visited.
1125 out[edge_idx2].y() = 1.;
1126 } else {
1127 assert(edge.is_finite());
1128 bool done = false;
1129 // Bisector of two line segments, distance along the bisector is linear.
1130 bool bisector = cell->contains_segment() && cell2->contains_segment();
1131 assert(edge.is_finite());
1132 // or a secondary line, again the distance along the secondary line is linear and starts at the contour (zero distance).
1133 if (bisector || edge.is_secondary()) {
1134 assert(edge.is_linear());
1135#ifndef NDEBUG
1136 if (edge.is_secondary()) {
1137 assert(cell->contains_point() != cell2->contains_point());
1138 // One of the vertices is on the input contour.
1139 assert((vertex_category(edge.vertex0()) == VertexCategory::OnContour) !=
1140 (vertex_category(edge.vertex1()) == VertexCategory::OnContour));
1141 assert(dmin == 0.);
1142 }
1143#endif // NDEBUG
1144 if (! bisector || (dmin != dmax && offset_distance >= dmin)) {
1145 double t = (offset_distance - dmin) / (dmax - dmin);
1146 t = std::clamp(t, 0., 1.);
1147 if (d1 < d0) {
1148 out[edge_idx2] = Slic3r::lerp(vertex_point(v1), vertex_point(v0), t);
1149 // mark visited
1150 out[edge_idx].y() = 1.;
1151 } else {
1152 out[edge_idx] = Slic3r::lerp(vertex_point(v0), vertex_point(v1), t);
1153 // mark visited
1154 out[edge_idx2].y() = 1.;
1155 }
1156 done = true;
1157 }
1158 } else {
1159 // Point - Segment or Point - Point edge, distance along this Voronoi edge may not be monotonous:
1160 // The distance function along the Voronoi edge may either have one maximum at one vertex and one minimum at the other vertex,
1161 // or it may have two maxima at the vertices and a minimum somewhere along the Voronoi edge, and this Voronoi edge
1162 // may be intersected twice by an offset curve.
1163 //
1164 // Tracing an offset curve accross Voronoi regions with linear edges of montonously increasing or decrasing distance
1165 // to a Voronoi region is stable in a sense, that if the distance of Voronoi vertices is calculated correctly, there will
1166 // be maximum one intersection of an offset curve found at each Voronoi edge and tracing these intersections shall
1167 // produce a set of closed curves.
1168 //
1169 // Having a non-monotonous distance function between the Voronoi edge end points may lead to splitting of offset curves
1170 // at these Voronoi edges. If a Voronoi edge is classified as having no intersection at all while it has some,
1171 // the extracted offset curve will contain self intersections at this Voronoi edge.
1172 //
1173 // If on the other side the two intersection points are found by a numerical error even though none should be found, then
1174 // it may happen that it would not be possible to connect these two points into a closed loop, which is likely worse
1175 // than the issue above.
1176 //
1177 // While it is likely not possible to avoid all the numerical issues, one shall strive for the highest numerical robustness.
1178 assert(cell->contains_point() || cell2->contains_point());
1179 size_t num_intersections = 0;
1180 bool point_vs_segment = cell->contains_point() != cell2->contains_point();
1181 const Point &pt0 = cell->contains_point() ? contour_point(*cell, line0) : contour_point(*cell2, line1);
1182 // Project p0 to line segment <v0, v1>.
1183 Vec2d p0(v0->x(), v0->y());
1184 Vec2d p1(v1->x(), v1->y());
1185 Vec2d px(pt0.x(), pt0.y());
1186 const Point *pt1 = nullptr;
1187 Vec2d dir;
1188 if (point_vs_segment) {
1189 const Line &line = cell->contains_segment() ? line0 : line1;
1190 dir = (line.b - line.a).cast<double>();
1191 } else {
1192 pt1 = &contour_point(*cell2, line1);
1193 // Perpendicular to the (pt1 - pt0) direction.
1194 dir = Vec2d(double(pt0.y() - pt1->y()), double(pt1->x() - pt0.x()));
1195 }
1196 double s0 = (p0 - px).dot(dir);
1197 double s1 = (p1 - px).dot(dir);
1198 if (offset_distance >= dmin) {
1199 // This Voronoi edge is intersected by the offset curve just once.
1200 // There may be numerical issues if dmin is close to the minimum of the non-monotonous distance function.
1201 num_intersections = 1;
1202 } else {
1203 // This Voronoi edge may not be intersected by the offset curve, or it may split the offset curve
1204 // into two loops. First classify this edge robustly with regard to the Point-Segment bisector or Point-Point bisector.
1205 double dmin_new;
1206 bool found = false;
1207 if (point_vs_segment) {
1208 if (s0 * s1 <= 0.) {
1209 // One end of the Voronoi edge is on one side of the Point-Segment bisector, the other end of the Voronoi
1210 // edge is on the other side of the bisector, therefore with a high probability we should find a minimum
1211 // of the distance to a nearest site somewhere inside this Voronoi edge (at the intersection of the bisector
1212 // and the Voronoi edge.
1213 const Line &line = cell->contains_segment() ? line0 : line1;
1214 dmin_new = 0.5 * (Geometry::foot_pt<Vec2d>(line.a.cast<double>(), dir, px) - px).norm();
1215 found = true;
1216 }
1217 } else {
1218 // Point-Point Voronoi sites.
1219 if (s0 * s1 <= 0.) {
1220 // This Voronoi edge intersects connection line of the two Point sites.
1221 dmin_new = 0.5 * (pt1->cast<double>() - px).norm();
1222 found = true;
1223 }
1224 }
1225 if (found) {
1226 assert(dmin_new < dmax + SCALED_EPSILON);
1227 assert(dmin_new < dmin + SCALED_EPSILON);
1228 if (dmin_new <= offset_distance) {
1229 // 1) offset_distance > dmin_new -> two new distinct intersection points are found.
1230 // 2) offset_distance == dmin_new -> one duplicate point is found.
1231 // If 2) is ignored, then two tangentially touching offset curves are created.
1232 // If not ignored, then the two offset curves merge at this double point.
1233 // We should merge the contours while pushing the the two copies of the tangent point away a bit.
1234 dmin = dmin_new;
1235 num_intersections = (offset_distance > dmin) + 1;
1236 }
1237 }
1238 }
1239 if (num_intersections > 0) {
1240 detail::Intersections intersections;
1241 if (point_vs_segment) {
1242 assert(cell->contains_point() || cell2->contains_point());
1243 intersections = detail::line_point_equal_distance_points(cell->contains_segment() ? line0 : line1, pt0, offset_distance);
1244 } else {
1245 intersections = detail::point_point_equal_distance_points(pt0, *pt1, offset_distance);
1246 }
1247 // The functions above perform complex calculations in doubles, therefore the results may not be quite accurate and
1248 // the number of intersections found may not be in accord to the number of intersections expected from evaluating simpler expressions.
1249 // Adjust the result to the number of intersection points expected.
1250 if (num_intersections == 2) {
1251 switch (intersections.count) {
1252 case 0:
1253 // No intersection found even though one or two were expected to be found.
1254 // Not trying to find the intersection means that we may produce offset curves, that intersect at this Voronoi edge.
1255 //FIXME We are fine with that for now, but we may try to create artificial split points in further revisions.
1256 break;
1257 case 1:
1258 // Tangential point found.
1259 //FIXME We are fine with that for now, but we may try to create artificial split points in further revisions.
1260 break;
1261 default:
1262 {
1263 // Two intersection points found. Sort them.
1264 assert(intersections.count == 2);
1265 double q0 = (intersections.pts[0] - px).dot(dir);
1266 double q1 = (intersections.pts[1] - px).dot(dir);
1267 // Both Voronoi edge end points and offset contour intersection points should be separated by the bisector.
1268 assert(q0 * q1 <= 0.);
1269 assert(s0 * s1 <= 0.);
1270 // Sort the intersection points by dir.
1271 if ((q0 < q1) != (s0 < s1))
1272 std::swap(intersections.pts[0], intersections.pts[1]);
1273 }
1274 }
1275 } else {
1276 assert(num_intersections == 1);
1277 switch (intersections.count) {
1278 case 0:
1279 // No intersection found. This should not happen.
1280 // Create one artificial intersection point by repeating the dmin point, which is supposed to be
1281 // close to the minimum.
1282 intersections.pts[0] = (dmin == d0) ? p0 : p1;
1283 intersections.count = 1;
1284 break;
1285 case 1:
1286 // One intersection found. This is a tangential point. Use it.
1287 break;
1288 default:
1289 // Two intersections found.
1290 // Now decide which of the point fall on this Voronoi edge.
1291 assert(intersections.count == 2);
1292 double q0 = (intersections.pts[0] - px).dot(dir);
1293 double q1 = (intersections.pts[1] - px).dot(dir);
1294 // Offset contour intersection points should be separated by the bisector.
1295 assert(q0 * q1 <= 0);
1296 double s = (dmax == d0) ? s0 : s1;
1297 bool take_2nd = (s > 0.) ? q1 > q0 : q1 < q0;
1298 if (take_2nd)
1299 intersections.pts[0] = intersections.pts[1];
1300 -- intersections.count;
1301 }
1302 }
1303 assert(intersections.count > 0);
1304 if (intersections.count == 2) {
1305 out[edge_idx] = intersections.pts[1];
1306 out[edge_idx2] = intersections.pts[0];
1307 done = true;
1308 } else if (intersections.count == 1) {
1309 if (d1 < d0)
1310 std::swap(edge_idx, edge_idx2);
1311 out[edge_idx] = intersections.pts[0];
1312 out[edge_idx2].y() = 1.;
1313 done = true;
1314 }
1315 }
1316 }
1317 if (! done)
1318 out[edge_idx].y() = out[edge_idx2].y() = 1.;
1319 }
1320 }
1321
1322 assert(debug::verify_offset_intersection_points(vd, lines, offset_distance, out));
1323
1324 return out;
1325}
static volatile int done
Definition bitbang.c:50
double BLASFUNC() dmax(int *, double *, int *)
double BLASFUNC() dmin(int *, double *, int *)
#define SCALED_EPSILON
Definition libslic3r.h:71
ColorRGB lerp(const ColorRGB &a, const ColorRGB &b, float t)
Definition Color.cpp:228
IGL_INLINE double dot(const double *a, const double *b)
Definition dot.cpp:11

References Slic3r::Line::a, Slic3r::Line::b, contour_point(), Slic3r::Voronoi::detail::Intersections::count, Slic3r::Voronoi::detail::dist_to_site(), dmax(), dmin(), done, Slic3r::dot(), edge_offset_has_intersection(), Slic3r::Voronoi::detail::first_circle_segment_intersection_parameter(), Slic3r::lerp(), Slic3r::Voronoi::detail::line_point_equal_distance_points(), OnContour, Outside, Slic3r::Voronoi::detail::point_point_equal_distance_points(), Slic3r::Voronoi::detail::Intersections::pts, SCALED_EPSILON, Slic3r::Voronoi::debug::verify_inside_outside_annotations(), Slic3r::Voronoi::debug::verify_offset_intersection_points(), vertex_category(), and vertex_point().

Referenced by offset().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ edge_offset_has_intersection()

static bool Slic3r::Voronoi::edge_offset_has_intersection ( const Vec2d intersection_point)
inlinestatic
113 { return ! edge_offset_no_intersection(intersection_point); }
static bool edge_offset_no_intersection(const Vec2d &intersection_point)
Definition VoronoiOffset.hpp:110

References edge_offset_no_intersection().

Referenced by edge_offset_contour_intersections(), offset(), and Slic3r::Voronoi::debug::verify_offset_intersection_points().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ edge_offset_no_intersection()

static bool Slic3r::Voronoi::edge_offset_no_intersection ( const Vec2d intersection_point)
inlinestatic
111 { return std::isnan(intersection_point.x()); }

Referenced by edge_offset_has_intersection().

+ Here is the caller graph for this function:

◆ offset() [1/2]

Polygons Slic3r::Voronoi::offset ( const Geometry::VoronoiDiagram vd,
const Lines lines,
const std::vector< double > &  signed_vertex_distances,
double  offset_distance,
double  discretization_error 
)
1333{
1334#ifdef VORONOI_DEBUG_OUT
1335 BoundingBox bbox;
1336 {
1337 bbox.merge(get_extents(lines));
1338 bbox.min -= (0.01 * bbox.size().cast<double>()).cast<coord_t>();
1339 bbox.max += (0.01 * bbox.size().cast<double>()).cast<coord_t>();
1340 }
1341 static int irun = 0;
1342 ++ irun;
1343 {
1344 Lines helper_lines;
1345 for (const VD::edge_type &edge : vd.edges())
1346 if (edge_category(edge) == (offset_distance > 0 ? EdgeCategory::PointsOutside : EdgeCategory::PointsInside) &&
1347 edge.vertex0() != nullptr) {
1348 const VD::vertex_type *v0 = edge.vertex0();
1349 const VD::vertex_type *v1 = edge.vertex1();
1350 Vec2d pt1(v0->x(), v0->y());
1351 Vec2d pt2;
1352 if (v1 == nullptr) {
1353 // Unconstrained edge. Calculate a trimmed position.
1354 assert(edge.is_linear());
1355 const VD::cell_type *cell = edge.cell();
1356 const VD::cell_type *cell2 = edge.twin()->cell();
1357 const Line &line0 = lines[cell->source_index()];
1358 const Line &line1 = lines[cell2->source_index()];
1359 if (cell->contains_point() && cell2->contains_point()) {
1360 const Point &pt0 = (cell->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) ? line0.a : line0.b;
1361 const Point &pt1 = (cell2->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) ? line1.a : line1.b;
1362 // Direction vector of this unconstrained Voronoi edge.
1363 Vec2d dir(double(pt0.y() - pt1.y()), double(pt1.x() - pt0.x()));
1364 pt2 = Vec2d(v0->x(), v0->y()) + dir.normalized() * scale_(10.);
1365 } else {
1366 // Infinite edges could not be created by two segment sites.
1367 assert(cell->contains_point() != cell2->contains_point());
1368 // Linear edge goes through the endpoint of a segment.
1369 assert(edge.is_secondary());
1370 const Point &ipt = cell->contains_segment() ?
1371 ((cell2->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) ? line1.a : line1.b) :
1372 ((cell->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) ? line0.a : line0.b);
1373 // Infinite edge starts at an input contour, therefore there is always an intersection with an offset curve.
1374 const Line &line = cell->contains_segment() ? line0 : line1;
1375 assert(line.a == ipt || line.b == ipt);
1376 // dir is perpendicular to line.
1377 Vec2d dir(line.a.y() - line.b.y(), line.b.x() - line.a.x());
1378 assert(dir.norm() > 0.);
1379 if (((line.a == ipt) == cell->contains_point()) == (v0 == nullptr))
1380 dir = - dir;
1381 pt2 = ipt.cast<double>() + dir.normalized() * scale_(10.);
1382 }
1383 } else {
1384 pt2 = Vec2d(v1->x(), v1->y());
1385 // Clip the line by the bounding box, so that the coloring of the line will be visible.
1386 Geometry::liang_barsky_line_clipping(pt1, pt2, BoundingBoxf(bbox.min.cast<double>(), bbox.max.cast<double>()));
1387 }
1388 helper_lines.emplace_back(Line(Point(pt1.cast<coord_t>()), Point(((pt1 + pt2) * 0.5).cast<coord_t>())));
1389 }
1390 dump_voronoi_to_svg(debug_out_path("voronoi-offset-candidates1-%d.svg", irun).c_str(), vd, Points(), lines, Polygons(), helper_lines);
1391 }
1392#endif // VORONOI_DEBUG_OUT
1393
1394 std::vector<Vec2d> edge_points = edge_offset_contour_intersections(vd, lines, signed_vertex_distances, offset_distance);
1395 const VD::edge_type *front_edge = &vd.edges().front();
1396
1397#ifdef VORONOI_DEBUG_OUT
1398 Lines helper_lines;
1399 {
1400 for (const VD::edge_type &edge : vd.edges())
1401 if (edge_offset_has_intersection(edge_points[&edge - front_edge]))
1402 helper_lines.emplace_back(Line(Point(edge.vertex0()->x(), edge.vertex0()->y()), Point(edge_points[&edge - front_edge].cast<coord_t>())));
1403 dump_voronoi_to_svg(debug_out_path("voronoi-offset-candidates2-%d.svg", irun).c_str(), vd, Points(), lines, Polygons(), helper_lines);
1404 }
1405#endif // VORONOI_DEBUG_OUT
1406
1407 auto next_offset_edge = [&edge_points, front_edge](const VD::edge_type *start_edge) -> const VD::edge_type* {
1408 for (const VD::edge_type *edge = start_edge->next(); edge != start_edge; edge = edge->next())
1409 if (edge_offset_has_intersection(edge_points[edge->twin() - front_edge]))
1410 return edge->twin();
1411 // assert(false);
1412 return nullptr;
1413 };
1414
1415 const bool inside_offset = offset_distance < 0.;
1416 if (inside_offset)
1417 offset_distance = - offset_distance;
1418
1419 // Track the offset curves.
1420 Polygons out;
1421 double angle_step = 2. * acos((offset_distance - discretization_error) / offset_distance);
1422 double cos_threshold = cos(angle_step);
1423 static constexpr double nan = std::numeric_limits<double>::quiet_NaN();
1424 for (size_t seed_edge_idx = 0; seed_edge_idx < vd.num_edges(); ++ seed_edge_idx) {
1425 Vec2d last_pt = edge_points[seed_edge_idx];
1426 if (edge_offset_has_intersection(last_pt)) {
1427 const VD::edge_type *start_edge = &vd.edges()[seed_edge_idx];
1428 const VD::edge_type *edge = start_edge;
1429 Polygon poly;
1430 do {
1431 // find the next edge
1432 const VD::edge_type *next_edge = next_offset_edge(edge);
1433#ifdef VORONOI_DEBUG_OUT
1434 if (next_edge == nullptr) {
1435 Lines hl = helper_lines;
1436 append(hl, to_lines(Polyline(poly.points)));
1437 dump_voronoi_to_svg(debug_out_path("voronoi-offset-open-loop-%d.svg", irun).c_str(), vd, Points(), lines, Polygons(), hl);
1438 }
1439#endif // VORONOI_DEBUG_OUT
1440 assert(next_edge);
1441 //std::cout << "offset-output: "; print_edge(edge); std::cout << " to "; print_edge(next_edge); std::cout << "\n";
1442 // Interpolate a circular segment or insert a linear segment between edge and next_edge.
1443 const VD::cell_type *cell = edge->cell();
1444 // Mark the edge / offset curve intersection point as consumed.
1445 Vec2d p1 = last_pt;
1446 Vec2d p2 = edge_points[next_edge - front_edge];
1447 edge_points[next_edge - front_edge].x() = nan;
1448#ifndef NDEBUG
1449 {
1450 double err = detail::dist_to_site(lines, *cell, p1) - offset_distance;
1451 double err2 = detail::dist_to_site(lines, *cell, p2) - offset_distance;
1452#ifdef VORONOI_DEBUG_OUT
1453 if (std::max(err, err2) >= SCALED_EPSILON) {
1454 Lines helper_lines;
1455 dump_voronoi_to_svg(debug_out_path("voronoi-offset-incorrect_pt-%d.svg", irun).c_str(), vd, Points(), lines, Polygons(), to_lines(poly));
1456 }
1457#endif // VORONOI_DEBUG_OUT
1458 assert(std::abs(err) < SCALED_EPSILON);
1459 assert(std::abs(err2) < SCALED_EPSILON);
1460 }
1461#endif /* NDEBUG */
1462 if (cell->contains_point()) {
1463 // Discretize an arc from p1 to p2 with radius = offset_distance and discretization_error.
1464 // The extracted contour is CCW oriented, extracted holes are CW oriented.
1465 // The extracted arc will have the same orientation. As the Voronoi regions are convex, the angle covered by the arc will be convex as well.
1466 const Line &line0 = lines[cell->source_index()];
1467 const Vec2d &center = ((cell->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) ? line0.a : line0.b).cast<double>();
1468 const Vec2d v1 = p1 - center;
1469 const Vec2d v2 = p2 - center;
1470 bool ccw = cross2(v1, v2) > 0;
1471 double cos_a = v1.dot(v2);
1472 double norm = v1.norm() * v2.norm();
1473 assert(norm > 0.);
1474 if (cos_a < cos_threshold * norm) {
1475 // Angle is bigger than the threshold, therefore the arc will be discretized.
1476 cos_a /= norm;
1477 assert(cos_a > -1. - EPSILON && cos_a < 1. + EPSILON);
1478 double angle = acos(std::max(-1., std::min(1., cos_a)));
1479 size_t n_steps = size_t(ceil(angle / angle_step));
1480 double astep = angle / n_steps;
1481 if (! ccw)
1482 astep *= -1.;
1483 double a = astep;
1484 for (size_t i = 1; i < n_steps; ++ i, a += astep) {
1485 double c = cos(a);
1486 double s = sin(a);
1487 Vec2d p = center + Vec2d(c * v1.x() - s * v1.y(), s * v1.x() + c * v1.y());
1488 poly.points.emplace_back(Point(coord_t(p.x()), coord_t(p.y())));
1489 }
1490 }
1491 }
1492 {
1493 Point pt_last(coord_t(p2.x()), coord_t(p2.y()));
1494 if (poly.empty() || poly.points.back() != pt_last)
1495 poly.points.emplace_back(pt_last);
1496 }
1497 edge = next_edge;
1498 last_pt = p2;
1499 } while (edge != start_edge);
1500
1501 while (! poly.empty() && poly.points.front() == poly.points.back())
1502 poly.points.pop_back();
1503 if (poly.size() >= 3)
1504 out.emplace_back(std::move(poly));
1505 }
1506 }
1507
1508 return out;
1509}
EIGEN_DEVICE_FUNC const AcosReturnType acos() const
Definition ArrayCwiseUnaryOps.h:262
EIGEN_DEVICE_FUNC const CeilReturnType ceil() const
Definition ArrayCwiseUnaryOps.h:402
EIGEN_DEVICE_FUNC const CosReturnType cos() const
Definition ArrayCwiseUnaryOps.h:202
EIGEN_DEVICE_FUNC const SinReturnType sin() const
Definition ArrayCwiseUnaryOps.h:220
EIGEN_DEVICE_FUNC CastXpr< NewType >::Type cast() const
Definition CommonCwiseUnaryOps.h:62
Definition Line.hpp:155
Definition Point.hpp:158
#define scale_(val)
Definition libslic3r.h:69
static constexpr double EPSILON
Definition libslic3r.h:51
const Scalar & y
Definition MathFunctions.h:552
std::vector< Vec2d > edge_offset_contour_intersections(const VD &vd, const Lines &lines, const std::vector< double > &vertex_distances, double offset_distance)
Definition VoronoiOffset.cpp:1010
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:15
std::vector< Line > Lines
Definition Line.hpp:17
Lines to_lines(const ExPolygon &src)
Definition ExPolygon.hpp:117
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:112
Definition args.hpp:18
TCoord< P > x(const P &p)
Definition geometry_traits.hpp:297
void append(SurfaceCut &sc, SurfaceCut &&sc_add)
Merge two surface cuts together Added surface cut will be consumed.
Definition CutSurface.cpp:3550
Slic3r::Polygon Polygon
Definition Emboss.cpp:34

References Slic3r::Line::a, acos(), Slic3r::angle(), Slic3r::append(), Slic3r::Line::b, ceil(), cos(), Slic3r::cross2(), Slic3r::debug_out_path(), Slic3r::Voronoi::detail::dist_to_site(), Slic3r::dump_voronoi_to_svg(), edge_category(), edge_offset_contour_intersections(), edge_offset_has_intersection(), Slic3r::MultiPoint::empty(), EPSILON, Slic3r::get_extents(), Slic3r::Geometry::liang_barsky_line_clipping(), Slic3r::BoundingBoxBase< PointType, APointsType >::max, Slic3r::BoundingBoxBase< PointType, APointsType >::merge(), Slic3r::BoundingBoxBase< PointType, APointsType >::min, Slic3r::MultiPoint::points, PointsInside, PointsOutside, scale_, SCALED_EPSILON, signed_vertex_distances(), sin(), Slic3r::BoundingBoxBase< PointType, APointsType >::size(), Slic3r::MultiPoint::size(), and Slic3r::to_lines().

Referenced by offset().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ offset() [2/2]

Polygons Slic3r::Voronoi::offset ( const VD vd,
const Lines lines,
double  offset_distance,
double  discretization_error 
)
1516{
1517 annotate_inside_outside(const_cast<VD&>(vd), lines);
1518 std::vector<double> dist = signed_vertex_distances(vd, lines);
1519 return offset(vd, lines, dist, offset_distance, discretization_error);
1520}
Definition Voronoi.hpp:23
std::vector< double > signed_vertex_distances(const VD &vd, const Lines &lines)
Definition VoronoiOffset.cpp:967
void annotate_inside_outside(VD &vd, const Lines &lines)
Definition VoronoiOffset.cpp:650

References annotate_inside_outside(), offset(), and signed_vertex_distances().

+ Here is the call graph for this function:

◆ reset_inside_outside_annotations()

void Slic3r::Voronoi::reset_inside_outside_annotations ( VD vd)
641{
642 for (const VD::vertex_type &v : vd.vertices())
643 set_vertex_category(const_cast<VD::vertex_type&>(v), VertexCategory::Unknown);
644 for (const VD::edge_type &e : vd.edges())
645 set_edge_category(const_cast<VD::edge_type&>(e), EdgeCategory::Unknown);
646 for (const VD::cell_type &c : vd.cells())
647 set_cell_category(const_cast<VD::cell_type&>(c), CellCategory::Unknown);
648}
void set_vertex_category(VD::vertex_type &v, VertexCategory c)
Definition VoronoiOffset.hpp:76

References set_cell_category(), set_edge_category(), set_vertex_category(), and Unknown.

Referenced by annotate_inside_outside().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ set_cell_category() [1/2]

void Slic3r::Voronoi::set_cell_category ( const VD::cell_type &  v,
CellCategory  c 
)
inline
95 { v.color(static_cast<VD::cell_type::color_type>(c)); }

Referenced by annotate_inside_outside(), and reset_inside_outside_annotations().

+ Here is the caller graph for this function:

◆ set_cell_category() [2/2]

void Slic3r::Voronoi::set_cell_category ( const VD::cell_type *  v,
CellCategory  c 
)
inline
97 { v->color(static_cast<VD::cell_type::color_type>(c)); }

◆ set_edge_category() [1/2]

void Slic3r::Voronoi::set_edge_category ( VD::edge_type &  e,
EdgeCategory  c 
)
inline
86 { e.color(static_cast<VD::edge_type::color_type>(c)); }

Referenced by annotate_inside_outside(), and reset_inside_outside_annotations().

+ Here is the caller graph for this function:

◆ set_edge_category() [2/2]

void Slic3r::Voronoi::set_edge_category ( VD::edge_type *  e,
EdgeCategory  c 
)
inline
88 { e->color(static_cast<VD::edge_type::color_type>(c)); }

◆ set_vertex_category() [1/2]

void Slic3r::Voronoi::set_vertex_category ( VD::vertex_type &  v,
VertexCategory  c 
)
inline
77 { v.color(static_cast<VD::vertex_type::color_type>(c)); }

Referenced by annotate_inside_outside(), and reset_inside_outside_annotations().

+ Here is the caller graph for this function:

◆ set_vertex_category() [2/2]

void Slic3r::Voronoi::set_vertex_category ( VD::vertex_type *  v,
VertexCategory  c 
)
inline
79 { v->color(static_cast<VD::vertex_type::color_type>(c)); }

◆ signed_vertex_distances()

std::vector< double > Slic3r::Voronoi::signed_vertex_distances ( const VD vd,
const Lines lines 
)
968{
969 // vd shall be annotated.
970 assert(debug::verify_inside_outside_annotations(vd));
971
972 std::vector<double> out(vd.vertices().size(), 0.);
973 const VD::vertex_type *first_vertex = &vd.vertices().front();
974 for (const VD::vertex_type &vertex : vd.vertices()) {
975 const VertexCategory vc = vertex_category(vertex);
976 double dist;
977 if (vc == VertexCategory::OnContour) {
978 dist = 0.;
979 } else {
980 const VD::edge_type *first_edge = vertex.incident_edge();
981 const VD::edge_type *edge = first_edge;
982 const VD::cell_type *point_cell = nullptr;
983 do {
984 if (edge->cell()->contains_point()) {
985 point_cell = edge->cell();
986 break;
987 }
988 edge = edge->rot_next();
989 } while (edge != first_edge);
990 if (point_cell == 0) {
991 // Project vertex onto a contour segment.
992 const Line &line = lines[edge->cell()->source_index()];
993 dist = Geometry::ray_point_distance<Vec2d>(
994 line.a.cast<double>(), (line.b - line.a).cast<double>(), vertex_point(vertex));
995 } else {
996 // Distance to a contour point.
997 dist = (contour_point(*point_cell, lines).cast<double>() - vertex_point(vertex)).norm();
998 }
999 if (vc == VertexCategory::Inside)
1000 dist = - dist;
1001 }
1002 out[&vertex - first_vertex] = dist;
1003 }
1004
1005 assert(debug::verify_signed_distances(vd, lines, out));
1006
1007 return out;
1008}
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:280
TPoint< S > & vertex(S &sh, unsigned long idx, const PolygonTag &)
Definition geometry_traits.hpp:1180

References Slic3r::Line::a, Slic3r::Line::b, contour_point(), Inside, OnContour, Slic3r::Voronoi::debug::verify_inside_outside_annotations(), Slic3r::Voronoi::debug::verify_signed_distances(), vertex_category(), and vertex_point().

Referenced by offset(), and offset().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ skeleton_edges_rough()

std::vector< Vec2d > Slic3r::Voronoi::skeleton_edges_rough ( const VD vd,
const Lines lines,
const double  threshold_alpha 
)
1538{
1539 // vd shall be annotated.
1540 assert(debug::verify_inside_outside_annotations(vd));
1541
1542 const VD::edge_type *first_edge = &vd.edges().front();
1543 static constexpr double nan = std::numeric_limits<double>::quiet_NaN();
1544 // By default no edge is annotated as being part of the skeleton.
1545 std::vector<Vec2d> out(vd.num_edges(), Vec2d(nan, nan));
1546 // Threshold at a sharp corner, derived from a dot product of the sharp corner edges.
1547 const double threshold_cos_alpha = cos(threshold_alpha);
1548 // For sharp corners, dr/dl = sin(alpha/2). Substituting the dr/dl threshold with tan(alpha/2) threshold
1549 // in Voronoi point - point and Voronoi point - line site functions.
1550 const double threshold_tan_alpha_half = tan(0.5 * threshold_alpha);
1551
1552 for (const VD::edge_type &edge : vd.edges()) {
1553 size_t edge_idx = &edge - first_edge;
1554 if (
1555 // Ignore secondary and unbounded edges, they shall never be part of the skeleton.
1556 edge.is_secondary() || edge.is_infinite() ||
1557 // Skip the twin edge of an edge, that has already been processed.
1558 &edge > edge.twin() ||
1559 // Ignore outer edges.
1560 (edge_category(edge) != EdgeCategory::PointsInside && edge_category(edge.twin()) != EdgeCategory::PointsInside))
1561 continue;
1562 const VD::vertex_type *v0 = edge.vertex0();
1563 const VD::vertex_type *v1 = edge.vertex1();
1564 const VD::cell_type *cell = edge.cell();
1565 const VD::cell_type *cell2 = edge.twin()->cell();
1566 const Line &line0 = lines[cell->source_index()];
1567 const Line &line1 = lines[cell2->source_index()];
1568 size_t edge_idx2 = edge.twin() - first_edge;
1569 if (cell->contains_segment() && cell2->contains_segment()) {
1570 // Bisector of two line segments, distance along the bisector is linear,
1571 // dr/dl is constant.
1572 // using trigonometric identity sin^2(a) = (1-cos(2a))/2
1573 Vec2d lv0 = (line0.b - line0.a).cast<double>();
1574 Vec2d lv1 = (line1.b - line1.a).cast<double>();
1575 double d = lv0.dot(lv1);
1576 if (d < 0.) {
1577 double cos_alpha = - d / (lv0.norm() * lv1.norm());
1578 if (cos_alpha > threshold_cos_alpha) {
1579 // The whole bisector is a skeleton segment.
1580 out[edge_idx] = vertex_point(v0);
1581 out[edge_idx2] = vertex_point(v1);
1582 }
1583 }
1584 } else {
1585 // An infinite Voronoi Edge-Point (parabola) or Point-Point (line) bisector, clipped to a finite Voronoi segment.
1586 // The infinite bisector has a distance (skeleton radius) minimum, which is also a minimum
1587 // of the skeleton function dr / dt.
1588 assert(cell->contains_point() || cell2->contains_point());
1589 if (cell->contains_point() != cell2->contains_point()) {
1590 // Point - Segment
1591 const Point &pt0 = cell->contains_point() ? contour_point(*cell, line0) : contour_point(*cell2, line1);
1592 const Line &line = cell->contains_segment() ? line0 : line1;
1593 std::tie(out[edge_idx], out[edge_idx2]) = detail::point_segment_dr_dl_thresholds(
1594 pt0, line, vertex_point(v0), vertex_point(v1), threshold_tan_alpha_half);
1595 } else {
1596 // Point - Point
1597 const Point &pt0 = contour_point(*cell, line0);
1598 const Point &pt1 = contour_point(*cell2, line1);
1599 std::tie(out[edge_idx], out[edge_idx2]) = detail::point_point_dr_dl_thresholds(
1600 pt0, pt1, vertex_point(v0), vertex_point(v1), threshold_tan_alpha_half);
1601 }
1602 }
1603 }
1604
1605#ifdef VORONOI_DEBUG_OUT
1606 {
1607 static int irun = 0;
1608 ++ irun;
1609 Lines helper_lines;
1610 for (const VD::edge_type &edge : vd.edges())
1611 if (&edge < edge.twin() && edge.is_finite()) {
1612 const Vec2d &skeleton_pt = out[&edge - first_edge];
1613 const Vec2d &skeleton_pt2 = out[edge.twin() - first_edge];
1614 bool has_skeleton_pt = ! std::isnan(skeleton_pt.x());
1615 bool has_skeleton_pt2 = ! std::isnan(skeleton_pt2.x());
1616 const Vec2d &vertex_pt = vertex_point(edge.vertex0());
1617 const Vec2d &vertex_pt2 = vertex_point(edge.vertex1());
1618 if (has_skeleton_pt && has_skeleton_pt2) {
1619 // Complete edge is part of the skeleton.
1620 helper_lines.emplace_back(Line(Point(vertex_pt.x(), vertex_pt.y()), Point(vertex_pt2.x(), vertex_pt2.y())));
1621 } else {
1622 if (has_skeleton_pt)
1623 helper_lines.emplace_back(Line(Point(vertex_pt2.x(), vertex_pt2.y()), Point(skeleton_pt.x(), skeleton_pt.y())));
1624 if (has_skeleton_pt2)
1625 helper_lines.emplace_back(Line(Point(vertex_pt.x(), vertex_pt.y()), Point(skeleton_pt2.x(), skeleton_pt2.y())));
1626 }
1627 }
1628 dump_voronoi_to_svg(debug_out_path("voronoi-skeleton-edges-%d.svg", irun).c_str(), vd, Points(), lines, Polygons(), helper_lines);
1629 }
1630#endif // VORONOI_DEBUG_OUT
1631
1632 return out;
1633}
EIGEN_DEVICE_FUNC const TanReturnType tan() const
Definition ArrayCwiseUnaryOps.h:234
Point b
Definition Line.hpp:198

References Slic3r::Line::a, Slic3r::Line::b, contour_point(), cos(), Slic3r::debug_out_path(), Slic3r::dump_voronoi_to_svg(), edge_category(), Slic3r::Voronoi::detail::point_point_dr_dl_thresholds(), Slic3r::Voronoi::detail::point_segment_dr_dl_thresholds(), PointsInside, tan(), Slic3r::Voronoi::debug::verify_inside_outside_annotations(), and vertex_point().

+ Here is the call graph for this function:

◆ vertex_category() [1/2]

VertexCategory Slic3r::Voronoi::vertex_category ( const VD::vertex_type &  v)
inline
73 { return static_cast<VertexCategory>(v.color()); }

Referenced by annotate_inside_outside(), Slic3r::Geometry::MedialAxis::build(), Slic3r::dump_voronoi_to_svg(), edge_offset_contour_intersections(), signed_vertex_distances(), Slic3r::Voronoi::debug::verify_inside_outside_annotations(), Slic3r::Voronoi::debug::verify_signed_distances(), and Slic3r::Voronoi::debug::verify_vertices_on_contour().

+ Here is the caller graph for this function:

◆ vertex_category() [2/2]

VertexCategory Slic3r::Voronoi::vertex_category ( const VD::vertex_type *  v)
inline
75 { return static_cast<VertexCategory>(v->color()); }

◆ vertex_point() [1/2]

Vec2d Slic3r::Voronoi::vertex_point ( const VD::vertex_type &  v)
inline
26{ return Vec2d(v.x(), v.y()); }

Referenced by annotate_inside_outside(), edge_offset_contour_intersections(), signed_vertex_distances(), skeleton_edges_rough(), Slic3r::Voronoi::debug::verify_signed_distances(), and Slic3r::Voronoi::debug::verify_vertices_on_contour().

+ Here is the caller graph for this function:

◆ vertex_point() [2/2]

Vec2d Slic3r::Voronoi::vertex_point ( const VD::vertex_type *  v)
inline
27{ return Vec2d(v->x(), v->y()); }