mirror of
https://git.mirrors.martin98.com/https://github.com/SoftFever/OrcaSlicer.git
synced 2025-08-14 11:15:53 +08:00
Merge remote-tracking branch 'remotes/wavexx/gyroid_improvements' into gyroid_improvements
This commit is contained in:
commit
85e7c9cb28
@ -161,43 +161,38 @@ void Fill3DHoneycomb::_fill_surface_single(
|
||||
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it)
|
||||
it->translate(bb.min(0), bb.min(1));
|
||||
|
||||
// clip pattern to boundaries
|
||||
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
||||
// clip pattern to boundaries, keeping the polyline order & ordering the fragment to be able to join them easily
|
||||
Polylines polylines_chained;
|
||||
for (size_t idx_polyline = 0; idx_polyline < polylines.size(); ++idx_polyline) {
|
||||
Polyline &poly_to_cut = polylines[idx_polyline];
|
||||
Polylines polylines_to_sort = intersection_pl(Polylines() = { poly_to_cut }, (Polygons)expolygon);
|
||||
for (Polyline &polyline : polylines_to_sort) {
|
||||
//TODO: replace by closest_index_point()
|
||||
if (poly_to_cut.points.front().distance_to_square(polyline.points.front()) > poly_to_cut.points.front().distance_to_square(polyline.points.back())) {
|
||||
polyline.reverse();
|
||||
}
|
||||
}
|
||||
if (polylines_to_sort.size() > 1) {
|
||||
Point nearest = poly_to_cut.points.front();
|
||||
//Bubble sort
|
||||
for (size_t idx_sort = polylines_to_sort.size() - 1; idx_sort > 0; idx_sort--) {
|
||||
for (size_t idx_bubble = 0; idx_bubble < idx_sort; idx_bubble++) {
|
||||
if (polylines_to_sort[idx_bubble + 1].points.front().distance_to_square(nearest) < polylines_to_sort[idx_bubble].points.front().distance_to_square(nearest)) {
|
||||
iter_swap(polylines_to_sort.begin() + idx_bubble, polylines_to_sort.begin() + idx_bubble + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
polylines_chained.insert(polylines_chained.end(), polylines_to_sort.begin(), polylines_to_sort.end());
|
||||
}
|
||||
// connect lines if needed
|
||||
if (!polylines_chained.empty()) {
|
||||
if (params.dont_connect) {
|
||||
polylines_out.insert(polylines_out.end(), polylines_chained.begin(), polylines_chained.end());
|
||||
} else {
|
||||
this->connect_infill(polylines_chained, expolygon, polylines_out, params);
|
||||
}
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
ExPolygon expolygon_off;
|
||||
{
|
||||
ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON);
|
||||
if (! expolygons_off.empty()) {
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
Polylines chained = PolylineCollection::chained_path_from(
|
||||
std::move(polylines),
|
||||
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
|
||||
bool first = true;
|
||||
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
const Point &first_point = it_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(), it_polyline->points.begin(), it_polyline->points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// The lines cannot be connected.
|
||||
polylines_out.emplace_back(std::move(*it_polyline));
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,4 +130,373 @@ std::pair<float, Point> Fill::_infill_direction(const Surface *surface) const
|
||||
return std::pair<float, Point>(out_angle, out_shift);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// 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.
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
Points getFrontier(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(const 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 = getFrontier(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 = getFrontier(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 = getFrontier(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 = getFrontier(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);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -109,6 +109,8 @@ protected:
|
||||
|
||||
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;
|
||||
|
||||
void connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms);
|
||||
|
||||
public:
|
||||
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(
|
||||
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;
|
||||
double period = points.back()(0);
|
||||
if (width != period) // do not extend if already truncated
|
||||
{
|
||||
points.reserve(one_period.size() * floor(width / period));
|
||||
points.pop_back();
|
||||
|
||||
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);
|
||||
points.back()(0) = width;
|
||||
} 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:
|
||||
Polyline polyline;
|
||||
polyline.points.reserve(points.size());
|
||||
for (auto& point : points) {
|
||||
point(1) += offset;
|
||||
point(1) = clamp(0., height, double(point(1)));
|
||||
@ -55,43 +62,54 @@ static inline Polyline make_wave(
|
||||
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;
|
||||
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);
|
||||
for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too
|
||||
x = std::min(x, limit);
|
||||
points.emplace_back(Vec2d(x,f(x, z_sin,z_cos, vertical, flip)));
|
||||
points.reserve(ceil(limit / tolerance / 3));
|
||||
|
||||
for (double x = 0.; x < limit - EPSILON; x += dx) {
|
||||
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
||||
}
|
||||
points.emplace_back(Vec2d(limit, f(limit, z_sin, z_cos, vertical, flip)));
|
||||
|
||||
// piecewise increase in resolution up to requested tolerance
|
||||
for(;;)
|
||||
{
|
||||
size_t size = points.size();
|
||||
for (unsigned int i = 1;i < size; ++i) {
|
||||
auto& lp = points[i-1]; // left point
|
||||
auto& rp = points[i]; // right point
|
||||
double x = lp(0) + (rp(0) - lp(0)) / 2;
|
||||
double y = f(x, z_sin, z_cos, vertical, flip);
|
||||
Vec2d ip = {x, y};
|
||||
if (std::abs(cross2(Vec2d(ip - lp), Vec2d(ip - rp))) > sqr(tolerance)) {
|
||||
points.emplace_back(std::move(ip));
|
||||
}
|
||||
}
|
||||
|
||||
// now we will check all internal points and in case some are too far from the line connecting its neighbours,
|
||||
// we will add one more point on each side:
|
||||
const double tolerance = .1;
|
||||
for (unsigned int i=1;i<points.size()-1;++i) {
|
||||
auto& lp = points[i-1]; // left point
|
||||
auto& tp = points[i]; // this point
|
||||
Vec2d lrv = tp - lp;
|
||||
auto& rp = points[i+1]; // right point
|
||||
// calculate distance of the point to the line:
|
||||
double dist_mm = unscale<double>(scaleFactor) * std::abs(cross2(rp, lp) - cross2(rp - lp, tp)) / lrv.norm();
|
||||
if (dist_mm > tolerance) { // if the difference from straight line is more than this
|
||||
double x = 0.5f * (points[i-1](0) + points[i](0));
|
||||
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
||||
x = 0.5f * (points[i+1](0) + points[i](0));
|
||||
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
||||
// we added the points to the end, but need them all in order
|
||||
std::sort(points.begin(), points.end(), [](const Vec2d &lhs, const Vec2d &rhs){ return lhs < rhs; });
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
@ -109,16 +127,20 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double
|
||||
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;
|
||||
|
||||
for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates odd polylines
|
||||
result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
|
||||
|
||||
flip = !flip; // even polylines are a bit shifted
|
||||
one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // updates the one period sample
|
||||
for (double y0 = lower_bound + M_PI; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates even polylines
|
||||
result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
|
||||
for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) {
|
||||
// creates odd polylines
|
||||
result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
|
||||
// creates even polylines
|
||||
y0 += M_PI;
|
||||
if (y0 < upper_bound + EPSILON) {
|
||||
result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -130,65 +152,88 @@ void FillGyroid::_fill_surface_single(
|
||||
ExPolygon &expolygon,
|
||||
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();
|
||||
// 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.
|
||||
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
||||
|
||||
// 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
|
||||
Polylines polylines = make_gyroid_waves(
|
||||
Polylines polylines_square = make_gyroid_waves(
|
||||
scale_(this->z),
|
||||
density_adjusted,
|
||||
this->spacing,
|
||||
ceil(bb.size()(0) / 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, keeping the polyline order & ordering the fragment to be able to join them easily
|
||||
Polylines polylines_chained;
|
||||
for (size_t idx_polyline = 0; idx_polyline < polylines_square.size(); ++idx_polyline) {
|
||||
// shift the polyline to the grid origin
|
||||
Polyline &poly_to_cut = polylines_square[idx_polyline];
|
||||
poly_to_cut.translate(bb.min);
|
||||
|
||||
// clip pattern to boundaries
|
||||
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
||||
// intersect
|
||||
Polylines polylines_to_sort = intersection_pl(Polylines() = { poly_to_cut }, (Polygons)expolygon);
|
||||
for (Polyline &polyline : polylines_to_sort) {
|
||||
//TODO: replace by closest_index_point()
|
||||
if (idx_polyline % 2 == 0) {
|
||||
if (poly_to_cut.points.front().distance_to_square(polyline.points.front()) > poly_to_cut.points.front().distance_to_square(polyline.points.back())) {
|
||||
polyline.reverse();
|
||||
}
|
||||
} else {
|
||||
if (poly_to_cut.points.back().distance_to_square(polyline.points.front()) > poly_to_cut.points.back().distance_to_square(polyline.points.back())) {
|
||||
polyline.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (polylines_to_sort.size() > 1) {
|
||||
Point nearest = poly_to_cut.points.front();
|
||||
if (idx_polyline % 2 != 0) {
|
||||
nearest = poly_to_cut.points.back();
|
||||
}
|
||||
//Bubble sort
|
||||
for (size_t idx_sort = polylines_to_sort.size() - 1; idx_sort > 0; idx_sort--) {
|
||||
for (size_t idx_bubble = 0; idx_bubble < idx_sort; idx_bubble++) {
|
||||
if (polylines_to_sort[idx_bubble + 1].points.front().distance_to_square(nearest) < polylines_to_sort[idx_bubble].points.front().distance_to_square(nearest)) {
|
||||
iter_swap(polylines_to_sort.begin() + idx_bubble, polylines_to_sort.begin() + idx_bubble + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
polylines_chained.insert(polylines_chained.end(), polylines_to_sort.begin(), polylines_to_sort.end());
|
||||
}
|
||||
|
||||
size_t polylines_out_first_idx = polylines_out.size();
|
||||
if (!polylines_chained.empty()) {
|
||||
// connect lines
|
||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
ExPolygon expolygon_off;
|
||||
{
|
||||
ExPolygons expolygons_off = offset_ex(expolygon, (float)SCALED_EPSILON);
|
||||
if (! expolygons_off.empty()) {
|
||||
// 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());
|
||||
if (params.dont_connect) {
|
||||
polylines_out.insert(polylines_out.end(), polylines_chained.begin(), polylines_chained.end());
|
||||
} else {
|
||||
this->connect_infill(polylines_chained, expolygon, polylines_out, params);
|
||||
}
|
||||
}
|
||||
Polylines chained = PolylineCollection::chained_path_from(
|
||||
std::move(polylines),
|
||||
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chained) {
|
||||
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
|
||||
// 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;
|
||||
|
||||
//remove too small bits (larger than longer);
|
||||
for (size_t idx = polylines_out_first_idx; idx < polylines_out.size(); idx++) {
|
||||
if (polylines_out[idx].length() < scale_(this->spacing * 3)) {
|
||||
polylines_out.erase(polylines_out.begin() + idx);
|
||||
idx--;
|
||||
}
|
||||
}
|
||||
// The lines cannot be connected.
|
||||
polylines_out.emplace_back(std::move(polyline));
|
||||
first = false;
|
||||
|
||||
// new paths must be rotated back
|
||||
if(abs(infill_angle) >= EPSILON) {
|
||||
for (Polylines::iterator it = polylines_out.begin() + polylines_out_first_idx;
|
||||
it != polylines_out.end(); ++it) {
|
||||
it->rotate(infill_angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,17 @@ public:
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
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:
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
|
@ -124,6 +124,13 @@ public:
|
||||
double ccw_angle(const Point &p1, const Point &p2) const;
|
||||
Point projection_onto(const MultiPoint &poly) const;
|
||||
Point projection_onto(const Line &line) const;
|
||||
|
||||
double distance_to(const Point &point) const { return (point - *this).cast<double>().norm(); }
|
||||
double distance_to_square(const Point &point) const {
|
||||
double dx = (point.x() - this->x());
|
||||
double dy = (point.y() - this->y());
|
||||
return dx*dx + dy*dy;
|
||||
}
|
||||
};
|
||||
|
||||
namespace int128 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user