update libslic3r

This commit is contained in:
QIDI TECH
2025-08-04 10:13:51 +08:00
parent e3f49c2fb5
commit 8d4d60ec48
96 changed files with 4993 additions and 1903 deletions

View File

@@ -20,6 +20,8 @@ struct TravelPoint
Point point;
// Index of the polygon containing this point. A negative value indicates that the point is not on any border.
int border_idx;
// simplify_travel() doesn't remove this point.
bool do_not_remove = false;
};
struct Intersection
@@ -32,6 +34,8 @@ struct Intersection
Point point;
// Distance from the first point in the corresponding boundary
float distance;
// simplify_travel() doesn't remove this point.
bool do_not_remove = false;
};
struct ClosestLine
@@ -207,8 +211,8 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
const ClosestLine &cl_start = start_lines[cl_indices.first];
const ClosestLine &cl_end = end_lines[cl_indices.second];
std::vector<Intersection> new_intersections;
new_intersections.push_back({cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)});
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)});
new_intersections.push_back({cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true});
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true});
return new_intersections;
}
}
@@ -259,7 +263,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
if (cl_start_idx != std::numeric_limits<size_t>::max()) {
// If there is any ClosestLine around the start point closer to the Intersection, then replace this Intersection with ClosestLine.
const ClosestLine &cl_start = start_lines[cl_start_idx];
new_intersections.front() = {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)};
new_intersections.front() = {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true};
} else {
// Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the
// vector of intersections. This allows in some cases when it is more than one around ClosestLine start point chose that one which
@@ -267,7 +271,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
// use the first one, which is the closest one to the start point.
size_t start_closest_lines_idx = find_closest_line_with_same_boundary_idx(start_lines, new_intersections, true);
const ClosestLine &cl_start = (start_closest_lines_idx != std::numeric_limits<size_t>::max()) ? start_lines[start_closest_lines_idx] : start_lines.front();
new_intersections.insert(new_intersections.begin(),{cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)});
new_intersections.insert(new_intersections.begin(), {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true});
}
}
@@ -276,7 +280,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
if (cl_end_idx != std::numeric_limits<size_t>::max()) {
// If there is any ClosestLine around the end point closer to the Intersection, then replace this Intersection with ClosestLine.
const ClosestLine &cl_end = end_lines[cl_end_idx];
new_intersections.back() = {cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)};
new_intersections.back() = {cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true};
} else {
// Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the
// vector of intersections. This allows in some cases when it is more than one around ClosestLine end point chose that one which
@@ -284,7 +288,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
// use the first one, which is the closest one to the end point.
size_t end_closest_lines_idx = find_closest_line_with_same_boundary_idx(end_lines, new_intersections, false);
const ClosestLine &cl_end = (end_closest_lines_idx != std::numeric_limits<size_t>::max()) ? end_lines[end_closest_lines_idx] : end_lines.front();
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)});
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true});
}
}
return new_intersections;
@@ -443,21 +447,28 @@ static std::vector<TravelPoint> simplify_travel(const AvoidCrossingPerimeters::B
visitor.pt_current = &current_point;
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
if (travel[point_idx_2].point == current_point) {
next = travel[point_idx_2];
point_idx = point_idx_2;
continue;
}
if (!next.do_not_remove)
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
// Workaround for some issue in MSVC 19.29.30037 32-bit compiler.
#if defined(_WIN32) && !defined(_WIN64)
if (bool volatile do_not_remove = travel[point_idx_2].do_not_remove; do_not_remove) break;
#else
if (travel[point_idx_2].do_not_remove) break;
#endif
if (travel[point_idx_2].point == current_point) {
next = travel[point_idx_2];
point_idx = point_idx_2;
continue;
}
visitor.pt_next = &travel[point_idx_2].point;
boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
// Check if deleting point causes crossing a boundary
if (!visitor.intersect) {
next = travel[point_idx_2];
point_idx = point_idx_2;
visitor.pt_next = &travel[point_idx_2].point;
boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
// Check if deleting point causes crossing a boundary
if (!visitor.intersect) {
next = travel[point_idx_2];
point_idx = point_idx_2;
}
}
}
simplified_path.emplace_back(next);
}
@@ -517,146 +528,22 @@ static float get_perimeter_spacing_external(const Layer &layer)
return perimeter_spacing;
}
// Called by avoid_perimeters() and by simplify_travel_heuristics().
static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &init_boundary,
const AvoidCrossingPerimeters::Boundary &boundary,
const Point &start_point,
const Point &end_point,
const Layer &layer,
std::vector<TravelPoint> &result_out)
static float get_external_perimeter_width(const Layer &layer)
{
const Polygons &boundaries = boundary.boundaries;
const EdgeGrid::Grid &edge_grid = boundary.grid;
Point start = start_point, end = end_point;
// Find all intersections between boundaries and the line segment, sort them along the line segment.
std::vector<Intersection> intersections;
{
intersections.reserve(boundaries.size());
AllIntersectionsVisitor visitor(edge_grid, intersections, Line(start, end));
edge_grid.visit_cells_intersecting_line(start, end, visitor);
Vec2d dir = (end - start).cast<double>();
// if do not intersect due to the boundaries inner-offset, try to find the closest point to do intersect again!
if (intersections.empty()) {
// try to find the closest point on boundaries to start/end with distance less than extend_distance, which is noted as new start_point/end_point
auto search_radius = 1.5 * get_perimeter_spacing(layer);
const std::vector<ClosestLine> closest_line_to_start = get_closest_lines_in_radius(boundary.grid, start, search_radius);
const std::vector<ClosestLine> closest_line_to_end = get_closest_lines_in_radius(boundary.grid, end, search_radius);
if (!(closest_line_to_start.empty() && closest_line_to_end.empty())) {
auto new_start_point = closest_line_to_start.empty() ? start : closest_line_to_start.front().point;
auto new_end_point = closest_line_to_end.empty() ? end : closest_line_to_end.front().point;
dir = (new_end_point - new_start_point).cast<double>();
auto unit_direction = dir.normalized();
// out-offset new_start_point/new_end_point epsilon along the Line(new_start_point, new_end_point) for right intersection!
new_start_point = new_start_point - (unit_direction * double(coord_t(SCALED_EPSILON))).cast<coord_t>();
new_end_point = new_end_point + (unit_direction * double(coord_t(SCALED_EPSILON))).cast<coord_t>();
AllIntersectionsVisitor visitor(edge_grid, intersections, Line(new_start_point, new_end_point));
edge_grid.visit_cells_intersecting_line(new_start_point, new_end_point, visitor);
if (!intersections.empty()) {
start = new_start_point;
end = new_end_point;
}
}
size_t regions_count = 0;
float perimeter_width = 0.f;
for (const LayerRegion *layer_region : layer.regions())
if (layer_region != nullptr && !layer_region->slices.empty()) {
perimeter_width += float(layer_region->flow(frExternalPerimeter).scaled_width());
++regions_count;
}
for (Intersection &intersection : intersections) {
float dist_from_line_begin = (intersection.point - boundary.boundaries[intersection.border_idx][intersection.line_idx]).cast<float>().norm();
intersection.distance = boundary.boundaries_params[intersection.border_idx][intersection.line_idx] + dist_from_line_begin;
}
std::sort(intersections.begin(), intersections.end(), [dir](const auto &l, const auto &r) { return (r.point - l.point).template cast<double>().dot(dir) > 0.; });
// Search radius should always be at least equals to the value of offset used for computing boundaries.
const float search_radius = 2.f * get_perimeter_spacing(layer);
// When the offset is too big, then original travel doesn't have to cross created boundaries.
// These cases are fixed by calling extend_for_closest_lines.
intersections = extend_for_closest_lines(intersections, boundary, start, end, search_radius);
}
std::vector<TravelPoint> result;
result.push_back({start, -1});
#if 0
auto crossing_boundary_from_inside = [&boundary](const Point &start, const Intersection &intersection) {
const Polygon &poly = boundary.boundaries[intersection.border_idx];
Vec2d poly_line = Line(poly[intersection.line_idx], poly[(intersection.line_idx + 1) % poly.size()]).normal().cast<double>();
Vec2d intersection_vec = (intersection.point - start).cast<double>();
return poly_line.normalized().dot(intersection_vec.normalized()) >= 0;
};
#endif
for (auto it_first = intersections.begin(); it_first != intersections.end(); ++it_first) {
// The entry point to the boundary polygon
const Intersection &intersection_first = *it_first;
// if(!crossing_boundary_from_inside(start, intersection_first))
// continue;
// Skip the it_first from the search for the farthest exit point from the boundary polygon
auto it_last_item = std::make_reverse_iterator(it_first) - 1;
// Search for the farthest intersection different from it_first but with the same border_idx
auto it_second_r = std::find_if(intersections.rbegin(), it_last_item, [&intersection_first](const Intersection &intersection) {
return intersection_first.border_idx == intersection.border_idx;
});
// Append the first intersection into the path
size_t left_idx = intersection_first.line_idx;
size_t right_idx = intersection_first.line_idx + 1 == boundaries[intersection_first.border_idx].points.size() ? 0 : intersection_first.line_idx + 1;
// Offset of the polygon's point using get_middle_point_offset is used to simplify the calculation of intersection between the
// boundary and the travel. The appended point is translated in the direction of inward normal. This translation ensures that the
// appended point will be inside the polygon and not on the polygon border.
result.push_back({get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, coord_t(SCALED_EPSILON)), int(intersection_first.border_idx)});
// Check if intersection line also exit the boundary polygon
if (it_second_r != it_last_item) {
// Transform reverse iterator to forward
auto it_second = it_second_r.base() - 1;
// The exit point from the boundary polygon
const Intersection &intersection_second = *it_second;
Direction shortest_direction = get_shortest_direction(boundary, intersection_first, intersection_second,
boundary.boundaries_params[intersection_first.border_idx].back());
// Append the path around the border into the path
if (shortest_direction == Direction::Forward)
for (int line_idx = int(intersection_first.line_idx); line_idx != int(intersection_second.line_idx);
line_idx = line_idx + 1 < int(boundaries[intersection_first.border_idx].size()) ? line_idx + 1 : 0)
result.push_back({get_polygon_vertex_offset(boundaries[intersection_first.border_idx],
(line_idx + 1 == int(boundaries[intersection_first.border_idx].points.size())) ? 0 : (line_idx + 1), coord_t(SCALED_EPSILON)), int(intersection_first.border_idx)});
else
for (int line_idx = int(intersection_first.line_idx); line_idx != int(intersection_second.line_idx);
line_idx = line_idx - 1 >= 0 ? line_idx - 1 : int(boundaries[intersection_first.border_idx].size()) - 1)
result.push_back({get_polygon_vertex_offset(boundaries[intersection_second.border_idx], line_idx + 0, coord_t(SCALED_EPSILON)), int(intersection_first.border_idx)});
// Append the farthest intersection into the path
left_idx = intersection_second.line_idx;
right_idx = (intersection_second.line_idx >= (boundaries[intersection_second.border_idx].points.size() - 1)) ? 0 : (intersection_second.line_idx + 1);
result.push_back({get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, coord_t(SCALED_EPSILON)), int(intersection_second.border_idx)});
// Skip intersections in between
it_first = it_second;
}
}
result.push_back({end, -1});
#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
{
static int iRun = 0;
export_travel_to_svg(boundaries, Line(start, end), result, intersections, debug_out_path("AvoidCrossingPerimetersInner-initial-%d-%d.svg", layer.id(), iRun++));
}
#endif /* AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT */
if (! intersections.empty()) {
if (!init_boundary.boundaries.empty()) result = simplify_travel(init_boundary, result);
else result = simplify_travel(boundary, result);
}
#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
{
static int iRun = 0;
export_travel_to_svg(boundaries, Line(start, end), result, intersections,
debug_out_path("AvoidCrossingPerimetersInner-final-%d-%d.svg", layer.id(), iRun++));
}
#endif /* AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT */
append(result_out, std::move(result));
return intersections.size();
assert(perimeter_width >= 0.f);
if (regions_count != 0)
perimeter_width /= float(regions_count);
else
perimeter_width = get_default_perimeter_spacing(*layer.object());
return perimeter_width;
}
static size_t avoid_perimeters_inner(
@@ -738,7 +625,7 @@ static size_t avoid_perimeters_inner(
// boundary and the travel. The appended point is translated in the direction of inward normal. This translation ensures that the
// appended point will be inside the polygon and not on the polygon border.
result.push_back({get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, coord_t(SCALED_EPSILON)),
int(intersection_first.border_idx)});
int(intersection_first.border_idx), intersection_first.do_not_remove});
// Check if intersection line also exit the boundary polygon
if (it_second_r != it_last_item) {
@@ -766,7 +653,7 @@ static size_t avoid_perimeters_inner(
left_idx = intersection_second.line_idx;
right_idx = (intersection_second.line_idx >= (boundaries[intersection_second.border_idx].points.size() - 1)) ? 0 : (intersection_second.line_idx + 1);
result.push_back({get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, coord_t(SCALED_EPSILON)),
int(intersection_second.border_idx)});
int(intersection_second.border_idx), intersection_second.do_not_remove});
// Skip intersections in between
it_first = it_second;
}
@@ -800,31 +687,6 @@ static size_t avoid_perimeters_inner(
return intersections.size();
}
// Called by AvoidCrossingPerimeters::travel_to()
static size_t avoid_perimeters(const AvoidCrossingPerimeters::Boundary &init_boundary, const AvoidCrossingPerimeters::Boundary &boundary,
const Point &start,
const Point &end,
const Layer &layer,
Polyline &result_out)
{
// Travel line is completely or partially inside the bounding box.
std::vector<TravelPoint> path;
size_t num_intersections = avoid_perimeters_inner(init_boundary, boundary, start, end, layer, path);
result_out = to_polyline(path);
#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
{
static int iRun = 0;
export_travel_to_svg(boundary.boundaries, Line(start, end), path, {}, debug_out_path("AvoidCrossingPerimeters-final-%d-%d.svg", layer.id(), iRun ++));
}
#endif /* AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT */
return num_intersections;
}
// Called by AvoidCrossingPerimeters::travel_to()
static size_t avoid_perimeters(const AvoidCrossingPerimeters::Boundary &boundary,
const Point &start,
@@ -903,14 +765,14 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vec
return false;
}
static bool need_wipe(const GCode &gcodegen,
const EdgeGrid::Grid &grid_lslice,
const Line &original_travel,
const Polyline &result_travel,
const size_t intersection_count)
static bool need_wipe(const GCode &gcodegen,
const ExPolygons &lslices_offset,
const std::vector<BoundingBox> &lslices_offset_bboxes,
const EdgeGrid::Grid &grid_lslices_offset,
const Line &original_travel,
const Polyline &result_travel,
const size_t intersection_count)
{
const ExPolygons &lslices = gcodegen.layer()->lslices;
const std::vector<BoundingBox> &lslices_bboxes = gcodegen.layer()->lslices_bboxes;
bool z_lift_enabled = gcodegen.config().z_hop.get_at(gcodegen.writer().filament()->id()) > 0.;
bool wipe_needed = false;
@@ -920,16 +782,16 @@ static bool need_wipe(const GCode &gcodegen,
// The original layer is intersected with defined boundaries. Then it is necessary to make a detailed test.
// If the z-lift is enabled, then a wipe is needed when the original travel leads above the holes.
if (z_lift_enabled) {
if (any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, original_travel)) {
if (any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, original_travel)) {
// Check if original_travel and result_travel are not same.
// If both are the same, then it is possible to skip testing of result_travel
wipe_needed = !(result_travel.size() > 2 && result_travel.first_point() == original_travel.a && result_travel.last_point() == original_travel.b) &&
!any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, result_travel);
!any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, result_travel);
} else {
wipe_needed = true;
}
} else {
wipe_needed = !any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, result_travel);
wipe_needed = !any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, result_travel);
}
}
@@ -1149,57 +1011,84 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid,
// Polygon offset which ensures that if a polygon breaks up into several separate parts, the original polygon will be used in these places.
// ExPolygons are handled one by one so returned ExPolygons could intersect.
static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset)
static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset_dis)
{
double min_contour_width = 2. * offset + SCALED_EPSILON;
double search_radius = 2. * (offset + min_contour_width);
ExPolygons ex_poly_result = ex_polygons;
resample_expolygons(ex_poly_result, offset / 2, scaled<double>(0.5));
// try different offset_dis distances
const std::vector<double> min_contour_width_values = {offset_dis / 2., offset_dis, 2. * offset_dis + SCALED_EPSILON};
ExPolygons ex_poly_result = ex_polygons;
// remove too small holes from the ex_poly
for (ExPolygon &ex_poly : ex_poly_result) {
for (auto iter = ex_poly.holes.begin(); iter != ex_poly.holes.end();) {
auto out_offset_holes = offset(*iter, scale_(1.0f));
if (out_offset_holes.empty()) {
iter = ex_poly.holes.erase(iter);
} else {
++iter;
}
}
}
resample_expolygons(ex_poly_result, offset_dis / 2, scaled<double>(0.5));
for (ExPolygon &ex_poly : ex_poly_result) {
BoundingBox bbox(get_extents(ex_poly));
bbox.offset(SCALED_EPSILON);
EdgeGrid::Grid grid;
grid.set_bbox(bbox);
grid.create(ex_poly, coord_t(0.7 * search_radius));
std::vector<std::vector<float>> ex_poly_distances;
precompute_expolygon_distances(ex_poly, ex_poly_distances);
// Filter out expolygons smaller than 0.1mm^2
if (Vec2d bbox_size = bbox.size().cast<double>(); bbox_size.x() * bbox_size.y() < Slic3r::sqr(scale_(0.1f))) continue;
std::vector<std::vector<float>> offsets;
offsets.reserve(ex_poly.holes.size() + 1);
for (size_t idx_contour = 0; idx_contour <= ex_poly.holes.size(); ++idx_contour) {
const Polygon &poly = (idx_contour == 0) ? ex_poly.contour : ex_poly.holes[idx_contour - 1];
assert(poly.is_counter_clockwise() == (idx_contour == 0));
std::vector<float> distances = contour_distance(grid, ex_poly_distances[idx_contour], idx_contour, poly, offset, search_radius);
for (float &distance : distances) {
if (distance < min_contour_width)
distance = 0.f;
else if (distance > min_contour_width + 2. * offset)
distance = - float(offset);
else
distance = - (distance - float(min_contour_width)) / 2.f;
}
offsets.emplace_back(distances);
}
for (const double &min_contour_width : min_contour_width_values) {
const size_t min_contour_width_idx = &min_contour_width - &min_contour_width_values.front();
const double search_radius = 2. * (offset_dis + min_contour_width);
ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets);
// If variable_offset_inner_ex produces empty result, then original ex_polygon is used
if (offset_ex_poly.size() == 1) {
ex_poly = std::move(offset_ex_poly.front());
} else if (offset_ex_poly.size() > 1) {
// fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces
// tiny artefacts polygons, so these artefacts are removed.
double max_area = offset_ex_poly.front().area();
size_t max_area_idx = 0;
for (size_t poly_idx = 1; poly_idx < offset_ex_poly.size(); ++poly_idx) {
double area = offset_ex_poly[poly_idx].area();
if (max_area < area) {
max_area = area;
max_area_idx = poly_idx;
EdgeGrid::Grid grid;
grid.set_bbox(bbox);
grid.create(ex_poly, coord_t(0.7 * search_radius));
std::vector<std::vector<float>> ex_poly_distances;
precompute_expolygon_distances(ex_poly, ex_poly_distances);
std::vector<std::vector<float>> offsets;
offsets.reserve(ex_poly.holes.size() + 1);
for (size_t idx_contour = 0; idx_contour <= ex_poly.holes.size(); ++idx_contour) {
const Polygon &poly = (idx_contour == 0) ? ex_poly.contour : ex_poly.holes[idx_contour - 1];
assert(poly.is_counter_clockwise() == (idx_contour == 0));
std::vector<float> distances = contour_distance(grid, ex_poly_distances[idx_contour], idx_contour, poly, offset_dis, search_radius);
for (float &distance : distances) {
if (distance < min_contour_width)
distance = 0.0f;
else if (distance > min_contour_width + 2. * offset_dis)
distance = -float(offset_dis);
else
distance = -(distance - float(min_contour_width)) / 2.f;
}
offsets.emplace_back(distances);
}
ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets);
// If variable_offset_inner_ex produces empty result, then original ex_polygon is used
if (offset_ex_poly.size() == 1 && offset_ex_poly.front().holes.size() == ex_poly.holes.size()) {
ex_poly = std::move(offset_ex_poly.front());
break;
} else if ((min_contour_width_idx + 1) < min_contour_width_values.size()) {
continue; // Try the next round with bigger min_contour_width.
} else if (offset_ex_poly.size() == 1) {
ex_poly = std::move(offset_ex_poly.front());
break;
} else if (offset_ex_poly.size() > 1) {
// fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces
// tiny artefacts polygons, so these artefacts are removed.
double max_area = offset_ex_poly.front().area();
size_t max_area_idx = 0;
for (size_t poly_idx = 1; poly_idx < offset_ex_poly.size(); ++poly_idx) {
double area = offset_ex_poly[poly_idx].area();
if (max_area < area) {
max_area = area;
max_area_idx = poly_idx;
}
}
ex_poly = std::move(offset_ex_poly[max_area_idx]);
break;
}
ex_poly = std::move(offset_ex_poly[max_area_idx]);
}
}
return ex_poly_result;
@@ -1238,49 +1127,12 @@ static ExPolygons get_boundary(const Layer &layer, float perimeter_spacing)
if (surface.is_top()) top_layer_polygons.emplace_back(surface.expolygon);
top_layer_polygons = union_ex(top_layer_polygons);
return diff_ex(boundary, offset_ex(top_layer_polygons, -perimeter_offset));
return diff_ex(boundary, offset_ex(top_layer_polygons, -1.2 * perimeter_offset));
}
return boundary;
}
// called by AvoidCrossingPerimeters::travel_to()
static ExPolygons get_slice_boundary_internal(const Layer &layer)
{
auto const *support_layer = dynamic_cast<const SupportLayer *>(&layer);
ExPolygons boundary = layer.lslices;
if(support_layer) {
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
append(boundary, support_layer->support_islands);
#endif
auto *layer_below = layer.object()->get_first_layer_bellow_printz(layer.print_z, EPSILON);
if (layer_below)
append(boundary, layer_below->lslices);
// After calling inner_offset it is necessary to call union_ex because of the possibility of intersection ExPolygons
boundary = union_ex(boundary);
}
// Collect all top layers that will not be crossed.
size_t polygons_count = 0;
for (const LayerRegion *layer_region : layer.regions())
for (const Surface &surface : layer_region->fill_surfaces.surfaces)
if (surface.is_top()) ++polygons_count;
if (polygons_count > 0) {
ExPolygons top_layer_polygons;
top_layer_polygons.reserve(polygons_count);
for (const LayerRegion *layer_region : layer.regions())
for (const Surface &surface : layer_region->fill_surfaces.surfaces)
if (surface.is_top()) top_layer_polygons.emplace_back(surface.expolygon);
top_layer_polygons = union_ex(top_layer_polygons);
return diff_ex(boundary, top_layer_polygons);
// return diff_ex(boundary, offset_ex(top_layer_polygons, -perimeter_offset));
}
return boundary;
}
// called by AvoidCrossingPerimeters::travel_to()
static Polygons get_boundary_external(const Layer &layer)
{
@@ -1369,7 +1221,7 @@ static void init_boundary(AvoidCrossingPerimeters::Boundary *boundary, Polygons
for (const auto& merge_point : merge_poins) {
bbox.merge(merge_point);
}
bbox.offset(SCALED_EPSILON);
bbox.offset(bbox.radius());
boundary->bbox = BoundingBoxf(bbox.min.cast<double>(), bbox.max.cast<double>());
boundary->grid.set_bbox(bbox);
// FIXME 1mm grid?
@@ -1377,24 +1229,6 @@ static void init_boundary(AvoidCrossingPerimeters::Boundary *boundary, Polygons
init_boundary_distances(boundary);
}
void init_boundary(AvoidCrossingPerimeters::Boundary *boundary, Polygons &&boundary_polygons, BoundingBox&& ref_bbox, const std::vector<Point>& merge_poins)
{
boundary->clear();
boundary->boundaries = std::move(boundary_polygons);
BoundingBox bbox(ref_bbox);
for (const auto& merge_point : merge_poins) {
bbox.merge(merge_point);
}
bbox.offset(SCALED_EPSILON);
boundary->bbox = BoundingBoxf(bbox.min.cast<double>(), bbox.max.cast<double>());
boundary->grid.set_bbox(bbox);
// FIXME 1mm grid?
boundary->grid.create(boundary->boundaries, coord_t(scale_(1.)));
init_boundary_distances(boundary);
}
// Plan travel, which avoids perimeter crossings by following the boundaries of the layer.
Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled)
{
@@ -1413,33 +1247,22 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
const ExPolygons &lslices = gcodegen.layer()->lslices;
const std::vector<BoundingBox> &lslices_bboxes = gcodegen.layer()->lslices_bboxes;
bool is_support_layer = (dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr);
if (!use_external && (is_support_layer || (!lslices.empty() && !any_expolygon_contains(lslices, lslices_bboxes, m_grid_lslice, travel)))) {
if (m_lslice_internal.boundaries.empty()) {
init_boundary(&m_lslice_internal, to_polygons(get_slice_boundary_internal(*gcodegen.layer())), {start, end});
} else if (!(m_lslice_internal.bbox.contains(startf) && m_lslice_internal.bbox.contains(endf))) {
// check if start and end are in bbox
m_lslice_internal.clear();
init_boundary(&m_lslice_internal, to_polygons(get_slice_boundary_internal(*gcodegen.layer())), {start, end});
}
if (!use_external && (is_support_layer || (!m_lslices_offset.empty() && !any_expolygon_contains(m_lslices_offset, m_lslices_offset_bboxes, m_grid_lslice, travel)))) {
// Initialize m_internal only when it is necessary.
if (m_internal.boundaries.empty()) {
init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer(), get_perimeter_spacing(*gcodegen.layer()))), get_extents(m_lslice_internal.boundaries),
{start, end});
init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer(), get_perimeter_spacing(*gcodegen.layer()))), {start, end});
} else if (!(m_internal.bbox.contains(startf) && m_internal.bbox.contains(endf))) {
// check if start and end are in bbox, if not, merge start and end points to bbox
m_internal.clear();
init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer(), get_perimeter_spacing(*gcodegen.layer()))), get_extents(m_lslice_internal.boundaries),
{start, end});
init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer(), get_perimeter_spacing(*gcodegen.layer()))), {start, end});
}
if (!m_internal.boundaries.empty()) {
travel_intersection_count = avoid_perimeters(m_lslice_internal, m_internal, start, end, *gcodegen.layer(), result_pl);
travel_intersection_count = avoid_perimeters(m_internal, start, end, *gcodegen.layer(), result_pl);
result_pl.points.front() = start;
result_pl.points.back() = end;
}
}
else if(use_external) {
} else if (use_external) {
// Initialize m_external only when exist any external travel for the current layer.
if (m_external.boundaries.empty()) {
init_boundary(&m_external, get_boundary_external(*gcodegen.layer()), {start, end});
@@ -1449,18 +1272,16 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
init_boundary(&m_external, get_boundary_external(*gcodegen.layer()), {start, end});
}
// Trim the travel line by the bounding box.
if (!m_external.boundaries.empty())
{
travel_intersection_count = avoid_perimeters(m_external, start, end, *gcodegen.layer(), result_pl);
result_pl.points.front() = start;
result_pl.points.back() = end;
}
}
if(result_pl.empty()) {
// Travel line is completely outside the bounding box.
result_pl = {start, end};
@@ -1487,7 +1308,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
} else if (max_detour_length_exceeded) {
*could_be_wipe_disabled = false;
} else
*could_be_wipe_disabled = !need_wipe(gcodegen, m_grid_lslice, travel, result_pl, travel_intersection_count);
*could_be_wipe_disabled = !need_wipe(gcodegen, m_lslices_offset, m_lslices_offset_bboxes, m_grid_lslice, travel, result_pl, travel_intersection_count);
return result_pl;
}
@@ -1497,15 +1318,23 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
void AvoidCrossingPerimeters::init_layer(const Layer &layer)
{
m_internal.clear();
m_lslice_internal.clear();
m_external.clear();
m_lslices_offset.clear();
m_lslices_offset_bboxes.clear();
for (auto coeff : {0.6f, 0.5f, 0.45f}) {
m_lslices_offset = offset_ex(layer.lslices, -get_external_perimeter_width(layer) * coeff);
if (!m_lslices_offset.empty()) break;
}
m_lslices_offset_bboxes.reserve(m_lslices_offset.size());
for (const auto &ex_polygon : m_lslices_offset) m_lslices_offset_bboxes.emplace_back(get_extents(ex_polygon));
BoundingBox bbox_slice(get_extents(layer.lslices));
bbox_slice.offset(SCALED_EPSILON);
m_grid_lslice.set_bbox(bbox_slice);
//FIXME 1mm grid?
m_grid_lslice.create(layer.lslices, coord_t(scale_(1.)));
m_grid_lslice.create(m_lslices_offset, coord_t(scale_(1.)));
}
#if 0

