Rework of hooks adding
This commit is contained in:
parent
0b4733f656
commit
53975eeaa3
3 changed files with 109 additions and 30 deletions
|
@ -621,27 +621,99 @@ static Line create_offset_line(const Line &line_to_offset, const Intersection &i
|
||||||
return offset_line;
|
return offset_line;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void connect_lines_with_hooks(Polylines &&lines, Polylines &polylines_out, const double spacing, const int hook_length)
|
namespace bg = boost::geometry;
|
||||||
|
namespace bgm = boost::geometry::model;
|
||||||
|
namespace bgi = boost::geometry::index;
|
||||||
|
|
||||||
|
// int64_t is needed because for coord_t bgi::intersects throws "bad numeric conversion: positive overflow"
|
||||||
|
using rtree_point_t = bgm::point<int64_t, 2, boost::geometry::cs::cartesian>;
|
||||||
|
using rtree_segment_t = bgm::segment<rtree_point_t>;
|
||||||
|
using rtree_t = bgi::rtree<std::pair<rtree_segment_t, size_t>, bgi::rstar<16, 4>>;
|
||||||
|
|
||||||
|
static void add_hook(const Intersection &intersection, const Line &hook_line, const double scaled_spacing, const int hook_length, const rtree_t &rtree)
|
||||||
{
|
{
|
||||||
namespace bgm = boost::geometry::model;
|
Vec2d hook_vector_norm = hook_line.vector().cast<double>().normalized();
|
||||||
namespace bgi = boost::geometry::index;
|
Vector hook_vector = (hook_length * hook_vector_norm).cast<coord_t>();
|
||||||
|
Line hook_line_offset = create_offset_line(hook_line, intersection, scaled_spacing);
|
||||||
|
|
||||||
using rtree_point_t = bgm::point<coord_t, 2, boost::geometry::cs::cartesian>;
|
Point intersection_point;
|
||||||
using rtree_segment_t = bgm::segment<rtree_point_t>;
|
bool intersection_found = intersection.intersect_line.intersection(hook_line_offset, &intersection_point);
|
||||||
using rtree_t = bgi::rtree<std::pair<rtree_segment_t, size_t>, bgi::rstar<16, 4>>;
|
assert(intersection_found);
|
||||||
|
|
||||||
std::vector<std::pair<rtree_segment_t, size_t>> rtee_segments;
|
Line hook_forward(intersection_point, intersection_point + hook_vector);
|
||||||
size_t poly_idx = 0;
|
Line hook_backward(intersection_point, intersection_point - hook_vector);
|
||||||
for (const Polyline &poly : lines) {
|
Line hook_forward_o = hook_forward, hook_backward_o = hook_backward;
|
||||||
rtee_segments.emplace_back(rtree_segment_t(rtree_point_t(poly.points.front().x(), poly.points.front().y()),
|
hook_forward_o.offset(-SCALED_EPSILON);
|
||||||
rtree_point_t(poly.points.back().x(), poly.points.back().y())),
|
hook_backward_o.offset(-SCALED_EPSILON);
|
||||||
poly_idx);
|
|
||||||
++poly_idx;
|
std::vector<std::pair<rtree_segment_t, size_t>> hook_intersections;
|
||||||
|
rtree.query(bgi::intersects(rtree_segment_t(rtree_point_t(hook_forward_o.a.x(), hook_forward_o.a.y()),
|
||||||
|
rtree_point_t(hook_forward_o.b.x(), hook_forward_o.b.y()))),
|
||||||
|
std::back_inserter(hook_intersections));
|
||||||
|
|
||||||
|
auto max_hook_length = [&hook_intersections, &hook_length](const Line &hook) {
|
||||||
|
coord_t max_length = hook_length;
|
||||||
|
for (const auto &hook_intersection : hook_intersections) {
|
||||||
|
const rtree_segment_t &segment = hook_intersection.first;
|
||||||
|
double dist = Line::distance_to(hook.a, Point(bg::get<0, 0>(segment), bg::get<0, 1>(segment)),
|
||||||
|
Point(bg::get<1, 0>(segment), bg::get<1, 1>(segment)));
|
||||||
|
max_length = std::min(coord_t(dist), max_length);
|
||||||
|
}
|
||||||
|
return max_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
Line hook_final;
|
||||||
|
if (hook_intersections.empty()) {
|
||||||
|
hook_final = std::move(hook_forward);
|
||||||
|
} else {
|
||||||
|
// There is not enough space for the hook, try another direction
|
||||||
|
coord_t hook_forward_max_length = max_hook_length(hook_forward);
|
||||||
|
hook_intersections.clear();
|
||||||
|
rtree.query(bgi::intersects(rtree_segment_t(rtree_point_t(hook_backward_o.a.x(), hook_backward_o.a.y()),
|
||||||
|
rtree_point_t(hook_backward_o.b.x(), hook_backward_o.b.y()))),
|
||||||
|
std::back_inserter(hook_intersections));
|
||||||
|
|
||||||
|
if (hook_intersections.empty()) {
|
||||||
|
hook_final = std::move(hook_backward);
|
||||||
|
} else {
|
||||||
|
// There is not enough space for hook in both directions, shrink the hook
|
||||||
|
coord_t hook_backward_max_length = max_hook_length(hook_backward);
|
||||||
|
if (hook_forward_max_length > hook_backward_max_length) {
|
||||||
|
Vector hook_vector_reduced = (hook_forward_max_length * hook_vector_norm).cast<coord_t>();
|
||||||
|
hook_final = Line(intersection_point, intersection_point + hook_vector_reduced);
|
||||||
|
} else {
|
||||||
|
Vector hook_vector_reduced = (hook_backward_max_length * hook_vector_norm).cast<coord_t>();
|
||||||
|
hook_final = Line(intersection_point, intersection_point - hook_vector_reduced);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intersection.forward) {
|
||||||
|
intersection.intersect_pl->points.front() = hook_final.a;
|
||||||
|
intersection.intersect_pl->points.emplace(intersection.intersect_pl->points.begin(), hook_final.b);
|
||||||
|
} else {
|
||||||
|
intersection.intersect_pl->points.back() = hook_final.a;
|
||||||
|
intersection.intersect_pl->points.emplace_back(hook_final.b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void connect_lines_using_hooks(Polylines &&lines, Polylines &polylines_out, const ExPolygon &boundary, const double spacing, const int hook_length)
|
||||||
|
{
|
||||||
|
rtree_t rtree;
|
||||||
|
size_t poly_idx = 0;
|
||||||
|
for (const Polyline &poly : lines) {
|
||||||
|
rtree.insert(std::make_pair(rtree_segment_t(rtree_point_t(poly.points.front().x(), poly.points.front().y()),
|
||||||
|
rtree_point_t(poly.points.back().x(), poly.points.back().y())),
|
||||||
|
poly_idx++));
|
||||||
}
|
}
|
||||||
rtree_t rtree(rtee_segments.begin(), rtee_segments.end());
|
|
||||||
|
|
||||||
std::vector<Intersection> intersections;
|
std::vector<Intersection> intersections;
|
||||||
|
coord_t scaled_spacing = scale_(spacing);
|
||||||
for (Polyline &line : lines) {
|
for (Polyline &line : lines) {
|
||||||
|
// Lines shorter than spacing are skipped because it is needed to shrink a line by the value of spacing.
|
||||||
|
// A shorter line than spacing could produce a degenerate polyline.
|
||||||
|
if (line.length() <= scaled_spacing) continue;
|
||||||
|
|
||||||
Point front_point = line.points.front();
|
Point front_point = line.points.front();
|
||||||
Point back_point = line.points.back();
|
Point back_point = line.points.back();
|
||||||
std::vector<std::pair<rtree_segment_t, size_t>> closest;
|
std::vector<std::pair<rtree_segment_t, size_t>> closest;
|
||||||
|
@ -664,6 +736,13 @@ static void connect_lines_with_hooks(Polylines &&lines, Polylines &polylines_out
|
||||||
std::vector<size_t> merged_with(lines.size());
|
std::vector<size_t> merged_with(lines.size());
|
||||||
std::iota(merged_with.begin(), merged_with.end(), 0);
|
std::iota(merged_with.begin(), merged_with.end(), 0);
|
||||||
|
|
||||||
|
// Appends the boundary polygon with all holes to rtee for detection if hooks not crossing the boundary
|
||||||
|
for (const Line &line : boundary.contour.lines())
|
||||||
|
rtree.insert(std::make_pair(rtree_segment_t(rtree_point_t(line.a.x(), line.a.y()), rtree_point_t(line.b.x(), line.b.y())), poly_idx++));
|
||||||
|
for (const Polygon &polygon : boundary.holes)
|
||||||
|
for (const Line &line : polygon.lines())
|
||||||
|
rtree.insert(std::make_pair(rtree_segment_t(rtree_point_t(line.a.x(), line.a.y()), rtree_point_t(line.b.x(), line.b.y())), poly_idx++));
|
||||||
|
|
||||||
auto get_merged_index = [&merged_with](size_t polyline_idx) {
|
auto get_merged_index = [&merged_with](size_t polyline_idx) {
|
||||||
for (size_t last = polyline_idx;;) {
|
for (size_t last = polyline_idx;;) {
|
||||||
size_t lower = merged_with[last];
|
size_t lower = merged_with[last];
|
||||||
|
@ -700,8 +779,8 @@ static void connect_lines_with_hooks(Polylines &&lines, Polylines &polylines_out
|
||||||
|
|
||||||
if (first_i.used || first_i.intersect_pl->points.size() == 0) continue;
|
if (first_i.used || first_i.intersect_pl->points.size() == 0) continue;
|
||||||
|
|
||||||
|
// A line between two intersections points
|
||||||
Line intersection_line(first_i.closest_point, nearest_i.closest_point);
|
Line intersection_line(first_i.closest_point, nearest_i.closest_point);
|
||||||
Point hook_vector = (hook_length * intersection_line.vector().cast<double>().normalized()).cast<coord_t>();
|
|
||||||
Line offset_line = create_offset_line(intersection_line, first_i, scale_(spacing));
|
Line offset_line = create_offset_line(intersection_line, first_i, scale_(spacing));
|
||||||
double intersection_line_length = intersection_line.length();
|
double intersection_line_length = intersection_line.length();
|
||||||
|
|
||||||
|
@ -762,14 +841,7 @@ static void connect_lines_with_hooks(Polylines &&lines, Polylines &polylines_out
|
||||||
first_i.used = true;
|
first_i.used = true;
|
||||||
nearest_i.used = true;
|
nearest_i.used = true;
|
||||||
} else {
|
} else {
|
||||||
if (first_i.forward) {
|
add_hook(first_i, intersection_line, scale_(spacing), hook_length, rtree);
|
||||||
first_i.intersect_pl->points.front() = first_i_point;
|
|
||||||
first_i.intersect_pl->points.emplace(first_i.intersect_pl->points.begin(), first_i_point + hook_vector);
|
|
||||||
} else {
|
|
||||||
first_i.intersect_pl->points.back() = first_i_point;
|
|
||||||
first_i.intersect_pl->points.emplace_back(first_i_point + hook_vector);
|
|
||||||
}
|
|
||||||
|
|
||||||
first_i.used = true;
|
first_i.used = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -781,12 +853,10 @@ static void connect_lines_with_hooks(Polylines &&lines, Polylines &polylines_out
|
||||||
if (!pl.empty()) polylines_out.emplace_back(std::move(pl));
|
if (!pl.empty()) polylines_out.emplace_back(std::move(pl));
|
||||||
}
|
}
|
||||||
|
|
||||||
coord_t get_hook_length(const double spacing) {
|
coord_t get_hook_length(const double spacing) { return scale_(spacing) * 5; }
|
||||||
return scale_(spacing) * 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Filler::_fill_surface_single(
|
void Filler::_fill_surface_single(
|
||||||
const FillParams & params,
|
const FillParams ¶ms,
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const std::pair<float, Point> &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
|
@ -841,14 +911,14 @@ void Filler::_fill_surface_single(
|
||||||
}
|
}
|
||||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||||
|
|
||||||
coord_t hook_length = get_hook_length(this->spacing);
|
coord_t hook_length = get_hook_length(this->spacing);
|
||||||
Polylines all_polylines_with_hooks;
|
Polylines all_polylines_with_hooks;
|
||||||
connect_lines_with_hooks(std::move(all_polylines), all_polylines_with_hooks, this->spacing, hook_length);
|
connect_lines_using_hooks(std::move(all_polylines), all_polylines_with_hooks, expolygon, this->spacing, hook_length);
|
||||||
|
|
||||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||||
{
|
{
|
||||||
static int iRun = 0;
|
static int iRun = 0;
|
||||||
export_infill_lines_to_svg(expolygon, all_polylines_with_hooks, debug_out_path("FillAdaptive-hooks-%d.svg", iRun ++));
|
export_infill_lines_to_svg(expolygon, all_polylines_with_hooks, debug_out_path("FillAdaptive-hooks-%d.svg", iRun++));
|
||||||
}
|
}
|
||||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,13 @@ bool Line::clip_with_bbox(const BoundingBox &bbox)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Line::offset(double offset)
|
||||||
|
{
|
||||||
|
Vector offset_vector = (offset * this->vector().cast<double>().normalized()).cast<coord_t>();
|
||||||
|
this->a -= offset_vector;
|
||||||
|
this->b += offset_vector;
|
||||||
|
}
|
||||||
|
|
||||||
Vec3d Linef3::intersect_plane(double z) const
|
Vec3d Linef3::intersect_plane(double z) const
|
||||||
{
|
{
|
||||||
auto v = (this->b - this->a).cast<double>();
|
auto v = (this->b - this->a).cast<double>();
|
||||||
|
|
|
@ -75,6 +75,8 @@ public:
|
||||||
double ccw(const Point& point) const { return point.ccw(*this); }
|
double ccw(const Point& point) const { return point.ccw(*this); }
|
||||||
// Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box.
|
// Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box.
|
||||||
bool clip_with_bbox(const BoundingBox &bbox);
|
bool clip_with_bbox(const BoundingBox &bbox);
|
||||||
|
// Resize a line from both sides by the offset.
|
||||||
|
void offset(double offset);
|
||||||
|
|
||||||
static inline double distance_to_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_squared(Line{a, b}, Vec<2, coord_t>{point}); }
|
static inline double distance_to_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_squared(Line{a, b}, Vec<2, coord_t>{point}); }
|
||||||
static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); }
|
static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); }
|
||||||
|
|
Loading…
Reference in a new issue