Merge remote-tracking branch 'origin/main' into dev/gizmo

This commit is contained in:
Noisyfox 2023-11-10 09:57:11 +08:00
commit cf5c19fa24
19 changed files with 328 additions and 170 deletions

View file

@ -22,9 +22,8 @@ public:
} else {
m_E = 0;
m_retracted = 0;
m_restart_extra = 0;
}
m_restart_extra = 0;
m_absolute_E = 0;
}
@ -34,7 +33,7 @@ public:
double retract(double length, double restart_extra);
double unretract();
double E() const { return m_share_extruder ? m_share_E : m_E; }
void reset_E() { m_E = 0.; m_share_E = 0.; }
void reset_E() { reset(); }
double e_per_mm(double mm3_per_mm) const { return mm3_per_mm * m_e_per_mm3; }
double e_per_mm3() const { return m_e_per_mm3; }
// Used filament volume in mm^3.

View file

@ -1402,6 +1402,8 @@ namespace DoExport {
//BBS
//if (ret.size() < MAX_TAGS_COUNT) check(_(L("Printing by object G-code")), config.printing_by_object_gcode.value);
//if (ret.size() < MAX_TAGS_COUNT) check(_(L("Color Change G-code")), config.color_change_gcode.value);
//Orca
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Change extrusion role G-code")), config.change_extrusion_role_gcode.value);
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Pause G-code")), config.machine_pause_gcode.value);
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Template Custom G-code")), config.template_custom_gcode.value);
if (ret.size() < MAX_TAGS_COUNT) {
@ -4755,6 +4757,18 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
double F = speed * 60; // convert mm/sec to mm/min
//Orca: process custom gcode for extrusion role change
if (path.role() != m_last_extrusion_role && !m_config.change_extrusion_role_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("extrusion_role", new ConfigOptionString(extrusion_role_to_string_for_parser(path.role())));
config.set_key_value("last_extrusion_role", new ConfigOptionString(extrusion_role_to_string_for_parser(m_last_extrusion_role)));
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
config.set_key_value("layer_z", new ConfigOptionFloat(m_layer == nullptr ? m_last_height : m_layer->print_z));
gcode += this->placeholder_parser_process("change_extrusion_role_gcode",
m_config.change_extrusion_role_gcode.value, m_writer.extruder()->id(), &config)
+ "\n";
}
// extrude arc or line
if (m_enable_extrusion_role_markers) {
if (path.role() != m_last_extrusion_role) {
@ -5002,6 +5016,35 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
return gcode;
}
//Orca: get string name of extrusion role. used for change_extruder_role_gcode
std::string GCode::extrusion_role_to_string_for_parser(const ExtrusionRole & role)
{
switch (role) {
case erPerimeter: return "Perimeter";
case erExternalPerimeter: return "ExternalPerimeter";
case erOverhangPerimeter: return "OverhangPerimeter";
case erInternalInfill: return "InternalInfill";
case erSolidInfill: return "SolidInfill";
case erTopSolidInfill: return "TopSolidInfill";
case erBottomSurface: return "BottomSurface";
case erBridgeInfill:
case erInternalBridgeInfill: return "BridgeInfill";
case erGapFill: return "GapFill";
case erIroning: return "Ironing";
case erSkirt: return "Skirt";
case erBrim: return "Brim";
case erSupportMaterial: return "SupportMaterial";
case erSupportMaterialInterface: return "SupportMaterialInterface";
case erSupportTransition: return "SupportTransition";
case erWipeTower: return "WipeTower";
case erCustom:
case erMixed:
case erCount:
case erNone:
default: return "Mixed";
}
}
std::string encodeBase64(uint64_t value)
{
//Always use big endian mode

View file

@ -205,6 +205,7 @@ public:
// inside the generated string and after the G-code export finishes.
std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr);
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
std::string extrusion_role_to_string_for_parser(const ExtrusionRole &);
// For Perl bindings, to be used exclusively by unit tests.
unsigned int layer_count() const { return m_layer_count; }
@ -213,7 +214,7 @@ public:
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment);
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::SpiralLift);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id, double print_z);
bool is_BBL_Printer();

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "../Layer.hpp"
#include "../GCode.hpp"
#include "../EdgeGrid.hpp"
@ -13,6 +17,8 @@
#include <unordered_set>
#include <boost/range/adaptor/reversed.hpp>
//#define AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
namespace Slic3r {
struct TravelPoint
@ -20,6 +26,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 +40,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 +217,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 +269,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 +277,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 +286,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 +294,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;
@ -350,8 +360,6 @@ static Polyline to_polyline(const std::vector<TravelPoint> &travel)
return result;
}
// #define AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
static void export_travel_to_svg(const Polygons &boundary,
const Line &original_travel,
@ -359,16 +367,17 @@ static void export_travel_to_svg(const Polygons &boundary,
const std::vector<Intersection> &intersections,
const std::string &path)
{
BoundingBox bbox = get_extents(boundary);
coordf_t stroke_width = scale_(0.05);
BoundingBox bbox = get_extents(boundary);
::Slic3r::SVG svg(path, bbox);
svg.draw_outline(boundary, "green");
svg.draw(original_travel, "blue");
svg.draw(result_travel, "red");
svg.draw(original_travel.a, "black");
svg.draw(original_travel.b, "grey");
svg.draw_outline(boundary, "green", stroke_width);
svg.draw(original_travel, "blue", stroke_width);
svg.draw(result_travel, "red", stroke_width);
svg.draw(original_travel.a, "black", stroke_width);
svg.draw(original_travel.b, "grey", stroke_width);
for (const Intersection &intersection : intersections)
svg.draw(intersection.point, "lightseagreen");
svg.draw(intersection.point, "lightseagreen", stroke_width);
}
static void export_travel_to_svg(const Polygons &boundary,
@ -433,21 +442,30 @@ static std::vector<TravelPoint> simplify_travel(const AvoidCrossingPerimeters::B
visitor.pt_current = &current_point;
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
if (travel[point_idx_2].point == current_point) {
next = travel[point_idx_2];
point_idx = point_idx_2;
continue;
}
if (!next.do_not_remove)
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
// Workaround for some issue in MSVC 19.29.30037 32-bit compiler.
#if defined(_WIN32) && !defined(_WIN64)
if (bool volatile do_not_remove = travel[point_idx_2].do_not_remove; do_not_remove)
break;
#else
if (travel[point_idx_2].do_not_remove)
break;
#endif
if (travel[point_idx_2].point == current_point) {
next = travel[point_idx_2];
point_idx = point_idx_2;
continue;
}
visitor.pt_next = &travel[point_idx_2].point;
boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
// Check if deleting point causes crossing a boundary
if (!visitor.intersect) {
next = travel[point_idx_2];
point_idx = point_idx_2;
visitor.pt_next = &travel[point_idx_2].point;
boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
// Check if deleting point causes crossing a boundary
if (!visitor.intersect) {
next = travel[point_idx_2];
point_idx = point_idx_2;
}
}
}
simplified_path.emplace_back(next);
}
@ -473,7 +491,7 @@ static float get_perimeter_spacing(const Layer &layer)
size_t regions_count = 0;
float perimeter_spacing = 0.f;
for (const LayerRegion *layer_region : layer.regions())
if (layer_region != nullptr && !layer_region->slices.empty()) {
if (layer_region != nullptr && ! layer_region->slices.empty()) {
perimeter_spacing += layer_region->flow(frPerimeter).scaled_spacing();
++regions_count;
}
@ -494,7 +512,7 @@ static float get_perimeter_spacing_external(const Layer &layer)
for (const PrintObject *object : layer.object()->print()->objects())
if (const Layer *l = object->get_layer_at_printz(layer.print_z, EPSILON); l)
for (const LayerRegion *layer_region : l->regions())
if (layer_region != nullptr && !layer_region->slices.empty()) {
if (layer_region != nullptr && ! layer_region->slices.empty()) {
perimeter_spacing += layer_region->flow(frPerimeter).scaled_spacing();
++ regions_count;
}
@ -507,6 +525,25 @@ static float get_perimeter_spacing_external(const Layer &layer)
return perimeter_spacing;
}
// Returns average perimeter width calculated from all LayerRegion within the layer.
static float get_external_perimeter_width(const Layer &layer)
{
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;
}
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;
}
// Called by avoid_perimeters() and by simplify_travel_heuristics().
static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &boundary,
const Point &start,
@ -566,7 +603,7 @@ static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &bo
// 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)});
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), intersection_first.do_not_remove});
// Check if intersection line also exit the boundary polygon
if (it_second_r != it_last_item) {
@ -590,7 +627,7 @@ static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &bo
// 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)});
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), intersection_second.do_not_remove});
// Skip intersections in between
it_first = it_second;
}
@ -645,22 +682,22 @@ static size_t avoid_perimeters(const AvoidCrossingPerimeters::Boundary &boundary
// Check if anyone of ExPolygons contains whole travel.
// called by need_wipe() and AvoidCrossingPerimeters::travel_to()
// FIXME Lukas H.: Maybe similar approach could also be used for ExPolygon::contains()
static bool any_expolygon_contains(const ExPolygons &ex_polygons,
const std::vector<BoundingBox> &ex_polygons_bboxes,
const EdgeGrid::Grid &grid_lslice,
static bool any_expolygon_contains(const ExPolygons &lslices_offset,
const std::vector<BoundingBox> &lslices_offset_bboxes,
const EdgeGrid::Grid &grid_lslices_offset,
const Line &travel)
{
assert(ex_polygons.size() == ex_polygons_bboxes.size());
if(!grid_lslice.bbox().contains(travel.a) || !grid_lslice.bbox().contains(travel.b))
assert(lslices_offset.size() == lslices_offset_bboxes.size());
if(!grid_lslices_offset.bbox().contains(travel.a) || !grid_lslices_offset.bbox().contains(travel.b))
return false;
FirstIntersectionVisitor visitor(grid_lslice);
FirstIntersectionVisitor visitor(grid_lslices_offset);
visitor.pt_current = &travel.a;
visitor.pt_next = &travel.b;
grid_lslice.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
grid_lslices_offset.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
if (!visitor.intersect) {
for (const ExPolygon &ex_polygon : ex_polygons) {
const BoundingBox &bbox = ex_polygons_bboxes[&ex_polygon - &ex_polygons.front()];
for (const ExPolygon &ex_polygon : lslices_offset) {
const BoundingBox &bbox = lslices_offset_bboxes[&ex_polygon - &lslices_offset.front()];
if (bbox.contains(travel.a) && bbox.contains(travel.b) && ex_polygon.contains(travel.a))
return true;
}
@ -670,18 +707,18 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons,
// Check if anyone of ExPolygons contains whole travel.
// called by need_wipe()
static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vector<BoundingBox> &ex_polygons_bboxes, const EdgeGrid::Grid &grid_lslice, const Polyline &travel)
static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vector<BoundingBox> &ex_polygons_bboxes, const EdgeGrid::Grid &grid_lslice_offset, const Polyline &travel)
{
assert(ex_polygons.size() == ex_polygons_bboxes.size());
if(std::any_of(travel.points.begin(), travel.points.end(), [&grid_lslice](const Point &point) { return !grid_lslice.bbox().contains(point); }))
if(std::any_of(travel.points.begin(), travel.points.end(), [&grid_lslice_offset](const Point &point) { return !grid_lslice_offset.bbox().contains(point); }))
return false;
FirstIntersectionVisitor visitor(grid_lslice);
FirstIntersectionVisitor visitor(grid_lslice_offset);
bool any_intersection = false;
for (size_t line_idx = 1; line_idx < travel.size(); ++line_idx) {
visitor.pt_current = &travel.points[line_idx - 1];
visitor.pt_next = &travel.points[line_idx];
grid_lslice.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
grid_lslice_offset.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
any_intersection = visitor.intersect;
if (any_intersection) break;
}
@ -698,13 +735,13 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vec
}
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)
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().extruder()->id()) > 0.;
bool wipe_needed = false;
@ -714,16 +751,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);
}
}
@ -945,55 +982,71 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid,
// ExPolygons are handled one by one so returned ExPolygons could intersect.
static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset)
{
double min_contour_width = 2. * offset + SCALED_EPSILON;
double search_radius = 2. * (offset + min_contour_width);
ExPolygons ex_poly_result = ex_polygons;
const std::vector<double> min_contour_width_values = {offset / 2., offset, 2. * offset + SCALED_EPSILON};
ExPolygons ex_poly_result = ex_polygons;
resample_expolygons(ex_poly_result, offset / 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 + 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, 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);
}
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;
@ -1133,10 +1186,8 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
Vec2d startf = start.cast<double>();
Vec2d endf = end .cast<double>();
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)))) {
bool is_support_layer = dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr;
if (!use_external && (is_support_layer || (!m_lslices_offset.empty() && !any_expolygon_contains(m_lslices_offset, m_lslices_offset_bboxes, m_grid_lslices_offset, travel)))) {
// Initialize m_internal only when it is necessary.
if (m_internal.boundaries.empty())
init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer())));
@ -1186,7 +1237,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_lslices_offset, travel, result_pl, travel_intersection_count);
return result_pl;
}
@ -1197,13 +1248,21 @@ void AvoidCrossingPerimeters::init_layer(const Layer &layer)
{
m_internal.clear();
m_external.clear();
m_lslices_offset.clear();
m_lslices_offset_bboxes.clear();
float perimeter_offset = -get_external_perimeter_width(layer) / float(2.);
m_lslices_offset = offset_ex(layer.lslices, perimeter_offset);
m_lslices_offset_bboxes.reserve(m_lslices_offset.size());
for (const ExPolygon &ex_poly : m_lslices_offset)
m_lslices_offset_bboxes.emplace_back(get_extents(ex_poly));
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_lslices_offset.set_bbox(bbox_slice);
m_grid_lslices_offset.create(m_lslices_offset, coord_t(scale_(1.)));
}
#if 0

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2020 - 2022 Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_AvoidCrossingPerimeters_hpp_
#define slic3r_AvoidCrossingPerimeters_hpp_
@ -58,8 +62,11 @@ 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;
EdgeGrid::Grid m_grid_lslices_offset;
// Store all needed data for travels inside object
Boundary m_internal;
// Store all needed data for travels outside object

