mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-11 16:57:53 -06:00
Merged pull request "Gyroid improvements" #2730
by @supermerill and @wavexx WIP: The function Fill::connect_infill() is being rewritten to utilize spatial structures wherever possible for lower CPU load and less dynamic memory allocation.
This commit is contained in:
commit
f2dd49a905
8 changed files with 957 additions and 125 deletions
|
@ -46,11 +46,29 @@ void EdgeGrid::Grid::create(const Polygons &polygons, coord_t resolution)
|
||||||
++ ncontours;
|
++ ncontours;
|
||||||
|
|
||||||
// Collect the contours.
|
// Collect the contours.
|
||||||
m_contours.assign(ncontours, NULL);
|
m_contours.assign(ncontours, nullptr);
|
||||||
ncontours = 0;
|
ncontours = 0;
|
||||||
for (size_t j = 0; j < polygons.size(); ++ j)
|
for (size_t j = 0; j < polygons.size(); ++ j)
|
||||||
if (! polygons[j].points.empty())
|
if (! polygons[j].points.empty())
|
||||||
m_contours[ncontours++] = &polygons[j].points;
|
m_contours[ncontours ++] = &polygons[j].points;
|
||||||
|
|
||||||
|
create_from_m_contours(resolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EdgeGrid::Grid::create(const std::vector<Points> &polygons, coord_t resolution)
|
||||||
|
{
|
||||||
|
// Count the contours.
|
||||||
|
size_t ncontours = 0;
|
||||||
|
for (size_t j = 0; j < polygons.size(); ++ j)
|
||||||
|
if (! polygons[j].empty())
|
||||||
|
++ ncontours;
|
||||||
|
|
||||||
|
// Collect the contours.
|
||||||
|
m_contours.assign(ncontours, nullptr);
|
||||||
|
ncontours = 0;
|
||||||
|
for (size_t j = 0; j < polygons.size(); ++ j)
|
||||||
|
if (! polygons[j].empty())
|
||||||
|
m_contours[ncontours ++] = &polygons[j];
|
||||||
|
|
||||||
create_from_m_contours(resolution);
|
create_from_m_contours(resolution);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +84,7 @@ void EdgeGrid::Grid::create(const ExPolygon &expoly, coord_t resolution)
|
||||||
++ ncontours;
|
++ ncontours;
|
||||||
|
|
||||||
// Collect the contours.
|
// Collect the contours.
|
||||||
m_contours.assign(ncontours, NULL);
|
m_contours.assign(ncontours, nullptr);
|
||||||
ncontours = 0;
|
ncontours = 0;
|
||||||
if (! expoly.contour.points.empty())
|
if (! expoly.contour.points.empty())
|
||||||
m_contours[ncontours++] = &expoly.contour.points;
|
m_contours[ncontours++] = &expoly.contour.points;
|
||||||
|
@ -91,7 +109,7 @@ void EdgeGrid::Grid::create(const ExPolygons &expolygons, coord_t resolution)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect the contours.
|
// Collect the contours.
|
||||||
m_contours.assign(ncontours, NULL);
|
m_contours.assign(ncontours, nullptr);
|
||||||
ncontours = 0;
|
ncontours = 0;
|
||||||
for (size_t i = 0; i < expolygons.size(); ++ i) {
|
for (size_t i = 0; i < expolygons.size(); ++ i) {
|
||||||
const ExPolygon &expoly = expolygons[i];
|
const ExPolygon &expoly = expolygons[i];
|
||||||
|
@ -1122,7 +1140,7 @@ EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt
|
||||||
Vec2d vfoot = foot - pt.cast<double>();
|
Vec2d vfoot = foot - pt.cast<double>();
|
||||||
double dist_foot = vfoot.norm();
|
double dist_foot = vfoot.norm();
|
||||||
double dist_foot_err = dist_foot - d_min;
|
double dist_foot_err = dist_foot - d_min;
|
||||||
assert(std::abs(dist_foot_err) < 1e-7 * d_min);
|
assert(std::abs(dist_foot_err) < 1e-7 || std::abs(dist_foot_err) < 1e-7 * d_min);
|
||||||
#endif /* NDEBUG */
|
#endif /* NDEBUG */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1145,7 +1163,7 @@ EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt
|
||||||
vfoot = p1.cast<double>() * (1. - result.t) + p2.cast<double>() * result.t - pt.cast<double>();
|
vfoot = p1.cast<double>() * (1. - result.t) + p2.cast<double>() * result.t - pt.cast<double>();
|
||||||
double dist_foot = vfoot.norm();
|
double dist_foot = vfoot.norm();
|
||||||
double dist_foot_err = dist_foot - std::abs(result.distance);
|
double dist_foot_err = dist_foot - std::abs(result.distance);
|
||||||
assert(std::abs(dist_foot_err) < 1e-7 * std::abs(result.distance));
|
assert(std::abs(dist_foot_err) < 1e-7 || std::abs(dist_foot_err) < 1e-7 * std::abs(result.distance));
|
||||||
}
|
}
|
||||||
#endif /* NDEBUG */
|
#endif /* NDEBUG */
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -21,6 +21,7 @@ public:
|
||||||
void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; }
|
void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; }
|
||||||
|
|
||||||
void create(const Polygons &polygons, coord_t resolution);
|
void create(const Polygons &polygons, coord_t resolution);
|
||||||
|
void create(const std::vector<Points> &polygons, coord_t resolution);
|
||||||
void create(const ExPolygon &expoly, coord_t resolution);
|
void create(const ExPolygon &expoly, coord_t resolution);
|
||||||
void create(const ExPolygons &expolygons, coord_t resolution);
|
void create(const ExPolygons &expolygons, coord_t resolution);
|
||||||
void create(const ExPolygonCollection &expolygons, coord_t resolution);
|
void create(const ExPolygonCollection &expolygons, coord_t resolution);
|
||||||
|
|
|
@ -77,6 +77,11 @@ public:
|
||||||
void triangulate_pp(Points *triangles) const;
|
void triangulate_pp(Points *triangles) const;
|
||||||
void triangulate_p2t(Polygons* polygons) const;
|
void triangulate_p2t(Polygons* polygons) const;
|
||||||
Lines lines() const;
|
Lines lines() const;
|
||||||
|
|
||||||
|
// Number of contours (outer contour with holes).
|
||||||
|
size_t num_contours() const { return this->holes.size() + 1; }
|
||||||
|
Polygon& contour_or_hole(size_t idx) { return (idx == 0) ? this->contour : this->holes[idx - 1]; }
|
||||||
|
const Polygon& contour_or_hole(size_t idx) const { return (idx == 0) ? this->contour : this->holes[idx - 1]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; }
|
inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; }
|
||||||
|
|
|
@ -158,43 +158,18 @@ void Fill3DHoneycomb::_fill_surface_single(
|
||||||
((this->layer_id/thickness_layers) % 2) + 1);
|
((this->layer_id/thickness_layers) % 2) + 1);
|
||||||
|
|
||||||
// move pattern in place
|
// move pattern in place
|
||||||
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it)
|
for (Polyline &pl : polylines)
|
||||||
it->translate(bb.min(0), bb.min(1));
|
pl.translate(bb.min);
|
||||||
|
|
||||||
// clip pattern to boundaries
|
// clip pattern to boundaries, chain the clipped polylines
|
||||||
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
Polylines polylines_chained = chain_polylines(intersection_pl(polylines, to_polygons(expolygon)));
|
||||||
|
|
||||||
// connect lines
|
// connect lines if needed
|
||||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
if (! polylines_chained.empty()) {
|
||||||
ExPolygon expolygon_off;
|
if (params.dont_connect)
|
||||||
{
|
append(polylines_out, std::move(polylines_chained));
|
||||||
ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON);
|
else
|
||||||
if (! expolygons_off.empty()) {
|
this->connect_infill(std::move(polylines_chained), expolygon, polylines_out, params);
|
||||||
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
|
|
||||||
assert(expolygons_off.size() == 1);
|
|
||||||
std::swap(expolygon_off, expolygons_off.front());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool first = true;
|
|
||||||
for (Polyline &polyline : chain_polylines(std::move(polylines))) {
|
|
||||||
if (! first) {
|
|
||||||
// Try to connect the lines.
|
|
||||||
Points &pts_end = polylines_out.back().points;
|
|
||||||
const Point &first_point = polyline.points.front();
|
|
||||||
const Point &last_point = pts_end.back();
|
|
||||||
// TODO: we should also check that both points are on a fill_boundary to avoid
|
|
||||||
// connecting paths on the boundaries of internal regions
|
|
||||||
if ((last_point - first_point).cast<double>().norm() <= 1.5 * distance &&
|
|
||||||
expolygon_off.contains(Line(last_point, first_point))) {
|
|
||||||
// Append the polyline.
|
|
||||||
pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The lines cannot be connected.
|
|
||||||
polylines_out.emplace_back(std::move(polyline));
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "../ClipperUtils.hpp"
|
#include "../ClipperUtils.hpp"
|
||||||
|
#include "../EdgeGrid.hpp"
|
||||||
#include "../Surface.hpp"
|
#include "../Surface.hpp"
|
||||||
#include "../PrintConfig.hpp"
|
#include "../PrintConfig.hpp"
|
||||||
|
#include "../libslic3r.h"
|
||||||
|
|
||||||
#include "FillBase.hpp"
|
#include "FillBase.hpp"
|
||||||
#include "FillConcentric.hpp"
|
#include "FillConcentric.hpp"
|
||||||
|
@ -148,4 +150,814 @@ std::pair<float, Point> Fill::_infill_direction(const Surface *surface) const
|
||||||
return std::pair<float, Point>(out_angle, out_shift);
|
return std::pair<float, Point>(out_angle, out_shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// From pull request "Gyroid improvements" #2730 by @supermerill
|
||||||
|
|
||||||
|
/// cut poly between poly.point[idx_1] & poly.point[idx_1+1]
|
||||||
|
/// add p1+-width to one part and p2+-width to the other one.
|
||||||
|
/// add the "new" polyline to polylines (to part cut from poly)
|
||||||
|
/// p1 & p2 have to be between poly.point[idx_1] & poly.point[idx_1+1]
|
||||||
|
/// if idx_1 is ==0 or == size-1, then we don't need to create a new polyline.
|
||||||
|
static void cut_polyline(Polyline &poly, Polylines &polylines, size_t idx_1, Point p1, Point p2) {
|
||||||
|
//reorder points
|
||||||
|
if (p1.distance_to_square(poly.points[idx_1]) > p2.distance_to_square(poly.points[idx_1])) {
|
||||||
|
Point temp = p2;
|
||||||
|
p2 = p1;
|
||||||
|
p1 = temp;
|
||||||
|
}
|
||||||
|
if (idx_1 == poly.points.size() - 1) {
|
||||||
|
//shouldn't be possible.
|
||||||
|
poly.points.erase(poly.points.end() - 1);
|
||||||
|
} else {
|
||||||
|
// create new polyline
|
||||||
|
Polyline new_poly;
|
||||||
|
//put points in new_poly
|
||||||
|
new_poly.points.push_back(p2);
|
||||||
|
new_poly.points.insert(new_poly.points.end(), poly.points.begin() + idx_1 + 1, poly.points.end());
|
||||||
|
//erase&put points in poly
|
||||||
|
poly.points.erase(poly.points.begin() + idx_1 + 1, poly.points.end());
|
||||||
|
poly.points.push_back(p1);
|
||||||
|
//safe test
|
||||||
|
if (poly.length() == 0)
|
||||||
|
poly.points = new_poly.points;
|
||||||
|
else
|
||||||
|
polylines.emplace_back(new_poly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the poly is like a polygon but with first_point != last_point (already removed)
|
||||||
|
static void cut_polygon(Polyline &poly, size_t idx_1, Point p1, Point p2) {
|
||||||
|
//reorder points
|
||||||
|
if (p1.distance_to_square(poly.points[idx_1]) > p2.distance_to_square(poly.points[idx_1])) {
|
||||||
|
Point temp = p2;
|
||||||
|
p2 = p1;
|
||||||
|
p1 = temp;
|
||||||
|
}
|
||||||
|
//check if we need to rotate before cutting
|
||||||
|
if (idx_1 != poly.size() - 1) {
|
||||||
|
//put points in new_poly
|
||||||
|
poly.points.insert(poly.points.end(), poly.points.begin(), poly.points.begin() + idx_1 + 1);
|
||||||
|
poly.points.erase(poly.points.begin(), poly.points.begin() + idx_1 + 1);
|
||||||
|
}
|
||||||
|
//put points in poly
|
||||||
|
poly.points.push_back(p1);
|
||||||
|
poly.points.insert(poly.points.begin(), p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// check if the polyline from pts_to_check may be at 'width' distance of a point in polylines_blocker
|
||||||
|
/// it use equally_spaced_points with width/2 precision, so don't worry with pts_to_check number of points.
|
||||||
|
/// it use the given polylines_blocker points, be sure to put enough of them to be reliable.
|
||||||
|
/// complexity : N(pts_to_check.equally_spaced_points(width / 2)) x N(polylines_blocker.points)
|
||||||
|
static bool collision(const Points &pts_to_check, const Polylines &polylines_blocker, const coordf_t width) {
|
||||||
|
//check if it's not too close to a polyline
|
||||||
|
coordf_t min_dist_square = width * width * 0.9 - SCALED_EPSILON;
|
||||||
|
Polyline better_polylines(pts_to_check);
|
||||||
|
Points better_pts = better_polylines.equally_spaced_points(width / 2);
|
||||||
|
for (const Point &p : better_pts) {
|
||||||
|
for (const Polyline &poly2 : polylines_blocker) {
|
||||||
|
for (const Point &p2 : poly2.points) {
|
||||||
|
if (p.distance_to_square(p2) < min_dist_square) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to find a path inside polylines that allow to go from p1 to p2.
|
||||||
|
/// width if the width of the extrusion
|
||||||
|
/// polylines_blockers are the array of polylines to check if the path isn't blocked by something.
|
||||||
|
/// complexity: N(polylines.points) + a collision check after that if we finded a path: N(2(p2-p1)/width) x N(polylines_blocker.points)
|
||||||
|
static Points get_frontier(Polylines &polylines, const Point& p1, const Point& p2, const coord_t width, const Polylines &polylines_blockers, coord_t max_size = -1) {
|
||||||
|
for (size_t idx_poly = 0; idx_poly < polylines.size(); ++idx_poly) {
|
||||||
|
Polyline &poly = polylines[idx_poly];
|
||||||
|
if (poly.size() <= 1) continue;
|
||||||
|
|
||||||
|
//loop?
|
||||||
|
if (poly.first_point() == poly.last_point()) {
|
||||||
|
//polygon : try to find a line for p1 & p2.
|
||||||
|
size_t idx_11, idx_12, idx_21, idx_22;
|
||||||
|
idx_11 = poly.closest_point_index(p1);
|
||||||
|
idx_12 = idx_11;
|
||||||
|
if (Line(poly.points[idx_11], poly.points[(idx_11 + 1) % (poly.points.size() - 1)]).distance_to(p1) < SCALED_EPSILON) {
|
||||||
|
idx_12 = (idx_11 + 1) % (poly.points.size() - 1);
|
||||||
|
} else if (Line(poly.points[(idx_11 > 0) ? (idx_11 - 1) : (poly.points.size() - 2)], poly.points[idx_11]).distance_to(p1) < SCALED_EPSILON) {
|
||||||
|
idx_11 = (idx_11 > 0) ? (idx_11 - 1) : (poly.points.size() - 2);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
idx_21 = poly.closest_point_index(p2);
|
||||||
|
idx_22 = idx_21;
|
||||||
|
if (Line(poly.points[idx_21], poly.points[(idx_21 + 1) % (poly.points.size() - 1)]).distance_to(p2) < SCALED_EPSILON) {
|
||||||
|
idx_22 = (idx_21 + 1) % (poly.points.size() - 1);
|
||||||
|
} else if (Line(poly.points[(idx_21 > 0) ? (idx_21 - 1) : (poly.points.size() - 2)], poly.points[idx_21]).distance_to(p2) < SCALED_EPSILON) {
|
||||||
|
idx_21 = (idx_21 > 0) ? (idx_21 - 1) : (poly.points.size() - 2);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//edge case: on the same line
|
||||||
|
if (idx_11 == idx_21 && idx_12 == idx_22) {
|
||||||
|
if (collision(Points() = { p1, p2 }, polylines_blockers, width)) return Points();
|
||||||
|
//break loop
|
||||||
|
poly.points.erase(poly.points.end() - 1);
|
||||||
|
cut_polygon(poly, idx_11, p1, p2);
|
||||||
|
return Points() = { Line(p1, p2).midpoint() };
|
||||||
|
}
|
||||||
|
|
||||||
|
//compute distance & array for the ++ path
|
||||||
|
Points ret_1_to_2;
|
||||||
|
double dist_1_to_2 = p1.distance_to(poly.points[idx_12]);
|
||||||
|
ret_1_to_2.push_back(poly.points[idx_12]);
|
||||||
|
size_t max = idx_12 <= idx_21 ? idx_21+1 : poly.points.size();
|
||||||
|
for (size_t i = idx_12 + 1; i < max; i++) {
|
||||||
|
dist_1_to_2 += poly.points[i - 1].distance_to(poly.points[i]);
|
||||||
|
ret_1_to_2.push_back(poly.points[i]);
|
||||||
|
}
|
||||||
|
if (idx_12 > idx_21) {
|
||||||
|
dist_1_to_2 += poly.points.back().distance_to(poly.points.front());
|
||||||
|
ret_1_to_2.push_back(poly.points[0]);
|
||||||
|
for (size_t i = 1; i <= idx_21; i++) {
|
||||||
|
dist_1_to_2 += poly.points[i - 1].distance_to(poly.points[i]);
|
||||||
|
ret_1_to_2.push_back(poly.points[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dist_1_to_2 += p2.distance_to(poly.points[idx_21]);
|
||||||
|
|
||||||
|
//compute distance & array for the -- path
|
||||||
|
Points ret_2_to_1;
|
||||||
|
double dist_2_to_1 = p1.distance_to(poly.points[idx_11]);
|
||||||
|
ret_2_to_1.push_back(poly.points[idx_11]);
|
||||||
|
size_t min = idx_22 <= idx_11 ? idx_22 : 0;
|
||||||
|
for (size_t i = idx_11; i > min; i--) {
|
||||||
|
dist_2_to_1 += poly.points[i - 1].distance_to(poly.points[i]);
|
||||||
|
ret_2_to_1.push_back(poly.points[i - 1]);
|
||||||
|
}
|
||||||
|
if (idx_22 > idx_11) {
|
||||||
|
dist_2_to_1 += poly.points.back().distance_to(poly.points.front());
|
||||||
|
ret_2_to_1.push_back(poly.points[poly.points.size() - 1]);
|
||||||
|
for (size_t i = poly.points.size() - 1; i > idx_22; i--) {
|
||||||
|
dist_2_to_1 += poly.points[i - 1].distance_to(poly.points[i]);
|
||||||
|
ret_2_to_1.push_back(poly.points[i - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dist_2_to_1 += p2.distance_to(poly.points[idx_22]);
|
||||||
|
|
||||||
|
if (max_size < dist_2_to_1 && max_size < dist_1_to_2) {
|
||||||
|
return Points();
|
||||||
|
}
|
||||||
|
|
||||||
|
//choose between the two direction (keep the short one)
|
||||||
|
if (dist_1_to_2 < dist_2_to_1) {
|
||||||
|
if (collision(ret_1_to_2, polylines_blockers, width)) return Points();
|
||||||
|
//break loop
|
||||||
|
poly.points.erase(poly.points.end() - 1);
|
||||||
|
//remove points
|
||||||
|
if (idx_12 <= idx_21) {
|
||||||
|
poly.points.erase(poly.points.begin() + idx_12, poly.points.begin() + idx_21 + 1);
|
||||||
|
if (idx_12 != 0) {
|
||||||
|
cut_polygon(poly, idx_11, p1, p2);
|
||||||
|
} //else : already cut at the good place
|
||||||
|
} else {
|
||||||
|
poly.points.erase(poly.points.begin() + idx_12, poly.points.end());
|
||||||
|
poly.points.erase(poly.points.begin(), poly.points.begin() + idx_21);
|
||||||
|
cut_polygon(poly, poly.points.size() - 1, p1, p2);
|
||||||
|
}
|
||||||
|
return ret_1_to_2;
|
||||||
|
} else {
|
||||||
|
if (collision(ret_2_to_1, polylines_blockers, width)) return Points();
|
||||||
|
//break loop
|
||||||
|
poly.points.erase(poly.points.end() - 1);
|
||||||
|
//remove points
|
||||||
|
if (idx_22 <= idx_11) {
|
||||||
|
poly.points.erase(poly.points.begin() + idx_22, poly.points.begin() + idx_11 + 1);
|
||||||
|
if (idx_22 != 0) {
|
||||||
|
cut_polygon(poly, idx_21, p1, p2);
|
||||||
|
} //else : already cut at the good place
|
||||||
|
} else {
|
||||||
|
poly.points.erase(poly.points.begin() + idx_22, poly.points.end());
|
||||||
|
poly.points.erase(poly.points.begin(), poly.points.begin() + idx_11);
|
||||||
|
cut_polygon(poly, poly.points.size() - 1, p1, p2);
|
||||||
|
}
|
||||||
|
return ret_2_to_1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//polyline : try to find a line for p1 & p2.
|
||||||
|
size_t idx_1, idx_2;
|
||||||
|
idx_1 = poly.closest_point_index(p1);
|
||||||
|
if (idx_1 < poly.points.size() - 1 && Line(poly.points[idx_1], poly.points[idx_1 + 1]).distance_to(p1) < SCALED_EPSILON) {
|
||||||
|
} else if (idx_1 > 0 && Line(poly.points[idx_1 - 1], poly.points[idx_1]).distance_to(p1) < SCALED_EPSILON) {
|
||||||
|
idx_1 = idx_1 - 1;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
idx_2 = poly.closest_point_index(p2);
|
||||||
|
if (idx_2 < poly.points.size() - 1 && Line(poly.points[idx_2], poly.points[idx_2 + 1]).distance_to(p2) < SCALED_EPSILON) {
|
||||||
|
} else if (idx_2 > 0 && Line(poly.points[idx_2 - 1], poly.points[idx_2]).distance_to(p2) < SCALED_EPSILON) {
|
||||||
|
idx_2 = idx_2 - 1;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//edge case: on the same line
|
||||||
|
if (idx_1 == idx_2) {
|
||||||
|
if (collision(Points() = { p1, p2 }, polylines_blockers, width)) return Points();
|
||||||
|
cut_polyline(poly, polylines, idx_1, p1, p2);
|
||||||
|
return Points() = { Line(p1, p2).midpoint() };
|
||||||
|
}
|
||||||
|
|
||||||
|
//create ret array
|
||||||
|
size_t first_idx = idx_1;
|
||||||
|
size_t last_idx = idx_2 + 1;
|
||||||
|
if (idx_1 > idx_2) {
|
||||||
|
first_idx = idx_2;
|
||||||
|
last_idx = idx_1 + 1;
|
||||||
|
}
|
||||||
|
Points p_ret;
|
||||||
|
p_ret.insert(p_ret.end(), poly.points.begin() + first_idx + 1, poly.points.begin() + last_idx);
|
||||||
|
|
||||||
|
coordf_t length = 0;
|
||||||
|
for (size_t i = 1; i < p_ret.size(); i++) length += p_ret[i - 1].distance_to(p_ret[i]);
|
||||||
|
|
||||||
|
if (max_size < length) {
|
||||||
|
return Points();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collision(p_ret, polylines_blockers, width)) return Points();
|
||||||
|
//cut polyline
|
||||||
|
poly.points.erase(poly.points.begin() + first_idx + 1, poly.points.begin() + last_idx);
|
||||||
|
cut_polyline(poly, polylines, first_idx, p1, p2);
|
||||||
|
//order the returned array to be p1->p2
|
||||||
|
if (idx_1 > idx_2) {
|
||||||
|
std::reverse(p_ret.begin(), p_ret.end());
|
||||||
|
}
|
||||||
|
return p_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Points();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect the infill_ordered polylines, in this order, from the back point to the next front point.
|
||||||
|
/// It uses only the boundary polygons to do so, and can't pass two times at the same place.
|
||||||
|
/// It avoid passing over the infill_ordered's polylines (preventing local over-extrusion).
|
||||||
|
/// return the connected polylines in polylines_out. Can output polygons (stored as polylines with first_point = last_point).
|
||||||
|
/// complexity: worst: N(infill_ordered.points) x N(boundary.points)
|
||||||
|
/// typical: N(infill_ordered) x ( N(boundary.points) + N(infill_ordered.points) )
|
||||||
|
void Fill::connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms) {
|
||||||
|
|
||||||
|
//TODO: fallback to the quick & dirty old algorithm when n(points) is too high.
|
||||||
|
Polylines polylines_frontier = to_polylines(((Polygons)boundary));
|
||||||
|
|
||||||
|
Polylines polylines_blocker;
|
||||||
|
coord_t clip_size = scale_(this->spacing) * 2;
|
||||||
|
for (const Polyline &polyline : infill_ordered) {
|
||||||
|
if (polyline.length() > 2.01 * clip_size) {
|
||||||
|
polylines_blocker.push_back(polyline);
|
||||||
|
polylines_blocker.back().clip_end(clip_size);
|
||||||
|
polylines_blocker.back().clip_start(clip_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//length between two lines
|
||||||
|
coordf_t ideal_length = (1 / params.density) * this->spacing;
|
||||||
|
|
||||||
|
Polylines polylines_connected_first;
|
||||||
|
bool first = true;
|
||||||
|
for (const Polyline &polyline : infill_ordered) {
|
||||||
|
if (!first) {
|
||||||
|
// Try to connect the lines.
|
||||||
|
Points &pts_end = polylines_connected_first.back().points;
|
||||||
|
const Point &last_point = pts_end.back();
|
||||||
|
const Point &first_point = polyline.points.front();
|
||||||
|
if (last_point.distance_to(first_point) < scale_(this->spacing) * 10) {
|
||||||
|
Points pts_frontier = get_frontier(polylines_frontier, last_point, first_point, scale_(this->spacing), polylines_blocker, (coord_t)scale_(ideal_length) * 2);
|
||||||
|
if (!pts_frontier.empty()) {
|
||||||
|
// The lines can be connected.
|
||||||
|
pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end());
|
||||||
|
pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The lines cannot be connected.
|
||||||
|
polylines_connected_first.emplace_back(std::move(polyline));
|
||||||
|
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Polylines polylines_connected;
|
||||||
|
first = true;
|
||||||
|
for (const Polyline &polyline : polylines_connected_first) {
|
||||||
|
if (!first) {
|
||||||
|
// Try to connect the lines.
|
||||||
|
Points &pts_end = polylines_connected.back().points;
|
||||||
|
const Point &last_point = pts_end.back();
|
||||||
|
const Point &first_point = polyline.points.front();
|
||||||
|
|
||||||
|
Polylines before = polylines_frontier;
|
||||||
|
Points pts_frontier = get_frontier(polylines_frontier, last_point, first_point, scale_(this->spacing), polylines_blocker);
|
||||||
|
if (!pts_frontier.empty()) {
|
||||||
|
// The lines can be connected.
|
||||||
|
pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end());
|
||||||
|
pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The lines cannot be connected.
|
||||||
|
polylines_connected.emplace_back(std::move(polyline));
|
||||||
|
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//try to link to nearest point if possible
|
||||||
|
for (size_t idx1 = 0; idx1 < polylines_connected.size(); idx1++) {
|
||||||
|
size_t min_idx = 0;
|
||||||
|
coordf_t min_length = 0;
|
||||||
|
bool switch_id1 = false;
|
||||||
|
bool switch_id2 = false;
|
||||||
|
for (size_t idx2 = idx1 + 1; idx2 < polylines_connected.size(); idx2++) {
|
||||||
|
double last_first = polylines_connected[idx1].last_point().distance_to_square(polylines_connected[idx2].first_point());
|
||||||
|
double first_first = polylines_connected[idx1].first_point().distance_to_square(polylines_connected[idx2].first_point());
|
||||||
|
double first_last = polylines_connected[idx1].first_point().distance_to_square(polylines_connected[idx2].last_point());
|
||||||
|
double last_last = polylines_connected[idx1].last_point().distance_to_square(polylines_connected[idx2].last_point());
|
||||||
|
double min = std::min(std::min(last_first, last_last), std::min(first_first, first_last));
|
||||||
|
if (min < min_length || min_length == 0) {
|
||||||
|
min_idx = idx2;
|
||||||
|
switch_id1 = (std::min(last_first, last_last) > std::min(first_first, first_last));
|
||||||
|
switch_id2 = (std::min(last_first, first_first) > std::min(last_last, first_last));
|
||||||
|
min_length = min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (min_idx > idx1 && min_idx < polylines_connected.size()){
|
||||||
|
Points pts_frontier = get_frontier(polylines_frontier,
|
||||||
|
switch_id1 ? polylines_connected[idx1].first_point() : polylines_connected[idx1].last_point(),
|
||||||
|
switch_id2 ? polylines_connected[min_idx].last_point() : polylines_connected[min_idx].first_point(),
|
||||||
|
scale_(this->spacing), polylines_blocker);
|
||||||
|
if (!pts_frontier.empty()) {
|
||||||
|
if (switch_id1) polylines_connected[idx1].reverse();
|
||||||
|
if (switch_id2) polylines_connected[min_idx].reverse();
|
||||||
|
Points &pts_end = polylines_connected[idx1].points;
|
||||||
|
pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end());
|
||||||
|
pts_end.insert(pts_end.end(), polylines_connected[min_idx].points.begin(), polylines_connected[min_idx].points.end());
|
||||||
|
polylines_connected.erase(polylines_connected.begin() + min_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//try to create some loops if possible
|
||||||
|
for (Polyline &polyline : polylines_connected) {
|
||||||
|
Points pts_frontier = get_frontier(polylines_frontier, polyline.last_point(), polyline.first_point(), scale_(this->spacing), polylines_blocker);
|
||||||
|
if (!pts_frontier.empty()) {
|
||||||
|
polyline.points.insert(polyline.points.end(), pts_frontier.begin(), pts_frontier.end());
|
||||||
|
polyline.points.insert(polyline.points.begin(), polyline.points.back());
|
||||||
|
}
|
||||||
|
polylines_out.emplace_back(polyline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
struct ContourPointData {
|
||||||
|
ContourPointData(float param) : param(param) {}
|
||||||
|
// Eucleidean position of the contour point along the contour.
|
||||||
|
float param = 0.f;
|
||||||
|
// Was the segment starting with this contour point extruded?
|
||||||
|
bool segment_consumed = false;
|
||||||
|
// Was this point extruded over?
|
||||||
|
bool point_consumed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify whether the contour from point idx_start to point idx_end could be taken (whether all segments along the contour were not yet extruded).
|
||||||
|
static bool could_take(const std::vector<ContourPointData> &contour_data, size_t idx_start, size_t idx_end)
|
||||||
|
{
|
||||||
|
for (size_t i = idx_start; i < idx_end; ) {
|
||||||
|
if (contour_data[i].segment_consumed || contour_data[i].point_consumed)
|
||||||
|
return false;
|
||||||
|
if (++ i == contour_data.size())
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
return ! contour_data[idx_end].point_consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect end of pl1 to the start of pl2 using the perimeter contour.
|
||||||
|
// The idx_start and idx_end are ordered so that the connecting polyline points will be taken with increasing indices.
|
||||||
|
static void take(Polyline &pl1, Polyline &&pl2, const Points &contour, std::vector<ContourPointData> &contour_data, size_t idx_start, size_t idx_end, bool reversed)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
size_t num_points_initial = pl1.points.size();
|
||||||
|
assert(idx_start != idx_end);
|
||||||
|
#endif /* NDEBUG */
|
||||||
|
|
||||||
|
{
|
||||||
|
// Reserve memory at pl1 for the connecting contour and pl2.
|
||||||
|
int new_points = int(idx_end) - int(idx_start) - 1;
|
||||||
|
if (new_points < 0)
|
||||||
|
new_points += int(contour.size());
|
||||||
|
pl1.points.reserve(pl1.points.size() + size_t(new_points) + pl2.points.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
contour_data[idx_start].point_consumed = true;
|
||||||
|
contour_data[idx_start].segment_consumed = true;
|
||||||
|
contour_data[idx_end ].point_consumed = true;
|
||||||
|
|
||||||
|
if (reversed) {
|
||||||
|
size_t i = (idx_end == 0) ? contour_data.size() - 1 : idx_end - 1;
|
||||||
|
while (i != idx_start) {
|
||||||
|
contour_data[i].point_consumed = true;
|
||||||
|
contour_data[i].segment_consumed = true;
|
||||||
|
pl1.points.emplace_back(contour[i]);
|
||||||
|
if (i == 0)
|
||||||
|
i = contour_data.size();
|
||||||
|
-- i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
size_t i = idx_start;
|
||||||
|
if (++ i == contour_data.size())
|
||||||
|
i = 0;
|
||||||
|
while (i != idx_end) {
|
||||||
|
contour_data[i].point_consumed = true;
|
||||||
|
contour_data[i].segment_consumed = true;
|
||||||
|
pl1.points.emplace_back(contour[i]);
|
||||||
|
if (++ i == contour_data.size())
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append(pl1.points, std::move(pl2.points));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an index of start of a segment and a point of the clipping point at distance from the end of polyline.
|
||||||
|
struct SegmentPoint {
|
||||||
|
// Segment index, defining a line <idx_segment, idx_segment + 1).
|
||||||
|
size_t idx_segment = std::numeric_limits<size_t>::max();
|
||||||
|
// Parameter of point in <0, 1) along the line <idx_segment, idx_segment + 1)
|
||||||
|
double t;
|
||||||
|
Vec2d point;
|
||||||
|
|
||||||
|
bool valid() const { return idx_segment != std::numeric_limits<size_t>::max(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline SegmentPoint clip_start_segment_and_point(const Points &polyline, double distance)
|
||||||
|
{
|
||||||
|
assert(polyline.size() >= 2);
|
||||||
|
assert(distance > 0.);
|
||||||
|
// Initialized to "invalid".
|
||||||
|
SegmentPoint out;
|
||||||
|
if (polyline.size() >= 2) {
|
||||||
|
const double d2 = distance * distance;
|
||||||
|
Vec2d pt_prev = polyline.front().cast<double>();
|
||||||
|
for (int i = 1; i < polyline.size(); ++ i) {
|
||||||
|
Vec2d pt = polyline[i].cast<double>();
|
||||||
|
Vec2d v = pt - pt_prev;
|
||||||
|
double l2 = v.squaredNorm();
|
||||||
|
if (l2 > d2) {
|
||||||
|
out.idx_segment = i;
|
||||||
|
out.t = distance / sqrt(l2);
|
||||||
|
out.point = pt + out.t * v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
distance -= sqrt(l2);
|
||||||
|
pt_prev = pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline SegmentPoint clip_end_segment_and_point(const Points &polyline, double distance)
|
||||||
|
{
|
||||||
|
assert(polyline.size() >= 2);
|
||||||
|
assert(distance > 0.);
|
||||||
|
// Initialized to "invalid".
|
||||||
|
SegmentPoint out;
|
||||||
|
if (polyline.size() >= 2) {
|
||||||
|
const double d2 = distance * distance;
|
||||||
|
Vec2d pt_next = polyline.back().cast<double>();
|
||||||
|
for (int i = int(polyline.size()) - 2; i >= 0; -- i) {
|
||||||
|
Vec2d pt = polyline[i].cast<double>();
|
||||||
|
Vec2d v = pt - pt_next;
|
||||||
|
double l2 = v.squaredNorm();
|
||||||
|
if (l2 > d2) {
|
||||||
|
out.idx_segment = i;
|
||||||
|
out.t = distance / sqrt(l2);
|
||||||
|
out.point = pt + out.t * v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
distance -= sqrt(l2);
|
||||||
|
pt_next = pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double segment_point_distance_squared(const Vec2d &p1a, const Vec2d &p1b, const Vec2d &p2)
|
||||||
|
{
|
||||||
|
const Vec2d v = p1b - p1a;
|
||||||
|
const Vec2d va = p2 - p1a;
|
||||||
|
const double l2 = v.squaredNorm();
|
||||||
|
if (l2 < EPSILON)
|
||||||
|
// p1a == p1b
|
||||||
|
return va.squaredNorm();
|
||||||
|
// Project p2 onto the (p1a, p1b) segment.
|
||||||
|
const double t = va.dot(v);
|
||||||
|
if (t < 0.)
|
||||||
|
return va.squaredNorm();
|
||||||
|
else if (t > l2)
|
||||||
|
return (p2 - p1b).squaredNorm();
|
||||||
|
return ((t / l2) * v - va).squaredNorm();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distance to the closest point of line.
|
||||||
|
static inline double min_distance_of_segments(const Vec2d &p1a, const Vec2d &p1b, const Vec2d &p2a, const Vec2d &p2b)
|
||||||
|
{
|
||||||
|
Vec2d v1 = p1b - p1a;
|
||||||
|
double l1_2 = v1.squaredNorm();
|
||||||
|
if (l1_2 < EPSILON)
|
||||||
|
// p1a == p1b: Return distance of p1a from the (p2a, p2b) segment.
|
||||||
|
return segment_point_distance_squared(p2a, p2b, p1a);
|
||||||
|
|
||||||
|
Vec2d v2 = p2b - p2a;
|
||||||
|
double l2_2 = v2.squaredNorm();
|
||||||
|
if (l2_2 < EPSILON)
|
||||||
|
// p2a == p2b: Return distance of p2a from the (p1a, p1b) segment.
|
||||||
|
return segment_point_distance_squared(p1a, p1b, p2a);
|
||||||
|
|
||||||
|
// Project p2a, p2b onto the (p1a, p1b) segment.
|
||||||
|
auto project_p2a_p2b_onto_seg_p1a_p1b = [](const Vec2d& p1a, const Vec2d& p1b, const Vec2d& p2a, const Vec2d& p2b, const Vec2d& v1, const double l1_2) {
|
||||||
|
Vec2d v1a2a = p2a - p1a;
|
||||||
|
Vec2d v1a2b = p2b - p1a;
|
||||||
|
double t1 = v1a2a.dot(v1);
|
||||||
|
double t2 = v1a2b.dot(v1);
|
||||||
|
if (t1 <= 0.) {
|
||||||
|
if (t2 <= 0.)
|
||||||
|
// Both p2a and p2b are left of v1.
|
||||||
|
return (((t1 < t2) ? p2b : p2a) - p1a).squaredNorm();
|
||||||
|
else if (t2 < l1_2)
|
||||||
|
// Project p2b onto the (p1a, p1b) segment.
|
||||||
|
return ((t2 / l1_2) * v1 - v1a2b).squaredNorm();
|
||||||
|
}
|
||||||
|
else if (t1 >= l1_2) {
|
||||||
|
if (t2 >= l1_2)
|
||||||
|
// Both p2a and p2b are right of v1.
|
||||||
|
return (((t1 < t2) ? p2a : p2b) - p1b).squaredNorm();
|
||||||
|
else if (t2 < l1_2)
|
||||||
|
// Project p2b onto the (p1a, p1b) segment.
|
||||||
|
return ((t2 / l1_2) * v1 - v1a2b).squaredNorm();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Project p1b onto the (p1a, p1b) segment.
|
||||||
|
double dist_min = ((t2 / l1_2) * v1 - v1a2a).squaredNorm();
|
||||||
|
if (t2 > 0. && t2 < l1_2)
|
||||||
|
dist_min = std::min(dist_min, ((t2 / l1_2) * v1 - v1a2b).squaredNorm());
|
||||||
|
return dist_min;
|
||||||
|
}
|
||||||
|
return std::numeric_limits<double>::max();
|
||||||
|
};
|
||||||
|
|
||||||
|
return std::min(
|
||||||
|
project_p2a_p2b_onto_seg_p1a_p1b(p1a, p1b, p2a, p2b, v1, l1_2),
|
||||||
|
project_p2a_p2b_onto_seg_p1a_p1b(p2a, p2b, p1a, p1b, v2, l2_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the segments of split boundary as consumed if they are very close to some of the infill line.
|
||||||
|
void mark_boundary_segments_touching_infill(
|
||||||
|
const std::vector<Points> &boundary,
|
||||||
|
std::vector<std::vector<ContourPointData>> &boundary_data,
|
||||||
|
const BoundingBox &boundary_bbox,
|
||||||
|
const Polylines &infill,
|
||||||
|
const double clip_distance,
|
||||||
|
const double distance_colliding)
|
||||||
|
{
|
||||||
|
EdgeGrid::Grid grid;
|
||||||
|
grid.set_bbox(boundary_bbox);
|
||||||
|
// Inflate the bounding box by a thick line width.
|
||||||
|
grid.create(boundary, clip_distance + scale_(10.));
|
||||||
|
|
||||||
|
struct Visitor {
|
||||||
|
Visitor(const EdgeGrid::Grid &grid, const std::vector<Points> &boundary, std::vector<std::vector<ContourPointData>> &boundary_data, const double dist2_max) :
|
||||||
|
grid(grid), boundary(boundary), boundary_data(boundary_data), dist2_max(dist2_max) {}
|
||||||
|
|
||||||
|
void init(const Vec2d &pt1, const Vec2d &pt2) {
|
||||||
|
this->pt1 = &pt1;
|
||||||
|
this->pt2 = &pt2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(coord_t iy, coord_t ix) {
|
||||||
|
// Called with a row and colum of the grid cell, which is intersected by a line.
|
||||||
|
auto cell_data_range = this->grid.cell_data_range(iy, ix);
|
||||||
|
for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) {
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
auto segment = this->grid.segment(*it_contour_and_segment);
|
||||||
|
const Vec2d seg_pt1 = segment.first.cast<double>();
|
||||||
|
const Vec2d seg_pt2 = segment.second.cast<double>();
|
||||||
|
if (min_distance_of_segments(seg_pt1, seg_pt2, *this->pt1, *this->pt2) < this->dist2_max) {
|
||||||
|
// Mark this boundary segment as touching the infill line.
|
||||||
|
ContourPointData&bdp = boundary_data[it_contour_and_segment->first][it_contour_and_segment->second];
|
||||||
|
bdp.segment_consumed = true;
|
||||||
|
// There is no need for checking seg_pt2 as it will be checked the next time.
|
||||||
|
if (segment_point_distance_squared(*this->pt1, *this->pt2, seg_pt1) < this->dist2_max)
|
||||||
|
bdp.point_consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Continue traversing the grid along the edge.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EdgeGrid::Grid &grid;
|
||||||
|
const std::vector<Points> &boundary;
|
||||||
|
std::vector<std::vector<ContourPointData>> &boundary_data;
|
||||||
|
// Maximum distance between the boundary and the infill line allowed to consider the boundary not touching the infill line.
|
||||||
|
const double dist2_max;
|
||||||
|
|
||||||
|
const Vec2d *pt1;
|
||||||
|
const Vec2d *pt2;
|
||||||
|
} visitor(grid, boundary, boundary_data, distance_colliding * distance_colliding);
|
||||||
|
|
||||||
|
for (const Polyline &polyline : infill) {
|
||||||
|
// Clip the infill polyline by the Eucledian distance along the polyline.
|
||||||
|
SegmentPoint start_point = clip_start_segment_and_point(polyline.points, clip_distance);
|
||||||
|
SegmentPoint end_point = clip_end_segment_and_point(polyline.points, clip_distance);
|
||||||
|
if (start_point.valid() && end_point.valid() &&
|
||||||
|
(start_point.idx_segment < end_point.idx_segment || (start_point.idx_segment == end_point.idx_segment && start_point.t < end_point.t))) {
|
||||||
|
// The clipped polyline is non-empty.
|
||||||
|
for (size_t point_idx = start_point.idx_segment; point_idx <= end_point.idx_segment; ++ point_idx) {
|
||||||
|
//FIXME extend the EdgeGrid to suport tracing a thick line.
|
||||||
|
#if 0
|
||||||
|
Point pt1, pt2;
|
||||||
|
Vec2d pt1d, pt2d;
|
||||||
|
if (point_idx == start_point.idx_segment) {
|
||||||
|
pt1d = start_point.point;
|
||||||
|
pt1 = pt1d.cast<coord_t>();
|
||||||
|
} else {
|
||||||
|
pt1 = polyline.points[point_idx];
|
||||||
|
pt1d = pt1.cast<double>();
|
||||||
|
}
|
||||||
|
if (point_idx == start_point.idx_segment) {
|
||||||
|
pt2d = end_point.point;
|
||||||
|
pt2 = pt1d.cast<coord_t>();
|
||||||
|
} else {
|
||||||
|
pt2 = polyline.points[point_idx];
|
||||||
|
pt2d = pt2.cast<double>();
|
||||||
|
}
|
||||||
|
visitor.init(pt1d, pt2d);
|
||||||
|
grid.visit_cells_intersecting_thick_line(pt1, pt2, distance_colliding, visitor);
|
||||||
|
#else
|
||||||
|
Vec2d pt1 = (point_idx == start_point.idx_segment) ? start_point.point : polyline.points[point_idx].cast<double>();
|
||||||
|
Vec2d pt2 = (point_idx == end_point .idx_segment) ? end_point .point : polyline.points[point_idx].cast<double>();
|
||||||
|
visitor.init(pt1, pt2);
|
||||||
|
// Simulate tracing of a thick line. This only works reliably if distance_colliding <= grid cell size.
|
||||||
|
Vec2d v = (pt2 - pt1).normalized() * distance_colliding;
|
||||||
|
Vec2d vperp(-v.y(), v.x());
|
||||||
|
Vec2d a = pt1 - v - vperp;
|
||||||
|
Vec2d b = pt1 + v - vperp;
|
||||||
|
grid.visit_cells_intersecting_line(a.cast<coord_t>(), b.cast<coord_t>(), visitor);
|
||||||
|
a = pt1 - v + vperp;
|
||||||
|
b = pt1 + v + vperp;
|
||||||
|
grid.visit_cells_intersecting_line(a.cast<coord_t>(), b.cast<coord_t>(), visitor);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fill::connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary_src, Polylines &polylines_out, const FillParams ¶ms)
|
||||||
|
{
|
||||||
|
assert(! infill_ordered.empty());
|
||||||
|
assert(! boundary_src.contour.points.empty());
|
||||||
|
|
||||||
|
BoundingBox bbox = get_extents(boundary_src.contour);
|
||||||
|
bbox.offset(SCALED_EPSILON);
|
||||||
|
|
||||||
|
// 1) Add the end points of infill_ordered to boundary_src.
|
||||||
|
std::vector<Points> boundary;
|
||||||
|
std::vector<std::vector<ContourPointData>> boundary_data;
|
||||||
|
boundary.assign(boundary_src.holes.size() + 1, Points());
|
||||||
|
boundary_data.assign(boundary_src.holes.size() + 1, std::vector<ContourPointData>());
|
||||||
|
// Mapping the infill_ordered end point to a (contour, point) of boundary.
|
||||||
|
std::vector<std::pair<size_t, size_t>> map_infill_end_point_to_boundary;
|
||||||
|
map_infill_end_point_to_boundary.assign(infill_ordered.size() * 2, std::pair<size_t, size_t>(std::numeric_limits<size_t>::max(), std::numeric_limits<size_t>::max()));
|
||||||
|
{
|
||||||
|
// Project the infill_ordered end points onto boundary_src.
|
||||||
|
std::vector<std::pair<EdgeGrid::Grid::ClosestPointResult, size_t>> intersection_points;
|
||||||
|
{
|
||||||
|
EdgeGrid::Grid grid;
|
||||||
|
grid.set_bbox(bbox);
|
||||||
|
grid.create(boundary_src, scale_(10.));
|
||||||
|
intersection_points.reserve(infill_ordered.size() * 2);
|
||||||
|
for (const Polyline &pl : infill_ordered)
|
||||||
|
for (const Point *pt : { &pl.points.front(), &pl.points.back() }) {
|
||||||
|
EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, SCALED_EPSILON);
|
||||||
|
if (cp.valid()) {
|
||||||
|
// The infill end point shall lie on the contour.
|
||||||
|
assert(cp.distance < 2.);
|
||||||
|
intersection_points.emplace_back(cp, (&pl - infill_ordered.data()) * 2 + (pt == &pl.points.front() ? 0 : 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::sort(intersection_points.begin(), intersection_points.end(), [](const std::pair<EdgeGrid::Grid::ClosestPointResult, size_t> &cp1, const std::pair<EdgeGrid::Grid::ClosestPointResult, size_t> &cp2) {
|
||||||
|
return cp1.first.contour_idx < cp2.first.contour_idx ||
|
||||||
|
(cp1.first.contour_idx == cp2.first.contour_idx &&
|
||||||
|
(cp1.first.start_point_idx < cp2.first.start_point_idx ||
|
||||||
|
(cp1.first.start_point_idx == cp2.first.start_point_idx && cp1.first.t < cp2.first.t)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
auto it = intersection_points.begin();
|
||||||
|
auto it_end = intersection_points.end();
|
||||||
|
for (size_t idx_contour = 0; idx_contour <= boundary_src.holes.size(); ++ idx_contour) {
|
||||||
|
const Polygon &contour_src = (idx_contour == 0) ? boundary_src.contour : boundary_src.holes[idx_contour - 1];
|
||||||
|
Points &contour_dst = boundary[idx_contour];
|
||||||
|
for (size_t idx_point = 0; idx_point < contour_src.points.size(); ++ idx_point) {
|
||||||
|
contour_dst.emplace_back(contour_src.points[idx_point]);
|
||||||
|
for (; it != it_end && it->first.contour_idx == idx_contour && it->first.start_point_idx == idx_point; ++ it) {
|
||||||
|
// Add these points to the destination contour.
|
||||||
|
const Vec2d pt1 = contour_src[idx_point].cast<double>();
|
||||||
|
const Vec2d pt2 = (idx_point + 1 == contour_src.size() ? contour_src.points.front() : contour_src.points[idx_point + 1]).cast<double>();
|
||||||
|
const Vec2d pt = lerp(pt1, pt2, it->first.t);
|
||||||
|
map_infill_end_point_to_boundary[it->second] = std::make_pair(idx_contour, contour_dst.size());
|
||||||
|
contour_dst.emplace_back(pt.cast<coord_t>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Parametrize the curve.
|
||||||
|
std::vector<ContourPointData> &contour_data = boundary_data[idx_contour];
|
||||||
|
contour_data.reserve(contour_dst.size());
|
||||||
|
contour_data.emplace_back(ContourPointData(0.f));
|
||||||
|
for (size_t i = 1; i < contour_dst.size(); ++ i)
|
||||||
|
contour_data.emplace_back(contour_data.back().param + (contour_dst[i].cast<float>() - contour_dst[i - 1].cast<float>()).norm());
|
||||||
|
contour_data.front().param = contour_data.back().param + (contour_dst.back().cast<float>() - contour_dst.front().cast<float>()).norm();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(boundary.size() == boundary_src.num_contours());
|
||||||
|
assert(std::all_of(map_infill_end_point_to_boundary.begin(), map_infill_end_point_to_boundary.end(),
|
||||||
|
[&boundary](const std::pair<size_t, size_t> &contour_point) {
|
||||||
|
return contour_point.first < boundary.size() && contour_point.second < boundary[contour_point.first].size();
|
||||||
|
}));
|
||||||
|
#endif /* NDEBUG */
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the points and segments of split boundary as consumed if they are very close to some of the infill line.
|
||||||
|
{
|
||||||
|
const double clip_distance = scale_(this->spacing);
|
||||||
|
const double distance_colliding = scale_(this->spacing);
|
||||||
|
mark_boundary_segments_touching_infill(boundary, boundary_data, bbox, infill_ordered, clip_distance, distance_colliding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chain infill_ordered.
|
||||||
|
//FIXME run the following loop through a heap sorted by the shortest perimeter edge that could be taken.
|
||||||
|
//length between two lines
|
||||||
|
//const float length_max = scale_(this->spacing);
|
||||||
|
const float length_max = scale_((2. / params.density) * this->spacing);
|
||||||
|
size_t idx_chain_last = 0;
|
||||||
|
for (size_t idx_chain = 1; idx_chain < infill_ordered.size(); ++ idx_chain) {
|
||||||
|
Polyline &pl1 = infill_ordered[idx_chain_last];
|
||||||
|
Polyline &pl2 = infill_ordered[idx_chain];
|
||||||
|
const std::pair<size_t, size_t> *cp1 = &map_infill_end_point_to_boundary[(idx_chain - 1) * 2 + 1];
|
||||||
|
const std::pair<size_t, size_t> *cp2 = &map_infill_end_point_to_boundary[idx_chain * 2];
|
||||||
|
const Points &contour = boundary[cp1->first];
|
||||||
|
std::vector<ContourPointData> &contour_data = boundary_data[cp1->first];
|
||||||
|
bool valid = false;
|
||||||
|
bool reversed = false;
|
||||||
|
if (cp1->first == cp2->first) {
|
||||||
|
// End points on the same contour. Try to connect them.
|
||||||
|
float param_lo = (cp1->second == 0) ? 0.f : contour_data[cp1->second].param;
|
||||||
|
float param_hi = (cp2->second == 0) ? 0.f : contour_data[cp2->second].param;
|
||||||
|
float param_end = contour_data.front().param;
|
||||||
|
if (param_lo > param_hi) {
|
||||||
|
std::swap(param_lo, param_hi);
|
||||||
|
std::swap(cp1, cp2);
|
||||||
|
reversed = true;
|
||||||
|
}
|
||||||
|
assert(param_lo >= 0.f && param_lo <= param_end);
|
||||||
|
assert(param_hi >= 0.f && param_hi <= param_end);
|
||||||
|
float dist1 = param_hi - param_lo;
|
||||||
|
float dist2 = param_lo + param_end - param_hi;
|
||||||
|
if (dist1 > dist2) {
|
||||||
|
std::swap(dist1, dist2);
|
||||||
|
std::swap(cp1, cp2);
|
||||||
|
reversed = ! reversed;
|
||||||
|
}
|
||||||
|
if (dist1 < length_max) {
|
||||||
|
// Try to connect the shorter path.
|
||||||
|
valid = could_take(contour_data, cp1->second, cp2->second);
|
||||||
|
// Try to connect the longer path.
|
||||||
|
if (! valid && dist2 < length_max) {
|
||||||
|
std::swap(cp1, cp2);
|
||||||
|
reversed = ! reversed;
|
||||||
|
valid = could_take(contour_data, cp1->second, cp2->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (valid)
|
||||||
|
take(pl1, std::move(pl2), contour, contour_data, cp1->second, cp2->second, reversed);
|
||||||
|
else if (++ idx_chain_last < idx_chain)
|
||||||
|
infill_ordered[idx_chain_last] = std::move(pl2);
|
||||||
|
}
|
||||||
|
infill_ordered.erase(infill_ordered.begin() + idx_chain_last + 1, infill_ordered.end());
|
||||||
|
append(polylines_out, std::move(infill_ordered));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
|
@ -111,6 +111,8 @@ protected:
|
||||||
|
|
||||||
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;
|
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;
|
||||||
|
|
||||||
|
void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance);
|
static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance);
|
||||||
|
|
||||||
|
|
|
@ -31,19 +31,26 @@ static inline double f(double x, double z_sin, double z_cos, bool vertical, bool
|
||||||
|
|
||||||
static inline Polyline make_wave(
|
static inline Polyline make_wave(
|
||||||
const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
|
const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
|
||||||
double z_cos, double z_sin, bool vertical)
|
double z_cos, double z_sin, bool vertical, bool flip)
|
||||||
{
|
{
|
||||||
std::vector<Vec2d> points = one_period;
|
std::vector<Vec2d> points = one_period;
|
||||||
double period = points.back()(0);
|
double period = points.back()(0);
|
||||||
points.pop_back();
|
if (width != period) // do not extend if already truncated
|
||||||
int n = points.size();
|
{
|
||||||
do {
|
points.reserve(one_period.size() * floor(width / period));
|
||||||
points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1)));
|
points.pop_back();
|
||||||
} while (points.back()(0) < width);
|
|
||||||
points.back()(0) = width;
|
int n = points.size();
|
||||||
|
do {
|
||||||
|
points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1)));
|
||||||
|
} while (points.back()(0) < width - EPSILON);
|
||||||
|
|
||||||
|
points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip)));
|
||||||
|
}
|
||||||
|
|
||||||
// and construct the final polyline to return:
|
// and construct the final polyline to return:
|
||||||
Polyline polyline;
|
Polyline polyline;
|
||||||
|
polyline.points.reserve(points.size());
|
||||||
for (auto& point : points) {
|
for (auto& point : points) {
|
||||||
point(1) += offset;
|
point(1) += offset;
|
||||||
point(1) = clamp(0., height, double(point(1)));
|
point(1) = clamp(0., height, double(point(1)));
|
||||||
|
@ -55,45 +62,56 @@ static inline Polyline make_wave(
|
||||||
return polyline;
|
return polyline;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip)
|
static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance)
|
||||||
{
|
{
|
||||||
std::vector<Vec2d> points;
|
std::vector<Vec2d> points;
|
||||||
double dx = M_PI_4; // very coarse spacing to begin with
|
double dx = M_PI_2; // exact coordinates on main inflexion lobes
|
||||||
double limit = std::min(2*M_PI, width);
|
double limit = std::min(2*M_PI, width);
|
||||||
for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too
|
points.reserve(ceil(limit / tolerance / 3));
|
||||||
x = std::min(x, limit);
|
|
||||||
points.emplace_back(Vec2d(x,f(x, z_sin,z_cos, vertical, flip)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// now we will check all internal points and in case some are too far from the line connecting its neighbours,
|
for (double x = 0.; x < limit - EPSILON; x += dx) {
|
||||||
// we will add one more point on each side:
|
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
||||||
const double tolerance = .1;
|
}
|
||||||
for (unsigned int i=1;i<points.size()-1;++i) {
|
points.emplace_back(Vec2d(limit, f(limit, z_sin, z_cos, vertical, flip)));
|
||||||
auto& lp = points[i-1]; // left point
|
|
||||||
auto& tp = points[i]; // this point
|
// piecewise increase in resolution up to requested tolerance
|
||||||
Vec2d lrv = tp - lp;
|
for(;;)
|
||||||
auto& rp = points[i+1]; // right point
|
{
|
||||||
// calculate distance of the point to the line:
|
size_t size = points.size();
|
||||||
double dist_mm = unscale<double>(scaleFactor) * std::abs(cross2(rp, lp) - cross2(rp - lp, tp)) / lrv.norm();
|
for (unsigned int i = 1;i < size; ++i) {
|
||||||
if (dist_mm > tolerance) { // if the difference from straight line is more than this
|
auto& lp = points[i-1]; // left point
|
||||||
double x = 0.5f * (points[i-1](0) + points[i](0));
|
auto& rp = points[i]; // right point
|
||||||
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
double x = lp(0) + (rp(0) - lp(0)) / 2;
|
||||||
x = 0.5f * (points[i+1](0) + points[i](0));
|
double y = f(x, z_sin, z_cos, vertical, flip);
|
||||||
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
Vec2d ip = {x, y};
|
||||||
// we added the points to the end, but need them all in order
|
if (std::abs(cross2(Vec2d(ip - lp), Vec2d(ip - rp))) > sqr(tolerance)) {
|
||||||
std::sort(points.begin(), points.end(), [](const Vec2d &lhs, const Vec2d &rhs){ return lhs < rhs; });
|
points.emplace_back(std::move(ip));
|
||||||
// decrement i so we also check the first newly added point
|
}
|
||||||
--i;
|
}
|
||||||
|
|
||||||
|
if (size == points.size())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// insert new points in order
|
||||||
|
std::sort(points.begin(), points.end(),
|
||||||
|
[](const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
|
static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
|
||||||
{
|
{
|
||||||
const double scaleFactor = scale_(line_spacing) / density_adjusted;
|
const double scaleFactor = scale_(line_spacing) / density_adjusted;
|
||||||
//scale factor for 5% : 8 712 388
|
|
||||||
// 1z = 10^-6 mm ?
|
// tolerance in scaled units. clamp the maximum tolerance as there's
|
||||||
|
// no processing-speed benefit to do so beyond a certain point
|
||||||
|
const double tolerance = std::min(line_spacing / 2, FillGyroid::PatternTolerance) / unscale<double>(scaleFactor);
|
||||||
|
|
||||||
|
//scale factor for 5% : 8 712 388
|
||||||
|
// 1z = 10^-6 mm ?
|
||||||
const double z = gridZ / scaleFactor;
|
const double z = gridZ / scaleFactor;
|
||||||
const double z_sin = sin(z);
|
const double z_sin = sin(z);
|
||||||
const double z_cos = cos(z);
|
const double z_cos = cos(z);
|
||||||
|
@ -109,16 +127,20 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double
|
||||||
std::swap(width,height);
|
std::swap(width,height);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Vec2d> one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time
|
std::vector<Vec2d> one_period_odd = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance); // creates one period of the waves, so it doesn't have to be recalculated all the time
|
||||||
|
flip = !flip; // even polylines are a bit shifted
|
||||||
|
std::vector<Vec2d> one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance);
|
||||||
Polylines result;
|
Polylines result;
|
||||||
|
|
||||||
for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates odd polylines
|
for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) {
|
||||||
result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
|
// creates odd polylines
|
||||||
|
result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
|
||||||
flip = !flip; // even polylines are a bit shifted
|
// creates even polylines
|
||||||
one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // updates the one period sample
|
y0 += M_PI;
|
||||||
for (double y0 = lower_bound + M_PI; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates even polylines
|
if (y0 < upper_bound + EPSILON) {
|
||||||
result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
|
result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -130,63 +152,49 @@ void FillGyroid::_fill_surface_single(
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines &polylines_out)
|
Polylines &polylines_out)
|
||||||
{
|
{
|
||||||
// no rotation is supported for this infill pattern (yet)
|
float infill_angle = this->angle + (CorrectionAngle * 2*M_PI) / 360.;
|
||||||
|
if(abs(infill_angle) >= EPSILON)
|
||||||
|
expolygon.rotate(-infill_angle);
|
||||||
|
|
||||||
BoundingBox bb = expolygon.contour.bounding_box();
|
BoundingBox bb = expolygon.contour.bounding_box();
|
||||||
// Density adjusted to have a good %of weight.
|
// Density adjusted to have a good %of weight.
|
||||||
double density_adjusted = std::max(0., params.density * 2.44);
|
double density_adjusted = std::max(0., params.density * DensityAdjust);
|
||||||
// Distance between the gyroid waves in scaled coordinates.
|
// Distance between the gyroid waves in scaled coordinates.
|
||||||
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
||||||
|
|
||||||
// align bounding box to a multiple of our grid module
|
// align bounding box to a multiple of our grid module
|
||||||
bb.merge(_align_to_grid(bb.min, Point(2.*M_PI*distance, 2.*M_PI*distance)));
|
bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
|
||||||
|
|
||||||
// generate pattern
|
// generate pattern
|
||||||
Polylines polylines = make_gyroid_waves(
|
Polylines polylines_square = make_gyroid_waves(
|
||||||
scale_(this->z),
|
scale_(this->z),
|
||||||
density_adjusted,
|
density_adjusted,
|
||||||
this->spacing,
|
this->spacing,
|
||||||
ceil(bb.size()(0) / distance) + 1.,
|
ceil(bb.size()(0) / distance) + 1.,
|
||||||
ceil(bb.size()(1) / distance) + 1.);
|
ceil(bb.size()(1) / distance) + 1.);
|
||||||
|
|
||||||
// move pattern in place
|
|
||||||
for (Polyline &polyline : polylines)
|
|
||||||
polyline.translate(bb.min(0), bb.min(1));
|
|
||||||
|
|
||||||
// clip pattern to boundaries
|
// shift the polyline to the grid origin
|
||||||
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
for (Polyline &pl : polylines_square)
|
||||||
|
pl.translate(bb.min);
|
||||||
|
|
||||||
// connect lines
|
Polylines polylines_chained = chain_polylines(intersection_pl(polylines_square, to_polygons(expolygon)));
|
||||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
|
||||||
ExPolygon expolygon_off;
|
size_t polylines_out_first_idx = polylines_out.size();
|
||||||
{
|
if (! polylines_chained.empty()) {
|
||||||
ExPolygons expolygons_off = offset_ex(expolygon, (float)SCALED_EPSILON);
|
// connect lines
|
||||||
if (! expolygons_off.empty()) {
|
if (params.dont_connect)
|
||||||
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
|
append(polylines_out, std::move(polylines_chained));
|
||||||
assert(expolygons_off.size() == 1);
|
else
|
||||||
std::swap(expolygon_off, expolygons_off.front());
|
this->connect_infill(std::move(polylines_chained), expolygon, polylines_out, params);
|
||||||
}
|
// remove too small bits (larger than longer)
|
||||||
}
|
polylines_out.erase(
|
||||||
bool first = true;
|
std::remove_if(polylines_out.begin() + polylines_out_first_idx, polylines_out.end(), [this](const Polyline &pl){ return pl.length() < scale_(this->spacing * 3); }),
|
||||||
for (Polyline &polyline : chain_polylines(std::move(polylines))) {
|
polylines_out.end());
|
||||||
if (! first) {
|
// new paths must be rotated back
|
||||||
// Try to connect the lines.
|
if (abs(infill_angle) >= EPSILON) {
|
||||||
Points &pts_end = polylines_out.back().points;
|
for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++ it)
|
||||||
const Point &first_point = polyline.points.front();
|
it->rotate(infill_angle);
|
||||||
const Point &last_point = pts_end.back();
|
}
|
||||||
// TODO: we should also check that both points are on a fill_boundary to avoid
|
|
||||||
// connecting paths on the boundaries of internal regions
|
|
||||||
// TODO: avoid crossing current infill path
|
|
||||||
if ((last_point - first_point).cast<double>().norm() <= 5 * distance &&
|
|
||||||
expolygon_off.contains(Line(last_point, first_point))) {
|
|
||||||
// Append the polyline.
|
|
||||||
pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The lines cannot be connected.
|
|
||||||
polylines_out.emplace_back(std::move(polyline));
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,17 @@ public:
|
||||||
// require bridge flow since most of this pattern hangs in air
|
// require bridge flow since most of this pattern hangs in air
|
||||||
virtual bool use_bridge_flow() const { return false; }
|
virtual bool use_bridge_flow() const { return false; }
|
||||||
|
|
||||||
|
// Correction applied to regular infill angle to maximize printing
|
||||||
|
// speed in default configuration (degrees)
|
||||||
|
static constexpr float CorrectionAngle = -45.;
|
||||||
|
|
||||||
|
// Density adjustment to have a good %of weight.
|
||||||
|
static constexpr double DensityAdjust = 2.44;
|
||||||
|
|
||||||
|
// Gyroid upper resolution tolerance (mm^-2)
|
||||||
|
static constexpr double PatternTolerance = 0.2;
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void _fill_surface_single(
|
virtual void _fill_surface_single(
|
||||||
const FillParams ¶ms,
|
const FillParams ¶ms,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue