767{
769 size_t poly_idx = 0;
770
771
772 const float scaled_offset = float(
scale_(spacing) * 0.81);
773
774 const float scaled_trim_distance = float(
scale_(spacing) * 0.5 * 0.75);
775
776
777 std::vector<std::pair<rtree_segment_t, size_t>> closest;
778
779 std::vector<std::pair<const Polyline*, const Polyline*>> lines_touching_at_endpoints;
780 {
781
782
785 assert(poly.points.size() == 2);
786 if (&poly != lines.data()) {
787
788 auto collinear_segment = [&rtree, &closest, &lines, &lines_touching_at_endpoints, r2_close](
const Point& pt,
const Point& pt_other,
const Polyline* polyline) -> std::pair<Polyline*, bool> {
789 closest.clear();
790 rtree.query(bgi::nearest(
mk_rtree_point(pt), 1), std::back_inserter(closest));
791 const Polyline *other = &lines[closest.front().second];
792 double dist2_front = (other->
points.front() - pt).cast<
double>().squaredNorm();
793 double dist2_back = (other->
points.back() - pt).cast<
double>().squaredNorm();
794 double dist2_min = std::min(dist2_front, dist2_back);
795 if (dist2_min < r2_close) {
796
797 double dist2_min_other = std::min((other->
points.front() - pt_other).cast<
double>().squaredNorm(), (other->
points.back() - pt_other).cast<
double>().squaredNorm());
798 if (dist2_min_other > dist2_min) {
799
802 Vec2d v1n = v1.normalized();
803 Vec2d v2n = v2.normalized();
804
805 double d = v1n.dot(v2n);
806 if (std::abs(d) > 0.99f) {
807
808 rtree.remove(closest.front());
809 return std::make_pair(
const_cast<Polyline*
>(other), dist2_min == dist2_front);
810 } else {
811 if (polyline > other)
812 std::swap(polyline, other);
813 lines_touching_at_endpoints.emplace_back(polyline, other);
814 }
815 }
816 }
817 return std::make_pair(static_cast<Polyline*>(nullptr), false);
818 };
819 auto collinear_front = collinear_segment(
poly.points.front(),
poly.points.back(), &poly);
820 auto collinear_back = collinear_segment(
poly.points.back(),
poly.points.front(), &poly);
821 assert(! collinear_front.first || ! collinear_back.first || collinear_front.first != collinear_back.first);
822 if (collinear_front.first) {
823 Polyline &other = *collinear_front.first;
824 assert(&other != &poly);
825 poly.points.front() = collinear_front.second ? other.points.back() : other.points.front();
826 other.points.clear();
827 }
828 if (collinear_back.first) {
829 Polyline &other = *collinear_back.first;
830 assert(&other != &poly);
831 poly.points.back() = collinear_back.second ? other.points.back() : other.points.front();
832 other.points.clear();
833 }
834 }
835 rtree.insert(std::make_pair(
mk_rtree_seg(
poly.points.front(),
poly.points.back()), poly_idx++));
836 }
837 }
838
839
841 lines_src.reserve(lines.size());
842 std::transform(lines.begin(), lines.end(), std::back_inserter(lines_src), [](const Polyline &pl) {
843 return pl.empty() ? Line(Point(0, 0), Point(0, 0)) : Line(pl.points.front(), pl.points.back()); });
844
846
847 std::vector<Intersection> intersections;
848 {
849
850
851
852 assert(scaled_offset > scaled_trim_distance);
853 const double line_len_threshold_drop_both_sides = scaled_offset * (2. / cos(
PI / 6.) + 0.5) +
SCALED_EPSILON;
854 const double line_len_threshold_anchor_both_sides = line_len_threshold_drop_both_sides + scaled_offset;
855 const double line_len_threshold_drop_single_side = scaled_offset * (1. / cos(
PI / 6.) + 1.5) +
SCALED_EPSILON;
856 const double line_len_threshold_anchor_single_side = line_len_threshold_drop_single_side + scaled_offset;
857 for (size_t line_idx = 0; line_idx < lines.size(); ++ line_idx) {
858 Polyline &line = lines[line_idx];
859 if (line.points.empty())
860 continue;
861
862 Point &front_point = line.points.front();
863 Point &back_point = line.points.back();
864
865
866 std::optional<size_t> tjoint_front, tjoint_back;
867 {
868 auto has_tjoint = [&
closest, line_idx, &rtree, &lines, &lines_src](
const Point &pt) {
869 auto filter_t_joint = [line_idx, &lines_src, pt](const auto &item) {
870 if (item.second != line_idx) {
871
872 const Line &line = lines_src[item.second];
875 const double l2 = v.squaredNorm();
877 const double t = va.dot(v);
879 }
880 }
881 return false;
882 };
884 rtree.query(bgi::nearest(
mk_rtree_point(pt), 1) && bgi::satisfies(filter_t_joint), std::back_inserter(closest));
885 std::optional<size_t> out;
887 const Polyline &pl = lines[
closest.front().second];
888 if (pl.points.empty()) {
889
890
891 #if 0
892 const auto &seg =
closest.front().first;
894 Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } };
896 #endif
897 } else if (pl.size() >= 2 &&
898
899 Line{ pl.front(), pl.back() }.distance_to_squared(pt) <= 1000 * 1000)
901 }
902 return out;
903 };
904
905 auto filter_end_point_connections = [&lines_touching_at_endpoints, &lines, &line](std::optional<size_t> in) {
906 std::optional<size_t> out;
907 if (in) {
908 const Polyline *lo = &line;
909 const Polyline *hi = &lines[*in];
910 if (lo > hi)
911 std::swap(lo, hi);
912 if (! std::binary_search(lines_touching_at_endpoints.begin(), lines_touching_at_endpoints.end(), std::make_pair(lo, hi)))
913
914 out = in;
915 }
916 return out;
917 };
918 tjoint_front = filter_end_point_connections(has_tjoint(front_point));
919 tjoint_back = filter_end_point_connections(has_tjoint(back_point));
920 }
921
922 int num_tjoints = int(tjoint_front.has_value()) + int(tjoint_back.has_value());
923 if (num_tjoints > 0) {
924 double line_len = line.length();
925 bool drop = false;
926 bool anchor = false;
927 if (num_tjoints == 1) {
928
929 drop = line_len < line_len_threshold_drop_single_side;
930 anchor = line_len > line_len_threshold_anchor_single_side;
931 } else {
932
933 assert(num_tjoints == 2);
934 drop = line_len < line_len_threshold_drop_both_sides;
935 anchor = line_len > line_len_threshold_anchor_both_sides;
936 }
937 if (drop) {
938
939
940
941 line.points.clear();
942 } else if (anchor) {
943 if (tjoint_front) {
944
945 intersections.emplace_back(lines_src[*tjoint_front], lines_src[line_idx], &line, front_point, true);
947 }
948 if (tjoint_back) {
949
950 intersections.emplace_back(lines_src[*tjoint_back], lines_src[line_idx], &line, back_point, false);
952 }
953 } else {
954 if (tjoint_front)
955
956
957 front_point += ((scaled_trim_distance * 1.155) * (back_point - front_point).cast<
double>().normalized()).cast<
coord_t>();
958 if (tjoint_back)
959
960
961 back_point += ((scaled_trim_distance * 1.155) * (front_point - back_point).cast<
double>().normalized()).cast<
coord_t>();
962 }
963 }
964 }
965
966 for (auto it = intersections.begin(); it != intersections.end(); ) {
967 assert(! lines[it->intersect_line - lines_src.data()].points.empty());
968 if (lines[it->closest_line - lines_src.data()].points.empty()) {
969 *it = intersections.back();
970 intersections.pop_back();
971 } else
972 ++ it;
973 }
974 }
976
977#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
978 static int iRun = 0;
979 int iStep = 0;
980 {
983 pts.emplace_back(i.intersect_point);
984 export_infill_lines_to_svg(boundary, lines,
debug_out_path(
"FillAdaptive-Tjoints-%d.svg", iRun++), pts);
985 }
986#endif
987
988
989 std::sort(intersections.begin(), intersections.end(),
991 return (i1.closest_line == i2.closest_line) ?
992 int(i1.left) < int(i2.left) :
993 i1.closest_line < i2.closest_line;
994 });
995
996 std::vector<size_t> merged_with(lines.size());
997 std::iota(merged_with.begin(), merged_with.end(), 0);
998
999
1000 {
1003 rtree.insert(std::make_pair(
mk_rtree_seg(prev, point), poly_idx++));
1004 prev = point;
1005 }
1006 for (
const Polygon &polygon : boundary.
holes) {
1007 Point prev = polygon.points.back();
1008 for (const Point &point : polygon.points) {
1009 rtree.insert(std::make_pair(
mk_rtree_seg(prev, point), poly_idx++));
1010 prev = point;
1011 }
1012 }
1013 }
1014
1015 auto update_merged_polyline_idx = [&merged_with](size_t pl_idx) {
1016
1017 for (size_t last = pl_idx;;) {
1018 size_t lower = merged_with[last];
1019 if (lower == last) {
1020 merged_with[pl_idx] = lower;
1021 return lower;
1022 }
1023 last = lower;
1024 }
1025 assert(false);
1026 return size_t(0);
1027 };
1029
1030 size_t intersect_pl_idx = update_merged_polyline_idx(
intersection.intersect_pl - lines.data());
1032
1037 }
1038 };
1039
1040
1041 for (auto it = lines_touching_at_endpoints.rbegin(); it != lines_touching_at_endpoints.rend(); ++ it) {
1042 Polyline *pl1 = const_cast<Polyline*>(it->first);
1043 Polyline *pl2 = const_cast<Polyline*>(it->second);
1044 assert(pl1 < pl2);
1045
1046
1047 pl2 = &lines[update_merged_polyline_idx(pl2 - lines.data())];
1048 assert(pl1 <= pl2);
1049
1050 if (pl1 != pl2 && ! pl1->points.empty() && ! pl2->points.empty()) {
1051
1052 assert(pl1 < pl2);
1053 assert(pl1->points.size() >= 2);
1054 assert(pl2->points.size() >= 2);
1055 double d11 = (pl1->points.front() - pl2->points.front()).cast<double>().squaredNorm();
1056 double d12 = (pl1->points.front() - pl2->points.back()) .cast<double>().squaredNorm();
1057 double d21 = (pl1->points.back() - pl2->points.front()).cast<double>().squaredNorm();
1058 double d22 = (pl1->points.back() - pl2->points.back()) .cast<double>().squaredNorm();
1059 double d1min = std::min(d11, d12);
1060 double d2min = std::min(d21, d22);
1061 if (d1min < d2min) {
1062 pl1->reverse();
1063 if (d12 == d1min)
1064 pl2->reverse();
1065 } else if (d22 == d2min)
1066 pl2->reverse();
1067 pl1->points.back() = (pl1->points.back() + pl2->points.front()) / 2;
1068 pl1->append(pl2->points.begin() + 1, pl2->points.end());
1069 pl2->points.clear();
1070 merged_with[pl2 - lines.data()] = pl1 - lines.data();
1071 }
1072 }
1073
1074
1075 std::vector<std::pair<Intersection*, double>> intersect_line;
1076 for (size_t min_idx = 0; min_idx < intersections.size();) {
1077 intersect_line.clear();
1078
1079 {
1080 const Vec2d line_dir = intersections[min_idx].closest_line->vector().cast<
double>();
1081 size_t max_idx = min_idx;
1082 for (; max_idx < intersections.size() &&
1083 intersections[min_idx].closest_line == intersections[max_idx].closest_line &&
1084 intersections[min_idx].left == intersections[max_idx].left;
1085 ++ max_idx)
1086 intersect_line.emplace_back(&intersections[max_idx], line_dir.dot(intersections[max_idx].intersect_point.cast<double>()));
1087 min_idx = max_idx;
1088 assert(intersect_line.size() > 0);
1089
1090 std::sort(intersect_line.begin(), intersect_line.end(), [](const auto &i1, const auto &i2) { return i1.second < i2.second; });
1091 }
1092
1093 if (intersect_line.size() == 1) {
1094
1096 update_merged_polyline(first_i);
1097 if (first_i.fresh()) {
1098
1099#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1100 export_infill_lines_to_svg(boundary, lines,
debug_out_path(
"FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
1101#endif
1102 add_hook(first_i, scaled_offset, hook_length, scaled_trim_distance, rtree, lines_src);
1103#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1104 export_infill_lines_to_svg(boundary, lines,
debug_out_path(
"FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
1105 ++ iStep;
1106#endif
1107 first_i.used = true;
1108 }
1109 continue;
1110 }
1111
1112 for (size_t first_idx = 0; first_idx < intersect_line.size(); ++ first_idx) {
1113 Intersection &first_i = *intersect_line[first_idx].first;
1114 update_merged_polyline(first_i);
1115 if (! first_i.fresh())
1116
1117 continue;
1118
1119
1120 if (first_idx > 0)
1121 update_merged_polyline(*intersect_line[first_idx - 1].first);
1122 if (first_idx + 1 < intersect_line.size())
1123 update_merged_polyline(*intersect_line[first_idx + 1].first);
1125 assert(first_i.closest_line == nearest_i.closest_line);
1126 assert(first_i.intersect_line != nearest_i.intersect_line);
1127 assert(first_i.intersect_line != first_i.closest_line);
1128 assert(nearest_i.intersect_line != first_i.closest_line);
1129
1130 Line offset_line =
create_offset_line(Line(first_i.intersect_point, nearest_i.intersect_point), first_i, scaled_offset);
1131
1132
1133 Point first_i_point, nearest_i_point;
1134 bool could_connect = false;
1135 if (nearest_i.fresh()) {
1136 could_connect = first_i.intersect_line->intersection(offset_line, &first_i_point) &&
1137 nearest_i.intersect_line->intersection(offset_line, &nearest_i_point);
1138 assert(could_connect);
1139 }
1140 Points &first_points = first_i.intersect_pl->points;
1141 Points &second_points = nearest_i.intersect_pl->points;
1142 could_connect &= (nearest_i_point - first_i_point).
cast<double>().squaredNorm() <=
Slic3r::sqr(hook_length_max);
1143 if (could_connect) {
1144
1145
1147 rtree.query(
1148 bgi::intersects(
mk_rtree_seg(first_i_point, nearest_i_point)) &&
1149 bgi::satisfies([&first_i, &nearest_i, &lines_src](const auto &item)
1150 { return item.second != (long unsigned int)(first_i.intersect_line - lines_src.data())
1151 && item.second != (long unsigned int)(nearest_i.intersect_line - lines_src.data()); }),
1152 std::back_inserter(closest));
1153 could_connect =
closest.empty();
1154#if 0
1155
1156 if (could_connect && first_i.intersect_pl != nearest_i.intersect_pl) {
1157 Line l(first_i_point, nearest_i_point);
1158 could_connect = ! first_i.other_hook_intersects(l) && ! nearest_i.other_hook_intersects(l);
1159 }
1160#endif
1161 }
1162 bool connected = false;
1163 if (could_connect) {
1164#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1165 export_infill_lines_to_svg(boundary, lines,
debug_out_path(
"FillAdaptive-connecting-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point, nearest_i.intersect_point });
1166#endif
1167
1168 if (first_i.intersect_pl == nearest_i.intersect_pl) {
1169
1170 assert(first_i.front != nearest_i.front);
1171 if (! first_i.front)
1172 std::swap(first_i_point, nearest_i_point);
1173 first_points.front() = first_i_point;
1174 first_points.back() = nearest_i_point;
1175
1176 first_points.emplace(first_points.begin(), nearest_i_point);
1177 } else {
1178
1179 Line l(first_i_point, nearest_i_point);
1180 l.translate((
perp(first_i.closest_line->vector().cast<
double>().normalized()) * (first_i.left ? scaled_trim_distance : - scaled_trim_distance)).cast<
coord_t>());
1181 Point pt_start, pt_end;
1182 bool trim_start = first_i .intersect_pl->points.size() == 3 && first_i .other_hook_intersects(l, pt_start);
1183 bool trim_end = nearest_i.intersect_pl->points.size() == 3 && nearest_i.other_hook_intersects(l, pt_end);
1184 first_points.reserve(first_points.size() + second_points.size());
1185 if (first_i.front)
1186 std::reverse(first_points.begin(), first_points.end());
1187 if (trim_start)
1188 first_points.front() = pt_start;
1189 first_points.back() = first_i_point;
1190 first_points.emplace_back(nearest_i_point);
1191 if (nearest_i.front)
1192 first_points.insert(first_points.end(), second_points.begin() + 1, second_points.end());
1193 else
1194 first_points.insert(first_points.end(), second_points.rbegin() + 1, second_points.rend());
1195 if (trim_end)
1196 first_points.back() = pt_end;
1197
1198 if (first_i.intersect_pl < nearest_i.intersect_pl) {
1199 second_points.clear();
1200 merged_with[nearest_i.intersect_pl - lines.data()] = first_i.intersect_pl - lines.data();
1201 } else {
1202 second_points = std::move(first_points);
1203 first_points.clear();
1204 merged_with[first_i.intersect_pl - lines.data()] = nearest_i.intersect_pl - lines.data();
1205 }
1206 }
1207 nearest_i.used = true;
1208 connected = true;
1209#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1210 export_infill_lines_to_svg(boundary, lines,
debug_out_path(
"FillAdaptive-connecting-post-%d-%d.svg", iRun, iStep), { first_i.intersect_point, nearest_i.intersect_point });
1211#endif
1212 }
1213 if (! connected) {
1214
1215#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1216 export_infill_lines_to_svg(boundary, lines,
debug_out_path(
"FillAdaptive-add_hook-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
1217#endif
1218 add_hook(first_i, scaled_offset, hook_length, scaled_trim_distance, rtree, lines_src);
1219#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1220 export_infill_lines_to_svg(boundary, lines,
debug_out_path(
"FillAdaptive-add_hook-post-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
1221#endif
1222 }
1223#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
1224 ++ iStep;
1225#endif
1226 first_i.used = true;
1227 }
1228 }
1229
1231 polylines_out.reserve(polylines_out.size() + std::count_if(lines.begin(), lines.end(), [](const Polyline &pl) { return !pl.empty(); }));
1232 for (Polyline &pl : lines)
1233 if (!pl.empty()) polylines_out.emplace_back(std::move(pl));
1234 return polylines_out;
1235}
Polygon contour
Definition ExPolygon.hpp:42
Polygons holes
Definition ExPolygon.hpp:43
Points points
Definition MultiPoint.hpp:22
Definition Polyline.hpp:25
static constexpr double PI
Definition libslic3r.h:81
#define scale_(val)
Definition libslic3r.h:106
bool validate_intersections(const std::vector< Intersection > &intersections)
Definition FillAdaptive.cpp:758
bool validate_intersection_t_joint(const Intersection &intersection)
Definition FillAdaptive.cpp:746
static rtree_point_t mk_rtree_point(const Point &pt)
Definition FillAdaptive.cpp:624
static void add_hook(const Intersection &intersection, const double scaled_offset, const coordf_t hook_length, double scaled_trim_distance, const rtree_t &rtree, const Lines &lines_src)
Definition FillAdaptive.cpp:635
bgi::rtree< std::pair< rtree_segment_t, size_t >, bgi::rstar< 16, 4 > > rtree_t
Definition FillAdaptive.cpp:622
static Intersection * get_nearest_intersection(std::vector< std::pair< Intersection *, double > > &intersect_line, const size_t first_idx)
Definition FillAdaptive.cpp:585
Foot closest(const Contour &c, const Vec2d &p, double stop=-1.0, const std::vector< Vec2d > *source_normals=nullptr, double normal_cosine=0.0)
Definition SeamContour.cpp:36
double distance_to_squared(const L &line, const Vec< Dim< L >, Scalar< L > > &point, Vec< Dim< L >, Scalar< L > > *nearest_point)
Definition Line.hpp:46
std::vector< Line > Lines
Definition Line.hpp:20
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
Eigen::Matrix< typename Derived::Scalar, 2, 1, Eigen::DontAlign > perp(const Eigen::MatrixBase< Derived > &v)
Definition Point.hpp:130
std::vector< Polyline > Polylines
Definition Polyline.hpp:22
constexpr T sqr(T x)
Definition libslic3r.h:301
void sort_remove_duplicates(std::vector< T > &vec)
Definition libslic3r.h:231
Definition FillAdaptive.cpp:528