mirror of
https://github.com/QIDITECH/QIDIStudio.git
synced 2026-02-07 12:21:50 +03:00
update libslic3r
This commit is contained in:
@@ -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 = ¤t_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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "TimelapsePosPicker.hpp"
|
||||
#include "Layer.hpp"
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user