View file

@ -320,7 +320,7 @@ std::string GCodeWriter::reset_e(bool force)
return "";
if (m_extruder != nullptr) {
if (m_extruder->E() == 0. && ! force)
if (is_zero(m_extruder->E()) && ! force)
return "";
m_extruder->reset_E();
}
@ -709,7 +709,7 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std
length = 1;
std::string gcode;
if (double dE = m_extruder->retract(length, restart_extra); dE != 0) {
if (double dE = m_extruder->retract(length, restart_extra); !is_zero(dE)) {
if (this->config.use_firmware_retraction) {
gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n";
}
@ -737,7 +737,7 @@ std::string GCodeWriter::unretract()
if (FLAVOR_IS(gcfMakerWare))
gcode = "M101 ; extruder on\n";
if (double dE = m_extruder->unretract(); dE != 0) {
if (double dE = m_extruder->unretract(); !is_zero(dE)) {
if (this->config.use_firmware_retraction) {
gcode += FLAVOR_IS(gcfMachinekit) ? "G23 ; unretract\n" : "G11 ; unretract\n";
gcode += this->reset_e();

View file

@ -819,7 +819,7 @@ static std::vector<std::string> s_Preset_printer_options {
"printer_technology",
"printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor",
"fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs",
"single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode",
"single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode", "change_extrusion_role_gcode",
"printer_model", "printer_variant", "printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",
"default_print_profile", "inherits",
"silent_mode",

View file

@ -3970,6 +3970,15 @@ def = this->add("filament_loading_speed", coFloats);
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("change_extrusion_role_gcode", coString);
def->label = L("Change extrusion role G-code");
def->tooltip = L("This gcode is inserted when the extrusion role is changed");
def->multiline = true;
def->full_width = true;
def->height = 5;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("top_surface_line_width", coFloatOrPercent);
def->label = L("Top surface");
def->category = L("Quality");

View file

@ -969,6 +969,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, single_extruder_multi_material_priming))
((ConfigOptionBool, wipe_tower_no_sparse_layers))
((ConfigOptionString, change_filament_gcode))
((ConfigOptionString, change_extrusion_role_gcode))
((ConfigOptionFloat, travel_speed))
((ConfigOptionFloat, travel_speed_z))
((ConfigOptionBool, silent_mode))

View file

@ -278,6 +278,12 @@ constexpr inline T sqr(T x)
return x * x;
}
template<typename Number> constexpr
inline bool is_zero(Number value)
{
return std::fabs(double(value)) < 1e-6;
}
template <typename T, typename Number>
constexpr inline T lerp(const T& a, const T& b, Number t)
{

View file

@ -2123,16 +2123,15 @@ void GUI_App::init_app_config()
app_config = new AppConfig();
//app_config = new AppConfig(is_editor() ? AppConfig::EAppMode::Editor : AppConfig::EAppMode::GCodeViewer);
m_config_corrupted = false;
// load settings
m_app_conf_exists = app_config->exists();
if (m_app_conf_exists) {
std::string error = app_config->load();
if (!error.empty()) {
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
throw Slic3r::RuntimeError(
_u8L("OrcaSlicer configuration file may be corrupted and is not abled to be parsed."
"Please delete the file and try again.") +
"\n\n" + app_config->config_path() + "\n\n" + error);
// Orca: if the config file is corrupted, we will show a error dialog and create a default config file.
m_config_corrupted = true;
}
// Save orig_version here, so its empty if no app_config existed before this run.
m_last_config_version = app_config->orig_version();//parse_semver_from_ini(app_config->config_path());
@ -2749,6 +2748,7 @@ bool GUI_App::on_init_inner()
if (m_post_initialized && app_config->dirty())
app_config->save();
});
m_initialized = true;
@ -2756,6 +2756,13 @@ bool GUI_App::on_init_inner()
flush_logs();
BOOST_LOG_TRIVIAL(info) << "finished the gui app init";
if (m_config_corrupted) {
m_config_corrupted = false;
show_error(nullptr,
_u8L(
"The OrcaSlicer configuration file may be corrupted and cannot be parsed.\nOrcaSlicer has attempted to recreate the "
"configuration file.\nPlease note, application settings will be lost, but printer profiles will not be affected."));
}
//BBS: delete splash screen
delete scrn;
return true;

View file

@ -658,6 +658,7 @@ private:
bool m_datadir_redefined { false };
std::string m_older_data_dir_path;
boost::optional<Semver> m_last_config_version;
bool m_config_corrupted { false };
};
DECLARE_APP(GUI_App)

View file

@ -149,6 +149,17 @@ bool GLGizmoText::on_init()
reset_text_info();
m_desc["font"] = _L("Font");
m_desc["size"] = _L("Size");
m_desc["thickness"] = _L("Thickness");
m_desc["text_gap"] = _L("Text Gap");
m_desc["angle"] = _L("Angle");
m_desc["embeded_depth"] = _L("Embeded\ndepth");
m_desc["input_text"] = _L("Input text");
m_desc["surface"] = _L("Surface");
m_desc["horizontal_text"] = _L("Horizontal text");
m_desc["rotate_text_caption"] = _L("Shift + Mouse move up or dowm");
m_desc["rotate_text"] = _L("Rotate text");
@ -667,13 +678,13 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 4.0f * currt_scale);
GizmoImguiBegin("Text", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
float space_size = m_imgui->get_style_scaling() * 8;
float font_cap = m_imgui->calc_text_size(_L("Font")).x;
float size_cap = m_imgui->calc_text_size(_L("Size")).x;
float thickness_cap = m_imgui->calc_text_size(_L("Thickness")).x;
float input_cap = m_imgui->calc_text_size(_L("Input text")).x;
float depth_cap = m_imgui->calc_text_size(_L("Embeded")).x;
float caption_size = std::max(std::max(font_cap, size_cap), std::max(depth_cap, input_cap)) + space_size + ImGui::GetStyle().WindowPadding.x;
const float space_size = m_imgui->get_style_scaling() * 8;
const std::array<std::string, 7> cap_array = std::array<std::string, 7>{ "font", "size", "thickness", "text_gap", "angle", "embeded_depth", "input_text" };
float caption_size = 0.0f;
for (const auto &t : cap_array) {
caption_size = std::max(caption_size, m_imgui->calc_text_size(m_desc[t]).x);
}
caption_size += space_size + ImGui::GetStyle().WindowPadding.x;
float input_text_size = m_imgui->scaled(10.0f);
float button_size = ImGui::GetFrameHeight();
@ -700,7 +711,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Font"));
m_imgui->text(m_desc["font"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(list_width);
push_combo_style(currt_scale);
@ -740,7 +751,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
ImGui::PopStyleVar(2);
pop_combo_style();
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Size"));
m_imgui->text(m_desc["size"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(input_size);
if(ImGui::InputFloat("###font_size", &m_font_size, 0.0f, 0.0f, "%.2f"))
@ -767,7 +778,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
ImGui::PopStyleVar(3);
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Thickness"));
m_imgui->text(m_desc["thickness"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(list_width);
float old_value = m_thickness;
@ -782,7 +793,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
const float drag_left_width = caption_size + slider_width + space_size;
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Text Gap"));
m_imgui->text(m_desc["text_gap"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(slider_width);
if (m_imgui->bbl_slider_float_style("##text_gap", &m_text_gap, -100, 100, "%.2f", 1.0f, true))
@ -793,7 +804,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
m_need_update_text = true;
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Angle"));
m_imgui->text(m_desc["angle"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(slider_width);
if (m_imgui->bbl_slider_float_style("##angle", &m_rotate_angle, 0, 360, "%.2f", 1.0f, true))
@ -804,7 +815,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
m_need_update_text = true;
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Embeded\ndepth"));
m_imgui->text(m_desc["embeded_depth"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(list_width);
old_value = m_embeded_depth;
@ -815,7 +826,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
m_need_update_text = true;
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Input text"));
m_imgui->text(m_desc["input_text"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(list_width);
@ -833,12 +844,12 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
ImGui::SameLine(caption_size);
ImGui::AlignTextToFramePadding();
if (m_imgui->bbl_checkbox(_L("Surface"), m_is_surface_text))
if (m_imgui->bbl_checkbox(m_desc["surface"], m_is_surface_text))
m_need_update_text = true;
ImGui::SameLine();
ImGui::AlignTextToFramePadding();
if (m_imgui->bbl_checkbox(_L("Horizontal text"), m_keep_horizontal))
if (m_imgui->bbl_checkbox(m_desc["horizontal_text"], m_keep_horizontal))
m_need_update_text = true;
//ImGui::SameLine();

View file

@ -16,7 +16,7 @@ constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2
static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar
static const float HORIZONTAL_SLIDER_WINDOW_HEIGHT = 64.0f;
static const float VERTICAL_SLIDER_WINDOW_WIDTH = 124.0f;
static const float VERTICAL_SLIDER_WINDOW_WIDTH = 160.0f;
static const float GROOVE_WIDTH = 12.0f;
static const ImVec2 ONE_LAYER_MARGIN = ImVec2(20.0f, 20.0f);
static const ImVec2 ONE_LAYER_BUTTON_SIZE = ImVec2(28.0f, 28.0f);

View file

@ -534,7 +534,7 @@ void PartPlate::calc_vertex_for_icons(int index, PickingModel &model)
ExPolygon poly;
auto bed_ext = get_extents(m_shape);
Vec2d p = bed_ext[2];
if (m_plater->get_build_volume_type() == BuildVolume_Type::Circle)
if (m_plater && m_plater->get_build_volume_type() == BuildVolume_Type::Circle)
p[1] -= std::max(
0.0, (bed_ext.size()(1) - 5 * PARTPLATE_ICON_SIZE - 4 * PARTPLATE_ICON_GAP_Y - PARTPLATE_ICON_GAP_TOP) / 2);

View file

@ -3232,6 +3232,17 @@ void TabPrinter::build_fff()
option.opt.height = gcode_field_height;//150;
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("Change extrusion role G-code"), L"param_gcode", 0);
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
option = optgroup->get_option("change_extrusion_role_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
option.opt.height = gcode_field_height;//150;
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("Pause G-code"), L"param_gcode", 0);
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);