OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::Voronoi Namespace Reference

Namespaces

namespace  detail
namespace  debug
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< Vec2d > edge_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< Vec2d > skeleton_edges_rough (const VD &vd, const Lines &lines, const double threshold_alpha)
const Point & contour_point (const VD::cell_type &cell, const Line &line)
Point & contour_point (const VD::cell_type &cell, Line &line)
const Point & contour_point (const VD::cell_type &cell, const Lines &lines)
Point & contour_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 
66{
67 // This Voronoi cell is split by an input segment to two halves, one is inside, the other is outside.
69 // This Voronoi cell is completely inside.
70 Inside,
71 // This Voronoi cell is completely outside.
72 Outside,
73 // Not known yet.
75};
@ Unknown
Definition PresetBundle.hpp:42
@ Boundary
Definition VoronoiOffset.hpp:68
@ Outside
Definition VoronoiOffset.hpp:44
@ Inside
Definition VoronoiOffset.hpp:42

◆ EdgeCategory

enum class Slic3r::Voronoi::EdgeCategory : unsigned char
strong
Enumerator
PointsToContour 
PointsInside 
PointsOutside 
Unknown 
53{
54 // This half-edge points onto the contour, this VD::edge_type::vertex1().color() is OnContour.
56 // This half-edge points inside, this VD::edge_type::vertex1().color() is Inside.
58 // This half-edge points outside, this VD::edge_type::vertex1().color() is Outside.
60 // Not known yet.
62};
@ PointsOutside
Definition VoronoiOffset.hpp:59
@ PointsToContour
Definition VoronoiOffset.hpp:55
@ PointsInside
Definition VoronoiOffset.hpp:57

◆ VertexCategory

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

Function Documentation

◆ annotate_inside_outside()

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

References Slic3r::Line::a, Slic3r::Line::b, Boundary, Slic3r::cast(), cell_category(), contour_point(), Slic3r::cross2(), Slic3r::debug_out_path(), Slic3r::dump_voronoi_to_svg(), edge_category(), Slic3r::Geometry::VoronoiDiagram::edges(), 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
96 { 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
98 { 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
22 { return ((cell.source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) ? line.a : line.b); }
Point b
Definition Line.hpp:199
Point a
Definition Line.hpp:198

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

References Slic3r::Line::a, Slic3r::Line::b, Slic3r::cast(), contour_point(), Slic3r::Voronoi::detail::Intersections::count, Slic3r::Voronoi::detail::dist_to_site(), Slic3r::dot(), edge_offset_has_intersection(), Slic3r::Geometry::VoronoiDiagram::edges(), Slic3r::Voronoi::detail::first_circle_segment_intersection_parameter(), Slic3r::Geometry::foot_pt(), Slic3r::lerp(), Slic3r::Voronoi::detail::line_point_equal_distance_points(), Slic3r::Geometry::VoronoiDiagram::num_edges(), 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(), vertex_point(), and Slic3r::Geometry::VoronoiDiagram::vertices().

Referenced by offset().

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

◆ edge_offset_has_intersection()

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

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()

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

References Slic3r::Line::a, Slic3r::angle(), Slic3r::append(), Slic3r::Line::b, Slic3r::cast(), Slic3r::center(), 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::Geometry::VoronoiDiagram::edges(), 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::Geometry::VoronoiDiagram::num_edges(), PointsInside, PointsOutside, scale_, SCALED_EPSILON, signed_vertex_distances(), Slic3r::BoundingBoxBase< PointType, APointsType >::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 )
1526{
1527 annotate_inside_outside(const_cast<VD&>(vd), lines);
1528 std::vector<double> dist = signed_vertex_distances(vd, lines);
1529 return offset(vd, lines, dist, offset_distance, discretization_error);
1530}
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298
Slic3r::Geometry::VoronoiDiagram VD
Definition VoronoiOffset.hpp:19
void annotate_inside_outside(VD &vd, const Lines &lines)
Definition VoronoiOffset.cpp:660
Polygons offset(const Geometry::VoronoiDiagram &vd, const Lines &lines, const std::vector< double > &signed_vertex_distances, double offset_distance, double discretization_error)
Definition VoronoiOffset.cpp:1337

References annotate_inside_outside(), Slic3r::Geometry::dist(), 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)
651{
652 for (const VD::vertex_type &v : vd.vertices())
654 for (const VD::edge_type &e : vd.edges())
656 for (const VD::cell_type &c : vd.cells())
658}
const cell_container_type & cells() const
Definition Voronoi.hpp:85

References Slic3r::Geometry::VoronoiDiagram::cells(), Slic3r::Geometry::VoronoiDiagram::edges(), set_cell_category(), set_edge_category(), set_vertex_category(), Unknown, and Slic3r::Geometry::VoronoiDiagram::vertices().

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
100 { 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
102 { 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
91 { 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
93 { 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
82 { 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
84 { 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 )
978{
979 // vd shall be annotated.
981
982 std::vector<double> out(vd.vertices().size(), 0.);
983 const VD::vertex_type *first_vertex = &vd.vertices().front();
984 for (const VD::vertex_type &vertex : vd.vertices()) {
985 const VertexCategory vc = vertex_category(vertex);
986 double dist;
987 if (vc == VertexCategory::OnContour) {
988 dist = 0.;
989 } else {
990 const VD::edge_type *first_edge = vertex.incident_edge();
991 const VD::edge_type *edge = first_edge;
992 const VD::cell_type *point_cell = nullptr;
993 do {
994 if (edge->cell()->contains_point()) {
995 point_cell = edge->cell();
996 break;
997 }
998 edge = edge->rot_next();
999 } while (edge != first_edge);
1000 if (point_cell == 0) {
1001 // Project vertex onto a contour segment.
1002 const Line &line = lines[edge->cell()->source_index()];
1004 line.a.cast<double>(), (line.b - line.a).cast<double>(), vertex_point(vertex));
1005 } else {
1006 // Distance to a contour point.
1007 dist = (contour_point(*point_cell, lines).cast<double>() - vertex_point(vertex)).norm();
1008 }
1009 if (vc == VertexCategory::Inside)
1010 dist = - dist;
1011 }
1012 out[&vertex - first_vertex] = dist;
1013 }
1014
1015 assert(debug::verify_signed_distances(vd, lines, out));
1016
1017 return out;
1018}
auto ray_point_distance(const T &ray_pt, const T &ray_dir, const T &pt)
Definition Geometry.hpp:204
bool verify_signed_distances(const VD &vd, const Lines &lines, const std::vector< double > &signed_distances)
Definition VoronoiOffset.cpp:610

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

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

References Slic3r::Line::a, Slic3r::Line::b, Slic3r::cast(), contour_point(), Slic3r::debug_out_path(), Slic3r::dump_voronoi_to_svg(), edge_category(), Slic3r::Geometry::VoronoiDiagram::edges(), Slic3r::Geometry::VoronoiDiagram::num_edges(), Slic3r::Voronoi::detail::point_point_dr_dl_thresholds(), Slic3r::Voronoi::detail::point_segment_dr_dl_thresholds(), PointsInside, 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
78 { 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
80 { return static_cast<VertexCategory>(v->color()); }

◆ vertex_point() [1/2]

Vec2d Slic3r::Voronoi::vertex_point ( const VD::vertex_type & v)
inline
31{ 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
32{ return Vec2d(v->x(), v->y()); }