diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 9dde9b176ef..2d339c0a104 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -59,8 +59,124 @@ bool export_clipper_input_polygons_bin(const char *path, const ClipperLib::Paths namespace ClipperUtils { Points EmptyPathsProvider::s_empty_points; Points SinglePathProvider::s_end; + + + // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) + // polygon. Useful as an optimization for expensive ClipperLib operations, for example when clipping source + // polygons one by one with a set of polygons covering the whole layer below. + template + inline void clip_clipper_polygon_with_subject_bbox_templ(const PointsType & src, + const BoundingBox &bbox, + PointsType & out) + { + using PointType = typename PointsType::value_type; + out.clear(); + const size_t cnt = src.size(); + if (cnt < 3) + return; + enum class Side { Left = 1, Right = 2, Top = 4, Bottom = 8 }; + auto sides = [bbox](const PointType &p) { + return int(p.x() < bbox.min.x()) * int(Side::Left) + int(p.x() > bbox.max.x()) * int(Side::Right) + + int(p.y() < bbox.min.y()) * int(Side::Bottom) + int(p.y() > bbox.max.y()) * int(Side::Top); + }; + int sides_prev = sides(src.back()); + int sides_this = sides(src.front()); + const size_t last = cnt - 1; + for (size_t i = 0; i < last; ++i) { + int sides_next = sides(src[i + 1]); + if ( // This point is inside. Take it. + sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (sides_prev & sides_this & sides_next) == 0) { + out.emplace_back(src[i]); + sides_prev = sides_this; + } else { + // All the three points (this, prev, next) are outside at the same side. + // Ignore this point. + } + sides_this = sides_next; + } + // Never produce just a single point output polygon. + if (!out.empty()) + if (int sides_next = sides(out.front()); + // The last point is inside. Take it. + sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (sides_prev & sides_this & sides_next) == 0) + out.emplace_back(src.back()); + } + void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out) + { + clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); + } + void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out) + { + clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); + } + template + [[nodiscard]] PointsType clip_clipper_polygon_with_subject_bbox_templ(const PointsType & src, + const BoundingBox &bbox) + { + PointsType out; + clip_clipper_polygon_with_subject_bbox(src, bbox, out); + return out; + } + [[nodiscard]] Points clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox) + { + return clip_clipper_polygon_with_subject_bbox_templ(src, bbox); + } + [[nodiscard]] ZPoints clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox) + { + return clip_clipper_polygon_with_subject_bbox_templ(src, bbox); + } + void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out) + { + clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points); + } + [[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox) + { + Polygon out; + clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points); + return out; + } + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox) + { + Polygons out; + out.reserve(src.size()); + for (const Polygon &p : src) out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox)); + out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), + out.end()); + return out; + } + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox) + { + Polygons out; + out.reserve(src.num_contours()); + out.emplace_back(clip_clipper_polygon_with_subject_bbox(src.contour, bbox)); + for (const Polygon &p : src.holes) out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox)); + out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), + out.end()); + return out; + } + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox) + { + Polygons out; + out.reserve(number_polygons(src)); + for (const ExPolygon &p : src) { + Polygons temp = clip_clipper_polygons_with_subject_bbox(p, bbox); + out.insert(out.end(), temp.begin(), temp.end()); + } + + out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), + out.end()); + return out; + } } + + static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree) { struct Inner { diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 35109821c6b..55a566851f5 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -297,7 +297,23 @@ namespace ClipperUtils { const SurfacesPtr &m_surfaces; size_t m_size; }; -} + + using ZPoint = Vec3i32; + using ZPoints = std::vector; + + // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon. + // Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by + // one with a set of polygons covering the whole layer below. + void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out); + void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out); + [[nodiscard]] Points clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox); + [[nodiscard]] ZPoints clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox); + void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out); + [[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox); + } // Perform union of input polygons using the non-zero rule, convert to ExPolygons. ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, bool do_union = false); diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 9445f98ed7c..5ab3fc9aac5 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -506,9 +506,9 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.length = std::abs(dif[3]); } line.feedrate = new_pos[4]; - if (line.feedrate > 0.f && line.length > 0.f) { + if (line.feedrate > 0.f && /*line.length*/ dxy2 > 0.f) { assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0); - assert(active_speed_modifier != size_t(-1)); + //assert(active_speed_modifier != size_t(-1)); // can happen in custom gcode. line.type |= current_stamp; } assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0 || line.feedrate > 0.f); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 00771703452..084c43533d7 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -202,7 +202,43 @@ ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const Polygons last_p = to_polygons(last); - Arachne::WallToolPaths wallToolPaths(last_p, this->get_ext_perimeter_spacing(), this->get_ext_perimeter_width(), this->get_perimeter_spacing(), this->get_perimeter_width(), coord_t(loop_number + 1), 0, this->layer->height, *this->object_config, *this->print_config); + // only_one_perimeter_top, from orca + std::vector out_shell; + if (loop_number > 0 && this->config->only_one_perimeter_top && !surface.has_mod_bridge() && upper_slices != nullptr) { + // Check if current layer has surfaces that are not covered by upper layer (i.e., top surfaces) + ExPolygons non_top_polygons; + ExPolygons fill_clip; + + split_top_surfaces(lower_slices, upper_slices, last, result.top_fills, non_top_polygons, fill_clip); + + if (result.top_fills.empty()) { + // No top surfaces, no special handling needed + } else { + // First we slice the outer shell + Polygons last_p = to_polygons(last); + Arachne::WallToolPaths wallToolPaths(last_p, this->get_ext_perimeter_spacing(),this->get_ext_perimeter_width(), + this->get_perimeter_spacing(), this->get_perimeter_width(), coord_t(1), 0, + this->layer->height, *this->object_config, *this->print_config); + out_shell = wallToolPaths.getToolPaths(); + // Make sure infill not overlap with wall + result.top_fills = intersection_ex(result.top_fills, wallToolPaths.getInnerContour()); + + if (!result.top_fills.empty()) { + // Then get the inner part that needs more walls + last = intersection_ex(non_top_polygons, wallToolPaths.getInnerContour()); + loop_number = 0; + } else { + // Give up the outer shell because we don't have any meaningful top surface + out_shell.clear(); + } + } + } + + + + Arachne::WallToolPaths wallToolPaths(last_p, this->get_ext_perimeter_spacing(), this->get_ext_perimeter_width(), + this->get_perimeter_spacing(), this->get_perimeter_width(), coord_t(loop_number + 1), 0, + this->layer->height, *this->object_config, *this->print_config); std::vector perimeters = wallToolPaths.getToolPaths(); loop_number = int(perimeters.size()) - 1; @@ -372,6 +408,104 @@ ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const return result; } +//TODO: merge with 'store surface for top infill if only_one_perimeter_top' +void PerimeterGenerator::split_top_surfaces(const ExPolygons *lower_slices, + const ExPolygons *upper_slices, + const ExPolygons &orig_polygons, + ExPolygons & top_fills, + ExPolygons & non_top_polygons, + ExPolygons & fill_clip) +{ + // other perimeters + coord_t perimeter_width = this->perimeter_flow.scaled_width(); + coord_t perimeter_spacing = this->perimeter_flow.scaled_spacing(); + + // external perimeters + coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width(); + coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing(); + + bool has_gap_fill = false; // this->config->gap_infill_speed.value > 0; + + // split the polygons with top/not_top + // get the offset from solid surface anchor* + const int32_t peri_count = this->config->perimeters.value; + coord_t offset_top_surface = scale_t( + 1.5 * (peri_count == 0 ? + 0. : unscaled(double(ext_perimeter_width + perimeter_spacing * int(peri_count - int(1)))))); + // if possible, try to not push the extra perimeters inside the sparse infill + if (offset_top_surface > 0.9 * (this->config->perimeters <= 1 ? 0. : (perimeter_spacing * (peri_count - 1)))) + offset_top_surface -= coord_t(0.9 * (peri_count <= 1 ? 0. : (perimeter_spacing * (peri_count - 1)))); + else + offset_top_surface = 0; + // don't takes into account too thin areas + // skip if the exposed area is smaller than "min_width_top_surface" + coordf_t min_width_top_surface = std::max(coordf_t(ext_perimeter_spacing / 2 + 10), + scale_d(this->config->min_width_top_surface.get_abs_value(unscaled(perimeter_width)))); + + Polygons grown_upper_slices = offset(*upper_slices, min_width_top_surface); + + // get boungding box of last + BoundingBox last_box = get_extents(orig_polygons); + last_box.offset(SCALED_EPSILON); + + // get the Polygons upper the polygon this layer + Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(grown_upper_slices, last_box); + + // set the clip to a virtual "second perimeter" + fill_clip = offset_ex(orig_polygons, -double(ext_perimeter_spacing)); + // get the real top surface + ExPolygons grown_lower_slices; + ExPolygons bridge_checker; + auto nozzle_diameter = 0.4; // this->print_config->nozzle_diameter.get_at(this->config->wall_filament - 1); + // Check whether surface be bridge or not + if (lower_slices != NULL) { + // BBS: get the Polygons below the polygon this layer + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, last_box); + coordf_t bridge_offset = std::max(coordf_t(ext_perimeter_spacing), (coordf_t(perimeter_width))); + // SoftFever: improve bridging + const coordf_t bridge_margin = scale_d(this->config->bridged_infill_margin.get_abs_value(unscaled(perimeter_width))); + bridge_checker = offset_ex(diff_ex(orig_polygons, lower_polygons_series_clipped, ApplySafetyOffset::Yes), + 1.5 * bridge_offset + bridge_margin + perimeter_spacing / 2); + } + ExPolygons delete_bridge = diff_ex(orig_polygons, bridge_checker, ApplySafetyOffset::Yes); + + ExPolygons top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes); + // get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the + // min_width_top_surface we removed a bit before) + ExPolygons temp_gap = diff_ex(top_polygons, fill_clip); + ExPolygons inner_polygons = diff_ex(orig_polygons, + offset_ex(top_polygons, offset_top_surface + min_width_top_surface - + double(ext_perimeter_spacing / 2)), + ApplySafetyOffset::Yes); + // get the enlarged top surface, by using inner_polygons instead of upper_slices, and clip it for it to be exactly + // the polygons to fill. + top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes); + // increase by half peri the inner space to fill the frontier between last and stored. + top_fills = union_ex(top_fills, top_polygons); + // set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't + // go outside even with a 100% overlap. + double infill_spacing_unscaled = this->config->infill_extrusion_width.get_abs_value(nozzle_diameter); + if (infill_spacing_unscaled == 0) + infill_spacing_unscaled = Flow::auto_extrusion_width(frInfill, nozzle_diameter); + fill_clip = offset_ex(orig_polygons, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2)); + // ExPolygons oldLast = last; + + non_top_polygons = intersection_ex(inner_polygons, orig_polygons); + if (has_gap_fill) + non_top_polygons = union_ex(non_top_polygons, temp_gap); + //{ + // std::stringstream stri; + // stri << this->layer_id << "_1_"<< i <<"_only_one_peri"<< ".svg"; + // SVG svg(stri.str()); + // svg.draw(to_polylines(top_fills), "green"); + // svg.draw(to_polylines(inner_polygons), "yellow"); + // svg.draw(to_polylines(top_polygons), "cyan"); + // svg.draw(to_polylines(oldLast), "orange"); + // svg.draw(to_polylines(last), "red"); + // svg.Close(); + //} +} + void PerimeterGenerator::process() { // other perimeters diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index f2c85cbb2b2..9486e045960 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -172,7 +172,9 @@ class PerimeterGenerator { ExtrusionLoop _extrude_and_cut_loop(const PerimeterGeneratorLoop& loop, const Point entryPoint, const Line& direction = Line(Point(0, 0), Point(0, 0)), bool enforce_loop = false) const; // sub-function of _traverse_and_join_loops, find the good splot to cut a loop to be able to join it with an other one PerimeterIntersectionPoint _get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) const; - + // for archne, to have one_peri_ont_op + void split_top_surfaces(const ExPolygons *lower_slices, const ExPolygons *upper_slices, const ExPolygons &orig_polygons, + ExPolygons & top_fills, ExPolygons & non_top_polygons, ExPolygons & fill_clip); }; diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 9da3b0a930e..a0a03ba6de7 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -357,12 +357,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) "thin_perimeters", "overhangs_reverse", "perimeter_round_corners"}) toggle_field(el, have_perimeters && !have_arachne); - toggle_field("only_one_perimeter_top", have_perimeters); // with arachne, it will only do it for the last layer + toggle_field("only_one_perimeter_top", have_perimeters); toggle_field("only_one_perimeter_first_layer", config->opt_int("perimeters") > 1); toggle_field("overhangs_width", config->option("overhangs_width_speed")->value > 0); toggle_field("overhangs_reverse_threshold", have_perimeters && config->opt_bool("overhangs_reverse")); toggle_field("overhangs_speed_enforce", have_perimeters && !config->opt_bool("perimeter_loop")); - toggle_field("min_width_top_surface", have_perimeters && config->opt_bool("only_one_perimeter_top") && !have_arachne); + toggle_field("min_width_top_surface", have_perimeters && config->opt_bool("only_one_perimeter_top")); toggle_field("thin_perimeters_all", have_perimeters && config->option("thin_perimeters")->get_float() != 0 && !have_arachne); bool have_thin_wall = !have_arachne && have_perimeters; toggle_field("thin_walls", have_thin_wall); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 63022e01bfd..a6c7b580ed8 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -569,25 +569,27 @@ void Tab::update_label_colours() m_phony_label_clr = wxGetApp().get_label_clr_phony(); //update options "decoration" - for (const std::pair &opt : m_options_list) - { + for (const auto &opt : m_options_list) { + const std::string &opt_key = opt.first; + const int & opt_idx = opt.second.first; + const int & opt_status = opt.second.second; const wxColour *color = &m_sys_label_clr; // value isn't equal to system value - if ((opt.second & osSystemValue) == 0) { + if ((opt_status & osSystemValue) == 0) { // value is equal to last saved - if ((opt.second & osInitValue) != 0) + if ((opt_status & osInitValue) != 0) color = &m_default_label_clr; // value is modified else color = &m_modified_label_clr; } - if ((opt.second & osCurrentPhony) != 0) + if ((opt_status & osCurrentPhony) != 0) color = &m_phony_label_clr; else { - if ((opt.second & osInitPhony) != 0) + if ((opt_status & osInitPhony) != 0) color = &m_modified_label_clr; - else if ((opt.second & osSystemPhony) != 0) + else if ((opt_status & osSystemPhony) != 0) color = &m_default_label_clr; } @@ -631,12 +633,14 @@ void Tab::decorate() { Field* field = nullptr; wxColour* colored_label_clr = nullptr; - + const std::string &opt_key = opt.first; + const int & opt_idx = opt.second.first; + const int & opt_status = opt.second.second; if (OptionsGroup::is_option_without_field(opt.first)) colored_label_clr = (m_colored_Label_colors.find(opt.first) == m_colored_Label_colors.end()) ? nullptr : &m_colored_Label_colors.at(opt.first); if (!colored_label_clr) { - field = get_field(opt.first); + field = get_field(opt_key, opt_idx); if (!field) continue; } @@ -652,18 +656,18 @@ void Tab::decorate() const wxString* tt = &m_tt_value_revert; // value isn't equal to system value - if ((opt.second & osSystemValue) == 0) { + if ((opt_status & osSystemValue) == 0) { is_nonsys_value = true; sys_icon = m_bmp_non_system; sys_tt = m_tt_non_system; // value is equal to last saved - if ((opt.second & osInitValue) != 0) + if ((opt_status & osInitValue) != 0) color = &m_default_label_clr; // value is modified else color = &m_modified_label_clr; } - if ((opt.second & osInitValue) != 0) + if ((opt_status & osInitValue) != 0) { is_modified_value = false; icon = &m_bmp_white_bullet; @@ -671,12 +675,12 @@ void Tab::decorate() } //color for phony things - if ((opt.second & osCurrentPhony) != 0) + if ((opt_status & osCurrentPhony) != 0) color = &m_phony_label_clr; else { - if ((opt.second & osInitPhony) != 0) + if ((opt_status & osInitPhony) != 0) color = &m_modified_label_clr; - else if ((opt.second & osSystemPhony) != 0) + else if ((opt_status & osSystemPhony) != 0) color = &m_default_label_clr; } @@ -714,8 +718,8 @@ void Tab::decorate() for (const std::string &dep : field->m_opt.depends_on) { const auto& it = m_options_list.find(dep); if (it != m_options_list.end()) { - is_not_sys |= ((it->second & osSystemValue) == 0); - is_not_initial |= ((it->second & osInitValue) == 0); + is_not_sys |= ((it->second.second & osSystemValue) == 0); + is_not_initial |= ((it->second.second & osInitValue) == 0); } } @@ -773,7 +777,7 @@ void Tab::update_changed_ui() } for (auto& it : m_options_list) - it.second = m_opt_status_value; + it.second.second = m_opt_status_value; dirty_options.insert(dirty_options.end(), m_options_dirty.begin(), m_options_dirty.end()); m_options_dirty.clear(); @@ -784,21 +788,21 @@ void Tab::update_changed_ui() for (auto& opt_key : m_presets->get_edited_preset().config.keys()) { if (edited_preset.config.option(opt_key)->is_phony()) //ensure that osCurrentPhony is in the bitmask - m_options_list[opt_key] |= osCurrentPhony; + m_options_list[opt_key].second |= osCurrentPhony; if (selected_preset.config.option(opt_key) && selected_preset.config.option(opt_key)->is_phony()) - m_options_list[opt_key] |= osInitPhony; + m_options_list[opt_key].second |= osInitPhony; if (system_preset && system_preset->config.option(opt_key) && system_preset->config.option(opt_key)->is_phony()) - m_options_list[opt_key] |= osSystemPhony; + m_options_list[opt_key].second |= osSystemPhony; } //don't let option that were phony be resetable. for (auto opt_key : dirty_options) - if( (m_options_list[opt_key] & osInitPhony) == 0) + if ((m_options_list[opt_key].second & osInitPhony) == 0) //ensure that osInitValue is not in the bitmask - m_options_list[opt_key] &= ~osInitValue; + m_options_list[opt_key].second &= ~osInitValue; for (auto opt_key : nonsys_options) - if ((m_options_list[opt_key] & osSystemPhony) == 0) - m_options_list[opt_key] &= ~osSystemValue; + if ((m_options_list[opt_key].second & osSystemPhony) == 0) + m_options_list[opt_key].second &= ~osSystemValue; decorate(); @@ -814,15 +818,15 @@ void Tab::init_options_list() m_options_list.clear(); for (const std::string& opt_key : m_config->keys()) - m_options_list.emplace(opt_key, m_opt_status_value); + m_options_list.emplace(opt_key, std::pair(-1, m_opt_status_value)); } template -void add_correct_opts_to_options_list(const std::string &opt_key, std::map& map, Tab *tab, const int& value) +void add_correct_opts_to_options_list(const std::string &opt_key, std::map>& map, Tab *tab, const int& value) { T *opt_cur = static_cast(tab->get_config()->option(opt_key)); for (size_t i = 0; i < opt_cur->values.size(); i++) - map.emplace(opt_key + "#" + std::to_string(i), value); + map.emplace(opt_key /* + "#" + std::to_string(i)*/, std::pair{i, value}); } void TabPrinter::init_options_list() @@ -833,7 +837,7 @@ void TabPrinter::init_options_list() for (const std::string& opt_key : m_config->keys()) { if (opt_key == "bed_shape") { - m_options_list.emplace(opt_key, m_opt_status_value); + m_options_list.emplace(opt_key, std::pair{-1, m_opt_status_value}); continue; } switch (m_config->option(opt_key)->type()) @@ -845,11 +849,11 @@ void TabPrinter::init_options_list() case coPercents:add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; case coFloatsOrPercents:add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; case coPoints: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; - default: m_options_list.emplace(opt_key, m_opt_status_value); break; + default: m_options_list.emplace(opt_key, std::pair{-1, m_opt_status_value}); break; } } if (m_printer_technology == ptFFF) - m_options_list.emplace("extruders_count", m_opt_status_value); + m_options_list.emplace("extruders_count", std::pair{-1, m_opt_status_value}); } void TabPrinter::msw_rescale() @@ -862,6 +866,15 @@ void TabPrinter::msw_rescale() Layout(); } +void TabFilament::init_options_list() +{ + if (!m_options_list.empty()) + m_options_list.clear(); + + for (const std::string &opt_key : m_config->keys()) + m_options_list.emplace(opt_key, std::pair(0, m_opt_status_value)); +} + void TabSLAMaterial::init_options_list() { if (!m_options_list.empty()) @@ -870,7 +883,7 @@ void TabSLAMaterial::init_options_list() for (const std::string& opt_key : m_config->keys()) { if (opt_key == "compatible_prints" || opt_key == "compatible_printers") { - m_options_list.emplace(opt_key, m_opt_status_value); + m_options_list.emplace(opt_key, std::pair{0, m_opt_status_value}); continue; } switch (m_config->option(opt_key)->type()) @@ -882,7 +895,7 @@ void TabSLAMaterial::init_options_list() case coPercents:add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; case coFloatsOrPercents:add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; case coPoints: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; - default: m_options_list.emplace(opt_key, m_opt_status_value); break; + default: m_options_list.emplace(opt_key, std::pair{0, m_opt_status_value}); break; } } } @@ -893,8 +906,8 @@ void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool if (opt == m_options_list.end()) return; - if (sys_page) sys_page = (opt->second & osSystemValue) != 0; - modified_page |= (opt->second & osInitValue) == 0; + if (sys_page) sys_page = (opt->second.second & osSystemValue) != 0; + modified_page |= (opt->second.second & osInitValue) == 0; } void Tab::update_changed_tree_ui() @@ -995,28 +1008,28 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) for (auto group : m_active_page->m_optgroups) { if (group->title == "Capabilities") { - if ((m_options_list["extruders_count"] & os) == 0) + if ((m_options_list["extruders_count"].second & os) == 0) to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count"); } if (group->title == "Size and coordinates") { - if ((m_options_list["bed_shape"] & os) == 0) { + if ((m_options_list["bed_shape"].second & os) == 0) { to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape"); load_key_value("bed_shape", true/*some value*/, true); } } if (group->title == "Toolchange parameters with single extruder MM printers") { - if ((m_options_list["filament_ramming_parameters"] & os) == 0) + if ((m_options_list["filament_ramming_parameters"].second & os) == 0) to_sys ? group->back_to_sys_value("filament_ramming_parameters") : group->back_to_initial_value("filament_ramming_parameters"); } if (group->title == "G-code Substitutions") { - if ((m_options_list["gcode_substitutions"] & os) == 0) { + if ((m_options_list["gcode_substitutions"].second & os) == 0) { to_sys ? group->back_to_sys_value("gcode_substitutions") : group->back_to_initial_value("gcode_substitutions"); load_key_value("gcode_substitutions", true/*some value*/, true); } } if (group->title == "Profile dependencies") { // "compatible_printers" option doesn't exists in Printer Settimgs Tab - if (type() != Preset::TYPE_PRINTER && (m_options_list["compatible_printers"] & os) == 0) { + if (type() != Preset::TYPE_PRINTER && (m_options_list["compatible_printers"].second & os) == 0) { to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers"); load_key_value("compatible_printers", true/*some value*/, true); @@ -1025,7 +1038,8 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) is_empty ? m_compatible_printers.btn->Disable() : m_compatible_printers.btn->Enable(); } // "compatible_prints" option exists only in Filament Settimgs and Materials Tabs - if ((type() == Preset::TYPE_FFF_FILAMENT || type() == Preset::TYPE_SLA_MATERIAL) && (m_options_list["compatible_prints"] & os) == 0) { + if ((type() == Preset::TYPE_FFF_FILAMENT || type() == Preset::TYPE_SLA_MATERIAL) && + (m_options_list["compatible_prints"].second & os) == 0) { to_sys ? group->back_to_sys_value("compatible_prints") : group->back_to_initial_value("compatible_prints"); load_key_value("compatible_prints", true/*some value*/, true); @@ -1036,7 +1050,7 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) } for (const auto &kvp : group->opt_map()) { const std::string& opt_key = kvp.first; - if ((m_options_list[opt_key] & os) == 0) + if ((m_options_list[opt_key].second & os) == 0) to_sys ? group->back_to_sys_value(opt_key) : group->back_to_initial_value(opt_key); } } diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 739c31f02dc..c3bf7b6dc16 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -321,8 +321,10 @@ class Tab: public wxPanel osInitPhony = 8, osCurrentPhony = 16, }; - std::map m_options_list; - std::map m_options_script; + // map> + std::map> m_options_list; + // map (script can't be vector) + std::map m_options_script; std::vector m_options_dirty; int m_opt_status_value = 0; @@ -550,6 +552,7 @@ class TabFilament : public Tab void toggle_options() override; void update() override; void clear_pages() override; + void init_options_list() override; PrinterTechnology get_printer_technology() const override { return ptFFF; } };