View File

@@ -17,6 +17,7 @@ class AvoidCrossingPerimeters
public:
// Routing around the objects vs. inside a single object.
void use_external_mp(bool use = true) { m_use_external_mp = use; };
bool used_external_mp() { return m_use_external_mp; }
void use_external_mp_once() { m_use_external_mp_once = true; }
bool used_external_mp_once() { return m_use_external_mp_once; }
void disable_once() { m_disabled_once = true; }
@@ -58,12 +59,13 @@ private:
// we enable it by default for the first travel move in print
bool m_disabled_once { true };
// Lslices offseted by half an external perimeter width. Used for detection if line or polyline is inside of any polygon.
ExPolygons m_lslices_offset;
std::vector<BoundingBox> m_lslices_offset_bboxes;
// Used for detection of line or polyline is inside of any polygon.
EdgeGrid::Grid m_grid_lslice;
// Store all needed data for travels inside object
Boundary m_internal;
// Store all needed data for travels inside object without inner offset
Boundary m_lslice_internal;
// Store all needed data for travels outside object
Boundary m_external;
};

View File

@@ -447,7 +447,7 @@ std::string GCodeEditor::write_layer_gcode(
//QDS
if (additional_fan_speed_new != m_additional_fan_speed) {
m_additional_fan_speed = additional_fan_speed_new;
if (type == SetFanType::sfImmediatelyApply)
if (type == SetFanType::sfImmediatelyApply && m_config.auxiliary_fan.value)
new_gcode += GCodeWriter::set_additional_fan(m_additional_fan_speed);
else if (type == SetFanType::sfChangingLayer)
this->m_set_addition_fan_changing_layer = true;
@@ -519,7 +519,7 @@ std::string GCodeEditor::write_layer_gcode(
//QDS: force to write a fan speed command again
if (m_current_fan_speed != -1)
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed);
if (m_additional_fan_speed != -1)
if (m_additional_fan_speed != -1 && m_config.auxiliary_fan.value)
new_gcode += GCodeWriter::set_additional_fan(m_additional_fan_speed);
} else if (line->type & CoolingLine::TYPE_SET_FAN_CHANGING_LAYER) {
//QDS: check whether fan speed need to changed when change layer
@@ -527,7 +527,7 @@ std::string GCodeEditor::write_layer_gcode(
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed);
m_set_fan_changing_layer = false;
}
if (m_additional_fan_speed != -1 && m_set_addition_fan_changing_layer) {
if (m_additional_fan_speed != -1 && m_set_addition_fan_changing_layer && m_config.auxiliary_fan.value) {
new_gcode += GCodeWriter::set_additional_fan(m_additional_fan_speed);
m_set_addition_fan_changing_layer = false;
}

View File

@@ -334,10 +334,11 @@ static void planner_forward_pass_kernel(GCodeProcessor::TimeBlock& prev, GCodePr
// full speed change within the block, we need to adjust the entry speed accordingly. Entry
// speeds have already been reset, maximized, and reverse planned by reverse planner.
// If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck.
// <20><><EFBFBD><EFBFBD>ǰһ<C7B0><D2BB>block<63><6B><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD><C9BC>٣<EFBFBD><D9A3><EFBFBD>ôҪ<C3B4><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ٵ<EFBFBD><D9B5><EFBFBD><EFBFBD>ٶȣ<D9B6><C8A3><EFBFBD><EFBFBD>¼<EFBFBD><C2BC>㵱ǰblock<63><6B>entry<72>ٶ<EFBFBD>
if (!prev.flags.nominal_length) {
if (prev.feedrate_profile.entry < curr.feedrate_profile.entry) {
float entry_speed = std::min(curr.feedrate_profile.entry, max_allowable_speed(-prev.acceleration, prev.feedrate_profile.entry, prev.distance));
// Check for junction speed change
if (curr.feedrate_profile.entry != entry_speed) {
curr.feedrate_profile.entry = entry_speed;
@@ -379,7 +380,7 @@ static void recalculate_trapezoids(std::vector<GCodeProcessor::TimeBlock>& block
// Recalculate if current block entry or exit junction speed has changed.
if (curr->flags.recalculate || next->flags.recalculate) {
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
GCodeProcessor::TimeBlock block = *curr;
GCodeProcessor::TimeBlock& block = *curr;
block.feedrate_profile.exit = next->feedrate_profile.entry;
block.calculate_trapezoid();
curr->trapezoid = block.trapezoid;
@@ -390,7 +391,7 @@ static void recalculate_trapezoids(std::vector<GCodeProcessor::TimeBlock>& block
// Last/newest block in buffer. Always recalculated.
if (next != nullptr) {
GCodeProcessor::TimeBlock block = *next;
GCodeProcessor::TimeBlock& block = *next;
block.feedrate_profile.exit = next->safe_feedrate;
block.calculate_trapezoid();
next->trapezoid = block.trapezoid;
@@ -534,7 +535,8 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
if (in.f == nullptr)
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": before process %1%") % filename.c_str();
std::string filename_safe = PathSanitizer::sanitize(filename);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": before process %1%") % filename_safe;
// temporary file to contain modified gcode
std::string filename_in = filename;
std::string filename_out = filename + ".postprocess";
@@ -675,7 +677,8 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
if (s_IsQDTPrinter) {
// Klipper estimator
sprintf(buf, "; estimated printing time (normal mode) = %s\n",
get_time_dhms(machine.time).c_str());
//y68
get_time_dhms(machine.time + 600.0f).c_str());
ret += buf;
}
else {
@@ -1151,6 +1154,8 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
filename_in = filename_out; // filename_out is opened in read|write mode. During second process ,we ues filename_out as input
filename_out = filename + ".postprocessed";
std::string filename_in_safe = PathSanitizer::sanitize(filename_in);
std::string filename_out_safe = PathSanitizer::sanitize(filename_out);
FilePtr new_out = boost::nowide::fopen(filename_out.c_str(), "wb");
std::fseek(out.f, 0, SEEK_SET); // move to start of the file and start reading gcode as in
@@ -1164,17 +1169,17 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
in.close();
out.close();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": after process %1%") % filename.c_str();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": after process %1%") % filename_safe;
if (boost::nowide::remove(filename_in.c_str()) != 0) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Failed to remove the temporary G-code file %1%") % filename_in.c_str();
throw Slic3r::RuntimeError(std::string("Failed to remove the temporary G-code file ") + filename_in + '\n' +
"Is " + filename_in + " locked?" + '\n');
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Failed to remove the temporary G-code file %1%") % filename_in_safe;
throw Slic3r::RuntimeError(std::string("Failed to remove the temporary G-code file ") + filename_in_safe + '\n' +
"Is " + filename_in_safe + " locked?" + '\n');
}
if (rename_file(filename_out, filename)) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Failed to rename the output G-code file from %1% to %2%") % filename_out.c_str() % filename.c_str();
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + filename_out + " to " + filename + '\n' +
"Is " + filename_out + " locked?" + '\n');
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Failed to rename the output G-code file from %1% to %2%") % filename_out_safe % filename_safe;
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + filename_out_safe + " to " + filename_safe + '\n' +
"Is " + filename_out_safe + " locked?" + '\n');
}
}
@@ -1607,7 +1612,9 @@ void GCodeProcessor::register_commands()
{"VG1", [this](const GCodeReader::GCodeLine& line) { process_VG1(line); }},
{"VM104", [this](const GCodeReader::GCodeLine& line) { process_VM104(line); }},
{"VM109", [this](const GCodeReader::GCodeLine& line) { process_VM109(line); }}
{"VM109", [this](const GCodeReader::GCodeLine& line) { process_VM109(line); }},
{"M622", [this](const GCodeReader::GCodeLine& line) { process_M622(line);}},
{"M623", [this](const GCodeReader::GCodeLine& line) { process_M623(line);}}
};
std::unordered_set<std::string>early_quit_commands = {
@@ -1795,6 +1802,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_time_processor.filament_load_times = static_cast<float>(config.machine_load_filament_time.value);
m_time_processor.filament_unload_times = static_cast<float>(config.machine_unload_filament_time.value);
m_time_processor.extruder_change_times = static_cast<float>(config.machine_switch_extruder_time.value);
m_time_processor.prepare_compensation_time = static_cast<float>(config.machine_prepare_compensation_time.value);
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
@@ -2058,6 +2066,10 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
if (machine_switch_extruder_time != nullptr)
m_time_processor.extruder_change_times = static_cast<float>(machine_switch_extruder_time->value);
const ConfigOptionFloat* machine_prepare_compensation_time = config.option<ConfigOptionFloat>("machine_prepare_compensation_time");
if (machine_prepare_compensation_time != nullptr)
m_time_processor.prepare_compensation_time = static_cast<float>(machine_prepare_compensation_time->value);
if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfKlipper) {
const ConfigOptionFloatsNullable* machine_max_acceleration_x = config.option<ConfigOptionFloatsNullable>("machine_max_acceleration_x");
if (machine_max_acceleration_x != nullptr)
@@ -2099,7 +2111,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
if (machine_max_jerk_y != nullptr)
m_time_processor.machine_limits.machine_max_jerk_y.values = machine_max_jerk_y->values;
const ConfigOptionFloatsNullable* machine_max_jerk_z = config.option<ConfigOptionFloatsNullable>("machine_max_jerkz");
const ConfigOptionFloatsNullable* machine_max_jerk_z = config.option<ConfigOptionFloatsNullable>("machine_max_jerk_z");
if (machine_max_jerk_z != nullptr)
m_time_processor.machine_limits.machine_max_jerk_z.values = machine_max_jerk_z->values;
@@ -2275,6 +2287,16 @@ static inline const char* remove_eols(const char *begin, const char *end) {
return end;
}
DynamicConfig GCodeProcessor::export_config_for_render() const
{
DynamicConfig config;
config.set_key_value("filament_colour", new ConfigOptionStrings(m_parser.get_config().filament_colour.values));
config.set_key_value("filament_is_support", new ConfigOptionBools(m_parser.get_config().filament_is_support.values));
config.set_key_value("filament_type", new ConfigOptionStrings(m_parser.get_config().filament_type.values));
config.set_key_value("filament_map", new ConfigOptionInts(m_parser.get_config().filament_map.values));
return config;
}
// Load a G-code into a stand-alone G-code viewer.
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
void GCodeProcessor::process_file(const std::string& filename, std::function<void()> cancel_callback)
@@ -3783,6 +3805,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
TimeMachine::State& prev = machine.prev;
std::vector<TimeBlock>& blocks = machine.blocks;
// m_feedrate <20><>gcode<64>н<EFBFBD><D0BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ã<EFBFBD><C3A3>ڱ<EFBFBD>֤<EFBFBD><D6A4>С<EFBFBD>ٶ<EFBFBD><D9B6><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>¸<EFBFBD><C2B8><EFBFBD>feedrate
curr.feedrate = (delta_pos[E] == 0.0f) ?
minimum_travel_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate) :
minimum_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate);
@@ -3800,15 +3823,23 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
//QDS: don't calculate travel time into extrusion path, except travel inside start and end gcode.
block.role = (type != EMoveType::Travel || m_extrusion_role == erCustom) ? m_extrusion_role : erNone;
block.distance = distance;
block.move_id = m_result.moves.size();
block.move_id = m_result.moves.size(); // new move will be pushed back at the end of the func, so use size of move as idx
block.g1_line_id = m_g1_line_id;
block.layer_id = std::max<unsigned int>(1, m_layer_id);
block.flags.prepare_stage = m_processing_start_custom_gcode;
// calculates block acceleration<6F><6E><EFBFBD><EFBFBD><EFBFBD>㵱ǰblock<63><6B><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܵ<EFBFBD><DCB5><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>ٶ<EFBFBD>
float acceleration =
(type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
(is_extrusion_only_move(delta_pos) ?
get_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)));
//QDS: limite the cruise according to centripetal acceleration
//Only need to handle when both prev and curr segment has movement in x-y plane
if ((prev.exit_direction(0) != 0.0f || prev.exit_direction(1) != 0.0f) &&
(curr.enter_direction(0) != 0.0f || curr.enter_direction(1) != 0.0f)) {
(curr.enter_direction(0) != 0.0f || curr.enter_direction(1) != 0.0f) &&
!is_extrusion_only_move(delta_pos)) {
Vec3f v1 = prev.exit_direction;
v1(2, 0) = 0.0f;
v1.normalize();
@@ -3826,12 +3857,12 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
float angle = float(atan2(double(cross), double(dot)));
float sin_theta_2 = sqrt((1.0f - cos(angle)) * 0.5f);
float r = sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y])) * 0.5 / sin_theta_2;
float acc = get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i));
curr.feedrate = std::min(curr.feedrate, sqrt(acc * r));
curr.feedrate = std::min(curr.feedrate, sqrt(acceleration * r));
}
}
// calculates block cruise feedrate
// <20>ٳ<EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>ϵ<EFBFBD><CFB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㵱ǰblock<63><6B><EFBFBD><EFBFBD><EFBFBD>ܵ<EFBFBD><DCB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ٶ<EFBFBD>
float min_feedrate_factor = 1.0f;
for (unsigned char a = X; a <= E; ++a) {
curr.axis_feedrate[a] = curr.feedrate * delta_pos[a] * inv_distance;
@@ -3840,7 +3871,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]);
if (curr.abs_axis_feedrate[a] != 0.0f) {
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), m_extruder_id);
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), get_extruder_id());
if (axis_max_feedrate != 0.0f) min_feedrate_factor = std::min<float>(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]);
}
}
@@ -3855,16 +3886,9 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
}
}
// calculates block acceleration
float acceleration =
(type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
(is_extrusion_only_move(delta_pos) ?
get_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)));
//QDS
for (unsigned char a = X; a <= E; ++a) {
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), m_extruder_id);
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), get_extruder_id());
if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration)
acceleration = axis_max_acceleration / (std::abs(delta_pos[a]) * inv_distance);
}
@@ -3887,75 +3911,36 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
// calculates block entry feedrate
float vmax_junction = curr.safe_feedrate;
if (!blocks.empty() && prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD) {
bool prev_speed_larger = prev.feedrate > block.feedrate_profile.cruise;
float smaller_speed_factor = prev_speed_larger ? (block.feedrate_profile.cruise / prev.feedrate) : (prev.feedrate / block.feedrate_profile.cruise);
// Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
vmax_junction = prev_speed_larger ? block.feedrate_profile.cruise : prev.feedrate;
vmax_junction = std::min(prev.feedrate, block.feedrate_profile.cruise);
float v_factor = 1.0f;
bool limited = false;
Vec3f exit_direction_unit = prev.exit_direction.normalized();
Vec3f enter_direction_unit = curr.enter_direction.normalized();
float k, k_min = 10000.f;
for (unsigned char a = X; a <= E; ++a) {
for (unsigned char a = X; a <= Z; ++a) {
// Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop.
if (a == X) {
Vec3f exit_v = prev.feedrate * (prev.exit_direction);
if (prev_speed_larger)
exit_v *= smaller_speed_factor;
Vec3f entry_v = block.feedrate_profile.cruise * (curr.enter_direction);
Vec3f jerk_v = entry_v - exit_v;
Vec3f jerk_v = enter_direction_unit - exit_direction_unit;
jerk_v = Vec3f(abs(jerk_v.x()), abs(jerk_v.y()), abs(jerk_v.z()));
Vec3f max_xyz_jerk_v = get_xyz_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i));
for (size_t i = 0; i < 3; i++)
{
if (jerk_v[i] > max_xyz_jerk_v[i]) {
v_factor *= max_xyz_jerk_v[i] / jerk_v[i];
jerk_v *= v_factor;
for (size_t i = 0; i < 3; i++){
if (jerk_v[i] > 0) {
limited = true;
k = max_xyz_jerk_v[i] / jerk_v[i];
if (k < k_min)
k_min = k;
}
}
}
else if (a == Y || a == Z) {
continue;
}
else {
float v_exit = prev.axis_feedrate[a];
float v_entry = curr.axis_feedrate[a];
if (prev_speed_larger)
v_exit *= smaller_speed_factor;
if (limited) {
v_exit *= v_factor;
v_entry *= v_factor;
}
// Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction.
float jerk =
(v_exit > v_entry) ?
(((v_entry > 0.0f) || (v_exit < 0.0f)) ?
// coasting
(v_exit - v_entry) :
// axis reversal
std::max(v_exit, -v_entry)) :
// v_exit <= v_entry
(((v_entry < 0.0f) || (v_exit > 0.0f)) ?
// coasting
(v_entry - v_exit) :
// axis reversal
std::max(-v_exit, v_entry));
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / jerk;
limited = true;
}
}
}
if (limited)
vmax_junction *= v_factor;
vmax_junction = k_min;
// Now the transition velocity is known, which maximizes the shared exit / entry velocity while
// respecting the jerk factors, it may be possible, that applying separate safe exit / entry velocities will achieve faster prints.
@@ -4279,7 +4264,7 @@ void GCodeProcessor::process_VG1(const GCodeReader::GCodeLine& line)
curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]);
if (curr.abs_axis_feedrate[a] != 0.0f) {
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), m_extruder_id);
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), get_extruder_id());
if (axis_max_feedrate != 0.0f) min_feedrate_factor = std::min<float>(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]);
}
}
@@ -4303,7 +4288,7 @@ void GCodeProcessor::process_VG1(const GCodeReader::GCodeLine& line)
//QDS
for (unsigned char a = X; a <= E; ++a) {
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), m_extruder_id);
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), get_extruder_id());
if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration)
acceleration = axis_max_acceleration / (std::abs(delta_pos[a]) * inv_distance);
}
@@ -4665,7 +4650,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]);
if (curr.abs_axis_feedrate[a] != 0.0f) {
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), m_extruder_id);
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), get_extruder_id());
if (axis_max_feedrate != 0.0f) min_feedrate_factor = std::min<float>(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]);
}
}
@@ -4692,7 +4677,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
axis_acc[a] = acceleration * std::abs(delta_pos[a]) * inv_distance;
if (axis_acc[a] != 0.0f) {
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), m_extruder_id);
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), get_extruder_id());
if (axis_max_acceleration != 0.0f && axis_acc[a] > axis_max_acceleration) min_acc_factor = std::min<float>(min_acc_factor, axis_max_acceleration / axis_acc[a]);
}
}
@@ -4710,74 +4695,39 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
static const float PREVIOUS_FEEDRATE_THRESHOLD = 0.0001f;
float vmax_junction = curr.safe_feedrate;
if (!blocks.empty() && prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD) {
bool prev_speed_larger = prev.feedrate > block.feedrate_profile.cruise;
float smaller_speed_factor = prev_speed_larger ? (block.feedrate_profile.cruise / prev.feedrate) : (prev.feedrate / block.feedrate_profile.cruise);
//QDS: Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
vmax_junction = prev_speed_larger ? block.feedrate_profile.cruise : prev.feedrate;
vmax_junction = std::min(prev.feedrate, block.feedrate_profile.cruise);
float v_factor = 1.0f;
bool limited = false;
for (unsigned char a = X; a <= E; ++a) {
Vec3f exit_direction_unit = prev.exit_direction.normalized();
Vec3f entry_direction_unit = curr.enter_direction.normalized();
float k=0, k_min = 10000.f;
for (unsigned char a = X; a <= Z; ++a) {
//QDS: Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop.
if (a == X) {
Vec3f exit_v = prev.feedrate * (prev.exit_direction);
if (prev_speed_larger)
exit_v *= smaller_speed_factor;
Vec3f entry_v = block.feedrate_profile.cruise * (curr.enter_direction);
Vec3f jerk_v = entry_v - exit_v;
Vec3f jerk_v = entry_direction_unit - exit_direction_unit;
jerk_v = Vec3f(abs(jerk_v.x()), abs(jerk_v.y()), abs(jerk_v.z()));
Vec3f max_xyz_jerk_v = get_xyz_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i));
for (size_t i = 0; i < 3; i++)
{
if (jerk_v[i] > max_xyz_jerk_v[i]) {
v_factor *= max_xyz_jerk_v[i] / jerk_v[i];
jerk_v *= v_factor;
if(jerk_v[i] > 0){
limited = true;
k = max_xyz_jerk_v[i] / jerk_v[i];
if(k < k_min)
k_min = k;
}
}
}
else if (a == Y || a == Z) {
continue;
}
else {
float v_exit = prev.axis_feedrate[a];
float v_entry = curr.axis_feedrate[a];
if (prev_speed_larger)
v_exit *= smaller_speed_factor;
if (limited) {
v_exit *= v_factor;
v_entry *= v_factor;
}
//QDS: Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction.
float jerk =
(v_exit > v_entry) ?
(((v_entry > 0.0f) || (v_exit < 0.0f)) ?
//QDS: coasting
(v_exit - v_entry) :
//QDS: axis reversal
std::max(v_exit, -v_entry)) :
(((v_entry < 0.0f) || (v_exit > 0.0f)) ?
//QDS: coasting
(v_entry - v_exit) :
//QDS: axis reversal
std::max(-v_exit, v_entry));
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / jerk;
limited = true;
}
}
}
if (limited)
vmax_junction *= v_factor;
vmax_junction = k_min;
//QDS: Now the transition velocity is known, which maximizes the shared exit / entry velocity while
// respecting the jerk factors, it may be possible, that applying separate safe exit / entry velocities will achieve faster prints.
@@ -5303,6 +5253,24 @@ void GCodeProcessor::process_M221(const GCodeReader::GCodeLine& line)
}
}
void GCodeProcessor::process_M622(const GCodeReader::GCodeLine& line)
{
float value_j;
if(line.has_value('J',value_j)){
int interger_j = (int)(std::round(value_j));
if(interger_j == 1 && !m_measure_g29_time)
m_measure_g29_time = true;
}
}
void GCodeProcessor::process_M623(const GCodeReader::GCodeLine& line)
{
if(m_measure_g29_time)
m_measure_g29_time = false;
}
void GCodeProcessor::process_M400(const GCodeReader::GCodeLine& line)
{
float value_s = 0.0;

View File

@@ -393,6 +393,7 @@ namespace Slic3r {
std::unique_ptr<TrieNode> root;
};
class GCodeProcessor
{
static const std::vector<std::string> ReservedTags;
@@ -415,7 +416,6 @@ namespace Slic3r {
Total_Layer_Number_Placeholder,
Wipe_Tower_Start,
Wipe_Tower_End,
//1.9.7.52
Used_Filament_Weight_Placeholder,
Used_Filament_Volume_Placeholder,
Used_Filament_Length_Placeholder,
@@ -443,10 +443,10 @@ namespace Slic3r {
// checks the given gcode for reserved tags and returns true when finding any
// (the first max_count found tags are returned into found_tag)
static bool contains_reserved_tags(const std::string& gcode, unsigned int max_count, std::vector<std::string>& found_tag);
static bool get_last_position_from_gcode(const std::string &gcode_str, Vec3f &pos);
static int get_gcode_last_filament(const std::string &gcode_str);
static bool get_last_z_from_gcode(const std::string& gcode_str, double& z);
static bool get_last_position_from_gcode(const std::string &gcode_str, Vec3f &pos);
static const float Wipe_Width;
static const float Wipe_Height;
@@ -686,7 +686,6 @@ namespace Slic3r {
friend class GCodeProcessor;
};
//1.9.7.52
struct TimeProcessContext
{
UsedFilaments used_filaments; // stores the accurate filament usage info
@@ -770,6 +769,7 @@ namespace Slic3r {
float filament_load_times;
float filament_unload_times;
float extruder_change_times;
float prepare_compensation_time;
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> machines;
@@ -778,7 +778,7 @@ namespace Slic3r {
// post process the file with the given filename to add remaining time lines M73
// and updates moves' gcode ids accordingly
void post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends, const TimeProcessContext& context);
private:
private:
void handle_offsets_of_first_process(
const std::vector<std::pair<unsigned int, unsigned int>>& offsets,
std::vector<GCodeProcessorResult::MoveVertex>& moves,
@@ -1020,7 +1020,6 @@ namespace Slic3r {
int m_object_label_id{-1};
float m_print_z{0.0f};
std::vector<float> m_remaining_volume;
//1.9.7.52
std::vector<Extruder> m_filament_lists;
std::vector<int> m_filament_nozzle_temp;
std::vector<std::string> m_filament_types;
@@ -1067,6 +1066,7 @@ namespace Slic3r {
size_t m_last_default_color_id;
bool m_detect_layer_based_on_tag {false};
int m_seams_count;
bool m_measure_g29_time {false};
#if ENABLE_GCODE_VIEWER_STATISTICS
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -1111,7 +1111,8 @@ namespace Slic3r {
const std::vector<int> &filament_map,
const std::vector<std::set<int>>& unprintable_filament_types );
void apply_config(const PrintConfig& config);
//1.9.7.52
DynamicConfig export_config_for_render() const;
void set_filaments(const std::vector<Extruder>&filament_lists) { m_filament_lists=filament_lists;}
void enable_stealth_time_estimator(bool enabled);
@@ -1296,9 +1297,11 @@ namespace Slic3r {
// Processes T line (Select Tool)
void process_T(const GCodeReader::GCodeLine& line);
void process_T(const std::string_view command);
void process_M1020(const GCodeReader::GCodeLine &line);
void process_M622(const GCodeReader::GCodeLine &line);
void process_M623(const GCodeReader::GCodeLine &line);
void process_filament_change(int id);
//QDS: different path_type is only used for arc move
void store_move_vertex(EMoveType type, EMovePathType path_type = EMovePathType::Noop_move);

View File

@@ -30,6 +30,8 @@
#endif
static const int average_filter_window_size = 5;
static const float overhang_filter = 0.0f;
static const float lensLimit = 1.0f;
namespace Slic3r {
namespace SeamPlacerImpl {
@@ -698,9 +700,12 @@ struct SeamComparator
distance_penalty_b = 1.0f - gauss((b.position.head<2>() - preffered_location).norm(), 0.0f, 1.0f, 0.005f);
}
double a_overhang_around_penalty = a.extra_overhang_point < overhang_filter ? 0 : a.extra_overhang_point;
double b_overhang_around_penalty = b.extra_overhang_point < overhang_filter ? 0 : b.extra_overhang_point;
// the penalites are kept close to range [0-1.x] however, it should not be relied upon
float penalty_a = a.overhang + a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle) + distance_penalty_a;
float penalty_b = b.overhang + b.visibility + angle_importance * compute_angle_penalty(b.local_ccw_angle) + distance_penalty_b;
float penalty_a = a.overhang + a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle) + distance_penalty_a + a_overhang_around_penalty;
float penalty_b = b.overhang + b.visibility + angle_importance * compute_angle_penalty(b.local_ccw_angle) + distance_penalty_b + b_overhang_around_penalty;
return penalty_a < penalty_b;
}
@@ -733,9 +738,11 @@ struct SeamComparator
if (setup == SeamPosition::spRear) { return a.position.y() + SeamPlacer::seam_align_score_tolerance * 5.0f > b.position.y(); }
float penalty_a = a.overhang + a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle);
float penalty_b = b.overhang + b.visibility + angle_importance * compute_angle_penalty(b.local_ccw_angle);
double a_overhang_around_penalty = a.extra_overhang_point < overhang_filter ? 0 : a.extra_overhang_point;
double b_overhang_around_penalty = b.extra_overhang_point < overhang_filter ? 0 : b.extra_overhang_point;
float penalty_a = a.overhang + a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle) + a_overhang_around_penalty;
float penalty_b = b.overhang + b.visibility + angle_importance * compute_angle_penalty(b.local_ccw_angle) + b_overhang_around_penalty;
return penalty_a <= penalty_b || penalty_a - penalty_b < SeamPlacer::seam_align_score_tolerance;
}
@@ -954,7 +961,6 @@ void SeamPlacer::calculate_candidates_visibility(const PrintObject *po, const Se
void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po)
{
using namespace SeamPlacerImpl;
std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()), [po, &layers](tbb::blocked_range<size_t> r) {
std::unique_ptr<PerimeterDistancer> prev_layer_distancer;
@@ -970,17 +976,65 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po)
bool should_compute_layer_embedding = regions_with_perimeter > 1;
std::unique_ptr<PerimeterDistancer> current_layer_distancer = std::make_unique<PerimeterDistancer>(po->layers()[layer_idx]);
for (SeamCandidate &perimeter_point : layers[layer_idx].points) {
int points_size = layers[layer_idx].points.size();
for (size_t i = 0; i < points_size; i++) {
SeamCandidate &perimeter_point = layers[layer_idx].points[i];
Vec2f point = Vec2f{perimeter_point.position.head<2>()};
if (prev_layer_distancer.get() != nullptr) {
perimeter_point.overhang = prev_layer_distancer->distance_from_perimeter(point) + 0.6f * perimeter_point.perimeter.flow_width -
tan(SeamPlacer::overhang_angle_threshold) * po->layers()[layer_idx]->height;
perimeter_point.overhang = perimeter_point.overhang < 0.0f ? 0.0f : perimeter_point.overhang;
double dist_temp = prev_layer_distancer->distance_from_perimeter(point);
perimeter_point.overhang = dist_temp + 0.6f * perimeter_point.perimeter.flow_width - tan(SeamPlacer::overhang_angle_threshold) * po->layers()[layer_idx]->height;
perimeter_point.overhang = perimeter_point.overhang < 0.0f ? 0.0f : perimeter_point.overhang;
perimeter_point.overhang_degree = (dist_temp + 0.6f * perimeter_point.perimeter.flow_width) / perimeter_point.perimeter.flow_width;
perimeter_point.overhang_degree = perimeter_point.overhang_degree < 0.0f ? 0.0f : perimeter_point.overhang_degree;
}
if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam)
perimeter_point.embedded_distance = current_layer_distancer->distance_from_perimeter(point) + 0.6f * perimeter_point.perimeter.flow_width;
}
size_t start_index = perimeter_point.perimeter.start_index;
size_t end_index = perimeter_point.perimeter.end_index;
if (po->config().seam_placement_away_from_overhangs.value && perimeter_point.overhang_degree > 0.0f && end_index - start_index > 1) {
// QDS. extend overhang range
float dist = 0.0f;
size_t idx = i;
double gauss_value = gauss(0.0f, 0.0f, 1.0f, 10.0f);
perimeter_point.extra_overhang_point = perimeter_point.overhang_degree * gauss_value;
// check left
while (true) {
int prev = idx;
idx = idx == start_index ? end_index - 1 : idx - 1;
if (idx == i)
break;
dist += sqrt((layers[layer_idx].points[idx].position.head<2>() - layers[layer_idx].points[prev].position.head<2>()).squaredNorm());
if (dist > lensLimit)
break;
double gauss_value_dist = gauss(dist, 0.0f, 1.0f, 10.0f);
if (layers[layer_idx].points[idx].extra_overhang_point > perimeter_point.overhang_degree * gauss_value_dist)
continue;
layers[layer_idx].points[idx].extra_overhang_point = perimeter_point.overhang_degree * gauss_value_dist;
}
//check right
dist = 0.0f;
idx = i;
while (true) {
int prev = idx;
idx = idx == end_index - 1 ? start_index : idx + 1;
if (idx == i)
break;
dist += sqrt((layers[layer_idx].points[idx].position.head<2>() - layers[layer_idx].points[prev].position.head<2>()).squaredNorm());
if (dist > lensLimit)
break;
double gauss_value_dist = gauss(dist, 0.0f, 1.0f, 10.0f);
if (layers[layer_idx].points[idx].extra_overhang_point > perimeter_point.overhang_degree * gauss_value_dist)
continue;
layers[layer_idx].points[idx].extra_overhang_point = perimeter_point.overhang_degree * gauss_value_dist;
}
}
}
prev_layer_distancer.swap(current_layer_distancer);
@@ -1220,6 +1274,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl::
// interpolate between current and fitted position, prefer current pos for large weights.
Vec3f final_position = t * current_pos + (1.0f - t) * to_3d(fitted_pos, current_pos.z());
Perimeter &perimeter = layers[pair.first].points[pair.second].perimeter;
perimeter.seam_index = pair.second;
perimeter.final_seam_position = final_position;
@@ -1249,7 +1304,6 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl::
fclose(aligns);
#endif
}
std::vector<std::pair<size_t, size_t>> SeamPlacer::gather_all_seams_of_object(const std::vector<PrintObjectSeamData::LayerSeams> &layers)
{
// gather vector of all seams on the print_object - pair of layer_index and seam__index within that layer

View File

@@ -69,7 +69,18 @@ struct Perimeter
struct SeamCandidate
{
SeamCandidate(const Vec3f &pos, Perimeter &perimeter, float local_ccw_angle, EnforcedBlockedSeamPoint type)
: position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), embedded_distance(0.0f), local_ccw_angle(local_ccw_angle), type(type), central_enforcer(false), enable_scarf_seam(false),is_grouped(false)
: position(pos)
, perimeter(perimeter)
, visibility(0.0f)
, overhang(0.0f)
, embedded_distance(0.0f)
, local_ccw_angle(local_ccw_angle)
, type(type)
, central_enforcer(false)
, enable_scarf_seam(false)
, is_grouped(false)
, extra_overhang_point(0.0f)
, overhang_degree(0.0f)
{}
const Vec3f position;
// pointer to Perimeter loop of this point. It is shared across all points of the loop
@@ -84,6 +95,8 @@ struct SeamCandidate
bool central_enforcer; // marks this candidate as central point of enforced segment on the perimeter - important for alignment
bool enable_scarf_seam; // marks this candidate as a candidate for scarf seam
bool is_grouped;
float extra_overhang_point;
float overhang_degree;
};
struct SeamCandidateCoordinateFunctor
@@ -133,6 +146,8 @@ public:
// For long polygon sides, if they are close to the custom seam drawings, they are oversampled with this step size
static constexpr float enforcer_oversampling_distance = 0.2f;
static constexpr float end_point_oversampling_threshold = 4.0f;
static constexpr float end_point_oversampling_distance = 1.5f;
// When searching for seam clusters for alignment:
// following value describes, how much worse score can point have and still be picked into seam cluster instead of original seam point on the same layer

View File

@@ -1,3 +1,4 @@
#include "ClipperUtils.hpp"
#include "TimelapsePosPicker.hpp"
#include "Layer.hpp"

View File

@@ -28,12 +28,12 @@ namespace Slic3r {
//! macro used to mark string used at localization,
//! return same string
#ifndef _L
#define _L(s) Slic3r::I18N::translate(s)
#endif
#ifndef _L
#define _L(s) Slic3r::I18N::translate(s)
#endif
const static bool g_wipe_into_objects = false;
constexpr double similar_color_threshold_de2000 = 20.0;
const static bool g_wipe_into_objects = false;
constexpr double similar_color_threshold_de2000 = 20.0;
static std::set<int>get_filament_by_type(const std::vector<unsigned int>& used_filaments, const PrintConfig* print_config, const std::string& type)
{
@@ -435,8 +435,26 @@ std::vector<unsigned int> ToolOrdering::generate_first_layer_tool_order(const Pr
std::map<int, double> min_areas_per_extruder;
for (auto object : print.objects()) {
auto first_layer = object->get_layer(0);
for (auto layerm : first_layer->regions()) {
const Layer* target_layer = nullptr;
for(auto layer : object->layers()){
for(auto layerm : layer->regions()){
for(auto& expoly : layerm->raw_slices){
if (!offset_ex(expoly, -0.2 * scale_(print.config().initial_layer_line_width)).empty()) {
target_layer = layer;
break;
}
}
if(target_layer)
break;
}
if(target_layer)
break;
}
if(!target_layer)
return tool_order;
for (auto layerm : target_layer->regions()) {
int extruder_id = layerm->region().config().option("wall_filament")->getInt();
for (auto expoly : layerm->raw_slices) {
@@ -492,8 +510,26 @@ std::vector<unsigned int> ToolOrdering::generate_first_layer_tool_order(const Pr
std::vector<unsigned int> tool_order;
int initial_extruder_id = -1;
std::map<int, double> min_areas_per_extruder;
auto first_layer = object.get_layer(0);
for (auto layerm : first_layer->regions()) {
const Layer* target_layer = nullptr;
for(auto layer : object.layers()){
for(auto layerm : layer->regions()){
for(auto& expoly : layerm->raw_slices){
if (!offset_ex(expoly, -0.2 * scale_(object.config().line_width)).empty()) {
target_layer = layer;
break;
}
}
if(target_layer)
break;
}
if(target_layer)
break;
}
if(!target_layer)
return tool_order;
for (auto layerm : target_layer->regions()) {
int extruder_id = layerm->region().config().option("wall_filament")->getInt();
for (auto expoly : layerm->raw_slices) {
if (offset_ex(expoly, -0.2 * scale_(object.config().line_width)).empty())
@@ -560,30 +596,6 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
// Collect extruders reuqired to print layers.
void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector<std::pair<double, unsigned int>> &per_layer_extruder_switches)
{
// Collect the support extruders.
for (auto support_layer : object.support_layers()) {
LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z);
ExtrusionRole role = support_layer->support_fills.role();
bool has_support = false;
bool has_interface = false;
for (const ExtrusionEntity *ee : support_layer->support_fills.entities) {
ExtrusionRole er = ee->role();
if (er == erSupportMaterial || er == erSupportTransition) has_support = true;
if (er == erSupportMaterialInterface) has_interface = true;
if (has_support && has_interface) break;
}
unsigned int extruder_support = object.config().support_filament.value;
unsigned int extruder_interface = object.config().support_interface_filament.value;
if (has_support)
layer_tools.extruders.push_back(extruder_support);
if (has_interface)
layer_tools.extruders.push_back(extruder_interface);
if (has_support || has_interface) {
layer_tools.has_support = true;
layer_tools.wiping_extrusions().is_support_overriddable_and_mark(role, object);
}
}
// Extruder overrides are ordered by print_z.
std::vector<std::pair<double, unsigned int>>::const_iterator it_per_layer_extruder_override;
it_per_layer_extruder_override = per_layer_extruder_switches.begin();
@@ -598,7 +610,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
// Override extruder with the next
// Override extruder with the next
for (; it_per_layer_extruder_override != per_layer_extruder_switches.end() && it_per_layer_extruder_override->first < layer->print_z + EPSILON; ++ it_per_layer_extruder_override)
extruder_override = (int)it_per_layer_extruder_override->second;
@@ -664,7 +676,56 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
sort_remove_duplicates(firstLayerExtruders);
const_cast<PrintObject&>(object).object_first_layer_wall_extruders = firstLayerExtruders;
// Collect the support extruders.
for (auto support_layer : object.support_layers()) {
LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z);
ExtrusionRole role = support_layer->support_fills.role();
bool has_support = false;
bool has_interface = false;
for (const ExtrusionEntity *ee : support_layer->support_fills.entities) {
ExtrusionRole er = ee->role();
if (er == erSupportMaterial || er == erSupportTransition) has_support = true;
if (er == erSupportMaterialInterface) has_interface = true;
if (has_support && has_interface) break;
}
unsigned int extruder_support = object.config().support_filament.value;
unsigned int extruder_interface = object.config().support_interface_filament.value;
if (has_support) {
if (extruder_support > 0 || !has_interface || extruder_interface == 0 || layer_tools.has_object)
layer_tools.extruders.push_back(extruder_support);
else {
auto all_extruders = object.print()->extruders();
auto get_next_extruder = [&](int current_extruder, const std::vector<unsigned int> &extruders) {
std::vector<float> flush_matrix(
cast<float>(get_flush_volumes_matrix(object.print()->config().flush_volumes_matrix.values, 0, object.print()->config().nozzle_diameter.values.size())));
const unsigned int number_of_extruders = (unsigned int) (sqrt(flush_matrix.size()) + EPSILON);
// Extract purging volumes for each extruder pair:
std::vector<std::vector<float>> wipe_volumes;
for (unsigned int i = 0; i < number_of_extruders; ++i)
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders));
int next_extruder = current_extruder;
float min_flush = std::numeric_limits<float>::max();
for (auto extruder_id : extruders) {
if (object.print()->config().filament_soluble.get_at(extruder_id) || extruder_id == current_extruder) continue;
if (wipe_volumes[extruder_interface - 1][extruder_id] < min_flush) {
next_extruder = extruder_id;
min_flush = wipe_volumes[extruder_interface - 1][extruder_id];
}
}
return next_extruder;
};
bool interface_not_for_body = object.config().support_interface_not_for_body;
layer_tools.extruders.push_back(get_next_extruder(interface_not_for_body ? extruder_interface - 1 : -1, all_extruders) + 1);
}
}
if (has_interface) layer_tools.extruders.push_back(extruder_interface);
if (has_support || has_interface) {
layer_tools.has_support = true;
layer_tools.wiping_extrusions().is_support_overriddable_and_mark(role, object);
}
}
for (auto& layer : m_layer_tools) {
// Sort and remove duplicates
sort_remove_duplicates(layer.extruders);
@@ -675,6 +736,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
}
}
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height)
{
if (m_layer_tools.empty())
@@ -1072,64 +1134,63 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
if (!print_config || m_layer_tools.empty())
return;
const unsigned int number_of_extruders = (unsigned int)(print_config->filament_colour.values.size() + EPSILON);
const unsigned int number_of_extruders = (unsigned int)(print_config->filament_colour.values.size() + EPSILON);
using FlushMatrix = std::vector<std::vector<float>>;
size_t nozzle_nums = print_config->nozzle_diameter.values.size();
std::vector<FlushMatrix> nozzle_flush_mtx;
for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
std::vector<float> flush_matrix(cast<float>(get_flush_volumes_matrix(print_config->flush_volumes_matrix.values, nozzle_id, nozzle_nums)));
std::vector<std::vector<float>> wipe_volumes;
for (unsigned int i = 0; i < number_of_extruders; ++i)
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders));
nozzle_flush_mtx.emplace_back(wipe_volumes);
}
using FlushMatrix = std::vector<std::vector<float>>;
size_t nozzle_nums = print_config->nozzle_diameter.values.size();
auto flush_multiplies = print_config->flush_multiplier.values;
flush_multiplies.resize(nozzle_nums, 1);
for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
for (auto& vec : nozzle_flush_mtx[nozzle_id]) {
for (auto& v : vec)
v *= flush_multiplies[nozzle_id];
}
std::vector<FlushMatrix> nozzle_flush_mtx;
for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
std::vector<float> flush_matrix(cast<float>(get_flush_volumes_matrix(print_config->flush_volumes_matrix.values, nozzle_id, nozzle_nums)));
std::vector<std::vector<float>> wipe_volumes;
for (unsigned int i = 0; i < number_of_extruders; ++i)
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders));
nozzle_flush_mtx.emplace_back(wipe_volumes);
}
auto flush_multiplies = print_config->flush_multiplier.values;
flush_multiplies.resize(nozzle_nums, 1);
for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
for (auto& vec : nozzle_flush_mtx[nozzle_id]) {
for (auto& v : vec)
v *= flush_multiplies[nozzle_id];
}
std::vector<int>filament_maps(number_of_extruders, 0);
FilamentMapMode map_mode = FilamentMapMode::fmmAutoForFlush;
std::vector<std::vector<unsigned int>> layer_filaments;
for (auto& lt : m_layer_tools) {
layer_filaments.emplace_back(lt.extruders);
}
std::vector<unsigned int> used_filaments = collect_sorted_used_filaments(layer_filaments);
std::vector<std::set<int>>geometric_unprintables = m_print->get_geometric_unprintable_filaments();
std::vector<std::set<int>>physical_unprintables = m_print->get_physical_unprintable_filaments(used_filaments);
filament_maps = m_print->get_filament_maps();
map_mode = m_print->get_filament_map_mode();
// only check and map in sequence mode, in by object mode, we check the map in print.cpp
if (print_config->print_sequence != PrintSequence::ByObject || m_print->objects().size() == 1) {
if (map_mode < FilamentMapMode::fmmManual) {
const PrintConfig* print_config = m_print_config_ptr;
if (!print_config && m_print_object_ptr) {
print_config = &(m_print_object_ptr->print()->config());
}
filament_maps = ToolOrdering::get_recommended_filament_maps(layer_filaments, m_print, map_mode, physical_unprintables, geometric_unprintables);
if (filament_maps.empty())
return;
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value + 1; });
m_print->update_filament_maps_to_config(filament_maps);
}
std::vector<int>filament_maps(number_of_extruders, 0);
FilamentMapMode map_mode = FilamentMapMode::fmmAutoForFlush;
std::vector<std::vector<unsigned int>> layer_filaments;
for (auto& lt : m_layer_tools) {
layer_filaments.emplace_back(lt.extruders);
}
std::vector<unsigned int> used_filaments = collect_sorted_used_filaments(layer_filaments);
std::vector<std::set<int>>geometric_unprintables = m_print->get_geometric_unprintable_filaments();
std::vector<std::set<int>>physical_unprintables = m_print->get_physical_unprintable_filaments(used_filaments);
filament_maps = m_print->get_filament_maps();
map_mode = m_print->get_filament_map_mode();
// only check and map in sequence mode, in by object mode, we check the map in print.cpp
if (print_config->print_sequence != PrintSequence::ByObject || m_print->objects().size() == 1) {
if (map_mode < FilamentMapMode::fmmManual) {
const PrintConfig* print_config = m_print_config_ptr;
if (!print_config && m_print_object_ptr) {
print_config = &(m_print_object_ptr->print()->config());
}
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value - 1; });
check_filament_printable_after_group(used_filaments, filament_maps, print_config);
filament_maps = ToolOrdering::get_recommended_filament_maps(layer_filaments, m_print, map_mode, physical_unprintables, geometric_unprintables);
if (filament_maps.empty())
return;
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value + 1; });
m_print->update_filament_maps_to_config(filament_maps);
}
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value - 1; });
check_filament_printable_after_group(used_filaments, filament_maps, print_config);
}
else {
// we just need to change the map to 0 based
@@ -1168,7 +1229,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
}
}
return false;
};
};
reorder_filaments_for_minimum_flush_volume(
filament_lists,
@@ -1276,12 +1337,12 @@ void ToolOrdering::mark_skirt_layers(const PrintConfig &config, coordf_t max_lay
static CustomGCode::Info custom_gcode_per_print_z;
void ToolOrdering::assign_custom_gcodes(const Print& print)
{
// Only valid for non-sequential print.
assert(print.config().print_sequence == PrintSequence::ByLayer);
// Only valid for non-sequential print.
assert(print.config().print_sequence == PrintSequence::ByLayer);
custom_gcode_per_print_z = print.model().get_curr_plate_custom_gcodes();
if (custom_gcode_per_print_z.gcodes.empty())
return;
if (custom_gcode_per_print_z.gcodes.empty())
return;
// QDS
auto num_filaments = unsigned(print.config().filament_diameter.size());
@@ -1325,6 +1386,7 @@ void ToolOrdering::assign_custom_gcodes(const Print& print)
extruder_print_above_by_layer.emplace(layer_idx, extruder_printing_above);
}
}
for (auto custom_gcode_it = custom_gcode_per_print_z.gcodes.rbegin(); custom_gcode_it != custom_gcode_per_print_z.gcodes.rend(); ++custom_gcode_it) {
if (custom_gcode_it->type == CustomGCode::ToolChange)
continue;

View File

@@ -418,7 +418,7 @@ Polylines remove_points_from_polygon(const Polygon &polygon, const std::vector<V
{
assert(polygon.size() > 2);
Polylines result;
std::vector<PointWithFlag> new_pl;// add intersection points for gaps, where bool indicates whether it's a gap point.
std::vector<PointWithFlag> new_pl; // add intersection points for gaps, where bool indicates whether it's a gap point.
std::vector<IntersectionInfo> inter_info;
Vec2f ray = is_left ? Vec2f(-1, 0) : Vec2f(1, 0);
auto polygon_box = get_extents(polygon);
@@ -632,7 +632,7 @@ public:
// Extrude with an explicitely provided amount of extrusion.
WipeTowerWriter &extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false ,LimitFlow limit_flow = LimitFlow::LimitPrintFlow)
{
if ((std::abs(x - m_current_pos.x()) <= (float)EPSILON) && (std::abs(y - m_current_pos.y()) < (float)EPSILON) && e == 0.f && (f == 0.f || f == m_current_feedrate))
if ((std::abs(x - m_current_pos.x()) <= (float)EPSILON) && (std::abs(y - m_current_pos.y()) < (float)EPSILON) && e == 0.f && (f == 0.f || f == m_current_feedrate))
// Neither extrusion nor a travel move.
return *this;
@@ -823,41 +823,7 @@ public:
} while (i != index_of_closest);
return (*this);
}
WipeTowerWriter &line(const WipeTower *wipe_tower, Vec2f p0, Vec2f p1,const float f = 0.f)
{
bool need_change_flow = wipe_tower->need_thick_bridge_flow(p0.y());
if (need_change_flow) {
set_extrusion_flow(wipe_tower->extrusion_flow(0.2));
append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + std::to_string(0.2) + "\n");
}
if (abs(x() - p0.x()) > abs(x() - p1.x())) std::swap(p0, p1);
travel(p0.x(), y());
travel(x(), p0.y());
extrude(p1, f);
if (need_change_flow) {
set_extrusion_flow(wipe_tower->get_extrusion_flow());
append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + std::to_string(m_layer_height) + "\n");
}
return (*this);
}
WipeTowerWriter &rectangle_fill_box(const WipeTower *wipe_tower, const WipeTower::box_coordinates &fill_box, std::vector<Vec2f> &finish_rect_wipe_path, const float f = 0.f)
{
float width = fill_box.rd.x() - fill_box.ld.x();
float height = fill_box.ru.y() - fill_box.rd.y();
if (height > wipe_tower->m_perimeter_width - wipe_tower->WT_EPSILON) {
rectangle_fill_box(wipe_tower, fill_box.ld, width, height, f);
Vec2f target = (pos() == fill_box.ld ? fill_box.rd : (pos() == fill_box.rd ? fill_box.ru : (pos() == fill_box.ru ? fill_box.lu : fill_box.ld)));
finish_rect_wipe_path.emplace_back(pos());
finish_rect_wipe_path.emplace_back(target);
} else if (height > wipe_tower->WT_EPSILON) {
line(wipe_tower, fill_box.ld, fill_box.rd);
Vec2f target = (pos() == fill_box.ld ? fill_box.rd : fill_box.ld);
finish_rect_wipe_path.emplace_back(pos());
finish_rect_wipe_path.emplace_back(target);
}
return (*this);
}
WipeTowerWriter &rectangle_fill_box(const WipeTower* wipe_tower, const Vec2f &ld, float width, float height, const float f = 0.f)
{
bool need_change_flow = wipe_tower->need_thick_bridge_flow(ld.y());
@@ -896,7 +862,41 @@ public:
} while (i != index_of_closest);
return (*this);
}
WipeTowerWriter &line(const WipeTower *wipe_tower, Vec2f p0, Vec2f p1,const float f = 0.f)
{
bool need_change_flow = wipe_tower->need_thick_bridge_flow(p0.y());
if (need_change_flow) {
set_extrusion_flow(wipe_tower->extrusion_flow(0.2));
append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + std::to_string(0.2) + "\n");
}
if (abs(x() - p0.x()) > abs(x() - p1.x())) std::swap(p0, p1);
travel(p0.x(), y());
travel(x(), p0.y());
extrude(p1, f);
if (need_change_flow) {
set_extrusion_flow(wipe_tower->get_extrusion_flow());
append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + std::to_string(m_layer_height) + "\n");
}
return (*this);
}
WipeTowerWriter &rectangle_fill_box(const WipeTower *wipe_tower, const WipeTower::box_coordinates &fill_box, std::vector<Vec2f> &finish_rect_wipe_path, const float f = 0.f)
{
float width = fill_box.rd.x() - fill_box.ld.x();
float height = fill_box.ru.y() - fill_box.rd.y();
if (height > wipe_tower->m_perimeter_width - wipe_tower->WT_EPSILON) {
rectangle_fill_box(wipe_tower, fill_box.ld, width, height, f);
Vec2f target = (pos() == fill_box.ld ? fill_box.rd : (pos() == fill_box.rd ? fill_box.ru : (pos() == fill_box.ru ? fill_box.lu : fill_box.ld)));
finish_rect_wipe_path.emplace_back(pos());
finish_rect_wipe_path.emplace_back(target);
} else if (height > wipe_tower->WT_EPSILON) {
line(wipe_tower, fill_box.ld, fill_box.rd);
Vec2f target = (pos() == fill_box.ld ? fill_box.rd : fill_box.ld);
finish_rect_wipe_path.emplace_back(pos());
finish_rect_wipe_path.emplace_back(target);
}
return (*this);
}
WipeTowerWriter& rectangle(const WipeTower::box_coordinates& box, const float f = 0.f)
{
rectangle(Vec2f(box.ld.x(), box.ld.y()),
@@ -904,7 +904,6 @@ public:
box.ru.y() - box.rd.y(), f);
return (*this);
}
WipeTowerWriter &polygon(const Polygon &wall_polygon, const float f = 0.f)
{
Polyline pl = to_polyline(wall_polygon);
@@ -1079,7 +1078,7 @@ public:
WipeTowerWriter& flush_planner_queue()
{
m_gcode += "G4 S0\n";
m_gcode += "G4 S0\n";
return *this;
}
@@ -1521,9 +1520,10 @@ TriangleMesh WipeTower::its_make_rib_tower(float width, float depth, float heigh
Polygon bottom = rib_section(width, depth, rib_length, rib_width, fillet_wall);
Polygon top = rib_section(width, depth, std::sqrt(width * width + depth * depth), rib_width, fillet_wall);
if (fillet_wall)
assert(bottom.points.size() == top.points.size());
assert(bottom.points.size() == top.points.size());
int offset = bottom.points.size();
res.its.vertices.reserve(offset * 2);
if (bottom.area() < scaled(EPSILON) || top.area() < scaled(EPSILON) || bottom.points.size() != top.points.size()) return res;
auto faces_bottom = Triangulation::triangulate(bottom);
auto faces_top = Triangulation::triangulate(top);
res.its.indices.reserve(offset * 2 + faces_bottom.size() + faces_top.size());
@@ -1546,6 +1546,7 @@ TriangleMesh WipeTower::its_make_rib_tower(float width, float depth, float heigh
TriangleMesh WipeTower::its_make_rib_brim(const Polygon& brim, float layer_height) {
TriangleMesh res;
if (brim.area() < scaled(EPSILON))return res;
int offset = brim.size();
res.its.vertices.reserve(brim.size() * 2);
auto faces= Triangulation::triangulate(brim);
@@ -2844,7 +2845,7 @@ WipeTower::ToolChangeResult WipeTower::merge_tcr(ToolChangeResult &first, ToolCh
WipeTower::ToolChangeResult out = first;
if ((first.end_pos - second.start_pos).norm() > (float)EPSILON) {
std::string travel_gcode = "G1 X" + Slic3r::float_to_string_decimal_point(second.start_pos.x(), 3) + " Y" +
Slic3r::float_to_string_decimal_point(second.start_pos.y(), 3) + "F" + std::to_string(m_max_speed) + "\n";
Slic3r::float_to_string_decimal_point(second.start_pos.y(), 3) + " F" + std::to_string(m_max_speed) + "\n";
bool need_insert_travel = true;
if (second.is_tool_change
&& is_approx(second.start_pos.x(), second.tool_change_start_pos.x())
@@ -3885,7 +3886,6 @@ void WipeTower::generate_wipe_tower_blocks()
}
}
}
void WipeTower::calc_block_infill_gap()
{
//1.calc block infill gap width
@@ -3966,6 +3966,7 @@ void WipeTower::calc_block_infill_gap()
}
m_extra_spacing = 1.f;
}
void WipeTower::plan_tower_new()
{
if (m_wipe_tower_brim_width < 0) m_wipe_tower_brim_width = get_auto_brim_by_height(m_wipe_tower_height);
@@ -4436,7 +4437,7 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall(bool is_new_mode)
.set_initial_tool(m_current_tool)
.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f));
set_for_wipe_tower_writer(writer);
set_for_wipe_tower_writer(writer);
// Slow down on the 1st layer.
bool first_layer = is_first_layer();
@@ -4717,9 +4718,11 @@ bool WipeTower::is_valid_last_layer(int tool) const
}
float WipeTower::get_block_gap_width(int tool,bool is_nozzlechangle)
{
assert(m_block_infill_gap_width.count(m_filpar[tool].category));
//assert(m_block_infill_gap_width.count(m_filpar[tool].category));//The code contains logic that attempts to access non-existent blocks,
// such as in case of involving two extruders with only a single head and a single layer,
// some code will attempt to access the block's nozzle_change_gap_width, even though the block does not exist.
if (!m_block_infill_gap_width.count(m_filpar[tool].category)) {
return m_perimeter_width;
return is_nozzlechangle ? m_nozzle_change_perimeter_width : m_perimeter_width;
}
return is_nozzlechangle ? m_block_infill_gap_width[m_filpar[tool].category].second : m_block_infill_gap_width[m_filpar[tool].category].first;