diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 35c7151911..da584fe622 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -288,12 +288,12 @@ inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, const Pol } } -inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) { dst.reserve(dst.size() + polylines.size()); for (Polyline &polyline : polylines) if (polyline.is_valid()) { - ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height); + ExtrusionPath *extrusion_path = can_reverse ? new ExtrusionPath(role, mm3_per_mm, width, height) : new ExtrusionPathOriented(role, mm3_per_mm, width, height); dst.push_back(extrusion_path); extrusion_path->polyline = std::move(polyline); } diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index f8e1dc6d75..fb2d12a680 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -461,23 +461,24 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: } active_speed_modifier = size_t(-1); } else if (boost::starts_with(sline, m_toolchange_prefix)) { - unsigned int new_extruder; + unsigned int new_extruder = 0; auto res = std::from_chars(sline.data() + m_toolchange_prefix.size(), sline.data() + sline.size(), new_extruder); - // Only change extruder in case the number is meaningful. User could provide an out-of-range index through custom gcodes - those shall be ignored. - if (new_extruder < map_extruder_to_per_extruder_adjustment.size()) { - if (new_extruder != current_extruder) { - // Switch the tool. - line.type = CoolingLine::TYPE_SET_TOOL; - current_extruder = new_extruder; - adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]]; + if (res.ec != std::errc::invalid_argument) { + // Only change extruder in case the number is meaningful. User could provide an out-of-range index through custom gcodes - those shall be ignored. + if (new_extruder < map_extruder_to_per_extruder_adjustment.size()) { + if (new_extruder != current_extruder) { + // Switch the tool. + line.type = CoolingLine::TYPE_SET_TOOL; + current_extruder = new_extruder; + adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]]; + } + } + else { + // Only log the error in case of MM printer. Single extruder printers likely ignore any T anyway. + if (map_extruder_to_per_extruder_adjustment.size() > 1) + BOOST_LOG_TRIVIAL(error) << "CoolingBuffer encountered an invalid toolchange, maybe from a custom gcode: " << sline; } } - else { - // Only log the error in case of MM printer. Single extruder printers likely ignore any T anyway. - if (map_extruder_to_per_extruder_adjustment.size() > 1) - BOOST_LOG_TRIVIAL(error) << "CoolingBuffer encountered an invalid toolchange, maybe from a custom gcode: " << sline; - } - } else if (boost::starts_with(sline, ";_BRIDGE_FAN_START")) { line.type = CoolingLine::TYPE_BRIDGE_FAN_START; } else if (boost::starts_with(sline, ";_BRIDGE_FAN_END")) { @@ -785,9 +786,9 @@ std::string CoolingBuffer::apply_layer_cooldown( if (line_start > pos) new_gcode.append(pos, line_start - pos); if (line->type & CoolingLine::TYPE_SET_TOOL) { - unsigned int new_extruder; + unsigned int new_extruder = 0; auto res = std::from_chars(line_start + m_toolchange_prefix.size(), line_end, new_extruder); - if (new_extruder != m_current_extruder) { + if (res.ec != std::errc::invalid_argument && new_extruder != m_current_extruder) { m_current_extruder = new_extruder; change_extruder_set_fan(); } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 6e71c782e3..c71d950590 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -754,7 +754,7 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector current_dependencies{}; if (shell > 0) { for (const auto &prev_path : dependencies[shell - 1]) { - if (paths_touch(get_path(current_path), get_path(prev_path.first), extrusion_spacing * 2.0)) { + if (paths_touch(get_path(current_path), get_path(prev_path.first), extrusion_spacing * 1.5f)) { current_dependencies.insert(prev_path.first); }; } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index b6d8eb2c15..e4cc8497f9 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1204,7 +1204,7 @@ void Print::alert_when_supports_needed() for (const auto &obj : objects_isssues) { for (const auto &issue : obj.second) { po_by_support_issues[issue].push_back(obj.first); - if (issue.first == SupportSpotsGenerator::SupportPointCause::SeparationFromBed){ + if (issue.first == SupportSpotsGenerator::SupportPointCause::SeparationFromBed && !obj.first->has_brim()){ recommend_brim = true; } } @@ -1235,7 +1235,9 @@ void Print::alert_when_supports_needed() } } - message += "\n" + L("Consider enabling supports") + (recommend_brim ? (" " + L("and/or brim")) : "") + "."; + bool brim_or_supp = recommend_brim && po_by_support_issues.size() < 2; + auto brim_part = " " + (brim_or_supp ? L("or") : L("and")) + " " + L("brim"); + message += "\n" + L("Consider enabling supports") + (recommend_brim ? brim_part : "") + "."; if (objects_isssues.size() > 0) { this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, message); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 08ae899f60..11d34cafdc 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -3246,6 +3246,10 @@ static Polylines draw_perimeters(const ExPolygon &expoly, double clip_length) for (size_t i = 0; i <= expoly.holes.size(); ++ i) { Polyline pl(i == 0 ? expoly.contour.points : expoly.holes[i - 1].points); pl.points.emplace_back(pl.points.front()); + if (i > 0) + // It is a hole, reverse it. + pl.reverse(); + // so that all contours are CCW oriented. pl.clip_end(clip_length); polylines.emplace_back(std::move(pl)); } @@ -3351,13 +3355,17 @@ static inline void tree_supports_generate_paths( const double anchor_length = spacing * 6.; ClipperLib_Z::Paths anchor_candidates; for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) { + std::unique_ptr eec; double area = expoly.area(); if (area > sqr(scaled(5.))) { + eec = std::make_unique(); + // Don't reoder internal / external loops of the same island, always start with the internal loop. + eec->no_sort = true; // Make the tree branch stable by adding another perimeter. ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); if (level2.size() == 1) { Polylines polylines; - extrusion_entities_append_paths(dst, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), // Disable reversal of the path, always start with the anchor, always print CCW. false); expoly = level2.front(); @@ -3369,20 +3377,21 @@ static inline void tree_supports_generate_paths( // The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection. anchor_candidates.clear(); shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates); - // Orient all contours CCW. + // Orient all contours CW. for (auto &path : anchor_candidates) - if (ClipperLib_Z::Area(path) < 0) + if (ClipperLib_Z::Area(path) > 0) std::reverse(path.begin(), path.end()); // Draw the perimeters. Polylines polylines; polylines.reserve(expoly.holes.size() + 1); - for (size_t idx_loop = 0; idx_loop <= expoly.holes.size(); ++ idx_loop) { + for (size_t idx_loop = 0; idx_loop < expoly.num_contours(); ++ idx_loop) { // Open the loop with a seam. - const Polygon &loop = idx_loop == 0 ? expoly.contour : expoly.holes[idx_loop - 1]; + const Polygon &loop = expoly.contour_or_hole(idx_loop); Polyline pl(loop.points); - // Orient all contours CCW. - if (loop.area() < 0) + // Orient all contours CW, because the anchor will be added to the end of polyline while we want to start a loop with the anchor. + if (idx_loop == 0) + // It is an outer contour. pl.reverse(); pl.points.emplace_back(pl.points.front()); pl.clip_end(clip_length); @@ -3421,7 +3430,7 @@ static inline void tree_supports_generate_paths( } if (d2min < sqr(flow.scaled_width() * 3.)) { // Try to cut an anchor from the closest_contour. - // Both closest_contour and pl are CCW oriented. + // Both closest_contour and pl are CW oriented. pl.points.emplace_back(closest_point.cast()); const ClipperLib_Z::Path &path = *closest_contour; double remaining_length = anchor_length - (seam_pt - closest_point).norm(); @@ -3460,9 +3469,15 @@ static inline void tree_supports_generate_paths( pl.reverse(); polylines.emplace_back(std::move(pl)); } - extrusion_entities_append_paths(dst, polylines, ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + + ExtrusionEntitiesPtr &out = eec ? eec->entities : dst; + extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), // Disable reversal of the path, always start with the anchor, always print CCW. false); + if (eec) { + std::reverse(eec->entities.begin(), eec->entities.end()); + dst.emplace_back(eec.release()); + } } } diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index cbb779be07..713a0c1c4d 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -2500,8 +2500,10 @@ static void create_nodes_from_area( // Point is chosen based on an inaccurate estimate where the branches will split into two, but every point inside the influence area would produce a valid result. { SupportElements *layer_above = move_bounds.size() > 1 ? &move_bounds[1] : nullptr; - for (SupportElement &elem : *layer_above) - elem.state.marked = false; + if (layer_above) { + for (SupportElement &elem : *layer_above) + elem.state.marked = false; + } for (SupportElement &init : move_bounds.front()) { init.state.result_on_layer = move_inside_if_outside(init.influence_area, init.state.next_position); // Also set the parent nodes, as these will be required for the first iteration of the loop below and mark the parent nodes. diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index de61488ef6..ca718e5e71 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -201,7 +201,14 @@ bool Field::is_matched(const std::string& string, const std::string& pattern) return std::regex_match(string, regex_pattern); } -static wxString na_value() { return _(L("N/A")); } +static wxString na_value(bool for_spin_ctrl = false) +{ +#ifdef __linux__ + if (for_spin_ctrl) + return ""; +#endif + return _(L("N/A")); +} void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true*/) { @@ -888,7 +895,7 @@ void SpinCtrl::set_value(const boost::any& value, bool change_event/* = false*/) if (m_opt.nullable) { const bool m_is_na_val = tmp_value == ConfigOptionIntsNullable::nil_value(); if (m_is_na_val) - dynamic_cast(window)->SetValue(na_value()); + dynamic_cast(window)->SetValue(na_value(true)); else { m_last_meaningful_value = value; dynamic_cast(window)->SetValue(tmp_value); @@ -909,7 +916,7 @@ void SpinCtrl::set_last_meaningful_value() void SpinCtrl::set_na_value() { - dynamic_cast(window)->SetValue(na_value()); + dynamic_cast(window)->SetValue(na_value(true)); m_value = ConfigOptionIntsNullable::nil_value(); propagate_value(); } @@ -917,7 +924,7 @@ void SpinCtrl::set_na_value() boost::any& SpinCtrl::get_value() { wxSpinCtrl* spin = static_cast(window); - if (spin->GetTextValue() == na_value()) + if (spin->GetTextValue() == na_value(true)) return m_value; int value = spin->GetValue(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 8a4bee03ed..69c566819e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -943,7 +943,7 @@ bool MainFrame::can_export_supports() const const PrintObjects& objects = m_plater->sla_print().objects(); for (const SLAPrintObject* object : objects) { - if (!object->support_mesh().empty()) + if (!object->support_mesh().empty() || !object->pad_mesh().empty()) { can_export = true; break;